首页 文章 精选 留言 我的

精选列表

搜索[AI产设研一体],共10000篇文章
优秀的个人博客,低调大师

2021年网络黑产市场恐比肩世界第三大经济体

随着组织数字化,犯罪也随之数字化,地下犯罪产业正在前所未有地蓬勃发展。如今,网络犯罪已经成为一项庞大的业务,随着公司与消费者一同投入这个万亿美元的数字世界。 世界经济论坛(WEF)在《2020全球风险报告》中指出,网络犯罪是未来十年全球第二大引人注目的风险、全球第七大有可能发生的风险以及全球第八大有影响力的风险。现如今,企业的收入、利润和品牌声誉都搬到了线上,关键信息基础设施正面临威胁,各个国家之间正在相互进行网络战和网络间谍行动。 放眼世界,沃尔玛是全美国收入最多的公司,去年创造了 5140 亿美元的收入。但网络犯罪市场的收入是其 12 倍以上。实际上,论收入来说不只是沃尔玛,再加上特斯拉、Facebook、微软、苹果和亚马逊共计六家公司的收入总和也只有 1.28 万亿美元。 网络犯罪市场也分为几个不同的类别,攻击者会竭尽全力聚集在秘密、专用的讨论区中以避免审查。攻击者不断发展网络犯罪服务,从 DDoS 攻击到网络钓鱼,从恶意软件到窃密数据,所有愿意为此付费的人都可以找到对应的服务。网络犯罪正在进行“工业化”,攻击者也提供常规公司所作的一切:产品开发、技术支持、渠道分销、质量保证以及客户服务。犯罪分子不仅出售新技术或秘密计划,甚至还窃取军事机密。 团队作战 网络犯罪是一个日益受到关注的问题,并且对犯罪分子来说比实施诸如抢劫银行等传统犯罪的风险更低。世界经济论坛表示,在美国逮捕网络犯罪分子并在法院审判的概率低于 0.05%。 犯罪分子通常拥有一支平稳运行的团队运行着庞大的服务,网络犯罪分子的收入比传统犯罪分子高大约 10% 到 15%。当然,不同职位的犯罪分子的收入也存在着巨大的差距。收入通常取决于工作性质、相关风险以及团伙人数,最高收入的每年可以赚取超过二百万美元的收入。 有人认为,攻击者通常来说是在黑暗地下室里穿着连帽衫的怪异少年。时代变了,现在的网络犯罪分子变得很正规,从招聘员工到任命高管一应俱全。一些团伙甚至拥有公开身份,用以维持团伙的声誉,这在暗网中非常重要。 英国国家网络安全中心(NCSC)指出,有组织的网络犯罪分子分工明确确保组织平稳运行。“负责人”负责协调工作与熟悉相关法律、“数据挖掘者”负责处理盗窃的数据、“编码者”负责编写与更改恶意软件代码、“入侵专家”负责渗透进入目标公司、“呼叫中心”负责伪装呈计算机支持人员为受害者的电脑按照恶意软件、“金融专家”负责洗钱。 最受欢迎的服务 根据欧洲刑警组织(Europol)的分析,Exploit Kit 不再是最流行的产品。并不是因为出现了更先进的产品取代了利用工具包,而是网络犯罪的趋势转变向使用勒索软件和 DDoS 攻击了,这样可以更容易获利。 例如,利用大量联网设备来生成大量的流量,攻击可能持续数天之长。小型的攻击成本只需要十美元,复杂的攻击需要数千美元。攻击的目的可能是勒索、蓄意破坏、占用资源,也可能是隐藏其他类型的攻击。剑桥大学的研究显示,此类攻击非常普遍,以至于购买者中甚至还包括学龄前儿童。 欧洲刑警组织发布的《2019 互联网有组织犯罪威胁评估》报告中描述 DDoS 攻击已经成为全球企业面临的最严重安全威胁之一。去年,犯罪分子首选的 DDoS 目标是银行和其他金融机构、执法机关等公共组织、地方政府。此外,互联网基础设施和在线游戏也是常受到攻击的目标。据 Europol 的报告,一些犯罪分子的被捕并未影响 DDoS 攻击在暗网中的增长率。 从小型服务到全球巨头,新型的数字服务对各种规模的组织都是必不可少的。如果服务中断数小时甚至数天,公司的收入和声誉会受到重创。过去,这种业务中断传开需要一段时间。现在,这种消息会瞬间传遍大江南北。这就是僵尸网络成为网络犯罪分子的摇钱树的原因:使用僵尸网络进行 DDoS 攻击破坏服务来进行勒索。 尽管风险意识正在增强,越来越多的公司也在网络风险管理上投入更多资金。但是 WEF 表示,对于威胁的规模来说,网络安全支出仍然远远没有达到需要。

优秀的个人博客,低调大师

1分钟完成MySQL5.6生产库自动化安装部署

