文章列表

上云不停服,自顶向下的平滑机房迁移方案!!!

作者:微信小助手

<section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651963156&amp;idx=1&amp;sn=472d881f6ea2f0cd4f2b63160a139cb1&amp;chksm=bd2d0ac88a5a83decc49a7d5a33faa112969d8ee0afb243acacf82f7628158c9fafde7691bd7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0" tab="innerlink">当年,我们是怎么平滑上云的?</a>》介绍了上云的背景,以及三个重要结论:</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(1)单机房架构的核心是“</span> <span style="box-sizing: border-box;color: rgb(255, 76, 0);font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">全连接</span> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">”;</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(2)机房迁移方案的设计目标是:</span> <span style="box-sizing: border-box;color: rgb(255, 76, 0);font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">平滑迁移,不停服务</span> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">;可以</span> <span style="box-sizing: border-box;color: rgb(255, 76, 0);font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">分批迁移</span> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">;随时可以回滚;</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(3)想要平滑的实施机房迁移,</span> <span style="box-sizing: border-box;color: rgb(255, 76, 0);font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">临时性的多机房架构不可避免</span> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">;</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="letter-spacing: 1px;font-size: 15px;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651963162&amp;idx=1&amp;sn=07f3f7d86f28afe8782c4d9dda16121a&amp;chksm=bd2d0ac68a5a83d078e596355160883ea19911f912af1878645f0fed642bb5f706b0943b947d&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0" tab="innerlink">多机房多活架构,究竟怎么玩?</a>》说明了在机房迁移的过程中,一定有一个“多机房多活”的中间状态:</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(1)<strong>理想多机房多活<span style="background-color: rgb(255, 255, 255);box-sizing: border-box;color: rgb(51, 51, 51);display: inline;font-style: normal;font-variant: normal;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;text-align: justify;text-decoration: none;text-indent: 0px;text-transform: none;word-spacing: 0px;">架构</span></strong><span style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;font-style: normal;font-variant: normal;text-decoration: none;word-spacing: 0px;display: inline;max-width: 100%;">,</span></span> <span style="margin: 0px;padding: 0px;text-align: justify;color: rgb(255, 76, 0);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-size: 15px;font-style: normal;font-variant: normal;text-decoration: none;word-spacing: 0px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;background-color: rgb(255, 255, 255);">是纯粹的“同机房连接”</span> <span style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-size: 15px;font-style: normal;font-variant: normal;text-decoration: none;word-spacing: 0px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;">,仅有异步数据同步会跨机房;</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(2)理想多机房多活架构,会有较严重数据一致性问题,</span> <span style="margin: 0px;padding: 0px;color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;">仅适用于具备数据聚集效应的业务场景</span> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">,例如:滴滴,快狗打车;</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">(3)<strong><span style="box-sizing: border-box;color: rgb(51, 51, 51);display: inline;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">伪多机房多活架构</span></strong><span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);display: inline;max-width: 100%;">,思路是“最小化跨机房连接”,机房区分主次,落地性强,</span></span> <span style="margin: 0px;padding: 0px;color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;">对原有架构冲击较小,强烈推荐</span> <span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;">;</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;"><br></span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;">多机房多活,只是平滑上云的一个中间状态,那上云的步骤究竟是怎么样的呢?</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;"><br></span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-family: -apple-system-font,BlinkMacSystemFont,&quot;Helvetica Neue&quot;,&quot;PingFang SC&quot;,&quot;Hiragino Sans GB&quot;,&quot;Microsoft YaHei UI&quot;,&quot;Microsoft YaHei&quot;,Arial,sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 700;text-decoration: none;word-spacing: 0px;display: inline !important;max-width: 100%;box-sizing: border-box;orphans: 2;float: none;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;background-color: rgb(255, 255, 255);">【5】核心问题五,如何分批平滑上云?</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="letter-spacing: 1px;font-size: 15px;"><img style="box-sizing: border-box;color: rgb(51, 51, 51);" data-ratio="0.47959183673469385" data-type="png" data-w="588" src="/upload/539f60dab8b2933e99fa833863451625.png" data-s="300,640"></span> <br> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">如上图,系统分层架构包含:</span> <span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">web,业务服务,基础服务,缓存,数据库</span> <span style="letter-spacing: 1px;font-size: 15px;">,它们都需要进行迁移。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">大的方向,有两种方案:</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(1)<strong>自底向上</strong>的迁移方案,从数据库开始迁移;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(2)<strong>自顶向下</strong>的迁移方案,从web开始迁移;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">这两种方案我分别在58同城和58到家实践过,都是平滑的,蚂蚁搬家式的,随时可回滚,对业务无任何影响的,本文重点介绍“<strong>自顶向下</strong>”的方案。</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:14-15年58同城“逐日”项目,2000台物理机平滑迁移至天津机房,我是当时项目总架构师。</span></em></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 18px;"><strong><span style="letter-spacing: 1px;font-size: 18px;">一、站点与服务迁移:无状态,迁移容易</span></strong></span> </section> <section style="line-height: 1.75em;"> <span style="display: inline !important;float: none;background-color: rgb(255, 255, 255);color: rgb(51, 51, 51);font-family: &quot;mp-quote&quot;,-apple-system-font,BlinkMacSystemFont,&quot;Helvetica Neue&quot;,&quot;PingFang SC&quot;,&quot;Hiragino Sans GB&quot;,&quot;Microsoft YaHei UI&quot;,&quot;Microsoft YaHei&quot;,Arial,sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: 1px;orphans: 2;text-align: justify;text-decoration: none;text-indent: 0px;text-transform: none;-webkit-text-stroke-width: 0px;word-spacing: 0px;"><br></span> </section> <p style="line-height: 1.75em;"><span style="text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-family: &quot;mp-quote&quot;,-apple-system-font,BlinkMacSystemFont,&quot;Helvetica Neue&quot;,&quot;PingFang SC&quot;,&quot;Hiragino Sans GB&quot;,&quot;Microsoft YaHei UI&quot;,&quot;Microsoft YaHei&quot;,Arial,sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;display: inline !important;orphans: 2;float: none;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);">站点和服务无状态,迁移起来并不困难。</span><br></p> <section style="line-height: 1.75em;"> <img data-ratio="0.5415162454873647" data-type="png" data-w="554" src="/upload/dd4fc93c233e233f718d4ff69191228e.png" data-s="300,640"> <br> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤一</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,前置条件:</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(1)新机房准备就绪;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(2)专线准备就绪;</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤二</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,在新机房搭建好待迁移的子业务,部署好web站点,业务服务,基础服务,做好充分的测试。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">这里要重点说明的是:</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(1)垂直拆分迁移,</span> <span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">每次迁移的范围不要太大</span> <span style="letter-spacing: 1px;font-size: 15px;">,划分好子业务和子系统;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(2)缓存和数据库还未迁移,存在跨机房连接;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(3)新机房的配置文件注意“同连”,不要跨机房调用业务服务与基础服务;</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音,只要不切流量:</span></em><em><span style="letter-spacing: 1px;font-size: 15px;"></span></em></span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">(1)依然老机房提供服务;</span></em><em><span style="letter-spacing: 1px;font-size: 15px;"></span></em></span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">(2)新机房随便玩;</span></em></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤三</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,灰度切流量,将被迁移的子业务切5%的流量到新机房,观察新机房的站点与服务是否异常。如果没有问题,再10%,20%,50%,100%的逐步放量,直至某个子业务迁移完成。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">第一个子业务的站点和服务迁移完之后,第二个子业务、第三个子业务,蚂蚁继续搬家,直至所有的业务把站点和服务都全流量的迁移到新机房。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">如何应对异常?</span></strong> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">在迁移过程中,任何一个子业务,任何时间发生异常,可以将流量切回旧机房。旧机房的站点、服务、配置都没有改动,依然能提供服务。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">这是一个非常稳的迁移方案。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 18px;"><strong><span style="letter-spacing: 1px;font-size: 18px;">二、缓存迁移:</span></strong><strong><span style="letter-spacing: 1px;font-size: 18px;">有状态,但数据可重建</span></strong></span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(51, 51, 51);"><br></span> </section> <p style="line-height: 1.75em;"><span style="color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;">站点和服务迁移完之后,接下来再迁缓存。</span></p> <section style="line-height: 1.75em;"> <img data-ratio="0.5610200364298725" data-type="png" data-w="549" src="/upload/64c0031ad7473b3f341da4d388f299f3.png" data-s="300,640"> <br> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">经过第一步的迁移,如上图:</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(1)所有的入口流量都已经迁到了新的机房;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(2)缓存和数据库,仍然使用旧机房;</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:旧机房的站点和服务不能停,只要旧机房不停,就保留了切回流量回滚的可能性。</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤四</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,在新机房搭建好缓存,缓存的规模和体量与旧机房一样。<br></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤五</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,按照子业务垂直逐步切换使用新机房的缓存,</span> <strong><span style="letter-spacing: 1px;font-size: 15px;">切换细节</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">为:</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(1)运维做一个缓存内网DNS的切换(内网域名不变,IP切到新机房);</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(2)杀掉原有缓存连接,业务线不需要做任何修改,只需要配合观察服务;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(3)缓存连接池会自动重连,重连会自动连接新机房的缓存;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">bingo,一个子业务缓存迁移完毕。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">这里要注意几个点:</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(1)如果没有使用内网域名,而是采用IP直连缓存,则需要业务层配合,换新机房IP重启;</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:说过无数次,一定要使用内网域名。</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(2)缓存迁移时间,尽量选在流量低峰期,新缓存是空数据,如果选在流量高峰期,短时间内可能会有大量请求透传到数据库上;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(3)对于同一个服务,缓存的切换时瞬时的,不会同时使用新旧机房的缓存;</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:否则容易出现一致性问题。</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">缓存的迁移也是按照子业务,垂直拆分,蚂蚁搬家式迁移的。整个迁移过程除了运维操作切内网域名,研发和测试都只是配合观察服务,风险非常低。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">缓存允许cache miss,不用转移旧缓存内的数据,所以迁移方案比较简单。</span> <br> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 18px;"><strong><span style="letter-spacing: 1px;font-size: 18px;">三、数据库迁移:有状态,数据也要迁移</span></strong></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">站点层,服务层,缓存层都迁移完之后,最后是数据库的迁移。</span> </section> <section style="line-height: 1.75em;"> <img data-ratio="1.0436046511627908" data-type="png" data-w="344" src="/upload/b57ffc007e7fa1cef222d09ddff8eced.png" data-s="300,640"> <br> <span style="letter-spacing: 1px;font-size: 15px;">在迁移数据库之前,服务通过专线跨机房连数据库。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">如何进行数据库迁移呢?</span></strong> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤六</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,先在新机房搭建新的数据库。</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:</span></em><em><span style="letter-spacing: 1px;font-size: 15px;">自建机房,需要自己搭建新的MySQL实例;</span></em><em><span style="letter-spacing: 1px;font-size: 15px;">到家直接使用阿里云的RDS。</span></em></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤七</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,数据同步。自建机房可以使用数据库MM/MS架构同步数据,阿里云可以使用DTS同步数据。</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:DTS同步有一个大坑,只能通过公网同步非RDS的数据,至少在16年是这样,不知道现在产品升级了没有。</span></em></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">数据库同步完之后,如何进行数据源切换呢?</span></strong> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">能不能像缓存的迁移一样,运维修改一个数据库内网DNS指向,然后切断数据库连接,让服务重连新的数据库呢?这样的话,业务服务不需要改动,也不需要重启。<br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">这个方式看上去很不错,但是:</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(1)</span> <span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">一定得保证数据库同步完成,才能切流量</span> <span style="letter-spacing: 1px;font-size: 15px;">,但数据同步总是有迟延的,旧机房一直在不停的写如数据,何时才算同步完成?</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">(2)</span> <span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">只有域名和端口不发生变化,才能不修改配置完成切换</span> <span style="letter-spacing: 1px;font-size: 15px;">,但如果域名和端口(主要是端口)发生变化,是做不到不修改配置和重启的。举个例子,假设原有数据库实例端口用了5858,而阿里云要求你使用3200,就必须改端口重启。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span> </section> <section style="line-height: 1.75em;"> <strong><span style="letter-spacing: 1px;font-size: 15px;">步骤八</span></strong> <span style="letter-spacing: 1px;font-size: 15px;">,最终的方案是,DBA在旧机房的数据库设置一个ReadOnly,停止数据的写入,在秒级别,RDS同步完成之后,服务修改数据库端口,重启连接新机房的数据库,完成数据层的切换。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">这个过程中,为了保证数据的一致性,会损失秒级别的写入可用性。</span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span> </section> <section style="line-height: 1.75em;"> <img data-ratio="0.47959183673469385" data-type="png" data-w="588" src="/upload/539f60dab8b2933e99fa833863451625.png" data-s="300,640"> <br> <span style="letter-spacing: 1px;font-size: 15px;">经过上述站点、服务、缓存、数据库的迁移,<span style="display: inline !important;float: none;background-color: rgb(255, 255, 255);color: rgb(51, 51, 51);font-family: &quot;mp-quote&quot;,-apple-system-font,BlinkMacSystemFont,&quot;Helvetica Neue&quot;,&quot;PingFang SC&quot;,&quot;Hiragino Sans GB&quot;,&quot;Microsoft YaHei UI&quot;,&quot;Microsoft YaHei&quot;,Arial,sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: 1px;orphans: 2;text-align: justify;text-decoration: none;text-indent: 0px;text-transform: none;-webkit-text-stroke-width: 0px;word-spacing: 0px;">平滑的蚂蚁搬家式上云</span>目标就这么完成啦。</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:几百台机器,几千个集群,耗时一个季度。</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span> </section> <h2 style="line-height: 1.75em;"><strong><span style="letter-spacing: 1px;font-size: 15px;">自顶向下的机房迁移方案总结</span></strong></h2> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;"><strong>一、先迁移站点层、业务服务层和基础服务层</strong></span></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(1)准备新机房与专线;</span></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(2)搭建集群,充分测试,子业务垂直拆分迁移;</span></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(3)灰度切流量;</span></h2> <h2 style="line-height: 1.75em;"><strong>二、<span style="letter-spacing: 1px;font-size: 15px;">缓存层迁移</span></strong></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(4)搭建新缓存;</span></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(5)运维修改缓存内网DNS,切断旧缓存连接,重连新缓存(这一步很骚),切流量;</span></h2> <h2 style="line-height: 1.75em;"><strong><span style="letter-spacing: 1px;font-size: 15px;">三、数据库迁移</span></strong></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(6)搭建新数据库;</span></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(7)同步数据;</span></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">(8)旧库ReadOnly,同步完成后(秒级),服务指向新库,改配置重启,切流量;</span></h2> <h2 style="line-height: 1.75em;"><br></h2> <h2 style="line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;">以上8大步骤,整个过程分批迁移,一个子业务一个子业务的迁移,一块缓存一块缓存的迁移,一个数据库一个数据库的迁移,任何步骤出现问题都可以回滚的,整个过程不停服务。&nbsp;</span><br></h2> <section style="line-height: 1.75em;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">思路比结论重要。</span></strong> <br style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"> </section> <p style="margin: 0px;padding: 0px;text-align: center;color: rgb(51, 51, 51);text-transform: none;line-height: normal;text-indent: 0px;letter-spacing: 0.54px;clear: both;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;min-height: 1em;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;font-size: 12px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><img width="auto" style="box-sizing: border-box;height: auto;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 677px;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;visibility: visible;width: 130px;" data-ratio="1" data-type="jpeg" data-w="250" src="/upload/7ddc9700032e2c5cee163f1f1a37b46c.jpg" data-s="300,640"></strong></span></p> <p style="margin: 0px;padding: 0px;text-align: center;color: rgb(51, 51, 51);text-transform: none;line-height: normal;text-indent: 0px;letter-spacing: 0.48px;clear: both;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;min-height: 1em;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"><span style="box-sizing: border-box;font-size: 12px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">架构师之路</strong>-分享技术思路</strong></span></p> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <br style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><span style="background-color: rgb(255, 255, 255);box-sizing: border-box;color: rgb(51, 51, 51);display: inline;float: none;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">相关文章</span></strong><span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);display: inline;max-width: 100%;background-color: rgb(255, 255, 255);">:</span></span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;background-color: rgb(255, 255, 255);">《<a style="box-sizing: border-box;color: rgb(87, 107, 149);cursor: pointer;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;text-decoration: none;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);" href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651963156&amp;idx=1&amp;sn=472d881f6ea2f0cd4f2b63160a139cb1&amp;chksm=bd2d0ac88a5a83decc49a7d5a33faa112969d8ee0afb243acacf82f7628158c9fafde7691bd7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0" tab="innerlink" hasload="1">当年,我们是怎么平滑上云的?</a>》</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;background-color: rgb(255, 255, 255);">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651963162&amp;idx=1&amp;sn=07f3f7d86f28afe8782c4d9dda16121a&amp;chksm=bd2d0ac68a5a83d078e596355160883ea19911f912af1878645f0fed642bb5f706b0943b947d&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0" tab="innerlink">多机房多活架构,究竟怎么玩?</a>》</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <br> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="box-sizing: border-box;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"><span style="background-color: rgb(255, 255, 255);box-sizing: border-box;color: rgb(51, 51, 51);display: inline;float: none;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">讨论</span></strong><span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);display: inline;max-width: 100%;background-color: rgb(255, 255, 255);">:</span></span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="background-color: rgb(255, 255, 255);box-sizing: border-box;color: rgb(51, 51, 51);display: inline;float: none;font-size: 15px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;overflow-wrap: break-word;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;">贵司是如何上云的,步骤如何?</span> </section> <section style="margin: 0px;padding: 0px;text-align: justify;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.48px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;max-width: 100%;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;overflow-wrap: break-word;"> <span style="margin: 0px;padding: 0px;color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;display: inline;max-width: 100%;box-sizing: border-box;float: none;overflow-wrap: break-word;background-color: rgb(255, 255, 255);">画外音:长文阅读量太低了,春节不写技术了。</span> </section>

一次900万+数据量的 SQL 查询优化分析

作者:微信小助手

<h3 style="max-width: 100%;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;" data-mpa-powered-by="yiban.io"> <section powered-by="xiumi.us" style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;line-height: 1.75em;margin-top: 5px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 16px;color: rgb(255, 76, 65);">祝大家新年快乐,在新的一年里,工作顺利,身体健康,万事如意!</span></p> <section style="margin-top: 15px;max-width: 100%;min-height: 1em;line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="font-size: 16px;color: rgb(255, 76, 65);">另外,最近大家没有特别重要的事情不要出门了,<span style="color: rgb(255, 76, 65);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);">尽量在家呆着,</span><span style="color: rgb(255, 76, 65);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);">减少与</span><span style="color: rgb(255, 76, 65);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);">他人接触</span><span style="color: rgb(255, 76, 65);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);">,</span>出门一定记得带有防护作用的口罩,注意安全!保护好自己!保护好你的家人!</span> </section> <section style="margin-top: 15px;max-width: 100%;min-height: 1em;line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="font-size: 16px;color: rgb(255, 76, 65);">加油武汉!加油中国!</span> </section> <section style="margin-top: 15px;max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-size: 14px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">往期热门文章:</span> </section> </section> </section> </section> </section></h3> <h4 style="max-width: 100%;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(62, 62, 62);box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;min-height: 1em;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;text-align: left;word-spacing: 1px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">1、</span><a href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&amp;mid=2247489898&amp;idx=1&amp;sn=5c5e41a0f695649046de3db1b1810df5&amp;chksm=e9c5e0dbdeb269cd52f798c675f58295ad0f632283c0fb07db4140949c72796deed7dfe7d434&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" hasload="1" style="color: rgb(62, 62, 62);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;letter-spacing: 0.544px;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">《</a><a href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&amp;mid=2247489402&amp;idx=2&amp;sn=af5c3bb38717e828d92ed48874f03fe8&amp;chksm=e9c5eecbdeb267dd3d05c159bdb9c611f24c4ca7fb7dafa12daf459becb0ef4fab7bcc2d1a67&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">往期</span><span style="max-width: 100%;cursor: pointer;letter-spacing: 0.544px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">精选优秀博文都在这里了!</span><span style="max-width: 100%;cursor: pointer;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);color: rgb(62, 62, 62);letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">》</span></a></p><p style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;min-height: 1em;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;text-align: left;word-spacing: 1px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">2、<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&amp;mid=2247490859&amp;idx=1&amp;sn=ae00908a017b8d67423a4b92419487ee&amp;chksm=e9c5e49adeb26d8cb12a6e7001e46a82064d7b829d1cc378b08791bbacd62c508625cb63a71a&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">7 款神秘的开源中间件!</a></span></p><p style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;min-height: 1em;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;text-align: left;word-spacing: 1px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&amp;mid=2247490850&amp;idx=1&amp;sn=e715d5a8761ec22cca3ee06e38ab4b02&amp;chksm=e9c5e493deb26d85d4575117b2ef821d484eac94da90eec2de9c9aa9714fd032b2f3a7b018c5&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">3、MyBatis她不香吗?为啥老外却喜欢Hibernate/Jpa?</a></span></p><p style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;min-height: 1em;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;text-align: left;word-spacing: 1px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">4<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&amp;mid=2247490844&amp;idx=1&amp;sn=1f92c6f6ae9e9dc48e219c30d86d9a54&amp;chksm=e9c5e4addeb26dbbda0c26678a9140bd250fc2fc02d3c23115e117711731219d85cb345671fc&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">、代码对比工具,我就用这7个!</a></span></p><p style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;min-height: 1em;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;text-align: left;word-spacing: 1px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">5<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&amp;mid=2247490812&amp;idx=1&amp;sn=77724eaa49f7a4f4efc4c0045e1db80f&amp;chksm=e9c5e54ddeb26c5bdad60eac3e35daa0391def6a1c1be7de10383958b606ce8940e77d47c916&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">、Mybatis 中经典的 9 种设计模式!面试可以吹牛了!</a></span></p></h4> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(0, 0, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;text-align: left;font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;font-size: 12px;white-space: pre-line;box-sizing: border-box !important;overflow-wrap: break-word !important;">来源 :</span></span><br></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(0, 0, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;font-size: 10px;white-space: pre-line;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://www.jianshu.com/p/0768ebc4e28d</span></span></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-bottom: 15px;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;">有一张流水表,未分库分表,目前的数据量为950w,分页查询使用到了limit,优化之前的查询耗时167s左右 (execution: 16s831ms, fetching: 107 ms)</p> <p style="margin-bottom: 15px;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;">按照下文的方式调整SQL后,耗时347ms (execution: 163 ms, fetching: 184 ms);优化前的SQL类似这样:</p> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="max-width: 100%;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="margin-right: 0.15em;margin-left: 0.15em;padding: 6px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(92, 99, 112);background: rgba(0, 0, 0, 0);width: 75px;text-decoration-style: solid;text-decoration-color: rgb(92, 99, 112);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">-- 优化前SQL</span><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 39px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">SELECT</span>&nbsp;&nbsp;各种字段<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 26px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">FROM</span>&nbsp;<span style="max-width: 100%;color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);width: 79px;text-decoration-style: solid;text-decoration-color: rgb(152, 195, 121);box-sizing: border-box !important;overflow-wrap: break-word !important;">`table_name`</span><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 33px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">WHERE</span>&nbsp;各种条件<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 33px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">LIMIT</span>&nbsp;<span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 7px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">0</span>,<span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 13px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">10</span>;</code></pre> </section> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;font-size: 16px;letter-spacing: 0.544px;text-align: start;white-space: pre-line;box-sizing: border-box !important;overflow-wrap: break-word !important;">优化后SQL是这样的(这种优化手段在高性能MySQL中有提及):</span></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="max-width: 100%;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="margin-right: 0.15em;margin-left: 0.15em;padding: 6px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(92, 99, 112);background: rgba(0, 0, 0, 0);width: 75px;text-decoration-style: solid;text-decoration-color: rgb(92, 99, 112);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">-- 优化后SQL</span><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 39px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">SELECT</span>&nbsp;&nbsp;各种字段<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 26px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">FROM</span>&nbsp;<span style="max-width: 100%;color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);width: 79px;text-decoration-style: solid;text-decoration-color: rgb(152, 195, 121);box-sizing: border-box !important;overflow-wrap: break-word !important;">`table_name`</span>&nbsp;main_tale<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 33px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">RIGHT</span>&nbsp;<span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 27px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">JOIN</span>&nbsp;<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">(<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 39px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">SELECT</span>&nbsp;&nbsp;子查询只查主键<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 26px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">FROM</span>&nbsp;<span style="max-width: 100%;color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);width: 79px;text-decoration-style: solid;text-decoration-color: rgb(152, 195, 121);box-sizing: border-box !important;overflow-wrap: break-word !important;">`table_name`</span><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 33px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">WHERE</span>&nbsp;各种条件<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 33px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">LIMIT</span>&nbsp;<span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 7px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">0</span>,<span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 13px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">10</span>;<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">) temp_table ON temp_table.主键 = main_table.主键</code></pre> </section> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-top: 15px;margin-bottom: 15px;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;">一,前言</p> <p style="margin-top: 15px;margin-bottom: 15px;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;">首先说明一下MySQL的版本:</p> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="max-width: 100%;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="margin-right: 0.15em;margin-left: 0.15em;padding: 6px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;">mysql&gt;&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 106px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 40px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">select</span>&nbsp;<span style="max-width: 100%;color: rgb(97, 174, 238);background: rgba(0, 0, 0, 0);width: 46px;text-decoration-style: solid;text-decoration-color: rgb(97, 174, 238);box-sizing: border-box !important;overflow-wrap: break-word !important;">version</span>(<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 0px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;"></span>)</span>;<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">+-----------+<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">| version() |<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">+-----------+<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">|&nbsp;<span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 20px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">5.7</span><span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 20px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">.17</span>&nbsp;&nbsp;&nbsp;&nbsp;|<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">+-----------+<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 6px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">1</span>&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 139px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">row&nbsp;<span style="max-width: 100%;color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);width: 14px;text-decoration-style: solid;text-decoration-color: rgb(198, 120, 221);box-sizing: border-box !important;overflow-wrap: break-word !important;">in</span>&nbsp;<span style="max-width: 100%;color: rgb(97, 174, 238);background: rgba(0, 0, 0, 0);width: 20px;text-decoration-style: solid;text-decoration-color: rgb(97, 174, 238);box-sizing: border-box !important;overflow-wrap: break-word !important;">set</span>&nbsp;(<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 53px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 27px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">0.00</span>&nbsp;sec</span>)</span></code></pre> </section> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-top: 15px;margin-bottom: 15px;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;">再看一下表结构:</p> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="max-width: 100%;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="margin-right: 0.15em;margin-left: 0.15em;padding: 6px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;">mysql&gt; desc test;<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">+--------+---------------------+------+-----+---------+----------------+<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 66px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| Field |</span>&nbsp;Type&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 53px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| Null |</span>&nbsp;Key&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 73px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| Default |</span>&nbsp;Extra&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 475px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">|<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">+--------+---------------------+------+-----+---------+----------------+<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">|</span>&nbsp;id&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 152px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| bigint(20) unsigned |</span>&nbsp;NO&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 47px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| PRI |</span>&nbsp;NULL&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 119px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| auto_increment |</span><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 66px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| val |</span>&nbsp;int(<span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 13px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflow-wrap: break-word !important;">10</span>) unsigned&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 53px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| NO |</span>&nbsp;MUL&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 73px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| 0 |</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 475px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">|<br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">|</span>&nbsp;source&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 152px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| int(10) unsigned |</span>&nbsp;NO&nbsp;<span style="max-width: 100%;background: rgba(0, 0, 0, 0);width: 47px;text-decoration-style: solid;text-decoration-color: rgb(171, 178, 191);box-sizing: border-box !important;overflow-wrap: break-word !important;">| |</span>&nbsp;<span style="max-width: 100%;color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);width: 7px;text-decoration-style: solid;text-decoration-color: rgb(209, 154, 102);box-sizing: border-box !important;overflo

UidGenerator:百度开源的分布式 ID 服务(解决了时钟回拨问题)

作者:微信小助手

<p style="text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">转自:阿飞的博客</span></p> </blockquote> <p><br></p> <p><span style="font-size: 15px;">UidGenerator是百度开源的Java语言实现,基于Snowflake算法的唯一ID生成器。而且,它非常适合虚拟环境,比如:Docker。另外,它通过消费未来时间克服了雪花算法的并发限制。UidGenerator提前生成ID并缓存在RingBuffer中。压测结果显示,单个实例的QPS能超过6000,000。依赖环境:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">JDK8+</span></p></li> <li><p><span style="font-size: 15px;">MySQL(用于分配WorkerId)</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">snowflake</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">由下图可知,雪花算法的几个核心组成部分:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">1位sign标识位;</span></p></li> <li><p><span style="font-size: 15px;">41位时间戳;</span></p></li> <li><p><span style="font-size: 15px;">10位workId(数据中心+工作机器,可以其他组成方式);</span></p></li> <li><p><span style="font-size: 15px;">12位自增序列;</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><img data-ratio="0.23329129886506936" data-type="png" data-w="793" title="snowflake" src="/upload/29715d1bb61232f21cb5249b7c94f58d.png" style="margin-right: auto;margin-left: auto;color: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: inherit;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;display: block;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="text-align: center;"><span style="font-size: 12px;">snowflake</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">但是百度对这些组成部分稍微调整了一下:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><img data-ratio="0.14545454545454545" data-type="png" data-w="880" title="baidu uid generator" src="/upload/e876b63305c4d9a5deca8fe2d765588d.png" style="margin-right: auto;margin-left: auto;color: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: inherit;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;display: block;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="text-align: center;"><span style="font-size: 12px;">baidu uid generator</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">由上图可知,UidGenerator的时间部分只有28位,这就意味着UidGenerator默认只能承受8.5年(2^28-1/86400/365)。当然,根据你业务的需求,UidGenerator可以适当调整delta seconds、worker node id和sequence占用位数。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">接下来分析百度UidGenerator的实现。需要说明的是UidGenerator有两种方式提供:和DefaultUidGenerator和CachedUidGenerator。我们先分析比较容易理解的DefaultUidGenerator。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">DefaultUidGenerator</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">delta seconds</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">这个值是指当前时间与epoch时间的时间差,且单位为秒。epoch时间就是指集成UidGenerator生成分布式ID服务第一次上线的时间,可配置,也一定要根据你的上线时间进行配置,因为默认的epoch时间可是2016-09-20,不配置的话,会浪费好几年的可用时间。</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">worker id</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">接下来说一下UidGenerator是如何给worker id赋值的,搭建UidGenerator的话,需要创建一个表:</span></p> <p><span style="font-size: 15px;"></span></p> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;">DROP TABLE IF EXISTS WORKER_NODE;<br><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">CREATE TABLE <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">WORKER_NODE</span><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(<br> ID BIGINT NOT <span style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> AUTO_INCREMENT PRIMARY KEY ,<br> HOST_NAME VARCHAR(<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">64</span>)</span> NOT <span style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> COMMENT 'host name',<br> PORT <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">VARCHAR</span><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">64</span>)</span> NOT <span style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> COMMENT 'port',<br> TYPE INT NOT <span style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> COMMENT 'node type: ACTUAL or CONTAINER',<br> LAUNCH_DATE DATE NOT <span style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> COMMENT 'launch date',<br> MODIFIED DATETIME NOT <span style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> COMMENT 'modified time',<br> CREATED DATEIMTE NOT <span style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> COMMENT 'created time'<br>)<br> COMMENT</span>=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">'DB WorkerID Assigner for UID Generator'</span>,ENGINE = INNODB;</code></pre> <p><br></p> <p><span style="font-size: 15px;">UidGenerator会在集成用它生成分布式ID的实例启动的时候,往这个表中插入一行数据,得到的id值就是准备赋给workerId的值。由于workerId默认22位,那么,集成UidGenerator生成分布式ID的所有实例重启次数是不允许超过4194303次(即2^22-1),否则会抛出异常。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">这段逻辑的核心代码来自DisposableWorkerIdAssigner.java中,当然,你也可以实现WorkerIdAssigner.java接口,自定义生成workerId。</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">sequence</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">核心代码如下,几个实现的关键点:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="color: rgb(0, 0, 0);"><strong><span style="font-size: 15px;">synchronized</span></strong></span><span style="font-size: 15px;">保证线程安全;</span></p></li> <li><p><span style="font-size: 15px;">如果时间有任何的回拨,那么</span><strong><span style="font-size: 15px;color: rgb(0, 0, 0);">直接</span></strong><span style="font-size: 15px;">抛出异常;</span></p></li> <li><p><span style="font-size: 15px;">如果当前时间和上一次是同一秒时间,那么sequence自增。如果同一秒内自增值超过2^13-1,那么就会自旋等待下一秒(getNextSecond);</span></p></li> <li><p><span style="font-size: 15px;">如果是新的一秒,那么sequence重新从0开始;</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;"><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">protected</span> synchronized <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">long</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">nextId</span><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">()</span> </span>{<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">long</span> currentSecond = getCurrentSecond();<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (currentSecond &lt; lastSecond) {<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">long</span> refusedSeconds = lastSecond - currentSecond;<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throw</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> UidGenerateException(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"Clock moved backwards. Refusing for %d seconds"</span>, refusedSeconds);<br> }<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (currentSecond == lastSecond) {<br> sequence = (sequence + <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">1</span>) &amp; bitsAllocator.getMaxSequence();<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (sequence == <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">0</span>) {<br> currentSecond = getNextSecond(lastSecond);<br> }<br> } <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">else</span> {<br> sequence = <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">0L</span>;<br> }<br> lastSecond = currentSecond;<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">return</span> bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);<br>}</code></pre> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">总结</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">通过DefaultUidGenerator的实现可知,它对时钟回拨的处理比较简单粗暴。另外如果使用UidGenerator的DefaultUidGenerator方式生成分布式ID,一定要根据你的业务的情况和特点,调整各个字段占用的位数:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;">&lt;property name=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"timeBits"</span> value=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"28"</span>/&gt;<br>&lt;property name=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"workerBits"</span> value=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"22"</span>/&gt;<br>&lt;property name=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"seqBits"</span> value=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"13"</span>/&gt;<br>&lt;property name=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"epochStr"</span> value=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"2016-09-20"</span>/&gt;</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">CachedUidGenerator</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CachedUidGenerator是UidGenerator的重要改进实现。它的核心利用了RingBuffer,如下图所示,它本质上是一个数组,数组中每个项被称为slot。UidGenerator设计了两个RingBuffer,一个保存唯一ID,一个保存flag。RingBuffer的尺寸是2^n,n必须是正整数:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><img data-ratio="0.6881720430107527" data-type="png" data-w="744" title="RingBuffer" src="/upload/8c0985c1e4e4db74db7fadf91fe28bdb.png" style="margin-right: auto;margin-left: auto;color: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: inherit;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;display: block;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="text-align: center;"><span style="font-size: 12px;">RingBuffer</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">RingBuffer Of Flag</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">其中,保存flag这个RingBuffer的每个slot的值都是0或者1,0是CAN_PUT_FLAG的标志位,1是CAN_TAKE_FLAG的标识位。每个slot的状态要么是CAN_PUT,要么是CAN_TAKE。以某个slot的值为例,初始值为0,即CAN_PUT。接下来会初始化填满这个RingBuffer,这时候这个slot的值就是1,即CAN_TAKE。等获取分布式ID时取到这个slot的值后,这个slot的值又变为0,以此类推。</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">RingBuffer Of UID</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">保存唯一ID的RingBuffer有两个指针,Tail指针和Cursor指针。</span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">Tail指针表示最后一个生成的唯一ID。如果这个指针追上了Cursor指针,意味着RingBuffer已经满了。这时候,不允许再继续生成ID了。用户可以通过属性rejectedPutBufferHandler指定处理这种情况的策略。</span></p></li> <li><p><span style="font-size: 15px;">Cursor指针表示最后一个已经给消费的唯一ID。如果Cursor指针追上了Tail指针,意味着RingBuffer已经空了。这时候,不允许再继续获取ID了。用户可以通过属性rejectedTakeBufferHandler指定处理这种异常情况的策略。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">另外,如果你想增强RingBuffer提升它的吞吐能力,那么需要配置一个更大的boostPower值:</span></p> <p><span style="font-size: 15px;"></span></p> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;">&lt;!-- RingBuffer size扩容参数, 可提高UID生成能力.即每秒产生ID数上限能力 --&gt;<br>&lt;!-- 默认:<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">3</span>,原bufferSize=<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">2</span>^<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">13</span>, 扩容后bufferSize = <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">2</span>^<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">13</span> &lt;&lt; <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">3</span> = <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">65536</span> --&gt;<br>&lt;property name=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"boostPower"</span> value=<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"3"</span>/&gt;</code></pre> <p><br></p> <p><span style="font-size: 15px;">CachedUidGenerator的理论讲完后,接下来就是它具体是如何实现的了,我们首先看它的申明,它是实现了DefaultUidGenerator,所以,它事实上就是对DefaultUidGenerator的增强:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">class</span> CachedUidGenerator extends DefaultUidGenerator implements DisposableBean {<br> ... ...<br>}</code></pre> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">worker id</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CachedUidGenerator的workerId实现继承自它的父类DefaultUidGenerator,即实例启动时往表WORKER_NODE插入数据后得到的自增ID值。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">接下来深入解读CachedUidGenerator的核心操作,即对RingBuffer的操作,包括初始化、取分布式唯一ID、填充分布式唯一ID等。</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">初始化</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CachedUidGenerator在初始化时除了给workerId赋值,还会初始化RingBuffer。</span><span style="font-size: 15px;">这个过程主要工作有:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">根据boostPower的值确定RingBuffer的size;</span></p></li> <li><p><span style="font-size: 15px;">构造RingBuffer,默认paddingFactor为50。</span><span style="font-size: 15px;">这个值的意思是当RingBuffer中剩余可用ID数量少于50%的时候,就会触发一个异步线程往RingBuffer中填充新的唯一ID(调用BufferPaddingExecutor中的paddingBuffer()方法,这个线程中会有一个标志位running控制并发问题),直到填满为止;</span></p></li> <li><p><span style="font-size: 15px;">判断是否配置了属性scheduleInterval,这是另外一种RingBuffer填充机制, 在Schedule线程中, 周期性检查填充。</span><span style="font-size: 15px;">默认:不配置, 即不使用Schedule线程. 如需使用, 请指定Schedule线程时间间隔, 单位:秒;</span></p></li> <li><p><span style="font-size: 15px;">初始化Put操作拒绝策略,对应属性rejectedPutBufferHandler。</span><span style="font-size: 15px;">即当RingBuffer已满, 无法继续填充时的操作策略。</span><span style="font-size: 15px;">默认无需指定, 将丢弃Put操作, 仅日志记录. 如有特殊需求, 请实现RejectedPutBufferHandler接口(支持Lambda表达式);</span></p></li> <li><p><span style="font-size: 15px;">初始化Take操作拒绝策略,对应属性rejectedTakeBufferHandler。</span><span style="font-size: 15px;">即当环已空, 无法继续获取时的操作策略。</span><span style="font-size: 15px;">默认无需指定, 将记录日志, 并抛出UidGenerateException异常. 如有特殊需求, 请实现RejectedTakeBufferHandler接口;</span></p></li> <li><p><span style="font-size: 15px;">初始化填满RingBuffer中所有slot(即塞满唯一ID,这一步和第2步骤一样都是调用BufferPaddingExecutor中的paddingBuffer()方法);</span></p></li> <li><p><span style="font-size: 15px;">开启buffer补丁线程(前提是配置了属性scheduleInterval),原理就是利用ScheduledExecutorService的scheduleWithFixedDelay()方法。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">说明:</span><span style="font-size: 15px;">第二步的异步线程实现非常重要,也是UidGenerator解决时钟回拨的关键:</span><span style="font-size: 15px;">在满足填充新的唯一ID条件时,通过时间值递增得到新的时间值(lastSecond.incrementAndGet()),而不是System.currentTimeMillis()这种方式,而lastSecond是AtomicLong类型,所以能保证线程安全问题。</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">取值</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">RingBuffer初始化有值后,接下来的取值就简单了。</span><span style="font-size: 15px;">不过,由于分布式ID都保存在RingBuffer中,取值过程中就会有一些逻辑判断:</span></p> <p><br></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">如果剩余可用ID百分比低于paddingFactor参数指定值,就会异步生成若干个ID集合,直到将RingBuffer填满。</span></p></li> <li><p><span style="font-size: 15px;">如果获取值的位置追上了tail指针,就会执行Task操作的拒绝策略。</span></p></li> <li><p><span style="font-size: 15px;">获取slot中的分布式ID。</span></p></li> <li><p><span style="font-size: 15px;">将这个slot的标志位只为CAN_PUT_FLAG。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">总结</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">通过上面对UidGenerator的分析可知,CachedUidGenerator方式主要通过采取如下一些措施和方案规避了时钟回拨问题和增强唯一性:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><strong><span style="font-size: 15px;">自增列:</span></strong><span style="font-size: 15px;">UidGenerator的workerId在实例每次重启时初始化,且就是数据库的自增ID,从而完美的实现每个实例获取到的workerId不会有任何冲突。</span></p></li> <li><p><strong><span style="font-size: 15px;">RingBuffer:</span></strong><span style="font-size: 15px;">UidGenerator不再在每次取ID时都实时计算分布式ID,而是利用RingBuffer数据结构预先生成若干个分布式ID并保存。</span></p></li> <li><p><strong><span style="font-size: 15px;">时间递增:</span></strong><span style="font-size: 15px;">传统的雪花算法实现都是通过System.currentTimeMillis()来获取时间并与上一次时间进行比较,这样的实现严重依赖服务器的时间。</span><span style="font-size: 15px;">而UidGenerator的时间类型是AtomicLong,且通过incrementAndGet()方法获取下一次的时间,从而脱离了对服务器时间的依赖,也就不会有时钟回拨的问题(这种做法也有一个<strong>小问题</strong>,即分布式ID中的时间信息可能并不是这个ID真正产生的时间点,例如:</span><span style="font-size: 15px;">获取的某分布式ID的值为3200169789968523265,它的反解析结果为{"timestamp":"2019-05-02 23:26:39","workerId":"21","sequence":"1"},但是这个ID可能并不是在"2019-05-02 23:26:39"这个时间产生的)。</span></p></li> </ul> <p><br></p> <section donone="shifuMouseDownCard('shifu_c_030')" label="Copyright Reserved by PLAYHUDONG." style="margin-top: 1em;margin-bottom: 1em;white-space: normal;text-align: start;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);border-width: 0px;border-style: initial;border-color: initial;"> <section style="margin-left: 1em;line-height: 1.4;"> <span style="padding: 3px 8px;border-radius: 4px;color: rgb(255, 255, 255);background-color: rgb(255, 105, 31);font-family: inherit;text-align: inherit;text-decoration: inherit;font-size: 16px;">推荐阅读</span>&nbsp;&nbsp; <span style="margin-left: 4px;padding: 3px 8px;border-radius: 1.2em;color: rgb(255, 255, 255);line-height: 1.2;background-color: rgb(204, 204, 204);font-family: inherit;text-align: inherit;text-decoration: inherit;border-color: rgb(249, 110, 87);font-size: 12px;">点击标题可跳转</span> </section> <section style="margin-top: -11px;padding: 22px 16px 16px;border-width: 1px;border-style: solid;border-color: rgb(255, 105, 31);color: rgb(51, 51, 51);font-size: 1em;font-family: inherit;text-align: inherit;text-decoration: inherit;"> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651479250&amp;idx=1&amp;sn=c7879f7b4ea2ebb67d44837c7134d8b1&amp;chksm=bd2530ad8a52b9bb6ca86ba59dd1760f20180950476e488a7d6d5d195b019de0bf3b858f197f&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">分布式 Unique ID 的生成方法一览</span></a><br></p> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651486149&amp;idx=1&amp;sn=ad1d194dc9743e487daff54f61f58e83&amp;chksm=bd251bba8a5292ac29e3e1ae85aece8f375f481910c097aa19f12707c09f7aa6acbef424f075&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">如何修改 Lambda 表达式中的变量值</span></a><br></p> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651486158&amp;idx=1&amp;sn=a0b4fe75dfefe62ec19a619d6a09d83d&amp;chksm=bd251bb18a5292a7292ec004791c9af7833365fadab133ba65b944da570b5491bf8fe210a34b&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">用 Kafka 和 Awaitility 测试 Spring Boot 应用</span></a><br></p> </section> </section> <p><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-ratio="0.9166666666666666" data-s="300,640" data-type="png" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p> <p style="text-align: right;"><span style="font-size: 14px;text-align: right;">好文章,我</span><span style="font-size: 14px;text-align: right;color: rgb(255, 41, 65);">在看</span><span style="font-size: 14px;text-align: right;">❤️</span></p>

