文章列表

阿里官方Redis开发规范!

作者:微信小助手

<p data-mpa-powered-by="yiban.io" style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(62, 71, 83);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;text-align: center;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;background-color: rgb(255, 255, 255);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);font-size: 15px;text-align: center;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;background-color: rgb(255, 255, 255);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(62, 62, 62);font-size: 15px;text-align: center;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-ratio="0.563" data-s="300,640" src="/upload/7bc3d1f8fdf705d8e8d743feadd8047a.jpg" data-type="jpeg" data-w="1000" style=""></p> <p style="margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">本文主要介绍在使用阿里云Redis的开发规范,从下面几个方面进行说明。</p> <ul style="margin-top: 1.4em;margin-bottom: 1.4em;display: table;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li style="list-style: none;display: table-row;"><p>键值设计</p></li> <li style="list-style: none;display: table-row;"><p>命令使用</p></li> <li style="list-style: none;display: table-row;"><p>客户端使用</p></li> <li style="list-style: none;display: table-row;"><p>相关工具</p></li> </ul> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">通过本文的介绍可以减少使用Redis过程带来的问题。</p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">一、键值设计</h3> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">1、key名设计</h3> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">可读性和可管理性</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id</p> <pre style="padding: 0.88889em;font-size: 0.9em;word-break: normal;overflow-wrap: normal;overflow: auto;background: rgb(246, 246, 246);border-radius: 4px;"><code style="border-radius: 0px;font-family: Menlo, Monaco, Consolas, &quot;Andale Mono&quot;, &quot;lucida console&quot;, &quot;Courier New&quot;, monospace;font-size: inherit;background-color: inherit;">ugc:video<span style="font-weight: 600;">:</span>1</code></pre> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">简洁性</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:</p> <pre style="padding: 0.88889em;font-size: 0.9em;word-break: normal;overflow-wrap: normal;overflow: auto;background: rgb(246, 246, 246);border-radius: 4px;"><code style="border-radius: 0px;font-family: Menlo, Monaco, Consolas, &quot;Andale Mono&quot;, &quot;lucida console&quot;, &quot;Courier New&quot;, monospace;font-size: inherit;background-color: inherit;">user:<span style="font-weight: 600;">{</span>uid<span style="font-weight: 600;">}:</span>friends<span style="font-weight: 600;">:</span>messages<span style="font-weight: 600;">:{</span>mid<span style="font-weight: 600;">}</span>简化为u<span style="font-weight: 600;">:{</span>uid<span style="font-weight: 600;">}:</span>fr<span style="font-weight: 600;">:</span>m<span style="font-weight: 600;">:{</span>mid<span style="font-weight: 600;">}</span><span style="color: rgb(241, 64, 60);">。</span></code></pre> <p style="margin-top: -0.8em;margin-bottom: -0.8em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">不要包含特殊字符</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">反例:包含空格、换行、单双引号以及其他转义字符</p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">2、value设计</h3> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">拒绝bigkey</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">防止网卡流量、慢查询,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。反例:一个包含200万个元素的list。非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)),查找方法和删除方法</p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">选择适合的数据类型</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如ziplist,但也要注意节省内存和性能之间的平衡)</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">反例:</p> <pre style="padding: 0.88889em;font-size: 0.9em;word-break: normal;overflow-wrap: normal;overflow: auto;background: rgb(246, 246, 246);border-radius: 4px;"><code style="border-radius: 0px;font-family: Menlo, Monaco, Consolas, &quot;Andale Mono&quot;, &quot;lucida console&quot;, &quot;Courier New&quot;, monospace;font-size: inherit;background-color: inherit;">set user<span style="font-weight: 600;">:</span>1<span style="font-weight: 600;">:</span>name tom<br>set user<span style="font-weight: 600;">:</span>1<span style="font-weight: 600;">:</span>age 19<br>set user<span style="font-weight: 600;">:</span>1<span style="font-weight: 600;">:</span>favor football</code></pre> <p style="margin-top: -0.8em;margin-bottom: -0.8em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">正例:</p> <pre style="padding: 0.88889em;font-size: 0.9em;word-break: normal;overflow-wrap: normal;overflow: auto;background: rgb(246, 246, 246);border-radius: 4px;"><code style="border-radius: 0px;font-family: Menlo, Monaco, Consolas, &quot;Andale Mono&quot;, &quot;lucida console&quot;, &quot;Courier New&quot;, monospace;font-size: inherit;background-color: inherit;">hmset user<span style="font-weight: 600;">:</span>1 name tom age 19 favor football</code></pre> <p style="margin-top: -0.8em;margin-bottom: -0.8em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">控制key的生命周期</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">redis不是垃圾桶,建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。</p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">二、命令使用</h3> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">1、O(N)命令关注N的数量</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscan、sscan、zscan代替。</p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">2、禁用命令</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。</p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">3、合理使用select</h3> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。</p> <h3 style="margin-top: 1.90909em;margin-bottom: 1.27273em;font-variant-numeric: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.1em;line-height: 1.5;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(18, 18, 18);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">4、使用批量操作提高效率</h3> <ul style="margin-top: 1.4em;margin-bottom: 1.4em;display: table;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li style="list-style: none;display: table-row;"><p>原生命令:例如mget、mset。</p></li> <li style="list-style: none;display: table-row;"><p>非原生命令:可以使用pipeline提高效率。</p></li> </ul> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: medium;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">但要注意控制一次批量操作的<span style="font-weight: 600;">元素个数</span>(例如500以内,实际也和元素字节数有关)。</p> <p style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(18, 18, 18);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;

我写的代码,又被CTO骂了......

作者:微信小助手