1分钟完成MySQL5.6安装部署 简介 Part1:写在最前 自动化运维是一个DBA应该掌握的技术,其中,自动化安装数据库是一项基本的技能,本文中的安装脚本已通过测试,作为生产库来说没有问题,鉴于每个公司存储规划要求不同,可以按需自行修改脚本。 由于源码安装费时费力,rpm包可定制性差,生产库一般采用二进制安装包,本文的安装包为二进制安装文件,本文使用的是mysql-5.6.25-linux-glibc2.5-x86_64.tar.gz,如版本不同,替换第109、110行的tar命令后的mysql安装包名即可 Part2:新特性简介 本脚本默认启用5.6部分新特性 innodb_buffer_pool_dump_at_shutdown=1 它dump的不是数据,是Id号 innodb_buffer_pool_load_at_startup=1 开启这个两个参数当数据库重启后把这些热数据重新加载回去 只有正常关库才会dump热数据块,宕机和kill -9不会 部分参数按需整改,例如innodb_buffer_pool_size,本文给的512M,一般为内存的50%-80% 来看一下脚本的具体情况 实战 Part1:自动化脚本 [root@HE3 ~]# cat mysql_auto_install.sh ######二进制自动安装数据库脚本root密码MANAGER将脚本和安装包放在/root目录即可############### ######数据库目录/usr/local/mysql############ ######数据目录/data/mysql############ ######日志目录/log/mysql############ ######端口号默认3306其余参数按需自行修改############ ################## #author:rrhelei@126.com# ################## #!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:~/bin exportPATH #Checkifuserisroot if[$(id-u)!="0"];then echo"Error:Youmustberoottorunthisscript,pleaseuseroottoinstall" exit1 fi clear echo"=========================================================================" echo"Atooltoauto-compile&installMySQL5.6.25onRedhat/CentOSLinux" echo"=========================================================================" cur_dir=$(pwd) #setmysqlrootpassword echo"===========================" mysqlrootpwd="MANAGER" echo-e"Pleaseinputtherootpasswordofmysql:" read-p"(Defaultpassword:MANAGER):"mysqlrootpwd if["$mysqlrootpwd"=""];then mysqlrootpwd="MANAGER" fi echo"===========================" echo"MySQLrootpassword:$mysqlrootpwd" echo"===========================" #whichMySQLVersiondoyouwanttoinstall? echo"===========================" isinstallmysql56="n" echo"InstallMySQL5.6.25,Pleaseinputy" read-p"(Pleaseinputy,n):"isinstallmysql56 case"$isinstallmysql56"in y|Y|Yes|YES|yes|yES|yEs|YeS|yeS) echo"YouwillinstallMySQL5.6.25" isinstallmysql56="y" ;; *) echo"INPUTerror,YouwillexitinstallMySQL5.6.25" isinstallmysql56="n" exit esac get_char() { SAVEDSTTY=`stty-g` stty-echo sttycbreak #ddif=/dev/ttybs=1count=12>/dev/null stty-raw sttyecho stty$SAVEDSTTY } echo"" echo"Pressanykeytostart...orPressCtrl+ctocancel" char=`get_char` #Initializetheinstallationrelatedcontent. functionInitInstall() { cat/etc/issue uname-a MemTotal=`free-m|grepMem|awk'{print$2}'` echo-e"\nMemoryis:${MemTotal}MB" #Settimezone #rm-rf/etc/localtime #ln-s/usr/share/zoneinfo/Asia/Shanghai/etc/localtime #DeleteOldMysqlprogram rpm-qa|grepmysql rpm-emysql #yum-yremovemysql-servermysqlmysql-libs #yum-yremovephp-mysql #yum-yinstallyum-fastestmirror #yum-yupdate #DisableSeLinux if[-s/etc/selinux/config];then sed-i's/SELINUX=enforcing/SELINUX=disabled/g'/etc/selinux/config fi setenforce0 } #Installationofdependonandoptimizationoptions. functionInstallDependsAndOpt() { cd$cur_dir cat>>/etc/security/limits.conf<<EOF *softnproc65535 *hardnproc65535 *softnofile65535 *hardnofile65535 EOF echo"fs.file-max=65535">>/etc/sysctl.conf } #InstallMySQL functionInstallMySQL56() { echo"============================InstallMySQL5.6.22==================================" cd$cur_dir #Backupoldmy.cnf #rm-f/etc/my.cnf if[-s/etc/my.cnf];then mv/etc/my.cnf/etc/my.cnf.`date+%Y%m%d%H%M%S`.bak fi #mysqldirectoryconfiguration groupaddmysql-g512 useradd-u512-gmysql-s/sbin/nologin-d/home/mysqlmysql tarxvf/root/mysql-5.6.25-linux-glibc2.5-x86_64.tar.gz mv/root/mysql-5.6.25-linux-glibc2.5-x86_64/usr/local/mysql mkdir-p/data/mysql mkdir-p/log/mysql chown-Rmysql:mysql/data/mysql chown-Rmysql:mysql/usr/local/mysql chown-Rmysql:mysql/log #edit/etc/my.cnf SERVERID=`ifconfigeth0|grep"inetaddr"|awk'{print$2}'|awk-F.'{print$3$4}'` cat>>/etc/my.cnf<<EOF [client] port=3306 socket=/tmp/mysql.sock default-character-set=utf8 [mysql] default-character-set=utf8 [mysqld] port=3306 socket=/tmp/mysql.sock basedir=/usr/local/mysql datadir=/data/mysql open_files_limit=3072 back_log=103 max_connections=800 max_connect_errors=100000 table_open_cache=512 external-locking=FALSE max_allowed_packet=32M sort_buffer_size=2M join_buffer_size=2M thread_cache_size=51 query_cache_size=32M tmp_table_size=96M max_heap_table_size=96M slow_query_log=1 slow_query_log_file=/log/mysql/slow.log log-error=/log/mysql/error.log long_query_time=1 server-id=$SERVERID log-bin=mysql-bin sync_binlog=1 binlog_cache_size=4M max_binlog_cache_size=4096M max_binlog_size=1024M expire_logs_days=60 key_buffer_size=32M read_buffer_size=1M read_rnd_buffer_size=16M bulk_insert_buffer_size=64M character-set-server=utf8 default-storage-engine=InnoDB binlog_format=row innodb_buffer_pool_dump_at_shutdown=1 innodb_buffer_pool_load_at_startup=1 binlog_rows_query_log_events=1 explicit_defaults_for_timestamp=1 #log_slave_updates=1 #gtid_mode=on #enforce_gtid_consistency=1 #innodb_write_io_threads=8 #innodb_read_io_threads=8 #innodb_thread_concurrency=0 transaction_isolation=REPEATABLE-READ innodb_additional_mem_pool_size=16M innodb_buffer_pool_size=512M #innodb_data_home_dir= innodb_data_file_path=ibdata1:1024M:autoextend innodb_flush_log_at_trx_commit=1 innodb_log_buffer_size=16M innodb_log_file_size=512M innodb_log_files_in_group=2 innodb_max_dirty_pages_pct=50 innodb_file_per_table=1 innodb_locks_unsafe_for_binlog=0 wait_timeout=14400 interactive_timeout=14400 skip-name-resolve [mysqldump] quick max_allowed_packet=32M EOF /usr/local/mysql/scripts/mysql_install_db--basedir=/usr/local/mysql--datadir=/data/mysql--defaults-file=/etc/my.cnf--user=mysql cp/usr/local/mysql/support-files/mysql.server/etc/init.d/mysqld chmod700/etc/init.d/mysqld chkconfig--addmysqld chkconfig--level2345mysqldon cat>>/etc/ld.so.conf.d/mysql-x86_64.conf<<EOF /usr/local/mysql/lib EOF ldconfig if[-d"/proc/vz"];then ulimit-sunlimited fi /etc/init.d/mysqldstart cat>>/etc/profile<<EOF exportPATH=$PATH:/usr/local/mysql/bin exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/mysql/lib EOF source/etc/profile /usr/local/mysql/bin/mysqladmin-urootpassword$mysqlrootpwd cat>/tmp/mysql_sec_script<<EOF usemysql; deletefrommysql.userwhereuser!='root'orhost!='localhost'; grantallprivilegeson*.*to'sys_admin'@'%'identifiedby'MANAGER'; flushprivileges; EOF /usr/local/mysql/bin/mysql-uroot-p$mysqlrootpwd-hlocalhost</tmp/mysql_sec_script #rm-f/tmp/mysql_sec_script /etc/init.d/mysqldrestart echo"============================MySQL5.6.25installcompleted=========================" } functionCheckInstall() { echo"=====================================Checkinstall===================================" clear ismysql="" echo"Checking..." if[-s/usr/local/mysql/bin/mysql]&&[-s/usr/local/mysql/bin/mysqld_safe]&&[-s/etc/my.cnf];then echo"MySQL:OK" ismysql="ok" else echo"Error:/usr/local/mysqlnotfound!!!MySQLinstallfailed." fi if["$ismysql"="ok"];then echo"InstallMySQL5.6.25completed!enjoyit." echo"=========================================================================" netstat-ntl else echo"Sorry,FailedtoinstallMySQL!" echo"Youcantail/root/mysql-install.logfromyourserver." fi } #Theinstallationlog InitInstall2>&1|tee/root/mysql-install.log CheckAndDownloadFiles2>&1|tee-a/root/mysql-install.log InstallDependsAndOpt2>&1|tee-a/root/mysql-install.log InstallMySQL562>&1|tee-a/root/mysql-install.log CheckInstall2>&1|tee-a/root/mysql-install.log Part2:安装 下载完毕安装包,将脚本和安装文件放在/root目录下,执行上述脚本即可(如不是5.6.25版本,需修改脚本132、133行为对应版本号) [root@HE3 ~]# sh -x mysql_auto_install.sh 如首次安装,请再次执行source /etc/profile [root@HE3 ~]#source /etc/profile 验证 执行脚本后,输入用户名密码(默认MANAGER)后登录数据库成功。 至此,MySQL5.6安装完成。

