作者:微信小助手
<p data-lake-id="f47942fff38c686ed626a530b6d84219" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;" data-mpa-powered-by="yiban.io"><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">点击上方蓝色“</span><span style="color: rgb(24, 144, 255);font-size: 13px;" data-mce-style="font-size: 10px">后端面试那些事儿</span><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">”,选择“设为星标”</span></p> <p data-lake-id="eca2a1864e13b3e1b84aafe9cb4abdff" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">学最好的别人,做最好的自己</span></p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: right;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">来自:r6d.cn/V9T7</span></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">没啥深入实践的理论系同学,在使用并发工具时,总是认为把HashMap改为ConcurrentHashMap,就完美解决并发了呀。或者使用写时复制的CopyOnWriteArrayList,性能更佳呀!技术言论虽然自由,但面对魔鬼面试官时,我们更在乎的是这些真的正确吗?</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">线程重用导致用户信息错乱</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">生产环境中,有时获取到的用户信息是别人的。查看代码后,发现是使用了ThreadLocal缓存获取到的用户信息。</p> <p style="text-align: center;"><img class="rich_pages" data-backh="209" data-backw="578" data-ratio="0.3613678373382625" data-s="300,640" src="/upload/b064d5353492c53420fcf8650a457800.png" data-type="png" data-w="1082" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">ThreadLocal适用于变量在线程间隔离,而在方法或类间共享的场景。若用户信息的获取比较昂贵(比如从DB查询),则在ThreadLocal中缓存比较合适。问题来了,为什么有时会出现用户信息错乱?</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">案例</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用ThreadLocal存放一个Integer值,代表需要在线程中保存的用户信息,初始null。先从ThreadLocal获取一次值,然后把外部传入的参数设置到ThreadLocal中,模拟从当前上下文获取用户信息,随后再获取一次值,最后输出两次获得的值和线程名称。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">固定思维认为,在设置用户信息前第一次获取的值始终是null,但要清楚程序运行在Tomcat,执行程序的线程是Tomcat的工作线程,其基于线程池。而线程池会重用固定线程,一旦线程重用,那么很可能首次从ThreadLocal获取的值是之前其他用户的请求遗留的值。这时,ThreadLocal中的用户信息就是其他用户的信息。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">bug 重现</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在配置文件设置Tomcat参数-工作线程池最大线程数设为1,这样始终是同一线程在处理请求:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">server.tomcat.max-threads=1<br></code></pre> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 先让用户1请求接口,第一、第二次获取到用户ID分别是null和1,符合预期 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 用户2请求接口,bug复现!第一、第二次获取到用户ID分别是1和2,显然第一次获取到了用户1的信息,因为Tomcat线程池重用了线程。两次请求线程都是同一线程:http-nio-45678-exec-1。 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">写业务代码时,首先要理解代码会跑在什么线程上:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> Tomcat服务器下跑的业务代码,本就运行在一个多线程环境(否则接口也不可能支持这么高的并发),并不能认为没有显式开启多线程就不会有线程安全问题 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 线程创建较昂贵,所以Web服务器会使用线程池处理请求,线程会被重用。使用类似ThreadLocal工具存放数据时,需注意在代码运行完后,显式清空设置的数据。 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">解决方案</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在finally代码块显式清除ThreadLocal中数据。即使新请求过来,使用了之前的线程,也不会获取到错误的用户信息。修正后代码:</p> <p style="text-align: center;"><img class="rich_pages" data-backh="235" data-backw="578" data-ratio="0.4066543438077634" data-s="300,640" src="/upload/74c8e26969f7269ab1c04dc43bf3ed98.png" data-type="png" data-w="1082" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">ThreadLocal利用独占资源的解决线程安全问题,若就是要资源在线程间共享怎么办?就需要用到线程安全的容器。使用了线程安全的并发工具,并不代表解决了所有线程安全问题。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">ThreadLocalRandom 可将其实例设置到静态变量,在多线程下重用吗?</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">current()的时候初始化一个初始化种子到线程,每次nextseed再使用之前的种子生成新的种子:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">UNSAFE.putLong(t = Thread.currentThread(), SEED,<br>r = UNSAFE.getLong(t, SEED) + GAMMA);<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果你通过主线程调用一次current生成一个ThreadLocalRandom实例保存,那么其它线程来获取种子的时候必然取不到初始种子,必须是每一个线程自己用的时候初始化一个种子到线程。可以在nextSeed设置一个断点看看:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">UNSAFE.getLong(Thread.currentThread(),SEED);<br></code></pre> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">ConcurrentHashMap真的安全吗?</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们都知道ConcurrentHashMap是个线程安全的哈希表容器,但它仅保证提供的原子性读写操作线程安全。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">案例</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">有个含900个元素的Map,现在再补充100个元素进去,这个补充操作由10个线程并发进行。开发人员误以为使用ConcurrentHashMap就不会有线程安全问题,于是不加思索地写出了下面的代码:在每一个线程的代码逻辑中先通过size方法拿到当前元素数量,计算ConcurrentHashMap目前还需要补充多少元素,并在日志中输出了这个值,然后通过putAll方法把缺少的元素添加进去。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">为方便观察问题,我们输出了这个Map一开始和最后的元素个数。</p> <p style="text-align: center;"><img class="rich_pages" data-backh="338" data-backw="578" data-ratio="0.5853432282003711" data-s="300,640" src="/upload/360c78b89c19b0052b36c190ff6fa2d9.png" data-type="png" data-w="1078" style="width: 100%;height: auto;"></p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 访问接口 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">分析日志输出可得:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 初始大小900符合预期,还需填充100个元素 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> worker13线程查询到当前需要填充的元素为49,还不是100的倍数 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 最后HashMap的总项目数是1549,也不符合填充满1000的预期 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">bug 分析</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">ConcurrentHashMap就像是一个大篮子,现在这个篮子里有900个桔子,我们期望把这个篮子装满1000个桔子,也就是再装100个桔子。有10个工人来干这件事儿,大家先后到岗后会计算还需要补多少个桔子进去,最后把桔子装入篮子。ConcurrentHashMap这篮子本身,可以确保多个工人在装东西进去时,不会相互影响干扰,但无法确保工人A看到还需要装100个桔子但是还未装时,工人B就看不到篮子中的桔子数量。你往这个篮子装100个桔子的操作不是原子性的,在别人看来可能会有一个瞬间篮子里有964个桔子,还需要补36个桔子。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">ConcurrentHashMap对外提供能力的限制:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 使用不代表对其的多个操作之间的状态一致,是没有其他线程在操作它的。如果需要确保需要手动加锁 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 诸如size、isEmpty和containsValue等聚合方法,在并发下可能会反映ConcurrentHashMap的中间状态。因此在并发情况下,这些方法的返回值只能用作参考,而不能用于流程控制。显然,利用size方法计算差异值,是一个流程控制 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 诸如putAll这样的聚合方法也不能确保原子性,在putAll的过程中去获取数据可能会获取到部分数据 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">解决方案</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">整段逻辑加锁:</p> <p style="text-align: center;"><img class="rich_pages" data-backh="292" data-backw="578" data-ratio="0.5055555555555555" data-s="300,640" src="/upload/54ddac1d2aab588a0ddd21014c04fb66.png" data-type="png" data-w="1080" style="width: 100%;height: auto;"></p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 只有一个线程查询到需补100个元素,其他9个线程查询到无需补,最后Map大小1000 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">既然使用ConcurrentHashMap还要全程加锁,还不如使用HashMap呢?不完全是这样。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">ConcurrentHashMap提供了一些原子性的简单复合逻辑方法,用好这些方法就可以发挥其威力。这就引申出代码中常见的另一个问题:在使用一些类库提供的高级工具类时,开发人员可能还是按照旧的方式去使用这些新类,因为没有使用其真实特性,所以无法发挥其威力。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">知己知彼,百战百胜</h3> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">案例</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用Map来统计Key出现次数的场景。</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 使用ConcurrentHashMap来统计,Key的范围是10 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 使用最多10个并发,循环操作1000万次,每次操作累加随机的Key </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 如果Key不存在的话,首次设置值为1。 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">show me code:</p> <p style="text-align: center;"><img class="rich_pages" data-backh="285" data-backw="578" data-ratio="0.4921077065923863" data-s="300,640" src="/upload/ef71175c9dc1636a1b2d85180c80c25b.png" data-type="png" data-w="1077" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">有了上节经验,我们这直接锁住Map,再做</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 判断 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 读取现在的累计值 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> +1 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 保存累加后值 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这段代码在功能上的确毫无没有问题,但却无法充分发挥ConcurrentHashMap的性能,优化后:</p> <p style="text-align: center;"><img class="rich_pages" data-backh="249" data-backw="578" data-ratio="0.43068391866913125" data-s="300,640" src="/upload/7bdb511851aec6c35e5e10489acd8b1.png" data-type="png" data-w="1082" style="width: 100%;height: auto;"></p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> ConcurrentHashMap的原子性方法computeIfAbsent做复合逻辑操作,判断K是否存在V,若不存在,则把Lambda运行后结果存入Map作为V,即新创建一个LongAdder对象,最后返回V 因为computeIfAbsent返回的V是LongAdder,是个线程安全的累加器,可直接调用其increment累加。这样在确保线程安全的情况下达到极致性能,且代码行数骤减。 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">性能测试</h4> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 使用StopWatch测试两段代码的性能,最后的断言判断Map中元素的个数及所有V的和是否符合预期来校验代码正确性 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 性能测试结果:比使用锁性能提升至少5倍。 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">computeIfAbsent高性能之道</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Java的Unsafe实现的CAS。它在JVM层确保写入数据的原子性,比加锁效率高:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,<br> Node<K,V> c, Node<K,V> v) {<br> <span style="color: rgb(230, 192, 123);line-height: 26px;">return</span> U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);<br>}<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">所以不要以为只要用了ConcurrentHashMap并发工具就是高性能的高并发程序。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">辨明 computeIfAbsent、putIfAbsent</h4> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 当Key存在的时候,如果Value获取比较昂贵的话,putIfAbsent就白白浪费时间在获取这个昂贵的Value上(这个点特别注意) </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> Key不存在的时候,putIfAbsent返回null,小心空指针,而computeIfAbsent返回计算后的值 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 当Key不存在的时候,putIfAbsent允许put null进去,而computeIfAbsent不能,之后进行containsKey查询是有区别的(当然了,此条针对HashMap,ConcurrentHashMap不允许put null value进去) </section></li> </ul> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">CopyOnWriteArrayList 之殇</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">再比如一段简单的非 DB操作的业务逻辑,时间消耗却超出预期时间,在修改数据时操作本地缓存比回写DB慢许多。原来是有人使用了CopyOnWriteArrayList缓存大量数据,而该业务场景下数据变化又很频繁。CopyOnWriteArrayList虽然是一个线程安全版的ArrayList,但其每次修改数据时都会复制一份数据出来,所以只适用读多写少或无锁读场景。所以一旦使用CopyOnWriteArrayList,一定是因为场景适宜而非炫技。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">CopyOnWriteArrayList V.S 普通加锁ArrayList读写性能</h4> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 测试并发写性能 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 测试结果:高并发写,CopyOnWriteArray比同步ArrayList慢百倍 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 测试并发读性能 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 测试结果:高并发读(100万次get操作),CopyOnWriteArray比同步ArrayList快24倍 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">高并发写时,CopyOnWriteArrayList为何这么慢呢?因为其每次add时,都用Arrays.copyOf创建新数组,频繁add时内存申请释放性能消耗大。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">总结</h3> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Don't !!!</h4> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 不要只会用并发工具,而不熟悉线程原理 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 不要觉得用了并发工具,就怎么都线程安全 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 不熟悉并发工具的优化本质,就难以发挥其真正性能 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 不要不结合当前业务场景,就随意选用并发工具,可能导致系统性能更差 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Do !!!</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">认真阅读官方文档,理解并发工具适用场景及其各API的用法,并自行测试验证,最后再使用 并发bug本就不易复现, 多自行进行性能压力测试</p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section mpa-from-tpl="t" style="margin-top: 10px;margin-bottom: 10px;"> <p style="margin-right: auto;margin-left: auto;width: 231.1875px;"><img data-ratio="0.16666666666666666" src="/upload/89aa6c9fe16e0dfcf56dad1a9f9078ff.png" data-type="gif" data-w="300" style="width: auto;"></p> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-recommend-type="list-title" data-recommend-tid="6" data-mpa-template="t" style="width: 100%;display: flex;justify-content: center;align-items: center;" data-mid="" data-from="yb-recommend"> <section style="width: 100%;padding: 14px;background: rgb(255, 255, 255);border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(232, 232, 235);" data-mid=""> <section style="width: 100%;display: flex;justify-content: center;align-items: center;align-items: flex-end;" data-mid=""> <section data-mid="" style="height: 28px;padding: 4px 22px;font-size: 14px;font-weight: 500;color: rgb(19, 52, 86);line-height: 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/sUbvrqLicbpzB81mjeBxPuxnYdalGxNnJo30L2Hq3WwGficcq8w5YJkLeXnsNHocN53k55TfN5mBpCdicGRyfDg1g/640?wx_fmt=png");background-repeat: no-repeat;background-size: 100% 100%;margin-bottom: -14px;z-index: 10;"> <p data-mid="">往期推荐</p> </section> </section> <section style="width: 100%;border-width: 1px;border-style: solid;border-color: rgb(198, 226, 255);padding: 17px 16px 9px;" data-mid=""> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247493708_1" data-recommend-article-time="1617686400" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkFlK4ENeh94JAxoZZRYz6dGTeD0tQScMauibXXuriaD9djse4XN8jPTSxC5VYAM9ibprplImNbIMwgmQ/0?wx_fmt=jpeg" data-recommend-article-title="90%的开发都不太考虑这个,但只要出问题直接公司完蛋!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493708&idx=1&sn=c2408f6d4029f98d0756b3561cf34f39&chksm=97b47654a0c3ff4219976ee91de14989b1ae3b8f143a3d575fefb49f39300d018a12887ef9bc#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493708&idx=1&sn=c2408f6d4029f98d0756b3561cf34f39&chksm=97b47654a0c3ff4219976ee91de14989b1ae3b8f143a3d575fefb49f39300d018a12887ef9bc&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">90%的开发都不太考虑这个,但只要出问题直接公司完蛋!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247493708_2" data-recommend-article-time="1617686400" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/8cpIYdAicCct4AnaECAgWicR7ZEWJicBffchMF0xib0grrJxsSQiaZYrWJ2Nk81bujsTvNpFHBewPVYSzlRic16zJicwQ/0?wx_fmt=jpeg" data-recommend-article-title="又在GitHub上挖到个宝藏:Switch模拟器!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493708&idx=2&sn=642fab82cdf1c822bfa37649e4498da9&chksm=97b47654a0c3ff42fc9b66315f400eab10dae45dd226855fc57b68cf3f8626ae7ffa2d6718ea#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493708&idx=2&sn=642fab82cdf1c822bfa37649e4498da9&chksm=97b47654a0c3ff42fc9b66315f400eab10dae45dd226855fc57b68cf3f8626ae7ffa2d6718ea&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">又在GitHub上挖到个宝藏:Switch模拟器!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247493703_1" data-recommend-article-time="1617426060" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZuZ9XFXCsTCGkLnHsibyUgK2SDibJkqibg4lW3FU5ajNVFxeFmaltnK4XImBaUWmicp8XM4jgY2rWy0w/0?wx_fmt=jpeg" data-recommend-article-title="955 不加班的公司名单!2021 最新版!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493703&idx=1&sn=ed4ccd76875bb07108c889404faff010&chksm=97b4765fa0c3ff498cf055b52aa1abbdd45a5293a4c098bf09544c07d7a3d32f963b2ae620c7#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493703&idx=1&sn=ed4ccd76875bb07108c889404faff010&chksm=97b4765fa0c3ff498cf055b52aa1abbdd45a5293a4c098bf09544c07d7a3d32f963b2ae620c7&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">955 不加班的公司名单!2021 最新版!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247493703_2" data-recommend-article-time="1617426060" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkFasVRlHA3OKGSD4U2ibFpb42b5s9QdGs94AklicC5XVAEMhazM3r54ZQvWtG9QViaibExawFI3QbxaYw/0?wx_fmt=jpeg" data-recommend-article-title="苹果开源代码中惊现“wechat”,老外注释的吐槽亮了!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493703&idx=2&sn=e7c7e51000f53648d384456421e714fb&chksm=97b4765fa0c3ff497122a74b546dab03fa823de2d05a0d42e21f0ae3bb4bd19c4c8b1fe446c9#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493703&idx=2&sn=e7c7e51000f53648d384456421e714fb&chksm=97b4765fa0c3ff497122a74b546dab03fa823de2d05a0d42e21f0ae3bb4bd19c4c8b1fe446c9&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">苹果开源代码中惊现“wechat”,老外注释的吐槽亮了!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247493689_1" data-recommend-article-time="1617339660" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZ8hkazCWYtrJeicRRwZSNlVWdmtLBW3xJTq5f2mZcJR7dibptu1Ij4gmCj2ranMqcwzfNwyl86pI0w/0?wx_fmt=jpeg" data-recommend-article-title="为何 IntelliJ IDEA 比 Eclipse 更好?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493689&idx=1&sn=2a05082c472a145a6e14433616d04255&chksm=97b47621a0c3ff37368fc6312b4af4cc5e5ca54b5b458d7ad576fa76913153f6aa3154e020d6#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493689&idx=1&sn=2a05082c472a145a6e14433616d04255&chksm=97b47621a0c3ff37368fc6312b4af4cc5e5ca54b5b458d7ad576fa76913153f6aa3154e020d6&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">为何 IntelliJ IDEA 比 Eclipse 更好?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247493689_2" data-recommend-article-time="1617339660" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkFvYQqWLMCj7X7sn6Twc712VNmHHwdNGia9ZQlwaTkEKdx6EiajxwOyL2CfDzyQkDKCZZrEhGMeCMgg/0?wx_fmt=jpeg" data-recommend-article-title="日本政府用AI分配对象了!给你分一个的话,敢不敢要?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493689&idx=2&sn=4a1dbb4784820d8062088f00a4a64d02&chksm=97b47621a0c3ff378a2bfc2f2fe4847cc063617a9fe7a3b344d749c9975df601b7cc88de135f#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247493689&idx=2&sn=4a1dbb4784820d8062088f00a4a64d02&chksm=97b47621a0c3ff378a2bfc2f2fe4847cc063617a9fe7a3b344d749c9975df601b7cc88de135f&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;border-bottom:none !important;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">日本政府用AI分配对象了!给你分一个的话,敢不敢要?</p> </section></a> </section> </section> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);letter-spacing: 0.008em;"></span></p> <section data-mpa-template="t" mpa-from-tpl="t" style="white-space: normal;"> <p style="text-align: center;"><strong><span style="color: rgb(140, 140, 140);letter-spacing: 0.008em;font-size: 14px;">一起进大厂,每日学干货</span></strong></p> </section> <section style="margin-top: 5px;white-space: normal;text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"> <span style="color: rgb(0, 0, 0);"><strong><span style="font-size: 14px;">关注我回复【</span></strong></span> <span style="color: rgb(255, 76, 65);"><strong><span style="font-size: 14px;">加群</span></strong></span> <span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">】,加入Java技术交流群</span></strong></span> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <p style="text-align: center;"><img data-ratio="0.5982532751091703" src="/upload/4e21037b66f60f7d73d060863de4b4b5.png" data-type="gif" data-w="458" data-width="100%" style="color: rgb(62, 62, 62);font-size: 16px;vertical-align: middle;width: 62.0938px;"></p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzIxMzQzNzMwMw==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/HmHDU48icAtYvlypOY9VaGVXQ639L63Iq6zHiclgibG0CAhgrJ2JLRibKbeCgVIx7WXcicbMW6AJL1Hos9AoJTqtVfA/0?wx_fmt=png" data-nickname="后端面试那些事" data-alias="" data-signature="后端老鸟带你进击大厂后端职位!这里分享面试题、内推渠道,还有各种后端工程师相关的学习资料与工具分享等干货内容!"></mpprofile> </section> </section> <p style="text-align: center;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-role="paragraph" mpa-from-tpl="t" style="white-space: normal;border-width: 0px;border-style: none;border-color: initial;"> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;box-sizing: border-box;"> <section style="padding: 0.5em;border-width: 1px;border-style: solid;border-color: rgb(249, 110, 87);box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;box-sizing: border-box;"> <section powered-by="xiumi.us" style="text-align: left;box-sizing: border-box;"> <section style="text-align: justify;color: rgb(64, 84, 115);box-sizing: border-box;"> <p style="box-sizing: border-box;"><img src="/upload/a53e4e397528931045198e4f89d7ba0.png" data-type="png" data-ratio="0.33611111111111114" data-w="1080"></p> <p style="box-sizing: border-box;">点击“阅读原文”,领取 2021 年<strong>最新免费技术资料大全</strong></p> </section> </section> </section> </section> <section powered-by="xiumi.us" style="font-size: 32px;color: rgb(249, 110, 87);box-sizing: border-box;text-align: left;"> ↓↓↓ </section> </section> </section> </section>
作者:じ☆ve不哭
> 亲测有效!!!(2021-04-12) J7QT3-3GCPG-9GVWT-CH2XR-GMRJM 激活工具:[https://studyjava.lanzous.com/iOd8Rnyxy3c](https://studyjava.lanzous.com/iOd8Rnyxy3c) 密码: study
作者:じ☆ve不哭
双击打开IDEA64.exe无反应 首先找到IDEA 安装目录bin文件夹下的idea.bat 文件  找到IDEA 安装目录bin文件夹下的idea.bat  在文件最后一行加入 pause 然后保存文件 双击idea.bat 运行  上面提示说是 D:\SoftWare\IDEA\IntelliJ IDEA 2019.3\bin\jetbrains-agent.jar 路径有错误 通过上面的方法我把问题锁定到jar包路径上,但是改完I盘路径上的idea64.exe.vmoptions 文件之后仍然报一样的错,最后我在电脑中全局搜索idea64.exe.vmoptions文件 发现在C:\Users\本地用户\AppData\Roaming\JetBrains\IntelliJIdea2020.1路径下还有一个一样的文件,改完后正常。
作者:微信小助手
<p style="padding: 8px 10px;line-height: 26px;color: black;margin: 1em 4px;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;letter-spacing: 2px;" data-mpa-powered-by="yiban.io">大家好,我是 Guide哥!</p> <p style="padding: 8px 10px;line-height: 26px;color: black;margin: 1em 4px;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;letter-spacing: 2px;">废话也不多说,一篇文章带你上手 Docker!<br></p> <p style="padding: 8px 10px;line-height: 26px;color: black;margin: 1em 4px;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;letter-spacing: 2px;"><strong>原创不易,有帮助的话,欢迎点赞分享啊!<img data-ratio="1" src="/upload/ac046868e21ad60c4a5c916787f8be88.png" data-type="png" data-w="20" style="display:inline-block;width:20px;vertical-align:text-bottom;"></strong></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;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;letter-spacing: 2px;line-height: 1.75em;"> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(41, 128, 185);border-bottom: 1px solid rgb(52, 152, 219);border-top-color: rgb(52, 152, 219);border-right-color: rgb(52, 152, 219);border-left-color: rgb(52, 152, 219);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Docker 介绍</h2> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>什么是 Docker?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">说实话关于 Docker 是什么并太好说,下面我通过四点向你说明 Docker 到底是个什么东西。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-size: 16px;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> Docker 是世界领先的软件容器平台,基于 <strong style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);">Go 语言</strong> <span style="color: rgb(1, 1, 1);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 2px;text-align: left;"> 进行开发实现。</span> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> Docker 能够自动执行重复性任务,例如搭建和配置开发环境,从而解放开发人员。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> Docker可以 <strong style="color: rgb(52, 152, 219);">对进程进行封装隔离,属于操作系统层面的虚拟化技术。</strong> 由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">官网地址:<span style="color: rgb(0, 82, 255);">https://www.docker.com/</span> 。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6477857878475798" src="/upload/2f3600a1ef0717515673a13c56d60db7.jpg" data-type="jpeg" data-w="971" style="display: block;margin-right: auto;margin-left: auto;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 什么是Docker </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>为什么要用 Docker?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">传统的开发流程中,我们的项目通常需要使用 MySQL、Redis、FastDFS 等等环境,这些环境都是需要我们手动去进行下载并配置的,安装配置流程极其复杂,而且不同系统下的操作也不一样。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Docker 的出现完美地解决了这一问题,我们可以在容器中安装 MySQL、Redis 等软件环境,使得应用和环境架构分开,它的优势在于:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-size: 16px;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 一致的运行环境,能够更轻松地迁移 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 对进程进行封装隔离,容器与容器之间互不影响,更高效地利用系统资源 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 可以通过镜像复制多个一致的容器 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">另外,<span style="font-weight: bold;color: #2C3E50;">《Docker 从入门到实践》</span><sup style="line-height: 0;font-weight: bold;color: #2C3E50;">[1]</sup> 这本开源书籍中也已经给出了使用 Docker 的原因。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.2885906040268456" src="/upload/7d84dc6401358c89fcd5547cc704f540.png" data-type="png" data-w="894" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(41, 128, 185);border-bottom: 1px solid rgb(52, 152, 219);border-top-color: rgb(52, 152, 219);border-right-color: rgb(52, 152, 219);border-left-color: rgb(52, 152, 219);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Docker 的安装</h2> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>Windows<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">接下来对 Docker 进行安装,以 Windows 系统为例,访问 Docker 的官网:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.40632603406326034" src="/upload/4f0d874a5508e1fc09a97450bfa190fc.png" data-type="png" data-w="1233" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">然后点击<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(41, 128, 185);">Get Started</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 data-ratio="0.5569285083848191" src="/upload/2ea1151bfc7175586ca577410dce5802.png" data-type="png" data-w="1133" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在此处点击<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(41, 128, 185);">Download for Windows</code>即可进行下载。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果你的电脑是<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(41, 128, 185);">Windows 10 64位专业版</code>的操作系统,则在安装 Docker 之前需要开启一下<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(41, 128, 185);">Hyper-V</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 data-ratio="0.5020297699594046" src="/upload/b5eada23defd6646a35814b4378be159.png" data-type="png" data-w="739" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">点击<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(41, 128, 185);">启用或关闭Windows功能</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 data-ratio="0.3001579778830964" src="/upload/61937d9b2e0aa209aaeae6944fd6e617.png" data-type="png" data-w="633" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">勾选上<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(41, 128, 185);">Hyper-V</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 data-ratio="1.0048899755501222" src="/upload/dd3dc1e30ded035430efb97bc526f0b7.png" data-type="png" data-w="409" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">完成更改后需要重启一下计算机。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">开启了<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(41, 128, 185);">Hyper-V</code>后,我们就可以对 Docker 进行安装了,打开安装程序后,等待片刻点击<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(41, 128, 185);">Ok</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 data-ratio="0.6861626248216833" src="/upload/38c3a4f61e47724e55ad51b713c95114.png" data-type="png" data-w="701" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">安装完成后,我们仍然需要重启计算机,重启后,若提示如下内容:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3548387096774194" src="/upload/c8434831252be87ab9ea99d753c4294b.png" data-type="png" data-w="682" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">它的意思是询问我们是否使用 WSL2,这是基于 Windows 的一个 Linux 子系统,这里我们取消即可,它就会使用我们之前勾选的<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(41, 128, 185);">Hyper-V</code>虚拟机。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">因为是图形界面的操作,这里就不介绍 Docker Desktop 的具体用法了。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>Mac<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">直接使用 Homebrew 安装即可</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">brew install --cask docker<br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>Linux<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">下面来看看 Linux 中如何安装 Docker,这里以 CentOS7 为例。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在测试或开发环境中,Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,执行这个脚本后就会自动地将一切准备工作做好,并且把 Docker 的稳定版本安装在系统中。</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">curl -fsSL get.docker.com -o get-docker.sh<br></code></pre> <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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">sh get-docker.sh --mirror Aliyun<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">安装完成后直接启动服务:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">systemctl start docker<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">推荐设置开机自启,执行指令:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">systemctl enable docker<br></code></pre> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(41, 128, 185);border-bottom: 1px solid rgb(52, 152, 219);border-top-color: rgb(52, 152, 219);border-right-color: rgb(52, 152, 219);border-left-color: rgb(52, 152, 219);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Docker 中的几个概念</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在正式学习 Docker 之前,我们需要了解 Docker 中的几个核心概念:</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>镜像<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">镜像就是一个只读的模板,镜像可以用来创建 Docker 容器,一个镜像可以创建多个容器</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>容器<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">容器是用镜像创建的运行实例,Docker 利用容器独立运行一个或一组应用。它可以被启动、开始、停止、删除,每个容器都是相互隔离的、保证安全的平台。可以把容器看作是一个简易的 Linux 环境和运行在其中的应用程序。容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>仓库<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">仓库是集中存放镜像文件的场所。仓库和仓库注册服务器是有区别的,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签。仓库分为公开仓库和私有仓库两种形式,最大的公开仓库是 DockerHub,存放了数量庞大的镜像供用户下载,国内的公开仓库有阿里云、网易云等</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(52, 152, 219);padding-left: 10px;border-left: 2px solid rgb(52, 152, 219);"><span style="display: none;"></span>总结<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">通俗点说,一个镜像就代表一个软件;而基于某个镜像运行就是生成一个程序实例,这个程序实例就是容器;而仓库是用来存储 Docker 中所有镜像的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">其中仓库又分为远程仓库和本地仓库,和 Maven 类似,倘若每次都从远程下载依赖,则会大大降低效率,为此,Maven 的策略是第一次访问依赖时,将其下载到本地仓库,第二次、第三次使用时直接用本地仓库的依赖即可,Docker 的远程仓库和本地仓库的作用也是类似的。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(41, 128, 185);border-bottom: 1px solid rgb(52, 152, 219);border-top-color: rgb(52, 152, 219);border-right-color: rgb(52, 152, 219);border-left-color: rgb(52, 152, 219);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Docker 初体验</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">下面我们来对 Docker 进行一个初步的使用,这里以下载一个 MySQL 的镜像为例<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(41, 128, 185);">(在CentOS7下进行)</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">和 GitHub 一样,Docker 也提供了一个 DockerHub 用于查询各种镜像的地址和安装教程,为此,我们先访问 DockerHub:<span style="font-weight: bold;color: #2C3E50;">https://hub.docker.com/</span><sup style="line-height: 0;font-weight: bold;color: #2C3E50;">[2]</sup></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4314800313234142" src="/upload/eb567f326b8a7e9f05ef753263eafb7f.png" data-type="png" data-w="1277" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在左上角的搜索框中输入<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(41, 128, 185);">MySQL</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 data-ratio="0.50859375" src="/upload/ce2850efcea4502af8ac5406c80895ad.png" data-type="png" data-w="1280" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">可以看到相关 MySQL 的镜像非常多,若右上角有<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(41, 128, 185);">OFFICIAL IMAGE</code>标识,则说明是官方镜像,所以我们点击第一个 MySQL 镜像:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.30866141732283464" src="/upload/9a6d788b8619ab5bba7af0ec9adf3cd4.png" data-type="png" data-w="1270" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">右边提供了下载 MySQL 镜像的指令为<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(41, 128, 185);">docker pull MySQL</code>,但该指令始终会下载 MySQL 镜像的最新版本。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若是想下载指定版本的镜像,则点击下面的<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(41, 128, 185);">View Available Tags</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 data-ratio="0.4240062353858145" src="/upload/11e54b3cd37560a355d5266313262fd6.png" data-type="png" data-w="1283" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这里就可以看到各种版本的镜像,右边有下载的指令,所以若是想下载 5.7.32 版本的 MySQL 镜像,则执行:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker pull MySQL:5.7.32<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">然而下载镜像的过程是非常慢的,所以我们需要配置一下镜像源加速下载,访问<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(41, 128, 185);">阿里云</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 data-ratio="0.19612403100775194" src="/upload/6029c1bf26ae15167ba769273bd56d6f.png" data-type="png" data-w="1290" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">点击控制台:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3107476635514019" src="/upload/52464e66b3135f4a99478fbc9ae600d6.png" data-type="png" data-w="1284" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">然后点击左上角的菜单,在弹窗的窗口中,将鼠标悬停在产品与服务上,并在右侧搜索容器镜像服务,最后点击容器镜像服务:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.8012578616352202" src="/upload/176f1dfa15aa6114e7756f8c3fc60676.png" data-type="png" data-w="795" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">点击左侧的镜像加速器,并依次执行右侧的配置指令即可。</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">sudo mkdir -p /etc/docker<br>sudo tee /etc/docker/daemon.json <<-'EOF'<br>{<br> "registry-mirrors": ["https://679xpnpz.mirror.aliyuncs.com"]<br>}<br>EOF<br>sudo systemctl daemon-reload<br>sudo systemctl restart docker<br></code></pre> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(41, 128, 185);border-bottom: 1px solid rgb(52, 152, 219);border-top-color: rgb(52, 152, 219);border-right-color: rgb(52, 152, 219);border-left-color: rgb(52, 152, 219);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Docker 镜像指令</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Docker 需要频繁地操作相关的镜像,所以我们先来了解一下 Docker 中的镜像指令。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若想查看 Docker 中当前拥有哪些镜像,则可以使用 <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(41, 128, 185);">docker images</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">[root@izrcf5u3j3q8xaz ~]# docker images<br>REPOSITORY TAG IMAGE ID CREATED SIZE<br>MySQL 5.7.32 f07dfa83b528 11 days ago 448MB<br>tomcat latest feba8d001e3f 2 weeks ago 649MB<br>nginx latest ae2feff98a0c 2 weeks ago 133MB<br>hello-world latest bf756fb1ae65 12 months ago 13.3kB<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">其中<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(41, 128, 185);">REPOSITORY</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(41, 128, 185);">TAG</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(41, 128, 185);">IMAGE ID</code>为镜像 id(唯一的),<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(41, 128, 185);">CREATED</code>为创建时间,注意这个时间并不是我们将镜像下载到 Docker 中的时间,而是镜像创建者创建的时间,<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(41, 128, 185);">SIZE</code>为镜像大小。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">该指令能够查询指定镜像名:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker image MySQL<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若如此做,则会查询出 Docker 中的所有 MySQL 镜像:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">[root@izrcf5u3j3q8xaz ~]# docker images MySQL<br>REPOSITORY TAG IMAGE ID CREATED SIZE<br>MySQL 5.6 0ebb5600241d 11 days ago 302MB<br>MySQL 5.7.32 f07dfa83b528 11 days ago 448MB<br>MySQL 5.5 d404d78aa797 20 months ago 205MB<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">该指令还能够携带<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(41, 128, 185);">-p</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(41, 128, 185);">docker images -q</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(41, 128, 185);">-q</code>表示仅显示镜像的 id:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">[root@izrcf5u3j3q8xaz ~]# docker images -q<br>0ebb5600241d<br>f07dfa83b528<br>feba8d001e3f<br>d404d78aa797<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若是要下载镜像,则使用:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker pull MySQL:5.7<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><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(41, 128, 185);">docker pull</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(41, 128, 185);">docker pull MySQL</code>,则会下载镜像的最新版本。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">一般在下载镜像前我们需要搜索一下镜像有哪些版本才能对指定版本进行下载,使用指令:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker search MySQL<br></code></pre> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.1732283464566929" src="/upload/d18310c762cdceaff1d8c97df4eccde3.png" data-type="png" data-w="1016" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">不过该指令只能查看 MySQL 相关的镜像信息,而不能知道有哪些版本,若想知道版本,则只能这样查询:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker search MySQL:5.5<br></code></pre> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.08036622583926754" src="/upload/3885722677350d81a68eca16d9ce6400.png" data-type="png" data-w="983" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若是查询的版本不存在,则结果为空:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.07167235494880546" src="/upload/28198c11f234596c9c7313282f6b9b2f.png" data-type="png" data-w="586" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">删除镜像使用指令:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker image rm MySQL:5.5<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若是不指定版本,则默认删除的也是最新版本。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">还可以通过指定镜像 id 进行删除:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker image rm bf756fb1ae65<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">然而此时报错了:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">[root@izrcf5u3j3q8xaz ~]# docker image rm bf756fb1ae65<br>Error response from daemon: conflict: unable to delete bf756fb1ae65 (must be forced) - image is being used by stopped container d5b6c177c151<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这是因为要删除的<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(41, 128, 185);">hello-world</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker image rm -f bf756fb1ae65<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">该指令会将镜像和通过该镜像执行的容器全部删除,谨慎使用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Docker 还提供了删除镜像的简化版本:<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(41, 128, 185);">docker rmi 镜像名:版本标志</code> 。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">此时我们即可借助<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(41, 128, 185);">rmi</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(41, 128, 185);">-p</code>进行一些联合操作,比如现在想删除所有的 MySQL 镜像,那么你需要查询出 MySQL 镜像的 id,并根据这些 id 一个一个地执行<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(41, 128, 185);">docker rmi</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker rmi -f $(docker images MySQL -q)<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">首先通过<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(41, 128, 185);">docker images MySQL -q</code>查询出 MySQL 的所有镜像 id,<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(41, 128, 185);">-q</code>表示仅查询 id,并将这些 id 作为参数传递给<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(41, 128, 185);">docker rmi -f</code>指令,这样所有的 MySQL 镜像就都被删除了。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(41, 128, 185);border-bottom: 1px solid rgb(52, 152, 219);border-top-color: rgb(52, 152, 219);border-right-color: rgb(52, 152, 219);border-left-color: rgb(52, 152, 219);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Docker 容器指令</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">掌握了镜像的相关指令之后,我们需要了解一下容器的指令,容器是基于镜像的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若需要通过镜像运行一个容器,则使用:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker run tomcat:8.0-jre8<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">当然了,运行的前提是你拥有这个镜像,所以先下载镜像:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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;">docker pull tomcat:8.0-jre8<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">下载完成后就可以运行了,运行后查看一下当前运行的容器:<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(41, 128, 185);">docker ps</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 data-ratio="0.06307977736549165" src="/upload/186d973398f2f598eca894ae28b61fa7.png" data-type="png" data-w="1078" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">其中<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(41, 128, 185);">CONTAINER_ID</code>为容器的 id,<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(41, 128, 185);">IMAGE</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(41, 128, 185);">COMMAND</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(41, 128, 185);">CREATED</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(41, 128, 185);">STATUS</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(41, 128, 185);">PORTS</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(41, 128, 185);">NAMES</code>为容器的名称。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">通过该方式运行的 tomcat 是不能直接被外部访问的,因为容器具有隔离性,若是想直接通过 8080 端口访问容器内部的 tomcat,则需要对宿主机端口与容器内的端口进行映射:</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/qbvaL9taELsjPKCaeYZwcPyDh1EgxTlu8pm5CRh70ncDf4f4Xpicmic8ZPglJMOQhK9jnXLrReraHlrYazfrb9Pg76EfiabOE5M/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: -webk
作者:微信小助手
<section powered-by="xiumi.us" style="margin-top: 30px;margin-bottom: 20px;white-space: normal;max-width: 100%;box-sizing: border-box;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;display: inline-block;width: 574px;vertical-align: top;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="padding: 5px 10px;max-width: 100%;box-sizing: border-box;display: inline-block;width: 574px;vertical-align: top;border-width: 3px;border-radius: 20px;border-style: solid;border-color: rgb(226, 241, 246);overflow: hidden;background-color: rgb(226, 241, 246);overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;transform: translate3d(1px, 0px, 0px);overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;text-align: center;font-size: 14px;color: rgb(149, 187, 202);overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;"> 文章来源 github</p> </section> </section> </section> </section> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"></p> </section> </section> </section> <section powered-by="xiumi.us" style="margin-bottom: -15px;white-space: normal;max-width: 100%;box-sizing: border-box;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: left;transform: translate3d(20px, 0px, 0px);overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;display: inline-block;width: 55px;height: 40px;vertical-align: top;overflow: hidden;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;text-align: center;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;vertical-align: middle;display: inline-block;line-height: 0;overflow-wrap: break-word !important;"> <img data-ratio="0.696" data-w="500" data-type="gif" class="__bg_gif" src="/upload/a040d38d88cdcfaed38ea2886a5b9df0.png" style="box-sizing: border-box;vertical-align: middle;overflow-wrap: break-word !important;visibility: visible !important;width: 500px !important;"> </section> </section> </section> </section> <section powered-by="xiumi.us" style="white-space: normal;max-width: 100%;box-sizing: border-box;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;letter-spacing: 0.544px;font-size: 16px;overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;text-align: center;overflow-wrap: break-word !important;"><br></p> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;text-align: center;overflow-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box;text-shadow: rgb(195, 134, 234) 2px 0px 7px;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;">点击蓝字:波哥的IT人生,关注我们</strong></span></p> </section> </section> <section powered-by="xiumi.us" style="margin-bottom: 10px;white-space: normal;max-width: 100%;box-sizing: border-box;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: right;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: middle;width: 229.594px;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;text-ali
作者:微信小助手
<p><strong><span style="font-size: 15px;">作者:唐辉</span></strong><br></p> <section data-tools="新媒体排版" data-id="2530744" data-style-type="undefined" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-style-type="1" data-id="10035" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;font-family: Arial;border-width: 0px;border-style: none;border-color: initial;color: rgb(0, 129, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;width: 677px;display: inline-block;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 12px;padding-right: 10px;padding-bottom: 4px;padding-left: 10px;max-width: 100%;width: 677px;float: left;font-size: 16px;border-bottom: 2.6px solid rgb(248, 92, 3);text-align: center;word-break: break-all;overflow: hidden;border-top-color: rgb(248, 92, 3);border-right-color: rgb(248, 92, 3);border-left-color: rgb(248, 92, 3);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-size: 20px;box-sizing: border-box !important;overflow-wrap: break-word !important;">1.文档编写目的</span> </section> <section style="margin-top: -20px;margin-left: 30px;max-width: 100%;width: 22px;height: 26px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin-top: -8px;margin-left: -10px;max-width: 100%;width: 16px;height: 20px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="max-width: 100%;clear: both;width: 0px;height: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> </section> </section> </section> </section> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">在《<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI4OTY3MTUyNg==&mid=2247503493&idx=1&sn=6efa9c1ac64ad99994f59c4649c436e6&chksm=ec291e8cdb5e979a656adaa1f1a45ecc1d98019e66fce6af968d5aafe8fe1d9f77569a01d140&scene=21#wechat_redirect" textvalue="如何安装及使用Prometheus" data-itemshowtype="0" tab="innerlink" data-linktype="2">如何安装及使用Prometheus</a>》文中有对Prometheus 做简单的介绍,并且通过node_exporter的模板示例介绍了如何监控主机信息。本文主要介绍如何使用Prometheus监控MySQL数据库信息 </span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">测试环境</span></p></li> </ul> <p><span style="font-size: 15px;">操作系统:Redhat 7.6</span></p> <p><span style="font-size: 15px;">服务器类型:x86</span></p> <p><span style="font-size: 15px;"><br></span></p> <section data-tools="新媒体排版" data-id="2530744" data-style-type="undefined" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-style-type="1" data-id="10035" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;font-family: Arial;border-width: 0px;border-style: none;border-color: initial;color: rgb(0, 129, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;width: 677px;display: inline-block;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 12px;padding-right: 10px;padding-bottom: 4px;padding-left: 10px;max-width: 100%;width: 677px;float: left;font-size: 16px;border-bottom: 2.6px solid rgb(248, 92, 3);text-align: center;word-break: break-all;overflow: hidden;border-top-color: rgb(248, 92, 3);border-right-color: rgb(248, 92, 3);border-left-color: rgb(248, 92, 3);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-size: 20px;box-sizing: border-box !important;overflow-wrap: break-word !important;">2.mysqld_exporter下载及配置</span> </section> <section style="margin-top: -20px;margin-left: 30px;max-width: 100%;width: 22px;height: 26px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin-top: -8px;margin-left: -10px;max-width: 100%;width: 16px;height: 20px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="max-width: 100%;clear: both;width: 0px;height: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> </section> </section> </section> </section> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p><span style="font-size: 15px;">1. mysqld_exporter安装包下载</span></p> <p><span style="font-size: 15px;">下载地址:https://prometheus.io/download/#mysqld_exporter</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.2634088200238379" data-s="300,640" src="/upload/aa2e1fb140e1da2770de824d06a69c3d.png" data-type="png" data-w="2517" style=""></p> <p><br></p> <p><span style="font-size: 15px;">可以离线下载后解压或者如下:</span></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;background: rgb(245, 247, 255);color: rgb(94, 102, 135);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(201, 73, 34);overflow-wrap: inherit !important;word-break: inherit !important;">wget</span> https://github.com/prometheus/mysqld_exporter/releases/download/v0.12.1/mysqld_exporter-0.12.1.linux-amd64.tar.gz <br><span style="font-size: inherit;line-height: inherit;color: rgb(107, 115, 148);overflow-wrap: inherit !important;word-break: inherit !important;">#解压</span><br>tar xvfz mysqld-<span style="font-size: inherit;line-height: inherit;color: rgb(201, 73, 34);overflow-wrap: inherit !important;word-break: inherit !important;">*.tar.gz</span> cd mysqld-*<br></code></pre> </section> <p><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.5855978260869565" data-s="300,640" src="/upload/58e5222d994ae9e75352028a10588393.png" data-type="png" data-w="2208" style=""></p> <p><br></p> <p><span style="font-size: 15px;">在MySQL中创建mysql_exporter用户并授权用于数据采集</span></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;background: rgb(245, 247, 255);color: rgb(94, 102, 135);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(201, 73, 34);overflow-wrap: inherit !important;word-break: inherit !important;">MariaDB</span> [(<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">none</span>)]> CREATE USER <span style="font-size: inherit;line-height: inherit;color: rgb(172, 151, 57);overflow-wrap: inherit !important;word-break: inherit !important;">'mysql_exporter'</span>@<span style="font-size: inherit;line-height: inherit;color: rgb(172, 151, 57);overflow-wrap: inherit !important;word-break: inherit !important;">'localhost'</span> IDENTIFIED BY <span style="font-size: inherit;line-height: inherit;color: rgb(172, 151, 57);overflow-wrap: inherit !important;word-break: inherit !important;">'password123'</span>;<br><span style="font-size: inherit;line-height: inherit;color: rgb(201, 73, 34);overflow-wrap: inherit !important;word-break: inherit !important;">Query</span> OK, <span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">0</span> rows affected (<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>.<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">11</span> sec)<br><br>MariaDB [(<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">none</span>)]> GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO <span style="font-size: inherit;line-height: inherit;color: rgb(172, 151, 57);overflow-wrap: inherit !important;word-break: inherit !important;">'mysql_exporter'</span>@<span style="font-size: inherit;line-height: inherit;color: rgb(172, 151, 57);overflow-wrap: inherit !important;word-break: inherit !important;">'localhost'</span>;<br></code></pre> </section> <p><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.2993239090350338" data-s="300,640" src="/upload/828a59ac3ff4793a342554bdc546c625.png" data-type="png" data-w="1627" style=""></p> <p><br></p> <p><span style="font-size: 15px;">2.编辑配置文件</span></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;background: rgb(245, 247, 255);color: rgb(94, 102, 135);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(61, 143, 209);overflow-wrap: inherit !important;word-break: inherit !important;">[client]</span><br><span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">user</span>=mysql_exporter<br><span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">password</span>=password123<br></code></pre> </section> <p><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.12034632034632034" data-s="300,640" src="/upload/70cc33654bdfe5522d3e608ae74862ad.png" data-type="png" data-w="1155" style=""></p> <p><br></p> <p><span style="font-size: 15px;">3.启动mysqld_exporter</span></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;background: rgb(245, 247, 255);color: rgb(94, 102, 135);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">./mysqld_exporter <span style="font-size: inherit;line-height: inherit;color: rgb(107, 115, 148);overflow-wrap: inherit !important;word-break: inherit !important;">--config.my-cnf mysql_exporter.cnf</span><br><span style="font-size: inherit;line-height: inherit;color: rgb(107, 115, 148);overflow-wrap: inherit !important;word-break: inherit !important;">#查看帮助 --help 可用于查看参数</span><br>./mysqld_exporter <span style="font-size: inherit;line-height: inherit;color: rgb(107, 115, 148);overflow-wrap: inherit !important;word-break: inherit !important;">--help</span><br></code></pre> </section> <p><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.1858246251703771" data-s="300,640" src="/upload/5aa8766216caacfa1ce62fd23ace20f7.png" data-type="png" data-w="2201" style=""></p> <p><br></p> <p><span style="font-size: 15px;">4.添加Prometheus 采集任务</span></p> <p><span style="font-size: 15px;">在prometheus.yml 添加如下配置并重启prometheus</span></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;background: rgb(245, 247, 255);color: rgb(94, 102, 135);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> - job_name: <span style="font-size: inherit;line-height: inherit;color: rgb(172, 151, 57);overflow-wrap: inherit !important;word-break: inherit !important;">'mysql'</span><br> static_configs:<br> - targets: [<span style="font-size: inherit;line-height: inherit;color: rgb(172, 151, 57);overflow-wrap: inherit !important;word-break: inherit !important;">'192.168.0.99:9104'</span>]<br></code></pre> </section> <p><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.4491471888818699" data-s="300,640" src="/upload/103b999ef9b23b488bf5c6665595bb07.png" data-type="png" data-w="1583" style=""></p> <p><br></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;background: rgb(245, 247, 255);color: rgb(94, 102, 135);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">nohup ./prometheus --config.file=prometheus.yml --web.listen-address=:<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">9200</span> > /var/<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">log</span>/prometheus.<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">log</span> <span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">2</span>>&<span style="font-size: inherit;line-height: inherit;color: rgb(199, 107, 41);overflow-wrap: inherit !important;word-break: inherit !important;">1</span> &<br></code></pre> </section> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.2453514739229025" data-s="300,640" src="/upload/5208d94c70820313875f0dd30faea69b.png" data-type="png" data-w="2205" style=""></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;">然后到Web UI查看监控状态,确认已成功监控</span></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;background: rgb(245, 247, 255);color: rgb(94, 102, 135);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">http:<span style="font-size: inherit;line-height: inherit;color: rgb(107, 115, 148);overflow-wrap: inherit !important;word-break: inherit !important;">//192.168.0.100:9200/targets</span><br></code></pre> </section> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.6659202866099417" data-s="300,640" src="/upload/99c99bcb21fe0b6d3ca304de1c1b432c.png" data-type="png" data-w="2233" style=""></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;"><br></span></p> <section data-tools="新媒体排版" data-id="2530744" data-style-type="undefined" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-style-type="1" data-id="10035" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;font-family: Arial;border-width: 0px;border-style: none;border-color: initial;color: rgb(0, 129, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;width: 677px;display: inline-block;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 12px;padding-right: 10px;padding-bottom: 4px;padding-left: 10px;max-width: 100%;width: 677px;float: left;font-size: 16px;border-bottom: 2.6px solid rgb(248, 92, 3);text-align: center;word-break: break-all;overflow: hidden;border-top-color: rgb(248, 92, 3);border-right-color: rgb(248, 92, 3);border-left-color: rgb(248, 92, 3);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-size: 20px;box-sizing: border-box !important;overflow-wrap: break-word !important;">3.集成Grafana展示</span> </section> <section style="margin-top: -20px;margin-left: 30px;max-width: 100%;width: 22px;height: 26px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin-top: -8px;margin-left: -10px;max-width: 100%;width: 16px;height: 20px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="max-width: 100%;clear: both;width: 0px;height: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> </section> </section> </section> </section> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p><span style="font-size: 15px;">关于如何下载安装Grafana,在之前的《<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI4OTY3MTUyNg==&mid=2247499961&idx=1&sn=8efbb2d0092b627448aafa8c0e0a3e08&chksm=ec2910b0db5e99a6d509c783fd13184b3587753c7c93da861dda8a25dca7a8a9268c992a1c4f&scene=21#wechat_redirect" textvalue="0707-如何安装Grafana并使用Cloudera Manager datasource插件" data-itemshowtype="0" tab="innerlink" data-linktype="2">0707-如何安装Grafana并使用Cloudera Manager datasource插件</a>》以及前面的《<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI4OTY3MTUyNg==&mid=2247503493&idx=1&sn=6efa9c1ac64ad99994f59c4649c436e6&chksm=ec291e8cdb5e979a656adaa1f1a45ecc1d98019e66fce6af968d5aafe8fe1d9f77569a01d140&scene=21#wechat_redirect" textvalue="如何安装及使用Prometheus" data-itemshowtype="0" tab="innerlink" data-linktype="2">如何安装及使用Prometheus</a>》有详细介绍。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">首先到Grafana 官网推荐的dashboard中下载一个展示样例模板来展示 MySQL Exporter的信息,下载地址https://grafana.com/grafana/dashboards</span></p> <p><span style="font-size: 15px;">本文举例使用https://grafana.com/grafana/dashboards/7362 MySQL Overview 模板用于展示</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.49534103615355946" data-s="300,640" src="/upload/632661e773df3f2bbafc5b90f00e3617.png" data-type="png" data-w="2683" style=""></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;"><br></span></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;">然后导入Grafana,如果有连接外网可以使用id 方式,离线可以通过下载JSON文件的方式导入,该模板的id 为7362</span><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.7258989056800417" data-s="300,640" src="/upload/78a4709816dba5bfdbf6ee34d556a5f0.png" data-type="png" data-w="1919" style=""></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;">导入后选择Prometheus数据源,然后执行导入</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.746031746031746" data-s="300,640" src="/upload/8a822f02b8f5175967b16b75b399d929.png" data-type="png" data-w="1953" style=""></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;">显示效果如下:</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.4506059493206023" data-s="300,640" src="/upload/7db2379bdffeb7f72bffceef10ffaba8.png" data-type="png" data-w="2723" style=""></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 15px;">举例说明指标信息:</span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="1.4265232974910393" data-s="300,640" src="/upload/dbb82e4e119e0e6f2c9653aa0280bf19.png" data-type="png" data-w="558" style=""></p> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, "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);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <section data-tools="新媒体排版" data-id="2530744" data-style-type="undefined" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-style-type="1" data-id="10035" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;font-family: Arial;border-width: 0px;border-style: none;border-color: initial;color: rgb(0, 129, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;width: 677px;display: inline-block;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 12px;padding-right: 10px;padding-bottom: 4px;padding-left: 10px;max-width: 100%;width: 677px;float: left;font-size: 16px;border-bottom: 2.6px solid rgb(248, 92, 3);text-align: center;word-break: break-all;overflow: hidden;border-top-color: rgb(248, 92, 3);border-right-color: rgb(248, 92, 3);border-left-color: rgb(248, 92, 3);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-size: 20px;box-sizing: border-box !important;overflow-wrap: break-word !important;">4.Prometheus简介</span> </section> <section style="margin-top: -20px;margin-left: 30px;max-width: 100%;width: 22px;height: 26px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="margin-top: -8px;margin-left: -10px;max-width: 100%;width: 16px;height: 20px;border-width: 2.6px;border-style: solid;border-radius: 20%;float: left;border-color: rgb(248, 92, 3);background-color: rgb(254, 254, 254);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="max-width: 100%;clear: both;width: 0px;height: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> </section> </section> </section> </section> <p><span style="font-size: 15px;">在CDH集群安装的过程中,大多数人用MySQL 来做集群的元数据库,但是在CM中却没有对元数据库有很好的监控信息,通过Prometheus监控MySQL 信息可以更方便的用于排查集群中的因元数据库导致的性能问题。</span></p> <p><span style="font-size: 15px;">相关链接:</span></p> <p><span style="font-size: 15px;">Prometheus官网:https://prometheus.io/</span></p> <p><span style="font-size: 15px;">Grafana 官网:https://grafana.com/</span></p>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;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;" data-mpa-powered-by="yiban.io"> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="155" data-source-title=""> <section class="js_blockquote_digest"> <section> <br> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <p style="clear: both;min-height: 1em;color: inherit;font-size: inherit;letter-spacing: 2px;word-spacing: 2px;line-height: inherit;text-align: right;"><img class="rich_pages js_insertlocalimg" data-cropselx1="0" data-cropselx2="578" data-cropsely1="0" data-cropsely2="215" data-ratio="0.36100746268656714" data-s="300,640" src="/upload/2a27b849b18b6f2dc9890c15129e9c7a.jpg" data-type="jpeg" data-w="2144" style="height: 209px;color: rgb(0, 82, 255);text-align: center;width: 578px;"><span style="color: rgb(0, 82, 255);">本文公众号来源:爱笑的架构师</span></p> <p style="clear: both;min-height: 1em;color: inherit;font-size: inherit;letter-spacing: 2px;word-spacing: 2px;line-height: inherit;text-align: right;"><span style="color: rgb(0, 82, 255);">作者:雷小帅</span></p> <p style="clear: both;min-height: 1em;color: inherit;font-size: inherit;letter-spacing: 2px;word-spacing: 2px;line-height: inherit;text-align: right;"><span style="color: rgb(0, 82, 255);">本文已收录至我的</span><strong mpa-from-tpl="t" style="color: rgb(51, 51, 51);font-size: 17px;"><span style="font-size: 14px;color: rgb(217, 33, 66);">GitHub</span></strong></p> </section> </section> <section> 头发很多的程序员:『师父,这个批量处理接口太慢了,有什么办法可以优化?』 <br> <br>架构师:『试试使用多线程优化』 <br> <br>第二天 <br> <br>头发很多的程序员:『师父,我已经使用了多线程,为什么接口还变慢了?』 <br> <br>架构师:『去给我买杯咖啡,我写篇文章告诉你』 <br> <br>……吭哧吭哧买咖啡去了 </section> </section> </blockquote> <p><br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">在实际工作中,错误使用多线程非但不能提高效率还可能使程序崩溃。以在路上开车为例:<br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">在一个单向行驶的道路上,每辆汽车都遵守交通规则,这时候整体通行是正常的。『单向车道』意味着『一个线程』,『多辆车』意味着『多个job任务』。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.26996805111821087" src="/upload/e6ddf176cbd0e180d6f911a98a7244d4.png" data-type="png" data-w="1252" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 单线程顺利同行 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">如果需要提升车辆的同行效率,一般的做法就是扩展车道,对应程序来说就是『加线程池』,增加线程数。这样在同一时间内,通行的车辆数远远大于单车道。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.46166394779771613" src="/upload/8812537cb4bd57f54d526ce6d851447e.png" data-type="png" data-w="1226" style="display: block;margin-right: auto;margin-left: auto;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 多线程顺利同行 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">然而成年人的世界没有那么完美,车道一旦多起来『加塞』的场景就会越来越多,出现碰撞后也会影响整条马路的通行效率。这么一对比下来『多车道』确实可能比『单车道』要慢。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.4578512396694215" src="/upload/c445d7dab4572bf262de79fe310e1176.png" data-type="png" data-w="1210" style="display: block;margin-right: auto;margin-left: auto;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 多线程故障 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">防止汽车频繁变道加塞可以采取在车道间增加『护栏』,那在程序的世界该怎么做呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">程序世界中多线程遇到的问题归纳起来就是三类:<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(0, 0, 153);">『线程安全问题』</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(0, 0, 153);">『活跃性问题』</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(0, 0, 153);">『性能问题』</code>,接下来会讲解这些问题,以及问题对应的解决手段。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1em;color: rgb(0, 0, 153);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 0, 153);"><span style="display: none;"></span>线程安全问题</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">有时候我们会发现,明明在单线程环境中正常运行的代码,在多线程环境中可能会出现意料之外的结果,其实这就是大家常说的『线程不安全』。那到底什么是线程不安全呢?往下看。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;"><strong>原子性</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">举一个银行转账的例子,比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元,两个操作都成功才意味着一次转账最终成功。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.5105348460291734" src="/upload/9c6133de3f9223c89097b5cbdde68fca.png" data-type="png" data-w="1234" style="display: block;margin-right: auto;margin-left: auto;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">试想一下,如果这两个操作不具备原子性,从A的账户扣减了1000元之后,操作突然终止了,账户B没有增加1000元,那问题就大了。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.48327759197324416" src="/upload/8ad635f8814c9220ac2350e74e35e2bc.png" data-type="png" data-w="1196" style="display: block;margin-right: auto;margin-left: auto;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">银行转账这个例子有两个步骤,出现了意外后导致转账失败,说明没有原子性。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;border-right: 2px solid rgb(136, 136, 136);font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"> <p style="padding-top: 8px;padding-bottom: 8px;text-align: justify;font-size: 15px;color: black;line-height: 26px;">原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。</p> <p style="padding-top: 8px;padding-bottom: 8px;text-align: justify;font-size: 15px;color: black;line-height: 26px;">原子操作:即不会被线程调度机制打断的操作,没有上下文切换。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">在并发编程中很多操作都不是原子操作,出个小题目:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;background: #282c34;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">i = <span style="color: #d19a66;line-height: 26px;">0</span>; <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 操作1</span><br>i++; <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 操作2</span><br>i = j; <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 操作3</span><br>i = i + <span style="color: #d19a66;line-height: 26px;">1</span>; <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 操作4</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">上面这四个操作中有哪些是原子操作,哪些不是的?不熟悉的人可能认为这些都是原子操作,其实只有操作1是原子操作。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 操作1:对基本数据类型变量的赋值是原子操作; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 操作2:包含三个操作,读取i的值,将i加1,将值赋给i; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 操作3:读取j的值,将j的值赋给i; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 操作4:包含三个操作,读取i的值,将i加1,将值赋给i; </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">在单线程环境下上述四个操作都不会出现问题,但是在多线程环境下,如果不通过加锁操作,往往可能得到意料之外的值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">在Java语言中通过可以使用synchronize或者lock来保证原子性。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;"><strong>可见性</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">talk is cheap,先show一段代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;background: #282c34;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">Test</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">int</span> i = <span style="color: #d19a66;line-height: 26px;">50</span>;<br> <span style="color: #c678dd;line-height: 26px;">int</span> j = <span style="color: #d19a66;line-height: 26px;">0</span>;<br> <br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">void</span> <span style="color: #61aeee;line-height: 26px;">update</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 线程1执行</span><br> i = <span style="color: #d19a66;line-height: 26px;">100</span>;<br> }<br> <br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">int</span> <span style="color: #61aeee;line-height: 26px;">get</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 线程2执行</span><br> j = i;<br> <span style="color: #c678dd;line-height: 26px;">return</span> j;<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">线程1执行update方法将 i 赋值为100,一般情况下线程1会在自己的工作内存中完成赋值操作,却没有及时将新值刷新到主内存中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">这个时候线程2执行get方法,首先会从主内存中读取i的值,然后加载到自己的工作内存中,这个时候读取到i的值是50,再将50赋值给j,最后返回j的值就是50了。原本期望返回100,结果返回50,这就是可见性问题,线程1对变量i进行了修改,线程2没有立即看到i的新值。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;border-right: 2px solid rgb(136, 136, 136);font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"> <p style="padding-top: 8px;padding-bottom: 8px;text-align: justify;font-size: 15px;color: black;line-height: 26px;">可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。</p> </blockquote> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="1.1482412060301508" src="/upload/e4e31944896a9ecab09e29e8a4ca3bd5.png" data-type="png" data-w="796" style="display: block;margin-right: auto;margin-left: auto;width: 77%;height: auto !important;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">如上图每个线程都有属于自己的工作内存,工作内存和主内存间需要通过store和load等进行交互。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">为了解决多线程可见性问题,Java语言提供了<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(0, 0, 153);">volatile</code>这个关键字。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通共享变量不能保证可见性,因为变量被修改后什么时候刷回到主存是不确定的,另外一个线程读的可能就是旧值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">当然Java的锁机制如synchronize和lock也是可以保证可见性的,加锁可以保证在同一时刻只有一个线程在执行同步代码块,释放锁之前会将变量刷回至主存,这样也就保证了可见性。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">关于线程不安全的表现还有『有序性』,这个问题会在后面的文章中深入讲解。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1em;color: rgb(0, 0, 153);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 0, 153);"><span style="display: none;"></span>活跃性问题</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">上面讲到为了解决<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(0, 0, 153);">可见性</code>问题,我们可以采取加锁方式解决,但是如果加锁使用不当也容易引入其他问题,比如『死锁』。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">在说『死锁』前我们先引入另外一个概念:<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(0, 0, 153);">活跃性问题</code>。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;border-right: 2px solid rgb(136, 136, 136);font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"> <p style="padding-top: 8px;padding-bottom: 8px;text-align: justify;font-size: 15px;color: black;line-height: 26px;">活跃性是指某件正确的事情最终会发生,当某个操作无法继续下去的时候,就会发生活跃性问题。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">概念是不是有点拗口,如果看不懂也没关系,你可以记住活跃性问题一般有这样几类:<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(0, 0, 153);">死锁</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(0, 0, 153);">活锁</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(0, 0, 153);">饥饿问题</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;"><strong>(1)死锁</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">死锁是指多个线程因为环形的等待锁的关系而永远的阻塞下去。一图胜千语,不多解释。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.5855263157894737" src="/upload/eeae82798eaac752a3832a31f5c2b53e.png" data-type="png" data-w="912" style="display: block;margin-right: auto;margin-left: auto;width: 99%;height: auto !important;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;"><strong>(2)活锁</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">死锁是两个线程都在等待对方释放锁导致阻塞。而<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(0, 0, 153);">活锁</code>的意思是线程没有阻塞,还活着呢。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">当多个线程都在运行并且修改各自的状态,而其他线程彼此依赖这个状态,导致任何一个线程都无法继续执行,只能重复着自身的动作和修改自身的状态,这种场景就是发生了活锁。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">如果大家还有疑惑,那我再举一个生活中的例子,大家平时在走路的时候,迎面走来一个人,两个人互相让路,但是又同时走到了一个方向,如果一直这样重复着避让,这俩人就是发生了活锁,学到了吧,嘿嘿。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;"><strong>(3)饥饿</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">如果一个线程无其他异常却迟迟不能继续运行,那基本是处于饥饿状态了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">常见有几种场景:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 高优先级的线程一直在运行消耗CPU,所有的低优先级线程一直处于等待; </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-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">有一个非常经典的饥饿问题就是<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(0, 0, 153);">哲学家用餐问题</code>,如下图所示,有五个哲学家在用餐,每个人必须要同时拿两把叉子才可以开始就餐,如果哲学家1和哲学家3同时开始就餐,那哲学家2、4、5就得饿肚子等待了。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.9495967741935484" src="/upload/1a9e177de86d984ce02f86d09af5645a.png" data-type="png" data-w="992" style="display: block;margin-right: auto;margin-left: auto;width: 84%;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;height: auto !important;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1em;color: rgb(0, 0, 153);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 0, 153);"><span style="display: none;"></span>性能问题</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">前面讲到了线程安全和死锁、活锁这些问题会影响多线程执行过程,如果这些都没有发生,多线程并发一定比单线程串行执行快吗,答案是不一定,因为多线程有<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(0, 0, 153);">创建线程</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(0, 0, 153);">线程上下文切换</code>的开销。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">创建线程是直接向系统申请资源的,对操作系统来说创建一个线程的代价是十分昂贵的,需要给它分配内存、列入调度等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">线程创建完之后,还会遇到线程<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(0, 0, 153);">上下文切换</code>。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.37219101123595505" src="/upload/f36d7380fdf98228a7355067dbaf7e5d.png" data-type="png" data-w="1424" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;border-radius: 6px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">CPU是很宝贵的资源速度也非常快,为了保证雨露均沾,通常为给不同的线程分配<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(0, 0, 153);">时间片</code>,当CPU从执行一个线程切换到执行另一个线程时,CPU 需要保存当前线程的本地数据,程序指针等状态,并加载下一个要执行的线程的本地数据,程序指针等,这个开关被称为『上下文切换』。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">一般减少上下文切换的方法有:<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(0, 0, 153);">无锁并发编程</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(0, 0, 153);">CAS 算法</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(0, 0, 153);">使用协程</code>等。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 1em;color: rgb(0, 0, 153);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 0, 153);"><span style="display: none;"></span>有态度的总结</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">多线程用好了可以让程序的效率成倍提升,用不好可能比单线程还要慢。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">用一张图总结一下上面讲的:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.47391304347826085" src="/upload/f9da7f774e655298477cc727ece42fb4.png" data-type="png" data-w="1380" style="display: block;margin-right: auto;margin-left: auto;border-radius: 6px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <br> </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: center;font-size: 15px;">-- END --</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">虽然讲了多线程并发会遇到的问题,你可能也发现了,文章中并没有给出具体的解决方案,因为这些问题在Java语言设计过程中大神都已经为你考虑过了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;font-size: 15px;">Java并发编程学起来有一定难度,但这也是从<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(0, 0, 153);">初级程序员</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(0, 0, 153);">中高级程序员</code>的必经道路。</p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzU4NzA3MTc5Mg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/E44aHibktsKbNnU5S5B6m5yVtNbmfEUsMmf4eqOBEsXzMILa9eAZvBR4mntYwkkp59nicicN1soBWsduGrxLFVzWQ/0?wx_fmt=png" data-nickname="面试造火箭" data-alias="" data-signature="入职拧螺丝"></mpprofile> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;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;"> <p data-tool="mdnice编辑器" style="padding-top: 23px;padding-bottom: 8px;letter-spacing: 0.544px;white-space: normal;font-size: 16px;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;background-color: rgb(255, 255, 255);color: rgb(74, 74, 74);line-height: 1.75em;">《对线面试官》系列目前已经连载<strong style="line-height: 1.75em;">19</strong>篇啦!进度是<strong style="line-height: 1.75em;">一周更新两篇</strong>,欢迎持续关注<br></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;letter-spacing: 0.544px;white-space: normal;font-size: 16px;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;background-color: rgb(255, 255, 255);color: black;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247483821&idx=1&sn=e9003410a8d3c8a092de0c4d2002bedd&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】Java注解</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247483823&idx=1&sn=cc887dc2c7e68a69e8d4d141c2ca9b5e&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】Java泛型</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247483854&idx=1&sn=aa450a03ac0d6e8cf12cf13d4719ede3&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】 Java NIO</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247483893&idx=1&sn=af51e626f2c2baec8cae4f4a15425957&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】Java反射 && 动态代理</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247483918&idx=1&sn=ab8550bb284edcf7cf0c6d0b41e0c2f6&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】多线程基础</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247483977&idx=1&sn=1a3aa3aec27073aa3b422bc41d7fbe2d&chksm=fdf0ea16ca8763005aff64834eeb7bef08bf4ee2d8febb7e8d4d8e5d1542336e13fac71e2881&scene=21&cur_album_id=1657204970858872832#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】 CAS</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247483980&idx=1&sn=c9b620834adb889ad8ccedb6afdcaed1&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】synchronized</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484035&idx=1&sn=ccaec352e192f1fd40020d9a984e9461&chksm=fdf0eadcca8763ca5c44bd19118fd00e843c163deb40cda444b3fc08430c57760db15eca1ea6&scene=21&cur_album_id=1657204970858872832#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】AQS&&ReentrantLock</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484036&idx=1&sn=75e9e93a82a811e9c71b8127cf7ac677&chksm=fdf0eadbca8763cd7ab74757f9472d061c0244d2373a1ea85b1cbc833941441fdb1e91ead5b4&scene=21&cur_album_id=1657204970858872832#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】线程池</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484118&idx=1&sn=9526a1dc0d42926dd9bcccfc55e6abc2&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】ThreadLocal</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484363&idx=1&sn=743dcdfb84f83cfc38882407f87c7c6d&chksm=fdf0eb94ca87628296d86d16769f25e10acd052bcd78f4a4608f4218e4948aff610b04a41f60&token=960279204&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】CountDownLatch和CyclicBarrier</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484253&idx=1&sn=532db3941f47502582295cbb003f753d&chksm=fdf0eb02ca8762145c66b33bbb429399f1f0f27b31c22f7cf6c693c235e9a7cffdafb6ce2fdc&token=57394744&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】List</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484280&idx=1&sn=87cfede653dabc26c909823a1dafd615&chksm=fdf0eb27ca876231095ff99f0b3e30acd7b2ee4cdc7ddb16da0bb6a3b02f531e27324059cf58&token=100834666&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】Map</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484064&idx=1&sn=3a59514a8262ab61036fc89cf0b0a27e&chksm=fdf0eaffca8763e90002ce1daf365f717a4bda3e50878f65943f52d14bee78fc65e837ef32f9&token=664255414&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】SpringMVC</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484147&idx=1&sn=ef282cd54351436fc33c47534b4c2ac1&chksm=fdf0eaacca8763ba9b6c69acdba6b0ae8801405c98295842a0b5d891fe80246d76a2a0470bea&token=1998524575&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】Spring基础</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484187&idx=1&sn=8f831c40dca9b2a57fdfbd051e4eab44&chksm=fdf0eb44ca87625253ea831471110860d3f27e04488b2748ba90ad442b079aca3d6b95d31bbe&token=1998524575&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】SpringBean生命周期</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484227&idx=1&sn=4a124a2dd5ef6ce062abdadf247b5cff&chksm=fdf0eb1cca87620a8679473dfdd50421eb6ccba2459a7cb59ae1652138f7bb508558f3d4649e&token=57394744&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】Redis基础</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzU4NzA3MTc5Mg==&mid=2247484323&idx=1&sn=c3306b3f9abb6f880e2672f169202a42&chksm=fdf0ebfcca8762eaf9b4873e79cd3445857b1f4476a854acdf9c19fb81e1a02146c65cff5078&token=610975656&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(60, 112, 198);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-weight: bold;border-bottom: 1px solid rgb(60, 112, 198);">【对线面试官】Redis持久化</a> </section></li> </ul> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 5px;padding-left: 5px;white-space: normal;color: rgb(43, 43, 43);font-size: 16px;text-align: left;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.5444px;background-color: rgb(255, 255, 255);line-height: 1.6;word-break: break-word;"> <br> </section> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;letter-spacing: 0.544px;white-space: normal;text-align: left;background-color: rgb(255, 255, 255);caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3302387267904509" data-type="jpeg" data-w="1508" src="/upload/e05acdfb7762b54ec9b09e2738219d94.jpg" style="margin-top: 10px;margin-right: auto;margin-left: auto;display: block;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;box-sizing: border-box !important;visibility: visible !important;width: 677px !important;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;letter-spacing: 0.544px;white-space: normal;text-align: left;background-color: rgb(255, 255, 255);caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <strong style="text-align: center;font-size: 16px;letter-spacing: 0px;"><span style="color: rgb(255, 104, 39);"><br>怎样偷偷努力 惊艳所有人?</span></strong> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzU4NzA3MTc5Mg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/E44aHibktsKbNnU5S5B6m5yVtNbmfEUsMmf4eqOBEsXzMILa9eAZvBR4mntYwkkp59nicicN1soBWsduGrxLFVzWQ/0?wx_fmt=png" data-nickname="面试造火箭" data-alias="" data-signature="入职拧螺丝"></mpprofile> </section> <strong style="text-align: center;font-size: 16px;letter-spacing: 0px;"><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;">点击小卡片关注【</span><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;color: rgb(255, 104, 39);">面试造火箭</span><span style="letter-spacing: 0px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">】</span><br><span style="color: rgb(255, 104, 39);"></span></strong> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;letter-spacing: 0.544px;white-space: normal;text-align: left;background-color: rgb(255, 255, 255);caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <strong style="text-align: center;font-size: 16px;letter-spacing: 0px;"><span style="letter-spacing: 0px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">关注后回复「</span><strong style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;"><span style="letter-spacing: 0px;color: rgb(255, 104, 39);">888</span></strong><span style="letter-spacing: 0px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">」还可获取网盘地址哟!</span></strong> </figure> </section> <section style="text-align: center;"> <span style="font-size: 14px;color: rgb(255, 0, 0);font-family: "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei", 黑体, Arial, sans-serif;text-align: center;background-color: rgb(255, 255, 255);"></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;overflow-wrap: break-word;text-align: left;font-family: Helvetica;"> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="font-size: 20px;color: #ab1942;">什么是热更新</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">模块热替换(<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">hot module replacement 或 HMR</code>)是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">webpack</code> 提供的最有用的功能之一。它允许在运行时更新所有类型的模块,而无需完全刷新</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">一般的刷新我们分两种:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 一种是页面刷新,不保留页面状态,就是简单粗暴,直接 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">window.location.reload()</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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">WDS (Webpack-dev-server)</code> 的模块热替换,只需要局部刷新页面上发生变化的模块,同时可以保留当前的页面状态,比如复选框的选中状态、输入框的输入等。 </section></li> </ul> <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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR</code> 作为一个 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Webpack</code> 内置的功能,可以通过 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HotModuleReplacementPlugin</code> 或 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">--hot</code> 开启。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">具体我们如何在 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">webpack</code> 中使用这个功能呢?</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span style="font-size: 20px;color: #ab1942;">热更新的使用以及简单分析</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 18px;color: #3da742;">如何使用热更新</span><span style="display: none;"></span></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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">npm install webpack webpack-dev-server --save-dev<br><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">设置 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HotModuleReplacementPlugin</code>,<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HotModuleReplacementPlugin</code> 是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">webpack</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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">plugins: {<br> <span style="color: #d19a66;line-height: 26px;">HotModuleReplacementPlugin</span>: <span style="color: #c678dd;line-height: 26px;">new</span> webpack.HotModuleReplacementPlugin()<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">再设置一下 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">devServer</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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">devServer: {<br> <span style="color: #d19a66;line-height: 26px;">contentBase</span>: path.resolve(__dirname, <span style="color: #98c379;line-height: 26px;">'dist'</span>),<br> <span style="color: #d19a66;line-height: 26px;">hot</span>: <span style="color: #56b6c2;line-height: 26px;">true</span>, <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 重点关注</span><br> <span style="color: #d19a66;line-height: 26px;">historyApiFallback</span>: <span style="color: #56b6c2;line-height: 26px;">true</span>,<br> <span style="color: #d19a66;line-height: 26px;">compress</span>: <span style="color: #56b6c2;line-height: 26px;">true</span><br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">hot</code> 为 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">true</code>,代表开启热更新 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 18px;color: #3da742;">两个重要的文件</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当我们改变我们项目的文件的时候,比如我修改 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Vue</code> 的一个 方法:</p> <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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">clickMe() {<br> <span style="color: #e6c07b;line-height: 26px;">console</span>.log(<span style="color: #98c379;line-height: 26px;">'我是 Gopal,欢迎关注「前端杂货铺」'</span>);<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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">clickMe() {<br> <span style="color: #e6c07b;line-height: 26px;">console</span>.log(<span style="color: #98c379;line-height: 26px;">'我是 Gopal,欢迎关注「前端杂货铺」,一起学习成长吧'</span>);<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 data-ratio="0.37037037037037035" src="/upload/afc729f4a57cc92a3fff7053abdb579d.png" data-type="png" data-w="1026" 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> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">JSON</code> 文件, <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">h</code> 代表本次新生成的 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Hash</code> 值为 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">0c256052432b51ed32c8</code>——本次输出的 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Hash</code> 值会被作为下次热更新的标识。 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">c</code> 表示当前要热更新的文件对应的是哪个模块,可以让 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">webpack</code> 知道它要更新哪个模块 </section></li> </ul> <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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">{<br> <span style="color: #98c379;line-height: 26px;">"h"</span>: <span style="color: #98c379;line-height: 26px;">"0c256052432b51ed32c8"</span>,<br> <span style="color: #98c379;line-height: 26px;">"c"</span>: {<br> <span style="color: #98c379;line-height: 26px;">"201"</span>: <span style="color: #56b6c2;line-height: 26px;">true</span><br> }<br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">js</code> 文件,就是本次修改的代码,重新编译打包后的,大致是下面这个样子(已删减一些并格式化过,这里看不懂没关系的,就记住是返回要更新的模块就好了), <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">webpackHotUpdate</code> 方法就是用来更新模块的, <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">201</code> 对应的是哪个模块(我们称它为模块标识),其他的就是要更新的模块的内容了 </section></li> </ul> <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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">webpackHotUpdate(<span style="color: #d19a66;line-height: 26px;">201</span>, {<br> <span style="color: #98c379;line-height: 26px;">"./src/views/moveTransfer/list/index.vue?vue&type=script&lang=js&"</span>: <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">function</span> (<span style="line-height: 26px;"><br> module,<br> exports,<br> __webpack_require__<br> </span>) </span>{<br> <span style="color: #98c379;line-height: 26px;">"use strict"</span>;<br><br> <span style="color: #c678dd;line-height: 26px;">var</span> _Object$defineProperty = __webpack_require__(<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">/*! @babel/runtime-corejs3/core-js-stable/object/define-property */</span> <span style="color: #98c379;line-height: 26px;">"./node_modules/@babel/runtime-corejs3/core-js-stable/object/define-property.js"</span><br> );<br><br> _Object$defineProperty(exports, <span style="color: #98c379;line-height: 26px;">"__esModule"</span>, {<br> <span style="color: #d19a66;line-height: 26px;">value</span>: <span style="color: #56b6c2;line-height: 26px;">true</span>,<br> });<br><br> exports.default = <span style="color: #c678dd;line-height: 26px;">void</span> <span style="color: #d19a66;line-height: 26px;">0</span>;<br><br> <span style="color: #c678dd;line-height: 26px;">var</span> _default = {<br> <span style="color: #d19a66;line-height: 26px;">data</span>: <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">function</span> <span style="color: #61aeee;line-height: 26px;">data</span>(<span style="line-height: 26px;"></span>) </span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> {};<br> },<br> <span style="color: #d19a66;line-height: 26px;">computed</span>: {},<br> <span style="color: #d19a66;line-height: 26px;">methods</span>: {<br> <span style="color: #d19a66;line-height: 26px;">clickMe</span>: <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">function</span> <span style="color: #61aeee;line-height: 26px;">clickMe</span>(<span style="line-height: 26px;"></span>) </span>{<br> <span style="color: #e6c07b;line-height: 26px;">console</span>.log(<span style="color: #98c379;line-height: 26px;">"我是 Gopal,欢迎关注「前端杂货铺」,一起学习成长吧"</span>);<br> },<br> },<br> };<br> exports.default = _default;<br> },<br>});<br><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">那么问题来了,<strong>我修改了文件,浏览器是怎么知道要更新的呢?</strong></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span style="font-size: 20px;color: #ab1942;">了解一下 Websocket</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">热更新使用到了 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Websocket</code>,这里不会细讲 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Websocket</code>,可以看下阮一峰老师的 WebSocket 教程,下面是一个 简单的例子</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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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: #5c6370;font-style: italic;line-height: 26px;">// 执行上面语句之后,客户端就会与服务器进行连接。</span><br><span style="color: #c678dd;line-height: 26px;">var</span> ws = <span style="color: #c678dd;line-height: 26px;">new</span> WebSocket(<span style="color: #98c379;line-height: 26px;">"wss://echo.websocket.org"</span>);<br><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">// 实例对象的 onopen 属性,用于指定连接成功后的回调函数</span><br>ws.onopen = <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">function</span>(<span style="line-height: 26px;">evt</span>) </span>{ <br> <span style="color: #e6c07b;line-height: 26px;">console</span>.log(<span style="color: #98c379;line-height: 26px;">"Connection open ..."</span>); <br> ws.send(<span style="color: #98c379;line-height: 26px;">"Hello WebSockets!"</span>);<br>};<br><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">// 实例对象的 onmessage 属性,用于指定收到服务器数据后的回调函数。可以接受二进制数据,blob 对象或者 Arraybuffer 对象</span><br>ws.onmessage = <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">function</span>(<span style="line-height: 26px;">evt</span>) </span>{<br> <span style="color: #e6c07b;line-height: 26px;">console</span>.log( <span style="color: #98c379;line-height: 26px;">"Received Message: "</span> + evt.data);<br> ws.close();<br>};<br><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">// 实例对象的 onclose 属性,用于指定连接关闭后的回调函数。</span><br>ws.onclose = <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">function</span>(<span style="line-height: 26px;">evt</span>) </span>{<br> <span style="color: #e6c07b;line-height: 26px;">console</span>.log(<span style="color: #98c379;line-height: 26px;">"Connection closed."</span>);<br>}; <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上面通过 new Websocket 创建一个客户端与服务端通信的实例,并通过 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">onmessage</code>属性,接受指定服务器返回的数据,并进行相应的处理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这里大概解释下,为什么是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Websocket</code> ?因为 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Websocket</code> 是一种双向协议,它最大的特点就是 <strong>服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送信息</strong>。这是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HTTP</code> 不具备的,<strong>热更新实际上就是服务器端的更新通知到客户端,所以选择了 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Websocket</code></strong></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;font-size: 22px;"><span style="display: none;"></span><span style="font-size: 20px;color: #ab1942;">热更新原理</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 18px;color: #3da742;">热更新的过程</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">几个重要的概念(这里有一个大致的概念就好,后面会把它们串起来):</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Webpack-complier</code> : <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">webpack</code> 的编译器,将 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">JavaScript</code> 编译成 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">bundle</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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Server</code>:将热更新的文件输出给 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Runtime</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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Bunble Server</code>:提供文件在浏览器的访问,也就是我们平时能够正常通过 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">localhost</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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Runtime</code>:开启了热更新的话,在打包阶段会被注入到浏览器中的 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">bundle.js</code>,这样 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">bundle.js</code> 就可以跟服务器建立连接,通常是使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">websocket</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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">bundle.js</code>:构建输出的文件 </section></li> </ul> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span><span style="font-size: 16px;">启动阶段</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">文件经过 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Webpack-complier</code> 编译好后传输给 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Bundle Server</code>,<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Bundle Server</code> 可以让浏览器访问到我们打包出来的文件</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">下面流程图中的 1、2、A、B阶段</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span><span style="font-size: 16px;">文件热更新阶段</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">文件经过 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Webpack-complier</code> 编译好后传输给 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Server</code>,<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Server</code> 知道哪个资源(模块)发生了改变,并通知 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Runtime</code> 有哪些变化(也就是上面我们看到的两个请求),<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Runtime</code> 就会更新我们的代码,这样我们浏览器就会更新并且不需要刷新</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">下面流程图的 1、2、3、4、5 阶段</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">参考 19 | webpack中的热更新及原理分析</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.46112115732368897" src="/upload/74a2f4d79cb5e0fc06b374b53c8868b0.png" data-type="png" data-w="1106" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span style="font-size: 20px;color: #ab1942;">深入——源码阅读</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们还看回上图,其中启动阶段图中的 1、2、A、B阶段就不讲解了,主要看热更新阶段主要讲 3、4 和 5 阶段</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在开始接下开的阅读前,我们再回到最初的问题上<strong>我本地修改了文件,浏览器是怎么知道要更新的呢?</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通过上面的流程图,其实我们可以猜测,<strong>本地实际上启动了一个 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Server</code> 服务,而且在启动 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Bundle Server</code> 的时候已经往我们的 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">bundle.js</code> 中注入了 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Runtime</code>(主要用来启动 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Websocket</code>,接受 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Server</code> 发来的变更)</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">所以我们聚焦以下几点:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Webpack</code> 如何启动了 HMR Server </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> HMR Server 如何跟 HMR Runtime 进行通信的 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">HMR Runtime</code> 接受到变更之后,如何生效的 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">以下的源码解析分别对应的版本是:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> webpack—— <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">5.24.3</code> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> webpack-dev-server—— <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">4.0.0-beta.0</code> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> webpack-dev-middleware—— <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">4.1.0</code> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 18px;color: #3da742;">启动 HMR Server</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这个工作主要是在 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">webpack-dev-server</code> 中完成的</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">看 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">lib/Server.js</code> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">setupApp</code> 方法,下面的 express 服务实际上对应的是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Bundle Server</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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">setupApp() {<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// Init express server</span><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// eslint-disable-next-line new-cap</span><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 初始化 express 服务</span><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 使用 express 框架启动本地 server,让浏览器可以请求本地的静态资源。</span><br> <span style="color: #c678dd;line-height: 26px;">this</span>.app = <span style="color: #c678dd;line-height: 26px;">new</span> express();<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">启动服务结束之后就通过 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">createSocketServer</code> 创建 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">websocket</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/3Lqm1xHojtYueY5PvxQMHztbDfzQTXdzRQf8PC7cqfqHTex8Rabyowe8Y3ER1mx4VLdm4W44zMAB3ZHdHvSOupxYdkm7gZibp/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;">listen(port, hostname, fn) {<br> <span style="color: #c678dd;line-height: 26px;">this</span>.hostname = hostname;<br> <span style="color: #c678dd;line-height: 26px;">return</span> (<br> findPort(port || <span style="color: #c678dd;line-height: 26px;">this</span>.options.port)<br> .then(<span style="line-height: 26px;">(<span style="line-height: 26px;">port</span>) =></span> {<br> <span style="color: #c678dd;line-height: 26px;">this</span>.port = port;<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #c678dd;line-height: 26px;">this</span>.server.listen(port, hostname, (err) => {<br> <span style="color: #c678dd;line-height: 26px;">if</span> (<span style="color: #c678dd;line-height: 26px;">this</span>.options.hot || <span style="color: #c678dd;line-height: 26px;">this</span>.options.liveReload) {<br>  
作者:微信小助手
<p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);" data-mpa-powered-by="yiban.io">说起注册中心,我们首先要知道注册中心是用来做什么的,注册中心一般都是用在微服务架构中,而微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信通常采用HTTP的方式,这些服务共用一个最小型的集中式的管理。这个最小型的集中式管理的组件就是服务注册中心。</p> <h2 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.6em;text-rendering: optimizelegibility;font-size: 18px;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">一、nacos 简介</h2> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">本文的目的在于详解 nacos 注册中心的服务注册流程,所以首先需要对 nacos 有个基本的了解。nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。</p> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">nacos 基本架构</h4> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);"><img data-ratio="0.45454545454545453" src="/upload/1448f5bdbaad3aa79c1c32451222011a.jpg" data-type="jpeg" data-w="550" style="max-width: 96%;vertical-align: middle;border-width: 0px;border-style: none;border-color: initial;margin-right: auto;margin-bottom: 10px;margin-left: auto;text-align: center;display: block;"></p> <h2 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.6em;text-rendering: optimizelegibility;font-size: 18px;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">二、nacos 注册中心</h2> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">nacos 服务注册中心,它是服务,其实例及元数据的数据库。服务实例在启动时注册到服务注册表,并在关闭时注销。服务和路由器的客户端查询服务注册表以查找服务的可用实例。服务注册中心可能会调用服务实例的健康检查 API 来验证它是否能够处理请求。</p> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">nacos 服务注册表结构:Map<namespace, Map<group::serviceName, Service>></h4> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);"><img data-ratio="0.62" src="/upload/ad6ce25e6da659419a5e7e1a7bdc7900.png" data-type="png" data-w="550" style="max-width: 96%;vertical-align: middle;border-width: 0px;border-style: none;border-color: initial;margin-right: auto;margin-bottom: 10px;margin-left: auto;text-align: center;display: block;"></p> <h2 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.6em;text-rendering: optimizelegibility;font-size: 18px;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">三、demo 搭建</h2> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">了解了 nacos 的基本架构和服务注册中心的功能之后,接下来就要来详解服务注册的流程原理了,首先建立一个 nacos 客户端工程,springboot 版本选择2.1.0,springcloud 版本选择 Greenwich</p> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><<span style="color: rgb(51, 51, 51);font-weight: 700;">spring-boot-version</span>>2.1.0.RELEASE</<span style="color: rgb(51, 51, 51);font-weight: 700;">spring-boot-version</span>><br><<span style="color: rgb(51, 51, 51);font-weight: 700;">spring-cloud.version</span>>Greenwich.RELEASE</<span style="color: rgb(51, 51, 51);font-weight: 700;">spring-cloud.version</span>><br>复制代码</pre> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">随后引入 springcloud,springboot 和 springcloud-alibaba 的依赖</p> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><<span style="color: rgb(51, 51, 51);font-weight: 700;">dependencyManagement</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">dependencies</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">dependency</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">groupId</span>>org.springframework.cloud</<span style="color: rgb(51, 51, 51);font-weight: 700;">groupId</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">artifactId</span>>spring-cloud-dependencies</<span style="color: rgb(51, 51, 51);font-weight: 700;">artifactId</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">version</span>>$<span style="color: rgb(188, 96, 96);">{spring-cloud.version}</span></<span style="color: rgb(51, 51, 51);font-weight: 700;">version</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">type</span>>pom</<span style="color: rgb(51, 51, 51);font-weight: 700;">type</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">scope</span>>import</<span style="color: rgb(51, 51, 51);font-weight: 700;">scope</span>><br> </<span style="color: rgb(51, 51, 51);font-weight: 700;">dependency</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">dependency</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">groupId</span>>org.springframework.boot</<span style="color: rgb(51, 51, 51);font-weight: 700;">groupId</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">artifactId</span>>spring-boot-dependencies</<span style="color: rgb(51, 51, 51);font-weight: 700;">artifactId</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">version</span>>$<span style="color: rgb(188, 96, 96);">{spring-boot-version}</span></<span style="color: rgb(51, 51, 51);font-weight: 700;">version</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">type</span>>pom</<span style="color: rgb(51, 51, 51);font-weight: 700;">type</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">scope</span>>import</<span style="color: rgb(51, 51, 51);font-weight: 700;">scope</span>><br> </<span style="color: rgb(51, 51, 51);font-weight: 700;">dependency</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">dependency</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">groupId</span>>com.alibaba.cloud</<span style="color: rgb(51, 51, 51);font-weight: 700;">groupId</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">artifactId</span>>spring-cloud-alibaba-dependencies</<span style="color: rgb(51, 51, 51);font-weight: 700;">artifactId</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">version</span>>$<span style="color: rgb(188, 96, 96);">{spring-boot-version}</span></<span style="color: rgb(51, 51, 51);font-weight: 700;">version</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">type</span>>pom</<span style="color: rgb(51, 51, 51);font-weight: 700;">type</span>><br> <<span style="color: rgb(51, 51, 51);font-weight: 700;">scope</span>>import</<span style="color: rgb(51, 51, 51);font-weight: 700;">scope</span>><br> </<span style="color: rgb(51, 51, 51);font-weight: 700;">dependency</span>><br> </<span style="color: rgb(51, 51, 51);font-weight: 700;">dependencies</span>><br></<span style="color: rgb(51, 51, 51);font-weight: 700;">dependencyManagement</span>><br></pre> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">创建服务提供者模块,在服务提供者模块的 pom 文件中引入 spring-cloud-starter-alibaba-nacos-discovery 依赖,在启动类中加上 @EnableDiscoveryClient 注解,使得注册中心可以发现该服务,运行启动类,可以看到 nacos 控制台中注册了该服务</p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">[<img data-ratio="0.14727272727272728" src="/upload/45ac846aebf9b20fff64b2a87cc7d62d.png" data-type="png" data-w="550" style="max-width: 96%;vertical-align: middle;border-width: 0px;border-style: none;border-color: initial;margin-right: auto;margin-bottom: 10px;margin-left: auto;text-align: center;display: block;">]( imgchr.com/i/rloNjg )</p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">点击详情,可以看到服务详情,其中临时实例属性为true,代表服务是临时实例,nacos 注册中心可以设置注册的实例是临时实例还是持久化实例,默认服务都是临时实例。</p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">[<img data-ratio="0.3927272727272727" src="/upload/9c9b1af85a3b515ba1226c19f2920cad.png" data-type="png" data-w="550" style="max-width: 96%;vertical-align: middle;border-width: 0px;border-style: none;border-color: initial;margin-right: auto;margin-bottom: 10px;margin-left: auto;text-align: center;display: block;">]( imgchr.com/i/rlTdxO )</p> <h2 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.6em;text-rendering: optimizelegibility;font-size: 18px;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">四、原理详解</h2> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">搭建好demo并且在 nacos 控制台上看到效果之后,接下来就要来分析服务注册的原理了,要先知道原理,最好的办法就是分析源码,首先要知道 nacos 客户端是什么时候向服务端去注册的,其次需要知道服务端是怎么执行服务注册的,对于客户端来说,由于引入了 spring-cloud-starter-alibaba-nacos-discovery 依赖,自然源码要到这里去找。PS:源码分析部分内容较多,望大家理解。</p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">根据 springboot 自动配置原理,很容易可以想到, 要到 spring.factories 文件下去找相关的 AutoConfiguration 配置类,果不其然,我们找到了 NacosDiscoveryAutoConfiguration 这个配置类</p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);"><img data-ratio="0.8618181818181818" src="/upload/302984e0357159c9bda8e2c4248fd19d.png" data-type="png" data-w="550" style="max-width: 96%;vertical-align: middle;border-width: 0px;border-style: none;border-color: initial;margin-right: auto;margin-bottom: 10px;margin-left: auto;text-align: center;display: block;"></p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">从图中我们看到这个配置类中有三个方法上有 @Bean 注解,熟悉 Spring 的小伙伴们都知道,@Bean 注解表示产生一个 Bean 对象,然后这个 Bean 对象交给 Spring 管理,同时我们又看到最后一个 nacosAutoServiceRegistration 这个方法中的参数里有上面两个方法产生的对象,说明上面两个方法的实现不是很重要,直接来看 nacosAutoServiceRegistration 的实现。</p> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><span style="color: rgb(51, 51, 51);font-weight: 700;">public</span> NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,<br> AutoServiceRegistrationProperties autoServiceRegistrationProperties,<br> NacosRegistration registration) {<br> <span style="color: rgb(136, 136, 136);">//父类先初始化,先看看父类的实现</span><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">super</span>(serviceRegistry, autoServiceRegistrationProperties);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">this</span>.registration = registration;<br>}<br></pre> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;">public <span style="color: rgb(51, 51, 51);font-weight: 700;">abstract</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">class</span> <span style="color: rgb(136, 0, 0);font-weight: bold;">AbstractAutoServiceRegistration<R</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">extends</span> <span style="color: rgb(136, 0, 0);font-weight: bold;">Registration></span><br> implements <span style="color: rgb(136, 0, 0);">AutoServiceRegistration</span>, <span style="color: rgb(136, 0, 0);">ApplicationContextAware</span>, <span style="color: rgb(136, 0, 0);">ApplicationListener</span><<span style="color: rgb(136, 0, 0);">WebServerInitializedEvent</span>><br></pre> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">我们看到父类实现了 ApplicationListener 接口,而实现该接口必须重新其 onApplicationEvent() 方法,我们看到方法中又调用了 bind() 方法</p> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;">public <span style="color: rgb(51, 51, 51);font-weight: 700;">void</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">bind</span>(WebServerInitializedEvent <span style="color: rgb(51, 51, 51);font-weight: 700;">event</span>) {<br> ApplicationContext <span style="color: rgb(51, 51, 51);font-weight: 700;">context</span> = <span style="color: rgb(51, 51, 51);font-weight: 700;">event</span><span style="color: rgb(188, 96, 96);">.getApplicationContext</span>();<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (<span style="color: rgb(51, 51, 51);font-weight: 700;">context</span> instanceof ConfigurableWebServerApplicationContext) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (<span style="color: rgb(136, 0, 0);">"management"</span><span style="color: rgb(188, 96, 96);">.equals</span>(<br> ((ConfigurableWebServerApplicationContext) <span style="color: rgb(51, 51, 51);font-weight: 700;">context</span>)<span style="color: rgb(188, 96, 96);">.getServerNamespace</span>())) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">return</span>;<br> }<br> }<br> <span style="color: rgb(136, 136, 136);">//CAS 原子操作</span><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">this</span><span style="color: rgb(188, 96, 96);">.port</span><span style="color: rgb(188, 96, 96);">.compareAndSet</span>(<span style="color: rgb(136, 0, 0);">0</span>, <span style="color: rgb(51, 51, 51);font-weight: 700;">event</span><span style="color: rgb(188, 96, 96);">.getWebServer</span>()<span style="color: rgb(188, 96, 96);">.getPort</span>());<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">this</span><span style="color: rgb(188, 96, 96);">.start</span>();<br>}<br></pre> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><span style="color: rgb(51, 51, 51);font-weight: 700;">public</span> void start() {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (!isEnabled()) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (logger.isDebugEnabled()) {<br> logger.debug(<span style="color: rgb(136, 0, 0);">"Discovery Lifecycle disabled. Not starting"</span>);<br> }<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">return</span>;<br> }<br><br> <span style="color: rgb(136, 136, 136);">// only initialize if nonSecurePort is greater than 0 and it isn't already running</span><br> <span style="color: rgb(136, 136, 136);">// because of containerPortInitializer below</span><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (!<span style="color: rgb(51, 51, 51);font-weight: 700;">this</span>.running.<span style="color: rgb(51, 51, 51);font-weight: 700;">get</span>()) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">this</span>.context.publishEvent(new InstancePreRegisteredEvent(<span style="color: rgb(51, 51, 51);font-weight: 700;">this</span>, getRegistration()));<br> <span style="color: rgb(136, 136, 136);">// 开始注册</span><br> register();<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (shouldRegisterManagement()) {<br> registerManagement();<br> }<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">this</span>.context.publishEvent(<br> new InstanceRegisteredEvent<>(<span style="color: rgb(51, 51, 51);font-weight: 700;">this</span>, getConfiguration()));<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">this</span>.running.compareAndSet(<span style="color: rgb(120, 169, 96);">false</span>, <span style="color: rgb(120, 169, 96);">true</span>);<br> }<br><br>}<br></pre> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register</h4> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><span style="color: rgb(51, 51, 51);font-weight: 700;">public</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">void</span> <span style="color: rgb(136, 0, 0);font-weight: bold;">register</span>(Registration registration) {<br><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (StringUtils.isEmpty(registration.getServiceId())) {<br> <span style="color: rgb(57, 115, 0);">log</span>.warn(<span style="color: rgb(136, 0, 0);">"No service to register for nacos client..."</span>);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">return</span>;<br> }<br><br> String serviceId = registration.getServiceId();<br><br> Instance instance = getNacosInstanceFromRegistration(registration);<br><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">try</span> {<br> <span style="color: rgb(136, 136, 136);">// 把 serviceId 和 instance传入,开始注册流程</span><br> namingService.registerInstance(serviceId, instance);<br> <span style="color: rgb(57, 115, 0);">log</span>.info(<span style="color: rgb(136, 0, 0);">"nacos registry, {} {}:{} register finished"</span>, serviceId,<br> instance.getIp(), instance.getPort());<br> }<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">catch</span> (Exception e) {<br> <span style="color: rgb(57, 115, 0);">log</span>.error(<span style="color: rgb(136, 0, 0);">"nacos registry, {} register failed...{},"</span>, serviceId,<br> registration.toString(), e);<br> }<br>}<br></pre> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">com.alibaba.nacos.client.naming.NacosNamingService#registerInstance(java.lang.String, java.lang.String, com.alibaba.nacos.api.naming.pojo.Instance)</h4> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;">public <span style="color: rgb(51, 51, 51);font-weight: 700;">void</span> registerInstance(String serviceName, String groupName, Instance <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span>) throws NacosException {<br> <span style="color: rgb(136, 136, 136);">//如果实例是临时的,组装beatInfo,发送心跳请求,因为默认是临时实例,所以肯定走到这段代码</span><br> <span style="color: rgb(136, 136, 136);">//为什么要发送心跳请求,因为nacos在此时实现的是cap理论中的ap模式,即不保证强一致性,但会保证可用性,这就需要做到动态感知服务的上下线,所以要通过心跳来判断服务是否还正常在线</span><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (<span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.isEphemeral</span>()) {<br> BeatInfo beatInfo = <span style="color: rgb(51, 51, 51);font-weight: 700;">new</span> BeatInfo();<br> beatInfo<span style="color: rgb(188, 96, 96);">.setServiceName</span>(NamingUtils<span style="color: rgb(188, 96, 96);">.getGroupedName</span>(serviceName, groupName));<br> beatInfo<span style="color: rgb(188, 96, 96);">.setIp</span>(<span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.getIp</span>());<br> beatInfo<span style="color: rgb(188, 96, 96);">.setPort</span>(<span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.getPort</span>());<br> beatInfo<span style="color: rgb(188, 96, 96);">.setCluster</span>(<span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.getClusterName</span>());<br> beatInfo<span style="color: rgb(188, 96, 96);">.setWeight</span>(<span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.getWeight</span>());<br> beatInfo<span style="color: rgb(188, 96, 96);">.setMetadata</span>(<span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.getMetadata</span>());<br> beatInfo<span style="color: rgb(188, 96, 96);">.setScheduled</span>(false);<br> long instanceInterval = <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.getInstanceHeartBeatInterval</span>();<br> beatInfo<span style="color: rgb(188, 96, 96);">.setPeriod</span>(instanceInterval == <span style="color: rgb(136, 0, 0);">0</span> ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);<br> <span style="color: rgb(136, 136, 136);">//添加心跳信息进行处理</span><br> beatReactor<span style="color: rgb(188, 96, 96);">.addBeatInfo</span>(NamingUtils<span style="color: rgb(188, 96, 96);">.getGroupedName</span>(serviceName, groupName), beatInfo);<br> }<br> <span style="color: rgb(136, 136, 136);">//服务代理类注册实例</span><br> serverProxy<span style="color: rgb(188, 96, 96);">.registerService</span>(NamingUtils<span style="color: rgb(188, 96, 96);">.getGroupedName</span>(serviceName, groupName), groupName, <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span>);<br>}<br></pre> <ul style="margin-bottom: 0.75em;margin-left: 25px;list-style-type: none;font-size: 16px;line-height: 1.7em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);" class="list-paddingleft-2"> <li style="line-height: 1.7em;list-style-type: disc;"><p>心跳请求部分</p></li> </ul> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">com.alibaba.nacos.client.naming.beat.BeatReactor#addBeatInfo</h4> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><span style="color: rgb(51, 51, 51);font-weight: 700;">public</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">void</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">addBeatInfo</span>(String serviceName, BeatInfo beatInfo) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">NAMING_LOGGER</span><span style="color: rgb(136, 0, 0);">.info</span>(<span style="color: rgb(136, 0, 0);">"[BEAT] adding beat: {} to beat map."</span>, beatInfo);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">dom2Beat</span><span style="color: rgb(136, 0, 0);">.put</span>(buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort()), beatInfo);<br> <span style="color: rgb(136, 136, 136);">//定时任务发送心跳请求</span><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">executorService</span><span style="color: rgb(136, 0, 0);">.schedule</span>(new BeatTask(beatInfo), <span style="color: rgb(136, 0, 0);">0</span>, TimeUnit.MILLISECONDS);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">MetricsMonitor</span><span style="color: rgb(136, 0, 0);">.getDom2BeatSizeMonitor</span>()<span style="color: rgb(136, 0, 0);">.set</span>(dom2Beat.size());<br>}<br></pre> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">com.alibaba.nacos.client.naming.beat.BeatReactor.BeatTask#run</h4> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><span style="color: rgb(51, 51, 51);font-weight: 700;">public</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">void</span> <span style="color: rgb(136, 0, 0);font-weight: bold;">run</span>() {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (beatInfo.isStopped()) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">return</span>;<br> }<br> <span style="color: rgb(136, 136, 136);">//服务代理类发送心跳,心跳时长为5秒钟,也就是每5秒发送一次心跳请求</span><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">long</span> result = serverProxy.sendBeat(beatInfo);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">long</span> nextTime = result > <span style="color: rgb(136, 0, 0);">0</span> ? result : beatInfo.getPeriod();<br> executorService.schedule(<span style="color: rgb(51, 51, 51);font-weight: 700;">new</span> BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);<br>}<br></pre> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">com.alibaba.nacos.client.naming.net.NamingProxy#sendBeat</h4> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;">public long sendBeat(BeatInfo beatInfo) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">try</span> {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (NAMING_LOGGER.isDebugEnabled()) {<br> NAMING_LOGGER.debug(<span style="color: rgb(136, 0, 0);">"[BEAT] {} sending beat to server: {}"</span>, namespaceId, beatInfo.toString());<br> }<br> <span style="color: rgb(57, 115, 0);">Map</span><<span style="color: rgb(57, 115, 0);">String</span>, <span style="color: rgb(57, 115, 0);">String</span>> params = <span style="color: rgb(51, 51, 51);font-weight: 700;">new</span> HashMap<<span style="color: rgb(57, 115, 0);">String</span>, <span style="color: rgb(57, 115, 0);">String</span>>(<span style="color: rgb(136, 0, 0);">4</span>);<br> params.put(<span style="color: rgb(136, 0, 0);">"beat"</span>, <span style="color: rgb(57, 115, 0);">JSON</span>.toJSONString(beatInfo));<br> params.put(CommonParams.NAMESPACE_ID, namespaceId);<br> params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());<br> <span style="color: rgb(136, 136, 136);">//通过http的put请求,向服务端发送心跳请求,请求路径为/v1/ns/instance/beat</span><br> <span style="color: rgb(57, 115, 0);">String</span> result = reqAPI(UtilAndComs.NACOS_URL_BASE + <span style="color: rgb(136, 0, 0);">"/instance/beat"</span>, params, HttpMethod.PUT);<br> JSONObject jsonObject = <span style="color: rgb(57, 115, 0);">JSON</span>.parseObject(result);<br><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (jsonObject != <span style="color: rgb(120, 169, 96);">null</span>) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">return</span> jsonObject.getLong(<span style="color: rgb(136, 0, 0);">"clientBeatInterval"</span>);<br> }<br> } <span style="color: rgb(51, 51, 51);font-weight: 700;">catch</span> (Exception e) {<br> NAMING_LOGGER.error(<span style="color: rgb(136, 0, 0);">"[CLIENT-BEAT] failed to send beat: "</span> + <span style="color: rgb(57, 115, 0);">JSON</span>.toJSONString(beatInfo), e);<br> }<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">return</span> <span style="color: rgb(136, 0, 0);">0</span>L;<br>}<br></pre> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">看到这里我们了解到,客户端是通过 http 请求的方式,和服务端进行通信,由于还涉及注册实例的源码和服务端的源码,心跳部分暂时告一段落,接下来回到服务代理类注册实例</p> <h4 style="margin-bottom: 0.5em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;font-weight: bold;line-height: 1.7em;text-rendering: optimizelegibility;text-indent: 1em;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">com.alibaba.nacos.client.naming.net.NamingProxy#registerService</h4> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;"><span style="color: rgb(51, 51, 51);font-weight: 700;">public</span> <span style="color: rgb(51, 51, 51);font-weight: 700;">void</span> <span style="color: rgb(136, 0, 0);font-weight: bold;">registerService</span>(String serviceName, String groupName, Instance instance) throws NacosException {<br><br> NAMING_LOGGER.info(<span style="color: rgb(136, 0, 0);">"[REGISTER-SERVICE] {} registering service {} with instance: {}"</span>,<br> namespaceId, serviceName, instance);<br> <span style="color: rgb(136, 136, 136);">//组装各种参数</span><br> final Map<String, String> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span> = <span style="color: rgb(51, 51, 51);font-weight: 700;">new</span> HashMap<String, String>(<span style="color: rgb(136, 0, 0);">9</span>);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(CommonParams.NAMESPACE_ID, namespaceId);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(CommonParams.SERVICE_NAME, serviceName);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(CommonParams.GROUP_NAME, groupName);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(CommonParams.CLUSTER_NAME, instance.getClusterName());<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(<span style="color: rgb(136, 0, 0);">"ip"</span>, instance.getIp());<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(<span style="color: rgb(136, 0, 0);">"port"</span>, String.valueOf(instance.getPort()));<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(<span style="color: rgb(136, 0, 0);">"weight"</span>, String.valueOf(instance.getWeight()));<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(<span style="color: rgb(136, 0, 0);">"enable"</span>, String.valueOf(instance.isEnabled()));<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(<span style="color: rgb(136, 0, 0);">"healthy"</span>, String.valueOf(instance.isHealthy()));<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(<span style="color: rgb(136, 0, 0);">"ephemeral"</span>, String.valueOf(instance.isEphemeral()));<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>.put(<span style="color: rgb(136, 0, 0);">"metadata"</span>, JSON.toJSONString(instance.getMetadata()));<br> <span style="color: rgb(136, 136, 136);">//发送注册实例请求,路径为/v1/ns/instance</span><br> reqAPI(UtilAndComs.NACOS_URL_INSTANCE, <span style="color: rgb(51, 51, 51);font-weight: 700;">params</span>, HttpMethod.POST);<br><br>}<br></pre> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">到这里,nacos 客户端部分的源码就分析完了,接下来开始分析 nacos 服务端的源码</p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">首先去到 nacos 的官方 github,clone 一份源码到本地,选择1.1.4版本,通过 maven 编译后打开,源码结构如下</p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);"><img data-ratio="2.1810344827586206" src="/upload/e73e1edb73a4f4d38efc17f2183136a8.png" data-type="png" data-w="348" style="max-width: 96%;vertical-align: middle;border-width: 0px;border-style: none;border-color: initial;margin-right: auto;margin-bottom: 10px;margin-left: auto;text-align: center;display: block;"></p> <p style="margin-bottom: 0.75em;font-size: 16px;line-height: 1.7em;text-indent: 1em;font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif;text-align: start;white-space: normal;background-color: rgb(254, 254, 254);">之前我们在分析客户端源码的时候看到客户端向服务端发送请求的部分都在 NacosNamingService 类中,相对应的在服务端应该也是到naming 的模块下寻找代码的入口,根据 springboot 开发接口的习惯,接口都是通过 controller 来实现调用的,所以直接找到controllers目录,其中在 InstanceController 中,我们看到了 register() 的方法,也看到了 beat() 方法,接上面的 客户端心跳发送部分,先分析 beat() 方法。</p> <pre style="padding: 0.5em;font-family: Menlo, Monaco, Consolas, "Courier New", monospace;color: rgb(68, 68, 68);border-radius: 4px;margin-bottom: 1.5em;font-size: 14px;line-height: 1.5em;word-break: break-all;background-color: rgb(246, 246, 246);border-width: initial;border-style: none;border-color: initial;overflow-x: auto;text-align: start;">public JSONObject beat(HttpServletRequest request) throws Exception {<br><br> JSONObject result = <span style="color: rgb(51, 51, 51);font-weight: 700;">new</span> JSONObject();<br><br> result<span style="color: rgb(188, 96, 96);">.put</span>(<span style="color: rgb(136, 0, 0);">"clientBeatInterval"</span>, switchDomain<span style="color: rgb(188, 96, 96);">.getClientBeatInterval</span>());<br> String serviceName = WebUtils<span style="color: rgb(188, 96, 96);">.required</span>(request, CommonParams<span style="color: rgb(188, 96, 96);">.SERVICE_NAME</span>);<br> String namespaceId = WebUtils<span style="color: rgb(188, 96, 96);">.optional</span>(request, CommonParams<span style="color: rgb(188, 96, 96);">.NAMESPACE_ID</span>,<br> Constants<span style="color: rgb(188, 96, 96);">.DEFAULT_NAMESPACE_ID</span>);<br> String beat = WebUtils<span style="color: rgb(188, 96, 96);">.required</span>(request, <span style="color: rgb(136, 0, 0);">"beat"</span>);<br> RsInfo clientBeat = JSON<span style="color: rgb(188, 96, 96);">.parseObject</span>(beat, RsInfo<span style="color: rgb(188, 96, 96);">.class</span>);<br><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (!switchDomain<span style="color: rgb(188, 96, 96);">.isDefaultInstanceEphemeral</span>() && !clientBeat<span style="color: rgb(188, 96, 96);">.isEphemeral</span>()) {<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">return</span> result;<br> }<br><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (StringUtils<span style="color: rgb(188, 96, 96);">.isBlank</span>(clientBeat<span style="color: rgb(188, 96, 96);">.getCluster</span>())) {<br> clientBeat<span style="color: rgb(188, 96, 96);">.setCluster</span>(UtilsAndCommons<span style="color: rgb(188, 96, 96);">.DEFAULT_CLUSTER_NAME</span>);<br> }<br><br> String clusterName = clientBeat<span style="color: rgb(188, 96, 96);">.getCluster</span>();<br><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (Loggers<span style="color: rgb(188, 96, 96);">.SRV_LOG</span><span style="color: rgb(188, 96, 96);">.isDebugEnabled</span>()) {<br> Loggers<span style="color: rgb(188, 96, 96);">.SRV_LOG</span><span style="color: rgb(188, 96, 96);">.debug</span>(<span style="color: rgb(136, 0, 0);">"[CLIENT-BEAT] full arguments: beat: {}, serviceName: {}"</span>, clientBeat, serviceName);<br> }<br><br> Instance <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span> = serviceManager<span style="color: rgb(188, 96, 96);">.getInstance</span>(namespaceId, serviceName, clientBeat<span style="color: rgb(188, 96, 96);">.getCluster</span>(),<br> clientBeat<span style="color: rgb(188, 96, 96);">.getIp</span>(),<br> clientBeat<span style="color: rgb(188, 96, 96);">.getPort</span>());<br><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">if</span> (<span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span> == <span style="color: rgb(120, 169, 96);">null</span>) {<br> <span style="color: rgb(136, 136, 136);">//如果实例为空,组装实例</span><br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span> = <span style="color: rgb(51, 51, 51);font-weight: 700;">new</span> Instance();<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.setPort</span>(clientBeat<span style="color: rgb(188, 96, 96);">.getPort</span>());<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.setIp</span>(clientBeat<span style="color: rgb(188, 96, 96);">.getIp</span>());<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.setWeight</span>(clientBeat<span style="color: rgb(188, 96, 96);">.getWeight</span>());<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.setMetadata</span>(clientBeat<span style="color: rgb(188, 96, 96);">.getMetadata</span>());<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.setClusterName</span>(clusterName);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.setServiceName</span>(serviceName);<br> <span style="color: rgb(51, 51, 51);font-weight: 700;">instance</span><span style="color: rgb(188, 96, 96);">.setInstanceId</span>(<span st
作者:微信小助手
<p data-lake-id="f47942fff38c686ed626a530b6d84219" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;" data-mpa-powered-by="yiban.io"><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">点击上方蓝色“</span><span style="color: rgb(24, 144, 255);font-size: 13px;" data-mce-style="font-size: 10px">后端面试那些事儿</span><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">”,选择“设为星标”</span></p> <p data-lake-id="eca2a1864e13b3e1b84aafe9cb4abdff" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">学最好的别人,做最好的自己</span></p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: right;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">来源:cnblogs.com/rickiyang/p/11074158.html</span></p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">前面刚说到Guava Cache,他的优点是封装了get,put操作;提供线程安全的缓存操作;提供过期策略;提供回收策略;缓存监控。当缓存的数据超过最大值时,使用LRU算法替换。这一篇我们将要谈到一个新的本地缓存框架:Caffeine Cache。它也是站在巨人的肩膀上-Guava Cache,借着他的思想优化了算法发展而来。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本篇博文主要介绍Caffine Cache 的使用方式,以及Caffine Cache在SpringBoot中的使用。</p> <h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">1. Caffine Cache 在算法上的优点-W-TinyLFU</span></h3> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">说到优化,Caffine Cache到底优化了什么呢?我们刚提到过LRU,常见的缓存淘汰算法还有FIFO,LFU:</p> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li style="box-sizing: border-box;"> <section style="box-sizing: border-box;margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> FIFO:先进先出,在这种淘汰算法中,先进入缓存的会先被淘汰,会导致命中率很低。 </section></li> <li style="box-sizing: border-box;"> <section style="box-sizing: border-box;margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> LRU:最近最少使用算法,每次访问数据都会将其放在我们的队尾,如果需要淘汰数据,就只需要淘汰队首即可。仍然有个问题,如果有个数据在 1 分钟访问了 1000次,再后 1 分钟没有访问这个数据,但是有其他的数据访问,就导致了我们这个热点数据被淘汰。 </section></li> <li style="box-sizing: border-box;"> <section style="box-sizing: border-box;margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> LFU:最近最少频率使用,利用额外的空间记录每个数据的使用频率,然后选出频率最低进行淘汰。这样就避免了 LRU 不能处理时间段的问题。 </section></li> </ol> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">上面三种策略各有利弊,实现的成本也是一个比一个高,同时命中率也是一个比一个好。Guava Cache虽然有这么多的功能,但是本质上还是对LRU的封装,如果有更优良的算法,并且也能提供这么多功能,相比之下就相形见绌了。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="box-sizing: border-box;color: rgb(53, 179, 120);">LFU的局限性</strong>:在 LFU 中只要数据访问模式的概率分布随时间保持不变时,其命中率就能变得非常高。比如有部新剧出来了,我们使用 LFU 给他缓存下来,这部新剧在这几天大概访问了几亿次,这个访问频率也在我们的 LFU 中记录了几亿次。但是新剧总会过气的,比如一个月之后这个新剧的前几集其实已经过气了,但是他的访问量的确是太高了,其他的电视剧根本无法淘汰这个新剧,所以在这种模式下是有局限性。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="box-sizing: border-box;color: rgb(53, 179, 120);">LRU的优点和局限性</strong>:LRU可以很好的应对突发流量的情况,因为他不需要累计数据频率。但LRU通过历史数据来预测未来是局限的,它会认为最后到来的数据是最可能被再次访问的,从而给与它最高的优先级。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在现有算法的局限性下,会导致缓存数据的命中率或多或少的受损,而命中略又是缓存的重要指标。HighScalability网站刊登了一篇文章,由前Google工程师发明的W-TinyLFU——一种现代的缓存 。Caffine Cache就是基于此算法而研发。Caffeine 因使用 <strong style="box-sizing: border-box;color: rgb(53, 179, 120);">Window TinyLfu</strong> 回收策略,提供了一个<strong style="box-sizing: border-box;color: rgb(53, 179, 120);">近乎最佳的命中率</strong>。</p> <blockquote style="box-sizing: border-box;margin: 10px 5px;border-top: none;border-right: 0px solid rgb(53, 179, 120);border-bottom: none;border-left-color: rgb(53, 179, 120);font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(97, 97, 97);padding: 10px 10px 10px 20px;quotes: none;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;"> <p style="box-sizing: border-box;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">当数据的访问模式不随时间变化的时候,LFU的策略能够带来最佳的缓存命中率。然而LFU有两个缺点:</p> <p style="box-sizing: border-box;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">首先,它需要给每个记录项维护频率信息,每次访问都需要更新,这是个巨大的开销;</p> <p style="box-sizing: border-box;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">其次,如果数据访问模式随时间有变,LFU的频率信息无法随之变化,因此早先频繁访问的记录可能会占据缓存,而后期访问较多的记录则无法被命中。</p> <p style="box-sizing: border-box;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">因此,大多数的缓存设计都是基于LRU或者其变种来进行的。相比之下,LRU并不需要维护昂贵的缓存记录元信息,同时也能够反应随时间变化的数据访问模式。然而,在许多负载之下,LRU依然需要更多的空间才能做到跟LFU一致的缓存命中率。因此,一个“现代”的缓存,应当能够综合两者的长处。</p> </blockquote> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">TinyLFU维护了近期访问记录的频率信息,作为一个过滤器,当新记录来时,只有满足TinyLFU要求的记录才可以被插入缓存。如前所述,作为现代的缓存,它需要解决两个挑战:</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一个是如何避免维护频率信息的高开销;</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">另一个是如何反应随时间变化的访问模式。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先来看前者,TinyLFU借助了数据流Sketching技术,Count-Min Sketch显然是解决这个问题的有效手段,它可以用小得多的空间存放频率信息,而保证很低的False Positive Rate。但考虑到第二个问题,就要复杂许多了,因为我们知道,任何Sketching数据结构如果要反应时间变化都是一件困难的事情,在Bloom Filter方面,我们可以有Timing Bloom Filter,但对于CMSketch来说,如何做到Timing CMSketch就不那么容易了。TinyLFU采用了一种基于滑动窗口的时间衰减设计机制,借助于一种简易的reset操作:每次添加一条记录到Sketch的时候,都会给一个计数器上加1,当计数器达到一个尺寸W的时候,把所有记录的Sketch数值都除以2,该reset操作可以起到衰减的作用 。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">W-TinyLFU主要用来解决一些稀疏的突发访问元素。在一些数目很少但突发访问量很大的场景下,TinyLFU将无法保存这类元素,因为它们无法在给定时间内积累到足够高的频率。因此W-TinyLFU就是结合LFU和LRU,前者用来应对大多数场景,而LRU用来处理突发流量。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在处理频率记录的方案中,你可能会想到用hashMap去存储,每一个key对应一个频率值。那如果数据量特别大的时候,是不是这个hashMap也会特别大呢。由此可以联想到 Bloom Filter,对于每个key,用n个byte每个存储一个标志用来判断key是否在集合中。原理就是使用k个hash函数来将key散列成一个整数。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在W-TinyLFU中使用Count-Min Sketch记录我们的访问频率,而这个也是布隆过滤器的一种变种。如下图所示:</p> <p style="text-align: center;"><img class="rich_pages" data-backh="203" data-backw="579" data-galleryid="" data-ratio="0.35081967213114756" data-s="300,640" src="/upload/54121cc1b0527f015ef2d1e593d0da09.png" data-type="png" data-w="610" style="width: 100%;height: auto;"></p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果需要记录一个值,那我们需要通过多种Hash算法对其进行处理hash,然后在对应的hash算法的记录中+1,为什么需要多种hash算法呢?由于这是一个压缩算法必定会出现冲突,比如我们建立一个byte的数组,通过计算出每个数据的hash的位置。比如张三和李四,他们两有可能hash值都是相同,比如都是1那byte[1]这个位置就会增加相应的频率,张三访问1万次,李四访问1次那byte[1]这个位置就是1万零1,如果取李四的访问评率的时候就会取出是1万零1,但是李四命名只访问了1次啊,为了解决这个问题,所以用了多个hash算法可以理解为long[][]二维数组的一个概念,比如在第一个算法张三和李四冲突了,但是在第二个,第三个中很大的概率不冲突,比如一个算法大概有1%的概率冲突,那四个算法一起冲突的概率是1%的四次方。通过这个模式我们取李四的访问率的时候取所有算法中,李四访问最低频率的次数。所以他的名字叫Count-Min Sketch。</p> <h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">2. 使用</span></h3> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Caffeine Cache 的github地址:点我。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">目前的最新版本是:</p> <pre style="box-sizing: border-box;font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="box-sizing: border-box;line-height: 26px;"><<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">dependency</span>></span><br style="box-sizing: border-box;"> <span style="box-sizing: border-box;line-height: 26px;"><<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">groupId</span>></span>com.github.ben-manes.caffeine<span style="box-sizing: border-box;line-height: 26px;"></<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">groupId</span>></span><br style="box-sizing: border-box;"> <span style="box-sizing: border-box;line-height: 26px;"><<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">artifactId</span>></span>caffeine<span style="box-sizing: border-box;line-height: 26px;"></<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">artifactId</span>></span><br style="box-sizing: border-box;"> <span style="box-sizing: border-box;line-height: 26px;"><<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">version</span>></span>2.6.2<span style="box-sizing: border-box;line-height: 26px;"></<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">version</span>></span><br style="box-sizing: border-box;"><span style="box-sizing: border-box;line-height: 26px;"></<span style="box-sizing: border-box;color: rgb(224, 108, 117);line-height: 26px;">dependency</span>></span><br style="box-sizing: border-box;"></code></pre> <h4 style="box-sizing: border-box;margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">2.1 缓存填充策略</span></h4> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Caffeine Cache提供了三种缓存填充策略:手动、同步加载和异步加载。</p> <h5 style="box-sizing: border-box;margin-top: 30px;margin-bottom: 15px;color: black;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.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">1.手动加载</span></h5> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在每次get key的时候指定一个同步的函数,如果key不存在就调用这个函数生成一个值。</p> <pre style="box-sizing: border-box;font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br style="box-sizing: border-box;"> * 手动加载<br style="box-sizing: border-box;"> * <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">@param</span> key<br style="box-sizing: border-box;"> * <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">@return</span><br style="box-sizing: border-box;"> */</span><br style="box-sizing: border-box;"><span style="box-sizing: border-box;line-height: 26px;"><span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">public</span> Object <span style="box-sizing: border-box;color: rgb(97, 174, 238);line-height: 26px;">manulOperator</span><span style="box-sizing: border-box;line-height: 26px;">(String key)</span> </span>{<br style="box-sizing: border-box;"> Cache<String, Object> cache = Caffeine.newBuilder()<br style="box-sizing: border-box;"> .expireAfterWrite(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">1</span>, TimeUnit.SECONDS)<br style="box-sizing: border-box;"> .expireAfterAccess(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">1</span>, TimeUnit.SECONDS)<br style="box-sizing: border-box;"> .maximumSize(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">10</span>)<br style="box-sizing: border-box;"> .build();<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//如果一个key不存在,那么会进入指定的函数生成value</span><br style="box-sizing: border-box;"> Object value = cache.get(key, t -> setValue(key).apply(key));<br style="box-sizing: border-box;"> cache.put(<span style="box-sizing: border-box;color: rgb(152, 195, 121);line-height: 26px;">"hello"</span>,value);<br style="box-sizing: border-box;"><br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//判断是否存在如果不存返回null</span><br style="box-sizing: border-box;"> Object ifPresent = cache.getIfPresent(key);<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//移除一个key</span><br style="box-sizing: border-box;"> cache.invalidate(key);<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">return</span> value;<br style="box-sizing: border-box;">}<br style="box-sizing: border-box;"><br style="box-sizing: border-box;"><span style="box-sizing: border-box;line-height: 26px;"><span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">public</span> Function<String, Object> <span style="box-sizing: border-box;color: rgb(97, 174, 238);line-height: 26px;">setValue</span><span style="box-sizing: border-box;line-height: 26px;">(String key)</span></span>{<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">return</span> t -> key + <span style="box-sizing: border-box;color: rgb(152, 195, 121);line-height: 26px;">"value"</span>;<br style="box-sizing: border-box;">}<br style="box-sizing: border-box;"></code></pre> <h5 style="box-sizing: border-box;margin-top: 30px;margin-bottom: 15px;color: black;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.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">2. 同步加载</span></h5> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">构造Cache时候,build方法传入一个CacheLoader实现类。实现load方法,通过key加载value。</p> <pre style="box-sizing: border-box;font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br style="box-sizing: border-box;"> * 同步加载<br style="box-sizing: border-box;"> * <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">@param</span> key<br style="box-sizing: border-box;"> * <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">@return</span><br style="box-sizing: border-box;"> */</span><br style="box-sizing: border-box;"><span style="box-sizing: border-box;line-height: 26px;"><span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">public</span> Object <span style="box-sizing: border-box;color: rgb(97, 174, 238);line-height: 26px;">syncOperator</span><span style="box-sizing: border-box;line-height: 26px;">(String key)</span></span>{<br style="box-sizing: border-box;"> LoadingCache<String, Object> cache = Caffeine.newBuilder()<br style="box-sizing: border-box;"> .maximumSize(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">100</span>)<br style="box-sizing: border-box;"> .expireAfterWrite(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">1</span>, TimeUnit.MINUTES)<br style="box-sizing: border-box;"> .build(k -> setValue(key).apply(key));<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">return</span> cache.get(key);<br style="box-sizing: border-box;">}<br style="box-sizing: border-box;"><br style="box-sizing: border-box;"><span style="box-sizing: border-box;line-height: 26px;"><span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">public</span> Function<String, Object> <span style="box-sizing: border-box;color: rgb(97, 174, 238);line-height: 26px;">setValue</span><span style="box-sizing: border-box;line-height: 26px;">(String key)</span></span>{<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">return</span> t -> key + <span style="box-sizing: border-box;color: rgb(152, 195, 121);line-height: 26px;">"value"</span>;<br style="box-sizing: border-box;">}<br style="box-sizing: border-box;"></code></pre> <h5 style="box-sizing: border-box;margin-top: 30px;margin-bottom: 15px;color: black;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.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">3. 异步加载</span></h5> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">AsyncLoadingCache是继承自LoadingCache类的,异步加载使用Executor去调用方法并返回一个CompletableFuture。异步加载缓存使用了响应式编程模型。</p> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果要以同步方式调用时,应提供CacheLoader。要以异步表示时,应该提供一个AsyncCacheLoader,并返回一个CompletableFuture。</p> <pre style="box-sizing: border-box;font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"> <span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br style="box-sizing: border-box;"> * 异步加载<br style="box-sizing: border-box;"> *<br style="box-sizing: border-box;"> * <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">@param</span> key<br style="box-sizing: border-box;"> * <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">@return</span><br style="box-sizing: border-box;"> */</span><br style="box-sizing: border-box;"><span style="box-sizing: border-box;line-height: 26px;"><span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">public</span> Object <span style="box-sizing: border-box;color: rgb(97, 174, 238);line-height: 26px;">asyncOperator</span><span style="box-sizing: border-box;line-height: 26px;">(String key)</span></span>{<br style="box-sizing: border-box;"> AsyncLoadingCache<String, Object> cache = Caffeine.newBuilder()<br style="box-sizing: border-box;"> .maximumSize(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">100</span>)<br style="box-sizing: border-box;"> .expireAfterWrite(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">1</span>, TimeUnit.MINUTES)<br style="box-sizing: border-box;"> .buildAsync(k -> setAsyncValue(key).get());<br style="box-sizing: border-box;"><br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">return</span> cache.get(key);<br style="box-sizing: border-box;">}<br style="box-sizing: border-box;"><br style="box-sizing: border-box;"><span style="box-sizing: border-box;line-height: 26px;"><span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">public</span> CompletableFuture<Object> <span style="box-sizing: border-box;color: rgb(97, 174, 238);line-height: 26px;">setAsyncValue</span><span style="box-sizing: border-box;line-height: 26px;">(String key)</span></span>{<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">return</span> CompletableFuture.supplyAsync(() -> {<br style="box-sizing: border-box;"> <span style="box-sizing: border-box;color: rgb(198, 120, 221);line-height: 26px;">return</span> key + <span style="box-sizing: border-box;color: rgb(152, 195, 121);line-height: 26px;">"value"</span>;<br style="box-sizing: border-box;"> });<br style="box-sizing: border-box;">}<br style="box-sizing: border-box;"></code></pre> <h4 style="box-sizing: border-box;margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">2.2 回收策略</span></h4> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Caffeine提供了3种回收策略:基于大小回收,基于时间回收,基于引用回收。</p> <h5 style="box-sizing: border-box;margin-top: 30px;margin-bottom: 15px;color: black;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.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">1. 基于大小的过期方式</span></h5> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">基于大小的回收策略有两种方式:一种是基于缓存大小,一种是基于权重。</p> <pre style="box-sizing: border-box;font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 根据缓存的计数进行驱逐</span><br style="box-sizing: border-box;">LoadingCache<String, Object> cache = Caffeine.newBuilder()<br style="box-sizing: border-box;"> .maximumSize(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">10000</span>)<br style="box-sizing: border-box;"> .build(key -> function(key));<br style="box-sizing: border-box;"><br style="box-sizing: border-box;"><br style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 根据缓存的权重来进行驱逐(权重只是用于确定缓存大小,不会用于决定该缓存是否被驱逐)</span><br style="box-sizing: border-box;">LoadingCache<String, Object> cache1 = Caffeine.newBuilder()<br style="box-sizing: border-box;"> .maximumWeight(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">10000</span>)<br style="box-sizing: border-box;"> .weigher(key -> function1(key))<br style="box-sizing: border-box;"> .build(key -> function(key));<br style="box-sizing: border-box;"></code></pre> <p style="box-sizing: border-box;margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">maximumWeight与maximumSize不可以同时使用。</p> <h5 style="box-sizing: border-box;margin-top: 30px;margin-bottom: 15px;color: black;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.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;">2.基于时间的过期方式</span></h5> <pre style="box-sizing: border-box;font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="box-sizing: border-box;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 基于固定的到期策略进行退出</span><br style="box-sizing: border-box;">LoadingCache<String, Object> cache = Caffeine.newBuilder()<br style="box-sizing: border-box;"> .expireAfterAccess(<span style="box-sizing: border-box;color: rgb(209, 154, 102);line-height: 26px;">5</span>, TimeUnit.MINUTES)<br style="box-sizing: border-box;"> .build(key -> function(key));<br style="box-sizing: border-box;">LoadingCache<String, Object> cache1 = Caffeine.newBuilder()<br style="box-sizing: border-box;"> .expireAfterWrite(<span style="