马上就要远程办公了?这些工具或许能帮到你

作者:微信小助手

<section style="color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;text-align: left;white-space: normal;padding-right: 10px;padding-left: 10px;" data-mpa-powered-by="yiban.io"> <p><img data-ratio="0.5625" src="/upload/4ca70c5f14bc1f9c3103a936c7d0b7a1.jpg" data-type="jpeg" data-w="1920" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;font-size: 12px;color: rgb(172, 172, 172);"><br></p> <section style="box-sizing: border-box;font-size: 16px;margin:2.3em 0;"> <section style="margin: 10px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 100%;vertical-align: top;border-style: solid;border-width: 1px;border-radius: 0px;border-color: transparent;background-color: rgb(255, 255, 255);box-sizing: border-box;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;vertical-align:top;width: 18%;box-sizing: border-box;"> <section style="text-align: left;transform: translate3d(-1px, 0px, 0px);margin-top: -1px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 40px;height: 40px;vertical-align: top;overflow: hidden;border-width: 1px;border-radius: 0px 0px 100%;border-style: solid;border-color: transparent;box-sizing: border-box;"> <section style="margin-top: 3px;margin-right: 0%;margin-left: 0%;transform: translate3d(4px, 0px, 0px);box-sizing: border-box;" powered-by="xiumi.us"> <section style="max-width: 100%;vertical-align: middle;display: inline-block;line-height: 0;width: 60%;box-sizing: border-box;"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewbox="0 0 108.54 70" style="vertical-align: middle;max-width: 100%;width: 100%;box-sizing: border-box;" width="100%"> <g style="box-sizing: border-box;"> <path style="box-sizing: border-box;" d="M56.65,49c1.8,9.84,9.99,18.14,19.69,20.35c10.49,2.39,21.34-1.97,27.48-10.74 c5.67-8.1,6.4-19.38,1.31-27.94c-5.49-9.23-16.5-14.16-27.06-12.52c2.31-2.91,4.5-5.9,6.74-8.85c1.41-1.86,5.28-5.28,3.75-7.88 c-2.03-3.45-5.06,0.21-6.63,1.86c-2.61,2.73-5.32,5.37-7.89,8.14C64.65,21.51,53.95,34.31,56.65,49z" fill="rgb(232,10,35)"></path> <path style="box-sizing: border-box;" d="M0.42,49c1.8,9.84,9.99,18.14,19.69,20.35c10.49,2.39,21.34-1.97,27.48-10.74 c5.67-8.1,6.4-19.38,1.31-27.94c-5.49-9.23-16.5-14.16-27.06-12.52c2.31-2.91,4.5-5.9,6.74-8.85c1.41-1.86,5.28-5.28,3.75-7.88 c-2.03-3.45-5.06,0.21-6.63,1.86c-2.61,2.73-5.32,5.37-7.89,8.14C8.42,21.51-2.27,34.31,0.42,49z" fill="rgb(232,10,35)"></path> </g> </svg> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: bottom;width: 82%;padding-right: 5px;border-color: transparent;border-width: 0px;border-radius: 0px;border-style: none;box-sizing: border-box;margin-top: -3px;"> <section style="transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-o-transform: rotate(0deg);box-sizing: border-box;" powered-by="xiumi.us"> <section style="transform: translate3d(-10px, 0px, 0px);-webkit-transform: translate3d(-10px, 0px, 0px);-moz-transform: translate3d(-10px, 0px, 0px);-o-transform: translate3d(-10px, 0px, 0px);box-sizing: border-box;margin-top: -2px;"> <section style="color: rgb(172, 172, 172);font-size: 15px;box-sizing: border-box;"> <p style="white-space: normal;box-sizing: border-box;margin-top: -3px;"><strong style="box-sizing: border-box;">在家也能轻松办公!</strong></p> </section> </section> </section> </section> </section> </section> </section> </section> <p line="Bzou" style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="color: rgb(40, 40, 40);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 15px;letter-spacing: 0.3px;background-color: rgb(255, 255, 255);">2 月 2 日春节假期结束之后,即使不需要立即上班,你可能也要马上投入到远程办公之中。</span></p> <p line="d33P" style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: justify;">今天我们就给大家介绍几款效率提升工具以及 Chrome 插件合集,让你在远程办公时也能高效工作,节省出更多休息时间。<br></p> <p line="d33P" style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><br></p> <h2 style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 30px;margin-bottom: 30px;font-weight: bold;font-size: 18px;line-height: 2;color: rgb(255, 255, 255);"><span style="background: rgb(232, 10, 35);padding: 0.3em 10px;-webkit-box-decoration-break: clone;">团队协作工具</span></h2> <p line="d33P" style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: justify;">在疫情期间,飞书、阿里钉钉、石墨文档等多款团队协作工具宣布免费开放相关功能和服务。</p> <p line="d33P" style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: justify;"><span style="font-weight: bold;color: rgb(232, 10, 35);">1)飞书</span></p> <p style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: justify;">1 月 28 日—5 月 1 日期间,飞书将为所有用户免费提供远程办公协作等商业版服务。此外,所有湖北地区的医院、学校、公益机构等组织,在此期间注册申请,可享有长达三年的飞书商业版免费服务。</p> <p style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: justify;"><span style="font-weight: bold;color: rgb(232, 10, 35);">2)石墨文档</span></p> <p style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: justify;">石墨文档在疫情期间,个人账户将自动升级为远程办公版(原个人高级版)新老用户均可免费使用。</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-weight: bold;color: rgb(232, 10, 35);">3)钉钉</span></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">钉钉陆续免费开放了在家办公系统、在线课堂等多项功能,发布了在家办公指南和「在线上课」计划。<span style="letter-spacing: 0.3px;"></span></p> <p style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: justify;"><br></p> <h2 style="margin-top: 30px;margin-bottom: 30px;font-weight: bold;font-size: 18px;line-height: 2;color: rgb(255, 255, 255);"><span style="background: rgb(232, 10, 35);padding: 0.3em 10px;-webkit-box-decoration-break: clone;">辅助类工具</span></h2> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">一款好的辅助工具,总能让我们的工作如虎添翼。在复杂繁琐的工作中,如果每一步都快一点,你就能节约出不少时间。</span></p> <p style="margin-top: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="color: rgb(232, 10, 35);"><strong>1)iSlide PPT 插件</strong></span></p> <p style="margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">官网:</span>https://www.islide.cc/</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">不管是正常上班还是远程办公,<span style="font-family:微软雅黑;">你肯定少不了要做</span>&nbsp;PPT。与其手动调整每个文字、图片、形状的对齐位置,不如把这些重复工作交给插件。</p> <p><img data-ratio="0.6731974921630094" src="/upload/81aba3a94bdb778f5be26b9192037e9b.png" data-type="png" data-w="1276" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);" width="auto"></p> <p><span style="font-size: 12px;color: rgb(178, 178, 178);">Windows 版插件界面,请注意顶部丰富的功能菜单</span><br></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">之前我们给大家介绍过的</span>&nbsp;iSlide 插件不仅提供了能一键对齐的设计工具面板,还能一键统一字体、一键排版。在 iSlide 的素材库中,也有丰富的模板、图标可以一键调用。</p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.7126436781609196" data-s="300,640" src="/upload/26f523789889040cbd09cfbcf5b519c6.png" data-type="png" data-w="1392" style=""></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">除此之外,在</span>&nbsp;iSlide 插件的 Windows 版本中还有安全导出功能,可以将 PPT 迅速导出为全图型PPT、长图、视频等格式,方便各种用途的使用。</p> <p style="margin-top: 1.4em;font-size: 15px;text-align: left;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="font-size: 12px;font-family: 微软雅黑;color: rgb(255, 255, 255);background-color: rgb(232, 10, 35);padding: 3px 8px;margin-bottom:-2px;">关联阅读</span></strong></p> <p style="text-align: center;margin-top: -2px;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5MTMyNTYzNA==&amp;mid=2663977398&amp;idx=1&amp;sn=0ea6fa1263e711a46830474c60bbc9e7&amp;chksm=8b43a41bbc342d0d78b760527f6417d7f9d028971f85993166800d52f1e6d808f7297eb89677&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="rich_pages js_insertlocalimg" data-ratio="0.232" data-s="300,640" src="/upload/329db5dd621ea55f07bd949a1254b04c.jpg" data-type="jpeg" data-w="750" style="margin: 0px;"></span></a></p> <p style="margin-top: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong style="letter-spacing: 0.3px;"><span style="color: rgb(232, 10, 35);">2)聚合搜索&nbsp;Mr.Otter</span></strong></p> <p style="margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;">Mr.Otter 官网:http://xlrocket.com/mrotter/</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">Mr.Otter 能帮你节省大量的搜索时间。你只需要在 Mr.Otter 中输入一次关键词,点击不同标签即可一键切换到不同的搜索引擎。</p> <p><img data-ratio="0.6241666666666666" src="/upload/ec53180a69f8abb303e24548beab762a.png" data-type="png" data-w="2400" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">这其中可不仅仅有百度、</span>bing、搜狗这样的传统搜索引擎,图片、资讯、消费等 7 个大分类中包含了超过 30 个小分类、超过 200 个垂直网站的资源可以搜索。这些搜索还不够的话,你还可以添加自定义的搜索站点。</p> <p><img data-ratio="0.6241666666666666" src="/upload/9b4a39fc58504243d12ce3d76f8d7a60.png" data-type="png" data-w="2400" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">Mr.Otter 有 macOS 和 Windows 两个版本的客户端,可以到官网下载。进入效率火箭活动页面还可以获得专属邀请码。链接:http://xlrocket.com/ptnr/xlr-otter/</p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;text-align: left;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="margin-bottom: -2px;padding: 3px 8px;font-size: 12px;font-family: 微软雅黑;color: rgb(255, 255, 255);background-color: rgb(232, 10, 35);">关联阅读</span></strong></p> <p style="margin-top: -2px;color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5MTMyNTYzNA==&amp;mid=2663976199&amp;idx=1&amp;sn=0391d2bea357140090c8691f49775b00&amp;chksm=8b43b8aabc3431bc3a6dfc18e5f9f80029edd72f59fccaf8b8db6c1614616c5745589c344987&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="129" data-ratio="0.232" data-s="300,640" src="/upload/6bd7d24991e3a8ac89facd4c28b049d3.jpg" data-type="jpeg" data-w="750" style="margin: 0px;width: 558px;height: 129px;"></span></a></p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong style="color: rgb(232, 10, 35);letter-spacing: 0.3px;text-align: justify;">3)如何优雅快速地处理 PDF 文件?</strong><br></p> <p style="margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">LightPDF 官网:https://lightpdf.com/zh/</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">PDF 文件在办公当中相当常用,想要优雅快速地处理 PDF 文件,你可以用到 LightPDF 这个工具。</p> <p><img data-ratio="0.5586658725431805" src="/upload/a2668e66048b7e84407c71f59163934b.png" data-type="png" data-w="3358" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">LightPDF 有超过 20 种 PDF 处理的相关功能,包括 PDF 的压缩、合并,PDF 与各种格式之间的转换等。LightPDF 还提供了免费客户端的下载,在客户端你可以处理 5MB 以上的文件,而在网页端的众多工具里,大都只能处理 5MB 以下的文件。</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong style="letter-spacing: 0.3px;"><span style="color: rgb(232, 10, 35);">4)一个浏览器就是一台电脑</span></strong><br></p> <p style="margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">UZER 官网:https://uzer.me/</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">要紧急处理图片,但是公司电脑还没装好</span>&nbsp;Photoshop 怎么办?你可以直接用浏览器打开这个免费的云电脑 uzer.me。</p> <p><img data-ratio="0.6530815109343936" src="/upload/52b1687e984b89284b163d45ba3864c1.png" data-type="png" data-w="2012" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">它提供了</span>&nbsp;Photoshop、Illustrator、AutoCAD、Word、PPT、Excel 等 13 个云应用,你只需要将你的文件上传至「文档库」中即可用这台「云电脑」打开。</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">打开应用之后,你就能轻松的操作软件,鼠标和键盘的操作也都跟本地的软件相同。</span></p> <p><img data-ratio="0.5580702799285289" src="/upload/b807dea220abc71884849650c352425d.png" data-type="png" data-w="3358" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><br></p> <h2 style="margin-top: 30px;margin-bottom: 30px;font-weight: bold;font-size: 18px;line-height: 2;color: rgb(255, 255, 255);"><span style="background: rgb(232, 10, 35);padding: 0.3em 10px;-webkit-box-decoration-break: clone;">不会&nbsp;PS 也能轻松做设计</span></h2> <p style="margin-top: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="color: rgb(232, 10, 35);">ARKIE,AI 帮你一键生成海报</span></strong></p> <p style="margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);">官网:https://www.arkie.cn/</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">虽然你的老板不会让你每天做</span>&nbsp;100 张海报出来,但是用 ARKIE 你真的可以每天做 100 张海报。</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><img data-ratio="0.5572109654350417" src="/upload/b1884de1face562ddd7b3a19f5c77cec.png" data-type="png" data-w="3356" height="auto" width="auto" style="text-align: left;-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">在</span>&nbsp;ARKIE 中输入少量关键字,即可自动生成若干种不同样式的海报图片,按照需求简单修改即可下载图片了。</p> <p><img data-ratio="0.751219512195122" src="/upload/25a91306db7c6e1850a89528e165e6c.png" data-type="png" data-w="2460" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p><img data-ratio="0.6607828089025326" src="/upload/e41fb50d6e1ec0ebe243202b80d7e200.png" data-type="png" data-w="2606" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><br></p> <h2 style="margin-top: 30px;margin-bottom: 30px;font-weight: bold;font-size: 18px;line-height: 2;color: rgb(255, 255, 255);"><span style="background: rgb(232, 10, 35);padding: 0.3em 10px;-webkit-box-decoration-break: clone;">Chrome 插件合辑</span></h2> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">在以往的文章中,我们给大家介绍过非常多好用的</span>&nbsp;Chrome 插件,也许有不少实用的功能都被你遗忘在了角落,趁今年开工之前赶紧来看看,说不定哪个就会用到。</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="color: rgb(232, 10, 35);">1)这 15 个 Chrome 扩展能让你的浏览器更加强大</span></strong></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">插件目录:</span></p> <ul class="list-paddingleft-2" style=""> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">Search by Image</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">View Image</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">View or download Google Image</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">图片助手</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">哔哩哔哩下载助手</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);"><span style="font-size: 13px;font-family: 微软雅黑;">下载</span>+</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">Infinity</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">RaterFox</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">Astrolabe</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">OneTab</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">Search to Play the Song</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">简悦</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">类似的网站</span></p></li> <li><p><span style="font-size: 13px;color: rgb(136, 136, 136);">Dark Reader</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">沙拉查词</span></p></li> </ul> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">关注极客之选公众号(</span>GeekChoice),回复关键词「Chrome 扩展」,获取全部工具地址。</p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;text-align: left;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="margin-bottom: -2px;padding: 3px 8px;font-size: 12px;font-family: 微软雅黑;color: rgb(255, 255, 255);background-color: rgb(232, 10, 35);">关联阅读</span></strong></p> <p style="margin-top: -2px;color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5MTMyNTYzNA==&amp;mid=2663975670&amp;idx=1&amp;sn=3fe71dc2e0ef4e7868d6a40b0cc81966&amp;chksm=8b43bf5bbc34364d806d18a5c581859efe5150dc35ffde9d14a063d7d89e5708f0f1c771649d&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="129" data-ratio="0.232" data-s="300,640" src="/upload/2d72eb1e7e37bbc9db95849187eb2467.jpg" data-type="jpeg" data-w="750" style="height: 129px;width: 558px;margin: 0px;"></span></a></p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><br style="letter-spacing: 0.3px;text-align: left;"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="color: rgb(232, 10, 35);">2)YouTube 学习伴侣</span></strong></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">插件目录:</span></p> <ul class="list-paddingleft-2" style=""> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">频道分组:</span><span style="font-size: 13px;color: rgb(136, 136, 136);">PocketTube</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">音乐模式:</span><span style="font-size: 13px;color: rgb(136, 136, 136);">Music Mode for YouTube</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">双语字幕:</span><span style="font-size: 13px;color: rgb(136, 136, 136);">Dualsub</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">视频内搜索:</span><span style="font-size: 13px;color: rgb(136, 136, 136);">Invideo for Youtube</span></p></li> <li><p><span style="font-family: 微软雅黑;font-size: 13px;color: rgb(136, 136, 136);">记笔记:</span><span style="font-size: 13px;color: rgb(136, 136, 136);">Rocket Note</span></p></li> </ul> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="font-family:微软雅黑;">关注极客之选微信公众号(</span>GeekChoice),后台回复「YouTube」,获取所有工具下载链接。</p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;text-align: left;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="margin-bottom: -2px;padding: 3px 8px;font-size: 12px;font-family: 微软雅黑;color: rgb(255, 255, 255);background-color: rgb(232, 10, 35);">关联阅读</span></strong></p> <p style="margin-top: -2px;color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5MTMyNTYzNA==&amp;mid=2663977453&amp;idx=1&amp;sn=438252c010b4acb0a4b8eb2afceed176&amp;chksm=8b43a440bc342d56abbca043a807a627fb4899555e63ebed53b46c180809f0cbdf0274c085a8&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="129" data-ratio="0.232" data-s="300,640" src="/upload/b0d5f0f67fec759cc1d892a7e74cc63b.jpg" data-type="jpeg" data-w="750" style="height: 129px;width: 558px;margin: 0px;"></span></a></p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><br style="letter-spacing: 0.3px;text-align: left;"></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="color: rgb(232, 10, 35);">3)打造你的专属右键</span></strong></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;font-size: 15px;text-align: justify;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><span style="letter-spacing: 0.3px;font-family: 微软雅黑;">通过</span><span style="letter-spacing: 0.3px;font-family: 微软雅黑;">「右键搜」</span><span style="letter-spacing: 0.3px;font-family: 微软雅黑;">插件,你可以给你的右键菜单增加更多功能,覆盖日常使用浏览器的各种操作。</span><span style="letter-spacing: 0.3px;"></span></p> <p><img data-ratio="0.5382568196939455" src="/upload/304874525c42ca2a0e6c5f4a671af546.png" data-type="png" data-w="1503" height="auto" style="-webkit-tap-highlight-color: transparent;vertical-align: bottom;width: 700px;margin: 5px auto;font-size: 12px;color: rgb(172, 172, 172);border-color: rgb(222, 222, 222);border-style: solid;border-width: 1px;" width="auto"></p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;text-align: left;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);"><strong><span style="margin-bottom: -2px;padding: 3px 8px;font-size: 12px;font-family: 微软雅黑;color: rgb(255, 255, 255);background-color: rgb(232, 10, 35);">关联阅读</span></strong></p> <p style="margin-top: -2px;color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5MTMyNTYzNA==&amp;mid=2663976859&amp;idx=2&amp;sn=9cc9963aecc71664eb9f483b4526dd09&amp;chksm=8b43ba36bc343320e93cc7ebf9b078d1fac0233e848977a2555d60a12ca601940db6d0941a98&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="129" data-ratio="0.232" data-s="300,640" src="/upload/f1475b272aa25c08837c14cb359819b.jpg" data-type="jpeg" data-w="750" style="height: 129px;width: 558px;margin: 0px;"></span></a></p> <p style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: center;"><br></p> <p style="margin-top: 1.4em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: center;"><span style="font-size: 14px;"><strong><span style="font-size: 14px;color: rgb(232, 10, 35);">你开始远程办公了吗?</span></strong></span></p> <p style="font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;white-space: normal;font-size: 15px;line-height: 1.8em;letter-spacing: 0.3px;color: rgb(40, 40, 40);text-align: center;"><span style="font-size: 14px;"><strong><span style="font-size: 14px;color: rgb(232, 10, 35);">留言跟我们一起聊聊~</span></strong></span></p> <p style="margin-top: 1.4em;margin-bottom: 1em;font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;text-align: left;white-space: normal;color: rgb(40, 40, 40);font-size: 15.5px;line-height: 1.7em;letter-spacing: 0.3px;"><img class="rich_pages" data-ratio="0.2388888888888889" src="/upload/f5252b2e94222b2633167c99a781dad3.gif" data-type="gif" data-w="1080" style="letter-spacing: 0.3px;color: rgb(0, 0, 0);font-size: 14px;text-align: center;"><br></p> <p style="color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><img class="rich_pages" data-ratio="0.16266666666666665" data-s="300,640" src="/upload/73d56e48b2c3c5ebdadd6faa13afe174.png" data-type="png" data-w="1500"></p> <p style="color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5MTMyNTYzNA==&amp;mid=2663977819&amp;idx=1&amp;sn=696ab7791d402a866f75a51cff45a2c2&amp;chksm=8b43a6f6bc342fe02ab6eb0ed3c8bfffe8d830c7c4a6e7331ca9305b6543f6dd971eaa50823b&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="129" data-ratio="0.232" data-s="300,640" src="/upload/5d6f906c60cc59177fa213ae0021d6d4.jpg" data-type="jpeg" data-w="750" style="height: 129px;width: 558px;margin: 0px;"></span></a></p> <p style="color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5MTMyNTYzNA==&amp;mid=2663977660&amp;idx=1&amp;sn=ea0fd7ec1d31becc95a6480e2978a190&amp;chksm=8b43a711bc342e07a67ede319ae6c895bfa80c8d7cca868dabee279b4cd423b34b1e5c780309&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;right: auto;bottom: auto;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="556" data-cropsely1="0" data-cropsely2="129" data-ratio="0.232" data-s="300,640" src="/upload/356eefbe88105c6f0bb11aee80dd7f06.jpg" data-type="jpeg" data-w="750" style="height: 129px;width: 556px;top: auto;left: auto;right: auto;bottom: auto;"></span></a><img class="rich_pages" data-cropselx1="0" data-cropselx2="554" data-cropsely1="0" data-cropsely2="194" data-ratio="0.35" data-s="300,640" src="/upload/9f877eec001ff8d94b93fcd3099d648d.gif" data-type="gif" data-w="640" style="height: 194px;width: 554px;border-color: rgb(226, 226, 226);border-style: solid;border-width: 1px;"></p> <p style="color: rgb(0, 0, 0);font-family: Helvetica, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, Roboto, &quot;Microsoft Yahei&quot;, sans-serif;font-size: 14px;white-space: normal;text-align: center;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="554" data-cropsely1="0" data-cropsely2="51" data-ratio="0.09247648902821316" data-s="300,640" src="/upload/1fd96c13b5e3c779e377df5a183531bb.gif" data-type="gif" data-w="638" style="height: 51px;width: 554px;"></p> </section>