<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;">大多数时候我都是写一些业务代码,可能一堆 CRUD 就能解决问题,但是这样的工作对技术人的提升并不多,如何让自己从业务中解脱出来找到写代码的乐趣呢,我做过一些尝试,使用设计模式改善自己的业务代码就是其中的一种。</span></p> </section> <section style="clear: both;box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </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-cropselx1="0" data-cropselx2="562" data-cropsely1="0" data-cropsely2="346" data-ratio="0.624" data-s="300,640" src="/upload/4822fa974a9d563b39a72aa5bc764f03.jpg" data-type="jpeg" data-w="500" style="width: 562px;height: 351px;"> </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;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“你这代码写的像坨屎</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">”</span></span>,今天我的代码又被当作典型被 CTO 骂了......于是他给我的建议如下:<br></span></p> <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="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;">责任链设计模式</p> </section> </section> </section> <p style="line-height: normal;"><br></p> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>模式定义</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </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> <p style="line-height: normal;"><br></p> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>适用场景</strong></p> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </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;">适用于多节点的流程处理,每个节点完成各自负责的部分,节点之间不知道彼此的存在,比如 OA 的审批流,Java Web 开发中的 Filter 机制。<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;">中介让我找门店客服,门店客服又让我找房东,房东又让我找她家老公,最终好说歹说才把这事了了(租房一定要找正规中介)。<br></span></p> <section style="line-height: normal;"> <br> </section> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>实践经验</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">笔者目前所做的业务是校园团餐的聚合支付,业务流程很简单:</span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li style="font-weight: bold;"><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;">学生打开手机付款码支付。<br></span></strong></p></li> <li style="font-weight: bold;"><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;"><br></span></p></li> </ul> <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;letter-spacing: 1px;color: rgb(71, 193, 168);">大体如下:</span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><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;">某档口只允许某类用户用户消费,比如教师档口只允许教师消费,学生档口不允许校外用户消费。</span></p></li> <li><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;">某个档口一天只允许某类用户消费几次,比如教师食堂一天只允许学生消费一次。</span></p></li> <li> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com"> <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;">是否允许非清真学生消费,比如某些清真餐厅,是不允许非清真学生消费的。</span></p> </section></li> </ul> <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;letter-spacing: 1px;color: rgb(71, 193, 168);">针对这几类情况我建立了三类过滤器,分别是:</span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><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;">SpecificCardUserConsumeLimitFilter:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">按用户类型判断是否允许消费。</span></p></li> <li><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;">DayConsumeTimesConsumeLimitFilter:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">按日消费次数判断是否允许消费。</span></p></li> <li><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;">MuslimConsumeLimitFilter:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">非清真用户是否允许消费。</span></p></li> </ul> <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;">判断逻辑是先通过 SpecificCardUserConsumeLimitFilter 判断当前用户是否可以在此档口消费。<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;">如果允许继续由 DayConsumeTimesConsumeLimitFilter 判断当天消费次数是否已用完;如果未用完继续由 MuslimConsumeLimitFilter 判断当前用户是否满足清真餐厅的就餐条件,前面三条判断,只要有一个不满足就提前返回。<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);">部分代码如下:</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;">public</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">boolean</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">canConsume</span><span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(String&nbsp;uid,String&nbsp;shopId,String&nbsp;supplierId)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">//获取用户信息,用户信息包含类型(student:学生,teacher:老师,unknown:未知用户)、名族(han:汉族,mg:蒙古族)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;UserInfo&nbsp;userInfo&nbsp;=&nbsp;getUserInfo(uid);<br>&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;ConsumeConfigInfo&nbsp;consumeConfigInfo&nbsp;=&nbsp;getConsumeConfigInfo(shopId,supplierId)&nbsp;<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">//&nbsp;构造消费限制过滤器链条</span><br>&nbsp;&nbsp;&nbsp;&nbsp;ConsumeLimitFilterChain&nbsp;filterChain&nbsp;=&nbsp;<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>&nbsp;ConsumeLimitFilterChain();<br>&nbsp;&nbsp;&nbsp;&nbsp;filterChain.addFilter(<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>&nbsp;SpecificCardUserConsumeLimitFilter());<br>&nbsp;&nbsp;&nbsp;&nbsp;filterChain.addFilter(<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>&nbsp;DayConsumeTimesConsumeLimitFilter());<br>&nbsp;&nbsp;&nbsp;&nbsp;filterChain.addFilter(<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>&nbsp;MuslimConsumeLimitFilter());<br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">boolean</span>&nbsp;checkResult&nbsp;=&nbsp;filterChain.doFilter(filterChain,&nbsp;schoolMemberInfo,&nbsp;consumeConfigInfo);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">//filterChain.doFilter方法</span><br>&nbsp;&nbsp;&nbsp;<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;">public</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">boolean</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">doFilter</span><span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(ConsumeLimitFilterChain&nbsp;filterChain,UserInfo&nbsp;userInfo,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ConsumeConfigInfo&nbsp;consumeConfigInfo&nbsp;)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>(index&lt;filters.size()){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;filters.get(index++).doFilter(filterChain,&nbsp;userInfo,&nbsp;consumeConfigInfo);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">//SpecificCardUserConsumeLimitFilter.doFilter方法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">public</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">boolean</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">doFilter</span><span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(ConsumeLimitFilterChain&nbsp;filterChain,UserInfo&nbsp;userInfo,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ConsumeConfigInfo&nbsp;consumeConfigInfo&nbsp;)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">//获取某一类型的消费限制,比如student允许消费,unknown不允许消费</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CardConsumeConfig&nbsp;cardConsumeConfig&nbsp;=&nbsp;findSuitCardConfig(userInfo,&nbsp;consumeConfigInfo);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">//&nbsp;判断当前卡用户是否允许消费</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;(consumeCardConfig&nbsp;!=&nbsp;<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>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;((!CAN_PAY.equals(cardConsumeConfig&nbsp;.getEnabledPay())))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;<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;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;filterChain.doFilter(filterChain,&nbsp;memberInfo,&nbsp;consumeConfig);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">//DayConsumeTimesConsumeLimitFilter.doFilter方法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">public</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">boolean</span>&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">doFilter</span><span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);overflow-wrap: inherit !important;word-break: inherit !important;">(ConsumeLimitFilterChain&nbsp;filterChain,UserInfo&nbsp;userInfo,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ConsumeConfigInfo&nbsp;consumeConfigInfo&nbsp;)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">//获取某一类型的消费限制,比如student可以消费2次</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CardConsumeConfig&nbsp;cardConsumeConfig&nbsp;=&nbsp;findSuitCardConfig(userInfo,&nbsp;consumeConfigInfo);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">int</span>&nbsp;consumeCnt&nbsp;=&nbsp;getConsumeCnt(userInfo)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>(consumeCnt&nbsp;&gt;=&nbsp;cardConsumeConfig.getDayConsumeTimesLimit()){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;<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;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;filterChain.doFilter(filterChain,&nbsp;memberInfo,&nbsp;consumeConfig);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre> </section> <section style="line-height: normal;"> <br> </section> <h4 data-tool="mdnice编辑器" 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;">将每种限制条件的判断逻辑封装到了具体的 Filter 中,如果某种限制条件的逻辑有修改不会影响其他条件,如果需要新加限制条件只需要重新构造一个 Filter 织入到 FilterChain 上即可。</span></h4> <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;">策略设计模式</p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>模式定义</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </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> <p style="line-height: normal;"><br></p> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>适用场景</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </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;">主要是为了消除大量的 if else 代码,将每种判断背后的算法逻辑提取到具体的策略对象中,当算法逻辑修改时对使用者无感知,只需要修改策略对象内部逻辑即可。<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> <p style="line-height: normal;"><br></p> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>实践经验</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </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;">但是由于历史原因,某些设备对接的推送平台是不一样的,A 类设备优先使用信鸽推送,如果失败了需要降级到长轮询机制,B 类设备直接使用自研的推送平台即可。<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;">还有个现状是 A 类和 B 类的消息格式是不一样的(不同的团队开发,后期被整合到一起)。<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;">鉴于此,我抽象出 PushStrategy 接口,其具体的实现有 IotPushStrategy 和 XingePushStrategy,分别对应自研推送平台的推送策略和信鸽平台的推送策略,使用者时针对不同的设备类型使用不同的推送策略即可。<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);">部分代码如下:</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;">/**<br>&nbsp;*&nbsp;推送策略<br>&nbsp;*&nbsp;/<br>public&nbsp;interface&nbsp;PushStrategy&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: inherit;line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span>&nbsp;deviceVO设备对象,包扣设备sn,信鸽pushid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: inherit;line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span>&nbsp;content,推送内容,一般为json<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">public</span>&nbsp;CallResult&nbsp;push(AppDeviceVO&nbsp;deviceVO,&nbsp;Object&nbsp;content);<br>}<br><br>IotPushStrategy&nbsp;implements&nbsp;PushStrategy{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: inherit;line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span>&nbsp;deviceVO设备对象,包扣设备sn,信鸽pushid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: inherit;line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span>&nbsp;content,推送内容,一般为json<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">public</span>&nbsp;CallResult&nbsp;push(AppDeviceVO&nbsp;deviceVO,&nbsp;Object&nbsp;content){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Message&nbsp;message&nbsp;=&nbsp;createPushMsg(deviceVO,content);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IotMessageService.pushMsg(message);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br>XingePushStrategy&nbsp;implements&nbsp;PushStrategy{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: inherit;line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span>&nbsp;deviceVO设备对象,包扣设备sn,信鸽pushid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: inherit;line-height: inherit;margin: 0px;padding: 0px;overflow-wrap: inherit !important;word-break: inherit !important;">@param</span>&nbsp;content,推送内容,一般为json<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<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;">public</span>&nbsp;CallResult&nbsp;push(AppDeviceVO&nbsp;deviceVO,&nbsp;Object&nbsp;content){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSONObject&nbsp;jsonObject&nbsp;=&nbsp;createPushMsg(content);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>(!XinggePush.pushMsg(message)){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<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;">/**<br>消息推送Service<br>*/</span><br>MessagePushService{<br>&nbsp;&nbsp;&nbsp;&nbsp;pushMsg(AppDeviceVO&nbsp;deviceVO,&nbsp;Object&nbsp;content){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>(A设备){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XingePushStrategy.push(deviceVO,content);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<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;">else</span>&nbsp;<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>(B设备){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IotPushStrategy.push(deviceVO,content);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> </section> <section style="line-height: normal;"> <br> </section> <h4 data-tool="mdnice编辑器" 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;">将每种通道的推送逻辑封装到了具体的策略中,某种策略的变更不会影响其他策略,由于实现了共同接口,所以策略可以互相替换,对使用者友好。</span></h4> <h4 data-tool="mdnice编辑器" style="line-height: normal;"><br></h4> <h4 data-tool="mdnice编辑器" 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;">比如 Java ThreadPoolExecutor 中的任务拒绝策略,当线程池已经饱和的时候会执行拒绝策略,具体的拒绝逻辑被封装到了 RejectedExecutionHandler 的 rejectedExecution 中。</span></h4> <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="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;">模板设计模式</p> </section> </section> </section> <p style="line-height: normal;"><br></p> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>模式定义</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </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> <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;letter-spacing: 1px;color: rgb(71, 193, 168);">比如炒土豆丝和炒麻婆豆腐,大体逻辑都是:</span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li style="font-weight: bold;"><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;">切菜<br></span></strong></p></li> <li style="font-weight: bold;"><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;">放油<br></span></strong></p></li> <li style="font-weight: bold;"><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;">炒菜<br></span></strong></p></li> <li style="font-weight: bold;"><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;"><br></span></p></li> </ul> <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;">1,2,4 都差不多,但是第 3 步是不一样的,炒土豆丝得拿铲子翻炒,但是炒麻婆豆腐得拿勺子轻推,否则豆腐会烂(疫情宅在家,学了不少菜)。<br></span></p> <section style="line-height: normal;"> <br> </section> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>使用场景</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </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> <p style="line-height: normal;"><br></p> <section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>实践经验</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">还是接着之前语音播报的例子来说,后期我们新加了两个需求:</span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><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;">消息推送需要增加 trace。</span></p></li> <li><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></li> </ul> <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;letter-spacing: 1px;color: rgb(71, 193, 168);">所以现在的流程变成了这样:</span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p style="text-align: just

我是如何阅读JDK源码的?

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">1. 前言</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">之前断断续续读过一部分 JDK 常用类的源码,这里想把过程中的一些心得和方法记录下来,如果能帮到需要的小伙伴就再好不过了!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文主要分享一下我的阅读工具和阅读顺序。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">PS: 由于当前主流使用的 JDK 版本仍是 1.8,因此源码阅读主要是 1.8 版本,有些地方可以参考 1.7(面试可能问到)。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">2. 工具</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">工欲善其事,必先利其器。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">需要的工具不多,IDE + Google 翻译足够了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用 IDE 的主要目的是可以写一些测试代码以便跟踪调试。这个还是很有必要的,跟进代码的执行流程更容易理解它的实现原理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">直接在 IDE 打开源码文件,源码中的注释通常很详细,遇到不懂的地方 Google 翻译一下。也可以加上官方文档,其实源码里面注释跟文档是一样的,有些地方可能更详细,只不过官方文档排版更漂亮一些。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">JDK 1.8 官方文档链接:https://docs.oracle.com/javase/8/docs/api/</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当然,阅读的先后顺序也很重要,下面介绍下我的阅读顺序。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">3. 阅读顺序</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.1 整体顺序<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">JDK 中的代码非常多,不可能、也没必要全部读完,因此要有的放矢。从整体上来讲,顺序大概是:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 集合框架类 </section></li> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: lower-alpha;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 主要包括 Collection、Map、Queue 等组成的一系列常用类和接口,包括 ArrayList、LinkedList、HashMap 等。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 这部分内容日常开发使用较多,而且面试高频出现,因此可以先从这里入手。 </section></li> </ol> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 并发包 </section></li> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: lower-alpha;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 即 java.util.concurrent (J.U.C) 包下的常用类,包括 ReentrantLock、ThreadPoolExecutor、AQS 等。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 该部分提供了并发编程的常用工具类,也是面试高频。 </section></li> </ol> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 其他常用类 </section></li> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: lower-alpha;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 例如 ThreadLocal、String、StringBuilder、StringBuffer 等。 </section></li> </ol> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">整体概览如下:</p> <p style="text-align: center;"><img class="rich_pages" data-ratio="1.1453125" data-s="300,640" src="/upload/244cf4fb11c93848802d3bcd91a5acea.png" data-type="png" data-w="1280" style=""></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">具体到某一个类,如何去阅读它的源码实现呢?下面继续介绍。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.2 具体顺序<span style="display: none;"></span></h3> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>3.2.1 类和接口<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如何阅读一个类的源码呢?主要步骤大概是:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 先读接口代码。包括接口说明文档、各个方法的定义和说明文档。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 再去读实现类的主要方法实现,通常有以下两条主线入口: </section></li> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: lower-alpha;" 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> </ol> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在 Java 中,接口通常意味着是一种“标准”、或者“协议”。一个接口可以有多个实现类,它们都会按照接口的这种标准来实现接口的各个方法。因此,理解了一个方法的定义,再去看它的实现会更容易理解。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">下面以常用的 ArrayList 为例,分析如何去阅读它的源码。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>3.2.2 ArrayList 源码分析<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">首先看下 ArrayList 的继承结构:</p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.45610034207525657" data-s="300,640" src="/upload/c6c4f60d244c1b4dee0a2ea0f36dfba8.png" data-type="png" data-w="1754" style=""></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可以看到它实现了很多接口,其中三个接口 Cloneable、RandomAccess、Serializable 都是空的,可以暂时忽略。主要去看 Iterable、Collection 以及 List 接口的方法定义。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Iterable 接口:</p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.44970414201183434" data-s="300,640" src="/upload/f61ee0a1ffe253d665557388ab600e7b.png" data-type="png" data-w="676" style="width: 336px;height: 151px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Collection 接口:</p> <p style="text-align: center;"><img class="rich_pages" data-ratio="1.5351351351351352" data-s="300,640" src="/upload/e69f6528c20eda5d86cf884c7ac99560.png" data-type="png" data-w="740" style="width: 333px;height: 511px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">List 接口:</p> <p style="text-align: center;"><img class="rich_pages" data-ratio="2.035532994923858" data-s="300,640" src="/upload/58e9cecf34ae3331f4e9fd1712aa1059.png" data-type="png" data-w="788" style="width: 339px;height: 690px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">看起来方法挺多,其实不少都是我们平时会用到的,大部分理解起来并不困难,而且方法也都有注释。这部分难度不大。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">接下来根据前面提到的两条主线入口,分析 ArrayList 的源码如何阅读。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 构造器 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分析一个类的源码时,构造器通常是一个好的切入点。比如 ArrayList 的三个构造器如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;background: #282c34;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">ArrayList</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.elementData&nbsp;=&nbsp;DEFAULTCAPACITY_EMPTY_ELEMENTDATA;<br>}<br><br><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">ArrayList</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;initialCapacity)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(initialCapacity&nbsp;&gt;&nbsp;<span style="color: #d19a66;line-height: 26px;">0</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.elementData&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;Object[initialCapacity];<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">else</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(initialCapacity&nbsp;==&nbsp;<span style="color: #d19a66;line-height: 26px;">0</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.elementData&nbsp;=&nbsp;EMPTY_ELEMENTDATA;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">throw</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;IllegalArgumentException(<span style="color: #98c379;line-height: 26px;">"Illegal&nbsp;Capacity:&nbsp;"</span>+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialCapacity);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">ArrayList</span><span style="line-height: 26px;">(Collection&lt;?&nbsp;extends&nbsp;E&gt;&nbsp;c)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;elementData&nbsp;=&nbsp;c.toArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;((size&nbsp;=&nbsp;elementData.length)&nbsp;!=&nbsp;<span style="color: #d19a66;line-height: 26px;">0</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;c.toArray&nbsp;might&nbsp;(incorrectly)&nbsp;not&nbsp;return&nbsp;Object[]&nbsp;(see&nbsp;6260652)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(elementData.getClass()&nbsp;!=&nbsp;Object[].class)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elementData&nbsp;=&nbsp;Arrays.copyOf(elementData,&nbsp;size,&nbsp;Object[].class);<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;replace&nbsp;with&nbsp;empty&nbsp;array.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.elementData&nbsp;=&nbsp;EMPTY_ELEMENTDATA;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">构造器中有不少成员变量,比如 elementData、EMPTY_ELEMENTDATA、DEFAULTCAPACITY_EMPTY_ELEMENTDATA 等,继续跟进这几个变量:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;background: #282c34;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">final</span>&nbsp;Object[]&nbsp;EMPTY_ELEMENTDATA&nbsp;=&nbsp;{};<br><br><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">final</span>&nbsp;Object[]&nbsp;DEFAULTCAPACITY_EMPTY_ELEMENTDATA&nbsp;=&nbsp;{};<br><br><span style="color: #c678dd;line-height: 26px;">transient</span>&nbsp;Object[]&nbsp;elementData;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;non-private&nbsp;to&nbsp;simplify&nbsp;nested&nbsp;class&nbsp;access</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">由此可以得知,当我们写了 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">new ArrayList()</code> 时,它的内部到底做了些什么。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 常用方法 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了构造器,常用方法也是一个主要的入口,比如 add、remove 等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">add 方法实现:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;background: #282c34;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">add</span><span style="line-height: 26px;">(E&nbsp;e)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;ensureCapacityInternal(size&nbsp;+&nbsp;<span style="color: #d19a66;line-height: 26px;">1</span>);&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;Increments&nbsp;modCount!!</span><br>&nbsp;&nbsp;&nbsp;&nbsp;elementData[size++]&nbsp;=&nbsp;e;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">true</span>;<br>}<br><br><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">ensureCapacityInternal</span><span style="line-height: 26px;">(<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;minCapacity)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;ensureExplicitCapacity(calculateCapacity(elementData,&nbsp;minCapacity));<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可以一行行跟进代码,查看 add 方法内部到底做了什么。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其他方法的套路也是如此,不再一一说明。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">按照这样一条条主线走下来,就可以对 ArrayList 的实现原理有个整体的认知了。整体部分搞清楚之后,接下来还可以去读一些不太常用的方法,包括剩余的所有部分。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">PS: 这里只是以常用的 ArrayList 为例,其他包下的类的阅读步骤也大同小异。</p> </blockquote> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.3 做笔记<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">此外,做笔记也很重要。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可以用思维导图梳理整体脉络,用笔记工具记录一个类的核心部分实现原理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当然,如果自己整理和写出来笔记更好,许多时候总觉得自己知道了,但是别人一问就懵了,可能还是没理解到位吧。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">有句话说得好:”教是最好的学“。当你能把某个知识点通俗易懂的讲给一个外行人,才是真的懂了。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>3.4 注意点<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">刚开始读时,可能会遇到某些地方难以理解,可以尝试写测试代码断点跟踪调试,或者参考别人的博客。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果遇到某个点实在难以理解,也可以先跳过,过段时间再重新思考也许就豁然开朗了。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">PS:</p> <ol 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);"> 此外,我把之前文章的笔记整理成了电子书,后台回复「JDK源码」即可获取。 </section></li> </ol> </blockquote> <section> <mp-qa class="js_uneditable custom_select_card qa_iframe" data-pluginname="insertquestion" data-id="1548110088865939457" data-bizuin="MzU4NzYyMDE4MQ==" data-title="大家有好的方法或吐槽欢迎提出来讨论(我也体验下这个功能😀)"></mp-qa> </section> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.5555555555555556" src="/upload/a74e064e7bbc84f09f9106e13470b5ad.png" data-type="gif" data-w="639" style=""></p> </section>

