业务实战中经典算法的应用
有网友提问:各种机器学习算法的应用场景分别是什么(比如朴素贝叶斯、决策树、K 近邻、SVM、逻辑回归最大熵模型)?
这些在一般工作中分别用到的频率多大?一般用途是什么?需要注意什么?
根据问题,核心关键词是基础算法和应用场景,比较担忧的点是这些基础算法能否学有所用?毕竟,上述算法是大家一上来就接触的,书架上可能还放着几本充满情怀的《数据挖掘导论》《模式分类》等经典书籍,但又对在深度学习时代基础算法是否有立足之地抱有担忧。
网上已经有很多内容解答了经典算法的基本思路和理论上的应用场景,这些场景更像是模型的适用范围,这与工业界算法实际落地中的场景其实有很大区别。
从工业界的角度看,业务价值才是衡量算法优劣的金钥匙,而业务场景往往包含业务目标、约束条件和实现成本。如果我们只看目标,那么前沿的算法往往占据主导,可如果我们需兼顾算法运行复杂度、快速迭代试错、各种强加的业务限制等,经典算法往往更好用,因而就占据了一席之地。
针对这个问题,淘系技术算法工程师感知同学写出本文详细解答。
在实际业务价值中,算法模型的影响面大致是10%
在工业界,算法从想法到落地,你不是一个人在战斗。我以推荐算法为例,假设我们现在接到的任务是支持某频道页feeds流的推荐。我们首先应该意识到对业务来说,模型的影响面大致是10%,其他几个重要影响因子是产品设计(40%)、数据(30%)、领域知识的表示和建模(20%)。
这意味着,你把普通的LR模型升级成深度模型,即使提升20%,可能对业务的贡献大致只有2%。当然,2%也不少,只是这个折扣打的让人脑壳疼。
当然,上面的比例划分并不是一成不变的,在阿里推荐算法元年也就是2015年起,个性化推荐往往对标运营规则,你要是不提升个20%都不好意思跟人打招呼。那么一个算法工程师的日常除了接需求,就是做优化:业务给我输入日志、特征和优化目标,剩下的事情就交给我吭哧吭哧。
可随着一年年的水涨船高,大家所用模型自然也越来越复杂,从LR->FTRL->WDL->DeepFM->MMOE,大家也都沿着前辈们躺过的路径一步步走下去。这时候你要是问谁还在用LR或是普通的决策树,那确实会得到一个尴尬的笑容。
但渐渐的,大家也意识到了,模型优化终究是一个边际收益递减的事情。当我们把业务方屏蔽在外面而只在一个密闭空间中优化,天花板就已经注定。于是渐渐的,淘系的推荐慢慢进入第二阶段,算法和业务共建阶段。业务需求和算法优化虽然还是分开走,但已经开始有融合的地方。
集团对算法工程师的要求也在改变:一个高大上的深度模型,如果不能说清楚业务价值,或带来特别明显提升,那么只能认为是自嗨式的闭门造车。这时,一个优秀的算法工程师,需要熟悉业务,通过和业务反复交流中,能够弄清楚业务痛点。
注意,业务方甚至可能当局者迷,会提出既要又要还要的需求给你,而你需要真正聚焦到那个最值得做的问题上。然后,才是对问题的算法描述。做到这一步你会发现,并不是你来定哪个模型牛逼,而是跟着问题走来选择模型。这个模型的第一版极大可能是一个经典算法,因为,你要尽快跑通链路,快速验证你的这个idea是有效的。后面的模型迭代提升,只是时间问题。
经典算法在淘系的应用场景示例:TF-IDF、K近邻、朴素贝叶斯、逻辑回归等
因而现阶段,在淘系的大多数场景中,并不是算法来驱动业务,而是配合业务一起来完成增长。一个只懂技术的算法工程师,最多只能拿到那10%的满分。为了让大家有体感,这里再举几个小例子:
比如业务问题是对用户进行人群打标,人群包括钓鱼控、豆蔻少女、耳机发烧友、男神style等。在实操中我们不仅需考虑用户年龄、性别、购买力等属性,还需考虑用户在淘系的长期行为,从而得到一个多分类任务。如果模型所用的的特征是按月访问频次,那么豆蔻少女很可能网罗非常多的用户,因为女装是淘系行为频次最多的类目。
比如,某用户对耳机发烧友和豆蔻少女一个月内都有4次访问,假设耳机发烧友人均访问次数是3.2次,而豆蔻少女是4.8次,那么可知该用户对耳机发烧友的偏好分应更高。因此,模型特征不仅应该使用用户对该人群的绝对行为频次,还需参照大盘的水位给出相对行为频次。
这时,入选吴军老师《数学之美》的TF-IDF算法就派上用场了。通过引入TF-IDF构建特征,可以显著提高人群标签的模型效果,而TF-IDF则是非常基础的文本分类算法。
在淘系推荐场景,提升feeds流的点击率或转化率往往是一个常见场景。可业务总会给你惊喜:比如商品的库存只有一件(阿里拍卖),比如推荐的商品大多是新品(天猫新品),比如通过小样来吸引用户复购正品,这些用户大多是第一次来(天猫U先),或者是在提升效率的同时还需兼顾类目丰富度(很多场景)。
在上述不同业务约束背景,才是我们真实面对的应用场景。面对这种情况首先是定方向,比如在阿里拍卖中的问题可描述为“如何在浅库存约束下进行个性化推荐”。假如你判断这是一个流量调控问题,就需要列出优化目标和约束条件,并调研如何用拉格朗日乘子法求解。重要的是,最终的结果还需要和个性化推荐系统结合。详见:阿里拍卖全链路导购策略首次揭秘。
面对上述应用场景,你需明白你的战略目标是证明浅库存约束下的推荐是一个流量调控问题,并可以快速验证拿到效果。采用一个成熟经典的方法先快速落地实验,后续再逐步迭代是明智的选择。
又比如,K近邻算法似乎能简单到能用一张图或一句话来描述。可它在解决正负样本不均衡问题中就能派上用场。上采样是说通过将少数类(往往是正样本)的数据复制多份,但上采样后存在重复数据集可能会导致过拟合。过拟合的一种原因是在局部范围正负样本比例存在差异,如下图所示:
我们只需对C类局部样本进行上采样,这其中就运用到了K近邻算法。本例中的经典算法虽然只是链路的一部分,甚至只是配角儿,但离了它不行。
朴素贝叶斯虽然简单了点,但贝叶斯理论往后的发展,包括贝叶斯网络、因果图就不那么simple了。比如不论是金融业务中的LR评分卡模型,或是推荐算法精排中的深度模型,交叉特征往往由人工经验配置,这甚至是算法最不自动的一个环节。
使用贝叶斯网络中的structure learning,并结合业务输入的行业知识,构建出贝叶斯概率图,并从中发现相关特征做交叉,会比人工配置带来一定提升,也具有更好的可解释性。这就是把业务领域知识和算法模型结合的一个很好的例子。可如果贝叶斯理论不扎实,很难走到这一步。
不论深度学习怎么火,LR都是大多场景的一个backup。比如在双十一大促投放场景中,如果在0:00~0:30这样的峰值期,所有场景都走深度模型,机器资源肯定不足,这时候就需要做好一个使用LR或FTRL的降级预案。
如何成为业界优秀的算法工程师?
在淘系,算法并不是孤立的存在,算法工程师也不只是一个闭关的剑客。怎么切中业务痛点,快速验证你的idea,怎么进行合适的算法选型,这要求你有很好的算法基本功,以及较广的算法视野。一个模型无论搞的多复杂最终的回答都是业务价值,一个有良好基本功、具备快速学习能力,且善于发掘业务价值的算法工程师,将有很大成长空间。
好吧,假设上面就是我们的职业目标,可怎么实现呢。元(ye)芳(jie),你怎么看?其实只有一个句话,理论和实践相结合。
理论
对算法基本思路的了解,查查知乎你可能只需要20分钟,可你忘记它可能也只需要2周。这里的理论是你从内而外对这个算法的感悟,比如说到决策树,你脑海里好像就能模拟出它的信息增益计算、特征选择、节点分裂的过程,知道它的优和劣。最终,都是为了在实践中能快速识别出它就是最适合解决当前问题的模型。
基本功弄扎实是一个慢活儿,方法上可以是看经典原著、学习视频分享或是用高级语言实现。重要的是心态,能平心静气,不设预期,保持热情;另外,如果你有个可以分享的兴趣小组,那么恭喜你。因为学习书籍或看paper的过程其实挺枯燥的,但如果有分享的动力你可以走的更远。
实践
都知道实践出真知,可实践往往也很残酷。因为需求和约束多如牛毛,问题需要你来发现,留给你的时间窗口又很短暂。也就是,算法是一个偏确定性、有适用边界、标准化的事情;而业务则是发散的、多目标的、经验驱动的事情。
你首先是需要有双发现的眼睛,找到那个最值得发力的点,这个需要数据分析配合业务经验,剥丝抽茧留下最主要的优化目标、约束条件,尽量的简化问题;其次是有一张能说会道的嘴,要不然业务怎么愿意给你这个时间窗口让你尝试呢;最后是,在赌上你的人设之后,需要尽快在这个窗口期做出效果,在这个压力之下,考验你算法基本功的时候就到了。
结语
最后,让大家猜个谜语:它是一个贪心的家伙,计算复杂度不大,可以动态的做特征选择,不论特征是离散还是连续;它还可以先剪枝或后剪枝避免过拟合;它融合了信息熵的计算,也可以不讲道理的引入随机因子;它可以孤立的存在,也完全可以做集成学习,还可以配合LR一起来解决特征组合的问题;对小样本、正负样本不均、在线数据流的学习也可以支持;所得到则是有很强可解释性的规则。它,就是决策树。
每一个基础算法,都好比是颗带有智慧的种子,这不是普通的锦上添花,而是石破天惊的原创思维。有时不妨傻傻的回到过去的年代,伴着大师原创的步伐走一走,这种原创的智慧对未来长期的算法之路,大有裨益。神经网络从种下到开花,将近50年;贝叶斯也正在开花结果的路上。下一个,将会是谁呢?
作者:阿里巴巴淘系技术
来源:https://juejin.cn/post/7025964095530598436
背包问题_概述(动态规划)
写在前
问题描述
有N件物品和一个最多能被重量为W 的背包。一个物品只有两个属性:重量和价值。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
注意:0-1 背包问题无法使用贪心算法来求解,也就是说不能按照先添加性价比最高的物品来达到最优,这是因为这种方式可能造成背包空间的浪费,从而无法达到最优。
基本思路
这里有两个可变量体积和价值,我们定义dp[i][j]表示前i件物品体积不超过j能达到的最大价值
,设第 i 件物品体积为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论:
第 i 件物品没添加到背包,最大价值:
dp[i][j] = dp[i - 1][j]
。第 i 件物品添加到背包中:
dp[i][j] = dp[i - 1][j - w] + v
。
第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w] + v)
代码实现
// W 为背包总重量
// N 为物品数量
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
public int knapsack(int W, int N, int[] weights, int[] values) {
// dp[i][0]和dp[0][j]没有价值已经初始化0
int[][] dp = new int[N + 1][W + 1];
// 从dp[1][1]开始遍历填表
for (int i = 1; i <= N; ++i) {
// 第i件物品的重量和价值
int w = weights[i - 1], v = values[i - 1];
for (int j = 1; j <= W; ++j) {
if (j < w) {
// 超过当前状态能装下的重量j
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]);
}
}
}
return dp[N][W];
}
dp[i][j]
的值只与dp[i-1][0,...,j-1]
有关,所以我们可以采用动态规划常用的方法(滚动数组)对空间进行优化(即去掉dp的第一维)。因此,0-1 背包的状态转移方程为: dp[j] = max(dp[j], dp[j - w] + v)
特别注意:为了防止上一层循环的dp[0,...,j-1]
被覆盖,循环的时候 j 只能逆向遍历。优化空间复杂度:
ps:滚动数组:即让数组滚动起来,每次都使用固定的几个存储空间,来达到压缩,节省存储空间的作用。
public int knapsack(int W, int N, int[] weights, int[] values) {
int[] dp = new int[W + 1];
for (int i = 1; i <= N; i++) {
int w = weights[i - 1], v = values[i - 1];
for (int j = W; j >= 1; --j) {
if (j >= w) {
dp[j] = Math.max(dp[j], dp[j - w] + v);
}
}
}
return dp[W];
}
ps:01背包内循环理解:还原成二维的dp就很好理解,一维的dp是二维dp在空间上进行复用的结果。dp[i]=f(dp[i-num])
,等式的右边其实是二维dp上一行的数据,应该是只读的,在被读取前不应该被修改。如果正序的话,靠后的元素在读取前右边的dp有可能被修改了,倒序可以避免读取前被修改的问题。
作者:_code_x
来源:https://www.jianshu.com/p/b789ec845641
驳“低代码开发取代程序员”论 为什么专业开发者也需要低代码?
低代码又火了。
近几年,腾讯、阿里、百度等互联网大厂纷纷入局,国内外低代码平台融资动辄数千万甚至数亿,以及伴随着热度而来的巨大争议……无不说明“低代码”的火爆。
事实上,低代码并非新概念,它可以追溯到上世纪80年代的“第四代编程语言”。2014年,Forrester正式提出低代码的概念。低代码是一种软件开发技术,衍生于软件开发的高级语言,让使用者通过可视化的方式,以更少的编码,更快速地构建和交付应用软件,全方位降低软件的开发成本。与传统软件开发方式相比,低代码开发平台整合了软件开发和部署所需的 IDE(集成开发环境)、服务器和数据库管理工具,覆盖软件开发的全生命周期,我们可以将其理解为 Visual Studio + IIS + SQL Management Studio(.NET 技 术)或 Eclipse + Tomcat + MySQL Workbench(Java 技术)的组合。
编码更少、交付更快、成本更低,还覆盖软件开发全生命周期,怎么看低代码都可以说是不错的软件开发工具。那么,它又为什么引发争议,甚至被其主要用户群体之一——程序员所诟病呢?“低代码开发会取代程序员” 这一观点大行其是,它说得对吗?
为什么低代码引起专业开发者的反感?
技术浪潮引发巨大变革,也带来了无数“取代论”,比如机器翻译是否取代人类翻译、机器人记者是否取代人类记者,以及低代码开发是否取代程序员。
低代码虽然火爆,但程序员对此抱有不同的心态:
轻视:低代码技术的诸多优势只是炒作,该技术更适合初学者,解决不了复杂的技术问题;
恐惧:担心被低代码取代;
抵触:低代码开发平台能够覆盖所有需求吗;大量封装组件使得低代码开发平台更像一个黑盒子,可能导致难以debug、难以修改和迭代升级等技术问题;低代码开发平台配置有大量组件,简单的拖拉拽动作即可完成大量开发工作,程序员不再需要厉害的技术能力。
那么,上述理由真的站得住脚吗?我们一一来看。
低代码的门槛真的低吗?
低代码开发过程常被比作拼积木:像拼搭积木一样,以可视化的方式,通过拖拉拽组件快速开发出数据填报、流程审批等应用程序,满足企业里比较简单的办公需求。
但这并不意味着低代码开发平台只能做到这些。
Gartner在2020年9月发布的《企业级低代码开发平台的关键能力报告》(Critical Capabilities for Enterprise Low-Code Application Platforms)中,列举了低代码的11项关键能力。
图源:http://www.gartner.com/en/document…
这里我们着重来看其中三项关键能力。
数据建模和管理:该指标就是通常所讲的 “模型驱动” 。相比于表单驱动,模型驱动能够提供满足数据库设计范式的数据模型设计和管理能力。开发的应用复杂度越高,系统集成的要求越高,这个能力就越关键。
流程和业务逻辑:流程应用与业务逻辑开发能力和效率。这个能力有两层,第一层是指使用该低代码开发平台能否开发出复杂的工作流和业务处理逻辑;第二层是开发这些功能时的便利性和易用性程度有多高。
接口和集成:编程接口与系统集成能力。为了避免“数据孤岛”现象,企业级应用通常需要与其他系统进行集成,协同增效。此时,内置的集成能力和编程接口就变得至关重要。除非确认可预期的未来中,项目不涉及系统集成和扩展开发,开发者都应该关注这个能力。
这些关键能力表明低代码平台在建模与逻辑方面具备较强的能力,而接口和集成能力可使专业开发人员完成低代码无法实现的部分,通过低代码与专业代码开发的协作实现复杂应用的开发。 在涉及高价值或复杂的核心业务时,专业开发人员需要理解业务需求,厘清业务逻辑。从这个层面上看,低代码开发的门槛并不低。事实也是如此:海比研究在《2021 年中国低代码/无代码市场研究报告》中提到,截至 2020 年底,技术人员在低代码使用者中的比例超 75%,占主体地位。
低代码什么都能做吗?
程序员的工作围绕开发需求展开。在选择开发工具时,程序员通常考虑的首要问题是:这款工具能否覆盖所有需求?如果需求增加或变更,该工具是否支持相关操作?这些问题同样适用于低代码平台的选型。
在实际项目交付过程中,如果我们仅可以满足99%的需求,另外1%的需求满足不了,那么真实用户大概率是不会买单的。因此,在评估低代码产品的时候,我们一定要保证该平台可以支撑所有系统模块类型的开发,同时也要具备足够的扩展性,确保使用纯代码开发出的模块能够与低代码模块进行无缝集成,而这离不开编程接口。
以国内主流低代码开发平台活字格为例。该平台提供开箱即用的开发组件,同时为系统的各个分层均提供编程扩展能力,以满足企业级应用开发对扩展性的高要求。借助分层编程接口,开发者可以用纯代码的方式实现新增功能,无需受限于低代码开发平台的版本和现有功能。
活字格的编程扩展能力
当然,就具体应用领域而言,低代码开发平台也有其擅长和不擅长的地方。目前,低代码开发更多地被应用于2B企业应用开发,而对于用户量特大的头部互联网应用、对算法和复杂数据结构要求较高的应用,低代码平台则不太适合。
低代码开发不可控?
“低代码开发平台是个黑盒子,内部出问题无法排查和解决。开发过程中发现有问题怎么办?迭代升级难以实现怎么办?”很多程序员会有这种疑惑。
但我们需要注意的是,低代码开发平台本质上仍是软件开发工具,用户模型与软件开发周期支持是其关键能力之一。也就是说,成熟的低代码开发平台具备软件开发全生命周期所需的各项功能,从而大大简化开发者的技术栈,进一步提高开发效率。
具体而言,在面对频繁的需求变更、棘手的问题排查时,低代码开发平台引入了版本管理机制,从而更高效地进行代码审查、版本管理与协调,以及软件的迭代升级。至于debug,日志分析无疑是个好办法。例如,活字格把执行过程及细节以日志方式输出,方便程序员高效debug。
对程序员而言,低代码平台是限制还是助力?
“低代码”意味着更少的代码。代码都不怎么写了,程序员又该怎么成长,怎么获得职业成就感呢?
其实不然。
首先,开发 ≠ 写代码。低代码平台可以减少大量重复工作,提升开发效率,把专业开发人员从简单、重复的开发需求中解放出来,把精力投入到更有价值的事情上,比如精进技术、理清业务逻辑。
其次,低代码平台的组件化和拖拽式配置降低了开发门槛,新手程序员能够借助此类平台快速入门,加速升级打怪;有经验的程序员也有机会参与更多项目,甚至带团队,积累更多经验值,实现快速成长。
宁波聚轩就是一个例子。这家公司自2009年起就专注于智能制造、工业4.0、系统方案集成等领域的探索研究。在接触了低代码之后,项目负责人发现开发效率得到极大提升,采用传统方式需要一个月开发量的项目,现在需要半个月甚至更短的时间就可以完成。此外,其实践经验表明,低代码开发的学习成本较低,毕业新生经过一周学习,两周就可做项目,一个月就能熟练开发。
该公司在2021企业级低代码应用大赛中获得了应用创新奖,获奖作品是一套轴承行业数字化智造系统。这套系统主要集成了ERP、MES、WMS和设备机联网系统,覆盖了销售、采购、仓库、计划、生产、财务等全流程功能,且已经在生产现场投入使用。在开发过程中,宁波聚轩的开发团队利用低代码平台成功解决了定制化要求高、多终端需求等难题,及时完成项目交付。
结语
当迷雾散尽,低代码开发平台重新露出高效率开发工具的本色时,你会选择它吗?
作者:SegmentFault思否
来源:https://juejin.cn/post/7023579572096466974
JVM整体结构
Java虚拟机主要负责自动内存管理、类加载与执行、主要包括执行引擎、垃圾回收器、PC寄存器、方法区、堆区、直接内存、Java虚拟机栈、本地方法栈、及类加载子系统几个部分,其中方法区与Java堆区由所有线程共享、Java虚拟机栈、本地方法栈、PC寄存器线程私有,宏观的结构如下图所示:
JVM结构图
类加载子系统
从文件或网络中加载Class信息,类信息存放于方法区,类的加载包括加载->验证->准备->解析->初始化->使用->卸载几个阶段,详细流程后续文章会介绍。
加载
从文件或网络中读取类的二进制数据、将字节流表示的静态存储结构转换为方法区运行时数据结构、并于堆中生成Java对象实例,类加载器既可以使用系统提供的加载器(默认),也可以自定义类加载器。
连接
连接分为验证、准备、解析3个阶段,验证阶段确保类加载的正确性、准备阶段为类的静态变量分配内存,并将其初始化为默认值、解析阶段将类中的符号引用转换为直接引用
初始化
初始化阶段负责类的初始化,Java中类变量初始化的方式有2种,声明类变量时指定初始值、静态代码块指定初始化,只有类被主动使用时才会触发类的初始化,类的初始化会先初始化父类,然后再初始化子类。
使用
类访问方法区内的数据结构的接口,对象是堆区的数据
卸载
程序执行了System.exit()、程序正常执行结束、JVM进程异常终止等
运行时数据区域
程序从静态的源代码到编译成为JVM执行引擎可执行的字节码,会经历类的加载过程,并于内存空间开辟逻辑上的运行时数据区域,便于JVM管理,其中各数据区域如下,其中垃圾回收JVM会自动自行管理
栈帧数据结构
Java中方法的执行在虚拟机栈中执行,为每个线程所私有,每次方法的调用和返回对应栈帧的压栈和出栈,其中栈帧中保存着局部变量表、方法返回地址、操作数栈及动态链接信息。
动态链接
Java中方法执行过程中,栈帧保存方法的符号引用,通过动态链接,将解析为符号引用。
内存管理
内存划分(逻辑上)
Java内存区域从逻辑上分为堆区和非堆区,Java8以前,非堆区又称为永久区,Java8以后统一为原数据区,堆区按照分代模型分为新生代和老年代,其中新生代分为Eden、so、s1,so和s1是大小相同的2块区域,生产环境可以根据具体的场景调整虚拟机内存分配比例参数,达到性能调优的效果。
堆区是JVM管理的最大内存区域,由所有线程共享,采用分代模型,堆区主要用于存放对象实例,堆可以是物理上不连续的空间,逻辑上连续即可,其中堆内存大小可以通过虚拟机参数-Xmx、-Xms指定,当堆无法继续扩展时,将抛出OOM异常。
运行时实例
假设存在如下的SimpleHeap测试类,则SimpleHeap在内存中的堆区、Java栈、方法区对应的映射关系如下图所示:
public class SimpleHeap {
/**
* 实例变量
*/
private int id;
/**
* 构造器
*
* @param id
*/
public SimpleHeap(int id) {
this.id = id;
}
/**
* 实例方法
*/
private void displayId() {
System.out.println("id:" + id);
}
public static void main(String[]args){
SimpleHeap heap1=new SimpleHeap(1);
SimpleHeap heap2=new SimpleHeap(2);
heap1.displayId();
heap2.displayId();
}
}
复制代码
同理,建设存在Person类,则创建对象实例后,内存中堆区、Java方法栈、方法区三者关系如下图:
直接内存
Java NIO库允许Java程序使用直接内存,直接内存是独立于Java堆区的一块内存区域,访问内存的速度优于堆区,出于性能考虑,针对读写频繁的场景,可以直接操作直接内存,它的大小不受Xmx参数的限制,但堆区内存和直接内存总和必须小于系统内存。
PC寄存器
线程私有空间,又称之为程序计数器,任意时刻,Java线程总在执行一个方法,正在执行的方法,我们称之为:"当前方法”,如果当前方法不是本地方法,则PC寄存器指向当前正在被执行的指令;若当前方法是本地方法,则PC寄存器的值是undefined。
垃圾回收系统
垃圾回收系统是Java虚拟机中的重要组成部分,其主要对方法区、堆区、直接内存空间进行回收,与C/C++不同,Java中所有对象的空间回收是隐式进行的,其中垃圾回收会根据GC算法自动完成内存管理。
作者:洞幺幺洞
来源:https://juejin.cn/post/7040742081236566029
Apache Log4j 漏洞(JNDI注入 CVE-2021-44228)
本周最热的事件莫过于 Log4j 漏洞,攻击者仅需向目标输入一段代码,不需要用户执行任何多余操作即可触发该漏洞,使攻击者可以远程控制用户受害者服务器,90% 以上基于 java 开发的应用平台都会受到影响。通过本文特推项目 2 你也能近距离感受这个漏洞的“魅力”,而特推 1 则是一个漏洞检测工具,能预防类似漏洞的发生。
除了安全相关的 2 个特推项目之外,本周 GitHub 热门项目还有高性能的 Rust 运行时项目,在你不知道用何词时给你参考词的反向词典 WantWords,还有可玩性贼高的终端模拟器 Tabby。
以下内容摘录自微博@HelloGitHub 的 GitHub Trending 及 Hacker News 热帖(简称 HN 热帖),选项标准:新发布
| 实用
| 有趣
,根据项目 release 时间分类,发布时间不超过 14 day 的项目会标注 New
,无该标志则说明项目 release 超过半月。由于本文篇幅有限,还有部分项目未能在本文展示,望周知 🌝
本文目录
本周特推
1.1 JNDI 注入测试工具:JNDI-Injection-Exploit
1.2 Apache Log4j 远程代码执行:CVE-2021-44228-Apache-Log4j-Rce
GitHub Trending 周榜
2.1 塞尔达传说·时之笛反编译:oot
2.2 终端模拟器:Tabby
2.3 反向词典:WantWords
2.4 CPU 性能分析和调优:perf-book
2.5 高性能 Rust Runtime:Monoio
往期回顾
1. 本周特推
1.1 JNDI 注入测试工具:JNDI-Injection-Exploit
本周 star 增长数: 200+
JNDI-Injection-Exploit 并非是一个新项目,它是一个可用于 Fastjson、Jackson 等相关漏洞的验证的工具,作为 JNDI 注入利用工具,它能生成 JNDI 链接并启动后端相关服务进而检测系统。
GitHub 地址→github.com/welk1n/JNDI…
1.2 Apache Log4j 远程代码执行:CVE-2021-44228-Apache-Log4j-Rce
本周 star 增长数: 1,500+
New
CVE-2021-44228-Apache-Log4j-Rce 是 Apache Log4j 远程代码执行,受影响的版本 < 2.15.0。项目开源 1 天便标星 1.5k+ 可见本次 Log4j 漏洞受关注程度。
GitHub 地址→github.com/tangxiaofen…
2. GitHub Trending 周榜
2.1 塞尔达传说·时之笛反编译:oot
本周 star 增长数:900+
oot 是一个反编译游戏塞尔达传说·时之笛的项目,目前项目处于半成品状态,会有较大的代码变更,项目从 scratch 中重新构建代码,并用游戏中发现的信息以及静态、动态分析。如果你想通过这个项目了解反编译知识,建议先保存好个人的塞尔代资产。
GitHub 地址→github.com/zeldaret/oo…
2.2 终端模拟器:Tabby
本周 star 增长数:1,800+
Tabby(曾叫 Terminus)是一个可配置、自定义程度高的终端模拟器、SSH 和串行客户端,适用于 Windows、macOS 和 Linux。它是一种替代 Windows 标准终端 conhost、PowerShell ISE、PuTTY、macOS terminal 的存在,但它不是新的 shell 也不是 MinGW 和 Cygwin 的替代品。此外,它并非一个轻量级工具,如果你注重内存,可以考虑 Conemu 或 Alacritty。
GitHub 地址→github.com/Eugeny/tabb…
2.3 反向词典:WantWords
本周 star 增长数:1,000+
WantWords 是清华大学计算机系自然语言处理实验室(THUNLP)开源的反向字词查询工具,反向词典并非是查询反义词的词典,而是基于目前网络词官广泛使用导致部分场景下,我们表达某个意思未能找到精准的用词,所以它可以让你通过想要表达的意思来找寻符合语境的词汇。你可以在线体验反向词典:wantwords.thunlp.org/ 。下图分别为项目 workflow 以及查询结果。
GitHub 地址→github.com/thunlp/Want…
2.4 CPU 性能分析和调优:perf-book
本周 star 增长数:1,300+
perf-book 是书籍《现代 CPU 的性能分析和调优》开源版本,你可以通过 python.exe export_book.py && pdflatex book.tex && bibtex book && pdflatex book.tex && pdflatex book.tex
命令导出 pdf。
GitHub 地址→github.com/dendibakh/p…
2.5 高性能 Rust Runtime:Monoio
本周 star 增长数:1,250+
New
Monoio 是字节开源基于 io-uring 的 thread-per-core 模型高性能 Rust Runtime,旨在为高性能网络中间件等场景提供必要的运行时。详细项目背景可以阅读团队的文章Monoio:基于 io-uring 的高性能 Rust Runtime。
GitHub 地址→github.com/bytedance/m…
3. 往期回顾
以上为 2021 年第 50 个工作周的 GitHub Trending 🎉如果你 Pick 其他好玩、实用的 GitHub 项目,记得来 HelloGitHub issue 区和我们分享下哟 🌝
最后,记得你在本文留言区留下你想看的主题 Repo(限公众号),例如:AI 换头。👀 和之前的送书活动类似,留言点赞 Top5 的小伙伴(棒),小鱼干会努力去找 Repo 的^^
作者:HelloGitHub
来源:https://juejin.cn/post/7040980646423953439
Redis分布式锁
需求
分布式应⽤进⾏逻辑处理时经常会遇到并发问题。
互斥访问某个网络上的资源,需要有一个存在于网络上的锁服务器,负责锁的申请与回收。Redis 可以充当锁服务器的角色。首先,Redis 是单进程单线程的工作模式,所有前来申请锁资源的请求都被排队处理,能保证锁资源的同步访问。
适用原因:
Redis 可以被多个客户端共享访问,是·共享存储系统,可以用来保存分布 式锁
Redis 的读写性能高,可以应对高并发的锁操作场景。
实现
在分布式场景下,锁变量需要由一个共享存储系统来维护,这样,多个客户端可以通过访问共享存储系统来访问锁变量。
简单实现
模仿单机上的锁,使用锁变量即可在Redis上实现分布式锁。
我们可以在 Redis 服务器设置一个键值对,用以表示一把互斥锁,当申请锁的时候,要求申请方设置(SET)这个键值对,当释放锁的时候,要求释放方删除(DEL)这个键值对。
但最基本需要保证加锁解锁操作的原子性。同时为了保证锁在异常情况下能被释放,必须设置超时时间。
Redis 2.8 版本中作者加⼊了 set 指令的扩展参数,使得 setnx 和 expire 指令可以⼀起执⾏
加锁原子操作
加锁包含了三个操作(读取锁变量、判断锁变量值以及把锁变量值设置为 1),而这 三个操作在执行时需要保证原子性。
首先是 SETNX 命令,它用于设置键值对的值。具体来说,就是这个命令在执行时会判断键 值对是否存在,如果不存在,就设置键值对的值,如果存在,就不做任何设置。
超时问题
Redis 的分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻 辑执⾏的太长,超出了锁的超时限制,就无法保证互斥。
解决方案
最简单的就是避免Redis 分布式锁⽤于较⻓时间的任务。如果真的偶尔出现了,数据出现的⼩波错乱可能需要⼈⼯介⼊解决。
判断拥有者
为了防止锁变量被拥有者之外的客户端进程删除,需要能区分来自不同客户端的锁操作
set 指令的 value 参数设置为⼀个 随机数,释放锁时先匹配随机数是否⼀致,然后再删除 key,这是为 了确保当前线程占有的锁不会被其它线程释放,除⾮这个锁是过期了被服务器⾃动释放的。
Redis 给 SET 命令提供 了类似的选项 NX,用来实现“不存在即设置”。如果使用了 NX 选项,SET 命令只有在键 值对不存在时,才会进行设置,否则不做赋值操作。此外,SET 命令在执行时还可以带上 EX 或 PX 选项,用来设置键值对的过期时间。
可重入问题
可重⼊性是指线程在持有锁的情况下再次请求加锁,如果⼀个锁⽀持 同⼀个线程的多次加锁,那么这个锁就是可重⼊的。Redis 分布式锁如果要⽀持 可重⼊,需要对客户端的 set ⽅法进⾏包装,使⽤线程的 Threadlocal 变量存储当前持有锁的计数。
分布式拓展
单Redis实例并不能满足我们的高可用要求,一旦实例崩溃,就无法对外分布式锁服务。
但在集群环境下,这种只对主Redis实例使用上述方案是有缺陷 的,它不是绝对安全的。
一旦主节点挂掉,但锁变量没有及时同步,就会导致互斥被破坏。
Redlock
为了解决这个问题,Antirez 发明了 Redlock 算法
Redlock 算法的基本思路,是让客户端和多个独立的 Redis 实例依次请求加锁,如果客户 端能够和半数以上的实例成功地完成加锁操作,那么我们就认为,客户端成功地获得分布 式锁了,否则加锁失败。
执行步骤:
获取当前时间
客户端按顺序依次向 N 个 Redis 实例执行加锁操作
一旦客户端完成了和所有 Redis 实例的加锁操作,客户端就要计算整个加锁过 程的总耗时。客户端只有在满足下面的这两个条件时,才能认为是加锁成功
客户端从超过半数(大于等于 N/2+1)的 Redis 实例上成功获取到了锁;
客户端获取锁的总耗时没有超过锁的有效时间。
作者:不是二向箔
来源:https://juejin.cn/post/7038155529566289957
短信跳小程序
方案:使用微信提供的url link方法 生成短链接 发送给用户 用户点击短链接会跳转到微信提供默认的默认页面 进而打开小程序
场景假设:经理人发布一条运输任务 司机收到短信点击打开小程序接单
实现:
经理人发布时点击发布按钮 h5调用服务端接口 传参服务端要跳转的小程序页面 所需要参数
服务端拿access_token和前端传的参数 加链接失效时间 调用微信api
api.weixin.qq.com/wxa/generat… 得到链接 如wxaurl.cn/ow7ctZP4n8v 将此链接发送短信给司机
司机点击此链接 效果如下图所示:打开小程序 h5写逻辑跳转指定页面
自己调postman调微信api post方式 接口: api.weixin.qq.com/wxa/generat…
传参
{
"path": "pages/index/index",\
"query": "?fromType=4&transportBulkLineId=111&isLinkUrlCome=1&SCANFROMTYPE=143&lineAssignRelId=111",\
"env_version": "trial",\
"is_expire": true,\
"expire_time": "1638855772"\
}
返参
{
"errcode": 0,
"errmsg": "ok",
"url_link": "https://wxaurl.cn/GAxGcil2Bbp"
}
url link说明文档: developers.weixin.qq.com/miniprogram…
url link方法需要服务端调用 调用接口方式参考: developers.weixin.qq.com/miniprogram…
作者:懿小诺
来源:https://juejin.cn/post/7037356611031007239
前端自动化部署:借助Gitlab CI/CD实现
🛫 前端自动化部署:借助Gitlab CI/CD实现
🌏 概论
传统的前端部署往往都要经历:本地代码更新
=> 本地打包项目
=> 清空服务器相应目录
=> 上传项目包至相应目录
几个阶段,这些都是机械重复的步骤。对于这一过程我们往往可以通过CI/CD
方法进行优化。所谓CI/CD
,即持续集成/持续部署,以上我们所说的步骤便可以看作是持续部署的一种形态,其更详细的解释大家可以自行了解。
Jenkins
、Travis CI
这些都是可以完成持续部署的工具。除此之外,Gitlab CI/CD
也能很好的完成这一需求。下面就来详细介绍下。
🌏 核心工具
GitLab Runner
GitLab Runner
是配合GitLab CI/CD
完成工作的核心程序,出于性能考虑,GitLab Runner
应该与Gitlab
部署在不同的服务器上(Gitlab
在单独的仓库服务器上,GitLab Runner
在部署web应用的服务器上)。GitLab Runner
在与GitLab
关联后,可以在服务器上完成诸如项目拉取、文件打包、资源复制等各种命令操作。
Git
web服务器上需要安装Git
来进行远程仓库的获取工作。
Node
用于在web服务器上完成打包工作。
NPM or Yarn or pnpm
用于在web服务器上完成依赖下载等工作(用yarn
,pnpm
亦可)。
🌏 流程
这里我自己用的是centOS环境:
1. 在web服务器上安装所需工具
(1)安装Node
# 下载node包
wget https://nodejs.org/dist/v16.13.0/node-v16.13.0-linux-x64.tar.xz
# 解压Node包
tar -xf node-v16.13.0-linux-x64.tar.xz
# 在配置文件(位置多在/etc/profile)末尾添加:
export PATH=$PATH:/root/node-v16.13.0-linux-x64/bin
# 刷新shell环境:
source /etc/profile
# 查看版本(输出版本号则安装成功):
node -v
#后续安装操作,都可通过-v或者--version来查看是否成功
npm
已内置在node
中,如要使用yarn
或,则可通过npm
进行全局安装,命令与我们本地环境下的使用命令是一样的:
npm i yarn -g
#or
npm i pnpm -g
(2)安装Git
# 利用yum安装git
yum -y install git
# 查看git版本
git --version
(3)安装Gitlab Runner
# 安装程序
wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
# 等待下载完成后分配权限
chmod +x /usr/local/bin/gitlab-runner
# 创建runner用户
useradd --comment 'test' --create-home gitlab-runner --shell /bin/bash
# 安装程序
gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
# 启动程序
gitlab-runner start
# 安装完成后可使用gitlab-runner --version查看是否成功
2. 配置Runner及CI/CD
基本的安装操作完成后,就是最核心的阶段:Runner与CI/CD的配置。
(1)配置Gitlab Runner
首先打开待添加自动部署功能的gitlab仓库,在其中设置 > CI/CD > Runner
中找到runner配置信息备用:
在web服务器中配置runner:
gitlab-runner register
>> Enter the GitLab instance URL (for example, https://gitlab.com/):
# 输入刚才获取到的gitlab仓库地址
>> Enter the registration token:
# 输入刚才获取到的token
>> Enter a description for the runner:
# 自定runner描述
>> Enter tags for the runner (comma-separated):
# 自定runner标签
>> Enter an executor: docker-ssh, docker+machine, docker-ssh+machine, docker, parallels, shell, ssh, virtualbox, kubernetes, custom:
# 选择执行器,此处我们输入shell
完整示例:
(2)配置.gitlab-ci.yml
.gitlab-ci.yml
文件是流水线执行的流程文件,Runner会据此完成规定的一系列流程。
我们在项目根目录中创建.gitlab-ci.yml
文件,然后在其中编写内容:
# 阶段
stages:
- install
- build
- deploy
cache:
paths:
- node_modules/
# 安装依赖
install:
stage: install
# 此处的tags必须填入之前注册时自定的tag
tags:
- deploy
# 规定仅在package.json提交时才触发此阶段
only:
changes:
- package.json
# 执行脚本
script:
yarn
# 打包项目
build:
stage: build
tags:
- deploy
script:
- yarn build
# 将此阶段产物传递至下一阶段
artifacts:
paths:
- dist/
# 部署项目
deploy:
stage: deploy
tags:
- deploy
script:
# 清空网站根目录,目录请根据服务器实际情况填写
- rm -rf /www/wwwroot/stjerne/salary/*
# 复制打包后的文件至网站根目录,目录请根据服务器实际情况填写
- cp -rf ${CI_PROJECT_DIR}/dist/* /www/wwwroot/stjerne/salary/
保存并推送至gitlab后即可自动开始构建部署。
构建中可在gitlab CI/CD面板查看构建进程:
待流水线JOB完成后可前往页面查看🛫🛫🛫🛫🛫
作者:星始流年
来源:https://juejin.cn/post/7037022688493338661
如何实现跨设备的双向连接? Labo涂鸦鸿蒙亲子版分布式开发技术分享
近期,首届HarmonyOS开发者创新大赛正式落下帷幕。大赛共历时5个月,超过3000支队伍的10000多名选手参赛,25000多位开发者参与了大赛学习,最终23支参赛队伍斩获奖项,产出了多款有创新、有创意、有价值的优秀作品。其中由“Labo Lado儿童艺术创想”团队打造的《Labo涂鸦鸿蒙亲子版》就是其中之一,其创造性地通过HarmonyOS分布式技术,实现了多设备下的亲子互动涂鸦功能,最终摘得大赛一等奖。
在很早以前,“Labo Lado儿童艺术创想”团队就做过一款涂鸦游戏的应用,该应用可以让孩子和父母在一个平板或者手机上进行绘画比赛,比赛的方式就是屏幕一分为二,两人各在设备的一边进行涂鸦。这种方式虽然有趣,但是对于绘画而言,屏幕尺寸限制了用户的发挥和操作。因此团队希望这类玩法能通过多个设备完成,于是他们研究了ZeroConf、iOS的Multipeer Connectivity、Google Nearby等近距离互联的技术, 结果发现这些技术在设备发现和应用拉起方面实现的都不理想,尤其是当目标用户是儿童的情况下,操作起来不够简便也不易上手。
HarmonyOS的出现给团队带来了希望。他们发现HarmonyOS的分布式技术有着很大的应用潜力,这项技术让设备的发现和应用拉起变的非常的简单自然,互联的过程也很流畅,很好地解决了单机操作的限制,让跨设备联机功能能够非常容易地实现。此外,HarmonyOS的开发也给团队留下了很深刻的印象,以往繁琐的开发步骤,在 HarmonyOS 中仅需几个配置、几行代码即可完成,无需花费太多精力。在《Labo涂鸦鸿蒙亲子版》里面的5个分布式玩法的开发只用了团队一名开发者不到两个月的时间,其中还包括了学习上手、解决文档不全和各种疑难问题的过程。
以下是“Labo Lado儿童艺术创想”团队基于HarmonyOS的分布式开发关键技术的简单分享:
一、分布式技术实践
HarmonyOS的分布式能力是在系统层面实现的,在设备双方同属一个局域网的情况下,设备都可以快速的发现和进行流畅的通讯。下面将从HarmonyOS设备的发现、应用的拉起、应用通讯和双向通讯几个部分来进行分享。
1、设备的发现
假设设备A想要邀请另外一个设备B加入,AB任何一方都无需启动特别的广播服务,只要发起方设备A在应用内调用设备发现代码,就可以列出附近符合条件可用的的设备。
以下是获取设备列表的示例代码:
public static List<DeviceInfo> getRemoteDevice() {
List<DeviceInfo> deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
return deviceInfoList;
}
列出设备之后,用户就可以通过设备名选择想要邀请的设备了。
(左侧设备A发现右侧名为“ye”的设备B的界面展示)
2、应用的拉起
设备A邀请了设备B之后,如果设备B上应用没启动,设备A可直接通过调用startAbility方法来拉起设备B上的应用。双方应用都启动了之后,就可以进行RPC通讯了。如果需要事先检查设备B上的应用是否已经启动或者是否在后台,可通过在应用中增加一个PA来实现。在拉起之前,设备A先连接设备B的应用中的PA可以实现更复杂精准的远程应用启动控制。
3、应用通讯
在应用中启动一个PA,专门用作通讯的服务器端。当设备B的应用被拉起之后,设备A就会通过connectAbility与设备B的PA进行连接,通讯采用RPC方式实现,并使用IDL定义通讯接口。
4、双向通讯
RPC的通讯方式使用简单,但是只能支持单向通讯。为了实现双向通讯,可在设备A与设备B发起建立连接成功之后,再让设备B与设备A发起建立一个连接,用两个连接实现了双向通讯。下面是这两个连接建立过程的示意时序图:
在设备A与设备B建立连接的时候,设备A必须将自己的DeviceId发送给设备B,然后设备B才可以主动发起一个与设备A的连接,获取当前设备的DeviceId方法如下:
KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId()
应用中,FA主要实现了界面层逻辑,PA部分用做数据通讯的服务端。为了防止拉起应用导致用户当前面的操作被中断,可通过PA来查询当前FA的状态,如果FA已经启动了,就跳过拉起,直接进行下一步操作即可。
二、数据接口与数据结构定义
使用了IDL定义了两个通用的接口,分别用来进行异步和同步调用:
int sendSyncCommand([in] int command, [in] String params);
void sendAsyncCommand([in] int command, [in] String params, [in] byte[] content);
大部分情况下,远程调用大部分都通过同步的方式进行,用户之间的绘画数据通过异步接口传输,数据在用户绘制的时候采集,每50ms左右发送一次,这个频率可以大概保证用户视觉上没有卡顿,而又不至于因为接口过度调用导致卡顿或者耗电量过大。采集的绘画数据的数据结构大致如下:
enum action //动作,表示落笔、移动、提笔等动作
int tagId //多点触摸识别标记
int x //x坐标
int y //y坐标
enum brushType //笔刷类型
int brushSize //笔刷大小
enum brushColor //笔刷颜色
int layer //图层
这款应用是支持多点触摸的,所以每个触摸点在落笔的的时候,都使用了tagId进行标记。这些数据除了通讯外,还会完整地保存在文件中,这样用户就可以通过应用内的播放功能播放该数据,回看绘画的整个过程。
三、教程录制与曲线平滑
1、教程制作
这款产品的特色之一是教程是动态的,用户可以自己拼装或者通过游戏生成教程角色。目前应用内置六种教程。这些教程预先由设计师在photoshop中画好并标记各个部位,然后再通过专门的photoshop脚本导出到教程录制应用中,再由设计师按部位逐个进行临摹绘制,绘制完成,应用会将设计师的绘制过程数据保存为json文件,通过将这些json的文件里的部位互换,我们就实现了用户自己拼装教程的功能了。
2、曲线平滑
绘制过程,为了让用户绘制的曲线更加平滑,采用二次贝塞尔曲线算法进行差值(Quadratic Bezier Curve),该算法简单效率也非常不错:
public Point quadraticBezier(Point p0, Point p1, Point p2, float t) {
Point pFinal = new Point();
pFinal.x = (float) (Math.pow(1 - t, 2) * p0.x + (1 - t) * 2 * t * p1.x + t * t * p2.x);
pFinal.y = (float) (Math.pow(1 - t, 2) * p0.y + (1 - t) * 2 * t * p1.y + t * t * p2.y);
return pFinal;
}
基于HarmonyOS的分布式特性,《Labo涂鸦鸿蒙亲子版》完成了一次已有应用的自我尝试和突破,大大的增加了用户在使用过程中的乐趣,为用户带来了全新的跨设备亲子交互体验,“Labo Lado儿童艺术创想”团队在未来将与更多的HarmonyOS开发者一起,为用户创作出更多更有趣的儿童创造类应用。
近一段时间以来,HarmonyOS 2的发布吸引了广大开发者的关注。作为一款面向万物互联时代的智能终端操作系统,HarmonyOS 2带来了诸多新特性、新功能和新玩法,等待开发者去探索、去学习、去实践。也欢迎广大开发者继续发挥创造力和想象力,基于HarmonyOS开发出更多有创新、有创意、有价值的作品,打造出专属于万物互联时代的创新产品。
收起阅读 »Uni-app:(不通用)HbuilderX启动别人uni-app项目运行到小程序,提示打开了小程序开发者工具但是不进页面
不是什么大问题,但是也很耽误时间,Hello world,连Hello都Hello不出来,最后研究了半天才解决了这个问题下面是解决方法:
除了查看对应版本和开启端口外还需注意,因为微信开发者工具是和对应的微信账号绑定的,所以需要设置对应账号的AppID才行,如果使用的是别人的需要用自己的AppID,但是启动别人的项目只用在,mainifest.json下的微信小程序配置,找到微信小程序APPID 删除掉然后再重新启动运行就可以了。
由于大小限制上传的图片是个压缩包,大家不清楚位置的可以下载下来瞜一眼。
收起阅读 »