微信高并发抢红包秒杀实战案例

作者:微信小助手

<p style="max-width: 100%;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;min-height: 1em;background-color: rgb(255, 255, 255);text-size-adjust: auto;font-size: 15px;word-spacing: 2px;text-align: center;margin-left: 0px;margin-right: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">点击▲关注 “</span><span style="max-width: 100%;font-size: 14px;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;letter-spacing: 0.544px;color: rgb(0, 128, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">爪哇笔记</span><span style="max-width: 100%;font-size: 14px;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">”&nbsp; &nbsp;给公众号标星置顶</span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;min-height: 1em;font-size: 16px;background-color: rgb(255, 255, 255);text-align: center;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;margin-left: 0px;margin-right: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">更多精彩 第一时间直达</span><strong style="font-size: 18px;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;"><img data-backh="316" data-backw="562" data-ratio="0.5625" src="/upload/e33f80bb3db3e6eb3a06d212a0e27083.jpg" data-type="jpeg" data-w="720" style="width: 100%;height: auto;"></strong></p> <h2 style="box-sizing: border-box;margin: 2rem 0px 1rem;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><span style="font-size: 18px;"><strong>前言</strong></span></h2> <section style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin: 15px 0px;line-height: 2em;text-align: start;white-space: normal;"> 群里有小伙伴咨询微信红包的架构,对于我来说,显然是不知道的,但是写一个相对高并发的抢红包案例还是完全可以的。 </section> <h2 style="box-sizing: border-box;margin: 2rem 0px 1rem;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><span style="font-size: 18px;"><strong>架构流程</strong></span></h2> <section style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin: 15px 0px;line-height: 2em;text-align: start;white-space: normal;"> <img data-ratio="0.41025641025641024" src="/upload/49d45f726e50a38f0d94ce02a437e6a3.png" data-type="png" data-w="1560" style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;"> </section> <h2 style="box-sizing: border-box;margin: 2rem 0px 1rem;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><span style="font-size: 18px;"><strong>架构设计</strong></span></h2> <ul style="margin-left: 0px;margin-right: 0px;" class="list-paddingleft-2"> <li><p style="box-sizing: border-box;font-size: 14px;color: rgb(62, 62, 62);margin-bottom: 15px;line-height: 2em;">老板发红包,此时缓存初始化红包个数,红包金额(单位分),并异步入库。</p></li> <li><p style="box-sizing: border-box;font-size: 14px;color: rgb(62, 62, 62);margin-bottom: 15px;line-height: 2em;">抢红包,判断缓存剩余红包金额,剩余金额大于零则抢到红包,否则手慢了,红包派完了</p></li> <li><p style="box-sizing: border-box;font-size: 14px;color: rgb(62, 62, 62);margin-bottom: 15px;line-height: 2em;">拆红包,根据&nbsp;<code style="box-sizing: border-box;font-family: Consolas, Menlo, Courier, monospace;font-size: 12px;background: white;padding: 2px 7px;border-radius: 2px;line-height: 12px;border-width: 0px !important;border-style: initial !important;border-color: initial !important;"><span style="box-sizing: border-box;color: rgb(153, 0, 0);font-family: Monaco, Consolas, &quot;Andale Mono&quot;, &quot;DejaVu Sans Mono&quot;, monospace;font-size: 11.4px;line-height: 15.96px;white-space: pre-wrap;">redPacketId</span></code>&nbsp;获取分布式锁,如果获取到锁,红包个数减一,如果剩余红包个数大于零抢红包成功、否则失败。成功则计算红包金额,缓存总红包金额减去抢到的红包金额,异步入库、异步到账。</p></li> <li><p style="box-sizing: border-box;font-size: 14px;color: rgb(62, 62, 62);margin-bottom: 15px;line-height: 2em;">若获取分布式锁失败,使用&nbsp;<code style="box-sizing: border-box;font-family: Consolas, Menlo, Courier, monospace;font-size: 12px;background: white;padding: 2px 7px;border-radius: 2px;line-height: 12px;border-width: 0px !important;border-style: initial !important;border-color: initial !important;"><span style="box-sizing: border-box;color: rgb(153, 0, 0);font-family: Monaco, Consolas, &quot;Andale Mono&quot;, &quot;DejaVu Sans Mono&quot;, monospace;font-size: 11.4px;line-height: 15.96px;white-space: pre-wrap;">Redis</span></code>的&nbsp;<code style="box-sizing: border-box;font-family: Consolas, Menlo, Courier, monospace;font-size: 12px;background: white;padding: 2px 7px;border-radius: 2px;line-height: 12px;border-width: 0px !important;border-style: initial !important;border-color: initial !important;"><span style="box-sizing: border-box;color: rgb(153, 0, 0);font-family: Monaco, Consolas, &quot;Andale Mono&quot;, &quot;DejaVu Sans Mono&quot;, monospace;font-size: 11.4px;line-height: 15.96px;white-space: pre-wrap;">decr</span></code>命令对红包个数加一。</p></li> </ul> <h2 style="box-sizing: border-box;margin: 2rem 0px 1rem;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><span style="font-size: 18px;"><strong>数据库设计</strong></span></h2> <ul style="margin-left: 0px;margin-right: 0px;" class="list-paddingleft-2"> <li><p><span style="font-size: 14px;">红包信息表</span></p></li> </ul> <pre style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);text-align: start;line-height: 12px;"> <ol class="list-paddingleft-2" style="margin-left: 0px;margin-right: 0px;"> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);">CREATE TABLE </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`red_racket`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`id`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> bigint</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">20</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL AUTO_INCREMENT COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'自增主键'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`red_packet_id`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> bigint</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">20</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'红包唯一ID'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`total_amount`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">11</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'红包金额单位分'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`total_packet`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">11</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'红包个数'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`type`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">11</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'红包类型'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`create_time`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> datetime DEFAULT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'创建时间'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`version`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">11</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'版本号'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> PRIMARY KEY </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`id`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> ENGINE</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">InnoDB</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> AUTO_INCREMENT</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">2</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> DEFAULT CHARSET</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">utf8 CHECKSUM</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">1</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> DELAY_KEY_WRITE</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">1</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> ROW_FORMAT</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">DYNAMIC COMMENT</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'红包信息表'</span></code></p></li> </ol></pre> <ul style="margin-left: 0px;margin-right: 0px;" class="list-paddingleft-2"> <li><p><span style="font-size: 14px;">抢红包记录表</span></p></li> </ul> <pre style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);text-align: start;line-height: 12px;"> <ol class="list-paddingleft-2" style="margin-left: 0px;margin-right: 0px;"> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);">CREATE TABLE </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`red_packet_record`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`id`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> bigint</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">20</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL AUTO_INCREMENT COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'自增主键'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`amount`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">11</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'抢到红包的金额'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`red_packet_id`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> bigint</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">20</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'红包ID'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`uid`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">11</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> NOT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'抢到红包用户的用户标识'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`create_time`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> datetime DEFAULT NULL COMMENT </span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'创建时间'</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> PRIMARY KEY </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">`id`</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> ENGINE</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">InnoDB</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> AUTO_INCREMENT</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">10</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> DEFAULT CHARSET</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">utf8 CHECKSUM</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">1</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> DELAY_KEY_WRITE</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">1</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> ROW_FORMAT</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">DYNAMIC COMMENT</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">'抢红包记录表'</span></code></p></li> </ol></pre> <h2 style="box-sizing: border-box;margin: 2rem 0px 1rem;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><strong><span style="font-size: 18px;">代码案例</span></strong></h2> <section style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin: 15px 0px;line-height: 2em;text-align: start;white-space: normal;"> 老板发了10个红包一共200人民币,100个人同时抢红包,伪代码分别为拆红包和抢红包相关业务逻辑。 </section> <section style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin: 15px 0px;line-height: 2em;text-align: start;white-space: normal;"> 模拟抢红包伪代码: </section> <pre style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);text-align: start;line-height: 12px;"> <ol class="list-paddingleft-2" style="margin-left: 0px;margin-right: 0px;"> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 抢红包 拆红包 抢到不一定能拆到</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * @param redPacketId</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * @return</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">@ApiOperation</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">value</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"抢红包二"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">nickname</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"爪哇笔记"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">@PostMapping</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"/startTwo"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">public</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Result</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> startTwo</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">long</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">){</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> skillNum </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">100</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">final</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">CountDownLatch</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> latch </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">new</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">CountDownLatch</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">skillNum</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span><span style="box-sizing: border-box;">//N个抢红包</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 初始化红包数据,抢红包拦截</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redisUtil</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">cacheValue</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">+</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"-num"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">10</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 初始化红包金额,单位为分</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redisUtil</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">cacheValue</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">+</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"-money"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">20000</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 模拟100个用户抢10个红包</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">for</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">1</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">&lt;=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">skillNum</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">++){</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> userId </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Runnable</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> task </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">()</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-&gt;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">{</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 抢红包 判断剩余金额</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Integer</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> money </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Integer</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redisUtil</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">getValue</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">+</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"-money"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">if</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">money</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">&gt;</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">0</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">){</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing:

面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;" data-mpa-powered-by="yiban.io"> <p data-darkmode-bgcolor="rgb(36, 36, 36)" data-style="white-space: normal; font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif; letter-spacing: 0.544px; background-color: rgb(255, 255, 255); text-align: center;" class="js_darkmode__1" style="white-space: normal;font-size: 16px;max-width: 100%;background-color: rgb(255, 255, 255);min-height: 1em;color: rgba(255, 255, 255, 0.8);font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;text-align: center;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;letter-spacing: 0.544px;widows: 1;word-spacing: 2px;caret-color: rgb(51, 51, 51);font-family: Optima-Regular, PingFangTC-light;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;color: rgb(127, 127, 127);font-size: 14px;line-height: 1.75em;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">点击上方“</span><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;font-size: 14px;line-height: 1.75em;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;color: rgb(0, 176, 240);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">JavaGuide</span></span><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;color: rgb(127, 127, 127);font-size: 14px;line-height: 1.75em;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">”,选择“</span></span><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;letter-spacing: 0.544px;widows: 1;word-spacing: 2px;caret-color: rgb(51, 51, 51);font-family: Optima-Regular, PingFangTC-light;color: rgb(136, 136, 136);font-size: 14px;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">设为星标</span><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;letter-spacing: 0.544px;widows: 1;word-spacing: 2px;caret-color: rgb(51, 51, 51);font-family: Optima-Regular, PingFangTC-light;color: rgb(127, 127, 127);font-size: 14px;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">”</span><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="max-width: 100%;letter-spacing: 0.544px;color: rgb(127, 127, 127);font-size: 14px;line-height: 1.75em;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></p> <p style="white-space: normal;color: rgb(0, 0, 0);font-family: PingFangSC-Light;font-size: 16px;text-align: center;"><span data-darkmode-bgcolor="rgb(36, 36, 36)" style="letter-spacing: 0.544px;widows: 1;word-spacing: 2px;caret-color: rgb(51, 51, 51);font-family: Optima-Regular, PingFangTC-light;max-width: 100%;color: rgb(127, 127, 127);font-size: 14px;line-height: 1.75em;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">回复”面试突击“获取Github 68k+ Star项目精华集合而成的《Java面试突击》</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.66640625" data-s="300,640" src="/upload/80dea9dc99c031d317227725c3ae100f.jpg" data-type="jpeg" data-w="1280" style=""></p> <p><br mpa-from-tpl="t"></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 24px;margin-top: 20px;margin-right: 10px;"><span style="font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);font-family: Arial, Helvetica, sans-serif;">初识 MyBatis</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。</span></p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 20px;"><span style="font-size: 14px;color: rgb(165, 213, 93);font-family: Arial, Helvetica, sans-serif;">MyBatis 的特点</span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">那么 MyBatis 具有什么特点呢?或许我们可以从如下几个方面来描述</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 中的 SQL 语句和主要业务代码分离,我们一般会把 MyBatis 中的 SQL 语句统一放在 XML 配置文件中,便于统一维护。</span> </section></li> </ul> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;"><img class="rich_pages" data-ratio="0.7790476190476191" data-s="300,640" src="/upload/2944ffc4d5811827e2264a0c5e96faf8.png" data-type="png" data-w="1050" style="color: rgb(0, 0, 0);font-family: PingFangSC-Light;font-size: 16px;text-align: center;white-space: normal;"></span> </section> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">解除 SQL 与程序代码的耦合,通过提供 DAO 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。SQL 和代码的分离,提高了可维护性。</span> </section></li> </ul> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.23220973782771537" data-s="300,640" src="/upload/d9bcc743a47c1b165a957f706896a111.png" data-type="png" data-w="1602" style=""></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 比较简单和轻量</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">本身就很小且简单。没有任何第三方依赖,只要通过配置 jar 包,或者如果你使用 Maven 项目的话只需要配置 Maven 以来就可以。易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">屏蔽样板代码</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 回屏蔽原始的 JDBC 样板代码,让你把更多的精力专注于 SQL 的书写和属性-字段映射上。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">编写原生 SQL,支持多表关联</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 最主要的特点就是你可以手动编写 SQL 语句,能够支持多表关联查询。</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.46411483253588515" data-s="300,640" src="/upload/b804eae78ba7c34ca9c6bff09163db6e.png" data-type="png" data-w="1254" style=""></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">提供映射标签,支持对象与数据库的 ORM 字段关系映射</span> </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;border-left: none;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(254, 238, 237);background: rgb(0, 0, 0);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <span style="color: rgb(255, 255, 255);font-size: 4em;line-height: 1em;font-weight: 700;font-family: Arial, Helvetica, sans-serif;"></span> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 13px;display: inline;"><span style="font-family: Arial, Helvetica, sans-serif;">ORM 是什么?</span><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">对象关系映射(Object Relational Mapping,简称ORM)</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> ,是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。</span></p> <span style="float: right;color: rgb(255, 255, 255);font-size: 3em;line-height: 1em;font-family: Arial, Helvetica, sans-serif;">”</span> </blockquote> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">提供 XML 标签,支持编写动态 SQL。</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">你可以使用 MyBatis XML 标签,起到 SQL 模版的效果,减少繁杂的 SQL 语句,便于维护。</span></p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 20px;"><span style="font-size: 14px;color: rgb(165, 213, 93);font-family: Arial, Helvetica, sans-serif;">MyBatis 整体架构</span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 最上面是接口层,接口层就是开发人员在 Mapper 或者是 Dao 接口中的接口定义,是查询、新增、更新还是删除操作;中间层是数据处理层,主要是配置 Mapper -&gt; XML 层级之间的参数映射,SQL 解析,SQL 执行,结果映射的过程。上述两种流程都由基础支持层来提供功能支撑,基础支持层包括连接管理,事务管理,配置加载,缓存处理等。</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.6684005201560468" data-s="300,640" src="/upload/daa6f882ab240b0232076aabcc7b5b01.png" data-type="png" data-w="1538" style=""></p> <h4 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;"><span style="font-family: Arial, Helvetica, sans-serif;">接口层<br></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">在不与Spring 集成的情况下,使用 MyBatis 执行数据库的操作主要如下:</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: rgb(255, 255, 255);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-family: Arial, Helvetica, sans-serif;">InputStream is = Resources.getResourceAsStream(<span style="color: rgb(196, 26, 22);line-height: 26px;">"myBatis-config.xml"</span>);<br>SqlSessionFactoryBuilder builder = <span style="color: rgb(170, 13, 145);line-height: 26px;">new</span> SqlSessionFactoryBuilder();<br>SqlSessionFactory factory = builder.build(is);<br>sqlSession = factory.openSession();<br></span></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">其中的</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSessionFactory</span></code><span style="font-family: Arial, Helvetica, sans-serif;">,</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSession</span></code><span style="font-family: Arial, Helvetica, sans-serif;">是 MyBatis 接口的核心类,尤其是 SqlSession,这个接口是MyBatis 中最重要的接口,这个接口能够让你执行命令,获取映射,管理事务。</span></p> <h4 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;"><span style="font-family: Arial, Helvetica, sans-serif;">数据处理层</span></h4> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;"><strong style="font-weight: border;color: rgb(248,57,41);">配置解析</strong><strong style="font-weight: border;color: rgb(248,57,41);"></strong></span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">在 Mybatis 初始化过程中,会加载 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">mybatis-config.xml</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">Configration</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> 对象中。之后,根据该对象创建SqlSessionFactory 对象。待 Mybatis 初始化完成后,可以通过 SqlSessionFactory 创建 SqlSession 对象并开始数据库操作。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;"><strong style="font-weight: border;color: rgb(248,57,41);">SQL 解析与 scripting 模块</strong><strong style="font-weight: border;color: rgb(248,57,41);"></strong></span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">Mybatis 实现的动态 SQL 语句,几乎可以编写出所有满足需要的 SQL。</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">Mybatis 中 scripting 模块会根据用户传入的参数,解析映射文件中定义的动态 SQL 节点,形成数据库能执行的SQL 语句。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;"><strong style="font-weight: border;color: rgb(248,57,41);">SQL 执行</strong><strong style="font-weight: border;color: rgb(248,57,41);"></strong></span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">SQL 语句的执行涉及多个组件,包括 MyBatis 的四大核心,它们是: </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">Executor</span></code><span style="font-family: Arial, Helvetica, sans-serif;">、</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">StatementHandler</span></code><span style="font-family: Arial, Helvetica, sans-serif;">、</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">ParameterHandler</span></code><span style="font-family: Arial, Helvetica, sans-serif;">、</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">ResultSetHandler</span></code><span style="font-family: Arial, Helvetica, sans-serif;">。SQL 的执行过程可以用下面这幅图来表示</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.854614412136536" src="/upload/a618badf9a5022a2ecd41f6d5ce193ba.png" data-type="png" data-w="1582" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: #84A1A8 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 层级结构各个组件的介绍(这里只是简单介绍,具体介绍在后面):</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSession</span></code> <span style="font-family: Arial, Helvetica, sans-serif;">:,它是 MyBatis 核心 API,主要用来执行命令,获取映射,管理事务。接收开发人员提供 Statement Id 和参数。并返回操作结果。</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">Executor</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> :执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成以及查询缓存的维护。</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">StatementHandler</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> : &nbsp;封装了JDBC Statement 操作,负责对 JDBC Statement 的操作,如设置参数、将Statement 结果集转换成 List 集合。</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">ParameterHandler</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> : &nbsp;负责对用户传递的参数转换成 JDBC Statement 所需要的参数。</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">ResultSetHandler</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> : 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">TypeHandler</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> : &nbsp;用于 Java 类型和 JDBC 类型之间的转换。</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">MappedStatement</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> : 动态 SQL 的封装</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSource</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> : &nbsp;表示从 XML 文件或注释读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的 SQL。</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">Configuration</span></code> <span style="font-family: Arial, Helvetica, sans-serif;">: &nbsp;MyBatis 所有的配置信息都维持在 Configuration 对象之中。</span> </section></li> </ul> <h4 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;"><span style="font-family: Arial, Helvetica, sans-serif;">基础支持层</span></h4> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">反射模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">Mybatis 中的反射模块,对 Java 反射进行了很好的封装,提供了简易的 API,方便上层调用,并且对反射操作进行了一系列的优化,比如,缓存了类的 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">元数据(MetaClass)</span></code><span style="font-family: Arial, Helvetica, sans-serif;">和对象的</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">元数据(MetaObject)</span></code><span style="font-family: Arial, Helvetica, sans-serif;">,提高了反射操作的性能。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">类型转换模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">Mybatis 的别名机制,能够简化配置文件,该机制是类型转换模块的主要功能之一。类型转换模块的另一个功能<strong style="font-weight: border;color: rgb(248,57,41);">是实现 JDBC 类型与 Java 类型的转换</strong>。在 SQL 语句绑定参数时,会将数据由 Java 类型转换成 JDBC 类型;在映射结果集时,会将数据由 JDBC 类型转换成 Java 类型。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">日志模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">在 Java 中,有很多优秀的日志框架,如 Log4j、Log4j2、slf4j 等。Mybatis 除了提供了详细的日志输出信息,还能够集成多种日志框架,其日志模块的主要功能就是集成第三方日志框架。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">资源加载模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">该模块主要封装了类加载器,确定了类加载器的使用顺序,并提供了加载类文件和其它资源文件的功能。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">解析器模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">该模块有两个主要功能:一个是封装了 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">XPath</span></code><span style="font-family: Arial, Helvetica, sans-serif;">,为 Mybatis 初始化时解析 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">mybatis-config.xml</span></code><span style="font-family: Arial, Helvetica, sans-serif;">配置文件以及映射配置文件提供支持;另一个为处理动态 SQL 语句中的占位符提供支持。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">数据源模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">Mybatis 自身提供了相应的数据源实现,也提供了与第三方数据源集成的接口。数据源是开发中的常用组件之一,很多开源的数据源都提供了丰富的功能,如连接池、检测连接状态等,选择性能优秀的数据源组件,对于提供ORM 框架以及整个应用的性能都是非常重要的。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">事务管理模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">一般地,Mybatis 与 Spring 框架集成,由 Spring 框架管理事务。但 Mybatis 自身对数据库事务进行了抽象,提供了相应的事务接口和简单实现。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">缓存模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">Mybatis 中有</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">一级缓存</span></code><span style="font-family: Arial, Helvetica, sans-serif;">和</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">二级缓存</span></code><span style="font-family: Arial, Helvetica, sans-serif;">,这两级缓存都依赖于缓存模块中的实现。但是需要注意,这两级缓存与Mybatis 以及整个应用是运行在同一个 JVM 中的,共享同一块内存,如果这两级缓存中的数据量较大,则可能影响系统中其它功能,所以需要缓存大量数据时,优先考虑使用 Redis、Memcache 等缓存产品。</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">Binding 模块</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">在调用 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSession</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> 相应方法执行数据库操作时,需要制定映射文件中定义的 SQL 节点,如果 SQL 中出现了拼写错误,那就只能在运行时才能发现。为了能尽早发现这种错误,Mybatis 通过 Binding 模块将用户自定义的Mapper 接口与映射文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。注意,在开发中,我们只是创建了 Mapper 接口,而并没有编写实现类,这是因为 Mybatis 自动为 Mapper 接口创建了动态代理对象。</span></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 24px;margin-top: 20px;margin-right: 10px;"><span style="font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);font-family: Arial, Helvetica, sans-serif;">MyBatis 核心组件</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">在认识了 MyBatis 并了解其基础架构之后,下面我们来看一下 MyBatis 的核心组件,就是这些组件实现了从 SQL 语句到映射到 JDBC 再到数据库字段之间的转换,执行 SQL 语句并输出结果集。首先来认识 MyBatis 的第一个核心组件</span></p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 20px;"><span style="font-size: 14px;color: rgb(165, 213, 93);font-family: Arial, Helvetica, sans-serif;">SqlSessionFactory</span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">对于任何框架而言,在使用该框架之前都要经历过一系列的初始化流程,MyBatis 也不例外。MyBatis 的初始化流程如下</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: rgb(255, 255, 255);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-family: Arial, Helvetica, sans-serif;">String resource = <span style="color: rgb(196, 26, 22);line-height: 26px;">"org/mybatis/example/mybatis-config.xml"</span>;<br>InputStream inputStream = Resources.getResourceAsStream(resource);<br>SqlSessionFactory sqlSessionFactory = <span style="color: rgb(170, 13, 145);line-height: 26px;">new</span> SqlSessionFactoryBuilder().build(inputStream);<br>sqlSessionFactory.openSession();<br></span></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">上述流程中比较重要的一个对象就是</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSessionFactory</span></code><span style="font-family: Arial, Helvetica, sans-serif;">,SqlSessionFactory 是 MyBatis 框架中的一个接口,它主要负责的是</span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">MyBatis 框架初始化操作</span> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <span style="font-family: Arial, Helvetica, sans-serif;">为开发人员提供</span> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSession</span></code> <span style="font-family: Arial, Helvetica, sans-serif;"> 对象</span> </section></li> </ul> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.4968632371392723" data-s="300,640" src="/upload/cef2b850918eaeebc2ba9677bd62fca9.png" data-type="png" data-w="1594" style=""></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSessionFactory</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> 有两个实现类,一个是 SqlSessionManager 类,一个是 DefaultSqlSessionFactory 类<br></span></p> <ul data-tool="mdnice编辑器" style="" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: black;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">DefaultSqlSessionFactory</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> : SqlSessionFactory 的默认实现类,是真正生产会话的工厂类,这个类的实例的生命周期是全局的,它只会在首次调用时生成一个实例(单例模式),就一直存在直到服务器关闭。</span></p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: black;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSessionManager :已被废弃,原因大概是: SqlSessionManager 中需要维护一个自己的线程池,而使用MyBatis 更多的是要与 Spring 进行集成,并不会单独使用,所以维护自己的 ThreadLocal 并没有什么意义,所以 SqlSessionManager 已经不再使用。</span></p> </section></li> </ul> <h4 style="margin-top: 40px;margin-bottom: 20px;color: rgb(0, 0, 0);font-weight: bold;font-size: 18px;font-family: PingFangSC-Light;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSessionFactory 的执行流程</span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">下面来对 SqlSessionFactory 的执行流程来做一个分析<br></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">首先第一步是 SqlSessionFactory 的创建</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: rgb(255, 255, 255);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSessionFactory sqlSessionFactory = <span style="color: rgb(170, 13, 145);line-height: 26px;">new</span> SqlSessionFactoryBuilder().build(inputStream);<br></span></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">从这行代码入手,首先创建了一个 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">SqlSessionFactoryBuilder</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> 工厂,这是一个建造者模式的设计思想,由 builder 建造者来创建 SqlSessionFactory 工厂</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">然后调用 SqlSessionFactoryBuilder 中的 </span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">build</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> 方法传递一个</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">InputStream</span></code><span style="font-family: Arial, Helvetica, sans-serif;"> 输入流,Inputstream 输入流中就是你传过来的配置文件 mybatis-config.xml,SqlSessionFactoryBuilder 根据传入的 InputStream 输入流和</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">environment</span></code><span style="font-family: Arial, Helvetica, sans-serif;">、</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">properties</span></code><span style="font-family: Arial, Helvetica, sans-serif;">属性创建一个</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">XMLConfigBuilder</span></code><span style="font-family: Arial, Helvetica, sans-serif;">对象。SqlSessionFactoryBuilder 对象调用XMLConfigBuilder 的</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">parse()</span></code><span style="font-family: Arial, Helvetica, sans-serif;">方法,流程如下。</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.6073619631901841" data-s="300,640" src="/upload/8ec837247ef02c730ab225e4183db67e.png" data-type="png" data-w="1630" style=""></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="font-family: Arial, Helvetica, sans-serif;">XMLConfigBuilder 会解析</span><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-family: Arial, Helvetica, sans-serif;">/configuration</span></code><span style="font-family: Arial, Helvetica, sans-serif;">标签,configuration 是 MyBatis 中最重要的一个标签,下面流程会介绍 Configuration 标签。<br></span></p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;border-left: none;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(254, 238, 237);background: rgb(0, 0, 0);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <span style="color: rgb(255, 255, 255);font-size: 4em;line-height: 1em;font

推荐一款高效的处理延迟任务神器

作者:微信小助手

<p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-size-adjust: auto;font-size: 15px;word-spacing: 2px;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">点击▲关注 “</span><span style="max-width: 100%;font-size: 14px;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;letter-spacing: 0.544px;color: rgb(0, 128, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">爪哇笔记</span><span style="max-width: 100%;font-size: 14px;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">”&nbsp; &nbsp;给公众号标星置顶</span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;font-size: 16px;background-color: rgb(255, 255, 255);text-align: center;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">更多精彩 第一时间直达</span><strong style="font-size: 18px;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;"><img src="/upload/1f961b6b4cecbcdb2fb179d54b82ff9d.jpg" data-type="jpeg" data-ratio="0.562037037037037" data-w="1080"></strong></p> <h2 style="margin-top: 2rem;margin-bottom: 1rem;white-space: normal;box-sizing: border-box;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;"><span style="font-size: 18px;"><strong>时间轮算法</strong></span></h2> <p style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;text-align: start;white-space: normal;">时间轮是一种高效、低延迟的调度数据结构。其在Linux内核中广泛使用,是Linux内核定时器的实现方法和基础之一。按使用场景,大致可以分为两种时间轮:原始时间轮和分层时间轮。分层时间轮是原始时间轮的升级版本,来应对时间“槽”数量比较大的情况,对内存和精度都有很高要求的情况。延迟任务的场景一般只需要用到原始时间轮就可以了。</p> <h2 style="box-sizing: border-box;margin-top: 2rem;margin-bottom: 1rem;color: rgb(21, 153, 87);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><span style="font-size: 18px;"><strong>代码案例</strong></span></h2> <p style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;text-align: start;white-space: normal;"><span style="color: rgb(85, 85, 85);font-family: Lato, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif;font-size: 14px;background-color: rgb(255, 255, 255);">推荐使用</span><code style="font-family: &quot;Input Mono&quot;, &quot;PT Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 14px;padding-right: 0.3em;padding-left: 0.3em;word-break: break-all;background: rgb(238, 238, 238);text-shadow: rgb(255, 255, 255) 0px 1px;color: rgb(85, 85, 85);white-space: normal;">Netty</code><span style="color: rgb(85, 85, 85);font-family: Lato, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif;font-size: 14px;background-color: rgb(255, 255, 255);">提供的</span><code style="font-family: &quot;Input Mono&quot;, &quot;PT Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 14px;padding-right: 0.3em;padding-left: 0.3em;word-break: break-all;background: rgb(238, 238, 238);text-shadow: rgb(255, 255, 255) 0px 1px;color: rgb(85, 85, 85);white-space: normal;">HashedWheelTimer</code><span style="color: rgb(85, 85, 85);font-family: Lato, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif;font-size: 14px;background-color: rgb(255, 255, 255);">工具类来实现延迟任务。</span></p> <p style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;text-align: start;white-space: normal;">引入依赖:</p> <pre style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);text-align: start;line-height: 12px;"> <ol class="list-paddingleft-2" style=""> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;dependency&gt;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;groupId&gt;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">io.netty</span><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;/groupId&gt;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;artifactId&gt;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">netty-common</span><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;/artifactId&gt;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;version&gt;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">4.1.23.Final</span><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;/version&gt;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(204, 102, 102);">&lt;/dependency&gt;</span></code></p></li> </ol></pre> <p style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;text-align: start;white-space: normal;">红包过期队列信息:</p> <pre style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);text-align: start;line-height: 12px;"> <ol class="list-paddingleft-2" style=""> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 红包过期队列信息</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(178, 148, 187);">public</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">class</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">RedPacketTimerTask</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">implements</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">TimerTask</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">{</span></code></p></li> <li><p><br></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">private</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">static</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">final</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">DateTimeFormatter</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> F </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">DateTimeFormatter</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">ofPattern</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"yyyy-MM-dd HH:mm:ss.SSS"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><br></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 红包 ID</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">private</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">final</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">long</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span></code></p></li> <li><p><br></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 创建时间戳</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">private</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">final</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">long</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> timestamp</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span></code></p></li> <li><p><br></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">public</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">RedPacketTimerTask</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">long</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">{</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">this</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">redPacketId </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">this</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">timestamp </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">System</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">currentTimeMillis</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">();</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">}</span></code></p></li> <li><p><br></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">@Override</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">public</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">void</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> run</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Timeout</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> timeout</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">{</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">//异步处理任务</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">System</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">out</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">println</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">String</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">format</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"任务执行时间:%s,红包创建时间:%s,红包ID:%s"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">LocalDateTime</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">now</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">().</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">format</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">F</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">),</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">LocalDateTime</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">ofInstant</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Instant</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">ofEpochMilli</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">timestamp</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">),</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">ZoneId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">systemDefault</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">()).</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">format</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">F</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">),</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> redPacketId</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">));</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">}</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);">}</span></code></p></li> </ol></pre> <p style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;text-align: start;white-space: normal;">测试用例:</p> <pre style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);text-align: start;line-height: 12px;"> <ol class="list-paddingleft-2" style=""> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * 基于 netty 的时间轮算法 HashedWheelTimer 实现的延迟任务</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(178, 148, 187);">public</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">class</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">RedPacketHashedWheelTimer</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">{</span></code></p></li> <li><p><br></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">private</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">static</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">final</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">DateTimeFormatter</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> F </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">DateTimeFormatter</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">ofPattern</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"yyyy-MM-dd HH:mm:ss.SSS"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><br></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">public</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">static</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">void</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> main</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">String</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">[]</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> args</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">throws</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Exception</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">{</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">ThreadFactory</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> factory </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> r </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-&gt;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">{</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Thread</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> thread </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">new</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Thread</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">r</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> thread</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">setDaemon</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">true</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> thread</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">setName</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"RedPacketHashedWheelTimerWorker"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">return</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> thread</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">};</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;">/**</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * @param tickDuration - 每tick一次的时间间隔</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * @param unit - tickDuration 的时间单位</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * @param ticksPerWheel - 时间轮中的槽数</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> * @param leakDetection - 检查内存溢出</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;"> */</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Timer</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> timer </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">new</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">HashedWheelTimer</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">factory</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">1</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">TimeUnit</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">SECONDS</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">100</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">true</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">System</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">out</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">println</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">String</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">format</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(181, 189, 104);">"开始任务时间:%s"</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">LocalDateTime</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">now</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">().</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">format</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">F</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">)));</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">for</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">int</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">1</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">&lt;</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">10</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">;</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">++){</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">TimerTask</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> timerTask </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">=</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(178, 148, 187);">new</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">RedPacketTimerTask</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> timer</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">newTimeout</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">timerTask</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> i</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">TimeUnit</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">SECONDS</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">}</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Thread</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">sleep</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">(</span><span style="box-sizing: border-box;color: rgb(129, 162, 190);">Integer</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">.</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">MAX_VALUE</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">);</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">}</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);">}</span></code></p></li> </ol></pre> <p style="box-sizing: border-box;font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);margin-bottom: 15px;margin-top: 15px;line-height: 2em;text-align: start;white-space: normal;">打印任务执行日志:</p> <pre style="box-sizing: border-box;overflow: auto;font-family: Consolas, Menlo, Courier, monospace;font-size: 10px;background: rgb(29, 31, 33);border-width: 1px;border-style: solid;border-color: rgb(136, 136, 136);padding: 2px;color: rgb(80, 97, 109);text-align: start;line-height: 12px;"> <ol class="list-paddingleft-2" style=""> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);">开始任务时间:</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">2020</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">02</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">12</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">15</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">:</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">22</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">:</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">23.404</span></code></p></li> <li><p><code style="box-sizing: border-box;font-family: monospace, monospace;display: inline;max-width: initial;overflow: initial;line-height: 12px;overflow-wrap: normal;background-color: transparent;border-width: 0px;border-style: initial;border-color: initial;"><span style="box-sizing: border-box;color: rgb(197, 200, 198);">任务执行时间:</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">2020</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">02</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">12</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);"> </span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">15</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">:</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">22</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">:</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">25.410</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">,红包创建时间:</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">2020</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">02</span><span style="box-sizing: border-box;color: rgb(197, 200, 198);">-</span><span style="box-sizing: border-box;color: rgb(222, 147, 95);">12</span><span style="box-sizing: borde

代码生成器:IDEA 强大的 Live Templates

作者:微信小助手

<p style="white-space: normal;text-align: center;" data-mpa-powered-by="yiban.io"><span style="color: rgb(136, 136, 136);font-family: Georgia, &quot;Times New Roman&quot;, Times, &quot;Songti SC&quot;, serif;font-size: 14px;letter-spacing: 0.544px;">点击上方蓝色“</span><span style="font-family: Georgia, &quot;Times New Roman&quot;, Times, &quot;Songti SC&quot;, serif;font-size: 14px;letter-spacing: 0.544px;color: rgb(0, 128, 255);">程序猿DD</span><span style="color: rgb(136, 136, 136);font-family: Georgia, &quot;Times New Roman&quot;, Times, &quot;Songti SC&quot;, serif;font-size: 14px;letter-spacing: 0.544px;">”,选择“设为星标”</span><br></p> <p style="white-space: normal;text-align: center;"><span style="color: rgb(136, 136, 136);font-family: Georgia, &quot;Times New Roman&quot;, Times, &quot;Songti SC&quot;, serif;font-size: 14px;letter-spacing: 0.544px;">回复“</span><span style="font-family: Georgia, &quot;Times New Roman&quot;, Times, &quot;Songti SC&quot;, serif;font-size: 14px;letter-spacing: 0.544px;color: rgb(0, 128, 255);">资源</span><span style="color: rgb(136, 136, 136);font-family: Georgia, &quot;Times New Roman&quot;, Times, &quot;Songti SC&quot;, serif;font-size: 14px;letter-spacing: 0.544px;">”获取独家整理的学习资料!</span></p> <p style="margin-top: 10px;margin-bottom: 10px;white-space: normal;text-align: center;"><span style="color: rgb(136, 136, 136);font-family: Georgia, &quot;Times New Roman&quot;, Times, &quot;Songti SC&quot;, serif;font-size: 14px;letter-spacing: 0.544px;"><img data-backh="34" data-backw="540" data-ratio="0.0625" data-s="300,640" data-type="jpeg" data-w="640" width="100%" src="/upload/8c292e55ba5a23cb6ebc11f2a2c4fece.null" style="font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;visibility: visible !important;width: 654px !important;"></span></p> <p style="white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: left;"><span style="color: rgb(178, 178, 178);font-family: Optima-Regular, PingFangTC-light;font-size: 13px;letter-spacing: 0.544px;">来源 |</span><span style="color: rgb(178, 178, 178);font-family: Optima-Regular, PingFangTC-light;font-size: 13px;letter-spacing: 0.544px;white-space: pre-line;">&nbsp;https://urlify.cn/nQZjim</span></p> <section style="margin-bottom: 15px;white-space: normal;text-align: left;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: justify;"></span> </section> <section data-mpa-template="t" mpa-paragraph-type="ignored" style="font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;color: rgb(62, 62, 62);font-size: 15px;background-color: rgb(255, 255, 255);"> <section data-mpa-template="t" mpa-paragraph-type="ignored" style="letter-spacing: 0.544px;"> <section style="color: rgb(0, 0, 0);font-size: 16px;text-align: center;margin-bottom: 20px;"> <span style="color: rgb(63, 63, 63);font-size: 22.4px;letter-spacing: 0.544px;">前</span> <span style="color: rgb(63, 63, 63);font-size: 22.4px;letter-spacing: 0.544px;">言</span> </section> </section> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="margin: 5px 8px 10px;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;line-height: 2em;color: rgb(62, 62, 62);font-size: 15px;background-color: rgb(255, 255, 255);"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="margin: 5px 8px 10px;line-height: 2em;letter-spacing: 0.544px;"> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">Java 开发过程经常需要编写有固定格式的代码,例如说声明一个私有变量,logger或者bean等等。</span><span style="font-size: 16px;">对于这种小范围的代码生成,我们可以利用 IDEA 提供的 Live Templates功能。</span><span style="font-size: 16px;">刚开始觉得它只是一个简单的Code Snippet,后来发现它支持变量函数配置,可以支持很复杂的代码生成。</span><span style="font-size: 16px;">下面我来介绍一下Live Templates的用法。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 80px;margin-bottom: 40px;font-size: 21px;text-align: center;color: rgb(63, 63, 63);">基本使用</h2> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">IDEA 自带很多常用的动态模板,在 Java 代码中输入fori,回车就会出现</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><p style="padding: 16px;overflow-x: auto;color: rgb(56, 58, 66);background: rgb(250, 250, 250);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="color: rgb(166, 38, 164);line-height: 26px;">for</span> (<span style="color: rgb(166, 38, 164);line-height: 26px;">int</span> i = <span style="color: rgb(152, 104, 1);line-height: 26px;">0</span>; i &lt; ; i++) {<br><br>}<br></p></pre> <p style="text-align: center;"><img class="rich_pages __bg_gif" data-ratio="0.1732283464566929" data-type="gif" data-w="635" src="/upload/915f8b660ab80a14d4d6dff2bc76e484.gif" style="width: 635px !important;visibility: visible !important;"></p> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">按Tab可以在各个空白处跳转,手动填值。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 80px;margin-bottom: 40px;font-size: 21px;text-align: center;color: rgb(63, 63, 63);">自定义 Template</h2> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">官方自带模板毕竟不能满足我们个人编码风格的需要,Live Templates提供了变量函数的方式供我们自定义。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 80px;margin-bottom: 40px;font-size: 21px;text-align: center;color: rgb(63, 63, 63);">简单用法</h2> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">新增自定义模板,首先需要填写触发单词(即 Abbreviation),描述是可选的,然后定义模板的上下文,点击define选择Java,这样在编辑 Java 的时候就会触发当前模板,定义完上下文之后,就可以填写模板了。</span></p> <p style="text-align: center;"><img class="rich_pages __bg_gif" data-ratio="0.4843260188087774" data-type="gif" data-w="638" src="/upload/93bd58c68f2b69e2480465756901fda6.gif" style="width: 638px !important;visibility: visible !important;"></p> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">下面列举几个我常用的简单模板</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><p style="padding: 16px;overflow-x: auto;color: rgb(56, 58, 66);background: rgb(250, 250, 250);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;">==========<br>&lt;out&gt;<br>----------<br>System.out.println($END$)<br>==========<br>&lt;pfs&gt;<br>----------<br>private final static String $varName$ = "$var$";`<br>==========<br>&lt;privateField&gt;<br>----------<br>/**<br> * $COMMENT$<br> */<br>@Getter<br>@Setter<br>private $TYPE$ $NAME$;<br>==========<br>&lt;main&gt;<br>----------<br>public static void main(String[] args) {<br> $END$<br>}<br>==========<br></p></pre> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">模板支持变量的定义,使用$$包围的字符表示一个变量。</span><span style="font-size: 16px;">$END$是一个特殊的预定义变量,表示光标最后跳转的位置。</span><span style="font-size: 16px;">每个变量的位置都可以跳转过去。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 80px;margin-bottom: 40px;font-size: 21px;text-align: center;color: rgb(63, 63, 63);">高级用法</h2> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">如果你用过 vim 的Code Sinppet插件,你会发现模板里面是可以执行函数的,强大的 Live Templates当然也支持,而且 IDEA 能够感知代码的语义,例如说当前编辑的函数的参数。</span><span style="font-size: 16px;">但这一点就能够让我们玩出花来。</span><span style="font-size: 16px;">我们从易到难来研究模板函数的功能。</span></p> <p style="text-align: center;"><img class="rich_pages __bg_gif" data-ratio="0.5211267605633803" data-type="gif" data-w="639" src="/upload/e4c0a0f55c7815796636d013e3fbc6bf.gif" style="width: 639px !important;visibility: visible !important;"></p> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">前面我们提到的变量可以绑定函数,配置方式如上图所示。</span></p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;line-height: 1.5;color: rgb(63, 63, 63);">快速声明变量</h3> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">声明变量是一个常用的操作,特别是需要声明变量需要加注解,注释的时候,这些代码写起来就很枯燥。</span><span style="font-size: 16px;">下面是我定义的模板:</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><p style="padding: 16px;overflow-x: auto;color: rgb(56, 58, 66);background: rgb(250, 250, 250);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;">&lt;osgiRef&gt;<br>----------<br>/**<br> * $END$<br> */<br>@OsgiReference<br>@Setter<br>private $TYPE$ $NAME$;<br></p></pre> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">乍一看这个模板跟我上面定义的privateField差不多,唯一的不同在于我给这些变量绑定了函数。</span></p> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">1.clipboard():</span><span style="font-size: 16px;">返回当前粘贴板的字符串</span></p> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">2.decapitalize():</span><span style="font-size: 16px;">将输入的字符串首字母变为小写</span></p> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">下面我们演示一下,我们先拷贝当前类名,然后输入osgiRef</span></p> <p style="text-align: center;"><img class="rich_pages __bg_gif" data-cropselx1="0" data-cropselx2="538" data-cropsely1="0" data-cropsely2="161" data-ratio="0.33125" data-type="gif" data-w="640" src="/upload/30196e2b239b21ec1c16213f29ce0378.gif" style="width: 538px !important;visibility: visible !important;"></p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;line-height: 1.5;color: rgb(63, 63, 63);">快速声明 logger</h3> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">声明 logger 也是一个常用的操作,上面我们是利用了粘贴函数来快速声明变量,现在我们来利用另一个函数className(),顾名思义,它的作用就是返回当前类名。</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><p style="padding: 16px;overflow-x: auto;color: rgb(56, 58, 66);background: rgb(250, 250, 250);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;">&lt;logger&gt;<br>----------<br>/** logger */<br>private static final Logger LOGGER = LoggerFactory.getLogger($CLASS$.class);<br></p></pre> <p style="text-align: center;"><img class="rich_pages __bg_gif" data-ratio="0.29984301412872844" data-type="gif" data-w="637" src="/upload/e4e27777f038079d4f769d904e634109.gif" style="width: 637px !important;visibility: visible !important;"></p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;line-height: 1.5;color: rgb(63, 63, 63);">最强大的 groovyScript()</h3> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">如果说上面用到的函数提供的能力有限,不够灵活,那么groovyScript()提供了一切你想要的能力,它支持执行 Groovy 脚本处理输入,然后输出处理后的字符串。</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><p style="padding: 16px;overflow-x: auto;color: rgb(56, 58, 66);background: rgb(250, 250, 250);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;">groovyScript("code", ...)<br><br>| code | 一段Groovy代码或者Groovy脚本代码绝对路径 |<br>| ... | 可选入参,这些参数会绑定到`_1, _2, _3, ..._n`, 在 Groovy 代码中使用。|<br></p></pre> <p style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);">下面我们来看一下它的实际应用。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;line-height: 1.5;color: rgb(63, 63, 63);">快速 bean 配置</h3> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">新增一个服务都要在 Spring 中注册一个 bean,一般这个配置无非就是将指明id和class,由于我们是在 xml 中配置,所以不能利用className()函数,但是我们可以利用clipboard()函数获取到类的全引用,在 IDEA 中我们直接右键类名,点击Copy Reference就行。</span><span style="font-size: 16px;">然后执行 groovy 脚本获取类名。</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><p style="padding: 16px;overflow-x: auto;color: rgb(56, 58, 66);background: rgb(250, 250, 250);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="line-height: 26px;">&lt;<span style="color: rgb(228, 86, 73);line-height: 26px;">bean</span>&gt;</span><br>----------<br><span style="line-height: 26px;">&lt;<span style="color: rgb(228, 86, 73);line-height: 26px;">bean</span> <span style="color: rgb(152, 104, 1);line-height: 26px;">id</span>=<span style="color: rgb(80, 161, 79);line-height: 26px;">"$id$"</span> <span style="color: rgb(152, 104, 1);line-height: 26px;">class</span>=<span style="color: rgb(80, 161, 79);line-height: 26px;">"$REF$"</span> /&gt;</span><br></p></pre> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">id绑定decapitalize(groovyScript("_1.tokenize('.')[-1]", clipboard())),首先取clipboard()的值得到类的全引用,然后执行 groovy 代码_1.tokenize('.')[-1](按.分割为字符串数组,然后取最后一个即可得到类名,然后用decapitalize()将首字母小写即可得到id。</span></p> <p style="text-align: center;"><img class="rich_pages __bg_gif" data-ratio="0.5179968701095462" data-type="gif" data-w="639" src="/upload/dc2b59021e614da7ac5177d02fea57dc.gif" style="width: 639px !important;visibility: visible !important;"></p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;font-size: 18px;line-height: 1.5;color: rgb(63, 63, 63);">快速打印当前上下文信息</h3> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">打印错误日志的时候需要打印当前上下文信息的,例如说入参,有时候入参很多的时候,写起来很痛苦,好在有模板函数methodParameters(),返回当前函数参数的列表,当然这个列表我们不能直接使用,需要结合groovyScript对它进行转化。</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><p style="padding: 16px;overflow-x: auto;color: rgb(56, 58, 66);background: rgb(250, 250, 250);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="line-height: 26px;">&lt;<span style="color: rgb(228, 86, 73);line-height: 26px;">printContext</span>&gt;</span><br>---------------<br>LogUtil.$TYPE$(LOGGER, "$MSG$ " + $params$);<br></p></pre> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">将params绑定到groovyScript("'\"' + _1.collect { it + ' = [\" + ' + it + ' + \"]'}.join(', ') + '\"'", methodParameters()),就能够自动将当前函数的参数格式化后输出。</span></p> <p style="text-align: center;"><img class="rich_pages __bg_gif" data-ratio="0.1732283464566929" data-type="gif" data-w="635" src="/upload/18fe08dafd1f5bc83ea7dae4afa2f575.gif" style="width: 635px !important;visibility: visible !important;"></p> <h2 data-tool="mdnice编辑器" style="margin-top: 80px;margin-bottom: 40px;font-size: 21px;text-align: center;color: rgb(63, 63, 63);">总结</h2> <p style="margin-top: 5px;margin-bottom: 10px;line-height: 2em;letter-spacing: 0.544px;"><span style="font-size: 16px;">上面我们简单介绍了常用的模板函数,其实 IDEA 还有很多其它模板函数,具体参考Creating and Editing Template Variables(https://www.jetbrains.com/help/idea/2016.3/creating-and-editing-template-variables.html)。</span><span style="font-size: 16px;">IDEA 是一个很强大的工具,善用工具能够极大的提高工作效率,将精力投入到关键的事情上,而不是将时间浪费在编写重复代码上面。</span><span style="font-size: 16px;">一些更高级的用法还有待大家去发掘。</span><span style="font-size: 16px;">最后推广一波代码生成插件CodeMaker(https://github.com/x-hansong/CodeMaker),好好利用也能节省很多重复编写代码的时间。</span></p> </section> </section> <p style="margin-top: 10px;white-space: normal;text-align: left;"><br></p> <p style="margin-top: 10px;margin-bottom: 10px;white-space: normal;text-align: center;"><img class="rich_pages" data-ratio="0.18524332810047095" src="/upload/d2a38b8f8a4c599687e2292f14dfd57b.gif" data-type="gif" data-w="637" style="height: 41px;width: 220px;"></p> <ul class="list-paddingleft-2" style="list-style-type: circle;"> <li><p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&amp;mid=2247489596&amp;idx=2&amp;sn=bbec92d3b1bd869c719bc5258da01bbb&amp;chksm=9bd0b5a4aca73cb2d9f5a8572b210b7f36683251256e3e7bc45921a66692f6fc8f67c8196fa9&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2"><span style="font-size: 14px;">Spring Boot 2.1之后如何在启动日志中打印请求路径列表</span></a></p></li> <li><p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&amp;mid=2247489578&amp;idx=1&amp;sn=d8586d7cefa603d23efff529508bd6c9&amp;chksm=9bd0b5b2aca73ca47bdedb263303c14d51a6cac12412dbc769197a121ff0c12f06a9bec6ac90&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2"><span style="font-size: 14px;">NASA立扫把挑战”?</span><span style="font-size: 14px;">牛顿的棺材板都按不住啦!</span></a></p></li> <li><p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&amp;mid=2247489563&amp;idx=2&amp;sn=0ece8631007629eff8aed856d8d6b485&amp;chksm=9bd0b583aca73c950da1f7505472e87b48faea2f8d5436b5e1abd446ebe28617065013c9a374&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2"><span style="font-size: 14px;">如何干掉恶心的 SQL 注入?</span></a></p></li> <li><p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&amp;mid=2247489554&amp;idx=2&amp;sn=15f37854533eaf77596e846dfd0ad338&amp;chksm=9bd0b58aaca73c9ca2aeadc5d8133c52df6f957f5a563bf0d4f31caf4cec22792f05346e5674&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2"><span style="font-size: 14px;">Spring Boot 2.x&nbsp;中使用国产数据库连接池Druid</span></a></p></li> <li><p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&amp;mid=2247489546&amp;idx=2&amp;sn=d81edd14eef572688faba9d6b4dd4716&amp;chksm=9bd0b592aca73c840c025307ca15fc39fb253754a162bc58cad752a364af4cb0e54a49a27b85&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 14px;" data-linktype="2">十个最常用的JVM 配置参数</a><br></p></li> </ul> <p style="margin-top: 5px;margin-bottom: 5px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 16px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: center;line-height: normal;"><br></p> <p style="white-space: normal;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);color: rgb(62, 62, 62);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;text-align: right;"><span style="letter-spacing: 0.544px;font-variant-numeric: normal;font-variant-east-asian: normal;font-weight: 700;widows: 1;font-family: Optima-Regular, PingFangTC-light;font-size: 14px;">朕已阅&nbsp;</span><img class="__bg_gif" data-ratio="1" data-type="gif" data-w="19" src="/upload/6d654f7f0528a43fa6e93c1b3334f39e.null" width="19px" style="letter-spacing: 0.544px;font-weight: 700;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-variant-numeric: normal;line-height: 25.6px;text-align: center;widows: 1;visibility: visible !important;width: 19px !important;"></p>

阿里面试官:分别说说微信和淘宝扫码登录背后的实现原理?

作者:微信小助手

<p style="font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;" data-mpa-powered-by="yiban.io"><strong style="letter-spacing: 0.544px;color: rgba(0, 0, 0, 0.498);font-size: 15px;widows: 1;"><span style="font-size: 14px;color: rgb(136, 136, 136);"></span></strong></p> <p data-mpa-powered-by="yiban.io" style="color: rgb(0, 0, 0);font-size: 16px;white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;"><strong style="letter-spacing: 0.544px;color: rgba(0, 0, 0, 0.498);font-size: 15px;widows: 1;"><span style="font-size: 14px;color: rgb(136, 136, 136);">点击上方“JavaGuide</span></strong><strong style="letter-spacing: 0.544px;color: rgba(0, 0, 0, 0.498);font-size: 15px;widows: 1;">“</strong></p> <p data-darkmode-color="rgb(230, 230, 230)" data-style="white-space: normal; color: rgb(0, 0, 0); font-family: PingFangSC-Light; font-size: 16px; text-align: center;" class="js_darkmode__2" style="color: rgb(0, 0, 0);font-family: PingFangSC-Light;font-size: 16px;white-space: normal;text-align: center;"><strong style="letter-spacing: 0.544px;color: rgba(0, 0, 0, 0.498);font-size: 15px;widows: 1;"><span style="font-size: 14px;color: rgb(136, 136, 136);">回复”</span></strong><strong style="letter-spacing: 0.544px;color: rgba(0, 0, 0, 0.498);font-size: 15px;widows: 1;"><span style="font-size: 14px;color: rgb(136, 136, 136);"><span style="font-size: 15px;letter-spacing: 0.544px;color: rgb(120, 172, 254);"><strong><span style="letter-spacing: 0.544px;font-size: 14px;">面试突击</span></strong></span></span></strong><strong style="letter-spacing: 0.544px;color: rgba(0, 0, 0, 0.498);font-size: 15px;widows: 1;"><span style="font-size: 14px;color: rgb(136, 136, 136);">“获取Github 68k+ Star项目精华集合而成的《Java面试突击》、</span></strong></p> <p style="font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(62, 62, 62);font-size: 15px;widows: 1;text-align: center;"><strong><span style="font-size: 14px;"><span style="color: rgb(136, 136, 136);"><br></span></span></strong></p> <p style="white-space: normal;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);font-size: 15px;text-align: center;"><span style="color: rgb(127, 127, 127);font-size: 14px;letter-spacing: 0.544px;"><img data-ratio="0.5625" src="/upload/8941e2c9a1dfeb33575129b830119010.null" data-w="960"></span></p> <section style=""> <section> <section> <section> <blockquote data-type="2" data-url="" data-author-name="" data-content-utf8-length="48" data-source-title="" style="color: rgba(0, 0, 0, 0.5);"> <section> <section> <p style="margin-bottom: 10px;letter-spacing: 0.544px;line-height: normal;"><span style="font-size: 13px;">作者:</span><span style="letter-spacing: 0.544px;font-size: 13px;">imtech&nbsp;</span></p> <p style="margin-bottom: 10px;letter-spacing: 0.544px;line-height: normal;"><span style="font-size: 13px;">my.oschina.net/u/4231722/blog/3154805</span></p> </section> </section> </blockquote> </section> </section> </section> </section> <h2 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.2em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1、引言</span></h2> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">扫码登录这个功能,最早应该是微信的PC端开始搞,虽然有点反人类的功能(不扫码也没别的方式登录),但不得不说还是很酷的。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">下面这张图,不管是IM开发者还是普通用户,应该很熟悉:</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><img data-ratio="1.3033333333333332" src="/upload/84b5ad58b3225dbf137016e451a87d56.jpg" data-type="jpeg" data-w="300"></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">于是,搞IM产品的老板和产品经理们,从此又多了一个要抛给程序员们的需求——“为什么微信有扫一扫登录,而我们的没有?”。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">好吧,每次只要是微信有的功能,IM程序员们想甩锅,难度就有点大了,毕竟老板们都都会想当然认为,微信有的“我”的IM产品里也得有。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><img data-ratio="1.0333333333333334" src="/upload/b079787c62e2d6d1d94332e7bcf144cc.jpg" data-type="jpeg" data-w="300"></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">既然无法回避,那就只能老老实实搞懂技术原理,然后自已使劲撸吧。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">本文将简要的介绍扫码登录功能的技术实现逻辑,并实际结合淘宝、微信的扫码登录功能,学习和研究大厂主流应用的技术实现思路。</p> <h2 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.2em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2、基本技术原理</span></h2> <h3 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.1em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.1 扫码登录功能到底是什么样的?</span></h3> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">首先介绍下什么是扫码登录。现在大部分同学手机上都装有微信、qq和淘宝这一类的软件。而这些app都有他们相对应的网页端。为了让用户在使用他们的网页时登录更加方便和安全,使用手机扫一扫就可以登录的服务,就显得自然而然了。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">几个主流大厂应用扫码登录时的界面效果如下:</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><img data-ratio="0.4127659574468085" src="/upload/802a7225860bee8a2140aa9ab40e1a05.jpg" data-type="jpeg" data-w="940"></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">有很多小伙伴可能会感到很神奇,网页上只是显示了个二维码,它怎么就知道是哪个手机扫到了二维码,并且进行登录的呢?而且,登录完成以后,还能直接把用户信息显示给用户,真的是很神奇啊。</p> <h3 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.1em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.2 扫码登录功能的完整技术逻辑</span></h3> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;">1)网页端与服务器的配合逻辑:</strong></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">接下来就是对于这个服务的详细实现。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">首先用户打开网站的登录页面的时候,向浏览器的服务器发送获取登录二维码的请求。服务器收到请求后,随机生成一个uuid,将这个id作为key值存入redis服务器,同时设置一个过期时间,再过期后,用户登录二维码需要进行刷新重新获取。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">同时,将这个key值和本公司的验证字符串合在一起,通过二维码生成接口,生成一个二维码的图片(二维码生成,网上有很多现成的接口和源码,这里不再介绍)。然后,将二维码图片和uuid一起返回给用户浏览器。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">浏览器拿到二维码和uuid后,会每隔一秒向浏览器发送一次,登录是否成功的请求。请求中携带有uuid作为当前页面的标识符。这里有的同学就会奇怪了,服务器只存了个uuid在redis中作为key值,怎么会有用户的id信息呢?</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">这里确实会有用户的id信息,这个id信息是由手机服务器存入redis中的。具体请继续阅读“手机端与服务器的配合逻辑”。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;">2)手机端与服务器的配合逻辑:</strong></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">话说,浏览器拿到二维码后,将二维码展示到网页上,并给用户一个提示:请掏出您的手机,打开扫一扫进行登录。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">用户拿出手机扫描二维码,就可以得到一个验证信息和一个uuid(扫描二维码获取字符串的功能在网上同样有很多demo,这里就不详细介绍了)。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><span style="background-color: rgb(255, 255, 255);">由于手机端已经进行过了登录,在访问手机端的服务器的时候,参数中都</span>会<span style="background-color: rgb(255, 255, 255);">携带一个用户的token,手机端服务器可以从中解析到用户的userId(这里从token中取值而不是手机端直接传userid是为了安全,直接传userid可能会被截获和修改,token是加密的,被修改的风险会小很多)。</span><span style="background-color: rgb(255, 255, 255);">手机端将解析到的数据和用户token一起作为参数,向服务器发送验证登录请求(这里的服务器是手机服务器,手机端的服务器跟网页端服务器不是同一台服务器)。</span></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">服务器收到请求后,首先对比参数中的验证信息,确定是否为用户登录请求接口。如果是,返回一个确认信息给手机端。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">手机端收到返回后,将登录确认框显示给用户(防止用户误操作,同时使登录更加人性化)。用户确认是进行的登录操作后,手机再次发送请求。服务器拿到uuId和userId后,将用户的userid作为value值存入redis中以uuid作为key的键值对中。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;">3)登录成功时的逻辑:</strong></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;"><span style="background-color: rgb(255, 255, 255);">然后,浏览器再次发送请求的时候,浏览器端的服务器就可以得到一个用户Id,并调用登录的方法,</span>生成<span style="background-color: rgb(255, 255, 255);">一个浏览器端的token,再浏览器再次发送请求的时候,将用户信息返回给浏览器,登录成功。</span><span style="background-color: rgb(255, 255, 255);">这里存储用户id而不是直接存储用户信息是因为,手机端的用户信息,不一定是和浏览器端的用户信息完全一致。</span></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;">4)详细的技术原理总结如下图所示:</strong></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;"><img data-ratio="1.1255813953488372" src="/upload/81b35d9e9f3c15ef8a43531049ee6f0.png" data-type="png" data-w="860"></strong></p> <h3 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.1em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">3、淘宝的扫码登录技术实现</span></h3> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">本节我们以淘宝的扫码登录为例,来实际研究分析一下淘宝的扫码登录实现逻辑。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">登录界面 https://login.taobao.com/member/login.jhtml 传回来的参数为:</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><img data-ratio="0.36230769230769233" src="/upload/1b95c533ace4e8c2f97d2c7edd07e121.png" data-type="png" data-w="1300"></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">然后请求(GET)报文是这样的:</p> <pre style="font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code style="margin-right: 2px;margin-left: 2px;padding: 0.5em;font-size: 12px;color: white;line-height: 15px;border-radius: 0px;background: rgb(51, 51, 51);display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;word-spacing: -3px;letter-spacing: 0px;overflow-wrap: normal !important;word-break: normal !important;overflow-y: auto !important;"><span style="font-size: inherit;color: rgb(255, 255, 170);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">https://qrlogin.taobao.com/qrcodelogin/qrcodeLoginCheck.do?</span><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">lgToken=2c3b4d53ef0513787bf4ce711ea5ba53&amp;defaulturl=&amp;_ksTS=1540106757739_2804&amp;callback=jsonp2805<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></code></pre> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">关键的就是lgToken,是网页的唯一ID,当打开了二维码登录的时候,网页在轮询(应该是长轮询long polling)调用接口去请求服务器。扩展:<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&amp;mid=2247488056&amp;idx=1&amp;sn=6137b4a15f5be31eb503415c16239ba5&amp;chksm=ebd62d14dca1a4025dd66901e8bb35914981582d0416070a0b830f94baab34c2335a5a203539&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="color: rgb(0, 0, 0);" data-linktype="2"><span style="color: rgb(0, 0, 0);">彻底理解cookie,session,token</span></a></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">如果没有扫码,返回的为:</p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.03088803088803089" data-s="300,640" src="/upload/a2fc9d27e0549d483a50b8be9cf07e60.png" data-type="png" data-w="518" style=""></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">如果扫了的话则会返回:</p> <pre style="font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code style="margin-right: 2px;margin-left: 2px;padding: 0.5em;font-size: 12px;color: white;line-height: 15px;border-radius: 0px;background: rgb(51, 51, 51);display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;word-spacing: -3px;letter-spacing: 0px;overflow-wrap: normal !important;word-break: normal !important;overflow-y: auto !important;">{<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"code"</span>:&nbsp;<span style="font-size: inherit;color: rgb(162, 252, 162);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"10001"</span>,<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"message"</span>:&nbsp;<span style="font-size: inherit;color: rgb(162, 252, 162);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"mobile&nbsp;scan&nbsp;QRCode&nbsp;success"</span>,<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"success"</span>:&nbsp;<span style="font-size: inherit;color: rgb(252, 194, 140);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">true</span><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">}<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></code></pre> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">长时间没有扫码的话,网页端会停止轮询,二维码失效!</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">当手机端确认登录后,接口返回的是:</p> <pre style="font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code style="margin-right: 2px;margin-left: 2px;padding: 0.5em;font-size: 12px;color: white;line-height: 15px;border-radius: 0px;background: rgb(51, 51, 51);display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;word-spacing: -3px;letter-spacing: 0px;overflow-wrap: normal !important;word-break: normal !important;overflow-y: auto !important;">{&nbsp;<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"code"</span>:&nbsp;<span style="font-size: inherit;color: rgb(162, 252, 162);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"10006"</span>,&nbsp;<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"success"</span>:&nbsp;<span style="font-size: inherit;color: rgb(252, 194, 140);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">true</span>,<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"url"</span>:&nbsp;<span style="font-size: inherit;color: rgb(162, 252, 162);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"https://login.taobao.com/member/loginByIm.do?uid=cntaobaoxxx&amp;token=ff82fc0d1d395a33d3b38ec5a4981336&amp;time=1530179143250&amp;asker=qrcodelogin&amp;ask_version=1.0.0&amp;defaulturl=https://www.taobao.com&amp;webpas=0b7aed2d43f01825183e4a49c6cae47d1479929926"</span><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">}<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></code></pre> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">表示登录成功,当然手机端与服务端在点击"确认登录"之间的交互可能就是这样:网页端生成的lgToken去请求服务端,服务端记住了这个lgToken并认为登录了,当网页端再次轮询请求接口时,就返回真正的登录态Token,网页端此时就可以凭着这个Token来登录了。</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">详细的技术逻辑如下图所示:</p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><img data-ratio="0.46063651591289784" src="/upload/2588559c28fdcdbf5a72b12b64ffccfd.png" data-type="png" data-w="597"></p> <h2 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.2em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">4、微信的扫码登录技术实现</span></h2> <h3 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.1em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">4.1 技术原理流程图</span></h3> <p><span style="font-size: inherit;color: inherit;line-height: inherit;"><img data-ratio="0.9482470784641068" src="/upload/5c5d471325bd17fdbccbec67f291ad85.png" data-type="png" data-w="599"></span></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">微信的网页版访问地址是:https://wx.qq.com/,有兴趣也可以自行深入研究。</p> <h3 style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 1.1em;color: rgb(21, 153, 87);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: inherit;color: inherit;line-height: inherit;">4.2 实际的技术实现逻辑</span></h3> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;">1)获取唯一的uuid, 以及包含uid信息的二维码:</strong></p> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;"><img data-ratio="0.3468085106382979" src="/upload/4b2334bfeab61878fa397b084b0bb96e.jpg" data-type="jpeg" data-w="940"></strong></p> <pre style="font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code style="margin-right: 2px;margin-left: 2px;padding: 0.5em;font-size: 12px;color: white;line-height: 15px;border-radius: 0px;background: rgb(51, 51, 51);display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;word-spacing: -3px;letter-spacing: 0px;overflow-wrap: normal !important;word-break: normal !important;overflow-y: auto !important;"><span style="font-size: inherit;color: rgb(136, 136, 136);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;获取uuid</span><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">getUUID:&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;color: rgb(252, 194, 140);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">function</span>(<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></span>)&nbsp;</span>{<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;vare&nbsp;=&nbsp;t.defer();<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;returnwindow.QRLogin&nbsp;=&nbsp;{},<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;$.ajax({<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">url</span>:&nbsp;i.API_jsLogin,<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">dataType</span>:&nbsp;<span style="font-size: inherit;color: rgb(162, 252, 162);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"script"</span><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;}).done(<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;color: rgb(252, 194, 140);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">function</span>(<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></span>)&nbsp;</span>{<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;color: rgb(211, 99, 99);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">200</span>&nbsp;==&nbsp;<span style="font-size: inherit;color: rgb(255, 255, 170);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">window</span>.QRLogin.code&nbsp;?&nbsp;e.resolve(<span style="font-size: inherit;color: rgb(255, 255, 170);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">window</span>.QRLogin.uuid)&nbsp;:&nbsp;e.reject(<span style="font-size: inherit;color: rgb(255, 255, 170);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">window</span>.QRLogin.code)<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;}).fail(<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;color: rgb(252, 194, 140);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">function</span>(<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></span>)&nbsp;</span>{<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.reject()<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;}),<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;e.promise<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">}<br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></code></pre> <p style="margin-top: 1.3em;margin-bottom: 1.3em;font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: inherit;font-size: inherit;line-height: inherit;">2)浏览器轮询服务器,获取扫码状态:</strong></p> <pre style="font-size: 15px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code style="margin-right: 2px;margin-left: 2px;padding: 0.5em;font-size: 12px;color: white;line-height: 15px;border-radius: 0px;background: rgb(51, 51, 51);display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;word-spacing: -3px;letter-spacing: 0px;overflow-wrap: normal !important;word-break: normal !important;overflow-y: auto !important;"><span style="font-size: inherit;color: rgb(136, 136, 136);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;查看扫码状态</span><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">checkLogin:&nbsp;<span style="font-size: in