通俗易懂的 RESTful API 设计规范

作者:微信小助手

<section style="font-size: 14px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;" data-mpa-powered-by="yiban.io"> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">RESTful 是目前最流行的 API 设计规范,用于 Web 数据接口的设计。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">它的大原则容易把握,但是细节不容易做对。本文总结 RESTful 的设计细节,介绍如何设计出易于理解和使用的 API。</p> <h2 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.4em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">一 URL设计</span></h2> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">动词+宾语</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">RESTful的核心思想就是,客户端发出的数据+操作指令都是“动词+宾语”的结构,比如GET<br>/articles这个命令,GET是动词,/articles是宾语,动词通常就有5种HTTP请求方法,对应CRUD操作,根据 HTTP 规范,动词一律大写。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># GET:读取(Read)</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># POST:新建(Create)</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># PUT:更新(Update)</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># PATCH:更新(Update),通常是部分更新</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># DELETE:删除(Delete)</span><br></code></pre> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">动词的覆盖</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">有些客户端只能使用 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">GET</code> 和 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">POST</code> 这两种方法。服务器必须接受 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">POST</code> 模拟其他三个方法( <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">PUT</code> 、 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">PATCH</code> 、 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">DELETE</code> )。这时,客户端发出的 HTTP 请求,要加上 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">X-HTTP-Method-Override</code><br>属性,告诉服务器应该使用哪一个动词,覆盖 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">POST</code> 方法。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">POST</span>&nbsp;/api/Person/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">4</span>&nbsp;HTTP/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1</span>.<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1</span>&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;X-HTTP-Method-Override:&nbsp;PUT<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">上面代码中, <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">X-HTTP-Method-Override</code> 指定本次请求的方法是 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">PUT</code> ,而不是 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">POST</code> 。</p> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">宾语必须是名词</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">宾语就是 API 的 URL,是 HTTP 动词作用的对象。它应该是名词,不能是动词。比如, <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">/articles</code> 这个 URL 就是正确的,而下面的<br>URL 不是名词,所以都是错误的。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;/getAllCars</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;/createNewCar</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;/deleteAllRedCars</span><br></code></pre> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">复数 URL</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">既然 URL 是名词,那么应该使用复数,还是单数?这没有统一的规定,但是常见的操作是读取一个集合,比如 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">GET /articles</code><br>(读取所有文章),这里明显应该是复数。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">为了统一起见,建议都使用复数 URL,比如 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">GET /articles/2</code> 要好于 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">GET /article/2</code> 。</p> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">避免多级 URL</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">常见的情况是,资源需要多级分类,因此很容易写出多级的 URL,比如获取某个作者的某一类文章。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;GET&nbsp;/authors/12/categories/2</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">这种 URL 不利于扩展,语义也不明确,往往要想一会,才能明白含义。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">更好的做法是,除了第一级,其他级别都用查询字符串表达。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;GET&nbsp;/authors/12?categories=2</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">下面是另一个例子,查询已发布的文章。你可能会设计成下面的 URL。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;GET&nbsp;/articles/published</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">查询字符串的写法明显更好</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">GET</span>&nbsp;/articles?published=<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">true</span><br></code></pre> <h2 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.4em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">二、状态码</span></h2> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">状态码必须精确</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">客户端的每一次请求,服务器都必须给出回应。回应包括 HTTP 状态码和数据两部分。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">HTTP 状态码就是一个三位数,分成五个类别。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 1xx:相关信息</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 2xx:操作成功</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 3xx:重定向</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 4xx:客户端错误</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 5xx:服务器错误</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">这五大类总共包含 &nbsp;100多种<br>状态码,覆盖了绝大部分可能遇到的情况。每一种状态码都有标准的(或者约定的)解释,客户端只需查看状态码,就可以判断出发生了什么情况,所以服务器应该返回尽可能精确的状态码。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">API 不需要 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">1xx</code> 状态码,下面介绍其他四类状态码的精确含义。</p> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">2XX状态码</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">200</code> 状态码表示操作成功,但是不同的方法可以返回更精确的状态码。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;GET:&nbsp;200&nbsp;OK</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;POST:&nbsp;201&nbsp;Created</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;PUT:&nbsp;200&nbsp;OK</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;PATCH:&nbsp;200&nbsp;OK</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#&nbsp;DELETE:&nbsp;204&nbsp;No&nbsp;Content</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">上面代码中, <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">POST</code> 返回 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">201</code> 状态码,表示生成了新的资源;<code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">DELETE</code> 返回 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">204</code> 状态码,表示资源已经不存在。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">此外, <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">202 Accepted</code> 状态码表示服务器已经收到请求,但还未进行处理,会在未来再处理,通常用于异步操作。下面是一个例子。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;HTTP/1.1&nbsp;202&nbsp;Accepted<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"task"</span>:&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"href"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/api/company/job-management/jobs/2130040"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"id"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"2130040"</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">3xx 状态码</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">API 用不到 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">301</code> 状态码(永久重定向)和 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">302</code> 状态码(暂时重定向, <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">307</code><br>也是这个含义),因为它们可以由应用级别返回,浏览器会直接跳转,API 级别可以不考虑这两种情况。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">API 用到的 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">3xx</code> 状态码,主要是 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">303 See Other</code> ,表示参考另一个 URL。它与 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">302</code> 和 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">307</code><br>的含义一样,也是"暂时重定向",区别在于 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">302</code> 和 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">307</code> 用于 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">GET</code> 请求,而 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">303</code> 用于 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">POST</code> 、 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">PUT</code> 和 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">DELETE</code> 请求。收到 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">303</code> 以后,浏览器不会自动跳转,而会让用户自己决定下一步怎么办。下面是一个例子。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;HTTP/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1.1</span>&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">303</span>&nbsp;See&nbsp;Other<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Location:&nbsp;<span style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">/api/</span>orders/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">12345</span><br></code></pre> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">4xx 状态码</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">4xx</code> 状态码表示客户端错误,主要有下面几种。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">400 Bad Request</code> :服务器不理解客户端的请求,未做任何处理。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">401 Unauthorized</code> :用户未提供身份验证凭据,或者没有通过身份验证。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">403 Forbidden</code> :用户通过了身份验证,但是不具有访问资源所需的权限。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">404 Not Found</code> :所请求的资源不存在,或不可用。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">405 Method Not Allowed</code> :用户已经通过身份验证,但是所用的 HTTP 方法不在他的权限之内。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">410 Gone</code> :所请求的资源已从这个地址转移,不再可用。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">415 Unsupported Media Type</code> :客户端要求的返回格式不支持。比如,API 只能返回 JSON 格式,但是客户端要求返回<br>XML 格式。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">422 Unprocessable Entity</code> :客户端上传的附件无法处理,导致请求失败。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">429 Too Many Requests</code> :客户端的请求次数超过限额。</p> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">5xx 状态码</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">5xx</code> 状态码表示服务端错误。一般来说,API 不会向用户透露服务器的详细信息,所以只要两个状态码就够了。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">500 Internal Server Error</code> :客户端请求有效,服务器处理时发生了意外。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">503 Service Unavailable</code> :服务器无法处理请求,一般用于网站维护状态。</p> <h2 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.4em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">三、服务器回应</span></h2> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">不要返回纯本文</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据。所以,服务器回应的 HTTP 头的 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Content-Type</code> 属性要设为 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">application/json</code> 。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">客户端请求时,也要明确告诉服务器,可以接受 JSON 格式,即请求的 HTTP 头的 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">ACCEPT</code> 属性也要设成 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">application/json</code> 。下面是一个例子。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">GET</span>&nbsp;/orders/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">2</span>&nbsp;HTTP/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1</span>.<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1</span>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Accept:&nbsp;application/json<br></code></pre> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">发生错误时,不要返回 200 状态码</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">有一种不恰当的做法是,即使发生错误,也返回 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">200</code> 状态码,把错误信息放在数据体里面,就像下面这样。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;HTTP/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1.1</span>&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">200</span>&nbsp;OK<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Content-<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">Type</span>:&nbsp;application/json<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"status"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"failure"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"data"</span>:&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"error"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"Expected&nbsp;at&nbsp;least&nbsp;two&nbsp;items&nbsp;in&nbsp;list."</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">上面代码中,解析数据体以后,才能得知操作失败。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">这张做法实际上取消了状态码,这是完全不可取的。正确的做法是,状态码反映发生的错误,具体的错误信息放在数据体里面返回。下面是一个例子。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;HTTP/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1.1</span>&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">400</span>&nbsp;Bad&nbsp;Request<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Content-<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">Type</span>:&nbsp;application/json<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"error"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"Invalid&nbsp;payoad."</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"detail"</span>:&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"surname"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"This&nbsp;field&nbsp;is&nbsp;required."</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre> <h3 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.3em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">提供链接</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">API 的使用者未必知道,URL 是怎么设计的。一个解决方法就是,在回应中,给出相关链接,便于下一步操作。这样的话,用户只要记住一个<br>URL,就可以发现其他的 URL。这种方法叫做 HATEOAS。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">举例来说,GitHub 的 API 都在 &nbsp;api.github.com<br>这个域名。访问它,就可以得到其他 URL。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">...</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"feeds_url"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"https://api.github.com/feeds"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"followers_url"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"https://api.github.com/user/followers"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"following_url"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"https://api.github.com/user/following{/target}"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"gists_url"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"https://api.github.com/gists{/gist_id}"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"hub_url"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"https://api.github.com/hub"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">...</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">上面的回应中,挑一个 URL 访问,又可以得到别的 URL。对于用户来说,不需要记住 URL 设计,只要从 api.github.com<br>一步步查找就可以了。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">HATEOAS 的格式没有统一规定,上面例子中,GitHub 将它们与其他属性放在一起。更好的做法应该是,将相关链接与其他属性分开。</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&nbsp;&nbsp;&nbsp;&nbsp;HTTP/<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1.1</span>&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">200</span>&nbsp;OK<br><br>&nbsp;&nbsp;&nbsp;&nbsp;Content-<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">Type</span>:&nbsp;application/json<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"status"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"In&nbsp;progress"</span>,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"links"</span>:&nbsp;{[<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"rel"</span>:<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"cancel"</span>,&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"method"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"delete"</span>,&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"href"</span>:<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/api/status/12345"</span>&nbsp;}&nbsp;,<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"rel"</span>:<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"edit"</span>,&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"method"</span>:&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"put"</span>,&nbsp;<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"href"</span>:<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"/api/status/12345"</span>&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre> <h2 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;color: rgb(0, 172, 193);font-size: 1.4em;"><span style="font-size: inherit;color: inherit;line-height: inherit;">四、参考链接</span></h2> <ul style="font-size: inherit;color: inherit;line-height: inherit;padding-left: 32px;" class="list-paddingleft-2"> <li style="font-size: inherit;color: inherit;line-height: inherit;margin-bottom: 0.5em;"><p>RESTful API Design: 13 Best Practices to Make Your Users Happy &nbsp;, by Florimond Manca</p></li> <li style="font-size: inherit;color: inherit;line-height: inherit;margin-bottom: 0.5em;"><p>API design &nbsp;, by MicroSoft Azure&nbsp;</p></li> </ul> </section>

