作者:微信小助手
<p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"><br></p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 前几天在B站刷到AI孙燕姿唱的《黑色毛衣》,让我听傻了,还原度还能这么高的。于是就研究了一下,做了几段居然效果还行,可以在这里听《爱得太迟》。 </p> <section class="channels_iframe_wrp"> <mp-common-videosnap class="js_uneditable custom_select_card channels_iframe videosnap_video_iframe" data-pluginname="mpvideosnap" data-weui-theme="light" data-url="https://findermp.video.qq.com/251/20350/stodownload?encfilekey=oibeqyX228riaCwo9STVsGLPj9UYCicgttvRGb0ibgTJM0xsIXiboNlEw4tp7vswo7LUJoWbKeLrPFtGLH1iatJibvQdRwO2l66rDp23KPDhPjdWhbqDOfQuOsGTbeQbnNQmFKDcGSTEpkrtos&adaptivelytrans=0&bizid=1023&dotrans=0&hy=SH&idx=1&m=48a44e59403c123265de83de1d117903&token=AxricY7RBHdXHBjDaXh90wHtMzlZ8e8TvlKpKY4Fsg4bbVZPvCYialZtnJ1PYsBaSgLbXjTia1Zoms" data-headimgurl="http://wx.qlogo.cn/finderhead/EiaeLNTCu3EW3KLfetdWEbxibyLQIupbCeOmlKsfPL0LA/0" data-username="v2_060000231003b20faec8c7eb8911c6d4cf00e432b077bc294083c45937fd15ae09793232d264@finder" data-nickname="歸藏2079" data-desc="AI孙燕姿《爱的太迟》,这个效果好太多了。 测试了一下粤语的效果。" data-nonceid="17050541699999615895" data-type="video" data-mediatype="undefined" data-authiconurl="" data-from="new" data-width="1920" data-height="1080" data-id="export/UzFfAgtgekIEAQAAAAAAKesL6yDIdgAAAAstQy6ubaLX4KHWvLEZgBPE2qMsbGlnZJiAzNPgMIsXc2OFy9A4nu5yV9mXtxss"></mp-common-videosnap> </section> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 简单解释一下,这个项目分为两个部分,模型的使用和模型的训练,模型使用的话对电脑的要求不是很高基本上差不多点的N卡都可以,模型的训练其实对显卡要求还挺高的小于6G会有各种各样的问题,当然也能炼,不过太麻烦了不建议。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 今天上午我更新了这个教程的第一个部分《<a href="https://mp.weixin.qq.com/s?__biz=MzU0MDk3NTUxMA==&mid=2247484118&idx=1&sn=c6d885bb0fa18664cc9741c02d1419e7&chksm=fb304a2fcc47c3399bb188d66c88aa2512dd4bc6f6dea339bcf844865b5453807733d845c51a&scene=21#wechat_redirect" style="color: rgb(252, 121, 48);" rel="noopener noreferrer" data-token-index="1" tabindex="0" data-linktype="2"><span style="border-bottom: 0.05em solid rgba(252, 121, 48, 0.4);border-top-color: rgba(252, 121, 48, 0.4);border-right-color: rgba(252, 121, 48, 0.4);border-left-color: rgba(252, 121, 48, 0.4);">教你打造属于自己的AI孙燕姿,AI歌手模型使用及训练保姆级课程 #1/2 使用模型</span></a>》如果还没有看的话可以先看一下,有一部分内容这节也会用到。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 主要使用的是So-VITS-SVC 4.0这个项目,github地址是:<span style="border-bottom: 0.05em solid rgba(252, 121, 48, 0.4);border-top-color: rgba(252, 121, 48, 0.4);border-right-color: rgba(252, 121, 48, 0.4);border-left-color: rgba(252, 121, 48, 0.4);">https://github.com/svc-develop-team/so-vits-svc</span> </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 我这里会使用整合包来推理(使用模型)和训练,目前B站有两个作者做的整合包分别是羽毛布団的和领航员未鸟的我把视频地址都放在下面。希望各位去视频给个三连毕竟用了人家的劳动成果。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 两位的视频教程也很好,我这边主要会更详细一些,会补充一些两位没说到但是有坑的地方。这次课程我使用的主要是羽毛布団的整合包,我把所有需要下载的都打包了。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 羽毛布団:<span style="border-bottom: 0.05em solid rgba(252, 121, 48, 0.4);border-top-color: rgba(252, 121, 48, 0.4);border-right-color: rgba(252, 121, 48, 0.4);border-left-color: rgba(252, 121, 48, 0.4);">https://www.bilibili.com/video/BV1H24y187Ko/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=e99f85042059f2864f5cca20d71575f0</span> </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 领航员未鸟:<span style="border-bottom: 0.05em solid rgba(252, 121, 48, 0.4);border-top-color: rgba(252, 121, 48, 0.4);border-right-color: rgba(252, 121, 48, 0.4);border-left-color: rgba(252, 121, 48, 0.4);">https://www.bilibili.com/video/BV1Eb411f7gX/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=e99f85042059f2864f5cca20d71575f0</span> </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 所需软件和模型下载(百度云):<span style="border-bottom: 0.05em solid rgba(252, 121, 48, 0.4);border-top-color: rgba(252, 121, 48, 0.4);border-right-color: rgba(252, 121, 48, 0.4);border-left-color: rgba(252, 121, 48, 0.4);">https://pan.baidu.com/s/1n_3j9NCAn5LwU8mb3IGCMg</span> 提取码:回复提取码获取。 </p> <h1 style="color: rgb(252, 121, 48);font-size: 18px;text-align: center;line-height: 1.75;font-weight: bold;display: table;margin-right: auto;margin-bottom: 1em;margin-left: auto;padding-right: 1em;padding-left: 1em;border-bottom: 2px solid rgb(252, 121, 48);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">训练模型</h1> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 接下来正式开始训练模型的部分,训练模型主要有两个步骤分别是数据准备和模型的训练。 </p> <h2 style="background: rgb(252, 121, 48);color: rgb(255, 255, 255);font-size: 17px;text-align: center;line-height: 1.75;font-weight: bold;display: table;margin: 4em auto 2em;padding-right: 0.2em;padding-left: 0.2em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">数据准备 </h2> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 首先我们需要准备你训练的人的声音素材,尽量找质量比较高人声比较清晰的音频。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 我们就以歌手为例子,歌手的声音素材是比较好找的,因为他们的歌就是天然的素材,我们在训练的时候最少要准备30分钟以上的人声素材,一般一个小时到两个小时最好。但是声音的质量大于时间长度,不要为了凑数搞一些质量不那么好的素材。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 在准备好足够的声音素材之后我们开始对素材进行处理,跟第一期一样,先把我们的素材转换为WAV格式,批量转换的话还是用格式工厂之类的本地软件比较快。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 获取到我们个WAV格式素材之后,继续进行跟上午一样的步骤去掉我们素材的伴奏以及混响之类的声音,只留下单纯的人声。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 这里我们还是使用UVR_v5.5.0这个软件每个素材都进行两次处理。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 在Select Input选择你需要处理的音频文件,处理完成后你可以在Output的文件夹下面找到处理完成的文件,后缀有(Vocals)就是人声,后缀为(Instrumental)就是伴奏,伴奏先不要删,我们后面合成的时候还需要。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 下图为第一次处理UVR的参数: </p> <figure> <img class="rich_pages wxw-img" data-ratio="1.0745" src="/upload/95b7c44830f4a02b52ea1860db2ed97c.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 完成第一次处理后我们再调整参数进行第二次处理,下面是第二次处理需要的参数设置: </p> <figure> <img class="rich_pages wxw-img" data-ratio="1.0745" src="/upload/170248f5cc7fd27f59600e71a97b95c8.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 处理完成后扔掉分离出来的伴奏,只留下人声素材,整理好备用。类似我下图这样扔到一个文件夹里。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.688" src="/upload/3a396f930e3f0c2cb192da8a0a1c6e74.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 接下来我们要对处理好的人声文件进行分割,因为如果训练的时候每段文件过长的话容易爆显存。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 这个时候就要用到下载文件里的【slicer-gui】这个软件了,它可以自动把声音素材分割成合适的大小。我们先打开slicer-gui,刚开始的参数按我的来就行。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 把你你准备好的人声素材拖到【Task List】里面,在Output位置设置好输出文件夹的位置,然后点Start就可以开始分割了。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.661" src="/upload/9fb47ed7f934568532f1055a6b34e9cc.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 处理好的文件,基本上就是下面这个文件的样子,处理完成后在输出文件夹把文件从大到小排序,看一下最大的文件时多长的,分割完的素材每一段尽量不要超过15秒。不然有可能会爆显存。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 如果你发现有几条素材比较大的话可以拖进slicer-gui里面重新分割一下,参数按我下面图片设置就行。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.661" src="/upload/977b28b8350ea4723c750e0938ebf371.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 所有数据处理好之后,我们准备开始训练了首先需要把准备好的素材移动到\so-vits-svc\dataset_raw这个文件夹下,注意不要直接把素材放在dataset_raw文件夹里,拿个文件夹装好放进去,所有的目录不要有中文字符。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.4775" src="/upload/7c35527be17c28bfcf1136f52dbefe65.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <h2 style="background: rgb(252, 121, 48);color: rgb(255, 255, 255);font-size: 17px;text-align: center;line-height: 1.75;font-weight: bold;display: table;margin: 4em auto 2em;padding-right: 0.2em;padding-left: 0.2em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">模型训练 </h2> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 我们开始模型训练,运行so-vits-svc根目录的【启动webui.bat】打开Web UI界面,切换到训练Tab下面。然后点击识别数据集,这时候上面就会展示你数据集文件夹的名字,也会是你模型的名字。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.516" src="/upload/c67e5b191c9ea2e08830212eca524b9b.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 之后就是选择与训练分支了,【vec768-layer12】好像效果会好一些,所以这里我选了这个分支。后就是点击【数据预处理】。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> <span style="font-weight:600;color:rgba(212, 76, 71, 1);fill:rgba(212, 76, 71, 1);" data-token-index="0">注意这里有个大坑,昨天折腾了我好久,你需要看一下你数据集里面有多少条数据,如果有几百条的的话,你需要把虚拟内存调大点,至于如何调整虚拟内存,这个百度就行,有很多教程。</span> </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 给个参考我的数据集是300多条,我把虚拟内存调到100G才能保证顺利处理完,不会中途蓝屏。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.5075" src="/upload/b2378bf90eafbc532d89094fc673a628.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 开始数据预处理之后这个框会有非常多的信息,基本都是进度到百分之几了,如果预处理出错,在这个框的最后会展示报错信息,如果没错这个回显的基本就到100%就结束了。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.542" src="/upload/631d29cb4a03446d2a60885fa2bf9a88.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 如果你数据预处理完了,不想看那一堆信息的话可以点那个【清空输出信息】。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 数据处理完之后我们来看一下下面的几个参数,调整一下,准备开始训练。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 每多少步生成一次评估日志这里,用默认的200步就行 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 【每隔多少步(steps)验证并保存一次模型】这里默认的800步也就够了,他的意思是每训练800步就会保存一次模型,这个保存的模型你是可以用的。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 【仅保留最新的X个模型】这个就是字面意思如果每800步保存一次模型的话,你训练到8800的时候第800步的模型就会被自动删除,一个模型大概有1G左右这里看你的硬盘。如果设置成0的话就永远不会自动删除。 </p> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 【批量大小】这个参数跟你的显卡的显存有关,6G建议是4,我的4070Ti是12G我昨天设置的8,我有点怂,其实12也行,我怕爆显存。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.495" src="/upload/457fc32b0557c1427cd444ec826c52fe.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 上面几个参数设置完之后,我们选择当前训练分支跟我们数据预处理的时候的一致就行,然后点击写入配置文件,输出信息那里会有写入的结果,如果有报错也会显示在那里。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.573" src="/upload/35eebcec866e0a3fbc55bd20683cdc4b.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 如果你是第一次训练点击这个【从头开始训练】就行,如果你之前训练过你还想继续训练的话就点这个【继续上一次训练进度】。如果你之前有训练进度,然后你点了【从头开始】的话你的训练进度就会被清空,从新开始从第0步训练。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.463" src="/upload/d09b56983bf4a6cf8ad87dce63b5d5b9.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 在你点击按钮之后会弹出这样一个弹窗里面就是训练进度,我框起来的地方就是每200步输出的信息,那个loss的值就是判断模型质量好坏的标准,越低越好,如果你觉得现在的已经可以了的话按CTAL+C键就会停止训练,你可以去推理tab下尝试你的模型,如果不满意还是可以重新回来训练的。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.552" src="/upload/f1642f92a1bd238ddee60d9e28ef454c.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 注意你如果设置的每800步保存的话起码要到800才能暂停训练,不然没有保存的模型供你使用。下面这个图就是模型已经保存的提示。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.552" src="/upload/a350e661859e3c8306a10f95b5c58620.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 如果你觉得可以了暂停训练之后回到推理 Tab 就能看到你刚才训练的模型了,可能会有好几个因为你选的最多保留十个。按照我们第一期的内容正常使用就可以了。 </p> <figure> <img class="rich_pages wxw-img" data-ratio="0.48" src="/upload/a803d59eec580aa5f57a589030cf58c.png" data-type="other" data-w="2000" style="border-radius: 8px;" width="100%"> <figcaption style="color: #37352FA6;text-align: center;font-size: 14px;"></figcaption> </figure> <p style="text-align: left;line-height: 1.75;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;margin-block: 10px;white-space: normal;color: rgb(63, 63, 63);font-size: 14px;"> 以上就是AI歌手的最后一部分内容了,感谢各位,如果觉得对你有帮助的话可以帮助藏师傅转发传播。 </p> <p><br></p> <p style="display: none;"> <mp-style-type data-value="3"></mp-style-type></p>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;letter-spacing: 0px;white-space: normal;font-size: 16px;padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 24px;" data-mpa-powered-by="yiban.io"> <section class="mp_profile_iframe_wrp"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="Mzg4NjYyODc4OA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/J4jTHmo8Xh6qM32ASOtVbXNoiaegrI26qLRw6r6FTI7dZw6TMT7vecvnjd1O8xSsM5MiajIuQZicxSC6KFK8TMpbg/0?wx_fmt=png" data-nickname="java突击队" data-alias="" data-signature="技术经验分享" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">哈喽大家好啊,我是苏三,今天来和大家聊聊服务的限流。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">服务限流,是指通过控制请求的速率或次数来达到保护服务的目的,在微服务中,我们通常会将它和熔断、降级搭配在一起使用,来避免瞬时的大量请求对系统造成负荷,来达到保护服务平稳运行的目的。下面就来看一看常见的6种限流方式,以及它们的实现与使用。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">固定窗口算法</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">固定窗口算法通过在单位时间内维护一个计数器,能够限制在每个固定的时间段内请求通过的次数,以达到限流的效果。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4181547619047619" src="/upload/ab0f794d92da60733176b9b6d57550fb.jpg" data-type="jpeg" data-w="672" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">算法实现起来也比较简单,可以通过构造方法中的参数指定时间窗口大小以及允许通过的请求数量,当请求进入时先比较当前时间是否超过窗口上边界,未越界且未超过计数器上限则可以放行请求。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs0K9AZ1YYTwjjYG8bZQCEKkQVLtejSdc1PhZpaRcty9l9kOfhsibr4LictShFWnZRfdvLMgbvG9JL8Ws6KZ4TcUk5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(97, 174, 238);line-height: 26px;">@Slf</span>4j<br><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">class</span> <span style="color: rgb(230, 192, 123);line-height: 26px;">FixedWindowRateLimiter</span> </span>{<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 时间窗口大小,单位毫秒</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> windowSize;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 允许通过请求数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> maxRequestCount;<br><br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 当前窗口通过的请求计数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> AtomicInteger count=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> AtomicInteger(<span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>);<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 窗口右边界</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> windowBorder;<br><br> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">FixedWindowRateLimiter</span><span style="line-height: 26px;">(<span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> windowSize,<span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> maxRequestCount)</span></span>{<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.windowSize = windowSize;<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.maxRequestCount = maxRequestCount;<br> windowBorder = System.currentTimeMillis()+windowSize;<br> }<br><br> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">synchronized</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">boolean</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">tryAcquire</span><span style="line-height: 26px;">()</span></span>{<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> currentTime = System.currentTimeMillis();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (windowBorder < currentTime){<br> log.info(<span style="color: rgb(152, 195, 121);line-height: 26px;">"window reset"</span>);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">do</span> {<br> windowBorder += windowSize;<br> }<span style="color: rgb(198, 120, 221);line-height: 26px;">while</span>(windowBorder < currentTime);<br> count=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> AtomicInteger(<span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>);<br> }<br><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (count.intValue() < maxRequestCount){<br> count.incrementAndGet();<br> log.info(<span style="color: rgb(152, 195, 121);line-height: 26px;">"tryAcquire success"</span>);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">true</span>;<br> }<span style="color: rgb(198, 120, 221);line-height: 26px;">else</span> {<br> log.info(<span style="color: rgb(152, 195, 121);line-height: 26px;">"tryAcquire fail"</span>);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">false</span>;<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">进行测试,允许在1000毫秒内通过5个请求:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs0K9AZ1YYTwjjYG8bZQCEKkQVLtejSdc1PhZpaRcty9l9kOfhsibr4LictShFWnZRfdvLMgbvG9JL8Ws6KZ4TcUk5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">void</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">test</span><span style="line-height: 26px;">()</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">throws</span> InterruptedException </span>{<br> FixedWindowRateLimiter fixedWindowRateLimiter<br> = <span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> FixedWindowRateLimiter(<span style="color: rgb(209, 154, 102);line-height: 26px;">1000</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">5</span>);<br><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (<span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> i = <span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>; i < <span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>; i++) {<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (fixedWindowRateLimiter.tryAcquire()) {<br> System.out.println(<span style="color: rgb(152, 195, 121);line-height: 26px;">"执行任务"</span>);<br> }<span style="color: rgb(198, 120, 221);line-height: 26px;">else</span>{<br> System.out.println(<span style="color: rgb(152, 195, 121);line-height: 26px;">"被限流"</span>);<br> TimeUnit.MILLISECONDS.sleep(<span style="color: rgb(209, 154, 102);line-height: 26px;">300</span>);<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">运行结果:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.8038740920096852" src="/upload/9f61d2f68bac58784da8c0ed1dbaf17.png" data-type="png" data-w="826" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">固定窗口算法的优点是实现简单,但是可能无法应对突发流量的情况,比如每秒允许放行100个请求,但是在0.9秒前都没有请求进来,这就造成了在0.9秒到1秒这段时间内要处理100个请求,而在1秒到1.1秒间可能会再进入100个请求,这就造成了要在0.2秒内处理200个请求,这种流量激增就可能导致后端服务出现异常。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4714828897338403" src="/upload/80313efffe6c186ec407bf20b528dbb7.jpg" data-type="jpeg" data-w="526" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">滑动窗口算法</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">滑动窗口算法在固定窗口的基础上,进行了一定的升级改造。它的算法的核心在于将时间窗口进行了更精细的分片,将固定窗口分为多个小块,每次仅滑动一小块的时间。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5422297297297297" src="/upload/ff3c4603171cf1496931b1a33f020e01.jpg" data-type="jpeg" data-w="592" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">并且在每个时间段内都维护了单独的计数器,每次滑动时,都减去前一个时间块内的请求数量,并再添加一个新的时间块到末尾,当时间窗口内所有小时间块的计数器之和超过了请求阈值时,就会触发限流操作。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">看一下算法的实现,核心就是通过一个<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">int</code>类型的数组循环使用来维护每个时间片内独立的计数器:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs0K9AZ1YYTwjjYG8bZQCEKkQVLtejSdc1PhZpaRcty9l9kOfhsibr4LictShFWnZRfdvLMgbvG9JL8Ws6KZ4TcUk5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(97, 174, 238);line-height: 26px;">@Slf</span>4j<br><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">class</span> <span style="color: rgb(230, 192, 123);line-height: 26px;">SlidingWindowRateLimiter</span> </span>{<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 时间窗口大小,单位毫秒</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> windowSize;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 分片窗口数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> shardNum;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 允许通过请求数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> maxRequestCount;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 各个窗口内请求计数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span>[] shardRequestCount;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 请求总数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> totalCount;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 当前窗口下标</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> shardId;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 每个小窗口大小,毫秒</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> tinyWindowSize;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 窗口右边界</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">private</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> windowBorder;<br><br> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">SlidingWindowRateLimiter</span><span style="line-height: 26px;">(<span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> windowSize, <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> shardNum, <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> maxRequestCount)</span> </span>{<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.windowSize = windowSize;<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.shardNum = shardNum;<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.maxRequestCount = maxRequestCount;<br> shardRequestCount = <span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span>[shardNum];<br> tinyWindowSize = windowSize/ shardNum;<br> windowBorder=System.currentTimeMillis();<br> }<br><br> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">synchronized</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">boolean</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">tryAcquire</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> currentTime = System.currentTimeMillis();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (currentTime > windowBorder){<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">do</span> {<br> shardId = (++shardId) % shardNum;<br> totalCount -= shardRequestCount[shardId];<br> shardRequestCount[shardId]=<span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>;<br> windowBorder += tinyWindowSize;<br> }<span style="color: rgb(198, 120, 221);line-height: 26px;">while</span> (windowBorder < currentTime);<br> }<br><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (totalCount < maxRequestCount){<br> log.info(<span style="color: rgb(152, 195, 121);line-height: 26px;">"tryAcquire success,{}"</span>,shardId);<br> shardRequestCount[shardId]++;<br> totalCount++;<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">true</span>;<br> }<span style="color: rgb(198, 120, 221);line-height: 26px;">else</span>{<br> log.info(<span style="color: rgb(152, 195, 121);line-height: 26px;">"tryAcquire fail,{}"</span>,shardId);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">false</span>;<br> }<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">进行一下测试,对第一个例子中的规则进行修改,每1秒允许100个请求通过不变,在此基础上再把每1秒等分为10个0.1秒的窗口。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs0K9AZ1YYTwjjYG8bZQCEKkQVLtejSdc1PhZpaRcty9l9kOfhsibr4LictShFWnZRfdvLMgbvG9JL8Ws6KZ4TcUk5/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">void</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">test</span><span style="line-height: 26px;">()</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">throws</span> InterruptedException </span>{<br> SlidingWindowRateLimiter slidingWindowRateLimiter<br> = <span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> SlidingWindowRateLimiter(<span style="color: rgb(209, 154, 102);line-height: 26px;">1000</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>);<br> TimeUnit.MILLISECONDS.sleep(<span style="color: rgb(209, 154, 102);line-height: 26px;">800</span>);<br><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (<span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> i = <span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>; i < <span style="color: rgb(209, 154, 102);line-height: 26px;">15</span>; i++) {<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">boolean</span> acquire = slidingWindowRateLimiter.tryAcquire();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (acquire){<br> System.out.println(<span style="color: rgb(152, 195, 121);line-height: 26px;">"执行任务"</span>);<br> }<span style="color: rgb(198, 120, 221);line-height: 26px;">else</span>{<br> System.out.println(<span style="color: rgb(152, 195, 121);line-height: 26px;">"被限流"</span>);<br> }<br> TimeUnit.MILLISECONDS.sleep(<span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">查看运行结果:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.872093023255814" src="/upload/a8fe9bd067fa0ec805154dbe3da0df54.png" data-type="png" data-w="946" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">程序启动后,在先休眠了一段时间后再发起请求,可以看到在0.9秒到1秒的时间窗口内放行了6个请求,在1秒到1.1秒内放行了4个请求,随后就进行了限流,解决了在固定窗口算法中相邻时间窗口内允许通过大量请求的问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">滑动窗口算法通过将时间片进行分片,对流量的控制更加精细化,但是相应的也会浪费一些存储空间,用来维护每一块时间内的单独计数,并且还没有解决固定窗口中可能出现的流量激增问题。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">漏桶算法</span><span sty
作者:微信小助手
<pre style="margin-bottom: 0em;outline: 0px;max-width: 100%;color: rgb(34, 34, 34);letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 16px;text-align: left;word-spacing: 0.8px;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;" data-mpa-powered-by="yiban.io"> <section> <br> </section></pre> <section class="mp_profile_iframe_wrp"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="MzkyNTI5NTQ1NQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/wxcY9TH8dPsYAnrjaZktBe0iahF8ic9QkF26cAw8pK6HPR1bfFEImdyJspvkQvQwmnYxP4eEVW60ewVVickcWXnrQ/0?wx_fmt=png" data-nickname="架构文摘" data-alias="ArchDigest" data-signature="每天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性能、高稳定)、大数据、机器学习、Java架构等各个热门领域。" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <p style="outline: 0px;max-width: 100%;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;margin-bottom: 0px;"><span style="color: rgb(136, 136, 136);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 12px;letter-spacing: 0.8px;word-spacing: 0.8px;text-align: right;"><br></span></p> <p style="outline: 0px;max-width: 100%;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;margin-bottom: 0px;"><span style="color: rgb(136, 136, 136);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 12px;letter-spacing: 0.8px;word-spacing: 0.8px;text-align: right;">来源:https://blog.csdn.net/zqqiang0307/article/details/120458586</span><br></p> <p style="text-align: right;margin-bottom: 0px;"><br></p> <section style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: rgb(154, 154, 154);font-size: 15px;margin-bottom: 0px;"> <span style="font-size: 16px;color: rgb(53, 53, 53);letter-spacing: 0.8px;word-spacing: 0.8px;"></span> </section> <section style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: rgb(154, 154, 154);font-size: 15px;margin-bottom: 0px;"> <span style="font-size: 16px;color: rgb(53, 53, 53);letter-spacing: 0.8px;word-spacing: 0.8px;">本文章实现最简单全面的Jenkins+docker+springboot 一键自动部署项目,步骤齐全,少走坑</span> <span style="font-size: 16px;color: rgb(53, 53, 53);letter-spacing: 0.8px;word-spacing: 0.8px;">路。</span> <br> </section> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;"><span style="font-weight: 700;color: rgb(248, 57, 41);">环境</span>:centos7+git(gitee)</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">简述实现步骤:在docker安装jenkins,配置jenkins基本信息,利用Dockerfile和shell脚本实现项目自动拉取打包并运行。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;"><span style="padding-left: 10px;color: rgb(34, 34, 34);display: inline-block;border-left: 5px solid rgb(248, 57, 41);font-size: 16px;">安装docker</span></h2> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">docker 安装社区版本CE</p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">确保 yum 包更新到最新。</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">yum update<br></span></code></pre> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">卸载旧版本(如果安装过旧版本的话)</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">yum remove docker docker-common docker-selinux docker-engine<br></span></code></pre> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">安装需要的软件包</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">yum install -y yum-utils device-mapper-persistent-data lvm2<br></span></code></pre> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">设置yum源</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo<br></span></code></pre> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">安装docker</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">yum install docker-ce <span style="color: rgb(117, 113, 94);line-height: 26px;">#由于repo中默认只开启stable仓库,故这里安装的是最新稳定版17.12.0</span><br>yum install <自己的版本> <span style="color: rgb(117, 113, 94);line-height: 26px;"># 例如:sudo yum install docker-ce-17.12.0.ce</span><br></span></code></pre> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">启动和开机启动</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">systemctl start docker<br>systemctl <span style="color: rgb(166, 226, 46);line-height: 26px;">enable</span> docker<br></span></code></pre> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">验证安装是否成功</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">docker version<br></span></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;"><span style="padding-left: 10px;color: rgb(34, 34, 34);display: inline-block;border-left: 5px solid rgb(248, 57, 41);font-size: 16px;">安装Jenkins</span></h2> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">Jenkins中文官网:https://www.jenkins.io/zh/</p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">安装J enkins</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">docker 安装一切都是那么简单,注意检查8080是否已经占用!如果占用修改端口</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">docker run --name jenkins -u root --rm -d -p 8080:8080 -p 50000:50000 -v /var/jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenkinsci/blueocean<br></span></code></pre> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">如果没改端口号的话</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">安装完成后访问地址-> <code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">http://{部署Jenkins所在服务IP}:8080</span></code></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">此处会有几分钟的等待时间。</p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">初始化 Jenkins</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">详情见官网教程-><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">https://www.jenkins.io</span></code></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">解锁 Jenkins</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">进入Jenkins容器:<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">docker exec -it {Jenkins容器名} bash</span></code></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">例如 <code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">docker exec -it jenkins bash</span></code></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">查看密码:<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">cat /var/lib/jenkins/secrets/initialAdminPassword</span></code></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">复制密码到输入框里面</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.4394394394394394" src="/upload/8d59f3f7eb01415dce139ba1bf0dca2c.png" data-type="png" data-w="999" style=";"></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">安装插件</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">选择第一个:安装推荐的插件</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;line-height: 1.75;text-align: center;"><span style="margin-right: auto;margin-left: auto;max-width: 95%;font-size: 16px;"><img class="rich_pages wxw-img" data-ratio="0.5387596899224806" src="/upload/f3b276c76c7a256ad95bf13469664741.png" data-type="png" data-w="774" style=";"></span></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">创建管理员用户</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">此账户一定要记住哦</p> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;"><span style="padding-left: 10px;color: rgb(34, 34, 34);display: inline-block;border-left: 5px solid rgb(248, 57, 41);font-size: 16px;">系统配置</span></h2> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">安装需要插件</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">进入【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">首页</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">系统管理</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">插件管理</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">可选插件</span></code>】</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">搜索以下需要安装的插件,点击安装即可。</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.8373493975903614" src="/upload/9eddd8d1f4496301e0b5d34434fac9df.png" data-type="png" data-w="498" style=";"></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 567.429px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;color: rgb(248, 57, 41);"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 安装Maven Integration </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 安装Publish Over SSH(如果不需要远程推送,不用安装) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 如果使用Gitee 码云,安装插件Gitee(Git自带不用安装) </section></li> </ul> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">配置Maven</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">进入【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">首页</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">系统管理</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">全局配置</span></code>】,拉到最下面maven–maven安装</p> <p style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;text-align: center;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-ratio="0.42962962962962964" src="/upload/6659584065432d290b0c3220f03e4a82.png" data-type="png" data-w="1080" style=";"></p> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;"><span style="padding-left: 10px;color: rgb(34, 34, 34);display: inline-block;border-left: 5px solid rgb(248, 57, 41);font-size: 16px;">创建任务</span></h2> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">新建任务</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">点击【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">新建任务</span></code>】,输入任务名称,点击构建一个自由风格的软件项目</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.28762135922330095" src="/upload/97c7957c127aa781865f3635ef57c01b.png" data-type="png" data-w="824" style=";"></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">源码管理</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">点击【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">源码管理</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">Git</span></code>】,输入仓库地址,添加凭证,选择好凭证即可。<br></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.7423664122137404" src="/upload/45ec719f982c0fbfdc2db5ae180713b7.png" data-type="png" data-w="524" style=";"><img class="rich_pages wxw-img" data-ratio="1.1605504587155964" src="/upload/df8d51c7da22c52124749e35b4af317f.png" data-type="png" data-w="436" style=";"></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">构建触发器</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">点击【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">构建触发器</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">构建</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">增加构建步骤</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">调用顶层Maven目标</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">填写配置</span></code>】–【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">保存</span></code>】</p> <p style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;text-align: center;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-ratio="0.969047619047619" src="/upload/6b14f58fb5a197dad6f2da79e6963e76.png" data-type="png" data-w="840" style=";"></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">此处命令只是install,看是否能生成jar包</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">clean install -Dmaven.test.skip=<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">true</span><br></span></code></pre> <p style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-ratio="0.3881401617250674" src="/upload/281eac54a2bd6a87fe95917d053e78fe.png" data-type="png" data-w="742" style=";"></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">保存</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">点击【<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">保存</span></code>】按钮即可</p> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;"><span style="padding-left: 10px;color: rgb(34, 34, 34);display: inline-block;border-left: 5px solid rgb(248, 57, 41);font-size: 16px;">测试</span></h2> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">该功能测试是否能正常打包</p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">构建</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">点击构建按钮<img class="rich_pages wxw-img" data-ratio="0.10740740740740741" data-type="png" data-w="1080" src="/upload/40436b09757fc0f0a89ea481dc679255.png" style=";"></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">查看日志</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">点击正在构建的任务,或者点击任务名称,进入详情页面,查看控制台输出,看是否能成功打成jar包。</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">该处日志第一次可能下载依赖jar包失败,再次点击构建即可成功。</p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.083086053412463" src="/upload/14a11725cee4922edebd36da6f6bed52.png" data-type="png" data-w="674" style="height: auto !important;border-radius: 0px;"><img class="rich_pages wxw-img" data-ratio="0.5918803418803419" src="/upload/fddabd4b6ddcb1aab302f040f3ffefc0.png" data-type="png" data-w="936" style=";"></p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">查看项目位置</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;"><span style="color: rgb(166, 226, 46);line-height: 26px;">cd</span> /var/jenkins_home/workspace<br></span></code></pre> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;"><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);"><span style="font-size: 16px;">ll</span></code>命令即可查看是否存在</p> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;"><span style="padding-left: 10px;color: rgb(34, 34, 34);display: inline-block;border-left: 5px solid rgb(248, 57, 41);font-size: 16px;">运行项目</span></h2> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">因为我们项目和jenkins在同一台服务器,所以我们用shell脚本运行项目,原理既是通过dockerfile 打包镜像,然后docker运行即可。</p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">Dockerfile</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">在springboot项目根目录新建一个名为Dockerfile的文件,注意没有后缀名,其内容如下:(大致就是使用jdk8,把jar包添加到docker然后运行prd配置文件)</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">FROM jdk:8<br>VOLUME /tmp<br>ADD target/zx-order-0.0.1-SNAPSHOT.jar app.jar<br>EXPOSE 8888<br>ENTRYPOINT [<span style="color: rgb(166, 226, 46);line-height: 26px;">"Bash"</span>,<span style="color: rgb(166, 226, 46);line-height: 26px;">"-DBash.security.egd=file:/dev/./urandom"</span>,<span style="color: rgb(166, 226, 46);line-height: 26px;">"-jar"</span>,<span style="color: rgb(166, 226, 46);line-height: 26px;">"/app.jar"</span>,<span style="color: rgb(166, 226, 46);line-height: 26px;">"--spring.profiles.active=prd"</span>]<br></span></code></pre> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">修改jenkins任务配置</h6> <p style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;text-align: center;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-ratio="1.0557029177718833" src="/upload/b26e59f2d907281544b6ab205990b05.png" data-type="png" data-w="754" style=";"></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">配置如下:</p> <p style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-ratio="0.575" src="/upload/8cfa25731d5c07712801ba37c954baa7.png" data-type="png" data-w="1080" style=";"></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">-t:指定新镜像名<br>.:表示Dockfile在当前路径<br></span></code></pre> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;"><span style="color: rgb(166, 226, 46);line-height: 26px;">cd</span> /var/jenkins_home/workspace/zx-order-api<br>docker stop zx-order || <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">true</span><br>docker rm zx-order || <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">true</span><br>docker rmi zx-order || <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">true</span><br>docker build -t zx-order .<br>docker run -d -p 8888:8888 --name zx-order zx-order:latest<br></span></code></pre> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">备注:</p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 567.429px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;color: rgb(248, 57, 41);"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 我上图用了docker logs -f 是为了方便看日志,真实不要用,因为会一直等待日志,构建任务会失败 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 加|| true 是如果命令执行失败也会继续实行,为了防止第一次没有该镜像报错 </section></li> </ul> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">保存</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">点击保存即可</p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">构建</h6> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.6337148803329865" src="/upload/95eae505deb12253b0baa05e2bca5523.png" data-type="png" data-w="961" style=";"></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">查看jenkins控制台输出,输出如下,证明成功!</p> <h6 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;color: black;">验证</h6> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(53, 53, 53);letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;"><code style="padding: 16px;overflow-x: auto;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;"><span style="font-size: 16px;">docker ps 查看是否有自己的容器<br>docker logs 自己的容器名 查看日志是否正确<br></span></code></pre> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;font-size: 16px;line-height: 1.75;">浏览器访问项目试一试</p>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;text-align: left;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"></h2> <h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;outline: 0px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;text-align: left;white-space: normal;background: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5gWohqZCBBOjUa71ia1g9Gfq7AmkLiarecQ4CVU3BdAL6tz2DLWnmtbkqpAbuz18O9NiakfQ07h2QqgA/640?wx_fmt=png") center center / 50px no-repeat rgb(255, 255, 255);color: black;visibility: visible;"><span style="outline: 0px;color: rgb(0, 82, 255);font-family: -apple-system, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-weight: bolder;letter-spacing: 0.544px;font-size: 18px;visibility: visible;">大家好,我是苏三,又跟大家见面了。</span></h2> <p style="margin-bottom: 0px;outline: 0px;color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);visibility: visible;"><span style="outline: 0px;color: rgb(0, 82, 255);font-family: -apple-system, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-weight: bolder;letter-spacing: 0.544px;font-size: 18px;visibility: visible;">文末留言送书啦!!!</span></p> <p style="text-align: center;"><br></p> <p style="margin-bottom: 0px;outline: 0px;color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: right;white-space: normal;background-color: rgb(255, 255, 255);visibility: visible;">Java技术突击网站:http://www.susan.net.cn</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">前言</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分类树</code>查询功能,在各个业务系统中可以说随处可见,特别是在电商系统中。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.4101851851851852" src="/upload/fcc9cdf4e42bd1577461e2c579b66681.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">但就是这样一个简单的分类树查询功能,我们却优化了<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">5</code>次。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">到底是怎么回事呢?</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">背景</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们的网站使用了<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">SpringBoot</code>推荐的模板引擎:<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Thymeleaf</code>,进行动态渲染。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">它是一个XML/XHTML/HTML5模板引擎,可用于Web与非Web环境中的应用开发。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">它提供了一个用于整合SpringMVC的可选模块,在应用开发中,我们可以使用Thymeleaf来完全代替JSP或其他模板引擎,如Velocity\FreeMarker等。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">前端开发写好Thymeleaf的模板文件,调用后端接口获取数据,进行动态绑定,就能把想要的内容展示给用户。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">由于当时这个是从0-1的新项目,为了开快速开发功能,我们第一版接口,直接从数据库中查询<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分类</code>数据,组装成<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分类树</code>,然后返回给前端。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">通过这种方式,简化了数据流程,快速把整个页面功能调通了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">第1次优化</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们将该接口部署到dev环境,刚开始没啥问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">随着开发人员添加的分类越来越多,很快就暴露出性能瓶颈。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们不得不做优化了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们第一个想到的是:<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">加Redis缓存</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">流程图如下:<img class="rich_pages wxw-img" data-ratio="2.0433212996389893" src="/upload/1bc391b17fbc64e157379ffd38cf6bc3.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">于是暂时这样优化了一下:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 用户访问接口获取分类树时,先从Redis中查询数据。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果Redis中有数据,则直接数据。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果Redis中没有数据,则再从数据库中查询数据,拼接成分类树返回。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 将从数据库中查到的分类树的数据,保存到Redis中,设置过期时间5分钟。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 将分类树返回给用户。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们在Redis中定义一个了key,value是一个分类树的json格式转换成了字符串,使用简单的key/value形式保存数据。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">经过这样优化之后,dev环境的联调和自测顺利完成了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">第2次优化</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们将这个功能部署到st环境了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">刚开始测试同学没有发现什么问题,但随着后面不断地深入测试,隔一段时间就出现一次首页访问很慢的情况。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">于是,我们马上进行了第2次优化。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们决定使用<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Job</code>定期<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">异步</code>更新分类树到Redis中,在系统上线之前,会先生成一份数据。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">当然为了保险起见,防止Redis在哪条突然挂了,之前分类树同步写入Redis的逻辑还是保留。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">于是,流程图改成了这样:<img class="rich_pages wxw-img" data-ratio="1.169811320754717" src="/upload/3fa820244397ff1efcfe1cd9e98e365d.png" data-type="png" data-w="954" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">增加了一个job每隔5分钟执行一次,从数据库中查询分类数据,封装成分类树,更新到Redis缓存中。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其他的流程保持不变。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此外,Redis的过期时间之前设置的5分钟,现在要改成永久。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">通过这次优化之后,st环境就没有再出现过分类树查询的性能问题了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">第3次优化</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">测试了一段时间之后,整个网站的功能快要上线了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">为了保险起见,我们需要对网站首页做一次压力测试。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">果然测出问题了,网站首页最大的qps是100多,最后发现是每次都从Redis获取分类树导致的网站首页的性能瓶颈。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们需要做第3次优化。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">该怎么优化呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">答:加内存缓存。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果加了内存缓存,就需要考虑数据一致性问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">内存缓存是保存在服务器节点上的,不同的服务器节点更新的频率可能有点差异,这样可能会导致数据的不一致性。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但分类本身是更新频率比较低的数据,对于用户来说不太敏感,即使在短时间内,用户看到的分类树有些差异,也不会对用户造成太大的影响。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">因此,分类树这种业务场景,是可以使用内存缓存的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">于是,我们使用了Spring推荐的<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">caffine</code>作为内存缓存。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">改造后的流程图如下:<img class="rich_pages wxw-img" data-ratio="1.3127572016460904" src="/upload/d26b2127e841c29ba934ff1e55c945c3.png" data-type="png" data-w="972" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"></p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 用户访问接口时改成先从本地缓存分类数查询数据。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果本地缓存有,则直接返回。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果本地缓存没有,则从Redis中查询数据。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果Redis中有数据,则将数据更新到本地缓存中,然后返回数据。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果Redis中也没有数据(说明Redis挂了),则从数据库中查询数据,更新到Redis中(万一Redis恢复了呢),然后更新到本地缓存中,返回返回数据。 </section></li> </ol> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">需要注意的是,需要改本地缓存设置一个过期时间,这里设置的5分钟,不然的话,没办法获取新的数据。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样优化之后,再次做网站首页的压力测试,qps提升到了500多,满足上线要求。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">第4次优化</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之后,这个功能顺利上线了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">使用了很长一段时间没有出现问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">两年后的某一天,有用户反馈说,网站首页有点慢。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们排查了一下原因发现,分类树的数据太多了,一次性返回了上万个分类。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">原来在系统上线的这两年多的时间内,运营同学在系统后台增加了很多分类。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们需要做第4次优化。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这时要如何优化呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">限制分类树的数量?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">答:也不太现实,目前这个业务场景就是有这么多分类,不能让用户选择不到他想要的分类吧?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这时我们想到最快的办法是开启<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">nginx</code>的<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">GZip</code>功能。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">让数据在传输之前,先压缩一下,然后进行传输,在用户<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">浏览器</code>中,自动解压,将真实的分类树数据展示给用户。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之前调用接口返回的分类树有1MB的大小,优化之后,接口返回的分类树的大小是100Kb,一下子缩小了10倍。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样简单的优化之后,性能提升了一些。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5jueeKrFmuaUtGSicjGfgmicicVhN1fHkuicaYQT3JumuGVic3KThl3icMmJuOMNhYluCIibgnIZ2k4addVQ/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">第5次优化</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">经过上面优化之后,用户很长一段时间都没有反馈性能问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但有一天公司同事在排查Redis中大key的时候,揪出了分类树。之前的分类树使用key/value的结构保存数据的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们不得不做第5次优化。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">为了优化在Redis中存储数据的大小,我们首先需要对数据进行瘦身。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">只保存需要用到的字段。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">例如:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@AllArgsConstructor</span><br><span style="color: #61aeee;line-height: 26px;">@Data</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">Category</span> </span>{<br><br> <span style="color: #c678dd;line-height: 26px;">private</span> Long id;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String name;<br> <span style="color: #c678dd;line-height: 26px;">private</span> Long parentId;<br> <span style="color: #c678dd;line-height: 26px;">private</span> Date inDate;<br> <span style="color: #c678dd;line-height: 26px;">private</span> Long inUserId;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String inUserName;<br> <span style="color: #c678dd;line-height: 26px;">private</span> List<Category> children;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">像这个分类对象中inDate、inUserId和inUserName字段是可以不用保存的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">修改自动名称。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">例如:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@AllArgsConstructor</span><br><span style="color: #61aeee;line-height: 26px;">@Data</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">Category</span> </span>{<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br> * 分类编号<br> */</span><br> <span style="color: #61aeee;line-height: 26px;">@JsonProperty</span>(<span style="color: #98c379;line-height: 26px;">"i"</span>)<br> <span style="color: #c678dd;line-height: 26px;">private</span> Long id;<br><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br> * 分类层级<br> */</span><br> <span style="color: #61aeee;line-height: 26px;">@JsonProperty</span>(<span style="color: #98c379;line-height: 26px;">"l"</span>)<br> <span style="color: #c678dd;line-height: 26px;">private</span> Integer level;<br><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br> * 分类名称<br> */</span><br> <span style="color: #61aeee;line-height: 26px;">@JsonProperty</span>(<span style="color: #98c379;line-height: 26px;">"n"</span>)<br> <span style="color: #c678dd;line-height: 26px;">private</span> String name;<br><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br> * 父分类编号<br> */</span><br> <span style="color: #61aeee;line-height: 26px;">@JsonProperty</span>(<span style="color: #98c379;line-height: 26px;">"p"</span>)<br> <span style="color: #c678dd;line-height: 26px;">private</span> Long parentId;<br><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br> * 子分类列表<br> */</span><br> <span style="color: #61aeee;line-height: 26px;">@JsonProperty</span>(<span style="color: #98c379;line-height: 26px;">"c"</span>)<br> <span style="color: #c678dd;line-height: 26px;">private</span> List<Category> children;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">由于在一万多条数据中,每条数据的字段名称是固定的,他们的重复率太高了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">由此,可以在json序列化时,改成一个简短的名称,以便于返回更少的数据大小。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这还不够,需要对存储的数据做压缩。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之前在Redis中保存的key/value,其中的value是json格式的字符串。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其实<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">RedisTemplate</code>支持,value保存<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">byte数组</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">先将json字符串数据用<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">GZip</code>工具类压缩成byte数组,然后保存到Redis中。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">再获取数据时,将byte数组转换成json字符串,然后再转换成分类树。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样优化之后,保存到Redis中的分类树的数据大小,一下子减少了10倍,Redis的大key问题被解决了。</p> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;text-align: left;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 24px;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">前言</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在实际工作中,我们经常需要在项目中调用第三方API接口,获取数据,或者上报数据,进行数据交换和通信。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那么,调用第三方API接口会遇到哪些问题?如何解决这些问题呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这篇文章就跟大家一起聊聊第三方API接口的话题,希望对你会有所帮助。</p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="1.2907407407407407" data-s="300,640" src="/upload/8d47f3eccf3125868d3ea23282c9232c.png" data-type="png" data-w="1080" style=""></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">1 域名访问不到</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">一般我们在第一次对接第三方平台的API接口时,可能会先通过浏览器或者postman调用一下,该接口是否可以访问。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些人可能觉得多次一举。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其实不然。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有可能你调用第三方平台的API接口时,他们的接口真的挂了,他们还不知道。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">还有一种最重要的情况,就是你的工作网络,是否可以访问这个外网的接口。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些公司为了安全考虑,对内网的开发环境,是设置了防火墙的,或者有一些其他的限制,有些ip白名单,只能访问一些指定的外网接口。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果你发现你访问的域名,在开发环境访问不通,就要到运维同学给你添加<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">ip白名单</code>了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2 签名错误</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">很多第三方API接口为了防止别人篡改数据,通常会增加数字签名(sign)的验证。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">sign = md5(多个参数拼接 + 密钥)</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在刚开始对接第三方平台接口时,会遇到参数错误,签名错误等问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其中参数错误比较好解决,重点是签名错误这个问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">签名是由一些算法生成的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">比如:将参数名和参数值用冒号拼接,如果有多个参数,则按首字母排序,然后再将多个参数一起拼接。然后加盐(即:密钥),再通过md5,生成一个签名。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果有多个参数,你是按首字母倒序的,则最后生成的签名会出问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果你开发环境的密钥,用的生产环境的,也可能会导致生产的签名出现问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果第三方平台要求最后3次md5生成签名,而你只用了1次,也可能会导致生产的签名出现问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">因此,接口签名在接口联调时是比较麻烦的事情。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果第三方平台有提供sdk生成签名是最好的,如果没有,就只能根据他们文档手写签名算法了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">3 签名过期</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">通过上面一步,我们将签名调通了,可以正常访问第三方平台获取数据了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但你可能会发现,同一个请求,15分钟之后,再获取数据,却返回失败了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">第三方平台在设计接口时,在签名中增加了时间戳校验,同一个请求在15分钟之内,允许返回数据。如果超过了15分钟,则直接返回失败。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这种设计是为了安全考虑。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">防止有人利用工具进行暴力破解,不停伪造签名,不停调用接口校验,如果一直穷举下去的话,总有一天可以校验通过的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">sign = md5(多个参数拼接 + 密钥 + 时间戳)</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">因此,有必要增加时间戳的校验。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果出现这种情况,不要慌,重新发起一次新的请求即可。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">4 接口突然没返回数据</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果你调用第三方平台的某个API接口查询数据,刚开始一直都有数据返回。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但突然某一天没返回数据了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但是该API接口能够正常响应。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不要感到意外,有可能是第三方平台将数据删除了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我对接完第三方平台的API接口后,部署到了测试环境,发现他们接口竟然没有返回数据,原因是他们有一天将测试环境的数据删完了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">因此,在部署测试环境之前,要先跟对方沟通,要用哪些数据测试,不能删除。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">5 token失效</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些平台的API接口在请求之前,先要调用另外一个API接口获取token,然后再header中携带该token信息才能访问其他的业务API接口。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在获取token的API接口中,我们需要传入账号、密码和密钥等信息。每个接口对接方,这些信息都不一样。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们在请求其他的API接口之前,每次都实时调用一次获取token的接口获取token?还是请求一次token,将其缓存到redis中,后面直接从redis获取数据呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">很显然我们更倾向于后者,因为如果每次请求其他的API接口之前,都实时调用一次获取token的接口获取token,这样每次都会请求两次接口,性能上会有一些影响。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果将请求的token,保存到redis,又会出现另外一个问题:<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">token失效</code>的问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们调用第三方平台获取token的接口获取到的token,一般都有个有效期,比如:1天,1个月等。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在有效期内,该API接口能够正常访问。如果超过了token的有效期,则该API接口不允许访问。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">好办,我们把redis的失效时间设置成跟token的有效期一样不就OK了?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">想法是不错,但是有问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">你咋保证,你们系统的服务器时间,跟第三方平台的服务器时间一模一样?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我之前遇到过某大厂,提供了获取token接口,在30天内发起请求,每次都返回相同的token值。如果超过了30天,则返回一个新的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有可能出现这种情况,你们系统的服务器时间要快一些,第三方平台的时间要慢一些。结果到了30天,你们系统调用第三方平台的获取token接口获取到了token还是老的token,更新到redis中了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">过一段时间,token失效了,你们系统还是用老的token访问第三方平台的其他API接口,一直都返回失败。但获取新的token却要等30天,这个时间太漫长了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">为了解决这个问题,需要捕获token失效的异常。如果在调用其他的API接口是发现token失效了,马上请求一次获取token接口,将新的token立刻更新到redis中。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样基本可以解决token失效问题,也能尽可能保证访问其他接口的稳定性和性能。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">6 接口超时</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">系统上线之后,调用第三方API接口,最容易出现的问题,应该是<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">接口超时</code>问题了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">系统到外部系统之间,有一条很复杂的链路,中间有很多环节出现问题,都可能影响API接口的相应时间。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">作为API接口的调用方,面对第三方API接口超时问题,除了给他们反馈问题,优化接口性能之外,我们更有效的方式,可能是增加接口调用的<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">失败重试机制</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">例如:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/wzJhLVPsrd3popmutk3BnQb2oapNmtD5YpOibdmkbYRcMGY8wyicMicaRWtAdu3ZU49ibickzxYe6fxibeefuDvo6NxDCYNUcKvsY6/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">int</span> retryCount=<span style="color: #d19a66;line-height: 26px;">0</span>;<br><span style="color: #c678dd;line-height: 26px;">do</span> {<br> <span style="color: #c678dd;line-height: 26px;">try</span> {<br> doPost();<br> <span style="color: #c678dd;line-height: 26px;">break</span>;<br> } <span style="color: #c678dd;line-height: 26px;">catch</span>(Exception e) {<br> log.warn(<span style="color: #98c379;line-height: 26px;">"接口调用失败"</span>)<br> retryCount++;<br> }<br>} where (retryCount <= <span style="color: #d19a66;line-height: 26px;">3</span>)<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果接口调用失败,则程序会立刻自动重试<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">3次</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果重试之后成功了,则该API接口调用<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">成功</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果重试3次之后还是失败,则该API接口调用<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">失败</code>。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">7 接口返回500</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">调用第三方API接口,偶尔因为参数的不同,可能会出现500的问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">比如:有些API接口对于参数校验不到位,少部分必填字段,没有校验不能为空。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">刚好系统的有些请求,通过某个参数去调用该API接口时,没有传入那个参数,对方可能会出现NPE问题。而该接口的返回code,很可能是500。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">还有一种情况,就是该API接口的内部bug,传入不同的参数,走了不同的条件分支逻辑,在走某个分支时,接口逻辑出现异常,可能会导致接口返回500。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这种情况做接口重试也没用,只能联系第三方API接口提供者,反馈相关问题,让他们排查具体原因。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">他们可能会通过修复bug,或者修复数据,来解决这个问题。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">8 接口返回404</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果你在系统日志中发现调用的第三方API接口返回了404,这就非常坑了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果第三方的API接口没有上线,很可能是他们把接口名改了,没有及时通知你。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这种情况,可以锤他们了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">还有一种情况是,如果第三方的API接口已经上线了,刚开始接口是能正常调用的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">第三方也没有改过接口地址。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">后来,突然有一天发现调用第三方的API接口还是出现了404问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这种情况很可能是他们网关出问题了,最新的配置没有生效,或者改了网关配置导致的问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">总之一个字:坑。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">9 接口返回少数据了</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之前我调过一个第三方的API接口分页查询数据,接入非常顺利,但后来上线之后,发现他们的接口少数据了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">一查原因发现是该分页查询接口,返回的<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">总页数</code>不对,比实际情况少了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些小伙伴可能会好奇,这么诡异的问题我是怎么发现?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之前调用第三方API接口分页查询分类数据,保存到我们的第三方分类表中。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">突然有一天,产品反馈说,第三方有个分类在分类树中找不到。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我确认之后,发现竟然是真的没有。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">从调用第三方API接口的响应日志中,也没有查到该分类的数据。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个API接口是分页查询接口,目前已经分了十几页查询数据,但还是没有查到我们想要的分类。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之前的做法是先调用一次API接口查询<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">第一页</code>的数据,同时查出<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">总页数</code>。然后再根据总页数循环调用,查询<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">其他页</code>的数据。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我当时猜测,可能是他们接口返回的总页数有问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">于是,可以将接口调用逻辑改成这样的:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 从第一页开始,后面每调用一次API接口查数据,页数就加1。然后判断接口返回的数据是否小于pageSize, </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果不小于,则进行下一次调用。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果小于,则说明已经是最后一页了,可以停止后续调用了。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">验证之后发现这样果然可以获取那个分类的数据,只能说明第三方的分页查询接口返回的总页数比实际情况小了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">10 偷偷改参数了</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我之前调用过某平台的API接口获取指标的状态,之前根据双方约定的状态有:<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">正常</code>和<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">禁用</code> 两种。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">然后将状态更新到我们的指标表中。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">后来,双方系统上线运行了好几个月。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">突然有一天,用户反馈说某一条数据明明删除了,为什么在页面上还是可以查到。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此时,我查我们这边的指标表,发现状态是正常的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">然后查看调用该平台的API接口日志,发现返回的该指标的状态是:<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">下架</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">what?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这是什么状态?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">跟该平台的开发人员沟通后,发现他们改了状态的枚举,增加了:上架、下架等多个值,而且没有通知我们。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这就坑了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们这边的代码中判断,如果状态非禁用状态,都认为是正常状态。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而下架状态,自动被判断为正常状态。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">经过跟对方沟通后,他们确认下架状态,是非正常状态,不应该显示指标。他们改了数据,临时解决了该指标的问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">后来,他们按接口文档又改回了之前的状态枚举值。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">11 接口时好时坏</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不知道你在调用第三方接口时,有没有遇到过接口时好时坏的情况。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">5分钟前,该接口还能正常返回数据。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">5分钟后,该接口返回503不可用。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">又过了几分钟,该接口又能正常返回数据了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这种情况大概率是第三方平台在重启服务,在重启的过程中,可能会出现服务暂时不可用的情况。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">还有另外一种情况:第三方接口部署了多个服务节点,有一部分服务节点挂了。也会导致请求第三方接口时,返回值时好时坏的情况。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此外还有一种情况:网关的配置没有及时更新,没有把已经下线的服务剔除掉。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样用户请求经过网关时,网关转发到了已经下线的服务,导致服务不可用。网关转发请求到正常的服务,该服务能够正常返回。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果遇到该问题,要尽快将问题反馈给第三方平台,然后增加接口失败重试机制。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><br></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">12 文档和接口逻辑不一致</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之前还遇到一个第三方平台提供的API查询接口,接口文档中明确写明了有个<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">dr</code>字段表示<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">删除状态</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有了这个字段,我们在同步第三方平台的分类数据时,就能够知道有哪些数据是被删除的,后面可以及时调整我们这边的数据,将相关的数据也做删除处理。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">后来发现有些分类,他们那边已经删除了,但是我们这边却没删除。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这是啥情况呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">代码逻辑很简单,我review了一下代码,也没有bug,为什么会出现这种情况呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">追查日志之后发现,调用第三方平台获取分类接口时,对方并没有把已删除的分类数据返回给我们。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">也就是说接口文档中的那个dr字段没有什么用,接口文档和接口逻辑不一致。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个问题估计好多小伙伴都遇到过。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果要解决这个问题,主要的方案有两种:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 第三方平台按文档修改接口逻辑,返回删除状态。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 我们系统在调用分类查询接口之后,根据分类code判断,如果数据库中有些分类的code不在接口返回值中,则删除这些分类。 </section></li> </ol> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/ibJZVicC7nz5iariaVrTspgbs76AhRnHXWxODPoAzXAukR0YXegZhZnyibWgBbCKzKAEPyb3GVGQnFutibg1hic70Ju4Q/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">13 欠费了</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们调用过百度的票据识别接口,可以自动识别发票信息,获取发票编号和金额等信息。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">之前是另外一个同事对接的接口,后来他离职了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">发票识别功能上线,使用了很长一段时间,一直都没有出问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">后来,某一天,生产环境用户反馈发票识别不了了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我查询了相关服务的日志,没有发现异常,这就奇怪了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">打开代码仔细看了一下,发现那位同事的代码中调用第三方的API接口,接收响应数据时,直接转换成了对象,没有打印当时返回的字符串。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">莫非,接口返回值有问题?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">后来,我增加了日志,打印出了该接口真正的返回内容值。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">原因一下查到了,原来是欠费了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果出现该了异常,百度的API接口返回的数据结构,用之前那位同事的实体有些参数没法获取到。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这是一个不小的坑。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们在接收第三方API接口返回数据时,尽可能先用字符串接收返回值,然后将字符串转换成相应实体类,一定要将该返回值在日志中打印出来,方便后面定位问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不要直接用实体对象接收返回值,有些API接口,如果出现不同的异常,返回的数据结构差异比较大。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些异常结果可能是他们网关系统直接返回的,有些异常是他们业务系统返回的。</p> </section> <section class="mp_profile_iframe_wrp"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="MzkzMDI1NjcyOQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/EoJib2tNvVtf7icAmS0BQH6oDVG37Q8NzcfdguS5qAqOhfxvZyIKqmuX5BbnDjynrBbZzktp1EiaeFLzapp1nHysw/0?wx_fmt=png" data-nickname="码哥字节" data-alias="MageByte" data-signature="拥抱硬核技术和对象,面向人民币编程。" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <p style="display: none;margin-bottom: 24px;"> <mp-style-type data-value="3"></mp-style-type></p>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;letter-spacing: 0px;white-space: normal;font-size: 16px;padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Guava是google公司开发的一款Java类库扩展工具包,内含了丰富的API,涵盖了集合、缓存、并发、I/O等多个方面。使用这些API一方面可以简化我们代码,使代码更为优雅,另一方面它补充了很多jdk中没有的功能,能让我们开发中更为高效。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天Hydra要给大家分享的就是Guava中封装的一些关于<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>的骚操作,在使用了这些功能后,不得不说一句真香。先引入依赖坐标,然后开始我们的正式体验吧~</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">groupId</span>></span>com.google.guava<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">artifactId</span>></span>guava<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">version</span>></span>30.1.1-jre<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">version</span>></span><br><span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">dependency</span>></span><br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">Table - 双键Map</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">java中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>只允许有一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>和一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>存在,但是guava中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>允许一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>存在两个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>。<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>中的两个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>分别被称为<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">rowKey</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">columnKey</code>,也就是行和列。(但是个人感觉将它们理解为行和列并不是很准确,看作两列的话可能会更加合适一些)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">举一个简单的例子,假如要记录员工每个月工作的天数。用java中普通的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>实现的话就需要两层嵌套:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Map<String,Map<String,Integer>> map=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> HashMap<>();<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//存放元素</span><br>Map<String,Integer> workMap=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> HashMap<>();<br>workMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>,<span style="color: rgb(209, 154, 102);line-height: 26px;">20</span>);<br>workMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>,<span style="color: rgb(209, 154, 102);line-height: 26px;">28</span>);<br>map.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>,workMap);<br><br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//取出元素</span><br>Integer dayCount = map.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>).get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>的话就很简单了,看一看简化后的代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Table<String,String,Integer> table= HashBasedTable.create();<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//存放元素</span><br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">20</span>);<br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">28</span>);<br><br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Trunks"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">28</span>);<br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Trunks"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">16</span>);<br><br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//取出元素</span><br>Integer dayCount = table.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们不需要再构建复杂的双层<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>,直接一层搞定。除了元素的存取外,下面再看看其他的实用操作。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">1、获得key或value的集合</h3> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//rowKey或columnKey的集合</span><br>Set<String> rowKeys = table.rowKeySet();<br>Set<String> columnKeys = table.columnKeySet();<br><br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//value集合</span><br>Collection<Integer> values = table.values();<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分别打印它们的结果,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>的集合是不包含重复元素的,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>集合则包含了所有元素并没有去重:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">[Hydra, Trunks]<br>[Jan, Feb]<br>[20, 28, 28, 16]<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">2、计算key对应的所有value的和</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">以统计所有<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">rowKey</code>对应的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>之和为例:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (String key : table.rowKeySet()) {<br> Set<Map.Entry<String, Integer>> rows = table.row(key).entrySet();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> total = <span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>;<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (Map.Entry<String, Integer> row : rows) {<br> total += row.getValue();<br> }<br> System.out.println(key + <span style="color: rgb(152, 195, 121);line-height: 26px;">": "</span> + total);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">打印结果:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Hydra: <span style="color: rgb(209, 154, 102);line-height: 26px;">48</span><br>Trunks: <span style="color: rgb(209, 154, 102);line-height: 26px;">44</span><br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">3、转换rowKey和columnKey</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这一操作也可以理解为行和列的转置,直接调用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Tables</code>的静态方法<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">transpose</code>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Table<String, String, Integer> table2 = Tables.transpose(table);<br>Set<Table.Cell<String, String, Integer>> cells = table2.cellSet();<br>cells.forEach(cell-><br> System.out.println(cell.getRowKey()+<span style="color: rgb(152, 195, 121);line-height: 26px;">","</span>+cell.getColumnKey()+<span style="color: rgb(152, 195, 121);line-height: 26px;">":"</span>+cell.getValue())<br>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">利用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">cellSet</code>方法可以得到所有的数据行,打印结果,可以看到<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">row</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">column</code>发生了互换:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Jan,Hydra:20<br>Feb,Hydra:28<br>Jan,Trunks:28<br>Feb,Trunks:16<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">4、转为嵌套的Map</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">还记得我们在没有使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>前存储数据的格式吗,如果想要将数据还原成嵌套<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>的那种形式,使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">rowMap</code>或<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">columnMap</code>方法就可以实现了:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Map<String, Map<String, Integer>> rowMap = table.rowMap();<br>Map<String, Map<String, Integer>> columnMap = table.columnMap();<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">查看转换格式后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>中的内容,分别按照行和列进行了汇总:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">{Hydra={Jan=20, Feb=28}, Trunks={Jan=28, Feb=16}}<br>{Jan={Hydra=20, Trunks=28}, Feb={Hydra=28, Trunks=16}}<br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">BiMap - 双向Map</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在普通<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>中,如果要想根据<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>查找对应的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>,没什么简便的办法,无论是使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">for</code>循环还是迭代器,都需要遍历整个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>。以循环<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">keySet</code>的方式为例:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> List<String> <span style="color: rgb(97, 174, 238);line-height: 26px;">findKey</span><span style="line-height: 26px;">(Map<String, String> map, String val)</span></span>{<br> List<String> keys=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> ArrayList<>();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (String key : map.keySet()) {<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (map.get(key).equals(val))<br> keys.add(key);<br> }<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> keys;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">而guava中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>提供了一种<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>双向关联的数据结构,先看一个简单的例子:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">HashBiMap<String, String> biMap = HashBiMap.create();<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Programmer"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Thanos"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Titan"</span>);<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//使用key获取value</span><br>System.out.println(biMap.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>));<br><br>BiMap<String, String> inverse = biMap.inverse();<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//使用value获取key</span><br>System.out.println(inverse.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Titan"</span>));<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">执行结果,:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">IronMan<br>Thanos<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">看上去很实用是不是?但是使用中还有几个坑得避一下,下面一个个梳理。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">1、反转后操作的影响</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上面我们用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">inverse</code>方法反转了原来<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>的键值映射,但是这个反转后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>并不是一个新的对象,它实现了一种视图的关联,所以对反转后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>执行的所有操作会作用于原先的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>上。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">HashBiMap<String, String> biMap = HashBiMap.create();<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Programmer"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Thanos"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Titan"</span>);<br>BiMap<String, String> inverse = biMap.inverse();<br><br>inverse.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Stark"</span>);<br>System.out.println(biMap);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对反转后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>中的内容进行了修改后,再看一下原先<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>中的内容:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">{Hydra=Programmer, Thanos=Titan, Stark=IronMan}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可以看到,原先值为<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">IronMan</code>时对应的键是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Tony</code>,虽然没有直接修改,但是现在键变成了<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Stark</code>。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">2、value不可重复</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>的底层继承了<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>,我们知道在<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>中<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>是不允许重复的,而双向的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>中<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>可以认为处于等价地位,因此在这个基础上加了限制,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>也是不允许重复的。看一下下面的代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">HashBiMap<String, String> biMap = HashBiMap.create();<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Stark"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这样代码无法正常结束,会抛出一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">IllegalArgumentException</code>异常:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.20480668756530826" src="/upload/574a6b168326c5b8e237d236879450f1.png" data-type="png" data-w="957" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果你非想把新的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>映射到已有的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>上,那么也可以使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">forcePut</code>方法强制替换掉原有的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">HashBiMap<String, String> biMap = HashBiMap.create();<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br>biMap.forcePut(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Stark"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">打印一下替换后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">{Stark=IronMan}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">顺带多说一句,由于<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>是不允许重复的,因此它的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">values</code>方法返回的是没有重复的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Set</code>,而不是普通<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-al
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-size: 14px;padding: 10px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">大家好,我是老三,之前里,我们讨论了<a href="https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247493498&idx=1&sn=052e79866106343fa66cf25b279f23f6&scene=21#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">Java的三种IO模型</a>,提到了网络通信框架Netty,它简化和优化了NIO的使用,这期,我们正式开始走近Netty。</p> <section class="mp_profile_iframe_wrp"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="MzkwODE5ODM0Ng==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWdFLJg0sAOqwHB1mb24icMADUgxm1qZQft5aN3H37NAmQnOvpGB7J9JVHxC6NSiacxbBP1DYdhIAeyA/0?wx_fmt=png" data-nickname="三分恶" data-alias="Fighter3FullStack" data-signature="一个号称能文能武的后端开发。" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));">为什么要用Netty?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">首先当然是NIO的使用,本身比较复杂,而且还存在一些问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除此之外,如果在项目的开发中,要实现稳定的网络通信,就得考虑网络的闪断、客户端的重复接入、客户端的安全认证、消息的编解码、半包读写……</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.8080808080808081" src="/upload/353d161b900b09a8a5cbc902c148fda.png" data-type="png" data-w="198" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 头大 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">所以,巧了,恰好有这么一个成熟稳定、性能强大、开箱即用的网络框架摆在我们面前,相比较Java NIO,Netty更加出色:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">易用性</strong>: Netty 在 NIO 基础上进行了更高层次的封装,屏蔽了 NIO 的复杂性,大大降低了开发难度;Netty 提供了很多开箱即用的工具,例如常用的行解码器、长度域解码器等,不需要自己再实现。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">稳定性</strong>: Netty 更加可靠稳定,修复和完善了 JDK NIO 较多已知问题,例如臭名昭著的 select 空转导致 CPU 消耗 100%,TCP 断线重连,keep-alive 检测等问题。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">可扩展性</strong>: Netty 的的可扩展性做的非常好,比如支持可定制化的线程模型。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们有什么理由拒绝这么一款优秀的网络通信框架呢?代码怎么写不是写喽!</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));">初识Netty</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;background-color: #000;color: #fff;padding: 2px 10px;width: fit-content;font-size: 17px;margin: 60px auto 10px;">什么是Netty?</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Netty官方是这么定义Netty的:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">Netty 是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。</p> </blockquote> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5878378378378378" src="/upload/d05c5f673be9ce2e910cd4e487221be7.png" data-type="png" data-w="592" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 组成图-来源官方 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">Netty是一个开源的、单线程模型的 Java 网络编程框架。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">Netty基于 NIO ,被广泛应用于各种网络应用程序开发。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">Netty支持多种协议,包括但不限于 HTTP、WebSocket、TCP、UDP 和 SSL/TLS 协议等。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">Netty 是非阻塞的,事件驱动的框架。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">Netty具有高性能、可扩展和易于使用的优点。</p> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;background-color: #000;color: #fff;padding: 2px 10px;width: fit-content;font-size: 17px;margin: 60px auto 10px;">Netty的现状?</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Netty 由 JBoss 社区开发维护的,它的社区相对比较活跃:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> https://github.com/netty/netty:Github已经收获31.2K星标 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> https://netty.io/:官方网站,提供了比较完整的文档 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">官方目前最新的版本是5.x,,但是很不幸,已经被社区放弃开发维护,属于废弃版本,最新的稳定版本是4.x 。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">一般使用,推荐4.x,Netty 4.x对3.x不做兼容,我们后续的学习也基于Netty 4.x版本。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;background-color: #000;color: #fff;padding: 2px 10px;width: fit-content;font-size: 17px;margin: 60px auto 10px;">谁在用Netty?</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作为最流行的网络通信框架,大量的公司选择它作为底层网络通信框架,包括不限于:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3500570125427594" src="/upload/f5a982a78c8cea84704167b988544d0e.png" data-type="png" data-w="877" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 使用Netty的公司 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们可能自己没有直接用过Netty,但其实熟悉的很多开源中间件,都用到了Netty,比如:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 服务治理:Apache Dubbo、gRPC。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 大数据:Hbase、Spark、Flink、Storm。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 搜索引擎:Elasticsearch。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 消息队列:RocketMQ、ActiveMQ。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">用到Netty的优秀产品非常多,大家感兴趣可以看看:https://netty.io/wiki/related-projects.html。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));">从"Hello World"开始</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">气氛衬托到这,不写个Demo也过不去,还是从"Hello World"开始,我们领略一下Netty的风采。</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 创建一个Maven项目:这个就不用多说了吧 </section></li> </ol> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4505928853754941" src="/upload/9e56aac2d4ee6ac6cbc5a31ab5aaa9da.png" data-type="png" data-w="1012" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 创建Maven项目 </figcaption> </figure> <ol start="2" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 导入依赖:我们直接用4.x最新的版本 </section></li> </ol> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><code style="overflow-x: auto;padding: 15px 16px 16px;display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(255, 255, 255);border-radius: 5px;"> <dependency><br> <groupId>io.netty</groupId><br> <artifactId>netty-all</artifactId><br> <version><span style="color: #1c00cf;line-height: 26px;">4.1.92</span>.Final</version><br> </dependency><br></code></pre> <ol start="3" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">编写代码:那么我们就开始编写这个Demo的服务器和客户端相关代码</p> </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;"><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">NettyServer</code>:基于Netty的客户端</p> <pre style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><code style="overflow-x: auto;display: -webkit-box;padding: 15px 4px 2px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);background: rgb(255, 255, 255);border-radius: 5px;"><span style="color: #007400;line-height: 26px;">/**<br> * <p>Date: 2023/5/14 10:29</p><br> * <p>Author: fighter3</p><br> * <p>Description: Netty服务端Demo</p><br> */</span><br><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">class</span> <span style="color: #5c2699;line-height: 26px;">NettyServer</span></span>{<br> <span style="color: #007400;line-height: 26px;">// 服务器监听的端口号</span><br> <span style="color: #aa0d91;line-height: 26px;">private</span> <span style="color: #aa0d91;line-height: 26px;">int</span> port;<br><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #1c00cf;line-height: 26px;">NettyServer</span><span style="color: #5c2699;line-height: 26px;">(<span style="color: #aa0d91;line-height: 26px;">int</span> port)</span> </span>{<br> <span style="color: #aa0d91;line-height: 26px;">this</span>.port = port;<br> }<br><br> <span style="color: #007400;line-height: 26px;">/**<br> * 启动Netty服务器<br> * <span style="font-weight: bold;line-height: 26px;">@throws</span> InterruptedException<br> */</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">run</span><span style="color: #5c2699;line-height: 26px;">()</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> InterruptedException </span>{<br> <span style="color: #007400;line-height: 26px;">// 创建boss线程组和worker线程组</span><br> <span style="color: #007400;line-height: 26px;">// bossGroup 用于监听客户端的连接请求,将连接请求发送给 workerGroup 进行处理</span><br> NioEventLoopGroup bossGroup = <span style="color: #aa0d91;line-height: 26px;">new</span> NioEventLoopGroup();<br> <span style="color: #007400;line-height: 26px;">// workerGroup 用于处理客户端连接的数据读写</span><br> NioEventLoopGroup workerGroup = <span style="color: #aa0d91;line-height: 26px;">new</span> NioEventLoopGroup();<br> <span style="color: #aa0d91;line-height: 26px;">try</span> {<br> <span style="color: #007400;line-height: 26px;">// 创建 ServerBootstrap 对象,用于启动 Netty 服务器</span><br> ServerBootstrap serverBootstrap = <span style="color: #aa0d91;line-height: 26px;">new</span> ServerBootstrap();<br> <span style="color: #007400;line-height: 26px;">// 绑定线程池事件组</span><br> serverBootstrap.group(bossGroup, workerGroup)<br> .channel(NioServerSocketChannel<span style="line-height: 26px;">.<span style="color: #aa0d91;line-height: 26px;">class</span>)<br> // 通道初始化回调函数,在启动的时候可以自动调用<br> .<span style="color: #5c2699;line-height: 26px;">childHandler</span>(<span style="color: #5c2699;line-height: 26px;">new</span> <span style="color: #5c2699;line-height: 26px;">ChannelInitializer</span><<span style="color: #5c2699;line-height: 26px;">SocketChannel</span>>() </span>{<br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">initChannel</span><span style="color: #5c2699;line-height: 26px;">(SocketChannel ch)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> ChannelPipeline pipeline = ch.pipeline();<br> <span style="color: #007400;line-height: 26px;">// 添加消息处理器</span><br> pipeline.addLast(<span style="color: #aa0d91;line-height: 26px;">new</span> NettyServerHandler());<br> }<br> });<br><br> <span style="color: #007400;line-height: 26px;">// 绑定端口,开始接收客户端请求</span><br> ChannelFuture channelFuture = serverBootstrap.bind(port).sync();<br><br> System.out.println(<span style="color: #c41a16;line-height: 26px;">"Netty服务器监听端口:"</span>+port);<br><br> <span style="color: #007400;line-height: 26px;">// 等待服务端监听端口关闭</span><br> channelFuture.channel().closeFuture().sync();<br> } <span style="color: #aa0d91;line-height: 26px;">finally</span> {<br> <span style="color: #007400;line-height: 26px;">//释放线程组资源</span><br> bossGroup.shutdownGracefully();<br> workerGroup.shutdownGracefully();<br> }<br> }<br><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">static</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">main</span><span style="color: #5c2699;line-height: 26px;">(String[] args)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> InterruptedException </span>{<br> <span style="color: #007400;line-height: 26px;">// 创建服务器对象,监听端口号为 8888</span><br> NettyServer server = <span style="color: #aa0d91;line-height: 26px;">new</span> NettyServer(<span style="color: #1c00cf;line-height: 26px;">8888</span>);<br> System.out.println(<span style="color: #c41a16;line-height: 26px;">"============Netty服务器启动...============="</span>);<br> <span style="color: #007400;line-height: 26px;">// 启动服务器</span><br> server.run();<br> System.out.println(<span style="color: #c41a16;line-height: 26px;">"============Netty服务器停止...============="</span>);<br> }<br>}<br></code></pre> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;"><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">NettyServerHandler</code>:服务器的消息处理器,用于处理各种事件</p> <pre style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><code style="overflow-x: auto;display: -webkit-box;padding: 15px 4px 2px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);background: rgb(255, 255, 255);border-radius: 5px;"><span style="color: #007400;line-height: 26px;">/**<br> * <p>Date: 2023/5/14 10:30</p><br> * <p>Author: fighter3</p><br> * <p>Description: Netty服务器消息处理器</p><br> */</span><br><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">class</span> <span style="color: #5c2699;line-height: 26px;">NettyServerHandler</span> <span style="color: #aa0d91;line-height: 26px;">extends</span> <span style="color: #5c2699;line-height: 26px;">ChannelInboundHandlerAdapter</span> </span>{<br><br> <span style="color: #007400;line-height: 26px;">/**<br> * 当客户端上线的时候会触发这个方法<br> * <span style="font-weight: bold;line-height: 26px;">@param</span> ctx<br> * <span style="font-weight: bold;line-height: 26px;">@throws</span> Exception<br> */</span><br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">channelActive</span><span style="color: #5c2699;line-height: 26px;">(ChannelHandlerContext ctx)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> String message=<span style="color: #c41a16;line-height: 26px;">"你好,靓仔!"</span>;<br> ByteBuf hello = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8);<br> <span style="color: #007400;line-height: 26px;">// 发送消息</span><br> ctx.writeAndFlush(hello);<br> }<br><br> <span style="color: #007400;line-height: 26px;">/**<br> *当 Channel 中有来自客户端的数据时就会触发这个方法<br> */</span><br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">channelRead</span><span style="color: #5c2699;line-height: 26px;">(ChannelHandlerContext ctx, Object msg)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> ByteBuf buf = (ByteBuf) msg;<br> System.out.println(<span style="color: #c41a16;line-height: 26px;">"客户端发来的消息:"</span> + buf.toString(CharsetUtil.UTF_8)); <span style="color: #007400;line-height: 26px;">// 接收消息并打印输出</span><br> }<br><br> <span style="color: #007400;line-height: 26px;">/**<br> * 当有异常时触发这个方法<br> */</span><br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">exceptionCaught</span><span style="color: #5c2699;line-height: 26px;">(ChannelHandlerContext ctx, Throwable cause)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> cause.printStackTrace();<br> ctx.close();<br> }<br>}<br></code></pre> </section></li> </ul> </ol> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;"><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">NettyClient</code>:使用Netty的客户端,通过ip和端口连接服务端</p> <pre style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><code style="overflow-x: auto;display: -webkit-box;padding: 15px 4px 2px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);background: rgb(255, 255, 255);border-radius: 5px;"><span style="color: #007400;line-height: 26px;">/**<br> * <p>Date: 2023/5/14 10:32</p><br> * <p>Author: fighter3</p><br> * <p>Description: Netty客户端Demo</p><br> */</span><br><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">class</span> <span style="color: #5c2699;line-height: 26px;">NettyClient</span> </span>{<br> <span style="color: #007400;line-height: 26px;">// 服务器 IP</span><br> <span style="color: #aa0d91;line-height: 26px;">private</span> String host;<br> <span style="color: #007400;line-height: 26px;">// 服务器监听的端口号</span><br> <span style="color: #aa0d91;line-height: 26px;">private</span> <span style="color: #aa0d91;line-height: 26px;">int</span> port;<br><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #1c00cf;line-height: 26px;">NettyClient</span><span style="color: #5c2699;line-height: 26px;">(String host, <span style="color: #aa0d91;line-height: 26px;">int</span> port)</span> </span>{<br> <span style="color: #aa0d91;line-height: 26px;">this</span>.host = host;<br> <span style="color: #aa0d91;line-height: 26px;">this</span>.port = port;<br> }<br><br> <span style="color: #007400;line-height: 26px;">/**<br> * 启动 Netty 客户端<br> */</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">run</span><span style="color: #5c2699;line-height: 26px;">()</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> InterruptedException </span>{<br> <span style="color: #007400;line-height: 26px;">// 创建事件循环组</span><br> NioEventLoopGroup group = <span style="color: #aa0d91;line-height: 26px;">new</span> NioEventLoopGroup();<br> <span style="color: #aa0d91;line-height: 26px;">try</span> {<br> <span style="color: #007400;line-height: 26px;">// 创建 Bootstrap 对象</span><br> Bootstrap bootstrap = <span style="color: #aa0d91;line-height: 26px;">new</span> Bootstrap();<br> <span style="color: #007400;line-height: 26px;">// 配置 Bootstrap 对象</span><br> <span style="color: #007400;line-height: 26px;">// 设置线程组</span><br> bootstrap.group(group)<br> <span style="color: #007400;line-height: 26px;">// 设置客户端通信的通道类型为NIO类型</span><br> .channel(NioSocketChannel<span style="line-height: 26px;">.<span style="color: #aa0d91;line-height: 26px;">class</span>)<br> .<span style="color: #5c2699;line-height: 26px;">handler</span>(<span style="color: #5c2699;line-height: 26px;">new</span> <span style="color: #5c2699;line-height: 26px;">ChannelInitializer</span><<span style="color: #5c2699;line-height: 26px;">SocketChannel</span>>() </span>{<br> <span style="color: #007400;line-height: 26px;">// 通道初始化回调函数,在启动的时候可以自动调用</span><br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">initChannel</span><span style="color: #5c2699;line-height: 26px;">(SocketChannel ch)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> <span style="color: #007400;line-height: 26px;">// 添加消息处理器</span><br> ch.pipeline().addLast(<span style="color: #aa0d91;line-height: 26px;">new</span> NettyClientHandler());<br> }<br> });<br> <span style="color: #007400;line-height: 26px;">// 连接服务器,异步等待连接成功</span><br> ChannelFuture channelFuture = bootstrap.connect(host, port).sync();<br> System.out.println(<span style="color: #c41a16;line-height: 26px;">"===========Netty客户端连接服务端========="</span>);<br><br> <span style="color: #007400;line-height: 26px;">// 等待客户端连接关闭</span><br> channelFuture.channel().closeFuture().sync();<br> } <span style="color: #aa0d91;line-height: 26px;">finally</span> {<br> <span style="color: #007400;line-height: 26px;">//释放资源</span><br> group.shutdownGracefully();<br> }<br> }<br><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">static</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">main</span><span style="color: #5c2699;line-height: 26px;">(String[] args)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> InterruptedException </span>{<br> <span style="color: #007400;line-height: 26px;">// 创建客户端对象,并连接到服务器</span><br> NettyClient client = <span style="color: #aa0d91;line-height: 26px;">new</span> NettyClient(<span style="color: #c41a16;line-height: 26px;">"127.0.0.1"</span>, <span style="color: #1c00cf;line-height: 26px;">8888</span>);<br> <span style="color: #007400;line-height: 26px;">// 启动客户端,开始发送消息</span><br> client.run();<br> }<br>}<br></code></pre> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">NettyClientHandler:Netty客户端处理器,用于处各种事件</p> <pre style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><code style="overflow-x: auto;display: -webkit-box;padding: 15px 4px 2px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);background: rgb(255, 255, 255);border-radius: 5px;"><span style="color: #007400;line-height: 26px;">/**<br> * <p>Date: 2023/5/14 10:33</p><br> * <p>Author: fighter3</p><br> * <p>Description: Netty客户端处理器</p><br> */</span><br><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">class</span> <span style="color: #5c2699;line-height: 26px;">NettyClientHandler</span> <span style="color: #aa0d91;line-height: 26px;">extends</span> <span style="color: #5c2699;line-height: 26px;">ChannelInboundHandlerAdapter</span> </span>{<br><br> <span style="color: #007400;line-height: 26px;">/**<br> * 当 Channel 准备就绪时就会触发这个方法<br> */</span><br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">channelActive</span><span style="color: #5c2699;line-height: 26px;">(ChannelHandlerContext ctx)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> String message=<span style="color: #c41a16;line-height: 26px;">"大佬,带带我!"</span>;<br> ByteBuf hello = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8);<br> <span style="color: #007400;line-height: 26px;">// 发送消息</span><br> ctx.writeAndFlush(hello);<br> }<br><br> <span style="color: #007400;line-height: 26px;">/**<br> * 当 Channel 中有来自服务器的数据时就会触发这个方法<br> */</span><br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">channelRead</span><span style="color: #5c2699;line-height: 26px;">(ChannelHandlerContext ctx, Object msg)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> ByteBuf buf = (ByteBuf) msg;<br> System.out.println(<span style="color: #c41a16;line-height: 26px;">"服务端发来的消息:"</span> + buf.toString(CharsetUtil.UTF_8)); <span style="color: #007400;line-height: 26px;">// 接收消息并打印输出</span><br> }<br><br> <span style="color: #007400;line-height: 26px;">/**<br> * 发生异常就会触发这个方法<br> */</span><br> <span style="color: #643820;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span> <span style="color: #aa0d91;line-height: 26px;">void</span> <span style="color: #1c00cf;line-height: 26px;">exceptionCaught</span><span style="color: #5c2699;line-height: 26px;">(ChannelHandlerContext ctx, Throwable cause)</span> <span style="color: #aa0d91;line-height: 26px;">throws</span> Exception </span>{<br> cause.printStackTrace();<br> ctx.close();<br> }<br>}<br></code></pre> </section></li> </ul> <ol start="4" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">运行一下:先启动NettyServer,再启动NettyClient,看下运行结果</p> <pre style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><code style="overflow-x: auto;display: -webkit-box;padding: 15px 4px 2px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);background: rgb(255, 255, 255);border-radius: 5px;">============Netty服务器启动...=============<br>Netty服务器监听端口:<span style="color: #1c00cf;line-height: 26px;">8888</span><br>客户端发来的消息:大佬,带带我!<br></code></pre> <pre style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><code style="overflow-x: auto;display: -webkit-box;padding: 15px 4px 2px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);background: rgb(255, 255, 255);border-radius: 5px;">===========Netty客户端连接服务端=========<br>服务端发来的消息:你好,靓仔!<br></code></pre> </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">好了,一个简单的Netty入门Demo就写完了,Netty是一个双工通信的网络框架,可以看到,服务端和客户端,流程基本上一致,主要包括这么几个步骤:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 创建事件循环组和相关对象,用于监听和处理网络事件; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 配置 Netty 服务器或客户端的启动参数,包括线程组、通道类型、TCP 参数等; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 给服务器或客户端的 ChannelPipeline 添加各种 ChannelHandler,用于处理不同的网络事件; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 绑定端口启动服务器或连接服务器; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 等待服务器或客户端连接关闭,释放相关资源。 </section></li> </ol> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.14444444444444443" src="/upload/ed80c76af27ca3d75f36ebaa97455140.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 服务器&客户端初始化启动流程 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然这个Demo比较简单,但其实已经用到了Netty里几个比较关键的组件:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ByteBuf</code>:Netty 的字节容器,类似于 Java 的 ByteBuffer,但是提供了更加强大、简便且安全的 API,用于在网络中传递二进制数据; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">EventLoopGroup</code>:Netty 的事件循环组,用于管理和调度连接到服务器或者从服务器连接出去的所有 Channel 上的事件循环; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ServerBootstrap</code>:Netty 的服务器启动类,用于启动和配置一个 TCP/IP 服务器; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Bootstrap</code>:Netty 的客户端启动类,用于启动和配置一个 TCP/IP 客户端; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Channel</code>:Netty 的核心概念,用于表示一个通信通道,可以读取和写入数据; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ChannelPipeline</code>:Netty 的 Channel 处理器,用于在传入的数据上执行一组 ChannelHandler; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ChannelHandler</code>:Netty 的核心组件,用于处理各种通信事件,例如读取数据、写数据、建立连接等; </section></li> </ol> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.587037037037037" src="/upload/4ecd62aa8b343276920fdb785af3135b.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> Netty的重要组件 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">后续,我们还会和这些组件打更多的交道。</p> <hr data-tool="mdnice编辑器" style="height: 1px;margin-top: 10px;margin-bottom: 10px;border-right: none;border-bottom: none;border-left: none;border-top-style: solid;border-top-color: black;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">好了,那么这期内容就到这了,这期里我们初步了解了Netty,包括什么是Netty、Netty现状、Netty的应用,还写了一个简单的Demo。下一期,我们继续深入了解Netty,敬请期待。</p> <br data-tool="mdnice编辑器"> <hr data-tool="mdnice编辑器" style="height: 1px;margin-top: 10px;margin-bottom: 10px;border-right: none;border-bottom: none;border-left: none;border-top-style: solid;border-top-color: black;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><big><strong>参考:</strong></big></p> <section style="padding-top: 8px;padding-bottom: 8px;line-height: normal;"> <span style="font-size: 10px;">[1].https://netty.io/</span> </section> <section style="padding-top: 8px;padding-bottom: 8px;line-height: normal;"> <span style="font-size: 10px;">[2].《Netty权威指南》</span> </section> <section style="padding-top: 8px;padding-bottom: 8px;line-height: normal;"> <span style="font-size: 10px;">[3]. 《Netty核心原理剖析与RPC实践》</span> </section> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 24px;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">前言</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="letter-spacing: 0px;">我们开发完需求,提测前,一般都需要</span><strong style="letter-spacing: 0px;">代码评审</strong><span style="letter-spacing: 0px;">。</span><span style="letter-spacing: 0px;">小伙伴们,你们知道代码评审,</span><strong style="letter-spacing: 0px;">一般都有哪些军规嘛</strong><span style="letter-spacing: 0px;">?</span><span style="letter-spacing: 0px;">今天</span><strong style="letter-spacing: 0px;">田螺哥</strong><span style="letter-spacing: 0px;">给你带来代码评审的18个军规。</span><br></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5703703703703704" src="/upload/9a088056a328b3b2d7cd40cb773661fb.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">1. 添加必要的注释</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其实,写代码的时候,没有必要写太多的注释,因为<strong>好的方法名、变量名</strong>,就是最好的注释。以下就是笔者总结的一些注释规范:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 所有的类都必须添加创建者和创建日期,以及简单的注释描述 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 方法内部的复杂业务逻辑或者算法,需要添加清楚的注释 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 一般情况下,注释描述类、方法、变量的作用 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 任何需要提醒的警告或 <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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">TODO</code>,也要注释清楚 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果是注释一行代码的,就用 <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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">//</code>;如果注释代码块或者接口方法的,有多行 <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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">/* **/</code> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">一块代码逻辑如果你站在一个陌生人的角度去看,第一遍看不懂的话,就需要添加注释了</strong> </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.975" src="/upload/c8770785d52c38a23bd9f1c35b47a2e2.png" data-type="png" data-w="400" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">以下就是一些添加注释的demo:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jko47W2yKibuVoL95RDbmZQI5sUN9ribqRiapQPyIA4w40hw5ukicNfIMxRpicDh2AmJCm1Kibn3MlOtGib3lsDy4Mzd50j/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">/**<br> * @author 田螺<br> * @date 2023/04/22 5:20 PM<br> * @desc 田螺的实现类,捡田螺、卖田螺 (更多干货,关注公众号:捡田螺的小男孩)<br> */<br>public class TianLuoClass {<br> <br> /**<br> * 这是卖田螺的两法,它将两个田螺的价格整数相加并返回结果。<br> * <br> * @param x 第一个整数<br> * @param y 第二个整数<br> * @<span style="color: #a6e22e;line-height: 26px;">return</span> 两个整数的和<br> */<br> public int sellTianLuo(int x, int y) {<br> <span style="color: #a6e22e;line-height: 26px;">return</span> x + y;<br> }<br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">2.日志打印规范</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">日志是快速定位问题的好帮手,是撕逼和甩锅的利器!打印好日志非常重要。如果代码评审的时候,<strong>这些日志规范没遵守,就需要修改</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 日志级别选择不对。常见的日志级别有 <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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">error、warn、info、debug</code>四种,不要反手就是 <code style="font-size: 14px;padding: 2p
作者:微信小助手
<section data-mpa-powered-by="yiban.io" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;line-height: 1.6;word-break: break-word;text-align: left;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 24px;"> <span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">常见的生产故障有哪些?</span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 24px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">在生产环境中,常见的故障类型包括但不限于以下几种:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">网络故障:网络故障可能包括网络连接中断、网络延迟过高、路由错误等。这可能导致系统无法正常访问外部资源,或导致应用程序无法与其他系统进行通信。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">服务器故障:服务器故障可能包括硬件故障、操作系统崩溃、服务崩溃等。这可能导致系统无法提供服务,导致应用程序不可用或性能下降。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">数据库故障:数据库故障可能包括数据库服务器崩溃、数据库连接错误、数据损坏等。这可能导致应用程序无法读取或写入数据,导致功能异常或数据不一致。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">软件错误:软件错误可能包括应用程序bug、配置错误、依赖项问题等。这可能导致应用程序崩溃、功能异常或性能下降。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">安全漏洞或攻击:安全漏洞或攻击可能导致系统遭受恶意行为,如未经授权访问、数据泄露、拒绝服务攻击等。这可能导致系统不稳定、数据损失或服务不可用。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">存储故障:存储故障可能包括磁盘故障、存储设备故障、数据丢失等。这可能导致数据不可用、文件损坏或无法恢复。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">配置错误:配置错误可能导致系统以错误的方式运行,例如错误的端口设置、错误的权限设置、错误的网络配置等。这可能导致应用程序无法正常工作或不可访问。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">第三方服务故障:如果应用程序依赖于第三方服务(如支付网关、短信网关等),当这些服务出现故障时,可能会导致应用程序无法正常工作或功能受限。</p> </section></li> </ol> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34,
作者:微信小助手
<section style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"> <br> </section> <section class="mp_profile_iframe_wrp"> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="MjM5MjMzMjE4MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/6tHzAJyhInBu9LYLryBFteHVfsYqZ7VHoK13sGsRd0LhBI1WxsusXgWXqZsU3yic2vTnSnbCpMPRnN2T1T1vVsw/0?wx_fmt=png" data-nickname="UX设计精选" data-alias="UEUXbest" data-signature="精选用户体验、交互设计文章,分享行业动态和工具资源。" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <h2 data-first-child="" data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.2em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-bottom: calc(1.16667em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 28px;">前言</span></h2> <p data-pid="Ox5RDzyX" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 16px;">虽然Al的爆火,越来越多的行业都在尝试着接入Al到工作流中。利用ChatGPT写文档产品、接入GPT的AP智能回答的客服、利用Tome做ppt的商务⋯ UI/Ux也不例外。</span></p> <p data-pid="i_zdHMQT" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 16px;">Midjourney的出现,其快速出图能力大大提高了设计师出初稿效率;其充满想象力的画面大大扩宽了设计师思路的边界;其多领域专精的特点成为设计师跨领域作战的利器。</span></p> <p data-pid="f6uCuwvU" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-synthesis: style;font-weight: 600;">UI设计利用Midjourney帮忙出图,可以从5个方向着手:</span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.375" data-s="300,640" src="/upload/b87bbfa245baadc2d86506cfcd4f0412.png" data-type="png" data-w="1080" style=""></p> <p><br></p> <hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"> <p style="text-align: center;"><br></p> <p style="text-align: center;"><br></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.058333333333333334" data-s="300,640" src="/upload/caba05d02b66682272a990604e58606a.png" data-type="png" data-w="1080" style=""></p> <p><span style="font-size: 28px;"><strong>一、确定界面载体</strong></span></p> <p><span style="font-size: 24px;"><strong><span style="color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;background-color: rgb(255, 255, 255);">UI/UX接触的硬件界面载体繁多。C端领域常见手机、电脑、平板电脑、watch端;B端领域除了电脑端,还有大屏、车机系统…</span></strong></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.6583333333333333" data-s="300,640" src="/upload/beec57ca31a09f813ea5c8b8d9715fda.png" data-type="png" data-w="1080" style=""></p> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 19px;"><br></span></h3> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 19px;">1)电脑端</span><br></h3> <p data-pid="xxQRuBtP" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">常见界面类型有网页设计,与客户端设计。以网页端为例,Midjourney绘制的网页,包含LOGO、导航栏、Banner位、主商品,且各模块之间的间距比较规整,乍一看美观性也足够。在原型阶段,可以作为结构参考</p> <p data-pid="xxQRuBtP" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="background-color: rgb(0, 0, 0);color: rgb(255, 255, 255);"> Prompt</span></p> <blockquote data-pid="5yHapUy7" style="border-left-color: rgb(211, 211, 211);color: rgb(100, 100, 100);margin-top: 1.4em;margin-bottom: 1.4em;padding-left: 1em;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <em>An e-commerce <span style="font-synthesis: style;font-weight: 600;">website </span>selling aromatherapy products with a minimalist design featuring a white background, deep green and forest color scheme, and illustrations of plants and leaves for a relaxed and soothing atmosphere, ui website design</em> </blockquote> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5074074074074074" data-s="300,640" src="/upload/578a81b05f80f6f3924fbb190e8503ff.png" data-type="png" data-w="1080" style=""></p> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">2)手机端</h3> <p><span style="color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;background-color: rgb(255, 255, 255);">Midjuorney可以做到的,包括但不限于icon、app ui、宣传/运营类插图、logo、吉祥物... 下文有展开。以常见app界面为例。</span></p> <p><span style="color: rgb(255, 255, 255);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;background-color: rgb(0, 0, 0);">Prompt</span></p> <blockquote data-pid="u3PmSCMk" style="border-left-color: rgb(211, 211, 211);color: rgb(100, 100, 100);margin-top: 1.4em;margin-bottom: 1.4em;padding-left: 1em;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <em><span style="font-synthesis: style;font-weight: 600;">An e-commerce app</span> selling plant essential oils with minimalist design, interface design, and deep green healing color scheme, <span style="font-synthesis: style;font-weight: 600;">ios app ui</span></em> </blockquote> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5074074074074074" data-s="300,640" src="/upload/49b441631b552251fd47b191ff80e17f.png" data-type="png" data-w="1080" style=""></p> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><br></h3> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">3)平板端</h3> <p data-pid="ytiIXZtY" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">与电脑端、手机的能展现的形式类似。</p> <p data-pid="ytiIXZtY" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(255, 255, 255);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;background-color: rgb(0, 0, 0);">Prompt</span></p> <blockquote data-pid="MwlRVR26" style="border-left-color: rgb(211, 211, 211);color: rgb(100, 100, 100);margin-top: 1.4em;margin-bottom: 1.4em;padding-left: 1em;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <em>a <span style="font-synthesis: style;font-weight: 600;">Tablet UI</span> anbout an e-commerce website selling aromatherapy products with a minimalist design featuring a white background , deep green and forest color scheme, and illustrations of plants and leaves for a relaxed and soothing atmosphere, tablet ui design</em> </blockquote> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5074074074074074" data-s="300,640" src="/upload/92dd9a2bc21e4558d9963fb8548a8522.png" data-type="png" data-w="1080" style=""></p> <p><br></p> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">4)手表端</h3> <p data-pid="SVNu1Kxp" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">手表端的UI设计作为设计样式与布局参考</p> <p><span style="font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;color: rgb(255, 255, 255);background-color: rgb(0, 0, 0);">Prompt</span></p> <blockquote data-pid="Hr7-AUEJ" style="border-left-color: rgb(211, 211, 211);color: rgb(100, 100, 100);margin-top: 1.4em;margin-bottom: 1.4em;padding-left: 1em;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <em>an <span style="font-synthesis: style;font-weight: 600;">iwatch app ui </span>about an app for fitness exercise, watchos</em> </blockquote> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5074074074074074" data-s="300,640" src="/upload/3cb2ff3d1888454801cf3ff265cd76e1.png" data-type="png" data-w="1080" style=""></p> <p><br></p> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">5)大屏端</h3> <p data-pid="7UfVCsKc" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">大屏用Midjourney出时,由于国内外对界面的设计不太一致。所以选择垫图的方式,能更好出效果。实际工作中,大屏的设计与布局更受限于具体需求,AI的图仅作为一定的样式参考。</p> <p data-pid="7UfVCsKc" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;color: rgb(255, 255, 255);background-color: rgb(0, 0, 0);">Prompt</span></p> <blockquote data-pid="PwoVcjDW" style="border-left-color: rgb(211, 211, 211);color: rgb(100, 100, 100);margin-top: 1.4em;margin-bottom: 1.4em;padding-left: 1em;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <em>A data dashboard for smart cities, featuring data visualization, <span style="font-synthesis: style;font-weight: 600;">data visualization big screen</span>, UI/UX, and a high-tech feel --ar 16:9</em> </blockquote> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5296296296296297" data-s="300,640" src="/upload/61da130b143b02887ddf7afb42c64277.png" data-type="png" data-w="1080" style=""></p> <p><br></p> <h3 data-into-catalog-status="" style="font-variant-numeric: inherit;font-variant-east-asian: inherit;font-variant-alternates: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-optical-sizing: inherit;font-kerning: inherit;font-feature-settings: inherit;font-variation-settings: inherit;margin-top: calc(1.90909em);margin-bottom: calc(1.27273em);clear: left;font-synthesis: style;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">6)车载端</h3> <p data-pid="q7vVb1oQ" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">车载HMI更多也是作为一个视觉成效的效果。具体应用场景暂时还比较有限。</p> <p data-pid="q7vVb1oQ" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;color: rgb(255, 255, 255);background-color: rgb(0, 0, 0);">Prompt</span></p> <blockquote data-pid="qCUk2Bb6" style="border-left-color: rgb(211, 211, 211);color: rgb(100, 100, 100);margin-top: 1.4em;margin-bottom: 1.4em;padding-left: 1em;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <em>Automotive HMI Concept Design for Electric Vehicle, <span style="font-synthesis: style;font-weight: 600;">car hmi design</span>, ui, ux</em> </blockquote> <p style="text-align: