作者:微信小助手
<p style="text-align: center;"><img data-ratio="0.5625" src="/upload/506af14682cadd903706672af0e2cd49.jpg" data-type="jpeg" data-w="976"></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;text-size-adjust: auto;"><br></span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color:#3c3c3c;"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);">本篇文章给大家分享平时开发中总结的一点小技巧!在工作中写过Java程序的朋友都知道,目前使用Java开发服务最主流的方式就是通过Spring MVC定义一个Controller层接口,并将接口请求或返回参数分别定义在一个Java实体类中,这样Spring MVC在接收到Http请求(POST/GET)后,就会自动将请求报文自动映射成一个Java对象。这样的代码通常是这样写的:</span></span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color:#3c3c3c;"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);"><br></span></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;color: rgb(169, 183, 198);background: none 0% 0% repeat scroll rgb(40, 43, 46);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(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@RestController</span><br><span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">class</span> <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">OrderController</span> </span>{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Autowired</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> OrderService orderServiceImpl;<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@PostMapping</span>(<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/createOrder"</span>)<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> CreateOrderBO <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">validationTest</span><span style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(@Validated CreateOrderDTO createOrderDTO)</span> </span>{<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span> orderServiceImpl.createOrder(createOrderDTO);<br> }<br>}<br></code></pre> </section> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color:#3c3c3c;"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);"></span></span><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(60, 60, 60);font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);">这样的代码<span style="color: rgb(60, 60, 60);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);background-color: rgb(255, 255, 255);">相信</span>大家并不陌生,但在后续的逻辑实现过程中却会遇到这样的问题:“在接收请求参数后如何实现报文对象数据值的合法性校验?”。一些同学也可能认为这并不是什么问题,因为具体某个参数字段是否为空、值的取值是否在约定范围、格式是否合法等等,在业务代码中校验就好了。例如可以在Service实现类中对报文格式进行各种if-else的数据校验。</span><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"><br></span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">从功能上说冗余的if-else代码没啥毛病,但从代码的优雅性来说冗长的if-else代码会显得非常臃肿。接下来的内容将给大家介绍一种处理此类问题的实用方法。具体将从以下几个方面进行介绍:</span><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: square;"> <li><p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">使用@Validated注解实现Controller接口层数据直接绑定校验;</span></p></li> <li><p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">扩展约束性注解实现数据取值范围的校验;</span></p></li> <li><p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">更加灵活的对象数据合法性校验工具类封装;<br></span></p></li> <li><p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">数据合法性校验结果异常统一返回处理;<br></span></p></li> </ul> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"><br></span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><span style="caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;text-size-adjust: auto;"></span></p> <p style="padding-right: 10px;padding-left: 9px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;color: rgb(60, 60, 60);font-size: 16px;font-weight: bold;letter-spacing: 1px;text-align: center;line-height: 1.8;background-color: rgb(255, 255, 255);z-index: 10000;">Controller接口层数据绑定校验<br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><img data-type="png" data-ratio="0.053125" data-w="640" src="/upload/16c3bc26d4547f0344ee22af769e1434.png" style="color: rgb(60, 60, 60);font-weight: bold;letter-spacing: 1px;text-align: center;font-size: 15px;display: inline-block;left: 0px;transform: rotateX(60deg);margin-top: 5px !important;box-sizing: border-box !important;visibility: visible !important;width: 632px !important;"></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">实际上在Java开发中目前普通使用的Bean数据校验工具是"hibernate-validator",它是一个hibernete独立的jar包,所以使用这个jar包并不需要一定要集成Hibernete框架。该jar包主要实现并扩展了javax.validation(是一个基于JSR-303标准开发出来的Bean校验规范)接口。</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">由于Spring Boot在内部默认集成了"hibernate-validator",所以使用Spring Boot构建的Java工程可以直接使用相关注解来实现Bean的数据校验。例如我们最常编写的Controller层接口参数对象,可以在定义Bean类时直接编写这样的代码:</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"><br></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;color: rgb(169, 183, 198);background: none 0% 0% repeat scroll rgb(40, 43, 46);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(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Data</span><br><span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">class</span> <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">CreateOrderDTO</span> </span>{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@NotNull</span>(message = <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"订单号不能为空"</span>)<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> String orderId;<br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@NotNull</span>(message = <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"订单金额不能为空"</span>)<br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Min</span>(value = <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1</span>, message = <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"订单金额不能小于0"</span>)<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> Integer amount;<br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Pattern</span>(regexp = <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"^1[3|4|5|7|8][0-9]{9}$"</span>, message = <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"用户手机号不合法"</span>)<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> String mobileNo;<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> String orderType;<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> String status;<br>}<br></code></pre> </section> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"></span><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">如上所示代码,我们可以使用@NotNull注解来约束该字段必须不能为空,也可以使用@Min注解来约束字段的最小取值,或者还可以通过@Pattern注解来使用正则表达式来约束字段的格式(如手机号格式)等等。</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"><br></span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">以上这些注解都是“<span style="color: rgb(60, 60, 60);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);background-color: rgb(255, 255, 255);">hibernate-validator</span>”依赖包默认提供的,更多常用的注解还有很多,例如:<br></span></p> <p style="text-align: center;"><img class="rich_pages" data-backh="448" data-backw="578" src="/upload/2af279a926c4cff88b5cf6277cb3992d.png" data-cropx1="7.474048442906574" data-cropx2="1080" data-cropy1="0" data-cropy2="837.0934256055363" data-ratio="0.7800559179869525" data-s="300,640" src="https://mmbiz.qpic.cn/mmbiz_jpg/l89kosVutolDCVewuCratH5LrdmcxSmg64rJ6XCHiaJp0VSF9YR0uFa7yUd1iaEHIQB4B4EiaHyAojiaGDUHYaAFNw/640?wx_fmt=jpeg" data-type="jpeg" data-w="1073" style="width: 574px;height: 448px;"></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">利用这些约束注解,我们就可以很轻松的搞定接口数据校验,而不需要在业务逻辑中编写大量的if-else来进行数据合法性校验。而定义好Bean参数对象并使用相关注解实现参数值约束后,在Controller层接口定义中只需要使用</span><span style="color: rgb(60, 60, 60);font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);">@Validated注解就可以实现在接收参数后自动进行数据绑定校验了,具体代码如下:</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(60, 60, 60);font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);"><br></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;color: rgb(169, 183, 198);background: none 0% 0% repeat scroll rgb(40, 43, 46);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(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@PostMapping</span>(<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/createOrder"</span>)<br><span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> CreateOrderBO <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">validationTest</span><span style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(@Validated CreateOrderDTO createOrderDTO)</span> </span>{<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span> orderServiceImpl.createOrder(createOrderDTO);<br>}<br></code></pre> </section> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(60, 60, 60);font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);"></span><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">如上所示,在Controller层中通过Spring提供的<span style="color: rgb(60, 60, 60);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);background-color: rgb(255, 255, 255);">@Validated</span>注解可以自动实现数据Bean的绑定校验,如果数据异常则会统一抛出校验异常!</span><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="padding-right: 10px;padding-left: 9px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;color: rgb(60, 60, 60);font-size: 16px;font-weight: bold;letter-spacing: 1px;text-align: center;line-height: 1.8;background-color: rgb(255, 255, 255);z-index: 10000;">约束性注解扩展</p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><img data-type="png" data-ratio="0.053125" data-w="640" src="/upload/16c3bc26d4547f0344ee22af769e1434.png" style="color: rgb(60, 60, 60);font-weight: bold;letter-spacing: 1px;text-align: center;font-size: 15px;display: inline-block;left: 0px;transform: rotateX(60deg);margin-top: 5px !important;box-sizing: border-box !important;visibility: visible !important;width: 632px !important;"></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">在“hibernate-validator”依赖jar包中,虽然提供了很多很方便的约束注解,但是也有不满足某些实际需要的情况,例如我们想针对参数中的某个值约定其值的枚举范围,如orderType订单类型只允许传“pay”、“refund”两种值,那么现有的约束注解可能就没有特别适用的了。此外,如果对这样的枚举值,我们还想在约束定义中直接匹配代码中的枚举定义,以更好地统一接口参数与业务逻辑的枚举定义。那么这种情况下,我们还可以自己扩展定义相应地约束注解逻辑。</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1607342938003" data-category_id_list="1|36|41|42|43|45|47|48|5|52|6" data-id="1607342938003"></mpcpc><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"></span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);">接下来我们定义新的约束注解@EnumValue,来实现上面我们所说的效果,具体代码如下:</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 15px;letter-spacing: 1px;caret-color: rgb(60, 60, 60);color: rgb(60, 60, 60);"><br></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;color: rgb(169, 183, 198);background: none 0% 0% repeat scroll rgb(40, 43, 46);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(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Target</span>({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})<br><span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Retention</span>(RUNTIME)<br><span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Documented</span><br><span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Constraint</span>(validatedBy = {EnumValueValidator.class})<br><span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@interface</span> EnumValue {<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//默认错误消息</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">String <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">message</span><span style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">()</span> <span style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">default</span> "必须为指定值"</span>;<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//支持string数组验证</span><br> String[] strValues() <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">default</span> {};<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//支持int数组验证</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">int</span>[] intValues() <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">default</span> {};<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//支持枚举列表验证</span><br> Class<?>[] enumValues() <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">default</span> {};<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//分组</span><br> Class<?>[] groups() <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">default</span> {};<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//负载</span><br> Class<? extends Payload>[] payload() <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">default</span> {};<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//指定多个时使用</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Target</span>({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})<br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Retention</span>(RUNTIME)<br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Documented</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@interface</span> List {<br> EnumValue[] value();<br> }<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">/**<br> * 校验类逻辑定义<br> */</span><br> <span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">class</span> <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">EnumValueValidator</span> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">implements</span> <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">ConstraintValidator</span><<span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">EnumValue</span>, <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">Object</span>> </span>{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//字符串类型数组</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> String[] strValues;<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//int类型数组</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">int</span>[] intValues;<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//枚举类</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">private</span> Class<?>[] enumValues;<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">/**<br> * 初始化方法<br> *<br> * <span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span> constraintAnnotation<br> */</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Override</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">void</span> <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">initialize</span><span style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(EnumValue constraintAnnotation)</span> </span>{<br> strValues = constraintAnnotation.strValues();<br> intValues = constraintAnnotation.intValues();<br> enumValues = constraintAnnotation.enumValues();<br> }<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">/**<br> * 校验方法<br> *<br> * <span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span> value<br> * <span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span> context<br> * <span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">@return</span><br> */</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@SneakyThrows</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);overflow-wrap: inherit !important;word-break: inherit !important;">@Override</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="font-size: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">boolean</span> <span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">isValid</span><span style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(Object value, ConstraintValidatorContext context)</span> </span>{<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//针对字符串数组的校验匹配</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (strValues != <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">null</span> && strValues.length > <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (value <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">instanceof</span> String) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">for</span> (String s : strValues) {<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//判断值类型是否为Integer类型</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (s.equals(value)) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">true</span>;<br> }<br> }<br> }<br> }<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//针对整型数组的校验匹配</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (intValues != <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">null</span> && intValues.length > <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (value <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">instanceof</span> Integer) {<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//判断值类型是否为Integer类型</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">for</span> (Integer s : intValues) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (s == value) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">true</span>;<br> }<br> }<br> }<br> }<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//针对枚举类型的校验匹配</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (enumValues != <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">null</span> && enumValues.length > <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">0</span>) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">for</span> (Class<?> cl : enumValues) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (cl.isEnum()) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//枚举类验证</span><br> Object[] objs = cl.getEnumConstants();<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">//这里需要注意,定义枚举时,枚举值名称统一用value表示</span><br> Method method = cl.getMethod(<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"getValue"</span>);<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">for</span> (Object obj : objs) {<br> Object code = method.invoke(obj, <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">null</span>);<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (value.equals(code.toString())) {<br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">true</span>;<br> }<br> }<br>  
作者:微信小助手
<p data-mpa-powered-by="yiban.io" style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;color: rgb(62, 71, 83);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;text-align: center;background-color: rgb(255, 255, 255);font-size: 15px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-family: Optima-Regular, PingFangTC-light;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 16px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(127, 127, 127);font-size: 14px;line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;">点击上方“</span><span style="max-width: 100%;font-size: 14px;line-height: 1.75em;color: rgb(0, 176, 240);box-sizing: border-box !important;overflow-wrap: break-word !important;">服务端思维</span><span style="max-width: 100%;color: rgb(127, 127, 127);font-size: 14px;line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;">”,选择“</span></span><span style="max-width: 100%;color: rgb(136, 136, 136);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">设为星标</span><span style="max-width: 100%;color: rgb(127, 127, 127);letter-spacing: 0.544px;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">”</span></span></p> <p style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;text-align: center;background-color: rgb(255, 255, 255);color: rgb(62, 62, 62);font-size: 15px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-family: Optima-Regular, PingFangTC-light;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(127, 127, 127);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">回复”</span><span style="max-width: 100%;letter-spacing: 0.544px;font-size: 14px;color: rgb(0, 122, 170);box-sizing: border-box !important;overflow-wrap: break-word !important;">669</span><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(127, 127, 127);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">“获取独家整理的精选资料集</span></span></p> <p style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;text-align: center;background-color: rgb(255, 255, 255);color: rgb(62, 62, 62);font-size: 15px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 0.544px;color: rgb(127, 127, 127);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">回复”</span><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(0, 122, 170);box-sizing: border-box !important;overflow-wrap: break-word !important;">加群</span><span style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">“加入全国服务端高端社群「后端圈」</span></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-backh="335" data-backw="500" data-ratio="0.668" data-s="300,640" src="/upload/df44c11a2de47161d849cd927326230d.jpg" data-type="jpeg" data-w="500" style="width: 100%;height: auto;"></p> <p><strong>一般系统大致架构如下:</strong></p> <p><img data-ratio="0.809375" src="/upload/172233df5fe0d23c7b17771d32aea978.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <blockquote> <p>需要说明的是,有些小伙伴会回复说,这个架构太简单了吧,太low了,什么网关啊,缓存啊,消息中间件啊,都没有。因为这篇主要聊API接口,所以我们聚焦这一点。</p> </blockquote> <h1>接口交互</h1> <p>前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。</p> <blockquote> <p>针对URL路径的restful风格,以及传入参数的公共请求头的要求(如:app_version,api_version,device等),这里就不介绍了,小伙伴们可以自行去了解,也比较简单。</p> </blockquote> <p>后端服务器如何实现把数据返回给前端?</p> <h1>返回格式</h1> <p>后端返回给前端我们一般用JSON体方式,定义如下:</p> <p>{</p> <pre><code> #返回状态码<br> code:integer,<br> #返回信息描述<br> message:string,<br> #返回值<br> data:object<br>}<br><span aria-hidden="true"></span></code></pre> <p><strong>CODE状态码</strong></p> <p>code返回状态码,一般小伙伴们是在开发的时候需要什么,就添加什么。</p> <p>如接口要返回用户权限异常,我们加一个状态码为101吧,下一次又要加一个数据参数异常,就加一个102的状态码。这样虽然能够照常满足业务,但状态码太凌乱了</p> <p>我们应该可以参考HTTP请求返回的状态码:下面是常见的HTTP状态码:</p> <pre><code>200 - 请求成功<br>301 - 资源(网页等)被永久转移到其它URL<br>404 - 请求的资源(网页等)不存在<br>500 - 内部服务器错误<br><span aria-hidden="true"></span></code></pre> <p><img data-ratio="1" src="/upload/a09f6dcdc48939af504a2e50428141c.png" data-type="other" data-w="1" style="cursor: zoom-in;"></p> <p><br></p> <p>我们可以参考这样的设计,这样的好处就把错误类型归类到某个区间内,如果区间不够,可以设计成4位数。</p> <p><em aria-label="icon: copy"> <svg viewbox="64 64 896 896" focusable="false" data-icon="copy" width="1em" height="1em" fill="currentColor" aria-hidden="true"> <path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"></path> </svg></em></p> <pre><code>#1000~1999 区间表示参数错误<br>#2000~2999 区间表示用户错误<br>#3000~3999 区间表示接口异常<br><span aria-hidden="true"></span></code></pre> <p>这样前端开发人员在得到返回值后,根据状态码就可以知道,大概什么错误,再根据message相关的信息描述,可以快速定位。</p> <p><strong>Message</strong></p> <p>这个字段相对理解比较简单,就是发生错误时,如何友好的进行提示。一般的设计是和code状态码一起设计,如</p> <p><img data-ratio="0.428125" src="/upload/84ab9cb3bee4677ffb5e1af7f296ac21.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>再在枚举中定义,状态码</p> <p><img data-ratio="0.9296875" src="/upload/5ce74a08b3c3797ad154acc8b1193a2f.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>状态码和信息就会一一对应,比较好维护。</p> <p><strong>Data</strong></p> <p>返回数据体,JSON格式,根据不同的业务又不同的JSON体。</p> <p>我们要设计一个返回体类Result</p> <p><img data-ratio="0.54375" src="/upload/d1e907aff1ec2de02205e5f0530a1e47.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <h1>控制层Controller</h1> <p>我们会在controller层处理业务请求,并返回给前端,以order订单为例</p> <p><img data-ratio="0.50625" src="/upload/9c7862f956d4c350cb85f1de2098cb7f.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>我们看到在获得order对象之后,我们是用的Result构造方法进行包装赋值,然后进行返回。小伙伴们有没有发现,构造方法这样的包装是不是很麻烦,我们可以优化一下。</p> <h1>美观美化</h1> <p>我们可以在Result类中,加入静态方法,一看就懂</p> <p><img data-ratio="0.8734375" src="/upload/a9038fa25ef093a2dab9bc92d2ce66b1.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>那我们来改造一下Controller</p> <p><br></p> <p><img data-ratio="0.50625" src="/upload/a207ea06007ff4184fcbe2853271d1c9.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>代码是不是比较简洁了,也美观了。</p> <h1>优雅优化</h1> <p>上面我们看到在Result类中增加了静态方法,使得业务处理代码简洁了。但小伙伴们有没有发现这样有几个问题:</p> <blockquote> <p>1、每个方法的返回都是Result封装对象,没有业务含义</p> <p>2、在业务代码中,成功的时候我们调用Result.success,异常错误调用Result.failure。是不是很多余</p> <p>3、上面的代码,判断id是否为null,其实我们可以使用validate做校验,没有必要在方法体中做判断。</p> </blockquote> <p>我们最好的方式直接返回真实业务对象,最好不要改变之前的业务方式,如下图</p> <p><img data-ratio="0.4515625" src="/upload/8f2755459a888ac5e999303bcc12ff21.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>这个和我们平时的代码是一样的,非常直观,直接返回order对象,这样是不是很完美。那实现方案是什么呢?</p> <h1>实现方案</h1> <p>小伙伴们怎么去实现是不是有点思路,在这个过程中,我们需要做几个事情</p> <blockquote> <p>1、自定义一个注解@ResponseResult,表示这个接口返回的值需要包装一下</p> <p>2、拦截请求,判断此请求是否需要被@ResponseResult注解</p> <p>3、核心步骤就是实现接口ResponseBodyAdvice和@ControllerAdvice,判断是否需要包装返回值,如果需要,就把Controller接口的返回值进行重写。</p> </blockquote> <p><strong>注解类</strong></p> <p>用来标记方法的返回值,是否需要包装</p> <p><img data-ratio="0.3609375" src="/upload/15c2531ebe24643d64891d61a34bef83.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p><strong>拦截器</strong></p> <p>拦截请求,是否此请求返回的值需要包装,其实就是运行的时候,解析@ResponseResult注解</p> <p><img data-ratio="0.5296875" src="/upload/cd4aa64e2d2259054ffdc4ac985edd3b.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>此代码核心思想,就是获取此请求,是否需要返回值包装,设置一个属性标记。</p> <p><strong>重写返回体</strong></p> <p><img data-ratio="0.45625" src="/upload/4b94c09eed0186eb613709bb6a4dabb5.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>上面代码就是判断是否需要返回值包装,如果需要就直接包装。这里我们只处理了正常成功的包装,如果方法体报异常怎么办?处理异常也比较简单,只要判断body是否为异常类。</p> <p><img data-ratio="0.26851851851851855" src="/upload/7b8cfb182fc47d07f4ec3c91759e2c37.png" data-type="other" data-w="1080" style="cursor: zoom-in;"></p> <p><br></p> <p><strong>重写Controller</strong></p> <p><img data-ratio="0.4671875" src="/upload/52f4b61493e6f8a4dd6f2063c877d8a.png" data-type="other" data-w="640" style="cursor: zoom-in;"></p> <p><br></p> <p>在控制器类上或者方法体上加上自定义注解@ResponseResult,这样就ok了,简单吧。到此返回的设计思路完成,是不是又简洁,又优雅。</p> <p>这个方案还有没有别的优化空间,当然是有的。如:每次请求都要反射一下,获取请求的方法是否需要包装,其实可以做个缓存,不需要每次都需要解析。当然整体思路了解,小伙伴们就可以在此基础上面再自行扩展。<br><br></p> <p>作者:马士兵老师<br>来源:简书</p> <section powered-by="xiumi.us" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;text-align: center;color: rgb(62, 71, 83);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="margin-right: 16px;margin-left: 16px;max-width: 100%;min-height: 1em;letter-spacing: 1.5px;line-height: 1.5em;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(77, 168, 238);box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 12px;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;font-size: 17px;letter-spacing: 0.544px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;">— 本文结束 —</span></strong></span></strong></span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-right: 16px;margin-left: 16px;max-width: 100%;min-height: 1em;letter-spacing: 1.5px;line-height: 1.5em;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <section data-mpa-template="t" mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-id="93709" mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section mpa-from-tpl="t" style="padding: 5px;max-width: 100%;border-style: solid;border-width: 2px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section mpa-from-tpl="t" style="padding: 15px;max-width: 100%;border-style: dashed;border-width: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span data-role="width" data-width="80%" style="max-width: 100%;display: inline-block;width: 425.215px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img border="0" class="__bg_gif" data-ratio="0.08658008658008658" data-type="gif" data-w="462" data-width="80%" height="" title="" width="80%" src="/upload/b0b0d65a8077fe5fc54ccd945913d458.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 340.156px !important;"></span> </section> <p style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;text-align: justify;letter-spacing: 1.5px;line-height: normal;color: rgb(136, 136, 136);font-size: 13px;box-sizing: border-box !important;overflow-wrap: break-word !important;">● <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5NDg3MjAwMQ==&mid=2457103446&idx=1&sn=0d0c0cb83235675963be01cb7b8ddb7b&chksm=87c8c4f8b0bf4dee91ab4a8041363afc0839df2dbc219e2c0938ed2e53d13af4a966121314f4&scene=21#wechat_redirect" textvalue="漫谈设计模式在 Spring 框架中的良好实践" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">漫谈设计模式在 Spring 框架中的良好实践</a></p> <p style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;text-align: justify;letter-spacing: 1.5px;line-height: normal;color: rgb(136, 136, 136);font-size: 13px;box-sizing: border-box !important;overflow-wrap: break-word !important;">● <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5NDg3MjAwMQ==&mid=2457103280&idx=1&sn=f3bd921a2f88a9a3440c0ff1e03d1bd9&chksm=87c8c31eb0bf4a0820a572626141cfb1fc87ea1c7072a16f61992d67d21d9b61b556de2d6cd7&scene=21#wechat_redirect" textvalue="颠覆微服务认知:深入思考微服务的七个主流观点" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">颠覆微服务认知:</a><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5NDg3MjAwMQ==&mid=2457103280&idx=1&sn=f3bd921a2f88a9a3440c0ff1e03d1bd9&chksm=87c8c31eb0bf4a0820a572626141cfb1fc87ea1c7072a16f61992d67d21d9b61b556de2d6cd7&scene=21#wechat_redirect" textvalue="颠覆微服务认知:深入思考微服务的七个主流观点" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">深入思考微服务的七个主流观点</a><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;text-align: justify;letter-spacing: 1.5px;line-height: normal;color: rgb(136, 136, 136);font-size: 13px;box-sizing: border-box !important;overflow-wrap: break-word !important;">● <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5NDg3MjAwMQ==&mid=2457103129&idx=1&sn=3e0386d5f6f3b706e6bd72195648e34e&chksm=87c8c3b7b0bf4aa1530354a3b1507b33c5033db70e4c61328e9ed68118e24212a36d763671d8&scene=21#wechat_redirect" textvalue="人人都是 API 设计者" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">人人都是 API 设计者</a></p> <p style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;text-align: justify;letter-spacing: 1.5px;line-height: normal;color: rgb(136, 136, 136);font-size: 13px;box-sizing: border-box !important;overflow-wrap: break-word !important;">● <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5NDg3MjAwMQ==&mid=2457103538&idx=1&sn=9261bb2b742fd41168889738e0377a9c&chksm=87c8c41cb0bf4d0a5401fac49320faebfc4ac5ad7711d39758ecb0fd9df43354d19aa5aa57e3&scene=21#wechat_redirect" textvalue="一文讲透微服务下如何保证事务的一致性" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">一文讲透微服务下如何保证事务的一致性</a><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;text-align: justify;letter-spacing: 1.5px;line-height: normal;color: rgb(136, 136, 136);font-size: 13px;box-sizing: border-box !important;overflow-wrap: break-word !important;">● <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA5NDg3MjAwMQ==&mid=2457103584&idx=1&sn=007226d602709eb3c840a0539dae678b&chksm=87c8c44eb0bf4d58e9aaba3221c0d9f61556f4a199691b428dd322231ab450023e748cfc620a&scene=21#wechat_redirect" textvalue="要黑盒测试微服务内部服务间调用,我该如何实现?" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">要黑盒测试微服务内部服务间调用,我该如何实现?</a><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> </section> </section> </section> </section> <p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br mpa-from-tpl="t" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> </section> </section> </section> </section> </section> </section> <section powered-by="xiumi.us" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;text-align: center;color: rgb(62, 71, 83);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;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;"><img data-ratio="0.3708738" data-type="png" data-w="515" src="/upload/328ca60e65e37255ffc9d00bfba58c3c.png" style="vertical-align: middle;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 515px !important;"></p> <p style="margin-right: 16px;margin-left: 16px;max-width: 100%;min-height: 1em;letter-spacing: 1.5px;line-height: 1.5em;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;display: inline-block;width: 574px;vertical-align: top;border-style: solid;border-width: 1px;border-radius: 0px;border-color: rgb(225, 238, 226);background-color: rgba(191, 224, 216, 0.58);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="padding: 10px;max-width: 100%;display: inline-block;width: 572px;vertical-align: top;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="max-width: 100%;font-size: 15px;color: rgb(46, 101, 91);line-height: 1.8;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;">关注我,回复 「加群」 加入各种主题讨论群。</span></p> </section> </section> </section> </section> </section> <section powered-by="xiumi.us" style="margin-top: 10px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;background-color: rgb(147, 195, 186);height: 2px;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> </section> </section> </section> </section> </section> </section> </section> <section powered-by="xiumi.us" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;text-align: center;color: rgb(62, 71, 83);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-right: 16px;margin-left: 16px;max-width: 100%;min-height: 1em;letter-spacing: 1.5px;line-height: 1.5em;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 12px;letter-spacing: 1px;color: rgb(0, 0, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">对「服务端思维」有期待,请在文末点个<span style="max-width: 100%;color: rgb(198, 19, 25);box-sizing: border-box !important;overflow-wrap: break-word !important;">在看</span></span></strong></p> <p style="margin-right: 16px;margin-left: 16px;max-width: 100%;min-height: 1em;letter-spacing: 1.5px;line-height: 1.5em;font-family: -apple-system-font, BlinkMacSystemFont, Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 12px;letter-spacing: 1px;color: rgb(0, 0, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">喜欢这篇文章,欢迎<span style="max-width: 100%;color: rgb(198, 19, 25);box-sizing: border-box !important;overflow-wrap: break-word !important;">转发、分享</span>朋友圈</span></strong></p> </section> </section> </section> <p style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;font-size: 14px;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <section data-role="outer" label="Powered by 135editor.com" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: rgb(63, 63, 63);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-role="outer" label="Powered by 135editor.com" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-tools="135编辑器" data-id="94783" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;display: flex;justify-content: flex-end;align-items: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;width: 20px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;width: 20px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <img data-ratio="1.037037037037037" data-type="png" data-w="27" data-width="100%" src="/upload/6087772642bee9a4b8104517c36c9aa7.png" style="display: block;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 20px !important;"> </section> </section> <section style="max-width: 100%;display: inline-block;text-align: right;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-brushtype="text" style="margin-bottom: -15px;max-width: 100%;letter-spacing: 2px;transform: rotate(0deg);color: rgb(0, 0, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;"> 在看点这里 </section> </section> <section style="max-width: 100%;width: 22px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 15px;max-width: 100%;width: 22px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <img class="__bg_gif" data-ratio="1" data-type="gif" data-w="100" data-width="100%" src="/upload/8b34c0e8ddcf33f56770ae9f44c95ce1.png" style="display: block;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 22px !important;"> </section> </section> </section> </section> </section> </section>
作者:微信小助手
<section powered-by="xiumi.us" style="margin-top: 30px;margin-bottom: 20px;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;white-space: normal;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;"> 文章来源 LeoLan's Blog</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;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;white-space: normal;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="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;white-space: normal;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;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;white-space: normal;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-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;width: 229.594px;overflow-wrap: break-word !important;"> <img data-ratio="0.2106667" data-w="750" width="100%" data-type="gif" class="__bg_gif" src="/upload/9a990984a6b8f7f7b3c167d0a48d8599.png" style="box-sizing: border-box;vertical-align: middle;overflow-wrap: break-word !important;visibility: visible !important;width: 229.594px !important;"> </section> </section> </section> <section style="max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: middle;width: 143.5px;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;margin-bottom: 0.5em;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;background-color: rgb(29, 29, 29);height: 1px;overflow-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> </section> </section> </section> <section powered-by="xiumi.us" style="max-width: 100%;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;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;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;font-size: 14px;letter-spacing: 0.05em;color: rgb(89, 89, 89);line-height: 2;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br> </figure> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;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;font-size: 15px;line-height: 2;color: rgb(89, 89, 89);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;margin-bottom: 1.2em !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-ratio="1.3215767634854771" data-type="png" data-w="482" src="/upload/403034b667fb8399ce56871b4b1863f.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 482px !important;visibility: visible !important;"></p> <h3 style="max-width: 100%;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;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: break-word !important;">一、TCP/IP模型</span></h3> <section style="max-width: 100%;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: left;line-height: 1.75em;margin-bottom: 1.2em !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-size: 15px;box-sizing: border-box !important;overflow-wrap: break-word !important;">TCP/IP协议模型(Transmission Control Protocol/Internet Protocol),包含了一系列构成互联网基础的网络协议,是Internet的核心协议。</span> </section> <section style="max-width: 100%;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: left;line-height: 1.75em;margin-bottom: 1.2em !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-size: 15px;box-sizing: border-box !important;overflow-wrap: break-word !important;">基于TCP/IP的参考模型将协议分成四个层次,它们分别是链路层、网络层、传输层和应用层。下图表示TCP/IP模型与OSI模型各层的对照关系。</span> </section> <p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC",
作者:微信小助手
<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;"><span style="color: #888888;">点击上方蓝色“</span></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;"><span style="color: #888888;">”,选择“设为星标”</span></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: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><img data-ratio="0.6666666666666666" src="/upload/572ac3665e381361a710a6b931b58076.jpg" data-type="jpeg" data-w="960"></p> <p mpa-paragraph-type="body" style="white-space: normal;letter-spacing: 0.544px;text-align: right;background-color: rgb(255, 255, 255);"><span style="font-size: 10px;color: rgb(136, 136, 136);">来源:老顾聊技术</span></p> <p mpa-paragraph-type="body" style="white-space: normal;letter-spacing: 0.544px;text-align: right;background-color: rgb(255, 255, 255);"><span style="font-size: 10px;color: rgb(136, 136, 136);">toutiao.com/i6682672464708764174</span></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;"> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 一、前言 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 二、分布式ID的几种生成方案 </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 2.1、UUID </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 2.2、MySQL主键自增 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 2.3、MySQL多实例主键自增 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 2.4、雪花snowflake算法 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 2.5、Redis生成方案 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 2.6、小结 </section></li> </ul> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 三、一线大厂是如何设计的呢? </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 3.1、改造数据库主键自增 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 3.2、竞争问题 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 3.3、突发阻塞问题 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 3.4、双buffer方案 </section></li> </ul> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 四、总结 </section></li> </ul> <hr data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;height: 1px;border-width: initial;border-style: none;border-color: initial;text-align: center;background-image: linear-gradient(to right, rgba(93, 186, 133, 0), rgba(93, 186, 133, 0.75), rgba(93, 186, 133, 0));"> <h1 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 24px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p4QGb38srSbibgab1mxM7o3JnPgzbBQG6VgaIt8z4AR4Z19ib0lKwUhBQ/640?wx_fmt=png");background-position: center top;background-repeat: no-repeat;background-size: 75px;line-height: 95px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="font-size: 20px;color: #48b378;border-bottom: 2px solid #2e7950;">一、前言</span></h1> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订单表。因为数据量巨大一张表无法承接,就会对其进行分库分表。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题,永不迁移数据和避免热点的文章中要求需要唯一ID的特性:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 整个系统ID唯一 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> ID是数字类型,而且是趋势递增的 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> ID简短,查询效率快 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">什么是递增?</strong> 如:第一次生成的ID为12,下一次生成的ID是13,再下一次生成的ID是14。这个就是生成ID递增。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">什么是趋势递增?</strong> 如:在一段时间内,生成的ID是递增的趋势。如:再一段时间内生成的ID在【0,1000】之间,过段时间生成的ID在【1000,2000】之间。但在【0-1000】区间内的时候,ID生成有可能第一次是12,第二次是10,第三次是14。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那有什么方案呢?往下看!</p> <h1 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 24px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p4QGb38srSbibgab1mxM7o3JnPgzbBQG6VgaIt8z4AR4Z19ib0lKwUhBQ/640?wx_fmt=png");background-position: center top;background-repeat: no-repeat;background-size: 75px;line-height: 95px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="font-size: 20px;color: #48b378;border-bottom: 2px solid #2e7950;">二、分布式ID的几种生成方案</span></h1> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2.1、UUID</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个方案是小伙伴们第一个能过考虑到的方案</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 代码实现简单。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 本机生成,没有性能问题 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 因为是全球唯一的ID,所以迁移数据容易 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">缺点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 每次生成的ID是无序的,无法保证趋势递增 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> UUID的字符串存储,查询效率慢 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 存储空间大 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> ID本事无业务含义,不可读 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">应用场景:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 类似生成token令牌的场景 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 不适用一些要求有趋势递增的ID场景 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此UUID方案是不适用老顾的需求。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2.2、MySQL主键自增</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个方案就是利用了MySQL的主键自增auto_increment,默认每次ID加1。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 数字化,id递增 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 查询效率高 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 具有一定的业务可读 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">缺点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 存在单点问题,如果mysql挂了,就没法生成iD了 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 数据库压力大,高并发抗不住 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2.3、MySQL多实例主键自增</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个方案就是解决mysql的单点问题,在auto_increment基本上面,设置step步长</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.821875" src="/upload/d14d68138474fa9ed0d09c23b8423a18.jpg" data-type="jpeg" data-w="640" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">每台的初始值分别为1,2,3...N,步长为N(这个案例步长为4)</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 解决了单点问题 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">缺点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 一旦把步长定好后,就无法扩容;而且单个数据库的压力大,数据库自身性能无法满足高并发 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">应用场景:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 数据不需要扩容的场景 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此方案也不满足老顾的需求,因为不方便扩容(记住这个方案,嘿嘿)</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2.4、雪花snowflake算法</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个算法网上介绍了很多,这里就不详细介绍。雪花算法生成64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.21302998965873837" src="/upload/b3818ad99df372348044d127b085f7bc.jpg" data-type="jpeg" data-w="967" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 1位标识符:始终是0 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 41位时间戳:41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截 )得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 10位机器标识码:可以部署在1024个节点,如果机器分机房(IDC)部署,这10位可以由 5位机房ID + 5位机器ID 组成 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 12位序列:毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 此方案每秒能够产生409.6万个ID,性能快 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 灵活度高,可以根据业务需求,调整bit位的划分,满足不同的需求 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">缺点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 依赖机器的时钟,如果服务器时钟回拨,会导致重复ID生成 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在分布式场景中,服务器时钟回拨会经常遇到,一般存在10ms之间的回拨;小伙伴们就说这点10ms,很短可以不考虑吧。但此算法就是建立在毫秒级别的生成方案,一旦回拨,就很有可能存在重复ID。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此方案暂不符合老顾的需求(嘿嘿,看看怎么优化这个方案,小伙伴们先记住)</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2.5、Redis生成方案</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">利用redis的incr原子性操作自增,一般算法为:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">年份 + 当天距当年第多少天 + 天数 + 小时 + redis自增</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 有序递增,可读性强 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">缺点:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 占用带宽,每次要向redis进行请求 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">整体测试了这个性能如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92pia02sQicAdU7r4icX4N0cF37m9r4Bmpxj6OFpPhShOgr4avmx7vU4FWvg/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">需求:同时<span style="line-height: 26px;">10</span>万个请求获取ID1、并发执行完耗时:<span style="line-height: 26px;">9</span>s左右<br><span style="line-height: 26px;">2</span>、单任务平均耗时:<span style="line-height: 26px;">74</span>ms<br><span style="line-height: 26px;">3</span>、单线程最小耗时:不到<span style="line-height: 26px;">1</span>ms<br><span style="line-height: 26px;">4</span>、单线程最大耗时:<span style="line-height: 26px;">4.1</span>s<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">性能还可以,如果对性能要求不是太高的话,这个方案基本符合老顾的要求。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但不完全符合业务,希望id从 1 开始趋势递增。(当然算法可以调整为 就一个 redis自增,不需要什么年份,多少天等)。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2.6、小结</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">以上介绍了常见的几种分布式ID生成方案。一线大厂的分布式ID方案绝没有这个简单,他们对高并发,高可用的要求很高。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如Redis方案中,每次都要去Redis去请求,有网络请求耗时,并发强依赖了Redis。这个设计是有风险的,一旦Redis挂了,整个系统不可用。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而且一线大厂也会考虑到ID安全性的问题,如:Redis方案中,用户是可以预测下一个ID号是多少,因为算法是递增的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样的话竞争对手第一天中午12点下个订单,就可以看到平台的订单ID是多少,第二天中午12点再下一单,又平台订单ID到多少。这样就可以猜到平台1天能产生多少订单了,这个是绝对不允许的,公司绝密啊。</p> <h1 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 24px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p4QGb38srSbibgab1mxM7o3JnPgzbBQG6VgaIt8z4AR4Z19ib0lKwUhBQ/640?wx_fmt=png");background-position: center top;background-repeat: no-repeat;background-size: 75px;line-height: 95px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="font-size: 20px;color: #48b378;border-bottom: 2px solid #2e7950;">三、一线大厂是如何设计的呢?</span></h1> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">一线大厂的设计思路其实和小伙伴们思路差不多,只是多想了1~2层,设计上面多了1~2个环节。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">3.1、改造数据库主键自增</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">上述我们介绍了利用数据库的自增主键的特性,可以实现分布式ID;这个ID比较简短明了,适合做userId,正好符合如何永不迁移数据和避免热点? 根据服务器指标分配数据量(揭秘篇)文章中的ID的需求。但这个方案有严重的问题:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 一旦步长定下来,不容易扩容 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 数据库压力山大 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">小伙伴们看看怎么优化这个方案。先看数据库压力大,为什么压力大?是因为我们每次获取ID的时候,都要去数据库请求一次。那我们可以不可以不要每次去取?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">思路我们可以请求数据库得到ID的时候,可设计成获得的ID是一个ID区间段。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.9046875" src="/upload/76a69119f8c218174f1ec3be1aa0ade5.jpg" data-type="jpeg" data-w="640" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们看上图,有张ID规则表:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">1、id表示为主键,无业务含义。</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">2、biz_tag为了表示业务,因为整体系统中会有很多业务需要生成ID,这样可以共用一张表维护</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">3、max_id表示现在整体系统中已经分配的最大ID</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">4、desc描述</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">5、update_time表示每次取的ID时间</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们再来看看整体流程:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">1、【用户服务】在注册一个用户时,需要一个用户ID;会请求【生成ID服务(是独立的应用)】的接口</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">2、【生成ID服务】会去查询数据库,找到user_tag的id,现在的max_id为0,step=1000</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">3、【生成ID服务】把max_id和step返回给【用户服务】;并且把max_id更新为max_id = max_id + step,即更新为1000</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">4、【用户服务】获得max_id=0,step=1000;</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">5、 这个用户服务可以用ID=【max_id + 1,max_id+step】区间的ID,即为【1,1000】</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">6、【用户服务】会把这个区间保存到jvm中</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">7、【用户服务】需要用到ID的时候,在区间【1,1000】中依次获取id,可采用AtomicLong中的getAndIncrement方法。</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">8、如果把区间的值用完了,再去请求【生产ID服务】接口,获取到max_id为1000,即可以用【max_id + 1,max_id+step】区间的ID,即为【1001,2000】</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个方案就非常完美的解决了数据库自增的问题,而且可以自行定义max_id的起点,和step步长,非常方便扩容。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而且也解决了数据库压力的问题,因为在一段区间内,是在jvm内存中获取的,而不需要每次请求数据库。即使数据库宕机了,系统也不受影响,ID还能维持一段时间。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">3.2、竞争问题</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">以上方案中,如果是多个用户服务,同时获取ID,同时去请求【ID服务】,在获取max_id的时候会存在并发问题。</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">如用户服务A,取到的max_id=1000 ;用户服务B取到的也是max_id=1000,那就出现了问题,Id重复了。那怎么解决?</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其实方案很多,加分布式锁,保证同一时刻只有一个用户服务获取max_id。当然也可以用数据库自身的锁去解决。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.15" src="/upload/2a023f45e2fb8c0012fba47bd6e53042.jpg" data-type="jpeg" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">利用事务方式加行锁,上面的语句,在没有执行完之前,是不允许第二个用户服务请求过来的,第二个请求只能阻塞。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">3.3、突发阻塞问题</span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.80625" src="/upload/9e4c694c9fc02609e3f5c94140b233ed.jpg" data-type="jpeg" data-w="640" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">上图中,多个用户服务获取到了各自的ID区间,在高并发场景下,ID用的很快,如果3个用户服务在某一时刻都用完了,同时去请求【ID服务】。因为上面提到的竞争问题,所有只有一个用户服务去操作数据库,其他二个会被阻塞。</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">小伙伴就会问,有这么巧吗?同时ID用完。我们这里举的是3个用户服务,感觉概率不大;如果是100个用户服务呢?概率是不是一下子大了。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">出现的现象就是一会儿突然系统耗时变长,一会儿好了,就是这个原因导致的,怎么去解决?</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p9PkzXaOVe2vpPoibT7c5So9qmCTZMzysWcUrJAb6EIkicYicOppBhu6cA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">3.4、双buffer方案</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在一般的系统设计中,双buffer会经常看到,怎么去解决上面的问题也可以采用双buffer方案。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.6171875" src="/upload/86ad10ed2f7b1c3eb4c1fb48d12d6d07.jpg" data-type="jpeg" data-w="640" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在设计的时候,采用双buffer方案,上图的流程:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">1、当前获取ID在buffer1中,每次获取ID在buffer1中获取</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">2、当buffer1中的Id已经使用到了100,也就是达到区间的10%</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">3、达到了10%,先判断buffer2中有没有去获取过,如果没有就立即发起请求获取ID线程,此线程把获取到的ID,设置到buffer2中。</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">4、如果buffer1用完了,会自动切换到buffer2</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">5、buffer2用到10%了,也会启动线程再次获取,设置到buffer1中</p> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">6、依次往返</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">双buffer的方案,小伙伴们有没有感觉很酷,这样就达到了业务场景用的ID,都是在jvm内存中获得的,从此不需要到数据库中获取了。允许数据库宕机时间更长了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">因为会有一个线程,会观察什么时候去自动获取。两个buffer之间自行切换使用。就解决了突发阻塞的问题。</p> <h1 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 24px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffBdQ2u7mibDAK9aMrPQX92p4QGb38srSbibgab1mxM7o3JnPgzbBQG6VgaIt8z4AR4Z19ib0lKwUhBQ/640?wx_fmt=png");background-position: center top;background-repeat: no-repeat;background-size: 75px;line-height: 95px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="font-size: 20px;color: #48b378;border-bottom: 2px solid #2e7950;">四、总结</span></h1> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此方案是某团使用的分布式ID算法,小伙伴们如果想了解更深,可以去网上搜下,这里应该介绍了比较详细了。</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">当然此方案美团还做了一些别的优化,监控ID使用频率,自动设置步长step,从而达到对ID节省使用。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但此ID存在一定的问题,就是太过连续,竞争对手可以预测,不适合订单ID。</p> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: flex;flex-flow: row nowrap;margin: 10px 0%;box-sizing: border-box;"> <section style="display: inline-block;vertical-align: middle;width: auto;flex: 100 100 0%;align-self: center;height: auto;box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;transform: translate3d(0px, 0px, 1px) rotateY(180deg);box-sizing: border-box;" powered-by="xiumi.us"> <section style="background-color: rgba(194, 150, 255, 0.45);height: 1px;box-sizing: border-box;line-height: 0;"> <br> </section> </section> </section> </section> </section> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section style="margin-top: 10px;margin-bottom: 10px;" mpa-from-tpl="t"> <p style="width:40%;margin-right: auto;margin-left: auto;"><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;"><span style="color: #8C8C8C;"><br></span></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="2247491529_1" data-recommend-article-time="1608856140" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZ2bCHibwhRK9Oyggs4LRs7CUWOhDeibCugkRicLGeE52BnYib31MUZxQ20uUVTpVkO9FOwcLv8Qf6fuw/0?wx_fmt=jpeg" data-recommend-article-title="性能测试过程中-磁盘 IO 高分析方法" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491529&idx=1&sn=84675561e0bc323013c217cbbe55cef9&chksm=97b781d1a0c008c7dc6f67167ada7f97dfced3bec1a840ff56ce919ed5560b30c81d478d6d83#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491529&idx=1&sn=84675561e0bc323013c217cbbe55cef9&chksm=97b781d1a0c008c7dc6f67167ada7f97dfced3bec1a840ff56ce919ed5560b30c81d478d6d83&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="">性能测试过程中-磁盘 IO 高分析方法</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247491514_1" data-recommend-article-time="1608769740" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZST2D75m068PxZLTCl2cKN3Y56Fxia9560q2QRffJqyS0FN2fFicjSSqBm9gj5icevdnT11IriamyurQ/0?wx_fmt=jpeg" data-recommend-article-title="新技能 MyBatis 千万数据表,快速分页!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491514&idx=1&sn=ad04b0f348aaf2a7f2083b5375232e22&chksm=97b781a2a0c008b40c48943e0fbb83e4e347fac53815410593b8aee1534def74f2054ab18886#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491514&idx=1&sn=ad04b0f348aaf2a7f2083b5375232e22&chksm=97b781a2a0c008b40c48943e0fbb83e4e347fac53815410593b8aee1534def74f2054ab18886&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="">新技能 MyBatis 千万数据表,快速分页!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247491495_1" data-recommend-article-time="1608683340" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZKLWDC7erZrkBY1WNenNaLfx3A4oEFgjBlhu5gMeNJlsOozunorC4gYrN8HXXhTMNuTnAHDuk6ew/0?wx_fmt=jpeg" data-recommend-article-title="太难了~面试官让我结合案例讲讲自己对Spring事务传播行为的理解!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491495&idx=1&sn=656837a003ef3eb6c379fc9b48ce9fc4&chksm=97b781bfa0c008a96e3c981fe9e2e32e68fef796f154d7afba3659b32f20cf78ed46237b8544#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491495&idx=1&sn=656837a003ef3eb6c379fc9b48ce9fc4&chksm=97b781bfa0c008a96e3c981fe9e2e32e68fef796f154d7afba3659b32f20cf78ed46237b8544&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="">太难了~面试官让我结合案例讲讲自己对Spring事务传播行为的理解!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247491405_1" data-recommend-article-time="1608596940" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtbqhGuCiaeplvksQZmI8vgz1Q6Z8cSQPhuwLz9XY6At4H4DqumnWqJE3swfQiaBnXTmc2HZ9KdfMg6Q/0?wx_fmt=jpeg" data-recommend-article-title="思考个问题,Java 最多支持多少个线程?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491405&idx=1&sn=c94dedd158b1db3e502aafc1fa62022a&chksm=97b78155a0c00843669a2979fcf9a3248b3b014980ab660925149a75c5365dc5f6cf7ee0323a#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247491405&idx=1&sn=c94dedd158b1db3e502aafc1fa62022a&chksm=97b78155a0c00843669a2979fcf9a3248b3b014980ab660925149a75c5365dc5f6cf7ee0323a&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="">思考个问题,Java 最多支持多少个线程?</p> </section></a> </section> </section> </section> </section> <p><br></p> <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> <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: #8C8C8C;"><br></span></p> <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: #8C8C8C;">一起进大厂,每日学干货</span></p> <p data-lake-id="95edb2040117493cdb083bce066a86c1" 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: #8C8C8C;">关注我,不迷路</span></p> <p style="text-align: center;"><img data-ratio="1" src="/upload/7b57b8ce8a74c4aebec729d708bae61d.png" data-type="png" data-w="258"></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-role="paragraph" style="border-width: 0px;border-style: none;border-color: initial;" mpa-from-tpl="t"> <p><br></p> <section style="white-space: normal;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;box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;border-color: rgb(249, 110, 87);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;">点击“阅读原文”,领取 2020 年<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> </section>
作者:微信小助手
<section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="text-align: center;margin: 10px 0%;box-sizing: border-box;"> <section style="display: inline-block;width: 100%;vertical-align: top;border-width: 1px;border-radius: 8px;border-style: solid;border-color: rgb(89, 89, 89);overflow: hidden;box-shadow: rgb(89, 89, 89) 0px 0px 0px;background-color: rgba(255, 255, 255, 0);padding: 0px 0px 10px;height: auto;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section style="margin: 0px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;"> <section style="display: inline-block;vertical-align: top;width: auto;flex: 100 100 0%;height: auto;box-sizing: border-box;"> <section style="text-align: right;justify-content: flex-end;box-sizing: border-box;"> <section style="display: inline-block;width: 12px;height: 12px;vertical-align: top;overflow: hidden;line-height: 0.5;box-sizing: border-box;"> <section style="text-align: center;margin: 0px 0%;justify-content: center;box-sizing: border-box;"> <section style="max-width: 100%;vertical-align: middle;display: inline-block;line-height: 0;box-sizing: border-box;"> <img src="/upload/c8341cb813d261ea0e254aa08145b10f.png" data-type="svg" style="vertical-align: middle;max-width: 100%;width: 300px;height: 100%;box-sizing: border-box;"> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: top;width: auto;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;background-color: rgb(89, 89, 89);border-width: 0px;border-radius: 0px 0px 10px 10px;border-style: none;border-color: rgb(62, 62, 62);overflow: hidden;align-self: flex-start;z-index: 2;margin: 0px;box-shadow: rgb(0, 0, 0) 0px 0px 0px;box-sizing: border-box;"> <section style="text-align: justify;font-size: 15px;color: rgb(255, 255, 255);padding: 0px 10px;line-height: 2;letter-spacing: 3px;box-sizing: border-box;"> <strong>送福利啦</strong> </section> </section> <section style="display: inline-block;vertical-align: top;width: auto;flex: 100 100 0%;height: auto;align-self: flex-start;z-index: 2;margin: 0px;line-height: 0;letter-spacing: 0px;box-sizing: border-box;"> <section style="text-align: left;justify-content: flex-start;box-sizing: border-box;"> <section style="display: inline-block;width: 12px;height: 12px;vertical-align: top;overflow: hidden;line-height: 0.5;box-sizing: border-box;"> <section style="text-align: center;margin: 0px 0%;box-sizing: border-box;"> <section style="max-width: 100%;vertical-align: middle;display: inline-block;line-height: 0;box-sizing: border-box;"> <img src="/upload/9e0e3d7a83c5d44814adeaf9bad13011.png" data-type="svg" style="vertical-align: middle;max-width: 100%;width: 300px;height: 100%;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> </section> <section style="text-align: center;"> <section style="display: inline-block;width: 100%;vertical-align: top;box-shadow: rgb(0, 0, 0) 0px 0px 0px;padding: 15px 10px 0px;box-sizing: border-box;"> <section style="text-align: justify;font-size: 15px;line-height: 1.8;color: rgb(106, 106, 106);box-sizing: border-box;"> <p style="white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;"><span style="color: rgb(106, 106, 106);box-sizing: border-box;"><span style="color: rgb(106, 106, 106);box-sizing: border-box;"><span style="font-size: 13px;color: rgb(204, 204, 204);box-sizing: border-box;"><span style="color: rgb(106, 106, 106);box-sizing: border-box;"></span></span></span></span><span style="font-size: 13px;color: rgb(204, 204, 204);box-sizing: border-box;"><img data-cropselx1="0" data-cropselx2="552" data-cropsely1="0" data-cropsely2="413" data-ratio="0.6146993318485523" data-type="png" data-w="898" style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;vertical-align: middle;overflow-wrap: break-word !important;visibility: visible !important;width: 578px !important;height: auto !important;" src="/upload/40a65a87f1f72521ac15099533e3b382.png"></span></p> <p style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;word-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;"><span style="font-size: 14px;"><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(106, 106, 106);">关注</span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(255, 41, 65);"><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">鸿蒙技术社区</strong></span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(106, 106, 106);">,回复</span><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(255, 41, 65);">【鸿蒙】</span></strong><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(106, 106, 106);">送</span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;color: rgb(89, 89, 89);overflow-wrap: break-word !important;">价值</span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(255, 41, 65);"><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">399元</strong></span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;color: rgb(89, 89, 89);overflow-wrap: break-word !important;">的鸿蒙</span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(255, 41, 65);"><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">开发板套件</strong></span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(89, 89, 89);">(数量有限,先到先得)</span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(106, 106, 106);">,还可以</span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(255, 41, 65);"><strong>免费下载</strong></span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(106, 106, 106);">鸿蒙</span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(255, 41, 65);"><strong>入门资料</strong></span><span style="font-size: 14px;margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;color: rgb(106, 106, 106);">!</span></span></p> <p style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;word-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;"><br style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p style="margin: 0px 8px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: normal;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-align: center;line-height: 1.75em;"><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;overflow-wrap: break-word !important;">👇</span><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;color: rgb(255, 41, 65);"><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;letter-spacing: 1px;font-size: 14px;overflow-wrap: break-word !important;">扫码</span></strong></span><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;letter-spacing: 1px;font-size: 14px;color: rgb(89, 89, 89);overflow-wrap: break-word !important;">立刻关注</span></strong><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;overflow-wrap: break-word !important;">👇<br style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="1" data-s="300,640" src="/upload/4d329a1404dc5a45553747b6028bc5ef.jpg" data-type="jpeg" data-w="258" style=""></p> <p style="text-align: center;"><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;letter-spacing: 1px;font-size: 14px;color: rgb(89, 89, 89);overflow-wrap: break-word !important;">专注开源技术,共建鸿蒙生态</span></strong></p> </section> </section> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding: 10px 10px 0px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;" title=""> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="letter-spacing: 1px;">提到锁大家肯定有了解,像 Synchronized、ReentrantLock,在单进程情况下,多个线程访问同一资源,可以用它们来保证线程的安全性。</span></p> </section> <section style="clear: both;box-sizing: border-box;"> <section> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.6628865979381443" data-s="300,640" src="/upload/141dad9301d112af15168c4b3df66a53.png" data-type="png" data-w="970" style=""> </section> <section style="text-align: center;line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>图片来自 Pexels</em></span> <br> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不过目前互联网项目越来越多的项目采用集群部署,也就是分布式情况,这两种锁就有些不够用了。<br></span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">来两张图举例说明下,本地锁的情况下:</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img data-height="600" data-ratio="0.6891891891891891" src="/upload/6aa250c48c2929b02894d0035c852c56.png" data-type="png" data-w="592" data-width="800"></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">分布式锁情况下:</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img data-height="600" data-ratio="0.5166163141993958" src="/upload/4ac43193dd1fecdaae0dc7ce9038f2e1.png" data-type="png" data-w="662" data-width="800"></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">就其思想来说,就是一种“我全都要”的思想,所有服务都到一个统一的地方来取锁,只有取到锁的才能继续执行下去。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img data-height="600" data-ratio="0.539440203562341" src="/upload/4600b6031e41b613d40bd58574af9022.png" data-type="png" data-w="393" data-width="800"> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">说完思想,下面来说一下具体的实现。<br></span></p> <section style="line-height: normal;"> <br> </section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin: 0.5em 0px;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-color: rgb(89, 89, 89);border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">Redis 实现</p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为实现分布式锁,在 Redis 中存在 SETNX key value 命令,意为 set if not exists(如果不存在该 key,才去 set 值),就比如说是张三去上厕所,看厕所门锁着,他就不进去了,厕所门开着他才去。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img data-height="600" data-ratio="0.22657952069716775" src="/upload/e4ae9f0727d708fcccbfe231a073556e.png" data-type="png" data-w="459" data-width="800"> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以看到,第一次 set 返回了 1,表示成功,但是第二次返回 0,表示 set 失败,因为已经存在这个 key 了。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当然只靠 setnx 这个命令可以吗?当然是不行的,试想一种情况,张三在厕所里,但他在里面一直没有释放,一直在里面蹲着,那外面人想去厕所全部都去不了,都想锤死他了。<br></span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 同理,假设已经进行了加锁,但是因为宕机或者出现异常未释放锁,就造成了所谓的“死锁”。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img data-height="600" data-ratio="0.9063829787234042" src="/upload/b6febe0ddc5b36ca40452c6576f33cd5.png" data-type="png" data-w="235" data-width="800"> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">聪明的你们肯定早都想到了,为它设置过期时间不就好了,可以 SETEX key seconds value 命令,为指定 key 设置过期时间,单位为秒。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但这样又有另一个问题,我刚加锁成功,还没设置过期时间,Redis 宕机了不就又死锁了,所以说要保证原子性吖,要么一起成功,要么一起失败。<br></span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当然我们能想到的 Redis 肯定早都为你实现好了,在 Redis 2.8 的版本后,Redis 就为我们提供了一条组合命令 SET key value ex seconds nx,加锁的同时设置过期时间。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img data-height="600" data-ratio="0.2768670309653916" src="/upload/e09b736427be1ff246b7d10021adedc3.png" data-type="png" data-w="549" data-width="800"> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">就好比是公司规定每人最多只能在厕所呆 2 分钟,不管释放没释放完都得出来,这样就解决了“死锁”问题。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但这样就没有问题了吗?怎么可能。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">试想又一种情况,厕所门肯定只能从里面开啊,张三上完厕所后张四进去锁上门,但是外面人以为还是张三在里面,而且已经过了 3 分钟了,就直接把门给撬开了,一看里面却是张四,这就很尴尬啊。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">换成 Redis 就是说比如一个业务执行时间很长,锁已经自己过期了,别人已经设置了新的锁,但是当业务执行完之后直接释放锁,就有可能是删除了别人加的锁,这不是乱套了吗。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以在加锁时候,要设一个随机值,在删除锁时进行比对,如果是自己的锁,才删除。<br></span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">多说无益,烦人,直接上代码:</span> </section> <section> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;"><code style="white-space:pre-wrap;overflow-wrap: break-word;margin: 0px 2px;line-height: 18px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);overflow-x: auto;padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//基于jedis和lua脚本来实现</span><br>privatestaticfinal <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> LOCK_SUCCESS = <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"OK"</span>;<br>privatestaticfinal Long RELEASE_SUCCESS = <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1</span>L;<br>privatestaticfinal <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> SET_IF_NOT_EXIST = <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"NX"</span>;<br>privatestaticfinal <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> SET_WITH_EXPIRE_TIME = <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"PX"</span>;<br><br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Override</span><br>public <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> acquire() {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">try</span> {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">// 获取锁的超时时间,超过这个时间则放弃获取锁</span><br> long end = System.currentTimeMillis() + acquireTimeout;<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">// 随机生成一个 value</span><br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> requireToken = UUID.randomUUID().toString();<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">while</span> (System.currentTimeMillis() < end) {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> result = jedis<br> .<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span>(lockKey, requireToken, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span> (LOCK_SUCCESS.equals(result)) {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">return</span> requireToken;<br> }<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">try</span> {<br> Thread.sleep(<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">100</span>);<br> } <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">catch</span> (InterruptedException e) {<br> Thread.currentThread().interrupt();<br> }<br> }<br> } <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">catch</span> (Exception e) {<br> log.error(<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"acquire lock due to error"</span>, e);<br> }<br><br> returnnull;<br>}<br><br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Override</span><br>public boolean release(<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> identify) {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span> (identify == <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">null</span>) {<br> returnfalse;<br> }<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//通过lua脚本进行比对删除操作,保证原子性</span><br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span> script = <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"</span>;<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">Object</span> result = <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">new</span> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">Object</span>();<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">try</span> {<br> result = jedis.eval(script, Collections.singletonList(lockKey),<br> Collections.singletonList(identify));<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span> (RELEASE_SUCCESS.equals(result)) {<br> log.info(<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"release lock success, requestToken:{}"</span>, identify);<br> returntrue;<br> }<br> } <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">catch</span> (Exception e) {<br> log.error(<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"release lock due to error"</span>, e);<br> } <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">finally</span> {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span> (jedis != <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">null</span>) {<br> jedis.close();<br> }<br> }<br><br> log.info(<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"release lock failed, requestToken:{}, result:{}"</span>, identify, result);<br> returnfalse;<br>}<br></code></pre> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">思考:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">加锁和释放锁的原子性可以用 lua 脚本来保证,那锁的自动续期改如何实现呢?<br></span></p> <section style="line-height: normal;"> <br> </section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin: 0.5em 0px;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-color: rgb(89, 89, 89);border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">Redisson 实现</p> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redisson 顾名思义,Redis 的儿子,本质上还是 Redis 加锁,不过是对 Redis 做了很多封装,它不仅提供了一系列的分布式的 Java 常用对象,还提供了许多分布式服务。<br></span></p> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">在引入 Redisson 的依赖后,就可以直接进行调用:</span> </section> <section> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;"><code style="white-space:pre-wrap;overflow-wrap: break-word;margin: 0px 2px;line-height: 18px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);overflow-x: auto;padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"><span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"><<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">dependency</span>></span><br> <span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"><<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">groupId</span>></span>org.redisson<span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"></<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">groupId</span>></span><br> <span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"><<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">artifactId</span>></span>redisson<span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"></<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">artifactId</span>></span><br> <span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"><<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">version</span>></span>3.13.4<span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"></<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">version</span>></span><br><span style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;word-wrap: inherit !important;word-break: inherit !important;"></<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">dependency</span>></span><br></code></pre> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">先来一段 Redisson 的加锁代码:</span> </section> <section> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;"><code style="white-space:pre-wrap;overflow-wrap: break-word;margin: 0px 2px;line-height: 18px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);overflow-x: auto;padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span style="line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">private</span> <span style="line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">void</span> <span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">test</span>() </span>{<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//分布式锁名 锁的粒度越细,性能越好</span><br> RLock <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lock</span> = redissonClient.getLock(<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"test_lock"</span>);<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lock</span>.<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lock</span>();<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">try</span> {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//具体业务......</span><br> } <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">finally</span> {<br> <span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">lock</span>.unlock();<br> }<br>}<br></code></pre> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="
作者:微信小助手
<p style="text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">转自:伍陆七,</span></p> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 14px;">链接:juejin.cn/post/6856541106626363399</span></p> </blockquote> <p style="text-align: left;"><span style="font-size: 15px;">之前也写过一篇关于 Spring Validation 使用的文章,不过自我感觉还是浮于表面,本次打算彻底搞懂 Spring Validation。本文会详细介绍 Spring Validation 各种场景下的最佳实践及其实现原理,死磕到底!</span><span style="font-size: 15px;"><br></span></p> <p style="text-align: left;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;text-align: left;"></span></strong></span><br></p> <h2 data-id="heading-0"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;text-align: left;">简单使用</span></strong></span></h2> <p style="text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: left;"><span style="font-size: 15px;">Java API 规范(JSR303)定义了 Bean 校验的标准 validation-api,但没有提供实现。Hibernate Validation 是对这个规范的实现,并增加了校验注解如 @Email、@Length等。Spring Validation 是对 Hibernate Validation 的二次封装,用于支持 Spring MVC 参数自动校验。接下来,我们以 spring-boot 项目为例,介绍 Spring Validation 的使用。</span></p> <h3 data-id="heading-1" style="text-align: left;"><br><span style="font-size: 15px;"></span></h3> <h3 data-id="heading-1" style="text-align: left;"><strong><span style="font-size: 15px;">引入依赖</span></strong></h3> <p><br></p> <p style="text-align: left;"><span style="font-size: 15px;">如果 spring-boot 版本小于 2.3.x,spring-boot-starter-web 会自动传入 hibernate-validator 依赖。如果 spring-boot 版本大于2.3.x,则需要手动引入依赖:<br></span></p> <p style="text-align: left;"><br></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag"><<span class="code-snippet__name">dependency</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">groupId</span>></span>org.hibernate<span class="code-snippet__tag"></<span class="code-snippet__name">groupId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">artifactId</span>></span>hibernate-validator<span class="code-snippet__tag"></<span class="code-snippet__name">artifactId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">version</span>></span>6.0.1.Final<span class="code-snippet__tag"></<span class="code-snippet__name">version</span>></span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag"></<span class="code-snippet__name">dependency</span>></span></span></code></pre> </section> <p><br></p> <p><span style="font-size: 15px;"></span></p> <p style="text-align: left;"><span style="font-size: 15px;">对于 Web 服务来说,为防止非法参数对业务造成影响,在 Controller 层一定要做参数校验的!大部分情况下,请求参数分为如下两种形式:<br></span></p> <p style="text-align: left;"><br><span style="font-size: 15px;"></span></p> <ol class="list-paddingleft-2" style="text-align: left;"> <li style="text-align: left;"><p style="text-align: left;"><span style="font-size: 15px;">POST、PUT 请求,使用 requestBody 传递参数;</span></p></li> <li style="text-align: left;"><p style="text-align: left;"><span style="font-size: 15px;">GET 请求,使用 requestParam/PathVariable 传递参数。</span></p></li> </ol> <p style="text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: left;"><span style="font-size: 15px;">下面我们简单介绍下 requestBody 和 requestParam/PathVariable 的参数校验实战!<br></span></p> <p style="text-align: left;"><br><span style="font-size: 15px;"></span></p> <h3 data-id="heading-2" style="text-align: left;"><strong><span style="font-size: 15px;">requestBody 参数校验</span></strong></h3> <p style="text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: left;"><span style="font-size: 15px;">POST、PUT 请求一般会使用 requestBody 传递参数,这种情况下,后端使用 DTO 对象进行接收。只要给 DTO 对象加上 @Validated 注解就能实现自动参数校验。<br></span></p> <p style="text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: left;"><span style="font-size: 15px;">比如,有一个保存 User 的接口,要求 userName 长度是 2-10,account 和 password 字段长度是 6-20。如果校验失败,会抛出 MethodArgumentNotValidException 异常,Spring 默认会将其转为 400(Bad Request)请求。<br></span></p> <p style="text-align: left;"><br><span style="font-size: 15px;"></span></p> <p style="text-align: left;"><span style="color: rgb(136, 136, 136);font-size: 14px;">DTO 表示数据传输对象(Data Transfer Object),用于服务器和客户端之间交互传输使用的。在 spring-web 项目中可以表示用于接收请求参数的Bean对象。</span></p> <p style="text-align: left;"><br><span style="font-size: 15px;"></span></p> <ul class="list-paddingleft-2" style="text-align: left;"> <li style="text-align: left;"><p><span style="font-size: 15px;">在 DTO 字段上声明约束注解:</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="kotlin"><code><span class="code-snippet_outer"><span class="code-snippet__meta">@Data</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">UserDTO</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">private</span> <span class="code-snippet__built_in">Long</span> userId;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@NotNull</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@Length(min = 2, max = 10)</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">private</span> String userName;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@NotNull</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@Length(min = 6, max = 20)</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">private</span> String account;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@NotNull</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@Length(min = 6, max = 20)</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">private</span> String password;</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p style="text-align: left;"><span style="font-size: 15px;"><br></span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p style="text-align: left;"><span style="font-size: 15px;">在方法参数上声明校验注解:</span></p></li> </ul> <p><br></p> <p><span style="font-size: 15px;"></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="kotlin"><code><span class="code-snippet_outer"><span class="code-snippet__meta">@PostMapping(<span class="code-snippet__meta-string">"/save"</span>)</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> Result saveUser(<span class="code-snippet__meta">@RequestBody</span> <span class="code-snippet__meta">@Validated</span> UserDTO userDTO) {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">// 校验通过,才会执行业务逻辑处理</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> Result.ok();</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: left;"><span style="color: rgb(136, 136, 136);font-size: 14px;">这种情况下,使用 @Valid 和 @Validated 都可以。<br></span></p> <p style="text-align: left;"><br><span style="color: rgb(136, 136, 136);font-size: 14px;"></span></p> <h3 data-id="heading-3" style="text-align: left;"><strong><span style="font-size: 15px;">requestParam/PathVariable 参数校验</span></strong></h3> <p><br></p> <p style="text-align: left;"><span style="font-size: 15px;">GET 请求一般会使用 requestParam/PathVariable 传参。如果参数比较多(比如超过6个),还是推荐使用 DTO 对象接收。否则,推荐将一个个参数平铺到方法入参中。在这种情况下,必须在 Controller 类上标注 @Validated 注解,并在入参上声明约束注解(如 @Min 等)。如果校验失败,会抛出 ConstraintViolationException 异常。代码示例如下:<br></span></p> <p style="text-align: left;"><br><span style="font-size: 15px;"></span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="kotlin"><code><span class="code-snippet_outer"><span class="code-snippet__meta">@RequestMapping(<span class="code-snippet__meta-string">"/api/user"</span>)</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__meta">@RestController</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__meta">@Validated</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">UserController</span> </span>{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">// 路径变量</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@GetMapping(<span class="code-snippet__meta-string">"{userId}"</span>)</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">public</span> Result detail(<span class="code-snippet__meta">@PathVariable(<span class="code-snippet__meta-string">"userId"</span>)</span> <span class="code-snippet__meta">@Min(10000000000000000L)</span> <span class="code-snippet__built_in">Long</span> userId) {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">// 校验通过,才会执行业务逻辑处理</span></span></code><code><span class="code-snippet_outer"> UserDTO userDTO = new UserDTO();</span></code><code><span class="code-snippet_outer"> userDTO.setUserId(userId);</span></code><code><span class="code-snippet_outer"> userDTO.setAccount(<span class="code-snippet__string">"11111111111111111"</span>);</span></code><code><span class="code-snippet_outer"> userDTO.setUserName(<span class="code-snippet__string">"xixi"</span>);</span></code><code><span class="code-snippet_outer"> userDTO.setAccount(<span class="code-snippet__string">"11111111111111111"</span>);</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> Result.ok(userDTO);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">// 查询参数</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">@GetMapping(<span class="code-snippet__meta-string">"getByAccount"</span>)</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">public</span> Result getByAccount(<span class="code-snippet__meta">@Length(min = 6, max = 20)</span> <span class="code-snippet__meta">@NotNull</span> String account) {</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">// 校验通过,才会执行业务逻辑处理</span></span></code><code><span class="code-snippet_outer"> UserDTO userDTO = new UserDTO();</span></code><code><span class="code-snippet_outer"> userDTO.setUserId(<span class="code-snippet__number">10000000000000003L</span>);</span></code><code><span class="code-snippet_outer"> userDTO.setAccount(account);</span></code><code><span class="code-snippet_outer"> userDTO.setUserName(<span class="code-snippet__string">"xixi"</span>);</span></code><code><span class="code-snippet_outer"> userDTO.setAccount(<span class="code-snippet__string">"11111111111111111"</span>);</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> Result.ok(userDTO);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <pre style="text-align: left;"><span style="font-size: 15px;"></span><br></pre> <h3 data-id="heading-4" style="text-align: left;"><strong><span style="font-size: 15px;">统一异常处理</span></strong></h3> <p><br></p> <p style="text-align: left;"><span style="font-size: 15px;">前面说过,如果校验失败,会抛出 MethodArgumentNotValidException 或者 ConstraintViolationException 异常。在实际项目开发中,通常会用统一异常处理来返回一个更友好的提示。比如我们系统要求无论发送什么异常,HTTP 的状态码必须返回 200,由业务码去区分系统的异常情况。<br></span></p> <p style="text-align: le
作者:微信小助手
<p style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;" data-mpa-powered-by="yiban.io">这是一道天猫面试真题,90%以上的求职者都回答不到点上去~<br></p> <p><br></p> <h2 data-tool="mdnice编辑器" style="margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 2px;white-space: normal;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);text-align: center;width: 457.297px;display: flex;flex-direction: column;justify-content: center;">前言</h2> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">我们知道,软件工程是为了解决软件危机的,它是采用工程的概念、原理、 技术和方法来开发与维护软件,把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">在软件开发的过程中,数据库设计是非常重要的,它需要根据需求分析设抽象出 E-R 图,逻辑结构设计,数据库选型,物理设计,实施及运维。下面就聊聊那些年数据库设计的那些事。</p> <h2 data-tool="mdnice编辑器" style="margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 2px;white-space: normal;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);text-align: center;width: 457.297px;display: flex;flex-direction: column;justify-content: center;">数据库设计基本步骤</h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5612980769230769" src="/upload/529ff4637510f9ec1c6f6e6e2f2cabe4.jpg" data-type="jpeg" data-w="1664" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 1.2em;margin-bottom: 1em;padding-left: 10px;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: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);border-left: 2px solid rgb(52, 152, 219);">需求分析阶段</h3> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">要进行数据库设计首先要了解用户需求,参与到用户需求分析中去,需求分析常用 SA(Structured Analysis:结构化分析方法)强调开发方法的结构合理性以及所开发软件的结构合理性的软件开发方法,是生命周期法的继承与发展,是生命周期法与结构化程序设计思想的结合。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">其基本思想是用系统工程的思想和工程化得方法,根据用户至上的原则,自始自终按照结构化、模块化,自顶向下地对系统进行分析与设计。建立的主要步骤如下:</p> <ol data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 537.453px;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: black;"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 首先画系统的输入输出,先画顶层数据流程图(DFD:Data Flow Diagram),顶层数据流程图只包含一个加工,用以表示被开发的系统,然后考虑该系统有哪些输入、输出数据流。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 画系统内部,即画下层数据流层图。 </section></li> </ol> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">下面是一个交易系统的 DFD,需要先画出顶层数据流图,主要包括系统子模块之间的交互,然后再进一步对子模块进行分解。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.7747489239598279" src="/upload/1c10ce0618285cf0bc5b58c545c340.jpg" data-type="jpeg" data-w="1394" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 1.2em;margin-bottom: 1em;padding-left: 10px;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: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);border-left: 2px solid rgb(52, 152, 219);">概念设计阶段</h3> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">概念设计是整个数据库设计的关键,它是对需求分析阶段的成果进行综合,归档以及抽象出一个独立具体的 DBMS 模型,与具体的 RDBMS 产品无关。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">在实际的开发中,常用 E-R(Entity-Relationship:实体关系)图来表示,常用的工具 PowerDesigner,可以实现 CDM(概念数据模型)->LDM(逻辑数据模型)->PDM(物理数据模型)->Database 的自动转换,这个过程称为<strong style="color: rgb(52, 152, 219);">正向工程</strong>,如果有 database 建库脚本,也可以通过 PowerDesigner 工具生成 CDM,即 Database->PDM->LDM->CDM,称为<strong style="color: rgb(52, 152, 219);">反向工程</strong>。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><img data-ratio="0.4360135900339751" src="/upload/794b655589d5b97499ddc8aed8f95834.jpg" data-type="jpeg" data-w="1766" style="margin-right: auto;margin-left: auto;display: block;">概念设计通常采用自底向上,首先定义各系统局部的概念模型,然后再将他们集成合并起来,得到全局的概念模型。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">举个例子说明下,现在负责交易系统的开发,主要涉及订单,价格模块,分别交给不同的开发去设计开发。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">首先每个人要根据需求分析抽象出自己的实体 Entity 及之间的关系 Relationship,设计初步完成之后就要开会讨论了,把每个开发的 ER 图合并起来,就得到全局交易系统的 CDM。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.2703349282296651" src="/upload/392f1c8a77ea5ad75865c368ef1ccd07.jpg" data-type="jpeg" data-w="1672" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">名词动词形容词分析法</strong></p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">开发如何根据需求分析设计 ER 图,完成模块的详细设计,提供接口文档,最重要的是需求分析抽象 CDM 阶段的 ER 图,一种行之有效的方法就是<strong style="color: rgb(52, 152, 219);">名称动词形容词分析法</strong>,下面就详细解释下这种分析方法。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">还是举例说明吧,现在让我负责交易系统的订单这块的开发,在需求分析文档里看到一句话<strong style="color: rgb(52, 152, 219);">实现订单的高效管理</strong>,分析的过程如下:</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">名称</strong>:订单,订单就是一个 Entity,也可以拆分成多个 Entity,抽象出每个 Entity 的 Attribute(前期可能由于需求不明确,可以只做确认的内容),Entity 通过 PowerDesigner 的正向工程转换成数据库里的数据表,Attribute 就是表的字段;数据表通过 ORM 映射到 Java 里的就是 Class,字段就是 private 属性。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">动词</strong>:管理,也就是要对订单要进行增删改查 CRUD 操作。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">形容词</strong>:高效,首先想到在订单表上创建合适的索引吧,其次根据业务的发展,订单表太大会影响写入性能,是否要进行读写分离,分库分表操作。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><br></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;color: rgb(89, 89, 89);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;word-break: break-word;line-height: 1.75em;"> <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.7418856259659969" src="/upload/532f54f6595c19e932f3d3aca27e2d14.jpg" data-type="jpeg" data-w="1294" style="margin-right: auto;margin-left: auto;display: block;"> </figure> </section> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">数据库设计三范式</strong><br></p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">第一范式 1NF</strong>:确保每个字段保持原子性,不可分割。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">对于用户表 users 来说,有用户姓名(一般由 first_name 和 last_name 组成),如果使用类似 Oracle 的复合数据类型,就违反了 1NF。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5668103448275862" src="/upload/4e46c52cf7d6884f0b91f793f8ced79.jpg" data-type="jpeg" data-w="928" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">很明显 users 的字段 user_name 是一个自定类型,是可分解的,这就违反了 1NF。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">第二范式 2NF</strong>:确保字段完全依赖于主键。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">一个表中只能保存一种数据,不可以把多种数据保存在一张表里,假如一张表既存储了用户信息,又存储商品信息,还存储了订单信息,这样就违反了 2NF,而应该将用户表,商品表,订单表拆分成三张表,确保字段是该表拥有的。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">第三范式 3NF</strong>:必须满足 2NF,实体中每个属性与主键直接相关而不能间接相关。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">这个也不难理解,对于订单表 orders 来讲,是要存储用户表 users 的 user_id,要明确哪个用户下的单,有些业务场景是要获取 users 表的用户姓名 user_name,为了减少 orders 和 users 表的关联查询,将 user_name 冗余到 orders 表中,这种设计就违反了 3NF,减少数据冗余,可以通过主外键进行表之间连接。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.236328125" src="/upload/aa2da2fbb0706f5485ecd2a99804cd31.jpg" data-type="jpeg" data-w="2048" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;"><strong style="color: rgb(52, 152, 219);">到底该不该使用外键 Foreign Key</strong></p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">外键目的是为了保证数据完整性和一致性,避免产生脏数据,设置外键有啥缺点呢。</p> <ol data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 537.453px;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: black;"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: rgb(52, 152, 219);">影响写入性能</strong>:对于 insert 来说,每次都要判断从表的外键列是否在主表中存在(例如每次插入 orders 表,都要判断下 user_id 是否在 users 中存在),会降低数据库的写入性能,对于 MySQL 本来就只有 Master 输出写能力的数据库,就不太合适了,MySQL 开发规范规定不允许使用外键也是有一定道理的。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: rgb(52, 152, 219);">并发问题</strong>:在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。在高并发大场景,使用外键造成死锁或锁等几率更大。 </section></li> </ol> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">实际开发中,更多的是不靠外键来保证数据的完整性和一致性,而是通过的业务逻辑,比如用户要下单,必须先登录系统,下单只需要将登录的用户编号写入到订单表,这个用户必然是存在于用户表的,对于一个用户友好的系统来说,尽量让用户选择,不要人工输入,这样可以保证数据一致性,避免脏数据的产生。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 1.2em;margin-bottom: 1em;padding-left: 10px;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: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);border-left: 2px solid rgb(52, 152, 219);">逻辑设计阶段</h3> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">逻辑设计阶段是将概念数据模型转换为具体的 DBMS 所支持的数据模型,并将进行优化。虽然 LDM 独立于 DBMS 的,但可以进行外键,索引,视图等对象的设计工作。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">在此阶段,各子模块的 E-R 图之间的冲突主要有三类:属性冲突,命名冲突和结构冲突,同时 E-R 图向关系模型的转换,要解决如何将实体性和实体间的联系转换为关系模式,确定这些关系模式的属性和码,实际开发中,逻辑设计阶段不是必须的,有些是从 CDM 直接到 PDM 了。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.16519174041297935" src="/upload/71a5f366901934e70dc5610b16c68741.jpg" data-type="jpeg" data-w="1356" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 1.2em;margin-bottom: 1em;padding-left: 10px;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: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);border-left: 2px solid rgb(52, 152, 219);">数据库选型</h3> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">数据库选型是非常重要的环节,一般在需求分析完成之后,通过架构评审会进行确认,数据库方面主要包括数据存储,检索,安全,读写分离,分库分表,数据归档,接入数据仓库都要进行确认,根据业务的场景对相关的数据库产品进行调研比对,选择最适合业务场景的数据库作为存储。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">举个例子:对于一个 DAU 1000W TPS 3W 的交易的业务场景,如果使用 MySQL 来存储,我们知道原生的 MySQL 写入瓶颈,以及订单相关表数据量增长过快导致的性能问题,不太适合这种高并发写的场景,可以考虑使用分布式 MySQL,例如常见的 DRDS,TiDB,OceanBase。既可以解决原生 MySQL 写入瓶颈,同时也可以处理单表数据量大导致的分库分表问题。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">同样对于优酷,爱奇艺这种视频类系统,使用 MySQL 来存储就不太合适了,应该采用 MongoDB 集群来存储;对于京东,淘宝的这种搜索服务采用 ElastSearch 数据库集群处理会更高效。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 1.2em;margin-bottom: 1em;padding-left: 10px;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: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);border-left: 2px solid rgb(52, 152, 219);">物理设计阶段</h3> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">逻辑设计阶段和数据库选型完成之后,就可以通过 LDM 生成 PDM 了,在物理设计阶段,需要设计跟 RDBMS 相关的对象,例如设计存储过程,触发器,用户自定义函数,表空间等。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.14972527472527472" src="/upload/b38bfdd56bd62c0a6998d92b9efaff1d.jpg" data-type="jpeg" data-w="1456" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 1.2em;margin-bottom: 1em;padding-left: 10px;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: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);border-left: 2px solid rgb(52, 152, 219);">数据库实施阶段</h3> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.12292358803986711" src="/upload/ae478b921720011069d60da9cd18be8f.jpg" data-type="jpeg" data-w="1806" style="margin-right: auto;margin-left: auto;display: block;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 14px;"> img </figcaption> </figure> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">例如选择的是 MySQL 数据库,通过 PDM 生成数据库的建库脚本之后,需要进行规范性检查,通过之后就可以创建表结构,规范性检查可以借助开源的 SQL 审核工具,如 Yearning,Archery 都可以设置规则,检查之后会给出整改建议,能够帮我们自动实现 SQL Review。Yearning 是用 go 开发,目前只支持 MySQL 数据库,Archery 可以支持多种数据库。下面是 Yearning 自动化 SQL 审核平台的一个 DDL 工单的检测示例。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5348314606741573" src="/upload/e99ca736c65fbed617753bbdafda07bf.jpg" data-type="jpeg" data-w="1335" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">检测通过后就可以提交工单了,审核通过后就会自动执行 DDL 脚本建库。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 1.2em;margin-bottom: 1em;padding-left: 10px;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: 2px;text-align: left;white-space: normal;color: rgb(52, 152, 219);border-left: 2px solid rgb(52, 152, 219);">数据库维护阶段</h3> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">数据库维护阶段主要包括业务支撑和数据库运维,简单总结了下,如下图所示。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(89, 89, 89);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;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5546995377503852" src="/upload/b671b2be22b27fabb28623ba12b82274.jpg" data-type="jpeg" data-w="1947" style="margin-right: auto;margin-left: auto;display: block;"> </figure> <h2 data-tool="mdnice编辑器" style="margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 2px;white-space: normal;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);text-align: center;width: 457.297px;display: flex;flex-direction: column;justify-content: center;">总结</h2> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">实际开发中,数据库设计阶段是非常重要,通常都是开发自己根据业务模块的需求去分析,抽取成 CDM 中的 E-R 图,转换成 LDM,经过数据库选型及生成 PDM,最终生成数据库表,然后才能开始 coding,测试、发布上线以及版本迭代,为了保证线上业务的安全稳定高效,就需要对数据库进行精细化管理和维护。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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;line-height: 26px;color: black;">仔细观察不难发现,数据库设计的核心就是对需求分析的理解以及抽取沉底出 E-R 图,这就需要对行业及相关业务有深刻立即及抽象能力,大家有木有发现,招聘 Java 工程师的前面附加了业务属性,例如用户域 Java 工程师,支付域 Java 工程师,主要体现在需求分析抽象以及数据模型设计能力上,开发过程中多参与业务需求讨论是非常有必要的。今天就聊这么多,希望对大家有所帮助。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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: black;background-color: rgb(255, 255, 255);line-height: 26px;"><strong style="color: rgb(52, 152, 219);">推荐<strong>👍:</strong> </strong><a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIwNDgzMzI3Mg==&action=getalbum&album_id=1571213952619954180&token=2007747701&lang=zh_CN#wechat_redirect&__biz=MzIwNDgzMzI3Mg==#wechat_redirect" data-linktype="2" style="color: rgb(231, 76, 80);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;border-bottom: 1px solid rgb(231, 76, 60);"><span style="border-bottom: 1px solid rgb(231, 76, 60);font-size: 15px;">Github掘金计划:Github上的一些优质项目搜罗</span></a></p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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: black;background-color: rgb(255, 255, 255);line-height: 26px;"><strong style="color: rgb(52, 152, 219);">推荐👍: </strong><a href="https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247497154&idx=1&sn=35bdbd53b614c492677054df48272ee0&chksm=cea1ba09f9d6331fc91cc708f6b47ea96a01db93a93370fdaef7fa985f82fa2b5ee502614ce0&token=163766441&lang=zh_CN&scene=21#wechat_redirect" data-linktype="2" style="color: rgb(231, 76, 80);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;border-bottom: 1px solid rgb(231, 76, 60);font-size: 15px;"><span style="border-bottom: 1px solid rgb(231, 76, 60);">Github 2020 年度报告,值得一看 -「编程杂感」第 5 期</span></a></p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;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: black;background-color: rgb(255, 255, 255);line-height: 26px;"><span style="color: rgb(214, 214, 214);font-size: 14px;"><em style="color: rgb(231, 76, 60);">我是Guide哥,Java后端开发,拥抱开源,喜欢烹饪,自由的少年。一个三观比主角还正的技术人。我们下期再见!</em></span></p> <p><br></p>
作者:微信小助手
<section style="margin-left: 8px;margin-right: 8px;"> <img src="/upload/8cfde709854c76b15aea7f91a72bb824.jpg" data-type="jpeg" data-ratio="0.562962962962963" data-w="1080"> </section> <h2 style="box-sizing: border-box;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Microsoft Yahei", "Helvetica Neue", Helvetica;font-weight: bold;line-height: 1.225;color: rgb(36, 41, 46);margin: 1em 8px 16px;font-size: 1.75em;padding-bottom: 0.3em;-webkit-tap-highlight-color: transparent;border-bottom: 1px solid rgb(238, 238, 238);text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;color: rgb(0, 209, 0);">简介</span></h2> <section style="box-sizing: border-box;margin-bottom: 16px;-webkit-tap-highlight-color: transparent;color: rgb(36, 41, 46);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 8px;margin-right: 8px;"> 日志服务是针对实时数据一站式服务,在阿里集团经历大量大数据场景锤炼而成。提供日志类数据采集、消费、投递及查询分析功能,全面提升海量日志处理/分析能力。 </section> <h2 style="box-sizing: border-box;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Microsoft Yahei", "Helvetica Neue", Helvetica;font-weight: bold;line-height: 1.225;color: rgb(36, 41, 46);margin: 1em 8px 16px;font-size: 1.75em;padding-bottom: 0.3em;-webkit-tap-highlight-color: transparent;border-bottom: 1px solid rgb(238, 238, 238);text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;color: rgb(0, 209, 0);">起因</span></h2> <section style="box-sizing: border-box;margin-bottom: 16px;-webkit-tap-highlight-color: transparent;color: rgb(36, 41, 46);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 8px;margin-right: 8px;"> 最近在做支付服务的日志监控,日志太多不想入库,又不想自己搭建日志服务,即使是 ELK 最简单的组合,对于个人用户来说也是重重的,主要是浪费服务器资源了。还好有小伙伴推荐阿里云的日志服务,撸主看了一眼,基本上来说就是白嫖了,在这里墙裂推荐给各位小伙伴。 </section> <h2 style="box-sizing: border-box;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Microsoft Yahei", "Helvetica Neue", Helvetica;font-weight: bold;line-height: 1.225;color: rgb(36, 41, 46);margin: 1em 8px 16px;font-size: 1.75em;padding-bottom: 0.3em;-webkit-tap-highlight-color: transparent;border-bottom: 1px solid rgb(238, 238, 238);text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;color: rgb(0, 209, 0);">价格</span></h2> <section style="box-sizing: border-box;margin-bottom: 16px;-webkit-tap-highlight-color: transparent;color: rgb(36, 41, 46);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 8px;margin-right: 8px;"> 每个月有 500MB 的免费额度,相信对于绝大部分个人项目,日志输出应该是绰绰有余了! </section> <section style="box-sizing: border-box;margin-bottom: 16px;-webkit-tap-highlight-color: transparent;color: rgb(36, 41, 46);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 8px;margin-right: 8px;"> <img data-ratio="0.21933751119068934" src="/upload/4ffd61d3bb69936bc8ac421e7e8706dc.png" data-type="png" data-w="1117" style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;vertical-align: middle;" title="null"> </section> <h2 style="box-sizing: border-box;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Microsoft Yahei", "Helvetica Neue", Helvetica;font-weight: bold;line-height: 1.225;color: rgb(36, 41, 46);margin: 1em 8px 16px;font-size: 1.75em;padding-bottom: 0.3em;-webkit-tap-highlight-color: transparent;border-bottom: 1px solid rgb(238, 238, 238);text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-size: 18px;color: rgb(0, 209, 0);">集成</span></h2> <section style="box-sizing: border-box;margin-bottom: 16px;-webkit-tap-highlight-color: transparent;color: rgb(36, 41, 46);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 8px;margin-right: 8px;"> 阿里云的日志服务支持多种方式,给大家截个图,这也仅仅是其中一部分而已。 </section> <section style="box-sizing: border-box;margin-bottom: 16px;-webkit-tap-highlight-color: transparent;color: rgb(36, 41, 46);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 8px;margin-right: 8px;"> <img data-ratio="0.5263843648208469" src="/upload/e9d3c8268b485672f91eae16c4f9c008.png" data-type="png" data-w="1535" style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;vertical-align: middle;display: inline;" title="null"> </section> <section style="box-sizing: border-box;margin-bottom: 16px;-webkit-tap-highlight-color: transparent;color: rgb(36, 41, 46);font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 8px;margin-right: 8px;"> 由于项目中使用了 LogBack,这里我们就使用 LogBack-SDK 的方式,代码无入侵,只需要配置下xml 文件即可。 </section> <pre style="box-sizing: border-box;overflow: auto;font-variant-numeric: normal;font-variant-east-asian: normal;font-stretch: normal;font-size: 12px;line-height: 1.45;font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace;padding: 1em;margin-bottom: 16px;word-break: break-all;overflow-wrap: break-word;background: 0% 0% / 30px, 0% 0% / 30px rgb(248, 248, 248);border-width: initial;border-style: none;border-color: initial;border-radius: 4px;-webkit-tap-highlight-color: transparent;max-height: 35em;text-align: left;"> <section style="margin-left: 8px;margin-right: 8px;"> <code style="box-sizing: border-box;font-family: "Source Code Pro", Consolas, "Liberation Mono", Menlo, Courier, "Microsoft Yahei", monospace;font-size: 1.1em;color: inherit;background: transparent;border-radius: 3px;white-space: inherit;border-width: 0px;border-style: initial;border-color: initial;word-break: break-all;display: inline;max-width: initial;overflow: hidden auto;line-height: inherit;overflow-wrap: normal;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(153, 153, 136);font-style: italic;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><!--为了防止进程退出时,内存中的数据丢失,请加上此选项--></span></span><br><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><</span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">shutdownHook</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"> </span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 128, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 128, 128);">class</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">=</span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(221, 17, 68);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(221, 17, 68);">"ch.qos.logback.core.hook.DelayingShutdownHook"</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">/></span></span><br><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(153, 153, 136);font-style: italic;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><!--阿里云日志写入 https://pay.cloudbed.vip --></span></span><br><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><</span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">appender</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"> </span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 128, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 128, 128);">name</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">=</span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(221, 17, 68);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(221, 17, 68);">"aliyun"</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"> </span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 128, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 128, 128);">class</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">=</span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(221, 17, 68);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(221, 17, 68);">"com.aliyun.openservices.log.logback.LoghubAppender"</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">></span></span><br> <span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(153, 153, 136);font-style: italic;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><!--公网域名 https://pay.cloudbed.vip--></span></span><br> <span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><</span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">endpoint</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">></span></span>cn-qingdao.log.aliyuncs.com<span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(0, 0, 128);"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"></</span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">endpoint</span></span></span><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;">></span></span><br> <span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;color: rgb(153, 153, 136);font-style: italic;"><span style="box-sizing: border-box;-webkit-tap-highlight-color: transparent;"><!--申请地址:https://ram.console.aliyun.c
作者:じ☆ve不哭
> 在使用knife4j文档(与swagger相同)时,发现无法识别泛型。最终经过几个小时的努力下解决问题,做了如下的总结。我是因为使用了JRebel导致无法识别泛型 ## 1.配置的swagger不展示泛型 - [Swagger字段属性说明不显示](https://doc.xiaominfo.com/knife4j/faq/swagger-des-not-found.html) - 如果使用JRebel热加载可能会导致无法识别泛型,使用正常的run或者debug即可解决问题 - 如上两种方式都不生效可以尝试将swagger相关依赖的jar包全部删除,重新使用maven下载
作者:微信小助手
<section style="line-height: 1.75em;" data-mpa-powered-by="yiban.io"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><strong>群消息,究竟存一份还是多份?</strong></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">任何技术方案,都不是天才般灵感乍现想到的,一定是一个演进迭代,逐步优化的过程。今天就聊一聊,</span> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">群消息,为啥只需要存一份</span> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">。</span> <br> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">群信息,用户信息,群成员关系都是基础数据:<br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">group_info(gid, group_info);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">user_info(uid, user_info);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">group_members(gid, uid);</span></em></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">假设一个群(gid)里有4个成员,其中:</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(1)三个在线(A, uid1, uid2);</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(2)一个不在线(uid3);</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">A发送了一条消息,很容易想到,对于不同的群友消息存多份,<span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">每个群友一个队列来存储</span>。但由于在线的用户会实时的收到消息,所以<strong>暂定只为离线的用户存储</strong>。</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">用户收到的群消息,也是基础数据:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">user_msgs(uid,msgid,gid,sender_uid,time,content);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <img style="white-space: normal;" data-ratio="0.6666666666666666" data-type="png" data-w="414" data-s="300,640" data-copyright="0" src="/upload/1c29fcbc05113e701890afc2b6ec0e5a.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">很容易想到,整个群消息的发送流程如上图1-4:<br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(1)发送消息;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(2)查询状态;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(3)不在线的存储离线;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(4)在线的实时推送;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">“在线的群友不存储,离线的群友才存储”</span>会<strong>带来的问题</strong>是,如果第四步发生异常,群友会丢失消息。</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">消息的可达性是聊天系统中最重要的要素(没有之一),故这个方案是不行的,需要优化为“<strong>不管是否在线,都要先存储</strong>”。</span> </section> <p><br></p> <section style="line-height: 1.75em;"> <img style="" data-ratio="0.6811594202898551" data-type="png" data-w="414" data-s="300,640" data-copyright="0" src="/upload/a376b227bdcc88082ab323e1a977bb29.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">发送群消息的流程优化为,如上图1-4:<br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(1)发送消息;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(2)所有人都存一份;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(3)查询状态;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(4)在线的实时推送;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">先将消息落地,能够保证消息可达性,<strong>那何时才能删除已经落地的群消息呢?</strong></span> </section> <p><br></p> <section style="line-height: 1.75em;"> <img style="" data-ratio="0.9450171821305842" data-type="png" data-w="291" data-s="300,640" data-copyright="0" src="/upload/eb9c81c9e23e0ec22d27004714c8aa65.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">对于<strong>在线的群友</strong>,收到群消息后,<span style="color: rgb(255, 76, 0);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">给个ack确认,才能删除</span>。<br></span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><em>画外音:逻辑删除,还是物理删除,根据业务是否有消息漫游决定。</em></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <img style="" data-ratio="0.9273356401384083" data-type="png" data-w="289" data-s="300,640" data-copyright="0" src="/upload/6c34b3cfe78fbef746e4442476c6b4b1.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">对于<strong>离线的群友</strong>,在下次登陆后,拉取完离线消息,再<span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">给ack确认,才能删除</span>。<br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">总之,为了保证消息的可达性,不管是在线消息,还是离线消息,必须接收方给ack确认,才能删除消息。</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">“不管是否在线,都冗余一份群消息”</span><strong>带来的问题</strong>是,同一条消息存储了很多次,对磁盘和带宽造成了很大的浪费。很容易想到的优化是:<strong>群消息实体存储一份,用户只冗余消息ID。</strong></span> </section> <p><br></p> <section style="line-height: 1.75em;"> <img style="" data-ratio="0.6674698795180722" data-type="png" data-w="415" data-s="300,640" data-copyright="0" src="/upload/cbeed924fa4ac38b13464504f2299103.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">故基础数据可以由:<br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">user_msgs(uid,msgid,gid,sender_uid,time,content);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">优化为:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">group_msgs(msgid,gid,sender_uid,time,content);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">user_msgs(uid, msgid, gid);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">这个优化,对于消息投递,以及消息删除的核心流程没有影响,几个实践为:</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(1)在线用户投递消息实体,ack消息ID;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(2)离线用户先拉取消息ID,再拉取消息实体,再ack消息ID;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">如此这般,假如在某个群友A期间,群里陆续发送了N条消息,则user_msgs(uid, msgid, gid)里,会有 uidA -> mid1,mid2, mid3, … midN 等N条离线记录,拉取离线消息时,可以把这N条消息一次性拉取出来,然后再删除:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">delete from user_msgs </span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;"> where msgid in($mid1,$mid2…, $midN) and gid=$gid</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">然而,<span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">群消息具备“偏序”特性</span>,上面的一次性删除完全可以优化为:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">delete from user_msgs </span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;"> where msgid >= $mid1 and gid=$gid</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">这就意味着,每个用户只需要<strong>记录</strong>“<span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">最近一次收到的消息ID</span>”,<strong>而不用记录</strong>“所有未收到的消息<span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">ID集合</span>”,每当收在线消息ack,以及拉离线消息ack时,只需要更新这个“最近一次收到的消息ID”即可。</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">于是乎,基础数据可以由:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">group_members(gid, uid);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">group_msgs(msgid,gid,sender_uid,time,content);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">user_msgs(uid, msgid, gid);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">优化为:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">group_members(gid, uid, <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">last_ack_msgid</span>);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">group_msgs(msgid,gid,sender_uid,time,content);</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="text-decoration: line-through;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 1px;">user_msgs(uid, msgid, gid); // 不再需要</span></em></span> </section> <p><br></p> <section style="line-height: 1.75em;"> <img style="" data-ratio="0.6298076923076923" data-type="png" data-w="416" data-s="300,640" data-copyright="0" src="/upload/fc50f4b550e6cb7fc7c538330fc789dd.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">即,群消息只存储一份,群友无需冗余任何消息实体,或者消息ID了。<br></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <img style="" data-ratio="0.9434628975265018" data-type="png" data-w="283" data-s="300,640" data-copyright="0" src="/upload/c080b7684e98f67fc1c2a5df19ac4ac.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">对于<strong>在线的群友</strong>,收<span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">到群消息后,修改</span>这个last_ack_msgid。<br></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <img style="" data-ratio="0.8561643835616438" data-type="png" data-w="292" data-s="300,640" data-copyright="0" src="/upload/6289a91c492845091b4a236bb997e693.png"> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">对于<strong>离线的群友</strong>,<span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(255, 76, 0);">拉取群消息后,也修改</span>这个last_ack_msgid。<br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;color: rgb(0, 82, 255);font-size: 15px;letter-spacing: 1px;"><em>画外音:这里的讨论,仅限于接收方收到了哪些消息,和发送方的已读回执没有关系。</em><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><strong>总结</strong></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">任何架构方案都不是灵光一现,而是逐步迭代优化产生的:</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(1)存多份,只存在线,消息容易丢;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(2)存多份,所有群友都存储,消息冗余多;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(3)存多份,只存ID,未利用偏序;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">(4)存一份,只存last_ack_msgid;</span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">架构不只是设计出来的,更是演进出来的。</span> </section> <section style="line-height: 1.75em;"> <strong style="font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;"><span style="font-size: 15px;letter-spacing: 1px;">任何脱离业务的架构设计都是耍流氓。</span></strong> </section> <p style="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;text-align: center;background-color: rgb(255, 255, 255);line-height: normal;"><span style="letter-spacing: 1px;font-size: 15px;"><strong><img data-ratio="1" data-type="jpeg" data-w="250" data-s="300,640" width="auto" src="/upload/7ddc9700032e2c5cee163f1f1a37b46c.jpg" style="max-width: 677px;box-sizing: border-box;visibility: visible !important;width: 130px !important;"></strong></span></p> <p style="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);text-align: center;line-height: normal;"><span style="letter-spacing: 1px;font-size: 12px;"><strong><strong>架构师之路</strong>-分享技术思路</strong></span></p> <section style="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);line-height: 1.75em;"> <br> </section> <section style="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);line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">若有收获,随手</span> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">转</span> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">发。</span> </section> <section style="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);line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="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);line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">相关文章:</span> </section> <section style="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);line-height: 1.75em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">《<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651966192&idx=1&sn=e50edd636a55611b1b285e60859315d3&chksm=bd2d7f2c8a5af63ac2907897dfb2b0b50bcd9f44ba95c1a8c54ee6fe59fceb98bd59ad0f239c&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">群消息已读回执(这个屌),究竟是推还是拉?</a>》</span> </section>