啥?我写的一条SQL让公司网站瘫痪了...

作者:微信小助手

<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;">一条慢查询会造成什么后果?之前我一直觉得不就是返回数据会慢一些么,用户体验变差?</span></p> </section> <section style="clear: both;box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </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.5855670103092784" data-s="300,640" src="/upload/31f8f4630a57209af66e8b74914f0797.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> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">其实远远不止,我经历过几次线上事故,有一次就是由一条 SQL 慢查询导致的。</span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><img class="rich_pages" data-ratio="0.6246056782334385" data-s="300,640" src="/upload/c89ced3edfde6a5b18c113551edfcacf.png" data-type="png" data-w="634" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">那次是一条 SQL 查询耗时达到 2-3 秒「没有命中索引,导致全表扫描」,由于是高频查询,并发一起来很快就把 DB 线程池打满了,导致大量查询请求堆积,DB 服务器 CPU 长时间 100%+,大量请求 timeout...<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="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">最终系统崩溃,老板登场!可见,团队如果对慢查询不引起足够的重视,风险是很大的。<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;"> <strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">经过那次事故我们老板就说了:</span></strong> <span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">谁的代码再出现类似事故,开发和部门领导一起走人,吓得一大堆领导心发慌,赶紧招了两位 DBA 同事🙂🙂🙂。</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages" data-ratio="0.5626566416040101" data-s="300,640" src="/upload/f6375df7055d439a7ecccb1875dc7749.png" data-type="png" data-w="798" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">慢查询,顾名思义,执行很慢的查询。有多慢?超过 long_query_time 参数设定的时间阈值(默认 10s),就被认为是慢的,是需要优化的。慢查询被记录在慢查询日志里。</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="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">慢查询日志默认是不开启的,如果你需要优化 SQL 语句,就可以开启这个功能,它可以让你很容易地知道哪些语句是需要优化的(想想一个 SQL 要 10s 就可怕)。好了,下面我们就一起来看看怎么处理慢查询。</span></p> <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="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;">慢查询配置</p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>开启慢查询</strong></p> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;color: rgb(71, 193, 168);">MySQL 支持通过以下方式开启慢查询:</span><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;"><br></span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">输入命令开启慢查询(临时),在 MySQL 服务重启后会自动关闭。<br></span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">配置 my.cnf(Windows 是 my.ini)系统文件开启,修改配置文件是持久化开启慢查询的方式。</span></p></li> </ul> <h3 data-anchor-id="7l92" style="line-height: normal;"><br></h3> <h3 data-anchor-id="7l92" style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">方式一:通过命令开启慢查询</span></strong></h3> <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;"> <strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">步骤 1:</span></strong> <span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">查询 slow_query_log 查看是否已开启慢查询日志:<br></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;">show</span>&nbsp;<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;">variables</span>&nbsp;<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;">like</span>&nbsp;<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;">'%slow_query_log%'</span>;<br></code></pre> </section> <section style="line-height: normal;"> <br> </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;">mysql&gt;&nbsp;show&nbsp;variables&nbsp;like&nbsp;<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;">'%slow_query_log%'</span>;<br>+---------------------+-----------------------------------+<br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>&nbsp;Value&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|<br>+---------------------+-----------------------------------+<br>|</span>&nbsp;slow_query_log&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;OFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span><br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;slow_query_log_file&nbsp;|</span>&nbsp;/var/lib/mysql/localhost-slow.log&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|<br>+---------------------+-----------------------------------+<br>2&nbsp;rows&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">in</span>&nbsp;set&nbsp;(0.01&nbsp;sec)<br></span></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;"> <strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">步骤 2:</span></strong> <span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">开启慢查询命令:<br></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;">set</span>&nbsp;<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;">global</span>&nbsp;slow_query_log=<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;">'ON'</span>;<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="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">步骤 3:</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">指定记录慢查询日志 SQL 执行时间得阈值(long_query_time 单位:秒,默认 10 秒)。</span></p> <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="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">如下我设置成了 1 秒,执行时间超过 1 秒的 SQL 将记录到慢查询日志中:<br></span></p> <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;">set</span>&nbsp;<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;">global</span>&nbsp;long_query_time=<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>;<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;"> <strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">步骤 4:</span></strong> <span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">查询 “慢查询日志文件存放位置”。<br></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;">show</span>&nbsp;<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;">variables</span>&nbsp;<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;">like</span>&nbsp;<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;">'%slow_query_log_file%'</span>;<br></code></pre> </section> <section style="line-height: normal;"> <br> </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;">mysql&gt;&nbsp;show&nbsp;variables&nbsp;like&nbsp;<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;">'%slow_query_log_file%'</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>|&nbsp;Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;Value&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<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>|&nbsp;slow_query_log_file&nbsp;|&nbsp;/var/lib/mysql/localhost-slow.<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;">log</span>&nbsp;|<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(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1</span>&nbsp;row&nbsp;<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;">in</span>&nbsp;set&nbsp;(<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;">0.01</span>&nbsp;sec)<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;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">slow_query_log_file 指定慢查询日志的存储路径及文件(默认和数据文件放一起)。</span></p> <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="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">步骤 5:</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">核对慢查询开启状态,需要退出当前 MySQL 终端,重新登录即可刷新。</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="letter-spacing: 1px;font-size: 15px;color: rgb(71, 193, 168);">配置了慢查询后,它会记录以下符合条件的 SQL:</span><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;"><br></span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li style="font-weight: bold;"><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">查询语句<br></span></strong></p></li> <li style="font-weight: bold;"><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">数据修改语句<br></span></strong></p></li> <li style="font-weight: bold;"><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">已经回滚的 SQL</span></strong></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">方式二:通过配置 my.cnf(Windows 是 my.ini)系统文件开启</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">(版本:MySQL 5.5 及以上)。</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="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">在 my.cnf 文件的 [mysqld] 下增加如下配置开启慢查询,如下图:<br></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;">#&nbsp;开启慢查询功能</span><br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">slow_query_log</span>=<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;">ON</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;">#&nbsp;指定记录慢查询日志SQL执行时间得阈值</span><br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">long_query_time</span>=<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><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;">#&nbsp;选填,默认数据文件路径</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;">#&nbsp;slow_query_log_file=/var/lib/mysql/localhost-slow.log</span><br></code></pre> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages" data-ratio="0.4174454828660436" data-s="300,640" src="/upload/369bf4c8ce2d2d58e31852836387552f.png" data-type="png" data-w="642" style=""> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">重启数据库后即持久化开启慢查询,查询验证如下:<br></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;">mysql&gt;&nbsp;show&nbsp;variables&nbsp;like&nbsp;<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;">'%_query_%'</span>;<br>+------------------------------+-----------------------------------+<br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>&nbsp;Value&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|<br>+------------------------------+-----------------------------------+<br>|</span>&nbsp;have_query_cache&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;YES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span><br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;long_query_time&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>&nbsp;<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.000000</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|<br>|</span>&nbsp;slow_query_log&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;ON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span><br><span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|&nbsp;slow_query_log_file&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>&nbsp;/var/lib/mysql/localhost-slow.log&nbsp;<span style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">|<br>+------------------------------+-----------------------------------+<br>6&nbsp;rows&nbsp;<span style="line-height: inherit;margin: 0px;padding: 0px;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">in</span>&nbsp;set&nbsp;(0.01&nbsp;sec)<br></span></code></pre> </section> <p style="line-height: normal;"><br></p> <section> <section> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin: 2px 0px;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;background-color: rgb(89, 89, 89);box-sizing: border-box;"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong>慢查询日志介绍</strong></p> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages" data-ratio="0.37249544626593806" data-s="300,640" src="/upload/5fb446cbe73487b4796cba7e6c52d250.png" data-type="png" data-w="1098" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="letter-spacing: 1px;font-size: 15px;color: rgb(71, 193, 168);">如上图,是执行时间超过 1 秒的 SQL 语句(测试):</span></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">第一行:</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">记录时间。</span></p></li> <li><p><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">第二行:</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">用户名 、用户的 IP 信息、线程 ID 号。</span></p></li> <li><p><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">第三行:</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">执行花费的时间【单位:秒】、执行获得锁的时间、获得的结果行数、扫描的数据行数。</span></p></li> <li><p><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">第四行:</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">这 SQL 执行的时间戳。</span></p></li> <li><p><strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">第五行:</span></strong><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">具体的 SQL 语句。</span></p></li> </ul> <p style="text-align: justify;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="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;">Explain 分析慢查询 SQL</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;">分析 MySQL 慢查询日志,利用 Explain 关键字可以模拟优化器执行 SQL 查询语句,来分析 SQL 慢查询语句。<br></span></p> <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;">下面我们的测试表是一张 137w 数据的 app 信息表,我们来举例分析一下。</span></p> <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="letter-spacing: 1px;font-size: 15px;color: rgb(71, 193, 168);">SQL 示例如下:<br></span></p> <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;">--&nbsp;1.185s</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;">SELECT</span>&nbsp;*&nbsp;<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;">from</span>&nbsp;vio_basic_domain_info&nbsp;<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;">where</span>&nbsp;app_name&nbsp;<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;">like</span>&nbsp;<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;">'%翻译%'</span>&nbsp;;<br></code></pre> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">这是一条普通的模糊查询语句,查询耗时:1.185s,查到了 148 条数据。<br style="color: rgb(44, 62, 80);font-family: &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Helvetica Neue&quot;, &quot;Microsoft Yahei&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(249, 249, 245);text-decoration-style: initial;text-decoration-color: initial;"></span> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 15px;">我们用 Explain 分析结果如下表,根据表信息可知:该 SQL 没有用到字段 app_name 上的索引,查询类型是全表扫描,扫描行数 137w。<br></span></p> <section> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;"><code style="white-space:pre-wrap;overflow-wrap: break

Spring Cloud架构的各个组件的原理分析

作者:微信小助手

<p style="margin-bottom: 10px;color: rgb(62, 62, 62);font-size: 15px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);line-height: normal;text-align: center;" data-mpa-powered-by="yiban.io"><span style="font-size: 14px;letter-spacing: 0.544px;word-spacing: 2px;color: rgb(136, 136, 136);">点击蓝色“</span><span style="font-size: 14px;letter-spacing: 0.544px;word-spacing: 2px;color: rgb(0, 128, 255);">架构文摘</span><span style="font-size: 14px;letter-spacing: 0.544px;word-spacing: 2px;color: rgb(136, 136, 136);">”关注我哟</span></p> <p style="margin-bottom: 10px;color: rgb(62, 62, 62);font-size: 15px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-size-adjust: auto;word-spacing: 2px;text-align: center;"><span style="font-size: 14px;color: rgb(136, 136, 136);">加个“</span><span style="color: rgb(0, 128, 255);font-size: 14px;">星标</span><span style="font-size: 14px;color: rgb(136, 136, 136);">”,每天上午 09:25,干货推送!</span></p> <p style="color: rgb(62, 62, 62);white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 14px;text-align: center;"><img data-backh="36" data-backw="578" data-ratio="0.0625" data-s="300,640" data-type="jpeg" data-w="640" width="100%" src="/upload/8c292e55ba5a23cb6ebc11f2a2c4fece.jpg" style="font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;widows: 1;word-spacing: 2px;color: rgb(136, 136, 136);box-sizing: border-box !important;visibility: visible !important;width: 677px !important;"></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 我们先认识一下SpringCloud的各个组件,然后知其所以然。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <p style="text-align: center;"><img data-ratio="0.6056451612903225" src="/upload/8f2a0dd8fb0c0ed769c21aa0dc3d0460.png" data-type="png" data-w="1240"></p> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 原理讲解前,先看一个最经典的业务场景,如开发一个电商网站,要实现支付订单的功能,流程如下: </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br mpa-from-tpl="t"> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <ol class="list-paddingleft-2" mpa-from-tpl="t" style="padding-left: 2.2em;color: rgb(51, 51, 51);font-size: 17px;letter-spacing: 0.544px;text-align: justify;list-style-type: decimal;"> <li style="font-size: 15px;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"><span style="font-size: 15px;">创建一个订单之后,如果用户立刻支付了这个订单,我们需要将订单状态更新为“已支付”</span></p></li> <li style="font-size: 15px;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"><span style="font-size: 15px;">扣减相应的商品库存</span></p></li> <li style="font-size: 15px;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;font-size: 15px;">通知仓储中心,进行发货</span></p></li> <li style="font-size: 15px;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"><span style="font-size: 15px;">给用户的这次购物增加相应的积分</span></p></li> </ol> </section> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <p style="text-align: center;"><img data-ratio="0.40369393139841686" src="/upload/238d15f71815d25dba3bb544e724a41c.jpg" data-type="jpeg" data-w="758"></p> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 如上,微服务的应用场景和核心竞争力: </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br mpa-from-tpl="t"> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t" style="color: rgb(51, 51, 51);font-size: 17px;text-align: justify;"> <ul class="list-paddingleft-2" mpa-from-tpl="t" style="padding-left: 2.2em;width: 577.422px;list-style-type: square;"> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">降低耦合:每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开发效率。</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">独立部署:由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">选型灵活:微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。由于每个微服务相对简单,故需要对技术栈进行升级时所面临的风险就较低,甚至完全重构一个微服务也是可行的。</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">容错机制:</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">当某一组建发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">在微服务架构下,故障会被隔离在单个服务中。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。</span></p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">灵活扩展:单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。</p></li> </ul> </section> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <p style="text-align: center;"><img data-ratio="0.4515625" src="/upload/e0239c9dbcc1e8b9c52d0cd7a11eb1ec.png" data-type="png" data-w="1280"></p> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> Dubbo对标Spring Cloud微服务: </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br mpa-from-tpl="t"> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t" style="color: rgb(51, 51, 51);font-size: 17px;text-align: justify;"> <ul class="list-paddingleft-2" mpa-from-tpl="t" style="padding-left: 2.2em;width: 577.422px;list-style-type: square;"> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">背景分析:Dubbo,是阿里巴巴服务化治理的核心框架,并被广泛应用于中国各互联网公司;Spring Cloud是知名的Spring家族的产品。阿里巴巴是一个商业公司,虽然也开源了很多的顶级的项目,但从整体战略上来讲,仍然是服务于自身的业务为主。Spring专注于企业级开源框架的研发,不论是在中国还是在世界上使用都非常广泛,开发出通用、开源、稳健的开源框架就是他们的主业。</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">活跃度对比:Dubbo是一个非常优秀的服务治理框架,并且在服务治理、灰度发布、流量分发这方面做的比Spring Cloud还好,除过当当网在基础上增加了rest支持外,已有两年多的时间几乎都没有任何更新了。在使用过程中出现问题,提交到GitHub的Issue也少有回复。相反Spring Cloud自从发展到现在,仍然在不断的高速发展,从GitHub上提交代码的频度和发布版本的时间间隔就可以看出,现在Spring Cloud即将发布2.0版本,到了后期会更加完善和稳定。</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">平台架构:Dubbo框架只是专注于服务之间的治理,如果我们需要使用配置中心、分布式跟踪这些内容都需要自己去集成,这样无形中使用Dubbo的难度就会增加。Spring Cloud几乎考虑了服务治理的方方面面,更有Spring Boot这个大将的支持,开发起来非常的便利和简单。</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">技术前景:</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">Dubbo在各中小公司也从中受益不少。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">经过了这么多年的发展,互联网行业也是涌现了更多先进的技术和理念,Dubbo有点可惜。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">Spring 推出Spring Boot/Cloud也是因为自身的很多原因。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">Spring最初推崇的轻量级框架,随着不断的发展也越来越庞大,随着集成项目越来越多,配置文件也越来越混乱,慢慢的背离最初的理念。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">随着这么多年的发展,微服务、分布式链路跟踪等更多新的技术理念的出现,Spring急需一款框架来改善以前的开发模式,因此才会出现Spring Boot/Cloud项目,我们现在访问Spring官网,会发现Spring Boot和Spring Cloud已经放到首页最重点突出的三个项目中的前两个,可见Spring对这两个框架的重视程度。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;">Dubbo实现如下:</span></p><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t"><img data-ratio="0.4467741935483871" src="/upload/51b2bf23e87ddc7c56ebe824612ac602.png" data-type="png" data-w="620" style="color: rgb(51, 51, 51);font-size: 17px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;"><br></p></li> </ul> </section> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <p><br></p> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> Spring Cloud实现思路: </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <p style="text-align: center;"><img data-ratio="0.3796875" src="/upload/c25dfbff99066246fedc52af525e8d7d.png" data-type="png" data-w="1280"></p> </section> </section> </section> <p><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section style="margin: 0px;padding: 0px 10px 0px 9px;color: rgb(60, 60, 60);font-size: 16px;font-weight: bold;letter-spacing: 1px;text-align: center;white-space: normal;line-height: 1.8;background: rgb(255, 255, 255);z-index: 10000;" mpa-from-tpl="t" mpa-is-content="t"> Eureka </section> <p><img data-type="png" data-ratio="0.053125" data-w="640" src="/upload/16c3bc26d4547f0344ee22af769e1434.png" style="margin-right: 0px;margin-bottom: 0px;margin-left: 0px;padding: 0px;color: rgb(60, 60, 60);font-weight: bold;letter-spacing: 1px;text-align: center;white-space: normal;background-color: rgb(255, 255, 255);font-size: 15px;display: inline-block;left: 0px;transform: rotateX(60deg);margin-top: 5px !important;height: auto !important;width: 632px !important;visibility: visible !important;"></p> <p><br></p> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 原理:主管服务注册与发现,也就是微服务的名称注册到Eureka,就可以通过Eureka找到微服务,而不需要修改服务调用的配置文件。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 分析:Spring Cloud封装了Netflix公司开发的Eureka模块来实现服务的注册与发现,采用的c-s的设计架构,Eureka Server作为服务注册功能的服务器,他是服务注册中心。而系统的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳。这样系统的维护人员可以通过Eureka Server来监控系统中的各个微服务是否正常运行。Spring Cloud的一些其他模块(比如Zuul)就可以通过Eureka Server来发现系统其他的微服务,并执行相关逻辑。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <strong>Eureka Server</strong> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册, 这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <strong>Eureka Client</strong> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> Eureka Client是一个Java客户端, 用于简化Eureka Server的交互,客户端同时也具备一个内置的、 使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒),以证明当前服务是可用状态。如果Eureka Server在一定的时间(默认90秒)未收到客户端的心跳,Eureka Server将会从服务注册表中把这个服务节点移除。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <strong>Eureka Server的自我保护机制</strong> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况: </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br mpa-from-tpl="t"> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t" style="color: rgb(51, 51, 51);font-size: 17px;text-align: justify;"> <ul class="list-paddingleft-2" mpa-from-tpl="t" style="padding-left: 2.2em;width: 577.422px;list-style-type: square;"> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)</p></li> <li style="clear: both;"><p style="padding-right: 10px;padding-left: 9px;clear: both;min-height: 1em;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);" mpa-is-content="t">当网络稳定时,当前实例新的注册信息会被同步到其它节点中</p></li> </ul> </section> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper那样使整个注册服务瘫痪。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <strong>Eureka和ZooKeeper</strong> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <span style="color: rgb(136, 136, 136);"><strong>ZooKeeper保证CP</strong></span> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> 当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是ZooKeeper会出现这样一种情况,当Master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s,且选举期间整个ZooKeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得ZooKeeper集群失去Master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。 </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <span style="color: rgb(136, 136, 136);"><strong>Eureka保证AP</strong></span> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> <br> </section> <section mpa-from-tpl="t" mpa-is-content="t" style="padding-right: 10px;padding-left: 9px;text-align: justify;font-size: 15px;letter-spacing: 1px;line-height: 1.8;color: rgb(60, 60, 60);"> Eurek在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证