优秀的个人博客,低调大师

抖音自研文生图评估体系 EvalMuse 入选 AAAI 2026

抖音多媒体质量实验室宣布,其自主研发的图像生成评估体系 EvalMuse 已成功入选 AAAI 2026。这一成果标志着中国团队在国际顶级人工智能学术会议上对文本生成图像(T2I)评估方法的突破性贡献。 EvalMuse 旨在解决当前 T2I 模型评估中存在的主观性与一致性不足问题。该体系通过引入多维度指标与自动化评估框架,提升了对生成图像的质量判定能力。 整体的工作架构图如下所示: 实验室方面指出,EvalMuse 能够更准确地反映模型在语义一致性、图像清晰度及创意表达上的表现,还为未来大规模模型的标准化评估提供了参考路径。 论文:https://arxiv.org/abs/2412.18150 开源地址:https://shh-han.github.io/EvalMuse-project/

优秀的个人博客,低调大师

华为自研仓颉编程语言将于 7 月 30 日开源

在华为开发者大会HDC 2025期间,华为宣布仓颉编程语言将于7月30日开源。 2024 年 6 月,华为终端 BG 软件部总裁龚体宣布,下一代编程语言仓颉今日起正式开启预览。官网介绍显示,仓颉编程语言是一款面向全场景智能的新一代编程语言,主打智能化、全场景、高性能、强安全。融入鸿蒙生态,为开发者提供良好的编程体验。 其具体特性表现为: 高效编程:面向应用开发,希望语言能够易学易用,降低开发者入门门槛和开发过程中的心智负担,支持各种常见的开发范式和编程模式,让开发者简洁高效地表达各种业务逻辑。仓颉是一门多范式编程语言,支持函数式、命令式和面向对象等多种范式,包括值类型、类和接口、泛型、代数数据类型、模式匹配、以及高阶函数等特性。此外,仓颉还支持类型推断,能够减轻开发者类型标注的负担;通过一系列简明高效的语法,能够减少冗余书写、提升开发效率;语言内置的各种语法糖和宏(macro)的能力,支持开发者基于仓颉快速开发领域专用语言(DSL),构建领域抽象。 安全可靠:作为现代编程语言,仓颉追求编码即安全,通过静态类型系统和自动内存管理,确保程序的类型安全和 null safety 等内存安全;同时,仓颉还提供各种运行时检查,包括数组下标越界检查、类型转换检查、数值计算溢出检查、以及字符串编码合法性检查等,能够及时发现程序运行中的错误;此外,还通过代码扫描工具、混淆工具以及消毒器,进一步提供跨语言互操作安全和代码资产保护等支持。 轻松并发:并发和异步编程能够有效提高处理器利用率,并在交互式应用中确保程序的响应速度,是应用开发中必不可少的能力。仓颉语言实现了轻量化用户态线程和并发对象库,让高效并发变得轻松。仓颉语言采用用户态线程模型,每个仓颉线程都是极其轻量级的执行实体,拥有独立的执行上下文但共享内存。对开发者来说,用户态线程的使用和传统的系统线程的使用方式保持一致,没有带来额外负担;而从运行态视角看,线程的管理由运行时完成,不依赖操作系统的线程管理,因此线程的创建、调度和销毁等操作更加高效,且资源占用比系统线程更少。为了避免数据竞争,仓颉语言提供了并发对象库,并发对象的方法是线程安全的,因此在多线程中调用这些方法和串行编程没有区别,应用逻辑的开发者无需额外关心并发管理。对于一些核心库,仓颉还提供了无锁或者细粒度锁的算法实现,能够进一步减少线程的阻塞,提升并发度。 卓越性能:仓颉编译器及运行时从全栈对编译进行优化,包括编译器前端基于 CHIR(Cangjie HighLevel IR)高层编译优化(比如语义感知的循环优化、语义感知的后端协同优化等),基于后端的编译优化(比如:SLP 向量化、Intrinsic 优化、InlineCache、过程间指针优化、Barrier 优化等),基于运行时的优化(比如轻量锁、分布式标记、并发 Tracing 优化等),一系列的优化让仓颉充分发挥处理器能力,为应用提供卓越的性能支持。另外仓颉语言对运行时进行原生的轻量化设计,通过对运行时模块化分层设计,定义仓颉公共对象模型和运行时公共基础组件,基于公共对象模型,实现运行时的内存管理、回栈、异常处理、跨语言调用等基础能力,大幅减少多个能力间的冗余对象设计,精简运行时体积。同时通过包的按需加载技术,减少仓颉应用启动的冗余包内存开销,因此对于资源敏感设备,占用资源更少,支持更友好。 除此之外,仓颉还支持面向应用开发的一系列工具链,包括语言服务(高亮、联想)、调试(跨语言调试、线程级可视化调试)、静态检查、性能分析、包管理、文档生成、Mock 工具、测试框架、覆盖率工具、Fuzz 工具以及智能辅助编程工具,进一步提升软件开发体验以及效率。 相关阅读: 华为仓颉编程语言正式亮相 仓颉编程语言官网正式上线,首个公测版本开放下载