初探 Java agent

作者:微信小助手

<section style="display: none;" data-tools="新媒体管家" data-label="powered by xmt.cn"> <br> </section> <p><strong style="color: rgb(0, 122, 170);font-size: 16px;"><span style="font-size: 17px;">引言</span></strong><br></p> <article tabindex="0"> <hr style="max-width: 100%;white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;border-style: solid;border-right-width: 0px;border-bottom-width: 0px;border-left-width: 0px;border-color: rgba(0, 0, 0, 0.1);transform-origin: 0px 0px 0px;transform: scale(1, 0.5);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p dir="ltr" style="max-width: 100%;min-height: 1em;white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">在本篇文章中,我会通过几个简单的程序来说明 agent 的使用,最后在实战环节我会通过 asm 字节码框架来实现一个小工具,用于在程序运行中采集指定方法的参数和返回值。</span> <span style="font-size: 15px;">有关 asm 字节码的内容不是本文的重点,不会过多的阐述,不明白的同学可以自己 google 下。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <br> </section> <h1 style="line-height: 1.75em;"><span style="color: rgb(0, 122, 170);"><strong><span style="color: rgb(0, 122, 170);font-size: 17px;">简介</span></strong></span></h1> <hr style="max-width: 100%;white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;border-style: solid;border-right-width: 0px;border-bottom-width: 0px;border-left-width: 0px;border-color: rgba(0, 0, 0, 0.1);transform-origin: 0px 0px 0px;transform: scale(1, 0.5);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p dir="ltr" style="max-width: 100%;min-height: 1em;white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">Java agent 提供了一种在加载字节码时,对字节码进行修改的方式。</span> <span style="font-size: 15px;">他共有两种方式执行,一种是在 main 方法执行之前,通过 premain 来实现,另一种是在程序运行中,通过 attach api 来实现。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">在介绍 agent 之前,先给大家简单说下 Instrumentation 。</span> <span style="font-size: 15px;">它是 JDK1.5 提供的 API ,用于拦截类加载事件,并对字节码进行修改,它的主要方法如下:</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">interface</span> <span class="code-snippet__title">Instrumentation</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">//注册一个转换器,类加载事件会被注册的转换器所拦截</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">void</span> <span class="code-snippet__title">addTransformer</span><span class="code-snippet__params">(ClassFileTransformer transformer, <span class="code-snippet__keyword">boolean</span> canRetransform)</span></span>;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">//重新触发类加载</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">void</span> <span class="code-snippet__title">retransformClasses</span><span class="code-snippet__params">(Class&lt;?&gt;... classes)</span> <span class="code-snippet__keyword">throws</span> UnmodifiableClassException</span>;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">//直接替换类的定义</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">void</span> <span class="code-snippet__title">redefineClasses</span><span class="code-snippet__params">(ClassDefinition... definitions)</span> <span class="code-snippet__keyword">throws</span> ClassNotFoundException, UnmodifiableClassException</span>;</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> <br> </section></pre> <section style="line-height: 1.75em;"> <br> </section> <h1 style="line-height: 1.75em;"><span style="color: rgb(0, 122, 170);"><strong><span style="color: rgb(0, 122, 170);font-size: 17px;">premain</span></strong></span></h1> <hr style="max-width: 100%;white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;border-style: solid;border-right-width: 0px;border-bottom-width: 0px;border-left-width: 0px;border-color: rgba(0, 0, 0, 0.1);transform-origin: 0px 0px 0px;transform: scale(1, 0.5);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p dir="ltr" style="max-width: 100%;min-height: 1em;white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">premain 是在 main 方法之前运行的方法,也是最常见的 agent 方式。</span> <span style="font-size: 15px;">运行时需要将 agent 程序打包成 jar 包,并在启动时添加命令来执行,如下文所示:</span> </section> <pre> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="nginx"><code><span class="code-snippet_outer"><span class="code-snippet__attribute">java</span> -javaagent:agent.jar=xunche HelloWorld</span></code></pre> </section> <section style="line-height: 1.75em;"> <br> </section></pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">premain 共提供以下 2 种重载方法, Jvm 启动时会先尝试使用第一种方法,若没有会使用第二种方法:</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer"><span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">premain</span>(<span class="code-snippet__params">String agentArgs, Instrumentation inst</span>)</span>;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">premain</span>(<span class="code-snippet__params">String agentArgs</span>)</span>;</span></code></pre> </section> <section style="line-height: 1.75em;"> <br> <span style="font-size: 15px;"></span> </section></pre> <h3 style="line-height: 1.75em;"><strong><span style="font-size: 16px;">一个简单的例子</span></strong></h3> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">下面我们通过一个程序来简单说明下 premain 的使用,首先我们准备下测试代码,测试代码比较简单,运行 main 方法并输出 hello world 。</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section></pre> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer">package org.xunche.app;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">class</span> <span class="code-snippet__title">HelloWorld</span> {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span>(<span class="code-snippet__params">String[] args</span>)</span> {</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">"Hello World"</span>);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">接下来我们看下 agent 的代码,运行 premain 方法并输出我们传入的参数。</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer">package org.xunche.agent;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">class</span> <span class="code-snippet__title">HelloAgent</span> {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">premain</span>(<span class="code-snippet__params">String args</span>)</span> {</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">"Hello Agent: "</span> + args);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <section style="line-height: 1.75em;"> <br> <span style="font-size: 15px;"></span> </section></pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">为了能够 agent 能够运行,我们需要将 META-INF/MANIFEST.MF 文件中的 Premain- Class 为我们编写的 agent 路径,然后通过以下方式将其打包成 jar&nbsp;包,当然你也可以使用 idea 直接导出 jar 包。</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">echo</span> <span class="code-snippet__string">'Premain-Class: org.xunche.agent.HelloAgent' &gt; manifest.mf</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">javac</span> <span class="code-snippet__string">org/xunche/agent/HelloAgent.java</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">javac</span> <span class="code-snippet__string">org/xunche/app/HelloWorld.java</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">jar</span> <span class="code-snippet__string">cvmf manifest.mf hello-agent.jar org/</span></span></code></pre> </section> <section style="line-height: 1.75em;"> <br> <span style="font-size: 15px;"></span> </section></pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">接下来,我们编译下并运行下测试代码,这里为了测试简单,我将编译后的 class 和 agent 的 jar 包放在了同级目录下</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section></pre> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="nginx"><code><span class="code-snippet_outer"><span class="code-snippet__attribute">java</span> -javaagent:hello-agent.jar=xunche org/xunche/app/HelloWorld</span></code></pre> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">可以看到输出结果如下,agent中的premain方法有限于main方法执行</span> </section> <section style="line-height: 1.75em;"> <br> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section></pre> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">Hello</span> <span class="code-snippet__string">Agent: xunche</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">Hello</span> <span class="code-snippet__string">World</span></span></code></pre> </section> <h3 style="line-height: 1.75em;"><span style="font-size: 15px;"><br></span></h3> <h3 style="line-height: 1.75em;"><strong><span style="font-size: 16px;">稍微复杂点的例子</span></strong><br></h3> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">通过上面的例子,是否对 agent 有个简单的了解呢?</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">下面我们来看个稍微复杂点,我们通过 agent 来实现一个方法监控的功能。</span> <span style="font-size: 15px;">思路大致是这样的,若是非 jdk 的方法,我们通过 asm 在方法的执行入口和执行出口处,植入几行记录时间戳的代码,当方法结束后,通过时间戳来获取方法的耗时。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">首先还是看下测试代码,逻辑很简单, main 方法执行时调用 sayHi 方法,输出 hi ,&nbsp; xunche ,并随机睡眠一段时间。</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">package</span> org.xunche.app;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">HelloXunChe</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span><span class="code-snippet__params">(String[] args)</span> <span class="code-snippet__keyword">throws</span> InterruptedException </span>{</span></code><code><span class="code-snippet_outer"> HelloXunChe helloXunChe = <span class="code-snippet__keyword">new</span> HelloXunChe();</span></code><code><span class="code-snippet_outer"> helloXunChe.sayHi();</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">sayHi</span><span class="code-snippet__params">()</span> <span class="code-snippet__keyword">throws</span> InterruptedException </span>{</span></code><code><span class="code-snippet_outer"> System.out.println(<span class="code-snippet__string">"hi, xunche"</span>);</span></code><code><span class="code-snippet_outer"> sleep();</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">sleep</span><span class="code-snippet__params">()</span> <span class="code-snippet__keyword">throws</span> InterruptedException </span>{</span></code><code><span class="code-snippet_outer"> Thread.sleep((<span class="code-snippet__keyword">long</span>) (Math.random() * <span class="code-snippet__number">200</span>));</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <section style="line-height: 1.75em;"> <br> <span style="font-size: 15px;"></span> </section></pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">接下来我们借助 asm 来植入我们自己的代码,在 jvm 加载类的时候,为类的每个方法加上统计方法调用耗时的代码,代码如下,这里的 asm 我使用了 jdk 自带的,当然你也可以使用官方的 asm 类库。</span> </section> <pre> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"></span> </section></pre> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> <pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">package</span> org.xunche.agent;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> jdk.internal.org.objectweb.asm.*;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> jdk.internal.org.objectweb.asm.commons.AdviceAdapter;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.lang.instrument.ClassFileTransformer;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.lang.instrument.Instrumentation;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.security.ProtectionDomain;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">TimeAgent</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">premain</span><span class="code-snippet__params">(String args, Instrumentation instrumentation)</span> </span>{</span></code><code><span class="code-snippet_outer"> instrumentation.addTransformer(<span class="code-snippet__keyword">new</span> TimeClassFileTransformer());</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">TimeClassFileTransformer</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">ClassFileTransformer</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">byte</span>[] transform(ClassLoader loader, String className, Class&lt;?&gt; classBeingRedefined, ProtectionDomain protectionDomain, <span class="code-snippet__keyword">byte</span>[] classfileBuffer) {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">if</span> (className.startsWith(<span class="code-snippet__string">"java"</span>) || className.startsWith(<span class="code-snippet__string">"jdk"</span>) || className.startsWith(<span class="code-snippet__string">"javax"</span>) || className.startsWith(<span class="code-snippet__string">"sun"</span>) || className.startsWith(<span class="code-snippet__string">"com/sun"</span>)|| className.startsWith(<span class="code-snippet__string">"org/xunche/agent"</span>)) {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">//return null或者执行异常会执行原来的字节码</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> <span class="code-snippet__keyword">null</span>;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> System.out.println(<span class="code-snippet__string">"loaded class: "</span> + className);</span></code><code><span class="code-snippet_outer"> ClassReader reader = <span class="code-snippet__keyword">new</span> ClassReader(classfileBuffer);</span></code><code><span class="code-snippet_outer"> ClassWriter writer = <span class="code-snippet__keyword">new</span> ClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);</span></code><code><span class="code-snippet_outer"> reader.accept(<span class="code-snippet__keyword">new</span> TimeClassVisitor(writer), ClassReader.EXPAND_FRAMES);</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> writer.toByteArray();</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">TimeClassVisitor</span> <span class="code-snippet__keyword">extends</span> <span class="code-snippet__title">ClassVisitor</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__title">TimeClassVisitor</span><span class="code-snippet__params">(ClassVisitor classVisitor)</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">super</span>(Opcodes.ASM5, classVisitor);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> MethodVisitor <span class="code-snippet__title">visitMethod</span><span class="code-snippet__params">(<span class="code-snippet__keyword">int</span> methodAccess, String methodName, String methodDesc, String signature, String[] exceptions)</span> </span>{</span></code><code><span class="code-snippet_outer"> MethodVisitor methodVisitor = cv.visitMethod(methodAccess, methodName, methodDesc, signature, exceptions);</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> <span class="code-snippet__keyword">new</span> TimeAdviceAdapter(Opcodes.ASM5, methodVisitor, methodAccess, methodName, methodDesc);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">TimeAdviceAdapter</span> <span class="code-snippet__keyword">extends</span> <span class="code-snippet__title">AdviceAdapter</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">private</span> String methodName;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">protected</span> <span class="code-snippet__title">TimeAdviceAdapter</span><span class="code-snippet__params">(<span class="code-snippet__keyword">int</span> api, MethodVisitor methodVisitor, <span class="code-snippet__keyword">int</span> methodAccess, String methodName, String methodDesc)</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">super</span>(api, methodVisitor, methodAccess, methodName, methodDesc);</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">this</span>.methodName = methodName;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">protected</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">onMethodEnter</span><span class="code-snippet__params">()</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">//在方法入口处植入</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">if</span> (<span class="code-snippet__string">"&lt;init&gt;"</span>.equals(methodName)|| <span class="code-snippet__string">"&lt;clinit&gt;"</span>.equals(methodName)) {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> mv.visitTypeInsn(NEW, <span class="code-snippet__string">"java/lang/StringBuilder"</span>);</span></code><code><span class="code-snippet_outer"> mv.visitInsn(DUP);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKESPECIAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"&lt;init&gt;"</span>, <span class="code-snippet__string">"()V"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitVarInsn(ALOAD, <span class="code-snippet__number">0</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/Object"</span>, <span class="code-snippet__string">"getClass"</span>, <span class="code-snippet__string">"()Ljava/lang/Class;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/Class"</span>, <span class="code-snippet__string">"getName"</span>, <span class="code-snippet__string">"()Ljava/lang/String;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitLdcInsn(<span class="code-snippet__string">"."</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitLdcInsn(methodName);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"toString"</span>, <span class="code-snippet__string">"()Ljava/lang/String;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKESTATIC, <span class="code-snippet__string">"org/xunche/agent/TimeHolder"</span>, <span class="code-snippet__string">"start"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)V"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">protected</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">onMethodExit</span><span class="code-snippet__params">(<span class="code-snippet__keyword">int</span> i)</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">//在方法出口植入</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">if</span> (<span class="code-snippet__string">"&lt;init&gt;"</span>.equals(methodName) || <span class="code-snippet__string">"&lt;clinit&gt;"</span>.equals(methodName)) {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> mv.visitTypeInsn(NEW, <span class="code-snippet__string">"java/lang/StringBuilder"</span>);</span></code><code><span class="code-snippet_outer"> mv.visitInsn(DUP);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKESPECIAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"&lt;init&gt;"</span>, <span class="code-snippet__string">"()V"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitVarInsn(ALOAD, <span class="code-snippet__number">0</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/Object"</span>, <span class="code-snippet__string">"getClass"</span>, <span class="code-snippet__string">"()Ljava/lang/Class;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/Class"</span>, <span class="code-snippet__string">"getName"</span>, <span class="code-snippet__string">"()Ljava/lang/String;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitLdcInsn(<span class="code-snippet__string">"."</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitLdcInsn(methodName);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"toString"</span>, <span class="code-snippet__string">"()Ljava/lang/String;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitVarInsn(ASTORE, <span class="code-snippet__number">1</span>);</span></code><code><span class="code-snippet_outer"> mv.visitFieldInsn(GETSTATIC, <span class="code-snippet__string">"java/lang/System"</span>, <span class="code-snippet__string">"out"</span>, <span class="code-snippet__string">"Ljava/io/PrintStream;"</span>);</span></code><code><span class="code-snippet_outer"> mv.visitTypeInsn(NEW, <span class="code-snippet__string">"java/lang/StringBuilder"</span>);</span></code><code><span class="code-snippet_outer"> mv.visitInsn(DUP);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKESPECIAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"&lt;init&gt;"</span>, <span class="code-snippet__string">"()V"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitVarInsn(ALOAD, <span class="code-snippet__number">1</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitLdcInsn(<span class="code-snippet__string">": "</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitVarInsn(ALOAD, <span class="code-snippet__number">1</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKESTATIC, <span class="code-snippet__string">"org/xunche/agent/TimeHolder"</span>, <span class="code-snippet__string">"cost"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)J"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"append"</span>, <span class="code-snippet__string">"(J)Ljava/lang/StringBuilder;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/lang/StringBuilder"</span>, <span class="code-snippet__string">"toString"</span>, <span class="code-snippet__string">"()Ljava/lang/String;"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> mv.visitMethodInsn(INVOKEVIRTUAL, <span class="code-snippet__string">"java/io/PrintStream"</span>, <span class="code-snippet__string">"println"</span>, <span class="code-snippet__string">"(Ljava/lang/String;)V"</span>, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">上述的代码略长, asm 的部分可以略过。</span> <span style="font-size: 15px;">我们通过 instrumentation.addTransformer 注册一个转换器,转换器重写了 transform 方法,方法入参中的 classfileBuffer 表示的是原始的字节码,方法返回值表示的是真正要进行加载的字节码。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">onMethodEnter 方法中的代码含义是调用 TimeHolder 的 start 方法并传入当前的方法名。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;">onMethodExit 方法中的代码含义是调用 TimeHolder 的 cost 方法并传入当前的方法名,并打印 cost 方法的返回值。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span