接口对前后端和测试的意义

作者:测试人生路

1.什么是接口? 接口测试主要用于外部系统与系统之间以及内部各个子系统之间的交互点,定义特定的交互点,然后通过这些交互点来,通过一些特殊的规则也就是协议,来进行数据之间的交互。 2.接口都有哪些类型?   接口一般分为两种:1.程序内部的接口 2.系统对外的接口   系统对外的接口:比如你要从别的网站或服务器上获取资源或信息,别人肯定不会把 数据库共享给你,他只能给你提供一个他们写好的方法来获取数据,你引用他提供的接口就能使用他写好的方法,从而达到数据共享的目的。   程序内部的接口:方法与方法之间,模块与模块之间的交互,程序内部抛出的接口,比如bbs系统,有登录模块、发帖模块等等,那你要发帖就必须先登录,那么这两个模块就得有交互,它就会抛出一个接口,供内部系统进行调用。   接口的分类:1.webservice接口 2.http api接口   webService接口是走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都用通过工具才能进行调用,测试。   http api接口是走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串,有get和post等方法,这也是最常用的两种请求方式。   json是一种通用的数据类型,所有的语言都认识它。(json的本质是字符串,他与其他语言无关,只是可以经过稍稍加工可以转换成其他语言的数据类型,比如可以转换成 Python中的字典,key-value的形式,可以转换成JavaScript中的原生对象,可以转换成 java中的类对象等。) 3.前后端和测试对应的接口使用 由于现在一款程序的开发实现的前后端分离,前端和后端直接的数据传输和交互都是通过接口来进行操作的。 前端:通过后端人员给的接口文档,来进行Ajax的设计,通过接口向服务发送请求,获取响应的数据,然后通过返回的数据进行下一步的页面跳转和显示。 后端:通过编写接口,为前端提供与服务器和数据请求交互的通道。编写对应的接口,需要传递的参数,参数类型等等。然后生成接口文档,分享给前端,让其按照接口文档编写对应的Ajax。 测试:可以通过接口文档,进行接口验证,查看后端开发的接口和前端所写的Ajax是否对应,有没有出错的接口,还可通过接口流程测试,知道整个系统之间的接口是否是相对应的,有没有接口是不对的,或者没有正常运行。 4.接口测试和接口文档生成的工具 接口测试工具:jmeter、apipost、postman jmeter:针对于接口测试和性能测试。它的功能主要是性能测试方面强大。 apipost和postman:针对于接口测试,功能都差不多,唯一不用的是apipost是中文版的还可以生成各种类型的接口文档。postman英语版的接口文档生成也是英文的。 接口文档生成工具:swagger、apipost swagger是一款通过接口注释生成接口文档的工具,不过生成的接口文档也全是英文的。 apipost是通过对开发好的接口进行测试生成的接口文档,文档可以生成在线的html、markdown和word格式的。 工具下载地址: apipost下载地址:http://www.apipost.cn jmeter下载地址:http://www.apipost.cn swagger下载地址:http://swagger.io/ 转载自:http://zhuanlan.zhihu.com/p/268703393