优秀的个人博客,低调大师

得物自研DGraph4.0推荐核心引擎升级之路

一、前言 DGraph是得物自主研发的新一代推荐系统核心引擎,基于C++语言构建,自2021年启动以来,经过持续迭代已全面支撑得物社区内容分发、电商交易等核心业务的推荐场景。DGraph在推荐链路中主要承担数据海选和粗排序功能,为上层精排提供高质量候选集。 核心技术特性: 索引层 - 支持KV(键值)、KVV(键-多值)、INVERT(倒排)、DENSE-KV(稠密键值)等。索引存储支持磁盘 & 内存两种模式,在预发等延迟压力低场景,通过磁盘索引使用低规格服务器提供基本服务。线上场景使用内存索引保证服务稳定性,提供毫秒级延迟响应。索引更新支持双buff热更新【内存足够】、服务下线滚动更新【内存受限】、Kafka流式数据实时更新等三种模式。 查询层 - 支持向量检索IVF & HNSW、键值(KV)查询、倒排检索、X2I关联查询、图查询。对外提供JavaSDK & C++ SDK。 系统依赖架构: 索引全生命周期管理由得物索引平台DIP统一管控。 服务发现基于ZooKeeper(zk)。 集群资源调度基于得物容器平台,目前已经支持HPA。 服务规模: 目前在线100+集群,2024年双11在线突破了100W qps。 本文主要介绍DGraph系统在2024年的一些重要改进点。主要包括两次架构调整 + 性能优化 + 用户体验提升方面的一些工作。 二、架构升级 2.1 垂直拆分业务集群支持 在2023年前,DGraph系统始终采用单一集群架构提供服务。该架构模式在平台发展初期展现出良好的经济性和运维便利性,但随着业务规模扩张,单集群架构在系统层面逐渐显露出三重刚性约束: 存储容量瓶颈 - 单节点内存上限导致数据规模受限; 网络带宽瓶颈 - 单物理机Pod共享10Gbps带宽,实际可用带宽持续承压,推荐引擎业务中部分核心集群200余张数据表(单表需20分钟级更新)的实时处理需求已遭遇传输瓶颈; 计算能力瓶颈 - 单实例最大64核的算力天花板,难以支撑复杂策略的快速迭代,核心场景响应时效与算法复杂度形成显著冲突; 稳定性 - 大规格集群对于容器调度平台不友好,在扩容、集群故障、集群发布时耗时较久;基于得物平台推荐数据量增长和算法迭代需求,我们实施业务垂直拆分的多集群架构升级,通过资源解耦与负载分离,有效突破了单节点资源约束,为复杂算法策略的部署预留出充足的技术演进空间。 系统改进点是在DGraph中增加了访问了其他DGraph集群 & FeatureStore特征集群的能力(图1)。为了成本考虑,我们复用了之前系统的传输协议flatbuffers,服务发现仍基于ZooKeeper。 图 1 DGraph 访问架构改进 改造的难点在图化集群! 目前推荐业务的核心场景都进行了图化改造,图化查询是把多路召回、打散、融合、粗排等策略打包到一个DAG图中一次发送到DGraph,DGraph的算子调度模块根据DAG的描述查询索引数据 & 执行算子最终把结果返回给业务系统,但这些DAG图规模都很大,部分业务DAG图涉及300+算子,因此如何在垂直拆分业务中把这些DAG图拆分到不同的DGraph集群中是一个非常复杂的问题,我们主要做了三方面改进: DAG管理 - 集群分主集群和从集群【多个】,DAG图部署在存在主集群中,DIP平台会分析DAG的拓步结构并把属于从集群的部分复制出来分发给从集群,为了保证DAG的一致性,只允许从主集群修改DAG图; 集群划分 - 通常按召回划分,比如Embedding召回、X2I召回、实验召回可以分别部署在不同的集群,另外也可以把粗排等算力需求大的部分单独放在一个集群,具体根据业务场景调整; 性能优化 - 核心表多个集群存放,减少主集群和从集群间数据交换量。 图 2 DGraph业务垂直拆分集群 2.2 分布式能力支持 垂直拆分集群,虽然把推荐N路召回分散到了M个集群,但是每个集群中每个表依然是全量。随着得物业务的发展,扩类目、扩商品,部分业务单表的数据量级已经接近单集群的存储瓶颈。因此需要DGraph中引入数据水平拆分的能力。 图 3 DGraph 分布式集群架构图 在DGraph分布式架构设计中,重点考虑了部署成本优化与业务迁移工作量: 分布式集群采用【分片数2】×【双活节点2】×【数据副本数2】的最小拓扑结构,理论上需要8台物理节点保障滚动更新与异常容灾时的稳定性。针对CPU负载较轻的场景,为避免独立Proxy集群带来的额外资源开销,DGraph将Proxy模块和DGraph引擎以对称架构部署到所有节点,通过本地优先的智能路由策略(本地节点轮询优先于跨节点访问)实现资源利用率与访问效率的平衡; 在业务兼容性方面,基础查询接口(KV检索、倒排索引、X2I关联查询)保持完全兼容以降低迁移成本,而DAG图查询需业务侧在查询链路中明确指定Proxy聚合算子的位置以发挥分布式性能优势。数据链路层面,通过DIP平台实现索引无缝适配,支持DataWorks原有任务无需改造即可对接分布式集群,同时增量处理模块内置分片过滤机制,可直接复用现有Flink实时计算集群进行数据同步。 三、性能优化 3.1 算子执行框架优化 在DGraph中,基于DGraph DAG图(参考图9)的一次查询就是图查询,内部简称graphSearch。在一个DAG图中,每个节点都是一个算子(简称Op),算子通过有向边连接其他算子,构成一个有向无环图,算子执行引擎按DAG描述的关系选择串行或者并发执行所有算子,通过组合不同算子DAG图能在推荐场景中灵活高效的完成各种复杂任务。 在实际应用场景中受DAG图规模 & 超时时间(需要控制在100ms内)限制,算子执行框架的效率非常重要。在最开始的版本中我们使用过Omp & 单队列线程池,集群在CPU负载低于30%时表现尚可,但在集群CPU负载超过30%后,rt99表现糟糕。在降本增效的背景下,我们重点对算子执行框架进行了优化,引入了更高效的线程池 & 减少了调度过程中锁的使用。优化后目前DGraph 在CPU压力超过60%依然可以提供稳定服务。 图4 DGraph算子执行框架优化 线程池优化:将原1:N 的线程池-队列架构调整为M:N 分组模式。具体实现为将N个工作线程划分为M个执行组(每组N/M线程),各组配备独立任务队列。任务提交采用轮询分发机制至对应组队列,通过资源分区有效降低线程调度时的锁竞争强度。 调度器优化:在DAG调度过程中存在两个典型多写场景 前驱算子节点完成时需并行更新后继节点标记; DAG全局任务计数器归零判断。原方案通过全局锁(Graph锁+Node锁)保障原子性,但在高负载场景引发显著锁竞争开销,影响线程执行效率。经分析发现这两个状态变更操作符合特定并发模式:所有写操作均为单调增减操作,因此可将锁机制替换为原子变量操作。针对状态标记和任务计数场景,分别采用原子变量的FetchAdd和FetchSub指令即可实现无锁化同步,无需引入CAS机制即满足线程安全要求。 3.2 传输协议编码解码优化 优化JavaSDK - DGraph数据传输过程:在DGraph部分场景,由于请求引擎返回的数据量很大,解码编码耗时占整个请求20%以上。分析已有的解码编码模块,引擎在编码阶段会把待传输数据编码到一个FlatBuffer中,然后通过rpc协议发送到业务侧的JavaSDK,sdk 解码FlatBuffer封装成List<map> 返回给业务代码,业务代码再把List<map> 转化成 List<业务Object>。过程中没有并发 & sdk侧多了一层冗余转换。 优化方案如下: 串行编码调整为根据文档数量动态调整编码块数量。各子编码块可以并发编码解码,加快编码&解码速度,提升整体传输性能; sdk 侧由 Doc -> Map -> JavaObject 的转化方式调整为 Doc -> JavaObject,减少解码端算力开销。 图5 DGraph 传输编码解码过程优化 四、用户体验优化 4.1 DAG图调试功能优化 目前我们已经把DGraph DAG图查询的调试能力集成到DIP平台。其原理是:DGraph 的算子基类实现了执行结果输出,由于算子的中间结果数据量极大,当调试模块发现调试标志后会先把当前算子的中间结果写入日志中,数据按TraceID + DAGID+ NodeID 组织,最终这些数据被采集到SLS日志平台。 图6 DGraph DAG图查询调试 从DIP平台调试DAG图请求,首先通过DGraph JavaSDK的调试入口拿到DAG图请求json,填入DIP平台图请求调试入口,发起请求。索引平台会根据请求体自动关联DAG图并结合最终执行结果通过页面的方式展示。DIP平台拿到结果后,在DAG图中成功的算子节点标记为绿色,失败的节点标记为红色(图6)。点击任意节点可以跳转到日志平台查看该节点的中间结果输出。可用于分析DAG图执行过程中的各种细节,提升业务排查业务问题效率。 4.2 DAG图支持TimeLine分析 基于Chrome浏览器中的TimeLine构建,用于DGraph DAG图查询时算子性能分析优化工作。TimeLine功能集成在算子基类中,启动时会记录每个算子的启动时间、等待时间、完成时间、执行线程pid等信息,这些信息首先输出到日志,然后被SLS日志平台采集。用户可以使用查询时的TraceID在日志平台搜索相关的TimeLine信息。 图7 DGraph DAG图例子 图8 使用浏览器查看DGraph DAG图 TimeLine 当我们拿到请求的TimeLine信息后,通过浏览器加载可以通过图形化的方式分析DAG执行过程中耗时分布。图7是一个DAG 请求,它有9个算子节点,图8是它的一次请求的TimeLine。通过分析这些算子的耗时,可以帮助我们定位当前DAG图查询的瓶颈点在哪里,从而精准去解决性能方面的问题。 4.3 DAG图支持动态子图 在DAG图召回中,业务的召回通常都带有一些固定模式,比如一个业务在一个DAG图召回中有N路召回,每一路召回都是:① 查找数据;② 关联可推池;③ 打散; 它们之间的区别可能仅仅是召回数据表名不同或者传递的参数不同。通常我们业务调整或者算法实验调整只需要增加或者减少部分召回,原有模式下这些操作需要去新增或者修改DAG图,加上算法实验很多,业务维护DAG图的成本会非常高。 DAG动态子图的引入就是为了解决这类问题,首先我们在DAG图中配置一个模板子图,它仅仅描述一个行为模式,代表会涉及几个算子,算子之间的关系如何,实际的参数以及召回路的数量则由业务方在发起请求时动态决定。子图的执行和主图的执行共用同一套调度框架,共享运行时资源以降低运行开销。 图9 DGraph 子图 图9是一个DAG召回使用DAG子图后的变化,它有8路召回,一个Merge节点,这些召回分为两类,一类是基于KV表(ForwardSearch)触发的向量召回,另外一类是基于KVV表(IvtSearch)触发的向量召回。引入DAG子图后,在主图中节点数量由17个降为3个。 五、展望未来 过去四年,DGraph聚焦于实现得物推荐引擎体系从0到1的突破,重点完成了核心系统架构搭建、算法策略支持及业务迭代空间拓展,取得多项基础性成果。基于2024年底的用户调研反馈结合DGraph当前的发展,后续将重点提升产品易用性、开发与运维效能及用户体验,同时在系统稳定性、可扩展架构和平台化建设方面持续深化。 算法团队大量HC,欢迎加入我们:得物技术大量算法岗位多地上线,“职”等你来! 文 / 寻风 关注得物技术,每周一、三更新技术干货 要是觉得文章对你有帮助的话,欢迎评论转发点赞~ 未经得物技术许可严禁转载,否则依法追究法律责任。