《我想进大厂》之JVM夺命连环10问

作者:微信小助手

<p style="white-space: normal;text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote style="white-space: normal;"> <p style="letter-spacing: 0.5440000295639038px;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;">作者:&nbsp;科技缪缪(本文来自作者投稿)</span></p> </blockquote> <p style="white-space: normal;text-align: left;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">1. 说说 JVM 的内存布局?</span></strong></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;"></span></strong></span></h2> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.37890625" data-s="300,640" src="/upload/795dc29a63a0fd302c23b30837df691d.png" data-type="png" data-w="1280" style=""></p> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">Java 虚拟机主要包含几个区域:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">堆</span></strong><span style="font-size: 15px;">:堆是 Java 虚拟机中最大的一块内存,是线程共享的内存区域,基本上所有的对象实例数组都是在堆上分配空间。堆区细分为 Young</span><span style="font-size: 15px;">&nbsp;区年轻代和 Old 区老年代,其中年轻代又分为 Eden、S0、S1 3个部分,他们默认的比例是 8:1:1 的大小。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">栈</span></strong><span style="font-size: 15px;">:栈是线程私有的内存区域,每个方法执行的时候都会在栈创建一个栈帧,方法的调用过程就对应着栈的入栈和出栈的过程。每个栈帧的结构又包含局部变量表、操作数栈、动态连接、方法返回地址。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">局部变量表用于存储方法参数和局部变量。当第一个方法被调用的时候,它的参数会被传递至从0开始的连续的局部变量表中。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">操作数栈用于一些字节码指令从局部变量表中传递至操作数栈,也用来准备方法调用的参数以及接收方法返回结果。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">动态连接用于将符号引用表示的方法转换为实际方法的直接引用。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">元数据</span></strong><span style="font-size: 15px;">:在 Java1.7 之前,包含方法区的概念,常量池就存在于方法区(永久代)中,而方法区本身是一个逻辑上的概念,在1.7之后则是把常量池移到了堆内,1.8之后移出了永久代的概念(方法区的概念仍然保留),实现方式则是现在的元数据。它包含类的元信息和运行时常量池。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">class 文件就是类和接口的定义信息。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">运行时常量池就是类和接口的常量池运行时的表现形式。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">本地方法栈</span></strong><span style="font-size: 15px;">:主要用于执行本地 native 方法的区域。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">程序计数器</span></strong><span style="font-size: 15px;">:也是线程私有的区域,用于记录当前线程下虚拟机正在执行的字节码的指令地址。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">2. 知道 new 一个对象的过程吗?</span></strong></span></h2> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"></span><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.40390625" data-s="300,640" src="/upload/6f532d38987c352d0423c52ab2aed6b9.png" data-type="png" data-w="1280" style=""></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">当虚拟机遇见 <strong>new </strong>关键字时候,实现判断当前类是否已经加载。如果类没有加载,首先执行类的加载机制,加载完成后再为对象分配空间、初始化等。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="text-align: left;"><span style="font-size: 15px;">首先<strong>校验当前类是否被加载</strong>,如果没有加载,执行类加载机制;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">加载</span></strong><span style="font-size: 15px;">:就是从字节码加载成二进制流的过程;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">验证</span></strong><span style="font-size: 15px;">:当然加载完成之后,当然需要校验 class 文件是否符合虚拟机规范,跟我们接口请求一样,第一件事情当然是先做个参数校验了;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">准备</span></strong><span style="font-size: 15px;">:为静态变量、常量赋默认值;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">解析</span></strong><span style="font-size: 15px;">:把常量池中符号引用(以符号描述引用的目标)替换为直接引用(指向目标的指针或者句柄等)的过程;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">初始化</span></strong><span style="font-size: 15px;">:执行 static 代码块 (cinit) 进行初始化,如果存在父类,先对父类进行初始化。</span></p></li> </ol> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">注意</span></strong><span style="font-size: 15px;">:静态代码块是绝对线程安全的,只能隐式被 Java 虚拟机在类加载过程中初始化调用!(此处该有问题:<strong>static 代码块线程安全吗?</strong>)</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">当类加载完成之后,紧接着就是对象分配内存空间和初始化的过程:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="text-align: left;"><span style="font-size: 15px;">首先为对象分配合适大小的内存空间;</span></p></li> <li><p style="text-align: left;"><span style="font-size: 15px;">接着为实例变量赋默认值;</span></p></li> <li><p style="text-align: left;"><span style="font-size: 15px;">设置对象的头信息,对象 hashcode、GC 分代年龄、元数据信息等;</span></p></li> <li><p style="text-align: left;"><span style="font-size: 15px;">执行构造函数 (init) 初始化。</span></p></li> </ol> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">3. 知道双亲委派模型吗?</span></strong></span></h2> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">类加载器自顶向下分为:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">Bootstrap ClassLoader</span></strong><span style="font-size: 15px;">(启动类加载器):默认会去加载 JAVA_HOME/lib 目录下的 jar;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">Extention ClassLoader</span></strong><span style="font-size: 15px;">(扩展类加载器):默认去加载 JAVA_HOME/lib/ext 目录下的 jar;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">Application ClassLoader</span></strong><span style="font-size: 15px;">(应用程序类加载器):比如我们的 Web 应用,会加载 Web 程序中 ClassPath 下的类;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">User ClassLoader</span></strong><span style="font-size: 15px;">(用户自定义类加载器):由用户自己定义。</span></p></li> </ol> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <br> </section> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">当我们在加载类的时候,首先都会向上询问自己的父加载器是否已经加载。如果没有则依次向上询问;如果没有加载,则从上到下依次尝试是否能加载当前类,直到加载成功。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.64609375" data-s="300,640" src="/upload/cd8622d790bf51cd40e21dba364923c2.png" data-type="png" data-w="1280" style=""></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;"><br></span></strong></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;">4. 说说有哪些垃圾回收算法?</span></strong><br><span style="font-size: 15px;"></span></p> <h3 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></h3> <h3 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">标记-清除</span></strong></h3> <p><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">统一标记出需要回收的对象,标记完成之后统一回收所有被标记的对象。而由于标记的过程需要遍历所有的 GC ROOT,清除的过程也要遍历堆中所有的对象,所以标记-清除算法的效率低下,同时也带来了内存碎片的问题。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h3 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">复制算法</span></strong><span style="font-size: 15px;"></span></h3> <p><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">为了解决性能的问题,复制算法应运而生。它将内存分为大小相等的两块区域,每次使用其中的一块。当一块内存使用完之后,将还存活的对象拷贝到另外一块内存区域中,然后把当前内存清空。这样性能和内存碎片的问题得以解决。但是同时带来了另外一个问题,可使用的内存空间缩小了一半!</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">因此,诞生了我们现在的常见的<strong>年轻代+老年代</strong>的内存结构:Eden+S0+S1 组成。因为根据 IBM 的研究显示,98%的对象都是朝生夕死,所以实际上存活的对象并不是很多,完全不需要用到一半内存浪费,所以默认的比例是 8:1:1。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">这样,在使用的时候只使用 Eden 区和 S0、S1 中的一个,每次都把存活的对象拷贝另外一个未使用的 Survivor 区,同时清空 Eden 和使用的 Survivor,这样下来内存的浪费就只有10%了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">如果最后未使用的 Survivor 放不下存活的对象,这些对象就进入 Old 老年代了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">注意</span></strong><span style="font-size: 15px;">:所以有一些初级点的问题会问你,为什么要分为 Eden 区和2个 Survior 区?有什么作用?就是为了节省内存和解决内存碎片的问题。这些算法都是为了解决问题而产生的,如果理解原因你就不需要死记硬背了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h3 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">标记-整理</span></strong><span style="font-size: 15px;"></span></h3> <p><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">针对老年代再用复制算法显然不合适,因为进入老年代的对象都存活率比较高了,这时候再频繁的复制对性能影响就比较大,而且也不会再有另外的空间进行兜底。所以针对老年代的特点,通过<strong>标记-整理</strong>算法,标记出所有的存活对象,让所有存活的对象都向一端移动,然后清理掉边界以外的内存空间。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;">5. 什么是 GC ROOT?有哪些 GC ROOT?</span></strong></h2> <p><span style="font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></span></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">上面提到的标记的算法,怎么标记一个对象是否存活?简单的通过引用计数法,给对象设置一个引用计数器,每当有一个地方引用他,就给计数器+1,反之则计数器-1,但是这个简单的算法<strong>无法解决循环引用的问题</strong>。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">Java 通过可达性分析算法来达到标记存活对象的目的,定义一系列的 GC ROOT 为起点。从起点开始向下开始搜索,搜索走过的路径称为引用链。当一个对象到 GC ROOT没有任何引用链相连的话,则对象可以判定是可以被回收的。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">而可以作为 GC ROOT 的对象包括:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">栈中引用的对象;</span></p></li> <li><p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">静态变量、常量引用的对象;</span></p></li> <li> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">本地方法栈 native 方法引用的对象。</span></p> </section></li> </ol> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <br> </section> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;">6. 垃圾回收器了解吗?年轻代和老年代都有哪些垃圾回收器?</span></strong></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.41953125" data-s="300,640" src="/upload/453587d477ee0bc2e705f264726153cf.png" data-type="png" data-w="1280" style=""></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"></span><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">年轻代的垃圾收集器包含有 Serial、ParNew、Parallell。老年代则包括 Serial Old 老年代版本、CMS、Parallel Old 老年代版本和 JDK11 中全新的 G1 收集器。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">Serial</span></strong><span style="font-size: 15px;">:单线程版本收集器,进行垃圾回收的时候会 STW(Stop The World),也就是进行垃圾回收的时候其他的工作线程都必须暂停。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">ParNew</span></strong><span style="font-size: 15px;">:Serial 的多线程版本,用于和 CMS 配合使用。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">Parallel Scavenge</span></strong><span style="font-size: 15px;">:可以并行收集的多线程垃圾收集器。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">Serial Old</span></strong><span style="font-size: 15px;">:Serial 的老年代版本,也是单线程。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">Parallel Old</span></strong><span style="font-size: 15px;">:Parallel Scavenge 的老年代版本。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">CMS(Concurrent Mark Sweep)</span></strong><span style="font-size: 15px;">:CMS 收集器是以获取最短停顿时间为目标的收集器。相对于其他的收集器 STW 的时间更短暂,可以并行收集是它的特点,同时它基于<strong>标记-清除</strong>算法。整个 GC 过程分为4步:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">初始标记</span></strong><span style="font-size: 15px;">:标记 GC ROOT 能关联到的对象,需要 STW;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">并发标记</span></strong><span style="font-size: 15px;">:从 GCRoots 的直接关联对象开始遍历整个对象图的过程,不需要 STW;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">重新标记</span></strong><span style="font-size: 15px;">:为了修正并发标记期间,因用户程序继续运作而导致标记产生改变的标记,需要 STW;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">并发清除</span></strong><span style="font-size: 15px;">:清理删除掉标记阶段判断的已经死亡的对象,不需要 STW。</span></p></li> </ol> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">从整个过程来看,并发标记和并发清除的耗时最长,但是不需要停止用户线程。而初始标记和重新标记的耗时较短,但是需要停止用户线程。总体而言,整个过程造成的停顿时间较短,大部分时候是可以和用户线程一起工作的。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">G1(Garbage First)</span></strong><span style="font-size: 15px;">:G1 收集器是 JDK9 的默认垃圾收集器,不再区分年轻代和老年代进行回收。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;">7. G1的原理了解吗?</span></strong></h2> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"></span><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.54453125" data-s="300,640" src="/upload/e0312813ebca3b2908451df818283385.png" data-type="png" data-w="1280" style=""></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">G1 作为 JDK9 之后的服务端默认收集器,不再区分年轻代和老年代进行垃圾回收。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">把内存划分为多个 Region,每个 Region 的大小可以通过 <strong>-XX:G1HeapRegionSize</strong> 设置,大小为1~32M。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">对于大对象的存储则衍生出 <strong>Humongous </strong>的概念。超过 Region 大小一半的对象会被认为是大对象,而超过整个 Region 大小的对象被认为是超级大对象,将会被存储在连续的 N 个 Humongous Region 中。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">G1 在进行回收的时候会在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间优先回收收益最大的 Region。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">G1 的回收过程分为以下四个步骤:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">初始标记</span></strong><span style="font-size: 15px;">:标记 GC ROOT 能关联到的对象,需要 STW;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">并发标记</span></strong><span style="font-size: 15px;">:从 GCRoots 的直接关联对象开始遍历整个对象图的过程,扫描完成后还会重新处理并发标记过程中产生变动的对象;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">最终标记</span></strong><span style="font-size: 15px;">:短暂暂停用户线程,再处理一次,需要 STW;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">筛选回收</span></strong><span style="font-size: 15px;">:更新 Region 的统计数据,对每个 Region 的回收价值和成本排序,根据用户设置的停顿时间制定回收计划。再把需要回收的 Region 中存活对象复制到空的 Region,同时清理旧的 Region。需要 STW。</span></p></li> </ol> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <br> </section> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">总的来说除了并发标记之外,其他几个过程也还是需要短暂的 STW。G1 的目标是在停顿和延迟可控的情况下尽可能提高吞吐量。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;">8. 什么时候会触发 YGC 和 FGC?对象什么时候会进入老年代?</span></strong></h2> <p><span style="font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></span></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">当一个新的对象来申请内存空间的时候,如果 Eden 区无法满足内存分配需求,则触发 YGC。使用中的 Survivor 区和 Eden 区存活对象送到未使用的 Survivor 区。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">如果 YGC 之后还是没有足够空间,则直接进入老年代分配。如果老年代也无法分配空间,触发 FGC,FGC 之后还是放不下则报出 OOM 异常。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="1.03125" data-s="300,640" src="/upload/b1e5f1f879ff8e04cb31b94e5dc2995b.png" data-type="png" data-w="1280" style=""></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">YGC 之后,存活的对象将会被复制到未使用的 Survivor 区。如果 S 区放不下,则直接晋升至老年代。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">而对于那些一直在 Survivor 区来回复制的对象,通过 <strong>-XX:MaxTenuringThreshold</strong> 配置交换阈值,默认15次。如果超过次数同样进入老年代。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">此外,还有一种动态年龄的判断机制,不需要等到 MaxTenuringThreshold 就能晋升老年代。如果在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;">9. 频繁 FullGC 怎么排查?</span></strong></h2> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">这种问题最好的办法就是结合有具体的例子举例分析,如果没有就说一般的分析步骤。发生 FGC 有可能是内存分配不合理,比如 Eden 区太小,导致对象频繁进入老年代,这时候通过启动参数配置就能看出来,另外有可能就是存在内存泄露,可以通过以下的步骤进行排查:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <span style="font-size: 15px;">1. <strong>jstat -gcutil</strong> 或者查看 gc.log 日志,查看内存回收情况。</span> </section> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <span style="font-size: 15px;"><br></span> </section> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.35078125" data-s="300,640" src="/upload/4c3555eeef02aa798a0f2be02456b734.png" data-type="png" data-w="1280" style=""></p> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <span style="font-size: 15px;"></span> <br> </section> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;">S0、S1 分别代表两个 Survivor 区占比;</span></p></li> <li><p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;">E 代表 Eden 区占比,图中可以看到使用了78%;</span></p></li> <li><p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;">O 代表老年代,M 代表元空间,YGC 发生54次,YGCT 代表 YGC 累计耗时,GCT 代表 GC 累计耗时。</span></p></li> </ul> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.196875" data-s="300,640" src="/upload/85905c1ee1b0aca8d89b00342cb30b75.png" data-type="png" data-w="1280" style=""></p> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;color: rgb(136, 136, 136);">[GC</span><span style="font-size: 15px;"> 或 </span><span style="font-size: 15px;color: rgb(136, 136, 136);">[FGC</span><span style="font-size: 15px;"> 开头代表垃圾回收的类型;</span></p></li> <li><p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;color: rgb(136, 136, 136);">PSYoungGen: 6130K-&gt;6130K(9216K)] 12274K-&gt;14330K(19456K), 0.0034895 secs</span><span style="font-size: 15px;"> 代表 YGC 前后内存使用情况;</span></p></li> <li><p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;color: rgb(136, 136, 136);">Times: user=0.02 sys=0.00, real=0.00 secs</span><span style="font-size: 15px;">:user 表示用户态消耗的 CPU 时间,sys 表示内核态消耗的 CPU 时间,real 表示各种墙时钟的等待时间;</span></p></li> <li><p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;">这两张图只是举例并没有关联关系。比如你从图里面看能到是否进行 FGC、FGC 的时间花费多长;GC 后老年代,年轻代内存是否有减少;得到一些初步的情况来做出判断。</span></p></li> </ul> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;">2. dump 出内存文件在具体分析。</span></p> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;">比如通过 jmap 命令 <strong>jmap -dump:format=b,file=dumpfile pid</strong>。</span><span style="font-size: 15px;">导出之后再</span><span style="font-size: 15px;">通过 </span><span style="font-size: 15px;">Eclipse Memory Analyze</span><span style="font-size: 15px;">r </span><span style="font-size: 15px;">等工具进行分析,定位到代码、</span><span style="font-size: 15px;">修复。</span></p> <p style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">这里还会可能存在一个提问的点,比如 <strong>CPU 飙高,同时 FGC 怎么办</strong>?办法比较类似:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="text-align: left;"><span style="font-size: 15px;">找到当前进程的 pid,<strong>top -p pid -H</strong> 查看资源占用,找到问题线程;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">printf “%x\n” pid</span></strong><span style="font-size: 15px;">,把线程 pid 转为16进制,比如 0x32d;</span></p></li> <li><p style="text-align: left;"><strong><span style="font-size: 15px;">jstack pid|grep -A 10 0x32d&nbsp;</span></strong><span style="font-size: 15px;">查看线程的堆栈日志,还找不到问题继续下一步;</span></p></li> <li><p style="text-align: left;"><span style="font-size: 15px;">dump 出内存文件用 MAT 等工具进行分析,定位到代码、修复。</span></p></li> </ol> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <br> </section> <h2 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong style="color: rgb(171, 25, 66);font-size: 16px;text-align: left;"><span style="font-size: 15px;">10. JVM调优有什么经验吗?</span></strong></h2> <p><span style="font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></span></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">要明白一点,所有的调优的目的都是为了用更小的硬件成本达到更高的吞吐,JVM 的调优也是一样。通过对垃圾收集器和内存分配的调优达到性能的最佳。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h3 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">简单的参数含义</span></strong><span style="font-size: 15px;"></span></h3> <p><span style="font-size: 15px;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;">首先,需要知道几个主要的参数含义。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.3625" data-s="300,640" src="/upload/192d45f4e0acbae8885479270e3c777b.png" data-type="png" data-w="1280" style=""></p> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"></span><br></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-Xms</span></strong> <span style="font-size: 15px;"> 设置初始堆的大小,<strong>-Xmx </strong>设置最大堆的大小;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-XX:NewSize</span></strong> <span style="font-size: 15px;"> 年轻代大小,<strong>-XX:MaxNewSize</strong> 年轻代最大值,<strong>-Xmn</strong> 则是相当于同时配置 -XX:NewSize 和 -XX:MaxNewSize 为一样的值;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-XX:NewRatio</span></strong> <span style="font-size: 15px;"> 设置年轻代和年老代的比值。如果为3,表示年轻代与老年代比值为 1:3,默认值为2;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-XX:SurvivorRatio</span></strong> <span style="font-size: 15px;"> 年轻代和两个 Survivor 的比值。默认值为8,代表比值为 8:1:1;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-XX:PretenureSizeThreshold</span></strong> <span style="font-size: 15px;"> 当创建的对象超过指定大小时,直接把对象分配在老年代;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-XX:MaxTenuringThreshold</span></strong> <span style="font-size: 15px;"> 设定对象在 Survivor 复制的最大年龄阈值,超过阈值转移到老年代;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-XX:MaxDirectMemorySize</span></strong> <span style="font-size: 15px;"> 当 Direct ByteBuffer 分配的堆外内存到达指定大小后,即触发 Full GC。</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><span style="font-size: 15px;"><br></span></p> <h3 data-tool="mdnice编辑器" style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"><strong><span style="font-size: 15px;">调优</span></strong></h3> <p><span style="font-size: 15px;"><br></span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <span style="font-size: 15px;">为了打印日志方便排查问题最好开启GC日志。开启GC日志对性能影响微乎其微,但是能帮助我们快速排查定位问题。<strong>-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.log</strong></span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <span style="font-size: 15px;">一般设置 <strong>-Xms=-Xmx</strong>。这样可以获得固定大小的堆内存,减少 GC 次数和耗时,可以使得堆相对稳定;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <strong><span style="font-size: 15px;">-XX:+HeapDumpOnOutOfMemoryError</span></strong> <span style="font-size: 15px;"> 让 JVM 在发生内存溢出的时候自动生成内存快照,方便排查问题;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <span style="font-size: 15px;"><strong>-Xmn</strong> 设置新生代的大小。太小会增加 YGC,太大会减小老年代大小,一般设置为<strong>整个堆的1/4到1/3</strong>;</span> </section></li> <li style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;"> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;text-align: left;"> <span style="font-size: 15px;">设置 <strong>-XX:+DisableExplicitGC </strong>禁止系统 System.gc()。防止手动误触发 FGC 造成问题。</span> </section></li> </ul> <p style="caret-color: rgb(0, 0, 0);white-space: normal;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-variant-ligatures: normal;orphans: 2;widows: 2;"><br></p> <section donone="shifuMouseDownCard('shifu_c_030')" label="Copyright Reserved by PLAYHUDONG." style="text-align: start;white-space: normal;margin-top: 1em;margin-bottom: 1em;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);border-width: 0px;border-style: initial;border-color: initial;"> <section style="margin-left: 1em;line-height: 1.4;"> <span style="padding: 3px 8px;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;color: rgb(255, 255, 255);background-color: rgb(255, 105, 31);font-family: inherit;text-align: inherit;text-decoration: inherit;font-size: 16px;">推荐阅读</span>&nbsp;&nbsp; <span style="margin-left: 4px;padding: 3px 8px;border-top-left-radius: 1.2em;border-top-right-radius: 1.2em;border-bottom-right-radius: 1.2em;border-bottom-left-radius: 1.2em;color: rgb(255, 255, 255);line-height: 1.2;background-color: rgb(204, 204, 204);font-family: inherit;text-align: inherit;text-decoration: inherit;border-color: rgb(249, 110, 87);font-size: 12px;">点击标题可跳转</span> </section> <section style="margin-top: -11px;padding: 22px 16px 16px;border-width: 1px;border-style: solid;border-color: rgb(255, 105, 31);color: rgb(51, 51, 51);font-size: 1em;font-family: inherit;text-align: inherit;text-decoration: inherit;"> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651482823&amp;idx=1&amp;sn=cd623fbfa900f1217b34d34f980b2e82&amp;chksm=bd2506b88a528faea643840bb2613ae67252085c24faaba2359006cc37335dc4fe042f1ca66d&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-family: inherit;text-align: inherit;font-size: 12px;" data-linktype="2">关于 JVM 内存的 N 个问题</a><br></p> <p style="text-align: start;white-space: normal;caret-color: rgb(0, 0, 0);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651488039&amp;idx=1&amp;sn=b812f2bd8b3e0e7edfdb2bb6c050894e&amp;chksm=bd2513588a529a4e02c41f59fa78adc2c064b48b112a5120d53687b8ee84aff87524acecb479&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;" data-linktype="2">JVM 史上最最最完整深入解析</a><br></p> <p style="text-align: start;white-space: normal;caret-color: rgb(0, 0, 0);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651486318&amp;idx=1&amp;sn=a0bf7b6896cc30cff1581243193200fc&amp;chksm=bd2514118a529d0769979c50eaac2c8b5fef25cb8fdf550d9e24534d211700d0bd6befae2b64&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;" data-linktype="2">Spring Boot项目优化和JVM调优</a></p> </section> </section> <p style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);text-align: start;white-space: normal;"><br></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-ratio="0.9166666666666666" data-s="300,640" data-type="jpeg" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.jpg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: right;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 14px;text-align: right;">好文章,我</span><span style="font-size: 14px;text-align: right;color: rgb(255, 41, 65);">在看</span><span style="font-size: 14px;text-align: right;">❤️</span></p> <p><br></p>