优秀的个人博客,低调大师

字节自研 K8s 存储 KubeBrain

KubeBrain 是字节跳动针对 Kubernetes 元信息存储的使用需求,基于分布式 KV 存储引擎设计并实现的、可以取代 etcd 的元信息存储系统,目前支撑着线上超过 20,000 节点的超大规模 Kubernetes 集群的稳定运行。 项目地址:github.com/kubewharf/kubebrain 分布式应用编排调度系统 Kubernetes 已经成为云原生应用基座的事实标准,但是其官方的稳定运行规模仅仅局限在 5,000 节点。这对于大部分的应用场景已经足够,但是对于百万规模机器节点的超大规模应用场景, Kubernetes 难以提供稳定的支撑。 尤其随着“数字化””云原生化”的发展,全球整体 IT 基础设施规模仍在加速增长,对于分布式应用编排调度系统,有两种方式来适应这种趋势: 水平扩展:即构建管理多个集群的能力,在集群故障隔离、混合云等方面更具优势,主要通过集群联邦(Cluster Federation)来实现; 垂直扩展:即提高单个集群的规模,在降低集群运维管理成本、减少资源碎片、提高整体资源利用率方面更具优势。 K8s 采用的是一种中心化的架构,所有组件都与 APIServer 交互,而 APIServer 则需要将集群元数据持久化到元信息存储系统中。 当前,etcd 是 APIServer 唯一支持的元信息存储系统,随着单个集群规模的逐渐增大,存储系统的读写吞吐以及总数据量都会不断攀升,etcd 不可避免地会成为整个分布式系统的瓶颈。 Kubernetes 元信息存储需求 APIServer 并不能直接使用一般的强一致 KV 数据库作为元信息存储系统,它与元信息存储系统的交互主要包括数据全量和增量同步的 List/Watch,以及单个 KV 读写。更近一步来说,它主要包含以下方面: 在版本控制方面,存储系统需要对 APIServer 暴露数据的版本信息,APIServer 侧依赖于数据的版本生成对应的 ResourceVersion; 在写操作方面,存储系统需要支持 Create/Update/Delete 三种语义的操作,更为重要的是,存储系统需要支持在写入或者删除数据时对数据的版本信息进行 CAS; 在读操作方面,存储系统需要支持指定版本进行快照 List 以此从存储中获取全量的数据,填充 APIServer 中的 WatchCache 或供查询使用,此外也需要支持读取数据的同时获取对应的数据版本信息; 在事件监听方面,存储系统需要支持获取特定版本之后的有序变更,这样 APIServer 通过 List 从元信息存储中获取了全量的数据之后,可以监听快照版本之后的所有变更事件,进而以增量的方式来更新 Watch Cache 以及向其他组件进行变更的分发,进而保证 K8s 各个组件中数据的最终一致性。 etcd 的实现方式与瓶颈 etcd 本质上是一种主从架构的强一致、高可用分布式 KV 存储系统: 节点之间,通过 Raft 协议进行选举,将操作抽象为 log 基于 Raft 的日志同步机制在多个状态机上同步; 单节点上,按顺序将 log 应用到状态机,基于 boltdb 进行状态持久化 。 对于 APIServer 元信息存储需求,etcd 大致通过以下方式来实现: 在版本控制方面,etcd 使用 Revision 作为逻辑时钟,对每一个修改操作,会分配递增的版本号 Revision,以此进行版本控制,并且在内存中通过 TreeIndex 管理 Key 到 Revision 的索引; 在写操作方面,etcd 以串行 Apply Raft Log 的方式实现,以 Revision 为键,Key/Value/Lease 等数据作为值存入 BoltDB 中,在此基础上实现了支持对 Revision 进行 CAS 的写事务; 在读操作方面,etcd 则是通过管理 Key 到 Revision 的 TreeIndex 来查询 Revision 进而查询 Value,并在此基础上实现快照读; 在事件监听方面,历史事件可以从 BoltDB 中指定 Revision 获取 KV 数据转换得到,而新事件则由写操作同步 Notify 得到。 etcd 并不是一个专门为 K8s 设计的元信息存储系统,其提供的能力是 K8s 所需的能力的超集。在使用过程中,其暴露出来的主要问题有: etcd 的网络接口层限流能力较弱,雪崩时自愈能力差; etcd 所采用的是单 raft group,存在单点瓶颈,单个 raft group 增加节点数只能提高容错能力,并不能提高写性能; etcd 的 ExpensiveRead 容易导致 OOM,如果采用分页读取的话,延迟相对会提高; boltdb 的串行写入,限制了写性能,高负载下写延迟会显著提高; 长期运行容易因为碎片问题导致写性能发生一定劣化,线上集群定期通过 defrag 整理碎片,一方面会比较复杂,另一方面也可能会影响可用性。 新的元数据存储 过去面对生产环境中 etcd 的性能问题,只能通过按 Resource 拆分存储、etcd 参数调优等手段来进行一定的缓解。但是面对 K8s 更大范围的应用之后带来的挑战,我们迫切的需要一个更高性能的元数据存储系统作为 etcd 的替代方案,从而能对上层业务有更有力的支撑。 在调研了 K8s 集群的需求以及相关开源项目之后,我们借鉴了 k3s 的开源项目 kine 的思想,设计并实现了基于分布式 KV 存储引擎的高性能 K8s 元数据存储项目—— KubeBrain。 KubeBrain 系统实现了 APIServer 所使用的元信息存储 API ,整体采用主从架构,主节点负责处理写操作和事件分发,从节点负责处理读操作,主节点和从节点之间共享一个分布式强一致 KV 存储,在此基础上进行数据读写。下面介绍 KubeBrain 的核心模块。 存储引擎 KubeBrain 统一抽象了逻辑层所使用的 KeyValue 存储引擎接口,以此为基础,项目实现了核心逻辑与底层存储引擎的解耦: 逻辑层基于存储引擎接口来操作底层数据,不关心底层实现; 对接新的存储引擎只需要实现对应的适配层,以实现存储接口。 目前项目已经实现了对 ByteKV 和 TiKV 的适配,此外还实现了用于测试的适配单机存储 Badger 的版本。需要注意的是,并非所有 KV 存储都能作为 KubeBrain 的存储引擎。当前 KubeBrain 对于存储引擎有着以下特性要求: 支持快照读 支持双向遍历 支持读写事务或者带有 CAS 功能的写事务 对外暴露逻辑时钟 此外,由于 KubeBrain 对于上层提供的一致性保证依赖于存储引擎的一致性保证, KubeBrain 要求存储引擎的事务需要达到以下级别(定义参考HATs): Isolation Guarantee: Snapshot Isolation Session Guarantee: Linearizable 在内部生产环境中, KubeBrain 均以 ByteKV 为存储引擎提供元信息存储服务。ByteKV 是一种强一致的分布式 KV 存储。在 ByteKV 中,数据按照 key 的字典序有序存储。当单个 Partition 数据大小超过阈值时, Partition 自动地分裂,然后可以通过 multi-raft group 进行水平扩展,还支持配置分裂的阈值以及分裂边界选择的规则的定制。此外, ByteKV 还对外暴露了全局的时钟,同时支持写事务和快照读,并且提供了极高的读写性能以及强一致的保证。 选主机制 KubeBrain 基于底层强一致的分布式 KV 存储引擎,封装实现了一种 ResourceLock,在存储引擎中指向一组特定的 KeyValue。ResourceLock 中包含主节点的地址以及租约的时长等信息。 KubeBrain 进程启动后均以从节点的身份对自己进行初始化,并且会自动在后台进行竞选。竞选时,首先尝试读取当前的 ResourceLock。如果发现当前 ResourceLock 为空,或者 ResourceLock 中的租约已经过期,节点会尝试将自己的地址以及租约时长以 CAS 的方式写入 ResourceLock,如果写入成功,则晋升为主节点。 从节点可以通过 ResourceLock 读取主节点的地址,从而和主节点建立连接,并进行必要的通信,但是主节点并不感知从节点的存在。即使没有从节点,单个 KubeBrain 主节点也可以提供完成的 APIServer 所需的 API,但是主节点宕机后可用性会受损。 逻辑时钟 KubeBrain 与 etcd 类似,都引入了 Revision 的概念进行版本控制。KubeBrain 集群的发号器仅在主节点上启动。当从节点晋升为主节点时,会基于存储引擎提供的逻辑时钟接口来进行初始化,发号器的Revision 初始值会被赋值成存储引擎中获取到的逻辑时间戳。 单个 Leader 的任期内,发号器发出的整数号码是单调连续递增的。主节点发生故障时,从节点抢到主,就会再次重复一个初始化的流程。由于主节点的发号是连续递增的,而存储引擎的逻辑时间戳可能是非连续的,其增长速度是远快于连续发号的发号器,因此能够保证切主之后, Revision 依然是递增的一个趋势,旧主节点上发号器所分配的最大的 Revision 会小于新主节点上发号器所分配的最小的Revision。 KubeBrain 主节点上的发号是一个纯内存操作,具备极高的性能。由于 KubeBrain 的写操作在主节点上完成,为写操作分配 Revision 时并不需要进行网络传输,因此这种高性能的发号器对于优化写操作性能也有很大的帮助。 请求解析数据模型 KubeBrain 对于 API Server 读写请求参数中的 Raw Key,会进行编码出两类 Internal Key写入存储引擎索引和数据。对于每个 Raw Key,索引 Revision Key 记录只有一条,记录当前 Raw Key 的最新版本号, Revision Key 同时也是一把锁,每次对 Raw Key 的更新操作需要对索引进行 CAS。数据记录 Object Key 有一到多条,每条数据记录了 Raw Key 的历史版本与版本对应的 Value。Object Key 的编码方式为magic+raw_key+split_key+revision,其中: magic为\x57\xfb\x80\x8b; raw_key为实际 API Server 输入到存储系统中的 Key ; split_key为$; revision为逻辑时钟对写操作分配的逻辑操作序号通过 BigEndian 编码成的 Bytes 。 根据 Kubernetes 的校验规则,raw_key 只能包含小写字母、数字,以及'-' 和 '.',所以目前选择 split_key 为 $ 符号。 特别的,Revision Key 的编码方式和 Object Key 相同,revision取长度为 8 的空 Bytes 。这种编码方案保证编码前和编码后的比较关系不变。 在存储引擎中,同一个 Raw Key 生成的所有 Internal Key 落在一个连续区间内 。 这种编码方式有以下优点: 编码可逆,即可以通过Encode(RawKey,Revision)得到InternalKey,相对应的可以通过Decode(InternalKey)得到Rawkey与Revision; 将 Kubernetes 的对象数据都转换为存储引擎内部的 Key-Value 数据,且每个对象数据都是有唯一的索引记录最新的版本号,通过索引实现锁操作; 可以很容易地构造出某行、某条索引所对应的 Key,或者是某一块相邻的行、相邻的索引值所对应的 Key 范围; 由于 Key 的格式非单调递增,可以避免存储引擎中的递增 Key 带来的热点写问题。 数据写入 每一个写操作都会由发号器分配一个唯一的写入 revision ,然后并发地对存储引擎进行写入。在 创建、更新和删除 Kubernetes 对象数据的时候,需要同时操作对象对应的索引和数据。由于索引和数据在底层存储引擎中是不同的 Key-Value 对,需要使用写事务保证更新过程的原子性,并且要求至少达到 Snapshot Isolation 。 同时 KubeBrain 依赖索引实现了乐观锁进行并发控制。KubeBrain 写入时,会先根据 APIServer 输入的 RawKey 以及被发号器分配的 Revision 构造出实际需要到存储引擎中的 Revision Key 和 Object Key,以及希望写入到 Revision Key 中的 Revision Bytes。在写事务过程中,先进行索引 Revision Key 的检查,检查成功后更新索引 Revision Key,在操作成功后进行数据 Object Key 的插入操作。 执行 Create 请求时,当 Revision Key 不存在时,才将 Revision Bytes 写入 Revision Key 中,随后将 API Server 写入的 Value 写到 Object Key 中; 执行 Update 请求时,当 Revision Key 中存放的旧 Revision Bytes 符合预期时,才将新 Revision Bytes 写入,随后将 API Server 写入的 Value 写到 Object Key 中; 执行 Delete 请求时,当 Revision Key 中存放的旧 Revision Bytes 符合预期时,才将新 Revision Bytes 附带上删除标记写入,随后将 tombstone 写到 Object Key 中。 由于写入数据时基于递增的 Revision 不断写入新的 KeyValue , KubeBrain 会进行后台的垃圾回收操作,将 Revision 过旧的数据进行删除,避免数据量无限增长。 数据读取 数据读取分成点读和范围查询查询操作,分别对应 API Server 的 Get 和 List 操作。 Get 需要指定读操作的ReadRevision,需要读最新值时则将 ReadRevision 置为最大值MaxUint64,构造 Iterator ,起始点为Encode(RawKey, ReadRevision),向Encode( RawKey, 0)遍历,取第一个。 范围查询需要指定读操作的ReadRevision。对于范围查找的 RawKey 边界[RawKeyStart, RawKeyEnd)区间, KubeBrain 构造存储引擎的 Iterator 快照读,通过编码将 RawKey 的区间映射到存储引擎中 InternalKey 的数据区间 InternalKey 上界InternalKeyStart为Encode(RawKeyStart, 0) InternalKey 的下界为InternalKeyEnd为Encode(RawKeyEnd, MaxRevision) 对于存储引擎中[InternalKeyStart, InternalKeyEnd)内的所有数据按序遍历,通过Decode(InternalKey)得到RawKey与Revision,对于一个RawKey相同的所有ObjectKey,在满足条件Revision<=ReadRevision的子集中取Revision最大的,对外返回。 事件机制 对于所有变更操作,会由 TSO 分配一个连续且唯一的 revision ,然后并发地写入存储引擎中。变更操作写入存储引擎之后,不论写入成功或失败,都会按照 revision 从小到大的顺序,将变更结果提交到滑动窗口中,变更结果包括变更的类型、版本、键、值、写入成功与否 。在记录变更结果的滑动窗口中,从起点到终点,所有变更数据中的 revision 严格递增,相邻 revision 差为 1。 记录变更结果的滑动窗口由事件生成器统一从起点开始消费,取出的变更结果后会根据变更的 revision 更新发号器的 commit index ,如果变更执行成功,则还会构造出对应的修改事件,将并行地写入事件缓存和分发到所有监听所创建出的通知队列。 在元数据存储系统中,需要监听指定逻辑时钟即指定 revision 之后发生的所有修改事件,用于下游的缓存更新等操作,从而保证分布式系统的数据最终一致性。注册监听时,需要传入起始 revision 和过滤参数,过滤参数包括 key 前缀等等。 当客户端发起监听时,服务端在建立事件流之后的处理,分成以下几个主要步骤: 处理监听注册请求时首先创建通知队列,将通知队列注册到事件生成组件中,获取下发的新增事件; 从事件缓存中拉取事件的 revision 大于等于给定要求 revision 所有事件到事件队列中,并放到输出队列中,以此获取历史事件; 将通知队列中的事件取出,添加到输出队列中, revision 去重之后添加到输出队列; 按照 revision 从小到大的顺序,依次使用过滤器进行过滤; 将过滤后符合客户端要求的事件,通过事件流推送到元数据存储系统外部的客户端。 落地效果 在 Benchmark 环境下,基于 ByteKV 的 KubeBrain 对比于 etcd 纯写场景吞吐提升 10 倍左右,延迟大幅度降低, PCT 50 降低至 1/6 ,PCT 90 降低至 1/20 ,PCT 99降低至 1/4 ;读写混合场景吞吐提升 4 倍左右;事件吞吐大约提升 5 倍; 在模拟 K8s Workload 的压测环境中,配合 APIServer 侧的优化和调优,支持 K8s 集群规模达到 5w Node 和 200w Pod; 在生产环境中,稳定上量至 2.1w Node ,高峰期写入超过 1.2w QPS,读写负载合计超过 1.8w QPS。 未来演进 项目未来的演进计划主要包括四个方面的工作: 探索实现多点写入的方案以支持水平扩展 现在 KubeBrain 本质上还是一个单主写入的系统,KubeBrain 后续会在水平扩展方面做进一步的探索,后续也会在社区中讨论; 提升切主的恢复速度 当前切主会触发 API Server 侧的 Re-list ,数据同步的开销较大,我们会在这方面进一步做优化; 实现内置存储引擎 实现两层存储融合,由于现在在存储引擎、KubeBrain 中存在两层 MVCC 设计,整体读写放大较多,实现融合有助于降低读写放大,更进一步提高性能; 完善周边组件 包括数据迁移工具、备份工具等等,帮助用户更好地使用 KubeBrain。 期待有更多朋友关注和加入 KubeBrain 社区,也欢迎大家在 GitHub 给我们提出各种建议!

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册