接口测试工具

作者:测试人生路

一、接口 接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式。 广义的接口包括人机界面、硬件接口、软件接口。 人机界面:人类与电脑等信息机器人或人类与程序之间的接口称为用户界面。 硬件接口:电脑等信息机器硬件组件间的接口叫硬件接口。 软件接口:电脑等信息机器软件组件间的接口叫软件接口。 我们常说的软件接口测试,是指程序之间提供服务的软件接口。 2.常见的接口测试软件 ![微信截图_20201027101449.png](/storage/thumbnails/_signature/1DOOQ99BBNN7CK8J6142K46R9O.png) 3.接口测试工具使用对比 jmeter 是一款100%纯Java编写的免费开源工具,主要用来做性能测试,但也可以做接口测试,配合后置处理器与断言,可以满足大部分的接口测试场景,JMeter提供了BeanShell编程能力,可以写出比较灵活的测试脚本,通过jmeter+ant+jenkins可以实现接口和性能自动化测试。 postman 是谷歌开发的一款接口测试软件,它使用简单,功能强大,能够发送任何类型的HTTP请求 (GET, HEAD, POST, PUT..),可以进行文件上传、响应验证、变量管理、环境参数管理,还可以批量管理和运行用例,并支持用例导出、导入,可以进行接口的自动化测试。 缺点是自动化断言不够强大,不能和jenkins、代码管理库进行持续集成测试,没有中文版。 sospUI 是一个开源测试工具,通过soap/http来检查、调用,实现Web Service的接口和性能测试。该工具既可作为一个单独的测试软件使用,也可利用插件集成到Eclipse,maven2.X,Netbeans和intellij中使用。 soapUI pro是soapUI的商业非开源版本,实现的功能较开源的soapUI更多,因为国外公司使用较多,所以是英文版的。 apipost 国产的一款接口测试和接口文档生成工具,它使用简单,功能强大,能够发送任何类型的HTTP请求 (GET, HEAD, POST, PUT..),可以进行文件上传、响应验证、变量管理、环境参数管理,还可以批量管理和运行用例,并支持用例导出、导入,可以进行接口的自动化测试。并且支持多人接口开发协作,可以生成各种中文版的接口文档。使用起来特别方便。 4.apipost使用教程 ![新建.png](/storage/thumbnails/_signature/1KD43F4SO12JP1N0UKRDHV9LF4.png) 实现一个简单的get请求,请求的百度。 ![百度.png](/storage/thumbnails/_signature/12KB2JM2TLFRPTAR9DAD9902CQ.png) 看一下接口文档分享和生成,可以导出各种格式的接口文档。 ![文档.png](/storage/thumbnails/_signature/16TNFC8VAQHVB5UB2UL44KLEHD.png) 下载地址:www.apipost.cn/?dt=20201027 转载地址:https://zhuanlan.zhihu.com/p/269037684

接口文档生成

作者:测试人生路

一、为什么要写接口文档? 1.正规的团队合作或者是项目对接,接口文档是非常重要的,一般接口文档都是通过开发人员写的。一个工整的文档显得是非重要。 2.项目开发过程中前后端工程师有一个统一的文件进行沟通交流开发,项目维护中或者项目人员更迭,方便后期人员查看、维护 二、接口文档的格式 接口主要分为四部分:方法、uri、请求参数、返回参数 ![1.png](/storage/thumbnails/_signature/1IBR7E5VPLICQPJ78541UJOKQV.png) 三、接口文档生成工具 apipost一款很不错的接口测试工具,它可以生成各种格式的接口文档,有在线版的,markdown格式和word格式的接口文档。 点击分享当前接口 ![2.png](/storage/thumbnails/_signature/499SGQN4JO87UFB9IQ3PMHR0H.png) 分享 ![3.png](/storage/thumbnails/_signature/3BEQ80G4228C1DFJJ360KG9R42.png) 复制链接在浏览器中打开 ![4.png](/storage/thumbnails/_signature/41258K6UI61FKPKFASNDRRQHD.png) 下载其他格式的接口文档 ![5.png](/storage/thumbnails/_signature/2GARNIPL5PF1BKLJLR9280US61.png) word格式的接口文档(word只支持json格式的排版,所以百度放回的数据格式在word中显示不规范) ![6.png](/storage/thumbnails/_signature/3J4MIJS5S87GVOHL95TVUVMA38.png) 单个接口的word格式的接口文档可以免费下载,下载多个简单文档和一个项目的接口文档的时间就需要开会员了。 还有就是apipost下载多个word格式的接口文档的时候,每个接口是单独的接口文档需要合并。wps和office里面都有合并功能。 在插入中的对象中找到文件中的文字然后点击选择所有接口就可以合并了。 ![7.png](/storage/thumbnails/_signature/1FPLS6NHHIB34J9AJ8IQN2KF6E.png) ![8.png](/storage/thumbnails/_signature/15C5C3CMSD0V3364H3U661V13M.png) ![9.png](/storage/thumbnails/_signature/Q8MJ701NKJQ7PIKL1J7UHIFIO.png) 这就是接口文档生成工具 apipost下载地址:https://www.apipost.cn/?dt=20201028