-
Notifications
You must be signed in to change notification settings - Fork 0
/
db.json
1 lines (1 loc) · 553 KB
/
db.json
1
{"meta":{"version":1,"warehouse":"4.0.0"},"models":{"Asset":[{"_id":"source/img/mapstruct-plugins.png","path":"img/mapstruct-plugins.png","modified":0,"renderable":0},{"_id":"source/img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png","path":"img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png","modified":0,"renderable":0},{"_id":"source/img/syncpool/image-20210930145240966.png","path":"img/syncpool/image-20210930145240966.png","modified":0,"renderable":0},{"_id":"source/img/syncpool/image-20210930143647762.png","path":"img/syncpool/image-20210930143647762.png","modified":0,"renderable":0},{"_id":"source/img/syncpool/image-20210930145657424.png","path":"img/syncpool/image-20210930145657424.png","modified":0,"renderable":0},{"_id":"source/img/puml/ants/ants.svg","path":"img/puml/ants/ants.svg","modified":0,"renderable":0},{"_id":"source/img/puml/suffix_express/suffix_express.svg","path":"img/puml/suffix_express/suffix_express.svg","modified":0,"renderable":0},{"_id":"themes/3-hexo/source/css/gitalk.css","path":"css/gitalk.css","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/mobile.styl","path":"css/mobile.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/style.styl","path":"css/style.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/js/gitalk.js","path":"js/gitalk.js","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/js/gitment.js","path":"js/gitment.js","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/js/iconfont.js","path":"js/iconfont.js","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/js/jquery.pjax.js","path":"js/jquery.pjax.js","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/js/script.js","path":"js/script.js","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/js/search.js","path":"js/search.js","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/js/titleTip.js","path":"js/titleTip.js","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/img/article-list-background.jpeg","path":"img/article-list-background.jpeg","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/img/avatar.jpg","path":"img/avatar.jpg","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/img/brown-papersq.png","path":"img/brown-papersq.png","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/img/gov.png","path":"img/gov.png","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/img/school-book.png","path":"img/school-book.png","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/icomoon.eot","path":"css/fonts/icomoon.eot","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/icomoon.svg","path":"css/fonts/icomoon.svg","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/icomoon.ttf","path":"css/fonts/icomoon.ttf","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/icomoon.woff","path":"css/fonts/icomoon.woff","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/iconfont.eot","path":"css/fonts/iconfont.eot","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/iconfont.svg","path":"css/fonts/iconfont.svg","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/iconfont.ttf","path":"css/fonts/iconfont.ttf","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/iconfont.woff","path":"css/fonts/iconfont.woff","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/iconfont.woff2","path":"css/fonts/iconfont.woff2","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/fonts/selection.json","path":"css/fonts/selection.json","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/atom-dark.styl","path":"css/hl_theme/atom-dark.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/atom-light.styl","path":"css/hl_theme/atom-light.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/brown-paper.styl","path":"css/hl_theme/brown-paper.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/darcula.styl","path":"css/hl_theme/darcula.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/github-gist.styl","path":"css/hl_theme/github-gist.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/github.styl","path":"css/hl_theme/github.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/gruvbox-dark.styl","path":"css/hl_theme/gruvbox-dark.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/gruvbox-light.styl","path":"css/hl_theme/gruvbox-light.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/kimbie-dark.styl","path":"css/hl_theme/kimbie-dark.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/kimbie-light.styl","path":"css/hl_theme/kimbie-light.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/railscasts.styl","path":"css/hl_theme/railscasts.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/rainbow.styl","path":"css/hl_theme/rainbow.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/school-book.styl","path":"css/hl_theme/school-book.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/sublime.styl","path":"css/hl_theme/sublime.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/sunburst.styl","path":"css/hl_theme/sunburst.styl","modified":0,"renderable":1},{"_id":"themes/3-hexo/source/css/hl_theme/zenbum.styl","path":"css/hl_theme/zenbum.styl","modified":0,"renderable":1},{"_id":"source/img/java双亲委派模型.png","path":"img/java双亲委派模型.png","modified":0,"renderable":0},{"_id":"source/img/java类加载过程.png","path":"img/java类加载过程.png","modified":0,"renderable":0}],"Cache":[{"_id":"themes/3-hexo/.gitignore","hash":"86a50fa08e69cab561892aa5edef24f9081bbde1","modified":1667269863464},{"_id":"themes/3-hexo/README.md","hash":"d2c42534ceabcb7cd2e58ed994bf7d6f734d1f3b","modified":1667269863464},{"_id":"themes/3-hexo/.DS_Store","hash":"0770f9d42bfdd8d420de48fed463015e001cf579","modified":1667269863438},{"_id":"themes/3-hexo/LICENSE","hash":"b04140c5f682db2b300428f97bb164fd7f5f18bd","modified":1667269863438},{"_id":"themes/3-hexo/_config.yml","hash":"8bfa2f52c5a49183a66a687050195705528a4a64","modified":1667545124932},{"_id":"themes/3-hexo/languages/en.yml","hash":"53ae29ae1237fc7822df85a6d2f8da6f0078625e","modified":1667269863463},{"_id":"themes/3-hexo/languages/zh-CN.yml","hash":"d2c6d86fe2ff03e6ee9bbc16dff8efe5b47ac297","modified":1667269863463},{"_id":"themes/3-hexo/layout/index.ejs","hash":"27ea3dac053d501b79bbef5117b4f3aff063d8cd","modified":1667269863439},{"_id":"themes/3-hexo/layout/post.ejs","hash":"4abd16c0f5e3f51103d23b73710d695dc7fdc5d2","modified":1667269863438},{"_id":"themes/3-hexo/layout/indexs.md","hash":"a24c567116e51cd069f5deff1d30a3021839bfb6","modified":1667269863445},{"_id":"themes/3-hexo/source/.DS_Store","hash":"fdcc907c46e093a14b153c5dc8c038461997ed3c","modified":1667269863445},{"_id":"themes/3-hexo/layout/_partial/article_copyright.ejs","hash":"0ebb17d001cb7bb7606c616c380049a2e7124496","modified":1667269863443},{"_id":"themes/3-hexo/layout/_partial/comment.ejs","hash":"5507b4dfab2032345e012a0c5356f63b01395157","modified":1667269863444},{"_id":"themes/3-hexo/layout/_partial/article.ejs","hash":"516844c4a0e13d6773f6029849f51c59613b6f69","modified":1667269863444},{"_id":"themes/3-hexo/layout/_partial/copyright.ejs","hash":"f66939a8c9d5258948b47842b8b4495e6ec45988","modified":1667269863442},{"_id":"themes/3-hexo/layout/_partial/dashang.ejs","hash":"bc94eee27701b67d238f328737b578e8270989eb","modified":1667269863443},{"_id":"themes/3-hexo/layout/_partial/friends.ejs","hash":"d11092791e5c140ff81f2aefa0d1b051f403239d","modified":1667269863443},{"_id":"themes/3-hexo/layout/_partial/full-toc.ejs","hash":"a734c26d86da6697003ed27672c1b9b82b216c82","modified":1667269863441},{"_id":"themes/3-hexo/layout/_partial/footer.ejs","hash":"ed479aa9affee3b02a76da06de45a7c40a97a706","modified":1667269863443},{"_id":"themes/3-hexo/layout/_partial/header.ejs","hash":"d0b84370ca81c3baa02a05613ff040003acd2985","modified":1667269863442},{"_id":"themes/3-hexo/layout/_partial/mathjax.ejs","hash":"c2e5cef2377884cd79e5f686fe4f74b082744306","modified":1667269863441},{"_id":"themes/3-hexo/layout/_partial/meta.ejs","hash":"8a9e93f9cbe80763264018290da0b14b4bbe8ac5","modified":1667269863442},{"_id":"themes/3-hexo/layout/_partial/nav-left.ejs","hash":"0e7de3e614ca49fe1c7420f233bea22e02641272","modified":1667269863444},{"_id":"themes/3-hexo/layout/_partial/tag.ejs","hash":"3cf8ba0c6112dfa8089603b8df3e31f70a272715","modified":1667269863441},{"_id":"themes/3-hexo/layout/_partial/nav-right.ejs","hash":"e205f9bb61fd4403d24d863693d4a34ea31b8539","modified":1667269863441},{"_id":"themes/3-hexo/layout/_partial/toc-ref.ejs","hash":"6406251dabda66ef686d4c15edbc3061b6d828b8","modified":1667269863442},{"_id":"themes/3-hexo/source/css/gitalk.css","hash":"58177ce227c50ee359fbf99a4fdd26058887afc5","modified":1667269863446},{"_id":"themes/3-hexo/source/css/style.styl","hash":"7090b81f34e26172670c68da6a366ca186523779","modified":1667269863454},{"_id":"themes/3-hexo/source/css/mobile.styl","hash":"59a4abd36cc8ff2107f1fcb3c0fe48d0492c9030","modified":1667269863446},{"_id":"themes/3-hexo/source/js/script.js","hash":"f261fb46d822e9b89296c454a965ff34e4b244f9","modified":1667269863460},{"_id":"themes/3-hexo/source/js/jquery.pjax.js","hash":"191c49fdb40dff115a49cfd2b30dffb888d86550","modified":1667269863460},{"_id":"themes/3-hexo/source/js/search.js","hash":"c80c9a231ee040c7adc07a477793873fb85ce8bc","modified":1667269863460},{"_id":"themes/3-hexo/source/js/titleTip.js","hash":"81dca549063e29ba3a4a278f0f4388eba8a2167b","modified":1667269863461},{"_id":"themes/3-hexo/source/js/iconfont.js","hash":"3a0869ca1b09af07d82987e343a3bc4cb9558ecb","modified":1667269863458},{"_id":"themes/3-hexo/source/img/avatar.jpg","hash":"3f43f4b7b03cd14fcd2769d57336b0b4edf41f85","modified":1667269863462},{"_id":"themes/3-hexo/source/img/article-list-background.jpeg","hash":"4fdf8b3e53dd02d6ee6360aebfadb0cba1fb5633","modified":1667269863462},{"_id":"themes/3-hexo/source/img/brown-papersq.png","hash":"3a1332ede3a75a3d24f60b6ed69035b72da5e182","modified":1667269863462},{"_id":"themes/3-hexo/source/img/gov.png","hash":"f31c9f47faedf7f33b9580d6284ab891fb697560","modified":1667269863461},{"_id":"themes/3-hexo/layout/_partial/comments/click2show.ejs","hash":"fa6675230f8c313236604e26926b142f4f418bdd","modified":1667269863440},{"_id":"themes/3-hexo/layout/_partial/comments/disqus.ejs","hash":"cd0022ce7e6d6efb07a00e87477cdf791f7f6703","modified":1667269863440},{"_id":"themes/3-hexo/source/img/school-book.png","hash":"711ec983c874e093bb89eb77afcbdf6741fa61ee","modified":1667269863462},{"_id":"themes/3-hexo/layout/_partial/comments/gitalk.ejs","hash":"fbd3c7d72c8354d700918390c6cbfc0a11408277","modified":1667269863439},{"_id":"themes/3-hexo/layout/_partial/comments/gitment.ejs","hash":"f16442568b43d034faaa8e3507f5ae8da34c7b72","modified":1667269863440},{"_id":"themes/3-hexo/layout/_partial/comments/livere.ejs","hash":"e820aa16b5ed4e024616b5e2d424925820d43e56","modified":1667269863440},{"_id":"themes/3-hexo/source/css/_partial/comment.styl","hash":"2a9b5ffb759be85545a89f6d1194579a800f51a5","modified":1667269863454},{"_id":"themes/3-hexo/layout/_partial/comments/utteranc.ejs","hash":"c76773b96860940083baf16470b7b80ac098e645","modified":1667269863440},{"_id":"themes/3-hexo/source/css/_partial/dashang.styl","hash":"f6447a2ac407228e1d53e3455db2919ac0e9f094","modified":1667269863453},{"_id":"themes/3-hexo/source/css/_partial/fade.styl","hash":"4f687cbc74caf8a0887f5e89250284a9bce8b5c1","modified":1667269863453},{"_id":"themes/3-hexo/source/css/_partial/font.styl","hash":"c68d6942ed62f9cce8a9042f5a59055a0ee5441c","modified":1667269863452},{"_id":"themes/3-hexo/source/css/_partial/full-toc.styl","hash":"0143711c1221cb4e70a3db866754d79c8a81d253","modified":1667269863452},{"_id":"themes/3-hexo/source/css/_partial/nav-left.styl","hash":"0f80414f4c663c5250c8ff41d01ce6c68450ee33","modified":1667269863452},{"_id":"themes/3-hexo/source/css/_partial/nav-right.styl","hash":"44bdb1fe3e75996f24f2656770d6a376330e898d","modified":1667269863453},{"_id":"themes/3-hexo/source/css/_partial/nprogress.styl","hash":"65efbddd23a264e7d1e85f4073228526770e833c","modified":1667269863453},{"_id":"themes/3-hexo/source/css/_partial/num-load.styl","hash":"4b996440bba8ec755aa70bc6d074d7dbba55ec0c","modified":1667269863451},{"_id":"themes/3-hexo/source/css/_partial/post.styl","hash":"0dd8d208081b92e05d6db3361743c520a2816828","modified":1667269863452},{"_id":"themes/3-hexo/source/css/fonts/icomoon.eot","hash":"b6195bedc1cb2f9cfcb26cc27021f2e94be2ab0a","modified":1667269863456},{"_id":"themes/3-hexo/source/css/fonts/icomoon.svg","hash":"37ac1ef28b03f46bf3ad2606c86f0e1ec3e4405f","modified":1667269863457},{"_id":"themes/3-hexo/source/css/fonts/icomoon.ttf","hash":"eb976d8b8559fcddfc2658a03a4350cb566fc06b","modified":1667269863455},{"_id":"themes/3-hexo/source/css/fonts/icomoon.woff","hash":"3985d29416bb9b19f50a2f20f2bbbce47f10af8d","modified":1667269863455},{"_id":"themes/3-hexo/source/css/fonts/iconfont.ttf","hash":"140829ecf12d30c6e18d8dc6dc0c188a66addd25","modified":1667269863456},{"_id":"themes/3-hexo/source/css/fonts/iconfont.eot","hash":"b14b8624988ff069aff3145f88c0d7ac49052bd3","modified":1667269863456},{"_id":"themes/3-hexo/source/css/fonts/iconfont.woff","hash":"0d2d4559f1ac4fa801eb8cc099fa5bf9dcf955ef","modified":1667269863455},{"_id":"themes/3-hexo/source/css/fonts/iconfont.woff2","hash":"b0317a0b2ebb1181a8bf5a97d03556dd54538645","modified":1667269863457},{"_id":"themes/3-hexo/source/css/hl_theme/atom-dark.styl","hash":"f3eb4e5feda9cbd6242ccf44ca064e2979b5d719","modified":1667269863447},{"_id":"themes/3-hexo/source/css/hl_theme/atom-light.styl","hash":"553987211d3323a7dfc0b08786b183a3435978c9","modified":1667269863449},{"_id":"themes/3-hexo/source/css/fonts/selection.json","hash":"57c7f100019d57b512aab509185cb0a6eb9aa4c8","modified":1667269863457},{"_id":"themes/3-hexo/source/css/fonts/iconfont.svg","hash":"13974fe35fca836e870a960ecb11b7eca2e036f8","modified":1667269863457},{"_id":"themes/3-hexo/source/css/hl_theme/brown-paper.styl","hash":"03af387edcc1cf8c18d12e9c440fd51b6cf425b6","modified":1667269863447},{"_id":"themes/3-hexo/source/css/hl_theme/darcula.styl","hash":"2bfc14f27ccca108b4b3755782de8366e8bd001e","modified":1667269863448},{"_id":"themes/3-hexo/source/css/hl_theme/github-gist.styl","hash":"5e05b19832c1099bd9d284bc3ed00dc8a3d7ee23","modified":1667269863450},{"_id":"themes/3-hexo/source/css/hl_theme/github.styl","hash":"53276ff1f224f691dfe811e82c0af7f4476abf5d","modified":1667269863447},{"_id":"themes/3-hexo/source/css/hl_theme/gruvbox-dark.styl","hash":"315ad610d303caba9eac80a7d51002193a15478a","modified":1667269863449},{"_id":"themes/3-hexo/source/css/hl_theme/gruvbox-light.styl","hash":"1bece084b1dbbbd4af064f05feffd8c332b96a48","modified":1667269863450},{"_id":"themes/3-hexo/source/css/hl_theme/kimbie-dark.styl","hash":"e9c190f9ffc37a13cac430512e4e0c760205be4a","modified":1667269863450},{"_id":"themes/3-hexo/source/css/hl_theme/kimbie-light.styl","hash":"0c3ccd0d64e7504c7061d246dc32737f502f64e4","modified":1667269863447},{"_id":"themes/3-hexo/source/css/hl_theme/railscasts.styl","hash":"a6e8cfd2202afd7893f5268f3437421e35066e7b","modified":1667269863448},{"_id":"themes/3-hexo/source/css/hl_theme/school-book.styl","hash":"51659351b391a2be5c68728bb51b7ad467c5e0db","modified":1667269863448},{"_id":"themes/3-hexo/source/css/hl_theme/rainbow.styl","hash":"e5c37646a9d9c1094f9aab7a7c65a4b242e8db00","modified":1667269863448},{"_id":"themes/3-hexo/source/css/hl_theme/sublime.styl","hash":"501d75ef0f4385bea24d9b9b4cc434ba68d4be27","modified":1667269863449},{"_id":"themes/3-hexo/source/css/hl_theme/sunburst.styl","hash":"2aa9817e68fb2ed216781ea04b733039ebe18214","modified":1667269863450},{"_id":"themes/3-hexo/source/css/hl_theme/zenbum.styl","hash":"933a3b196d01254dea5e6f48105ea15e210ae000","modified":1667269863451},{"_id":"source/_posts/docker/docker-常用命令.md","hash":"d4a043c6684264ae02e130c4733a57c109e483fc","modified":1679374628978},{"_id":"source/_posts/git/git-config.md","hash":"442d5cfd63518194f92de1c3f6038255c252bda9","modified":1667271522349},{"_id":"source/_posts/git/git-常用命令.md","hash":"db33b5c52bb16d486f36e8ab6bbc097474219803","modified":1667533264794},{"_id":"source/_posts/golang/golang future 实现.md","hash":"b4b15d1640346a9407fcc0b659a34984671346fd","modified":1667209257106},{"_id":"source/_posts/golang/go-micro 使用问题记录.md","hash":"49375cd4d02012a838fd9465e451813c9ba71b18","modified":1667270567276},{"_id":"source/_posts/git/git-统计命令.md","hash":"360cc0b9987babbc205a41842a5bec3466881ada","modified":1667271347605},{"_id":"source/_posts/golang/gin 验证器.md","hash":"2a82371816c1a9bde90a37cb6748530a4782a6d5","modified":1667271914547},{"_id":"source/_posts/golang/golang 函数调用链.md","hash":"c14067fc430e6d12d8fa110cf034d50039c16ac1","modified":1667271962582},{"_id":"source/_posts/golang/golang 启动一个简单 http 代理.md","hash":"522331b0565a1b87cac67eb7421b16814cae916a","modified":1667209257106},{"_id":"source/_posts/golang/golang 生成随机字符串.md","hash":"448c550d9e44fe501923e875cfc32b1566cede72","modified":1667206820493},{"_id":"source/_posts/golang/golang-实现计算器.md","hash":"4322b3228fb817ddf7e7d7ccc472b26fa0e55ecf","modified":1667209257108},{"_id":"source/_posts/golang/golang 正则表达式常用.md","hash":"a9a486c9616c69daa8d7068045e99b66c4110022","modified":1667272010461},{"_id":"source/_posts/golang/golang-helloworld-docker-启动.md","hash":"660d4cb3c907a7573364cd138fd06f7b00e6d6b9","modified":1667270576887},{"_id":"source/_posts/golang/golang-ants-协程池.md","hash":"b433a03c616591e056f90b88f521fc3460e43ae9","modified":1667209257107},{"_id":"source/_posts/java/maven-template.md","hash":"38896410fbae5be09af2d069adc86040edb70d4a","modified":1667377924176},{"_id":"source/_posts/golang/golang-并发赋值安全性.md","hash":"7e4db0647de37d565098e13a65273fee1492fc49","modified":1667270567276},{"_id":"source/_posts/golang/gorm 中使用 json.md","hash":"df471b5447f714a9dff325604209df741da0534f","modified":1667270581772},{"_id":"source/_posts/java/java-mapstruct-详解.md","hash":"f632297fcc5f86364db97ff915c8809a9ba5df04","modified":1667377915892},{"_id":"source/_posts/java/springboot-事务.md","hash":"619f7e1f8b1dd80f74a52100fab21b529e22f10e","modified":1667270599954},{"_id":"source/_posts/mac/mac-item2-终端.md","hash":"b797e2451a8b5b8b72cab57d30bb557cf453ec70","modified":1667272190072},{"_id":"source/_posts/mysql/SQL中的in与not-in、exists与not-exists的区别以及性能分析.md","hash":"3626a12d246f701e6d11071e081fead3a6be9b23","modified":1667270611312},{"_id":"source/_posts/mysql/mysql 距离函数 st_distance 使用.md","hash":"ae80f521a7409dc40c52d887870f53d47b698d1b","modified":1667270605286},{"_id":"source/_posts/mysql/mysql-慢日志查询分析.md","hash":"75efb0430d73fd9b57878fce0cd7e93886486796","modified":1667209257112},{"_id":"source/_posts/golang/sync-Pool-对象池使用问题记录.md","hash":"d02751c2393f30ccf88f33aeed38fe7b05ea65c6","modified":1667209257113},{"_id":"source/_posts/python/uvicorn-初识.md","hash":"edfd2b98c9bb6a8f54934263cb8c30ea49846cd3","modified":1667209257113},{"_id":"source/_posts/python/python 正则表达式简介.md","hash":"71f09cb9b84c006d6aac6a188eae0fc0da60bef1","modified":1667270618057},{"_id":"source/_posts/linux/ssh.md","hash":"40b14d4bfc5d5dab102ef7999bd3f971c05e8db4","modified":1667271659190},{"_id":"source/_posts/其他/http 状态码.md","hash":"c6a15a680ec2a02c92c84aa7a02fc80c48c0597e","modified":1667270470634},{"_id":"source/_posts/其他/状态机.md","hash":"235ab791fd9ac032395fb2e95b06089b3d391a73","modified":1667270463859},{"_id":"source/_posts/redis/redis-详解.md","hash":"127a3c9878d947d5f1c6d4e3730b6b2faeb79c74","modified":1667270626905},{"_id":"source/_posts/其他/jetbrains-插件推荐.md","hash":"a6200c3ccb58889a4f39c04d0aa1e3afe4561310","modified":1667272244745},{"_id":"source/_posts/算法/时间轮.md","hash":"5d2b28f51e775367b95fc998a4e62075ce560d43","modified":1667209257114},{"_id":"source/_posts/其他/markdown 语法简介.md","hash":"2ddedadfec9ce2f081e592985a1b6d678b5a4e17","modified":1667270479882},{"_id":"source/_posts/其他/网站收藏.md","hash":"bc706d291fdba71e70e618461a524b0a63dadce5","modified":1667270451532},{"_id":"source/img/puml/ants/ants.svg","hash":"39e3c7f00e2f4c09ecd89dadebec586644a6bfad","modified":1667206820499},{"_id":"source/img/puml/suffix_express/suffix_express.svg","hash":"1d320a7994b754d7e02f646a96e4cee118134245","modified":1667206820500},{"_id":"themes/3-hexo/source/js/gitment.js","hash":"59a1e03f2b0ce61dd9bd405d3c52d3e07cc10dec","modified":1667269863460},{"_id":"source/img/mapstruct-plugins.png","hash":"04a62bd593aec8c4d9fa8cf9d04c83a81c9d0000","modified":1667206820499},{"_id":"source/img/syncpool/image-20210930145657424.png","hash":"71f7c733b03cd6086bcd29a0e8efc260f3aaa7d0","modified":1667206820507},{"_id":"source/img/syncpool/image-20210930145240966.png","hash":"4db4fc70b01ccaf051eb92d3562ab3ef569c7dbf","modified":1667206820506},{"_id":"source/img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png","hash":"ac90cf3c6174ffdee192192e02252ae125acbb51","modified":1667271751603},{"_id":"source/img/syncpool/image-20210930143647762.png","hash":"fed976df9deb881e37af29770ceeed89488c9bc9","modified":1667206820504},{"_id":"themes/3-hexo/source/js/gitalk.js","hash":"26ba4841dcb4b178f730f53a8d1f4a7c89442b4f","modified":1667269863459},{"_id":"public/baidusitemap.xml","hash":"914c4ce9b56d1bf39b15536a9541d36ed2a419d6","modified":1667533269459},{"_id":"public/content.json","hash":"3faf27a462f6634541852fd6dd69186e9771ac06","modified":1667533269459},{"_id":"public/sitemap.xml","hash":"69f913a2abe3239cc7814d3268161434d4bcb392","modified":1667533269459},{"_id":"public/2022/11/02/java/maven-template/index.html","hash":"89f36b5ac962453eb64d34e07d0bad15ea546fee","modified":1667377744221},{"_id":"public/2022/11/01/git/git-config/index.html","hash":"9b0495d6530bb552537fba8c3276148fc939fd1d","modified":1667544714120},{"_id":"public/2022/11/01/docker/docker-常用命令/index.html","hash":"cd0632e77af15b232942c782eb56ec42910f3970","modified":1667544714120},{"_id":"public/2022/11/01/git/git-统计命令/index.html","hash":"0915da2031b23cf34c5f5d1a5d7548491804ebe0","modified":1667544714120},{"_id":"public/2022/11/01/golang/gin 验证器/index.html","hash":"d5addea6f131337c6e863eaaa7a50e44b7c94ca4","modified":1667544714120},{"_id":"public/2022/11/01/golang/golang 函数调用链/index.html","hash":"5469413f90ea79d63bab4c5e8bc54f090dc659b0","modified":1667544714120},{"_id":"public/2022/11/01/golang/golang 正则表达式常用/index.html","hash":"7d52c1ea28f9082abfab754e4214fd89f9889327","modified":1667544714120},{"_id":"public/2022/11/01/mac/mac-item2-终端/index.html","hash":"11fdf5fb3db6f1471bbe85c4af05ab99b1b0a3dc","modified":1667544714120},{"_id":"public/2022/11/01/linux/ssh/index.html","hash":"17d0a43c513c242a4c6f22725382cb7b091f70c0","modified":1667544714120},{"_id":"public/2021/10/25/java/java-mapstruct-详解/index.html","hash":"2c85a5f0ca877df9eef92886e86ec5c81c088cba","modified":1667544714120},{"_id":"public/2021/09/30/golang/golang-并发赋值安全性/index.html","hash":"40b998a2df0ac149df5f062b9c4f46781fbb6c13","modified":1667544714120},{"_id":"public/2021/09/30/golang/sync-Pool-对象池使用问题记录/index.html","hash":"a52fd093cd0b4eefecfd460d65566e2a476259ec","modified":1667544714120},{"_id":"public/2021/09/30/算法/时间轮/index.html","hash":"217e0e240912aedd535e0e5506d7e283380766aa","modified":1667544714120},{"_id":"public/2021/09/24/golang/golang-实现计算器/index.html","hash":"150749256ab6d39ded00519d9112f4c8648a49c2","modified":1667544714120},{"_id":"public/2021/09/23/其他/状态机/index.html","hash":"ebe3be35adc23cf2e53ae43cd851757c5beb85ad","modified":1667544714120},{"_id":"public/2021/09/18/golang/golang-ants-协程池/index.html","hash":"987115673cd7fa182bd0beeee34eb7b2f8c51b38","modified":1667544714120},{"_id":"public/2021/09/18/其他/jetbrains-插件推荐/index.html","hash":"6b502b2e6a351525458b4e5b0b3a20478bfb820f","modified":1667544714120},{"_id":"public/2021/09/17/mysql/mysql-慢日志查询分析/index.html","hash":"cde682ab7bb506bf6db1080f08278d7944c9ddda","modified":1667544714120},{"_id":"public/2021/09/14/mysql/SQL中的in与not-in、exists与not-exists的区别以及性能分析/index.html","hash":"ff448bdd0b69c80f53c7fd2cb20dfd7d8d7b82b7","modified":1667544714120},{"_id":"public/2021/09/13/java/springboot-事务/index.html","hash":"0ac6fe70426459b5afd728db9a89649c06debcb9","modified":1667544714120},{"_id":"public/2021/09/09/python/uvicorn-初识/index.html","hash":"08135c82b2271614bee41099547bae3140b9f9ca","modified":1667544714120},{"_id":"public/2021/09/08/golang/golang-helloworld-docker-启动/index.html","hash":"fa04294e6d6c10dd06508f35de285b7fc4702fed","modified":1667544714120},{"_id":"public/2021/09/08/git/git-常用命令/index.html","hash":"4e25b95e8791885f1dcb0f87eacc03535637dbcb","modified":1667544714120},{"_id":"public/2021/09/08/redis/redis-详解/index.html","hash":"8accafa75adcc27b099a21548ba8d8bdf7baccaa","modified":1667544714120},{"_id":"public/2020/05/08/golang/golang 启动一个简单 http 代理/index.html","hash":"f391d7aba0312c43e6bf79ab4e4ba9689599d95f","modified":1667544714120},{"_id":"public/2019/11/05/golang/go-micro 使用问题记录/index.html","hash":"54c74405caf015c5dd210f50f018fc8533941118","modified":1667544714120},{"_id":"public/2019/06/13/golang/golang 生成随机字符串/index.html","hash":"c244e28513ed404e795751a243a222bc41945ab4","modified":1667544714120},{"_id":"public/2019/06/12/mysql/mysql 距离函数 st_distance 使用/index.html","hash":"91048d854e4423cd997385352a4b22464493c683","modified":1667544714120},{"_id":"public/2019/06/11/golang/gorm 中使用 json/index.html","hash":"783e7edd3ceb7864c35c00b1a3a92ebf1214ccf7","modified":1667544714120},{"_id":"public/2019/06/06/golang/golang future 实现/index.html","hash":"c0978a1a4e4b32ae3951546aa2b8ac94aaeb6060","modified":1667544714120},{"_id":"public/2019/06/05/其他/markdown 语法简介/index.html","hash":"a854233907e96063f2cb39dcfe711733d4080d1d","modified":1667544714120},{"_id":"public/2019/06/04/其他/网站收藏/index.html","hash":"455a9f2109fc2aac5274d8fc2006d5cca3be16c8","modified":1667544714120},{"_id":"public/2018/04/18/其他/http 状态码/index.html","hash":"ad32a2eb14135d83178f7d1b058c976252276d06","modified":1667544714120},{"_id":"public/2017/11/13/python/python 正则表达式简介/index.html","hash":"c710eb8d89a16ff902790c18468efc5ba8486100","modified":1667544714120},{"_id":"public/archives/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/page/3/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/page/4/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2017/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2017/11/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2018/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2018/04/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2019/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2019/06/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2019/11/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2020/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2020/05/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2021/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2021/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2021/09/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2021/09/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2021/10/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2022/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2022/11/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/git/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/docker/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/golang/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/golang/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/java/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/mac/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/mysql/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/linux/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/python/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/redis/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/其他/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/categories/算法/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/page/3/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/page/4/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/git/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/docker/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/golang/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/golang/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/gin/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/future/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/go-micro/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/微服务/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/注意事项/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/http/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/regex/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/ants/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/goroutine/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/docker-compose/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/helloworld/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/算法/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/concurrent/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/gorm/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/sync-Pool/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/java/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/mapstruct/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/plugins/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/springboot/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/事务/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/mac/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/sql/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/性能/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/mysql/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/function/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/慢日志/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/ssh/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/linux/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/python/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/web/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/jetbrains/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/markdown/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/api/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/site/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/timewheels/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/img/puml/suffix_express/suffix_express.svg","hash":"1d320a7994b754d7e02f646a96e4cee118134245","modified":1667377744221},{"_id":"public/img/puml/ants/ants.svg","hash":"39e3c7f00e2f4c09ecd89dadebec586644a6bfad","modified":1667377744221},{"_id":"public/img/article-list-background.jpeg","hash":"4fdf8b3e53dd02d6ee6360aebfadb0cba1fb5633","modified":1667377744221},{"_id":"public/img/avatar.jpg","hash":"3f43f4b7b03cd14fcd2769d57336b0b4edf41f85","modified":1667377744221},{"_id":"public/img/brown-papersq.png","hash":"3a1332ede3a75a3d24f60b6ed69035b72da5e182","modified":1667377744221},{"_id":"public/img/gov.png","hash":"f31c9f47faedf7f33b9580d6284ab891fb697560","modified":1667377744221},{"_id":"public/img/school-book.png","hash":"711ec983c874e093bb89eb77afcbdf6741fa61ee","modified":1667377744221},{"_id":"public/css/fonts/icomoon.eot","hash":"b6195bedc1cb2f9cfcb26cc27021f2e94be2ab0a","modified":1667377744221},{"_id":"public/css/fonts/icomoon.svg","hash":"37ac1ef28b03f46bf3ad2606c86f0e1ec3e4405f","modified":1667377744221},{"_id":"public/css/fonts/icomoon.woff","hash":"3985d29416bb9b19f50a2f20f2bbbce47f10af8d","modified":1667377744221},{"_id":"public/css/fonts/iconfont.eot","hash":"b14b8624988ff069aff3145f88c0d7ac49052bd3","modified":1667377744221},{"_id":"public/css/fonts/icomoon.ttf","hash":"eb976d8b8559fcddfc2658a03a4350cb566fc06b","modified":1667377744221},{"_id":"public/css/fonts/iconfont.svg","hash":"13974fe35fca836e870a960ecb11b7eca2e036f8","modified":1667377744221},{"_id":"public/css/fonts/iconfont.ttf","hash":"140829ecf12d30c6e18d8dc6dc0c188a66addd25","modified":1667377744221},{"_id":"public/css/fonts/iconfont.woff","hash":"0d2d4559f1ac4fa801eb8cc099fa5bf9dcf955ef","modified":1667377744221},{"_id":"public/css/fonts/iconfont.woff2","hash":"b0317a0b2ebb1181a8bf5a97d03556dd54538645","modified":1667377744221},{"_id":"public/css/mobile.css","hash":"5998f6fc27998596beb1e40e4bc3c43be2ed764c","modified":1667377744221},{"_id":"public/js/search.js","hash":"c80c9a231ee040c7adc07a477793873fb85ce8bc","modified":1667377744221},{"_id":"public/js/titleTip.js","hash":"81dca549063e29ba3a4a278f0f4388eba8a2167b","modified":1667377744221},{"_id":"public/css/hl_theme/atom-dark.css","hash":"88d11052a24e8100af6248eb4dbe1ce7b0e96408","modified":1667377744221},{"_id":"public/css/hl_theme/atom-light.css","hash":"d31edb9816dae6b01410028bceb91757a962f780","modified":1667377744221},{"_id":"public/css/hl_theme/brown-paper.css","hash":"500c8e750373f6656ff49a7857c871ceedcf8777","modified":1667377744221},{"_id":"public/css/hl_theme/darcula.css","hash":"4341074bae4bc9f0b86e32b623e27babc0159b6e","modified":1667377744221},{"_id":"public/css/hl_theme/github-gist.css","hash":"7a41c1c479d09df875f99f1f6d94aac42e9e2ad0","modified":1667377744221},{"_id":"public/css/hl_theme/github.css","hash":"e05a0806a508a26b9f3f3794b6b588ec6504ad3f","modified":1667377744221},{"_id":"public/css/hl_theme/gruvbox-dark.css","hash":"8c440d9b4ee19ac03eaee3c6af78ba52e5ba5535","modified":1667377744221},{"_id":"public/css/hl_theme/gruvbox-light.css","hash":"30514aaa242a34647aa666cfca4fc74c595ea8f2","modified":1667377744221},{"_id":"public/css/hl_theme/kimbie-dark.css","hash":"728527fcc308da454722c119b89e6da3025bd1e3","modified":1667377744221},{"_id":"public/css/hl_theme/kimbie-light.css","hash":"0c61926c989163faefb031d27bce3e287d6e10f2","modified":1667377744221},{"_id":"public/css/hl_theme/railscasts.css","hash":"511f2fd2a84d426e5da5cb17880cc08f73beb002","modified":1667377744221},{"_id":"public/css/hl_theme/rainbow.css","hash":"7ff4251938076ddb7e4e49413db82653e5b61321","modified":1667377744221},{"_id":"public/css/hl_theme/school-book.css","hash":"ffbbcd13a74ac2404262c50b7a43053dfd0096ff","modified":1667377744221},{"_id":"public/css/hl_theme/sublime.css","hash":"f65c5b116d9213afb9c324384a2f3bc86cb71121","modified":1667377744221},{"_id":"public/css/hl_theme/sunburst.css","hash":"8a135abac1512cf430d1d1ad2304b79afa1a4d6e","modified":1667377744221},{"_id":"public/css/hl_theme/zenbum.css","hash":"0a78f74a93568e20b32ca7427c719e9bae9a0b55","modified":1667377744221},{"_id":"public/css/gitalk.css","hash":"58177ce227c50ee359fbf99a4fdd26058887afc5","modified":1667377744221},{"_id":"public/css/style.css","hash":"39c55a665a57cef5d2363ee1358bd713f4fe3288","modified":1667377744221},{"_id":"public/js/iconfont.js","hash":"3a0869ca1b09af07d82987e343a3bc4cb9558ecb","modified":1667377744221},{"_id":"public/js/jquery.pjax.js","hash":"191c49fdb40dff115a49cfd2b30dffb888d86550","modified":1667377744221},{"_id":"public/js/gitment.js","hash":"59a1e03f2b0ce61dd9bd405d3c52d3e07cc10dec","modified":1667377744221},{"_id":"public/js/script.js","hash":"f261fb46d822e9b89296c454a965ff34e4b244f9","modified":1667377744221},{"_id":"public/css/fonts/selection.json","hash":"047b615ea32dc48dae5b964061427d41feaaafdf","modified":1667377744221},{"_id":"public/img/mapstruct-plugins.png","hash":"04a62bd593aec8c4d9fa8cf9d04c83a81c9d0000","modified":1667377744221},{"_id":"public/js/gitalk.js","hash":"26ba4841dcb4b178f730f53a8d1f4a7c89442b4f","modified":1667377744221},{"_id":"public/img/syncpool/image-20210930145240966.png","hash":"4db4fc70b01ccaf051eb92d3562ab3ef569c7dbf","modified":1667377744221},{"_id":"public/img/syncpool/image-20210930145657424.png","hash":"71f7c733b03cd6086bcd29a0e8efc260f3aaa7d0","modified":1667377744221},{"_id":"public/img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png","hash":"ac90cf3c6174ffdee192192e02252ae125acbb51","modified":1667377744221},{"_id":"public/img/syncpool/image-20210930143647762.png","hash":"fed976df9deb881e37af29770ceeed89488c9bc9","modified":1667377744221},{"_id":"public/2021/11/02/java/maven-template/index.html","hash":"df7f16b8de9a27d474d9f554b688eb7a50643daf","modified":1667544714120},{"_id":"public/archives/2021/11/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/maven/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"source/_posts/java/java双亲委派机制及用法.md","hash":"0ee2497c14b50ab60ea3e5121882a47339f7a961","modified":1667445840607},{"_id":"source/img/java类加载过程.png","hash":"b086e63e052521d11b3a717845a9c440770d0b80","modified":1667445000811},{"_id":"source/_posts/java/java双亲委派机制及用法.assets/image-20221103111000811.png","hash":"b086e63e052521d11b3a717845a9c440770d0b80","modified":1667445000811},{"_id":"source/img/java双亲委派模型.png","hash":"34180acd2b485e29f105afd7ebf9f12933f0069b","modified":1667445233810},{"_id":"source/_posts/java/java双亲委派机制及用法.assets/image-20221103111353810.png","hash":"34180acd2b485e29f105afd7ebf9f12933f0069b","modified":1667445233810},{"_id":"public/2022/11/03/java/java双亲委派机制及用法/index.html","hash":"39010ca70569a9c8d5684c73a67cac00104c67ad","modified":1667544714120},{"_id":"public/img/java类加载过程.png","hash":"b086e63e052521d11b3a717845a9c440770d0b80","modified":1667445443761},{"_id":"public/img/java双亲委派模型.png","hash":"34180acd2b485e29f105afd7ebf9f12933f0069b","modified":1667445443761},{"_id":"source/_posts/java/arthas.md","hash":"f597caedd602d66e91013028b837c637b6363001","modified":1667477015643},{"_id":"public/2022/11/03/java/arthas/index.html","hash":"0b008305dfb1363b0c7119214da363eefa26bcf8","modified":1667544714120},{"_id":"source/_posts/java/Java-知识图谱.md","hash":"437f2eeb895f271fd6ce010b4b2ada5daa455c60","modified":1667477446912},{"_id":"public/2022/11/03/java/Java-知识图谱/index.html","hash":"b0cef18c5c46250b8e88e87ec2f38a6d5fb73cf0","modified":1667544714120},{"_id":"public/archives/2022/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/archives/2022/11/page/2/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"public/tags/知识图谱/index.html","hash":"a86ca0775ab9251cf0136fa1c227c05a465e8078","modified":1667544714120},{"_id":"source/_posts/todo.md","hash":"470c5f3b420a88f898d0c57a6d337c1f4e9ca640","modified":1678330901751},{"_id":"source/.DS_Store","hash":"22fd8b217f20329f074d7508aae9b29a72e3d418","modified":1670407565567},{"_id":"source/_posts/consul/consul 常用命令.md","hash":"3c2eb50d2ebc0f351e2ef800a67a2a8f7bf36c72","modified":1678962666597},{"_id":"source/_posts/java/caffeine 使用.md","hash":"aa5198dc18d81e113d9298c2b925d100f5b555b9","modified":1679300341055},{"_id":"source/_posts/redis/redis 知识图谱.md","hash":"b0ba70a940e4358a8a3351b9173d34df6038476b","modified":1667556459025},{"_id":"source/_posts/java/spring-boot-consul-配置中心.md","hash":"29181fb3328fbabe94ca530afb8d7c8a0edf7f65","modified":1678857333040},{"_id":"source/_posts/powerjob/快速开始.md","hash":"846fc4fd059b4f3a0922a046f20cf59eccc0ab7f","modified":1678963925116}],"Category":[{"name":"git","_id":"cl9zdnpmr0002iysy4yexey3j"},{"name":"docker","_id":"cl9zdnpmu0007iysye3nd4aze"},{"name":"golang","_id":"cl9zdnpn0000kiysy1qgcdw23"},{"name":"java","_id":"cl9zdnpn9001iiysy27vi0lrd"},{"name":"mac","_id":"cl9zdnpnc001wiysybfjxhcgy"},{"name":"mysql","_id":"cl9zdnpnf0023iysyaxg2cqbm"},{"name":"linux","_id":"cl9zdnpnk002niysy5f9ob47k"},{"name":"python","_id":"cl9zdnpnl002tiysy1ss4756s"},{"name":"redis","_id":"cl9zdnpnm0031iysy2g0uhd9n"},{"name":"其他","_id":"cl9zdnpno0036iysy7p4tfmc8"},{"name":"算法","_id":"cl9zdnpo2005liysycnw77fgz"},{"name":"consul","_id":"clfhsb9tt0002cesyhypx4yd7"},{"name":"powerjob","_id":"clfhsb9u40008cesyg3he52mo"}],"Data":[],"Page":[],"Post":[{"title":"git config","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n## git config\n\n官方文档地址:<https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE>\n自定义 Git - 配置 Git: <https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git>\n\n### 用户信息\n\n安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:\n\n```shell\n$ git config --global user.name \"Joemuhuang\"\n$ git config --global user.email \"Joemuhuang@tencent.com`\n```\n\n### 查看不同级别的配置文件\n\n```shell\n#查看系统config\ngit config --system --list\n \n#查看当前用户(global)配置\ngit config --global --list\n \n#查看当前仓库配置信息\ngit config --local --list\n```\n\n### 检查配置信息\n\n```shell\ngit config --list\n```\n\n### 生成 sshkey\n\n```shell\nssh-keygen -t rsa -C \"youxiang@tencent.com\"\n```\n\n### 验证和修改\n\n```shell\nssh -T git@github.com\n```","source":"_posts/git/git-config.md","raw":"---\ntitle: git config\ntags:\n - git\ncategories:\n - git\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n## git config\n\n官方文档地址:<https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE>\n自定义 Git - 配置 Git: <https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git>\n\n### 用户信息\n\n安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:\n\n```shell\n$ git config --global user.name \"Joemuhuang\"\n$ git config --global user.email \"Joemuhuang@tencent.com`\n```\n\n### 查看不同级别的配置文件\n\n```shell\n#查看系统config\ngit config --system --list\n \n#查看当前用户(global)配置\ngit config --global --list\n \n#查看当前仓库配置信息\ngit config --local --list\n```\n\n### 检查配置信息\n\n```shell\ngit config --list\n```\n\n### 生成 sshkey\n\n```shell\nssh-keygen -t rsa -C \"youxiang@tencent.com\"\n```\n\n### 验证和修改\n\n```shell\nssh -T git@github.com\n```","slug":"git/git-config","published":1,"updated":"2022-11-01T02:58:42.349Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpml0000iysy0qlahgkn","content":"<h2 id=\"git-config\"><a href=\"#git-config\" class=\"headerlink\" title=\"git config\"></a>git config</h2><p>官方文档地址:<a href=\"https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE\">https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE</a><br>自定义 Git - 配置 Git: <a href=\"https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git\">https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git</a></p>\n<h3 id=\"用户信息\"><a href=\"#用户信息\" class=\"headerlink\" title=\"用户信息\"></a>用户信息</h3><p>安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:</p>\n<pre><code class=\"shell\">$ git config --global user.name "Joemuhuang"\n$ git config --global user.email "Joemuhuang@tencent.com`\n</code></pre>\n<h3 id=\"查看不同级别的配置文件\"><a href=\"#查看不同级别的配置文件\" class=\"headerlink\" title=\"查看不同级别的配置文件\"></a>查看不同级别的配置文件</h3><pre><code class=\"shell\">#查看系统config\ngit config --system --list\n \n#查看当前用户(global)配置\ngit config --global --list\n \n#查看当前仓库配置信息\ngit config --local --list\n</code></pre>\n<h3 id=\"检查配置信息\"><a href=\"#检查配置信息\" class=\"headerlink\" title=\"检查配置信息\"></a>检查配置信息</h3><pre><code class=\"shell\">git config --list\n</code></pre>\n<h3 id=\"生成-sshkey\"><a href=\"#生成-sshkey\" class=\"headerlink\" title=\"生成 sshkey\"></a>生成 sshkey</h3><pre><code class=\"shell\">ssh-keygen -t rsa -C "youxiang@tencent.com"\n</code></pre>\n<h3 id=\"验证和修改\"><a href=\"#验证和修改\" class=\"headerlink\" title=\"验证和修改\"></a>验证和修改</h3><pre><code class=\"shell\">ssh -T git@github.com\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"git-config\"><a href=\"#git-config\" class=\"headerlink\" title=\"git config\"></a>git config</h2><p>官方文档地址:<a href=\"https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE\">https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE</a><br>自定义 Git - 配置 Git: <a href=\"https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git\">https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git</a></p>\n<h3 id=\"用户信息\"><a href=\"#用户信息\" class=\"headerlink\" title=\"用户信息\"></a>用户信息</h3><p>安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:</p>\n<pre><code class=\"shell\">$ git config --global user.name "Joemuhuang"\n$ git config --global user.email "Joemuhuang@tencent.com`\n</code></pre>\n<h3 id=\"查看不同级别的配置文件\"><a href=\"#查看不同级别的配置文件\" class=\"headerlink\" title=\"查看不同级别的配置文件\"></a>查看不同级别的配置文件</h3><pre><code class=\"shell\">#查看系统config\ngit config --system --list\n \n#查看当前用户(global)配置\ngit config --global --list\n \n#查看当前仓库配置信息\ngit config --local --list\n</code></pre>\n<h3 id=\"检查配置信息\"><a href=\"#检查配置信息\" class=\"headerlink\" title=\"检查配置信息\"></a>检查配置信息</h3><pre><code class=\"shell\">git config --list\n</code></pre>\n<h3 id=\"生成-sshkey\"><a href=\"#生成-sshkey\" class=\"headerlink\" title=\"生成 sshkey\"></a>生成 sshkey</h3><pre><code class=\"shell\">ssh-keygen -t rsa -C "youxiang@tencent.com"\n</code></pre>\n<h3 id=\"验证和修改\"><a href=\"#验证和修改\" class=\"headerlink\" title=\"验证和修改\"></a>验证和修改</h3><pre><code class=\"shell\">ssh -T git@github.com\n</code></pre>\n"},{"title":"docker 常用命令","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n## docker 常用命令\n\n> 镜像导入导出\n\n> 导出\n\n```shell\ndocker save {imageid} > filename.tar imagesname\n```\n举例\n```shell\ndocker save 4e8c7ea9f913 > pulsar-manager_0.2.0.tar apachepulsar/pulsar-manager\n```\n\n> 导入\n\n``` shell\ndocker load [OPTIONS]\n```\nOPTIONS 说明:\n\n--input , -i : 指定导入的文件,代替 STDIN。\n--quiet , -q : 精简输出信息。\n\n> 批量删除未使用的容器\n\n```shell\n# 方法一\ndocker rm `docker ps -a|grep Exited|awk '{print $1}'`\n# 方法二\ndocker container prune\n```\n\n> 删除所有镜像\n\n```shell\nsudo docker rmi $(docker images -q)\n```\n\n> 清理镜像\n\n```shell\ndocker container prune\ndocker system prune\ndocker volume prune\n```\n\n","source":"_posts/docker/docker-常用命令.md","raw":"---\ntitle: docker 常用命令\ntags:\n - docker\ncategories:\n - docker\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n## docker 常用命令\n\n> 镜像导入导出\n\n> 导出\n\n```shell\ndocker save {imageid} > filename.tar imagesname\n```\n举例\n```shell\ndocker save 4e8c7ea9f913 > pulsar-manager_0.2.0.tar apachepulsar/pulsar-manager\n```\n\n> 导入\n\n``` shell\ndocker load [OPTIONS]\n```\nOPTIONS 说明:\n\n--input , -i : 指定导入的文件,代替 STDIN。\n--quiet , -q : 精简输出信息。\n\n> 批量删除未使用的容器\n\n```shell\n# 方法一\ndocker rm `docker ps -a|grep Exited|awk '{print $1}'`\n# 方法二\ndocker container prune\n```\n\n> 删除所有镜像\n\n```shell\nsudo docker rmi $(docker images -q)\n```\n\n> 清理镜像\n\n```shell\ndocker container prune\ndocker system prune\ndocker volume prune\n```\n\n","slug":"docker/docker-常用命令","published":1,"updated":"2023-03-21T04:57:08.978Z","_id":"cl9zdnpmp0001iysy32vof5gp","comments":1,"layout":"post","photos":[],"link":"","content":"<h2 id=\"docker-常用命令\"><a href=\"#docker-常用命令\" class=\"headerlink\" title=\"docker 常用命令\"></a>docker 常用命令</h2><blockquote>\n<p>镜像导入导出</p>\n</blockquote>\n<blockquote>\n<p>导出</p>\n</blockquote>\n<pre><code class=\"shell\">docker save {imageid} > filename.tar imagesname\n</code></pre>\n<p>举例</p>\n<pre><code class=\"shell\">docker save 4e8c7ea9f913 > pulsar-manager_0.2.0.tar apachepulsar/pulsar-manager\n</code></pre>\n<blockquote>\n<p>导入</p>\n</blockquote>\n<pre><code class=\"shell\">docker load [OPTIONS]\n</code></pre>\n<p>OPTIONS 说明:</p>\n<p>–input , -i : 指定导入的文件,代替 STDIN。<br>–quiet , -q : 精简输出信息。</p>\n<blockquote>\n<p>批量删除未使用的容器</p>\n</blockquote>\n<pre><code class=\"shell\"># 方法一\ndocker rm `docker ps -a|grep Exited|awk '{print $1}'`\n# 方法二\ndocker container prune\n</code></pre>\n<blockquote>\n<p>删除所有镜像</p>\n</blockquote>\n<pre><code class=\"shell\">sudo docker rmi $(docker images -q)\n</code></pre>\n<blockquote>\n<p>清理镜像</p>\n</blockquote>\n<pre><code class=\"shell\">docker container prune\ndocker system prune\ndocker volume prune\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"docker-常用命令\"><a href=\"#docker-常用命令\" class=\"headerlink\" title=\"docker 常用命令\"></a>docker 常用命令</h2><blockquote>\n<p>镜像导入导出</p>\n</blockquote>\n<blockquote>\n<p>导出</p>\n</blockquote>\n<pre><code class=\"shell\">docker save {imageid} > filename.tar imagesname\n</code></pre>\n<p>举例</p>\n<pre><code class=\"shell\">docker save 4e8c7ea9f913 > pulsar-manager_0.2.0.tar apachepulsar/pulsar-manager\n</code></pre>\n<blockquote>\n<p>导入</p>\n</blockquote>\n<pre><code class=\"shell\">docker load [OPTIONS]\n</code></pre>\n<p>OPTIONS 说明:</p>\n<p>–input , -i : 指定导入的文件,代替 STDIN。<br>–quiet , -q : 精简输出信息。</p>\n<blockquote>\n<p>批量删除未使用的容器</p>\n</blockquote>\n<pre><code class=\"shell\"># 方法一\ndocker rm `docker ps -a|grep Exited|awk '{print $1}'`\n# 方法二\ndocker container prune\n</code></pre>\n<blockquote>\n<p>删除所有镜像</p>\n</blockquote>\n<pre><code class=\"shell\">sudo docker rmi $(docker images -q)\n</code></pre>\n<blockquote>\n<p>清理镜像</p>\n</blockquote>\n<pre><code class=\"shell\">docker container prune\ndocker system prune\ndocker volume prune\n</code></pre>\n"},{"title":"git 常用命令","auto_excerpt":{"enable":true,"length":150},"date":"2021-09-08T07:43:04.000Z","_content":"\n**git pull**\n\n**git push** \n\n**git checkout <branch>**\n\n```shell\n# 切换分支到本地 develop 分支\ngit checkout develop\n\n# 从当前分支新建一个分支 develop,并切换到 develop 分支\ngit checkout -b develop \n```\n\n**git status**\n\n**git log**\n\n**git stash**\n\n**git branch**\n\n**git rebase**\n\n```shell\n# 变基到两个提交之前\ngit rebase -i HEAD~2\n```\n\n**git submodule**\n\n```shell\n# 所有子模块执行 git reset --hard\ngit submodule foreach --recursive git reset --hard\n\n# 更新子模块\ngit submodule update --init \t\ngit submodule update --remote\n\n```\n\n**git filter-branch 批量修改提交人**\n\n```shell\ngit filter-branch -f --env-filter '\nif [ \"$GIT_AUTHOR_NAME\" = \"oldName\" ]\nthen\nexport GIT_AUTHOR_NAME=\"newName\"\nexport GIT_AUTHOR_EMAIL=\"newEmail\"\nfi\n' HEAD\n\ngit filter-branch -f --env-filter '\nif [ \"$GIT_COMMITTER_NAME\" = \"oldName\" ]\nthen\nexport GIT_COMMITTER_NAME=\"newName\"\nexport GIT_COMMITTER_EMAIL=\"newEmail\"\nfi\n' HEAD\n```\n\n","source":"_posts/git/git-常用命令.md","raw":"---\ntitle: git 常用命令\ntags:\n - git\ncategories:\n - git\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-09-08 15:43:04\n---\n\n**git pull**\n\n**git push** \n\n**git checkout <branch>**\n\n```shell\n# 切换分支到本地 develop 分支\ngit checkout develop\n\n# 从当前分支新建一个分支 develop,并切换到 develop 分支\ngit checkout -b develop \n```\n\n**git status**\n\n**git log**\n\n**git stash**\n\n**git branch**\n\n**git rebase**\n\n```shell\n# 变基到两个提交之前\ngit rebase -i HEAD~2\n```\n\n**git submodule**\n\n```shell\n# 所有子模块执行 git reset --hard\ngit submodule foreach --recursive git reset --hard\n\n# 更新子模块\ngit submodule update --init \t\ngit submodule update --remote\n\n```\n\n**git filter-branch 批量修改提交人**\n\n```shell\ngit filter-branch -f --env-filter '\nif [ \"$GIT_AUTHOR_NAME\" = \"oldName\" ]\nthen\nexport GIT_AUTHOR_NAME=\"newName\"\nexport GIT_AUTHOR_EMAIL=\"newEmail\"\nfi\n' HEAD\n\ngit filter-branch -f --env-filter '\nif [ \"$GIT_COMMITTER_NAME\" = \"oldName\" ]\nthen\nexport GIT_COMMITTER_NAME=\"newName\"\nexport GIT_COMMITTER_EMAIL=\"newEmail\"\nfi\n' HEAD\n```\n\n","slug":"git/git-常用命令","published":1,"updated":"2022-11-04T03:41:04.794Z","_id":"cl9zdnpms0004iysycvwk73p6","comments":1,"layout":"post","photos":[],"link":"","content":"<p><strong>git pull</strong></p>\n<p><strong>git push</strong> </p>\n<p><strong>git checkout <branch></strong></p>\n<pre><code class=\"shell\"># 切换分支到本地 develop 分支\ngit checkout develop\n\n# 从当前分支新建一个分支 develop,并切换到 develop 分支\ngit checkout -b develop \n</code></pre>\n<p><strong>git status</strong></p>\n<p><strong>git log</strong></p>\n<p><strong>git stash</strong></p>\n<p><strong>git branch</strong></p>\n<p><strong>git rebase</strong></p>\n<pre><code class=\"shell\"># 变基到两个提交之前\ngit rebase -i HEAD~2\n</code></pre>\n<p><strong>git submodule</strong></p>\n<pre><code class=\"shell\"># 所有子模块执行 git reset --hard\ngit submodule foreach --recursive git reset --hard\n\n# 更新子模块\ngit submodule update --init \ngit submodule update --remote\n</code></pre>\n<p><strong>git filter-branch 批量修改提交人</strong></p>\n<pre><code class=\"shell\">git filter-branch -f --env-filter '\nif [ "$GIT_AUTHOR_NAME" = "oldName" ]\nthen\nexport GIT_AUTHOR_NAME="newName"\nexport GIT_AUTHOR_EMAIL="newEmail"\nfi\n' HEAD\n\ngit filter-branch -f --env-filter '\nif [ "$GIT_COMMITTER_NAME" = "oldName" ]\nthen\nexport GIT_COMMITTER_NAME="newName"\nexport GIT_COMMITTER_EMAIL="newEmail"\nfi\n' HEAD\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p><strong>git pull</strong></p>\n<p><strong>git push</strong> </p>\n<p><strong>git checkout <branch></strong></p>\n<pre><code class=\"shell\"># 切换分支到本地 develop 分支\ngit checkout develop\n\n# 从当前分支新建一个分支 develop,并切换到 develop 分支\ngit checkout -b develop \n</code></pre>\n<p><strong>git status</strong></p>\n<p><strong>git log</strong></p>\n<p><strong>git stash</strong></p>\n<p><strong>git branch</strong></p>\n<p><strong>git rebase</strong></p>\n<pre><code class=\"shell\"># 变基到两个提交之前\ngit rebase -i HEAD~2\n</code></pre>\n<p><strong>git submodule</strong></p>\n<pre><code class=\"shell\"># 所有子模块执行 git reset --hard\ngit submodule foreach --recursive git reset --hard\n\n# 更新子模块\ngit submodule update --init \ngit submodule update --remote\n</code></pre>\n<p><strong>git filter-branch 批量修改提交人</strong></p>\n<pre><code class=\"shell\">git filter-branch -f --env-filter '\nif [ "$GIT_AUTHOR_NAME" = "oldName" ]\nthen\nexport GIT_AUTHOR_NAME="newName"\nexport GIT_AUTHOR_EMAIL="newEmail"\nfi\n' HEAD\n\ngit filter-branch -f --env-filter '\nif [ "$GIT_COMMITTER_NAME" = "oldName" ]\nthen\nexport GIT_COMMITTER_NAME="newName"\nexport GIT_COMMITTER_EMAIL="newEmail"\nfi\n' HEAD\n</code></pre>\n"},{"title":"git 统计命令","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n\n\n## Git 统计命令\n\n#### 统计某人的代码提交量,包括增加,删除\n\n``` shell\ngit log --author=\"$(git config --get user.name)\" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf \"added lines: %s removed lines : %s total lines: %s\\n\",add,subs,loc }'\n```\n#### 仓库提交者排名前 5(如果看全部,去掉 head 管道即可)\n```shell\ngit log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5\n```\n#### 仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\n```shell\ngit log --pretty=format:%ae | gawk -- '{ ++c[$0]; } END { for(cc in c) printf \"%5d %s\\n\",c[cc],cc; }' | sort -u -n -r | head -n 5 \n```\n#### 贡献者统计:\n```shell\ngit log --pretty='%aN' | sort -u | wc -l\n```\n\n#### 提交数统计:\n```shell\ngit log --oneline | wc -l \n````\n#### 添加或修改的代码行数:\n```shell\ngit log --stat|perl -ne 'END { print $c } $c += $1 if /(\\d+) insertions/'\n```\n\n#### git log 参数说明:\n```\n--author 指定作者\n--stat 显示每次更新的文件修改统计信息,会列出具体文件列表\n--shortstat 统计每个commit 的文件修改行数,包括增加,删除,但不列出文件列表: \n--numstat 统计每个commit 的文件修改行数,包括增加,删除,并列出文件列表:\n \n \n-p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新\n 例如:git log -p -2\n--name-only 仅在提交信息后显示已修改的文件清单\n--name-status 显示新增、修改、删除的文件清单\n--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符\n--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)\n--graph 显示 ASCII 图形表示的分支合并历史\n--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)\n 例如: git log --pretty=oneline ; git log --pretty=short ; git log --pretty=full ; git log --pretty=fuller\n--pretty=tformat: 可以定制要显示的记录格式,这样的输出便于后期编程提取分析\n 例如:git log --pretty=format:\"\"%h - %an, %ar : %s\"\"\n 下面列出了常用的格式占位符写法及其代表的意义。 \n 选项 说明 \n %H 提交对象(commit)的完整哈希字串 \n %h 提交对象的简短哈希字串 \n %T 树对象(tree)的完整哈希字串 \n %t 树对象的简短哈希字串 \n %P 父对象(parent)的完整哈希字串 \n %p 父对象的简短哈希字串 \n %an 作者(author)的名字 \n %ae 作者的电子邮件地址 \n %ad 作者修订日期(可以用 -date= 选项定制格式) \n %ar 作者修订日期,按多久以前的方式显示 \n %cn 提交者(committer)的名字 \n %ce 提交者的电子邮件地址 \n %cd 提交日期 \n %cr 提交日期,按多久以前的方式显示 \n %s 提交说明 \n--since 限制显示输出的范围,\n 例如: git log --since=2.weeks 显示最近两周的提交\n 选项 说明 \n -(n) 仅显示最近的 n 条提交 \n --since, --after 仅显示指定时间之后的提交。 \n --until, --before 仅显示指定时间之前的提交。 \n --author 仅显示指定作者相关的提交。 \n --committer 仅显示指定提交者相关的提交。\n \n 一些例子: git log --until=1.minute.ago // 一分钟之前的所有 log git log --since=1.day.ago //一天之内的log git log --since=1.hour.ago //一个小时之内的 log git log --since=`.month.ago --until=2.weeks.ago //一个月之前到半个月之前的log git\nlog --since ==2013-08.01 --until=2013-09-07 //某个时间段的 log \n````\n\n#### git blame\n看看某一个文件的相关历史记录\n例如:\n```shell\ngit blame index.html --date short\n```","source":"_posts/git/git-统计命令.md","raw":"---\ntitle: git 统计命令\ntags:\n - git\ncategories:\n - git\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n\n\n## Git 统计命令\n\n#### 统计某人的代码提交量,包括增加,删除\n\n``` shell\ngit log --author=\"$(git config --get user.name)\" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf \"added lines: %s removed lines : %s total lines: %s\\n\",add,subs,loc }'\n```\n#### 仓库提交者排名前 5(如果看全部,去掉 head 管道即可)\n```shell\ngit log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5\n```\n#### 仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\n```shell\ngit log --pretty=format:%ae | gawk -- '{ ++c[$0]; } END { for(cc in c) printf \"%5d %s\\n\",c[cc],cc; }' | sort -u -n -r | head -n 5 \n```\n#### 贡献者统计:\n```shell\ngit log --pretty='%aN' | sort -u | wc -l\n```\n\n#### 提交数统计:\n```shell\ngit log --oneline | wc -l \n````\n#### 添加或修改的代码行数:\n```shell\ngit log --stat|perl -ne 'END { print $c } $c += $1 if /(\\d+) insertions/'\n```\n\n#### git log 参数说明:\n```\n--author 指定作者\n--stat 显示每次更新的文件修改统计信息,会列出具体文件列表\n--shortstat 统计每个commit 的文件修改行数,包括增加,删除,但不列出文件列表: \n--numstat 统计每个commit 的文件修改行数,包括增加,删除,并列出文件列表:\n \n \n-p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新\n 例如:git log -p -2\n--name-only 仅在提交信息后显示已修改的文件清单\n--name-status 显示新增、修改、删除的文件清单\n--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符\n--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)\n--graph 显示 ASCII 图形表示的分支合并历史\n--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)\n 例如: git log --pretty=oneline ; git log --pretty=short ; git log --pretty=full ; git log --pretty=fuller\n--pretty=tformat: 可以定制要显示的记录格式,这样的输出便于后期编程提取分析\n 例如:git log --pretty=format:\"\"%h - %an, %ar : %s\"\"\n 下面列出了常用的格式占位符写法及其代表的意义。 \n 选项 说明 \n %H 提交对象(commit)的完整哈希字串 \n %h 提交对象的简短哈希字串 \n %T 树对象(tree)的完整哈希字串 \n %t 树对象的简短哈希字串 \n %P 父对象(parent)的完整哈希字串 \n %p 父对象的简短哈希字串 \n %an 作者(author)的名字 \n %ae 作者的电子邮件地址 \n %ad 作者修订日期(可以用 -date= 选项定制格式) \n %ar 作者修订日期,按多久以前的方式显示 \n %cn 提交者(committer)的名字 \n %ce 提交者的电子邮件地址 \n %cd 提交日期 \n %cr 提交日期,按多久以前的方式显示 \n %s 提交说明 \n--since 限制显示输出的范围,\n 例如: git log --since=2.weeks 显示最近两周的提交\n 选项 说明 \n -(n) 仅显示最近的 n 条提交 \n --since, --after 仅显示指定时间之后的提交。 \n --until, --before 仅显示指定时间之前的提交。 \n --author 仅显示指定作者相关的提交。 \n --committer 仅显示指定提交者相关的提交。\n \n 一些例子: git log --until=1.minute.ago // 一分钟之前的所有 log git log --since=1.day.ago //一天之内的log git log --since=1.hour.ago //一个小时之内的 log git log --since=`.month.ago --until=2.weeks.ago //一个月之前到半个月之前的log git\nlog --since ==2013-08.01 --until=2013-09-07 //某个时间段的 log \n````\n\n#### git blame\n看看某一个文件的相关历史记录\n例如:\n```shell\ngit blame index.html --date short\n```","slug":"git/git-统计命令","published":1,"updated":"2022-11-01T02:55:47.605Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpmt0005iysye7gu9qj1","content":"<h2 id=\"Git-统计命令\"><a href=\"#Git-统计命令\" class=\"headerlink\" title=\"Git 统计命令\"></a>Git 统计命令</h2><h4 id=\"统计某人的代码提交量,包括增加,删除\"><a href=\"#统计某人的代码提交量,包括增加,删除\" class=\"headerlink\" title=\"统计某人的代码提交量,包括增加,删除\"></a>统计某人的代码提交量,包括增加,删除</h4><pre><code class=\"shell\">git log --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\\n",add,subs,loc }'\n</code></pre>\n<h4 id=\"仓库提交者排名前-5(如果看全部,去掉-head-管道即可)\"><a href=\"#仓库提交者排名前-5(如果看全部,去掉-head-管道即可)\" class=\"headerlink\" title=\"仓库提交者排名前 5(如果看全部,去掉 head 管道即可)\"></a>仓库提交者排名前 5(如果看全部,去掉 head 管道即可)</h4><pre><code class=\"shell\">git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5\n</code></pre>\n<h4 id=\"仓库提交者(邮箱)排名前-5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\"><a href=\"#仓库提交者(邮箱)排名前-5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\" class=\"headerlink\" title=\"仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\"></a>仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字</h4><pre><code class=\"shell\">git log --pretty=format:%ae | gawk -- '{ ++c[$0]; } END { for(cc in c) printf "%5d %s\\n",c[cc],cc; }' | sort -u -n -r | head -n 5 \n</code></pre>\n<h4 id=\"贡献者统计:\"><a href=\"#贡献者统计:\" class=\"headerlink\" title=\"贡献者统计:\"></a>贡献者统计:</h4><pre><code class=\"shell\">git log --pretty='%aN' | sort -u | wc -l\n</code></pre>\n<h4 id=\"提交数统计:\"><a href=\"#提交数统计:\" class=\"headerlink\" title=\"提交数统计:\"></a>提交数统计:</h4><pre><code class=\"shell\">git log --oneline | wc -l \n</code></pre>\n<h4 id=\"添加或修改的代码行数:\"><a href=\"#添加或修改的代码行数:\" class=\"headerlink\" title=\"添加或修改的代码行数:\"></a>添加或修改的代码行数:</h4><pre><code class=\"shell\">git log --stat|perl -ne 'END { print $c } $c += $1 if /(\\d+) insertions/'\n</code></pre>\n<h4 id=\"git-log-参数说明:\"><a href=\"#git-log-参数说明:\" class=\"headerlink\" title=\"git log 参数说明:\"></a>git log 参数说明:</h4><pre><code>--author 指定作者\n--stat 显示每次更新的文件修改统计信息,会列出具体文件列表\n--shortstat 统计每个commit 的文件修改行数,包括增加,删除,但不列出文件列表: \n--numstat 统计每个commit 的文件修改行数,包括增加,删除,并列出文件列表:\n \n \n-p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新\n 例如:git log -p -2\n--name-only 仅在提交信息后显示已修改的文件清单\n--name-status 显示新增、修改、删除的文件清单\n--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符\n--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)\n--graph 显示 ASCII 图形表示的分支合并历史\n--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)\n 例如: git log --pretty=oneline ; git log --pretty=short ; git log --pretty=full ; git log --pretty=fuller\n--pretty=tformat: 可以定制要显示的记录格式,这样的输出便于后期编程提取分析\n 例如:git log --pretty=format:""%h - %an, %ar : %s""\n 下面列出了常用的格式占位符写法及其代表的意义。 \n 选项 说明 \n %H 提交对象(commit)的完整哈希字串 \n %h 提交对象的简短哈希字串 \n %T 树对象(tree)的完整哈希字串 \n %t 树对象的简短哈希字串 \n %P 父对象(parent)的完整哈希字串 \n %p 父对象的简短哈希字串 \n %an 作者(author)的名字 \n %ae 作者的电子邮件地址 \n %ad 作者修订日期(可以用 -date= 选项定制格式) \n %ar 作者修订日期,按多久以前的方式显示 \n %cn 提交者(committer)的名字 \n %ce 提交者的电子邮件地址 \n %cd 提交日期 \n %cr 提交日期,按多久以前的方式显示 \n %s 提交说明 \n--since 限制显示输出的范围,\n 例如: git log --since=2.weeks 显示最近两周的提交\n 选项 说明 \n -(n) 仅显示最近的 n 条提交 \n --since, --after 仅显示指定时间之后的提交。 \n --until, --before 仅显示指定时间之前的提交。 \n --author 仅显示指定作者相关的提交。 \n --committer 仅显示指定提交者相关的提交。\n \n 一些例子: git log --until=1.minute.ago // 一分钟之前的所有 log git log --since=1.day.ago //一天之内的log git log --since=1.hour.ago //一个小时之内的 log git log --since=`.month.ago --until=2.weeks.ago //一个月之前到半个月之前的log git\nlog --since ==2013-08.01 --until=2013-09-07 //某个时间段的 log \n</code></pre>\n<h4 id=\"git-blame\"><a href=\"#git-blame\" class=\"headerlink\" title=\"git blame\"></a>git blame</h4><p>看看某一个文件的相关历史记录<br>例如:</p>\n<pre><code class=\"shell\">git blame index.html --date short\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"Git-统计命令\"><a href=\"#Git-统计命令\" class=\"headerlink\" title=\"Git 统计命令\"></a>Git 统计命令</h2><h4 id=\"统计某人的代码提交量,包括增加,删除\"><a href=\"#统计某人的代码提交量,包括增加,删除\" class=\"headerlink\" title=\"统计某人的代码提交量,包括增加,删除\"></a>统计某人的代码提交量,包括增加,删除</h4><pre><code class=\"shell\">git log --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\\n",add,subs,loc }'\n</code></pre>\n<h4 id=\"仓库提交者排名前-5(如果看全部,去掉-head-管道即可)\"><a href=\"#仓库提交者排名前-5(如果看全部,去掉-head-管道即可)\" class=\"headerlink\" title=\"仓库提交者排名前 5(如果看全部,去掉 head 管道即可)\"></a>仓库提交者排名前 5(如果看全部,去掉 head 管道即可)</h4><pre><code class=\"shell\">git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5\n</code></pre>\n<h4 id=\"仓库提交者(邮箱)排名前-5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\"><a href=\"#仓库提交者(邮箱)排名前-5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\" class=\"headerlink\" title=\"仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字\"></a>仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字</h4><pre><code class=\"shell\">git log --pretty=format:%ae | gawk -- '{ ++c[$0]; } END { for(cc in c) printf "%5d %s\\n",c[cc],cc; }' | sort -u -n -r | head -n 5 \n</code></pre>\n<h4 id=\"贡献者统计:\"><a href=\"#贡献者统计:\" class=\"headerlink\" title=\"贡献者统计:\"></a>贡献者统计:</h4><pre><code class=\"shell\">git log --pretty='%aN' | sort -u | wc -l\n</code></pre>\n<h4 id=\"提交数统计:\"><a href=\"#提交数统计:\" class=\"headerlink\" title=\"提交数统计:\"></a>提交数统计:</h4><pre><code class=\"shell\">git log --oneline | wc -l \n</code></pre>\n<h4 id=\"添加或修改的代码行数:\"><a href=\"#添加或修改的代码行数:\" class=\"headerlink\" title=\"添加或修改的代码行数:\"></a>添加或修改的代码行数:</h4><pre><code class=\"shell\">git log --stat|perl -ne 'END { print $c } $c += $1 if /(\\d+) insertions/'\n</code></pre>\n<h4 id=\"git-log-参数说明:\"><a href=\"#git-log-参数说明:\" class=\"headerlink\" title=\"git log 参数说明:\"></a>git log 参数说明:</h4><pre><code>--author 指定作者\n--stat 显示每次更新的文件修改统计信息,会列出具体文件列表\n--shortstat 统计每个commit 的文件修改行数,包括增加,删除,但不列出文件列表: \n--numstat 统计每个commit 的文件修改行数,包括增加,删除,并列出文件列表:\n \n \n-p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新\n 例如:git log -p -2\n--name-only 仅在提交信息后显示已修改的文件清单\n--name-status 显示新增、修改、删除的文件清单\n--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符\n--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)\n--graph 显示 ASCII 图形表示的分支合并历史\n--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)\n 例如: git log --pretty=oneline ; git log --pretty=short ; git log --pretty=full ; git log --pretty=fuller\n--pretty=tformat: 可以定制要显示的记录格式,这样的输出便于后期编程提取分析\n 例如:git log --pretty=format:""%h - %an, %ar : %s""\n 下面列出了常用的格式占位符写法及其代表的意义。 \n 选项 说明 \n %H 提交对象(commit)的完整哈希字串 \n %h 提交对象的简短哈希字串 \n %T 树对象(tree)的完整哈希字串 \n %t 树对象的简短哈希字串 \n %P 父对象(parent)的完整哈希字串 \n %p 父对象的简短哈希字串 \n %an 作者(author)的名字 \n %ae 作者的电子邮件地址 \n %ad 作者修订日期(可以用 -date= 选项定制格式) \n %ar 作者修订日期,按多久以前的方式显示 \n %cn 提交者(committer)的名字 \n %ce 提交者的电子邮件地址 \n %cd 提交日期 \n %cr 提交日期,按多久以前的方式显示 \n %s 提交说明 \n--since 限制显示输出的范围,\n 例如: git log --since=2.weeks 显示最近两周的提交\n 选项 说明 \n -(n) 仅显示最近的 n 条提交 \n --since, --after 仅显示指定时间之后的提交。 \n --until, --before 仅显示指定时间之前的提交。 \n --author 仅显示指定作者相关的提交。 \n --committer 仅显示指定提交者相关的提交。\n \n 一些例子: git log --until=1.minute.ago // 一分钟之前的所有 log git log --since=1.day.ago //一天之内的log git log --since=1.hour.ago //一个小时之内的 log git log --since=`.month.ago --until=2.weeks.ago //一个月之前到半个月之前的log git\nlog --since ==2013-08.01 --until=2013-09-07 //某个时间段的 log \n</code></pre>\n<h4 id=\"git-blame\"><a href=\"#git-blame\" class=\"headerlink\" title=\"git blame\"></a>git blame</h4><p>看看某一个文件的相关历史记录<br>例如:</p>\n<pre><code class=\"shell\">git blame index.html --date short\n</code></pre>\n"},{"title":"gin 验证器","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n\n\n[toc]\n\n### 前言\ngin的验证器底层使用的是 validator 库实现的,故gin验证器可以使用validator库中的所有功能\n- 所有标签\n- 翻译器\n- 自定义校验器\n- 其他(不一一列举)\n\n不同的是gin使用的Tag 为 binding,而validator库使用的是 validate;\ngin中在初始化validate时通过validate.SetTagName(\"binding\")设置标签\n\n```golang\nfunc (v *defaultValidator) lazyinit() {\n\tv.once.Do(func() {\n\t\tv.validate = validator.New()\n\t\tv.validate.SetTagName(\"binding\")\n\t})\n}\n```\n\n\n本文中 gin 版本为 v1.6.3, validator版本为 v10\n* [gin](https://github.com/gin-gonic/gin)\n* [validator](https://github.com/go-playground/validator)\n\n### validator 库字段\n**gin 使用时把 validate tag 改为 binding 即可**\n\n| 字段 | 描述 | 例子 |\n| -------------------- | ------------------------------------------------------------ | -------------------------------------------------------- |\n| required | 必填 | Field或Struct validate:\"required\" |\n| omitempty | 空时忽略 | Field或Struct validate:\"omitempty\" |\n| len | 长度 | Field validate:\"len=0\" |\n| eq | 等于 | Field validate:\"eq=0\" |\n| gt | 大于 | Field validate:\"gt=0\" |\n| gte | 大于等于 | Field validate:\"gte=0\" |\n| lt | 小于 | Field validate:\"lt=0\" |\n| lte | 小于等于 | Field validate:\"lte=0\" |\n| eqfield | 同一结构体字段相等 | Field validate:\"eqfield=Field2\" |\n| nefield | 同一结构体字段不相等 | Field validate:\"nefield=Field2\" |\n| gtfield | 大于同一结构体字段 | Field validate:\"gtfield=Field2\" |\n| gtefield | 大于等于同一结构体字段 | Field validate:\"gtefield=Field2\" |\n| ltfield | 小于同一结构体字段 | Field validate:\"ltfield=Field2\" |\n| ltefield | 小于等于同一结构体字段 | Field validate:\"ltefield=Field2\" |\n| eqcsfield | 跨不同结构体字段相等 | Struct1.Field validate:\"eqcsfield=Struct2.Field2\" |\n| necsfield | 跨不同结构体字段不相等 | Struct1.Field validate:\"necsfield=Struct2.Field2\" |\n| gtcsfield | 大于跨不同结构体字段 | Struct1.Field validate:\"gtcsfield=Struct2.Field2\" |\n| gtecsfield | 大于等于跨不同结构体字段 | Struct1.Field validate:\"gtecsfield=Struct2.Field2\" |\n| ltcsfield | 小于跨不同结构体字段 | Struct1.Field validate:\"ltcsfield=Struct2.Field2\" |\n| ltecsfield | 小于等于跨不同结构体字段 | Struct1.Field validate:\"ltecsfield=Struct2.Field2\" |\n| min | 最大值 | Field validate:\"min=1\" |\n| max | 最小值 | Field validate:\"max=2\" |\n| structonly | 仅验证结构体,不验证任何结构体字段 | Struct validate:\"structonly\" |\n| nostructlevel | 不运行任何结构级别的验证 | Struct validate:\"nostructlevel\" |\n| dive | 向下延伸验证,多层向下需要多个dive标记 | [][]string validate:\"gt=0,dive,len=1,dive,required\" |\n| dive | Keys& EndKeys\t与dive同时使用,用于对map对象的键的和值的验证,keys为键,endkeys为值 | map[string]string validate :\"gt=0,dive,keys,eq=1 |\n| required_with | 其他字段其中一个不为空且当前字段不为空 | Field validate:\"required_with=Field1 Field2\" |\n| required_with_all | 其他所有字段不为空且当前字段不为空 | Field validate:\"required_with_all=Field1 Field2\" |\n| required_without | 其他字段其中一个为空且当前字段不为空 | Field `validate:”required_without=Field1 Field2” |\n| required_without_all | 其他所有字段为空且当前字段不为空 | Field validate:\"required_without_all=Field1 Field2\" |\n| isdefault | 是默认值 | Field validate:\"isdefault=0\" |\n| oneof | 其中之一 | Field validate:\"oneof=5 7 9\" |\n| containsfield | 字段包含另一个字段 | Field validate:\"containsfield=Field2\" |\n| excludesfield | 字段不包含另一个字段 | Field validate:\"excludesfield=Field2\" |\n| unique | 是否唯一,通常用于切片或结构体 | Field validate:\"unique\" |\n| alphanum | 字符串值是否只包含 | ASCII 字母数字字符\tField validate:\"alphanum\" |\n| alphaunicode | 字符串值是否只包含 | unicode 字符\tField validate:\"alphaunicode\" |\n| alphanumunicode | 字符串值是否只包含 | unicode 字母数字字符\tField validate:\"alphanumunicode\" |\n| numeric | 字符串值是否包含基本的数值 | Field validate:\"numeric\" |\n| hexadecimal | 字符串值是否包含有效的十六进制 | Field validate:\"hexadecimal\" |\n| hexcolor | 字符串值是否包含有效的十六进制颜色 | Field validate:\"hexcolor\" |\n| lowercase | 符串值是否只包含小写字符 | Field validate:\"lowercase\" |\n| uppercase | 符串值是否只包含大写字符 | Field validate:\"uppercase\" |\n| email | 字符串值包含一个有效的电子邮件 | Field validate:\"email\" |\n| json | 字符串值是否为有效的 | JSON\tField validate:\"json\" |\n| file | 符串值是否包含有效的文件路径,以及该文件是否存在于计算机上 | Field validate:\"file\" |\n| url | 符串值是否包含有效的 | url\tField validate:\"url\" |\n| uri | 符串值是否包含有效的 | uri\tField validate:\"uri\" |\n| base64 | 字符串值是否包含有效的 | base64值\tField validate:\"base64\" |\n| contains | 字符串值包含子字符串值 | Field validate:\"contains=@\" |\n| containsany | 字符串值包含子字符串值中的任何字符 | Field validate:\"containsany=abc\" |\n| containsrune | 字符串值包含提供的特殊符号值 | Field validate:\"containsrune=☢\" |\n| excludes | 字符串值不包含子字符串值 | Field validate:\"excludes=@\" |\n| excludesall | 字符串值不包含任何子字符串值 | Field validate:\"excludesall=abc\" |\n| excludesrune | 字符串值不包含提供的特殊符号值 | Field validate:\"containsrune=☢\" |\n| startswith | 字符串以提供的字符串值开始 | Field validate:\"startswith=abc\" |\n| endswith | 字符串以提供的字符串值结束 | Field validate:\"endswith=abc\" |\n| ip | 字符串值是否包含有效的 | IP 地址\tField validate:\"ip\" |\n| ipv4 | 字符串值是否包含有效的 | ipv4地址\tField validate:\"ipv4\" |\n| datetime | 字符串值是否包含有效的 | 日期\tField validate:\"datetime\" |\n\n\n### 常用验证Tag\n```golang\ntype Test struct {\n\tID int `binding:\"required\"` //数字确保不为0\n\tName string `binding:\"required,min=1,max=8\"` //字符串确保不为\"\",且长度 >=1 && <=8 (min=1,max=8等于gt=0,lt=9)\n\tValue string `binding:\"required,gte=1,lte=8\"` //字符串确保不为\"\",且长度 >=1 && <=8\n\tStatus int `binding:\"min=1,max=10\"` //最小为0,最大为10(min=0,max=10等于gt=0,lt=11)\n\tPhoneNumber string `binding:\"required,len=11\"` //不为\"\"且长度为11\n\tTime string `binding:\"datetime=2006-01-02\"` //必须如2006-01-02的datetime格式\n\tColor string `binding:\"oneof=red green\"` //是能是red或者green\n\tSize int `binding:\"oneof=37 39 41\"` //是能是37或者39或者41\n\tEmail string `binding:\"email\"` //必须邮件格式\n\tJSON string `binding:\"json\"` //必须json格式\n\tURL string `binding:\"url\"` //必须url格式\n\tUUID string `binding:\"uuid\"` //必须uuid格式\n}\n```\n\n### 自定义验证器1\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/gin-gonic/gin/binding\"\n)\n\n// Booking contains binded and validated data.\ntype Booking struct {\n\tCheckIn time.Time `form:\"check_in\" binding:\"required,bookabledate\" time_format:\"2006-01-02\"`\n\tCheckOut time.Time `form:\"check_out\" binding:\"required,gtfield=CheckIn\" time_format:\"2006-01-02\"`\n}\n\n//validator v9以上写法\nfunc bookableDate(fl validator.FieldLevel) bool {\n\tif date, ok := fl.Field().Interface().(time.Time); ok {\n\t\ttoday := time.Now()\n\t\tfmt.Println(\"date:\", date)\n\t\tif date.Unix() > today.Unix() {\n\t\t\tfmt.Println(\"date unix :\", date.Unix())\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n//validator v8写法\n//func bookableDate(\n// v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,\n// field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,\n//) bool {\n// if date, ok := field.Interface().(time.Time); ok {\n// today := time.Now()\n// if date.Unix()>today.Unix(){\n// return true\n// }\n// }\n// return false\n//}\n\nfunc main() {\n\troute := gin.Default()\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\t\t_ = v.RegisterValidation(\"bookabledate\", bookableDate)\n\t}\n\troute.GET(\"/bookable\", getBookable)\n\troute.Run()\n}\n\nfunc getBookable(c *gin.Context) {\n\tvar b Booking\n\tif err := c.ShouldBindWith(&b, binding.Query); err != nil {\n\t\tc.JSON(http.StatusBadRequest, gin.H{\"error\": err.Error()})\n\t} else {\n\t\tc.JSON(http.StatusOK, gin.H{\"message\": \"ok\", \"booking\": b})\n\t}\n}\n\n```\n\n### 验证器自定义错误信息1\n原有的错误信息如下:\n```json\n{\n \"message\": \"Key: 'LoginRequest.Mobile' Error:Field validation for 'Mobile' failed on the 'required' tag\\nKey: 'LoginRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag\"\n}\n```\n这样的提示信息不是很友好, 在 validator 文档中也说明了这个信息只是用在开发时进行调试用的. 那么我们怎么返回自定义的验证提示呢. 参考 validator 文档, 我是这样来实现的.\n\n```golang\nimport (\n \"gopkg.in/go-playground/validator.v8\"\n)\n\n// 绑定模型\ntype LoginRequest struct {\n Mobile string `form:\"mobile\" json:\"mobile\" binding:\"required\"`\n Code string `form:\"code\" json:\"code\" binding:\"required\"`\n}\n\n// 绑定模型获取验证错误的方法\nfunc (r *LoginRequest) GetError (err validator.ValidationErrors) string {\n\n // 这里的 \"LoginRequest.Mobile\" 索引对应的是模型的名称和字段\n if val, exist := err[\"LoginRequest.Mobile\"]; exist {\n if val.Field == \"Mobile\" {\n switch val.Tag{\n case \"required\":\n return \"请输入手机号码\"\n }\n }\n }\n if val, exist := err[\"LoginRequest.Code\"]; exist {\n if val.Field == \"Code\" {\n switch val.Tag{\n case \"required\":\n return \"请输入验证码\"\n }\n }\n }\n return \"参数错误\"\n}\n```\n如何使用模型, 以登录方法为例\n```golang\nimport (\n \"github.com/gin-gonic/gin\"\n \"net/http\"\n \"gopkg.in/go-playground/validator.v8\"\n)\n\n\nfunc Login(c *gin.Context) {\n var loginRequest LoginRequest\n\n if err := c.ShouldBind(&loginRequest); err == nil { \n // 参数接收正确, 进行登录操作\n\n c.JSON(http.StatusOK, loginRequest)\n }else{\n // 验证错误\n c.JSON(http.StatusUnprocessableEntity, gin.H{\n \"message\": loginRequest.GetError(err.(validator.ValidationErrors)), // 注意这里要将 err 进行转换\n })\n }\n}\n```\n\n### 验证器自定义错误信息2\n目录结构\n```\n.\n├── main.go\n└── valid\n └── valid.go\n\n```\nvalid.valid.go\n```golang\npackage valid\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tzh_translations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\nvar v *validator.Validate\nvar Trans ut.Translator\n\n// 初始化翻译器\nfunc InitTrans() (err error) {\n\t//修改gin框架中的Validator属性,实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t//v.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t//\treturn fld.Tag.Get(\"comment\") //field.Tag.Get(\"json\")\n\t\t//})\n\n\t\tv.RegisterValidation(\"checkMobile\", checkMobile)\n\n\t\tzhT := zh.New() //中文翻译器\n\n\t\t// 第一个参数是备用(fallback)的语言环境\n\t\t// 后面的参数是应该支持的语言环境(支持多个)\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(zhT, zhT)\n\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\tTrans, ok = uni.GetTranslator(\"zh\")\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", \"zh\")\n\t\t}\n\t\tzh_translations.RegisterDefaultTranslations(v, Trans)\n\n\t\t// 添加额外翻译\n\t\t_ = v.RegisterTranslation(\"checkMobile\", Trans, func(ut ut.Translator) error {\n\t\t\treturn ut.Add(\"checkMobile\", \"{0} 电话错误!\", true)\n\t\t}, func(ut ut.Translator, fe validator.FieldError) string {\n\t\t\tt, _ := ut.T(\"checkMobile\", fe.Field())\n\t\t\treturn t\n\t\t})\n\n\t}\n\treturn\n}\nfunc checkMobile(fl validator.FieldLevel) bool {\n\tok, _ := regexp.MatchString(`^1[3-9][0-9]{9}$`, fl.Field().String())\n\treturn ok\n}\n\nfunc ParseErr(errs validator.ValidationErrors) string {\n\tvar errList []string\n\tfor _, e := range errs {\n\t\t// can translate each error one at a time.\n\t\terrList = append(errList, e.Translate(Trans))\n\t\t//errList = append(errList, e.Field())\n\t}\n\treturn strings.Join(errList, \"|\")\n\n}\n\n```\nmain.go\n```golang\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"my-test/gin-validator/valid\"\n)\n\nfunc main() {\n\troute := gin.Default()\n\t_ = valid.InitTrans()\n\troute.POST(\"/valid\", validHandler)\n\troute.Run()\n}\n\ntype req struct {\n\tName string `json:\"name\" binding:\"required\"`\n\tMobile string `json:\"mobile\" binding:\"checkMobile\"`\n\tAge int `json:\"mobile\" binding:\"gt=12,lt=100\" comment:\"年龄大于12,小于100\"`\n}\n\nfunc validHandler(c *gin.Context) {\n\treq := req{}\n\terr := c.ShouldBindJSON(&req)\n\tif err != nil {\n\t\terrmsg := err.Error()\n\t\tif e, ok := err.(validator.ValidationErrors); ok {\n\t\t\terrmsg = valid.ParseErr(e)\n\t\t}\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"error\": errmsg,\n\t\t})\n\t\treturn\n\t}\n\tc.JSON(http.StatusOK, \"Success\")\n}\n\n```\n\n### 参考文档\n- [gin - validator 参数校验](https://segmentfault.com/a/1190000022527284)\n- [gin框架自定义验证错误提示信息](https://www.jianshu.com/p/0183d959edf1)\n- [Golang使用validator进行数据校验及自定义翻译器](https://wangyangyangisme.github.io/2020/10/20/golang-Golang%E4%BD%BF%E7%94%A8validator%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C%E5%8F%8A%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BF%BB%E8%AF%91%E5%99%A8/#1-%E5%AE%9A%E4%B9%89%E9%94%99%E8%AF%AF%E7%BF%BB%E8%AF%91%E5%99%A8)\n- [validator Go doc](https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags)","source":"_posts/golang/gin 验证器.md","raw":"---\ntitle: gin 验证器\ntags:\n - golang\n - gin\ncategories:\n - golang\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n\n\n[toc]\n\n### 前言\ngin的验证器底层使用的是 validator 库实现的,故gin验证器可以使用validator库中的所有功能\n- 所有标签\n- 翻译器\n- 自定义校验器\n- 其他(不一一列举)\n\n不同的是gin使用的Tag 为 binding,而validator库使用的是 validate;\ngin中在初始化validate时通过validate.SetTagName(\"binding\")设置标签\n\n```golang\nfunc (v *defaultValidator) lazyinit() {\n\tv.once.Do(func() {\n\t\tv.validate = validator.New()\n\t\tv.validate.SetTagName(\"binding\")\n\t})\n}\n```\n\n\n本文中 gin 版本为 v1.6.3, validator版本为 v10\n* [gin](https://github.com/gin-gonic/gin)\n* [validator](https://github.com/go-playground/validator)\n\n### validator 库字段\n**gin 使用时把 validate tag 改为 binding 即可**\n\n| 字段 | 描述 | 例子 |\n| -------------------- | ------------------------------------------------------------ | -------------------------------------------------------- |\n| required | 必填 | Field或Struct validate:\"required\" |\n| omitempty | 空时忽略 | Field或Struct validate:\"omitempty\" |\n| len | 长度 | Field validate:\"len=0\" |\n| eq | 等于 | Field validate:\"eq=0\" |\n| gt | 大于 | Field validate:\"gt=0\" |\n| gte | 大于等于 | Field validate:\"gte=0\" |\n| lt | 小于 | Field validate:\"lt=0\" |\n| lte | 小于等于 | Field validate:\"lte=0\" |\n| eqfield | 同一结构体字段相等 | Field validate:\"eqfield=Field2\" |\n| nefield | 同一结构体字段不相等 | Field validate:\"nefield=Field2\" |\n| gtfield | 大于同一结构体字段 | Field validate:\"gtfield=Field2\" |\n| gtefield | 大于等于同一结构体字段 | Field validate:\"gtefield=Field2\" |\n| ltfield | 小于同一结构体字段 | Field validate:\"ltfield=Field2\" |\n| ltefield | 小于等于同一结构体字段 | Field validate:\"ltefield=Field2\" |\n| eqcsfield | 跨不同结构体字段相等 | Struct1.Field validate:\"eqcsfield=Struct2.Field2\" |\n| necsfield | 跨不同结构体字段不相等 | Struct1.Field validate:\"necsfield=Struct2.Field2\" |\n| gtcsfield | 大于跨不同结构体字段 | Struct1.Field validate:\"gtcsfield=Struct2.Field2\" |\n| gtecsfield | 大于等于跨不同结构体字段 | Struct1.Field validate:\"gtecsfield=Struct2.Field2\" |\n| ltcsfield | 小于跨不同结构体字段 | Struct1.Field validate:\"ltcsfield=Struct2.Field2\" |\n| ltecsfield | 小于等于跨不同结构体字段 | Struct1.Field validate:\"ltecsfield=Struct2.Field2\" |\n| min | 最大值 | Field validate:\"min=1\" |\n| max | 最小值 | Field validate:\"max=2\" |\n| structonly | 仅验证结构体,不验证任何结构体字段 | Struct validate:\"structonly\" |\n| nostructlevel | 不运行任何结构级别的验证 | Struct validate:\"nostructlevel\" |\n| dive | 向下延伸验证,多层向下需要多个dive标记 | [][]string validate:\"gt=0,dive,len=1,dive,required\" |\n| dive | Keys& EndKeys\t与dive同时使用,用于对map对象的键的和值的验证,keys为键,endkeys为值 | map[string]string validate :\"gt=0,dive,keys,eq=1 |\n| required_with | 其他字段其中一个不为空且当前字段不为空 | Field validate:\"required_with=Field1 Field2\" |\n| required_with_all | 其他所有字段不为空且当前字段不为空 | Field validate:\"required_with_all=Field1 Field2\" |\n| required_without | 其他字段其中一个为空且当前字段不为空 | Field `validate:”required_without=Field1 Field2” |\n| required_without_all | 其他所有字段为空且当前字段不为空 | Field validate:\"required_without_all=Field1 Field2\" |\n| isdefault | 是默认值 | Field validate:\"isdefault=0\" |\n| oneof | 其中之一 | Field validate:\"oneof=5 7 9\" |\n| containsfield | 字段包含另一个字段 | Field validate:\"containsfield=Field2\" |\n| excludesfield | 字段不包含另一个字段 | Field validate:\"excludesfield=Field2\" |\n| unique | 是否唯一,通常用于切片或结构体 | Field validate:\"unique\" |\n| alphanum | 字符串值是否只包含 | ASCII 字母数字字符\tField validate:\"alphanum\" |\n| alphaunicode | 字符串值是否只包含 | unicode 字符\tField validate:\"alphaunicode\" |\n| alphanumunicode | 字符串值是否只包含 | unicode 字母数字字符\tField validate:\"alphanumunicode\" |\n| numeric | 字符串值是否包含基本的数值 | Field validate:\"numeric\" |\n| hexadecimal | 字符串值是否包含有效的十六进制 | Field validate:\"hexadecimal\" |\n| hexcolor | 字符串值是否包含有效的十六进制颜色 | Field validate:\"hexcolor\" |\n| lowercase | 符串值是否只包含小写字符 | Field validate:\"lowercase\" |\n| uppercase | 符串值是否只包含大写字符 | Field validate:\"uppercase\" |\n| email | 字符串值包含一个有效的电子邮件 | Field validate:\"email\" |\n| json | 字符串值是否为有效的 | JSON\tField validate:\"json\" |\n| file | 符串值是否包含有效的文件路径,以及该文件是否存在于计算机上 | Field validate:\"file\" |\n| url | 符串值是否包含有效的 | url\tField validate:\"url\" |\n| uri | 符串值是否包含有效的 | uri\tField validate:\"uri\" |\n| base64 | 字符串值是否包含有效的 | base64值\tField validate:\"base64\" |\n| contains | 字符串值包含子字符串值 | Field validate:\"contains=@\" |\n| containsany | 字符串值包含子字符串值中的任何字符 | Field validate:\"containsany=abc\" |\n| containsrune | 字符串值包含提供的特殊符号值 | Field validate:\"containsrune=☢\" |\n| excludes | 字符串值不包含子字符串值 | Field validate:\"excludes=@\" |\n| excludesall | 字符串值不包含任何子字符串值 | Field validate:\"excludesall=abc\" |\n| excludesrune | 字符串值不包含提供的特殊符号值 | Field validate:\"containsrune=☢\" |\n| startswith | 字符串以提供的字符串值开始 | Field validate:\"startswith=abc\" |\n| endswith | 字符串以提供的字符串值结束 | Field validate:\"endswith=abc\" |\n| ip | 字符串值是否包含有效的 | IP 地址\tField validate:\"ip\" |\n| ipv4 | 字符串值是否包含有效的 | ipv4地址\tField validate:\"ipv4\" |\n| datetime | 字符串值是否包含有效的 | 日期\tField validate:\"datetime\" |\n\n\n### 常用验证Tag\n```golang\ntype Test struct {\n\tID int `binding:\"required\"` //数字确保不为0\n\tName string `binding:\"required,min=1,max=8\"` //字符串确保不为\"\",且长度 >=1 && <=8 (min=1,max=8等于gt=0,lt=9)\n\tValue string `binding:\"required,gte=1,lte=8\"` //字符串确保不为\"\",且长度 >=1 && <=8\n\tStatus int `binding:\"min=1,max=10\"` //最小为0,最大为10(min=0,max=10等于gt=0,lt=11)\n\tPhoneNumber string `binding:\"required,len=11\"` //不为\"\"且长度为11\n\tTime string `binding:\"datetime=2006-01-02\"` //必须如2006-01-02的datetime格式\n\tColor string `binding:\"oneof=red green\"` //是能是red或者green\n\tSize int `binding:\"oneof=37 39 41\"` //是能是37或者39或者41\n\tEmail string `binding:\"email\"` //必须邮件格式\n\tJSON string `binding:\"json\"` //必须json格式\n\tURL string `binding:\"url\"` //必须url格式\n\tUUID string `binding:\"uuid\"` //必须uuid格式\n}\n```\n\n### 自定义验证器1\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/gin-gonic/gin/binding\"\n)\n\n// Booking contains binded and validated data.\ntype Booking struct {\n\tCheckIn time.Time `form:\"check_in\" binding:\"required,bookabledate\" time_format:\"2006-01-02\"`\n\tCheckOut time.Time `form:\"check_out\" binding:\"required,gtfield=CheckIn\" time_format:\"2006-01-02\"`\n}\n\n//validator v9以上写法\nfunc bookableDate(fl validator.FieldLevel) bool {\n\tif date, ok := fl.Field().Interface().(time.Time); ok {\n\t\ttoday := time.Now()\n\t\tfmt.Println(\"date:\", date)\n\t\tif date.Unix() > today.Unix() {\n\t\t\tfmt.Println(\"date unix :\", date.Unix())\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n//validator v8写法\n//func bookableDate(\n// v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,\n// field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,\n//) bool {\n// if date, ok := field.Interface().(time.Time); ok {\n// today := time.Now()\n// if date.Unix()>today.Unix(){\n// return true\n// }\n// }\n// return false\n//}\n\nfunc main() {\n\troute := gin.Default()\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\t\t_ = v.RegisterValidation(\"bookabledate\", bookableDate)\n\t}\n\troute.GET(\"/bookable\", getBookable)\n\troute.Run()\n}\n\nfunc getBookable(c *gin.Context) {\n\tvar b Booking\n\tif err := c.ShouldBindWith(&b, binding.Query); err != nil {\n\t\tc.JSON(http.StatusBadRequest, gin.H{\"error\": err.Error()})\n\t} else {\n\t\tc.JSON(http.StatusOK, gin.H{\"message\": \"ok\", \"booking\": b})\n\t}\n}\n\n```\n\n### 验证器自定义错误信息1\n原有的错误信息如下:\n```json\n{\n \"message\": \"Key: 'LoginRequest.Mobile' Error:Field validation for 'Mobile' failed on the 'required' tag\\nKey: 'LoginRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag\"\n}\n```\n这样的提示信息不是很友好, 在 validator 文档中也说明了这个信息只是用在开发时进行调试用的. 那么我们怎么返回自定义的验证提示呢. 参考 validator 文档, 我是这样来实现的.\n\n```golang\nimport (\n \"gopkg.in/go-playground/validator.v8\"\n)\n\n// 绑定模型\ntype LoginRequest struct {\n Mobile string `form:\"mobile\" json:\"mobile\" binding:\"required\"`\n Code string `form:\"code\" json:\"code\" binding:\"required\"`\n}\n\n// 绑定模型获取验证错误的方法\nfunc (r *LoginRequest) GetError (err validator.ValidationErrors) string {\n\n // 这里的 \"LoginRequest.Mobile\" 索引对应的是模型的名称和字段\n if val, exist := err[\"LoginRequest.Mobile\"]; exist {\n if val.Field == \"Mobile\" {\n switch val.Tag{\n case \"required\":\n return \"请输入手机号码\"\n }\n }\n }\n if val, exist := err[\"LoginRequest.Code\"]; exist {\n if val.Field == \"Code\" {\n switch val.Tag{\n case \"required\":\n return \"请输入验证码\"\n }\n }\n }\n return \"参数错误\"\n}\n```\n如何使用模型, 以登录方法为例\n```golang\nimport (\n \"github.com/gin-gonic/gin\"\n \"net/http\"\n \"gopkg.in/go-playground/validator.v8\"\n)\n\n\nfunc Login(c *gin.Context) {\n var loginRequest LoginRequest\n\n if err := c.ShouldBind(&loginRequest); err == nil { \n // 参数接收正确, 进行登录操作\n\n c.JSON(http.StatusOK, loginRequest)\n }else{\n // 验证错误\n c.JSON(http.StatusUnprocessableEntity, gin.H{\n \"message\": loginRequest.GetError(err.(validator.ValidationErrors)), // 注意这里要将 err 进行转换\n })\n }\n}\n```\n\n### 验证器自定义错误信息2\n目录结构\n```\n.\n├── main.go\n└── valid\n └── valid.go\n\n```\nvalid.valid.go\n```golang\npackage valid\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tzh_translations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\nvar v *validator.Validate\nvar Trans ut.Translator\n\n// 初始化翻译器\nfunc InitTrans() (err error) {\n\t//修改gin框架中的Validator属性,实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t//v.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t//\treturn fld.Tag.Get(\"comment\") //field.Tag.Get(\"json\")\n\t\t//})\n\n\t\tv.RegisterValidation(\"checkMobile\", checkMobile)\n\n\t\tzhT := zh.New() //中文翻译器\n\n\t\t// 第一个参数是备用(fallback)的语言环境\n\t\t// 后面的参数是应该支持的语言环境(支持多个)\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(zhT, zhT)\n\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\tTrans, ok = uni.GetTranslator(\"zh\")\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", \"zh\")\n\t\t}\n\t\tzh_translations.RegisterDefaultTranslations(v, Trans)\n\n\t\t// 添加额外翻译\n\t\t_ = v.RegisterTranslation(\"checkMobile\", Trans, func(ut ut.Translator) error {\n\t\t\treturn ut.Add(\"checkMobile\", \"{0} 电话错误!\", true)\n\t\t}, func(ut ut.Translator, fe validator.FieldError) string {\n\t\t\tt, _ := ut.T(\"checkMobile\", fe.Field())\n\t\t\treturn t\n\t\t})\n\n\t}\n\treturn\n}\nfunc checkMobile(fl validator.FieldLevel) bool {\n\tok, _ := regexp.MatchString(`^1[3-9][0-9]{9}$`, fl.Field().String())\n\treturn ok\n}\n\nfunc ParseErr(errs validator.ValidationErrors) string {\n\tvar errList []string\n\tfor _, e := range errs {\n\t\t// can translate each error one at a time.\n\t\terrList = append(errList, e.Translate(Trans))\n\t\t//errList = append(errList, e.Field())\n\t}\n\treturn strings.Join(errList, \"|\")\n\n}\n\n```\nmain.go\n```golang\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"my-test/gin-validator/valid\"\n)\n\nfunc main() {\n\troute := gin.Default()\n\t_ = valid.InitTrans()\n\troute.POST(\"/valid\", validHandler)\n\troute.Run()\n}\n\ntype req struct {\n\tName string `json:\"name\" binding:\"required\"`\n\tMobile string `json:\"mobile\" binding:\"checkMobile\"`\n\tAge int `json:\"mobile\" binding:\"gt=12,lt=100\" comment:\"年龄大于12,小于100\"`\n}\n\nfunc validHandler(c *gin.Context) {\n\treq := req{}\n\terr := c.ShouldBindJSON(&req)\n\tif err != nil {\n\t\terrmsg := err.Error()\n\t\tif e, ok := err.(validator.ValidationErrors); ok {\n\t\t\terrmsg = valid.ParseErr(e)\n\t\t}\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"error\": errmsg,\n\t\t})\n\t\treturn\n\t}\n\tc.JSON(http.StatusOK, \"Success\")\n}\n\n```\n\n### 参考文档\n- [gin - validator 参数校验](https://segmentfault.com/a/1190000022527284)\n- [gin框架自定义验证错误提示信息](https://www.jianshu.com/p/0183d959edf1)\n- [Golang使用validator进行数据校验及自定义翻译器](https://wangyangyangisme.github.io/2020/10/20/golang-Golang%E4%BD%BF%E7%94%A8validator%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C%E5%8F%8A%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BF%BB%E8%AF%91%E5%99%A8/#1-%E5%AE%9A%E4%B9%89%E9%94%99%E8%AF%AF%E7%BF%BB%E8%AF%91%E5%99%A8)\n- [validator Go doc](https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags)","slug":"golang/gin 验证器","published":1,"updated":"2022-11-01T03:05:14.547Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpmu0006iysyho6jg8yw","content":"<p>[toc]</p>\n<h3 id=\"前言\"><a href=\"#前言\" class=\"headerlink\" title=\"前言\"></a>前言</h3><p>gin的验证器底层使用的是 validator 库实现的,故gin验证器可以使用validator库中的所有功能</p>\n<ul>\n<li>所有标签</li>\n<li>翻译器</li>\n<li>自定义校验器</li>\n<li>其他(不一一列举)</li>\n</ul>\n<p>不同的是gin使用的Tag 为 binding,而validator库使用的是 validate;<br>gin中在初始化validate时通过validate.SetTagName(“binding”)设置标签</p>\n<pre><code class=\"golang\">func (v *defaultValidator) lazyinit() {\n v.once.Do(func() {\n v.validate = validator.New()\n v.validate.SetTagName("binding")\n })\n}\n</code></pre>\n<p>本文中 gin 版本为 v1.6.3, validator版本为 v10</p>\n<ul>\n<li><a href=\"https://github.com/gin-gonic/gin\">gin</a></li>\n<li><a href=\"https://github.com/go-playground/validator\">validator</a></li>\n</ul>\n<h3 id=\"validator-库字段\"><a href=\"#validator-库字段\" class=\"headerlink\" title=\"validator 库字段\"></a>validator 库字段</h3><p><strong>gin 使用时把 validate tag 改为 binding 即可</strong></p>\n<table>\n<thead>\n<tr>\n<th>字段</th>\n<th>描述</th>\n<th>例子</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>required</td>\n<td>必填</td>\n<td>Field或Struct validate:”required”</td>\n</tr>\n<tr>\n<td>omitempty</td>\n<td>空时忽略</td>\n<td>Field或Struct validate:”omitempty”</td>\n</tr>\n<tr>\n<td>len</td>\n<td>长度</td>\n<td>Field validate:”len=0”</td>\n</tr>\n<tr>\n<td>eq</td>\n<td>等于</td>\n<td>Field validate:”eq=0”</td>\n</tr>\n<tr>\n<td>gt</td>\n<td>大于</td>\n<td>Field validate:”gt=0”</td>\n</tr>\n<tr>\n<td>gte</td>\n<td>大于等于</td>\n<td>Field validate:”gte=0”</td>\n</tr>\n<tr>\n<td>lt</td>\n<td>小于</td>\n<td>Field validate:”lt=0”</td>\n</tr>\n<tr>\n<td>lte</td>\n<td>小于等于</td>\n<td>Field validate:”lte=0”</td>\n</tr>\n<tr>\n<td>eqfield</td>\n<td>同一结构体字段相等</td>\n<td>Field validate:”eqfield=Field2”</td>\n</tr>\n<tr>\n<td>nefield</td>\n<td>同一结构体字段不相等</td>\n<td>Field validate:”nefield=Field2”</td>\n</tr>\n<tr>\n<td>gtfield</td>\n<td>大于同一结构体字段</td>\n<td>Field validate:”gtfield=Field2”</td>\n</tr>\n<tr>\n<td>gtefield</td>\n<td>大于等于同一结构体字段</td>\n<td>Field validate:”gtefield=Field2”</td>\n</tr>\n<tr>\n<td>ltfield</td>\n<td>小于同一结构体字段</td>\n<td>Field validate:”ltfield=Field2”</td>\n</tr>\n<tr>\n<td>ltefield</td>\n<td>小于等于同一结构体字段</td>\n<td>Field validate:”ltefield=Field2”</td>\n</tr>\n<tr>\n<td>eqcsfield</td>\n<td>跨不同结构体字段相等</td>\n<td>Struct1.Field validate:”eqcsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>necsfield</td>\n<td>跨不同结构体字段不相等</td>\n<td>Struct1.Field validate:”necsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>gtcsfield</td>\n<td>大于跨不同结构体字段</td>\n<td>Struct1.Field validate:”gtcsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>gtecsfield</td>\n<td>大于等于跨不同结构体字段</td>\n<td>Struct1.Field validate:”gtecsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>ltcsfield</td>\n<td>小于跨不同结构体字段</td>\n<td>Struct1.Field validate:”ltcsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>ltecsfield</td>\n<td>小于等于跨不同结构体字段</td>\n<td>Struct1.Field validate:”ltecsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>min</td>\n<td>最大值</td>\n<td>Field validate:”min=1”</td>\n</tr>\n<tr>\n<td>max</td>\n<td>最小值</td>\n<td>Field validate:”max=2”</td>\n</tr>\n<tr>\n<td>structonly</td>\n<td>仅验证结构体,不验证任何结构体字段</td>\n<td>Struct validate:”structonly”</td>\n</tr>\n<tr>\n<td>nostructlevel</td>\n<td>不运行任何结构级别的验证</td>\n<td>Struct validate:”nostructlevel”</td>\n</tr>\n<tr>\n<td>dive</td>\n<td>向下延伸验证,多层向下需要多个dive标记</td>\n<td>[][]string validate:”gt=0,dive,len=1,dive,required”</td>\n</tr>\n<tr>\n<td>dive</td>\n<td>Keys& EndKeys 与dive同时使用,用于对map对象的键的和值的验证,keys为键,endkeys为值</td>\n<td>map[string]string validate :”gt=0,dive,keys,eq=1</td>\n</tr>\n<tr>\n<td>required_with</td>\n<td>其他字段其中一个不为空且当前字段不为空</td>\n<td>Field validate:”required_with=Field1 Field2”</td>\n</tr>\n<tr>\n<td>required_with_all</td>\n<td>其他所有字段不为空且当前字段不为空</td>\n<td>Field validate:”required_with_all=Field1 Field2”</td>\n</tr>\n<tr>\n<td>required_without</td>\n<td>其他字段其中一个为空且当前字段不为空</td>\n<td>Field `validate:”required_without=Field1 Field2”</td>\n</tr>\n<tr>\n<td>required_without_all</td>\n<td>其他所有字段为空且当前字段不为空</td>\n<td>Field validate:”required_without_all=Field1 Field2”</td>\n</tr>\n<tr>\n<td>isdefault</td>\n<td>是默认值</td>\n<td>Field validate:”isdefault=0”</td>\n</tr>\n<tr>\n<td>oneof</td>\n<td>其中之一</td>\n<td>Field validate:”oneof=5 7 9”</td>\n</tr>\n<tr>\n<td>containsfield</td>\n<td>字段包含另一个字段</td>\n<td>Field validate:”containsfield=Field2”</td>\n</tr>\n<tr>\n<td>excludesfield</td>\n<td>字段不包含另一个字段</td>\n<td>Field validate:”excludesfield=Field2”</td>\n</tr>\n<tr>\n<td>unique</td>\n<td>是否唯一,通常用于切片或结构体</td>\n<td>Field validate:”unique”</td>\n</tr>\n<tr>\n<td>alphanum</td>\n<td>字符串值是否只包含</td>\n<td>ASCII 字母数字字符 Field validate:”alphanum”</td>\n</tr>\n<tr>\n<td>alphaunicode</td>\n<td>字符串值是否只包含</td>\n<td>unicode 字符 Field validate:”alphaunicode”</td>\n</tr>\n<tr>\n<td>alphanumunicode</td>\n<td>字符串值是否只包含</td>\n<td>unicode 字母数字字符 Field validate:”alphanumunicode”</td>\n</tr>\n<tr>\n<td>numeric</td>\n<td>字符串值是否包含基本的数值</td>\n<td>Field validate:”numeric”</td>\n</tr>\n<tr>\n<td>hexadecimal</td>\n<td>字符串值是否包含有效的十六进制</td>\n<td>Field validate:”hexadecimal”</td>\n</tr>\n<tr>\n<td>hexcolor</td>\n<td>字符串值是否包含有效的十六进制颜色</td>\n<td>Field validate:”hexcolor”</td>\n</tr>\n<tr>\n<td>lowercase</td>\n<td>符串值是否只包含小写字符</td>\n<td>Field validate:”lowercase”</td>\n</tr>\n<tr>\n<td>uppercase</td>\n<td>符串值是否只包含大写字符</td>\n<td>Field validate:”uppercase”</td>\n</tr>\n<tr>\n<td>email</td>\n<td>字符串值包含一个有效的电子邮件</td>\n<td>Field validate:”email”</td>\n</tr>\n<tr>\n<td>json</td>\n<td>字符串值是否为有效的</td>\n<td>JSON Field validate:”json”</td>\n</tr>\n<tr>\n<td>file</td>\n<td>符串值是否包含有效的文件路径,以及该文件是否存在于计算机上</td>\n<td>Field validate:”file”</td>\n</tr>\n<tr>\n<td>url</td>\n<td>符串值是否包含有效的</td>\n<td>url Field validate:”url”</td>\n</tr>\n<tr>\n<td>uri</td>\n<td>符串值是否包含有效的</td>\n<td>uri Field validate:”uri”</td>\n</tr>\n<tr>\n<td>base64</td>\n<td>字符串值是否包含有效的</td>\n<td>base64值 Field validate:”base64”</td>\n</tr>\n<tr>\n<td>contains</td>\n<td>字符串值包含子字符串值</td>\n<td>Field validate:”contains=@”</td>\n</tr>\n<tr>\n<td>containsany</td>\n<td>字符串值包含子字符串值中的任何字符</td>\n<td>Field validate:”containsany=abc”</td>\n</tr>\n<tr>\n<td>containsrune</td>\n<td>字符串值包含提供的特殊符号值</td>\n<td>Field validate:”containsrune=☢”</td>\n</tr>\n<tr>\n<td>excludes</td>\n<td>字符串值不包含子字符串值</td>\n<td>Field validate:”excludes=@”</td>\n</tr>\n<tr>\n<td>excludesall</td>\n<td>字符串值不包含任何子字符串值</td>\n<td>Field validate:”excludesall=abc”</td>\n</tr>\n<tr>\n<td>excludesrune</td>\n<td>字符串值不包含提供的特殊符号值</td>\n<td>Field validate:”containsrune=☢”</td>\n</tr>\n<tr>\n<td>startswith</td>\n<td>字符串以提供的字符串值开始</td>\n<td>Field validate:”startswith=abc”</td>\n</tr>\n<tr>\n<td>endswith</td>\n<td>字符串以提供的字符串值结束</td>\n<td>Field validate:”endswith=abc”</td>\n</tr>\n<tr>\n<td>ip</td>\n<td>字符串值是否包含有效的</td>\n<td>IP 地址 Field validate:”ip”</td>\n</tr>\n<tr>\n<td>ipv4</td>\n<td>字符串值是否包含有效的</td>\n<td>ipv4地址 Field validate:”ipv4”</td>\n</tr>\n<tr>\n<td>datetime</td>\n<td>字符串值是否包含有效的</td>\n<td>日期 Field validate:”datetime”</td>\n</tr>\n</tbody></table>\n<h3 id=\"常用验证Tag\"><a href=\"#常用验证Tag\" class=\"headerlink\" title=\"常用验证Tag\"></a>常用验证Tag</h3><pre><code class=\"golang\">type Test struct {\n ID int `binding:"required"` //数字确保不为0\n Name string `binding:"required,min=1,max=8"` //字符串确保不为"",且长度 >=1 && <=8 (min=1,max=8等于gt=0,lt=9)\n Value string `binding:"required,gte=1,lte=8"` //字符串确保不为"",且长度 >=1 && <=8\n Status int `binding:"min=1,max=10"` //最小为0,最大为10(min=0,max=10等于gt=0,lt=11)\n PhoneNumber string `binding:"required,len=11"` //不为""且长度为11\n Time string `binding:"datetime=2006-01-02"` //必须如2006-01-02的datetime格式\n Color string `binding:"oneof=red green"` //是能是red或者green\n Size int `binding:"oneof=37 39 41"` //是能是37或者39或者41\n Email string `binding:"email"` //必须邮件格式\n JSON string `binding:"json"` //必须json格式\n URL string `binding:"url"` //必须url格式\n UUID string `binding:"uuid"` //必须uuid格式\n}\n</code></pre>\n<h3 id=\"自定义验证器1\"><a href=\"#自定义验证器1\" class=\"headerlink\" title=\"自定义验证器1\"></a>自定义验证器1</h3><pre><code class=\"golang\">package main\n\nimport (\n "fmt"\n "net/http"\n "time"\n\n "github.com/go-playground/validator/v10"\n\n "github.com/gin-gonic/gin"\n "github.com/gin-gonic/gin/binding"\n)\n\n// Booking contains binded and validated data.\ntype Booking struct {\n CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`\n CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`\n}\n\n//validator v9以上写法\nfunc bookableDate(fl validator.FieldLevel) bool {\n if date, ok := fl.Field().Interface().(time.Time); ok {\n today := time.Now()\n fmt.Println("date:", date)\n if date.Unix() > today.Unix() {\n fmt.Println("date unix :", date.Unix())\n return true\n }\n }\n return false\n}\n\n//validator v8写法\n//func bookableDate(\n// v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,\n// field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,\n//) bool {\n// if date, ok := field.Interface().(time.Time); ok {\n// today := time.Now()\n// if date.Unix()>today.Unix(){\n// return true\n// }\n// }\n// return false\n//}\n\nfunc main() {\n route := gin.Default()\n if v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n _ = v.RegisterValidation("bookabledate", bookableDate)\n }\n route.GET("/bookable", getBookable)\n route.Run()\n}\n\nfunc getBookable(c *gin.Context) {\n var b Booking\n if err := c.ShouldBindWith(&b, binding.Query); err != nil {\n c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})\n } else {\n c.JSON(http.StatusOK, gin.H{"message": "ok", "booking": b})\n }\n}\n</code></pre>\n<h3 id=\"验证器自定义错误信息1\"><a href=\"#验证器自定义错误信息1\" class=\"headerlink\" title=\"验证器自定义错误信息1\"></a>验证器自定义错误信息1</h3><p>原有的错误信息如下:</p>\n<pre><code class=\"json\">{\n "message": "Key: 'LoginRequest.Mobile' Error:Field validation for 'Mobile' failed on the 'required' tag\\nKey: 'LoginRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag"\n}\n</code></pre>\n<p>这样的提示信息不是很友好, 在 validator 文档中也说明了这个信息只是用在开发时进行调试用的. 那么我们怎么返回自定义的验证提示呢. 参考 validator 文档, 我是这样来实现的.</p>\n<pre><code class=\"golang\">import (\n "gopkg.in/go-playground/validator.v8"\n)\n\n// 绑定模型\ntype LoginRequest struct {\n Mobile string `form:"mobile" json:"mobile" binding:"required"`\n Code string `form:"code" json:"code" binding:"required"`\n}\n\n// 绑定模型获取验证错误的方法\nfunc (r *LoginRequest) GetError (err validator.ValidationErrors) string {\n\n // 这里的 "LoginRequest.Mobile" 索引对应的是模型的名称和字段\n if val, exist := err["LoginRequest.Mobile"]; exist {\n if val.Field == "Mobile" {\n switch val.Tag{\n case "required":\n return "请输入手机号码"\n }\n }\n }\n if val, exist := err["LoginRequest.Code"]; exist {\n if val.Field == "Code" {\n switch val.Tag{\n case "required":\n return "请输入验证码"\n }\n }\n }\n return "参数错误"\n}\n</code></pre>\n<p>如何使用模型, 以登录方法为例</p>\n<pre><code class=\"golang\">import (\n "github.com/gin-gonic/gin"\n "net/http"\n "gopkg.in/go-playground/validator.v8"\n)\n\n\nfunc Login(c *gin.Context) {\n var loginRequest LoginRequest\n\n if err := c.ShouldBind(&loginRequest); err == nil { \n // 参数接收正确, 进行登录操作\n\n c.JSON(http.StatusOK, loginRequest)\n }else{\n // 验证错误\n c.JSON(http.StatusUnprocessableEntity, gin.H{\n "message": loginRequest.GetError(err.(validator.ValidationErrors)), // 注意这里要将 err 进行转换\n })\n }\n}\n</code></pre>\n<h3 id=\"验证器自定义错误信息2\"><a href=\"#验证器自定义错误信息2\" class=\"headerlink\" title=\"验证器自定义错误信息2\"></a>验证器自定义错误信息2</h3><p>目录结构</p>\n<pre><code>.\n├── main.go\n└── valid\n └── valid.go\n</code></pre>\n<p>valid.valid.go</p>\n<pre><code class=\"golang\">package valid\n\nimport (\n "fmt"\n "regexp"\n "strings"\n\n "github.com/gin-gonic/gin/binding"\n "github.com/go-playground/locales/zh"\n ut "github.com/go-playground/universal-translator"\n "github.com/go-playground/validator/v10"\n zh_translations "github.com/go-playground/validator/v10/translations/zh"\n)\n\nvar v *validator.Validate\nvar Trans ut.Translator\n\n// 初始化翻译器\nfunc InitTrans() (err error) {\n //修改gin框架中的Validator属性,实现自定制\n if v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n //v.RegisterTagNameFunc(func(fld reflect.StructField) string {\n // return fld.Tag.Get("comment") //field.Tag.Get("json")\n //})\n\n v.RegisterValidation("checkMobile", checkMobile)\n\n zhT := zh.New() //中文翻译器\n\n // 第一个参数是备用(fallback)的语言环境\n // 后面的参数是应该支持的语言环境(支持多个)\n // uni := ut.New(zhT, zhT) 也是可以的\n uni := ut.New(zhT, zhT)\n\n var ok bool\n // 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n Trans, ok = uni.GetTranslator("zh")\n if !ok {\n return fmt.Errorf("uni.GetTranslator(%s) failed", "zh")\n }\n zh_translations.RegisterDefaultTranslations(v, Trans)\n\n // 添加额外翻译\n _ = v.RegisterTranslation("checkMobile", Trans, func(ut ut.Translator) error {\n return ut.Add("checkMobile", "{0} 电话错误!", true)\n }, func(ut ut.Translator, fe validator.FieldError) string {\n t, _ := ut.T("checkMobile", fe.Field())\n return t\n })\n\n }\n return\n}\nfunc checkMobile(fl validator.FieldLevel) bool {\n ok, _ := regexp.MatchString(`^1[3-9][0-9]{9}$`, fl.Field().String())\n return ok\n}\n\nfunc ParseErr(errs validator.ValidationErrors) string {\n var errList []string\n for _, e := range errs {\n // can translate each error one at a time.\n errList = append(errList, e.Translate(Trans))\n //errList = append(errList, e.Field())\n }\n return strings.Join(errList, "|")\n\n}\n</code></pre>\n<p>main.go</p>\n<pre><code class=\"golang\">package main\n\nimport (\n "net/http"\n\n "github.com/gin-gonic/gin"\n "github.com/go-playground/validator/v10"\n\n "my-test/gin-validator/valid"\n)\n\nfunc main() {\n route := gin.Default()\n _ = valid.InitTrans()\n route.POST("/valid", validHandler)\n route.Run()\n}\n\ntype req struct {\n Name string `json:"name" binding:"required"`\n Mobile string `json:"mobile" binding:"checkMobile"`\n Age int `json:"mobile" binding:"gt=12,lt=100" comment:"年龄大于12,小于100"`\n}\n\nfunc validHandler(c *gin.Context) {\n req := req{}\n err := c.ShouldBindJSON(&req)\n if err != nil {\n errmsg := err.Error()\n if e, ok := err.(validator.ValidationErrors); ok {\n errmsg = valid.ParseErr(e)\n }\n c.JSON(http.StatusOK, gin.H{\n "error": errmsg,\n })\n return\n }\n c.JSON(http.StatusOK, "Success")\n}\n</code></pre>\n<h3 id=\"参考文档\"><a href=\"#参考文档\" class=\"headerlink\" title=\"参考文档\"></a>参考文档</h3><ul>\n<li><a href=\"https://segmentfault.com/a/1190000022527284\">gin - validator 参数校验</a></li>\n<li><a href=\"https://www.jianshu.com/p/0183d959edf1\">gin框架自定义验证错误提示信息</a></li>\n<li><a href=\"https://wangyangyangisme.github.io/2020/10/20/golang-Golang%E4%BD%BF%E7%94%A8validator%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C%E5%8F%8A%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BF%BB%E8%AF%91%E5%99%A8/#1-%E5%AE%9A%E4%B9%89%E9%94%99%E8%AF%AF%E7%BF%BB%E8%AF%91%E5%99%A8\">Golang使用validator进行数据校验及自定义翻译器</a></li>\n<li><a href=\"https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags\">validator Go doc</a></li>\n</ul>\n","site":{"data":{}},"excerpt":"","more":"<p>[toc]</p>\n<h3 id=\"前言\"><a href=\"#前言\" class=\"headerlink\" title=\"前言\"></a>前言</h3><p>gin的验证器底层使用的是 validator 库实现的,故gin验证器可以使用validator库中的所有功能</p>\n<ul>\n<li>所有标签</li>\n<li>翻译器</li>\n<li>自定义校验器</li>\n<li>其他(不一一列举)</li>\n</ul>\n<p>不同的是gin使用的Tag 为 binding,而validator库使用的是 validate;<br>gin中在初始化validate时通过validate.SetTagName(“binding”)设置标签</p>\n<pre><code class=\"golang\">func (v *defaultValidator) lazyinit() {\n v.once.Do(func() {\n v.validate = validator.New()\n v.validate.SetTagName("binding")\n })\n}\n</code></pre>\n<p>本文中 gin 版本为 v1.6.3, validator版本为 v10</p>\n<ul>\n<li><a href=\"https://github.com/gin-gonic/gin\">gin</a></li>\n<li><a href=\"https://github.com/go-playground/validator\">validator</a></li>\n</ul>\n<h3 id=\"validator-库字段\"><a href=\"#validator-库字段\" class=\"headerlink\" title=\"validator 库字段\"></a>validator 库字段</h3><p><strong>gin 使用时把 validate tag 改为 binding 即可</strong></p>\n<table>\n<thead>\n<tr>\n<th>字段</th>\n<th>描述</th>\n<th>例子</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>required</td>\n<td>必填</td>\n<td>Field或Struct validate:”required”</td>\n</tr>\n<tr>\n<td>omitempty</td>\n<td>空时忽略</td>\n<td>Field或Struct validate:”omitempty”</td>\n</tr>\n<tr>\n<td>len</td>\n<td>长度</td>\n<td>Field validate:”len=0”</td>\n</tr>\n<tr>\n<td>eq</td>\n<td>等于</td>\n<td>Field validate:”eq=0”</td>\n</tr>\n<tr>\n<td>gt</td>\n<td>大于</td>\n<td>Field validate:”gt=0”</td>\n</tr>\n<tr>\n<td>gte</td>\n<td>大于等于</td>\n<td>Field validate:”gte=0”</td>\n</tr>\n<tr>\n<td>lt</td>\n<td>小于</td>\n<td>Field validate:”lt=0”</td>\n</tr>\n<tr>\n<td>lte</td>\n<td>小于等于</td>\n<td>Field validate:”lte=0”</td>\n</tr>\n<tr>\n<td>eqfield</td>\n<td>同一结构体字段相等</td>\n<td>Field validate:”eqfield=Field2”</td>\n</tr>\n<tr>\n<td>nefield</td>\n<td>同一结构体字段不相等</td>\n<td>Field validate:”nefield=Field2”</td>\n</tr>\n<tr>\n<td>gtfield</td>\n<td>大于同一结构体字段</td>\n<td>Field validate:”gtfield=Field2”</td>\n</tr>\n<tr>\n<td>gtefield</td>\n<td>大于等于同一结构体字段</td>\n<td>Field validate:”gtefield=Field2”</td>\n</tr>\n<tr>\n<td>ltfield</td>\n<td>小于同一结构体字段</td>\n<td>Field validate:”ltfield=Field2”</td>\n</tr>\n<tr>\n<td>ltefield</td>\n<td>小于等于同一结构体字段</td>\n<td>Field validate:”ltefield=Field2”</td>\n</tr>\n<tr>\n<td>eqcsfield</td>\n<td>跨不同结构体字段相等</td>\n<td>Struct1.Field validate:”eqcsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>necsfield</td>\n<td>跨不同结构体字段不相等</td>\n<td>Struct1.Field validate:”necsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>gtcsfield</td>\n<td>大于跨不同结构体字段</td>\n<td>Struct1.Field validate:”gtcsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>gtecsfield</td>\n<td>大于等于跨不同结构体字段</td>\n<td>Struct1.Field validate:”gtecsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>ltcsfield</td>\n<td>小于跨不同结构体字段</td>\n<td>Struct1.Field validate:”ltcsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>ltecsfield</td>\n<td>小于等于跨不同结构体字段</td>\n<td>Struct1.Field validate:”ltecsfield=Struct2.Field2”</td>\n</tr>\n<tr>\n<td>min</td>\n<td>最大值</td>\n<td>Field validate:”min=1”</td>\n</tr>\n<tr>\n<td>max</td>\n<td>最小值</td>\n<td>Field validate:”max=2”</td>\n</tr>\n<tr>\n<td>structonly</td>\n<td>仅验证结构体,不验证任何结构体字段</td>\n<td>Struct validate:”structonly”</td>\n</tr>\n<tr>\n<td>nostructlevel</td>\n<td>不运行任何结构级别的验证</td>\n<td>Struct validate:”nostructlevel”</td>\n</tr>\n<tr>\n<td>dive</td>\n<td>向下延伸验证,多层向下需要多个dive标记</td>\n<td>[][]string validate:”gt=0,dive,len=1,dive,required”</td>\n</tr>\n<tr>\n<td>dive</td>\n<td>Keys& EndKeys 与dive同时使用,用于对map对象的键的和值的验证,keys为键,endkeys为值</td>\n<td>map[string]string validate :”gt=0,dive,keys,eq=1</td>\n</tr>\n<tr>\n<td>required_with</td>\n<td>其他字段其中一个不为空且当前字段不为空</td>\n<td>Field validate:”required_with=Field1 Field2”</td>\n</tr>\n<tr>\n<td>required_with_all</td>\n<td>其他所有字段不为空且当前字段不为空</td>\n<td>Field validate:”required_with_all=Field1 Field2”</td>\n</tr>\n<tr>\n<td>required_without</td>\n<td>其他字段其中一个为空且当前字段不为空</td>\n<td>Field `validate:”required_without=Field1 Field2”</td>\n</tr>\n<tr>\n<td>required_without_all</td>\n<td>其他所有字段为空且当前字段不为空</td>\n<td>Field validate:”required_without_all=Field1 Field2”</td>\n</tr>\n<tr>\n<td>isdefault</td>\n<td>是默认值</td>\n<td>Field validate:”isdefault=0”</td>\n</tr>\n<tr>\n<td>oneof</td>\n<td>其中之一</td>\n<td>Field validate:”oneof=5 7 9”</td>\n</tr>\n<tr>\n<td>containsfield</td>\n<td>字段包含另一个字段</td>\n<td>Field validate:”containsfield=Field2”</td>\n</tr>\n<tr>\n<td>excludesfield</td>\n<td>字段不包含另一个字段</td>\n<td>Field validate:”excludesfield=Field2”</td>\n</tr>\n<tr>\n<td>unique</td>\n<td>是否唯一,通常用于切片或结构体</td>\n<td>Field validate:”unique”</td>\n</tr>\n<tr>\n<td>alphanum</td>\n<td>字符串值是否只包含</td>\n<td>ASCII 字母数字字符 Field validate:”alphanum”</td>\n</tr>\n<tr>\n<td>alphaunicode</td>\n<td>字符串值是否只包含</td>\n<td>unicode 字符 Field validate:”alphaunicode”</td>\n</tr>\n<tr>\n<td>alphanumunicode</td>\n<td>字符串值是否只包含</td>\n<td>unicode 字母数字字符 Field validate:”alphanumunicode”</td>\n</tr>\n<tr>\n<td>numeric</td>\n<td>字符串值是否包含基本的数值</td>\n<td>Field validate:”numeric”</td>\n</tr>\n<tr>\n<td>hexadecimal</td>\n<td>字符串值是否包含有效的十六进制</td>\n<td>Field validate:”hexadecimal”</td>\n</tr>\n<tr>\n<td>hexcolor</td>\n<td>字符串值是否包含有效的十六进制颜色</td>\n<td>Field validate:”hexcolor”</td>\n</tr>\n<tr>\n<td>lowercase</td>\n<td>符串值是否只包含小写字符</td>\n<td>Field validate:”lowercase”</td>\n</tr>\n<tr>\n<td>uppercase</td>\n<td>符串值是否只包含大写字符</td>\n<td>Field validate:”uppercase”</td>\n</tr>\n<tr>\n<td>email</td>\n<td>字符串值包含一个有效的电子邮件</td>\n<td>Field validate:”email”</td>\n</tr>\n<tr>\n<td>json</td>\n<td>字符串值是否为有效的</td>\n<td>JSON Field validate:”json”</td>\n</tr>\n<tr>\n<td>file</td>\n<td>符串值是否包含有效的文件路径,以及该文件是否存在于计算机上</td>\n<td>Field validate:”file”</td>\n</tr>\n<tr>\n<td>url</td>\n<td>符串值是否包含有效的</td>\n<td>url Field validate:”url”</td>\n</tr>\n<tr>\n<td>uri</td>\n<td>符串值是否包含有效的</td>\n<td>uri Field validate:”uri”</td>\n</tr>\n<tr>\n<td>base64</td>\n<td>字符串值是否包含有效的</td>\n<td>base64值 Field validate:”base64”</td>\n</tr>\n<tr>\n<td>contains</td>\n<td>字符串值包含子字符串值</td>\n<td>Field validate:”contains=@”</td>\n</tr>\n<tr>\n<td>containsany</td>\n<td>字符串值包含子字符串值中的任何字符</td>\n<td>Field validate:”containsany=abc”</td>\n</tr>\n<tr>\n<td>containsrune</td>\n<td>字符串值包含提供的特殊符号值</td>\n<td>Field validate:”containsrune=☢”</td>\n</tr>\n<tr>\n<td>excludes</td>\n<td>字符串值不包含子字符串值</td>\n<td>Field validate:”excludes=@”</td>\n</tr>\n<tr>\n<td>excludesall</td>\n<td>字符串值不包含任何子字符串值</td>\n<td>Field validate:”excludesall=abc”</td>\n</tr>\n<tr>\n<td>excludesrune</td>\n<td>字符串值不包含提供的特殊符号值</td>\n<td>Field validate:”containsrune=☢”</td>\n</tr>\n<tr>\n<td>startswith</td>\n<td>字符串以提供的字符串值开始</td>\n<td>Field validate:”startswith=abc”</td>\n</tr>\n<tr>\n<td>endswith</td>\n<td>字符串以提供的字符串值结束</td>\n<td>Field validate:”endswith=abc”</td>\n</tr>\n<tr>\n<td>ip</td>\n<td>字符串值是否包含有效的</td>\n<td>IP 地址 Field validate:”ip”</td>\n</tr>\n<tr>\n<td>ipv4</td>\n<td>字符串值是否包含有效的</td>\n<td>ipv4地址 Field validate:”ipv4”</td>\n</tr>\n<tr>\n<td>datetime</td>\n<td>字符串值是否包含有效的</td>\n<td>日期 Field validate:”datetime”</td>\n</tr>\n</tbody></table>\n<h3 id=\"常用验证Tag\"><a href=\"#常用验证Tag\" class=\"headerlink\" title=\"常用验证Tag\"></a>常用验证Tag</h3><pre><code class=\"golang\">type Test struct {\n ID int `binding:"required"` //数字确保不为0\n Name string `binding:"required,min=1,max=8"` //字符串确保不为"",且长度 >=1 && <=8 (min=1,max=8等于gt=0,lt=9)\n Value string `binding:"required,gte=1,lte=8"` //字符串确保不为"",且长度 >=1 && <=8\n Status int `binding:"min=1,max=10"` //最小为0,最大为10(min=0,max=10等于gt=0,lt=11)\n PhoneNumber string `binding:"required,len=11"` //不为""且长度为11\n Time string `binding:"datetime=2006-01-02"` //必须如2006-01-02的datetime格式\n Color string `binding:"oneof=red green"` //是能是red或者green\n Size int `binding:"oneof=37 39 41"` //是能是37或者39或者41\n Email string `binding:"email"` //必须邮件格式\n JSON string `binding:"json"` //必须json格式\n URL string `binding:"url"` //必须url格式\n UUID string `binding:"uuid"` //必须uuid格式\n}\n</code></pre>\n<h3 id=\"自定义验证器1\"><a href=\"#自定义验证器1\" class=\"headerlink\" title=\"自定义验证器1\"></a>自定义验证器1</h3><pre><code class=\"golang\">package main\n\nimport (\n "fmt"\n "net/http"\n "time"\n\n "github.com/go-playground/validator/v10"\n\n "github.com/gin-gonic/gin"\n "github.com/gin-gonic/gin/binding"\n)\n\n// Booking contains binded and validated data.\ntype Booking struct {\n CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`\n CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`\n}\n\n//validator v9以上写法\nfunc bookableDate(fl validator.FieldLevel) bool {\n if date, ok := fl.Field().Interface().(time.Time); ok {\n today := time.Now()\n fmt.Println("date:", date)\n if date.Unix() > today.Unix() {\n fmt.Println("date unix :", date.Unix())\n return true\n }\n }\n return false\n}\n\n//validator v8写法\n//func bookableDate(\n// v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,\n// field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,\n//) bool {\n// if date, ok := field.Interface().(time.Time); ok {\n// today := time.Now()\n// if date.Unix()>today.Unix(){\n// return true\n// }\n// }\n// return false\n//}\n\nfunc main() {\n route := gin.Default()\n if v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n _ = v.RegisterValidation("bookabledate", bookableDate)\n }\n route.GET("/bookable", getBookable)\n route.Run()\n}\n\nfunc getBookable(c *gin.Context) {\n var b Booking\n if err := c.ShouldBindWith(&b, binding.Query); err != nil {\n c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})\n } else {\n c.JSON(http.StatusOK, gin.H{"message": "ok", "booking": b})\n }\n}\n</code></pre>\n<h3 id=\"验证器自定义错误信息1\"><a href=\"#验证器自定义错误信息1\" class=\"headerlink\" title=\"验证器自定义错误信息1\"></a>验证器自定义错误信息1</h3><p>原有的错误信息如下:</p>\n<pre><code class=\"json\">{\n "message": "Key: 'LoginRequest.Mobile' Error:Field validation for 'Mobile' failed on the 'required' tag\\nKey: 'LoginRequest.Code' Error:Field validation for 'Code' failed on the 'required' tag"\n}\n</code></pre>\n<p>这样的提示信息不是很友好, 在 validator 文档中也说明了这个信息只是用在开发时进行调试用的. 那么我们怎么返回自定义的验证提示呢. 参考 validator 文档, 我是这样来实现的.</p>\n<pre><code class=\"golang\">import (\n "gopkg.in/go-playground/validator.v8"\n)\n\n// 绑定模型\ntype LoginRequest struct {\n Mobile string `form:"mobile" json:"mobile" binding:"required"`\n Code string `form:"code" json:"code" binding:"required"`\n}\n\n// 绑定模型获取验证错误的方法\nfunc (r *LoginRequest) GetError (err validator.ValidationErrors) string {\n\n // 这里的 "LoginRequest.Mobile" 索引对应的是模型的名称和字段\n if val, exist := err["LoginRequest.Mobile"]; exist {\n if val.Field == "Mobile" {\n switch val.Tag{\n case "required":\n return "请输入手机号码"\n }\n }\n }\n if val, exist := err["LoginRequest.Code"]; exist {\n if val.Field == "Code" {\n switch val.Tag{\n case "required":\n return "请输入验证码"\n }\n }\n }\n return "参数错误"\n}\n</code></pre>\n<p>如何使用模型, 以登录方法为例</p>\n<pre><code class=\"golang\">import (\n "github.com/gin-gonic/gin"\n "net/http"\n "gopkg.in/go-playground/validator.v8"\n)\n\n\nfunc Login(c *gin.Context) {\n var loginRequest LoginRequest\n\n if err := c.ShouldBind(&loginRequest); err == nil { \n // 参数接收正确, 进行登录操作\n\n c.JSON(http.StatusOK, loginRequest)\n }else{\n // 验证错误\n c.JSON(http.StatusUnprocessableEntity, gin.H{\n "message": loginRequest.GetError(err.(validator.ValidationErrors)), // 注意这里要将 err 进行转换\n })\n }\n}\n</code></pre>\n<h3 id=\"验证器自定义错误信息2\"><a href=\"#验证器自定义错误信息2\" class=\"headerlink\" title=\"验证器自定义错误信息2\"></a>验证器自定义错误信息2</h3><p>目录结构</p>\n<pre><code>.\n├── main.go\n└── valid\n └── valid.go\n</code></pre>\n<p>valid.valid.go</p>\n<pre><code class=\"golang\">package valid\n\nimport (\n "fmt"\n "regexp"\n "strings"\n\n "github.com/gin-gonic/gin/binding"\n "github.com/go-playground/locales/zh"\n ut "github.com/go-playground/universal-translator"\n "github.com/go-playground/validator/v10"\n zh_translations "github.com/go-playground/validator/v10/translations/zh"\n)\n\nvar v *validator.Validate\nvar Trans ut.Translator\n\n// 初始化翻译器\nfunc InitTrans() (err error) {\n //修改gin框架中的Validator属性,实现自定制\n if v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n //v.RegisterTagNameFunc(func(fld reflect.StructField) string {\n // return fld.Tag.Get("comment") //field.Tag.Get("json")\n //})\n\n v.RegisterValidation("checkMobile", checkMobile)\n\n zhT := zh.New() //中文翻译器\n\n // 第一个参数是备用(fallback)的语言环境\n // 后面的参数是应该支持的语言环境(支持多个)\n // uni := ut.New(zhT, zhT) 也是可以的\n uni := ut.New(zhT, zhT)\n\n var ok bool\n // 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n Trans, ok = uni.GetTranslator("zh")\n if !ok {\n return fmt.Errorf("uni.GetTranslator(%s) failed", "zh")\n }\n zh_translations.RegisterDefaultTranslations(v, Trans)\n\n // 添加额外翻译\n _ = v.RegisterTranslation("checkMobile", Trans, func(ut ut.Translator) error {\n return ut.Add("checkMobile", "{0} 电话错误!", true)\n }, func(ut ut.Translator, fe validator.FieldError) string {\n t, _ := ut.T("checkMobile", fe.Field())\n return t\n })\n\n }\n return\n}\nfunc checkMobile(fl validator.FieldLevel) bool {\n ok, _ := regexp.MatchString(`^1[3-9][0-9]{9}$`, fl.Field().String())\n return ok\n}\n\nfunc ParseErr(errs validator.ValidationErrors) string {\n var errList []string\n for _, e := range errs {\n // can translate each error one at a time.\n errList = append(errList, e.Translate(Trans))\n //errList = append(errList, e.Field())\n }\n return strings.Join(errList, "|")\n\n}\n</code></pre>\n<p>main.go</p>\n<pre><code class=\"golang\">package main\n\nimport (\n "net/http"\n\n "github.com/gin-gonic/gin"\n "github.com/go-playground/validator/v10"\n\n "my-test/gin-validator/valid"\n)\n\nfunc main() {\n route := gin.Default()\n _ = valid.InitTrans()\n route.POST("/valid", validHandler)\n route.Run()\n}\n\ntype req struct {\n Name string `json:"name" binding:"required"`\n Mobile string `json:"mobile" binding:"checkMobile"`\n Age int `json:"mobile" binding:"gt=12,lt=100" comment:"年龄大于12,小于100"`\n}\n\nfunc validHandler(c *gin.Context) {\n req := req{}\n err := c.ShouldBindJSON(&req)\n if err != nil {\n errmsg := err.Error()\n if e, ok := err.(validator.ValidationErrors); ok {\n errmsg = valid.ParseErr(e)\n }\n c.JSON(http.StatusOK, gin.H{\n "error": errmsg,\n })\n return\n }\n c.JSON(http.StatusOK, "Success")\n}\n</code></pre>\n<h3 id=\"参考文档\"><a href=\"#参考文档\" class=\"headerlink\" title=\"参考文档\"></a>参考文档</h3><ul>\n<li><a href=\"https://segmentfault.com/a/1190000022527284\">gin - validator 参数校验</a></li>\n<li><a href=\"https://www.jianshu.com/p/0183d959edf1\">gin框架自定义验证错误提示信息</a></li>\n<li><a href=\"https://wangyangyangisme.github.io/2020/10/20/golang-Golang%E4%BD%BF%E7%94%A8validator%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C%E5%8F%8A%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BF%BB%E8%AF%91%E5%99%A8/#1-%E5%AE%9A%E4%B9%89%E9%94%99%E8%AF%AF%E7%BF%BB%E8%AF%91%E5%99%A8\">Golang使用validator进行数据校验及自定义翻译器</a></li>\n<li><a href=\"https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags\">validator Go doc</a></li>\n</ul>\n"},{"title":"golang future 实现","date":"2019-06-06T07:16:40.000Z","_content":"\n实现如下\n\n```golang\npackage utils\n\nimport (\n \"sync\"\n \"time\"\n)\n\n// Future \ntype Future struct {\n isfinished bool\n result interface{}\n resultchan chan interface{}\n l sync.Mutex\n}\n\n// GetResult get return value\nfunc (f *Future) GetResult() interface{} {\n f.l.Lock()\n defer f.l.Unlock()\n if f.isfinished {\n return f.result\n }\n\n select {\n // timeout\n case <-time.Tick(time.Second * 6):\n f.isfinished = true\n f.result = nil\n return nil\n case f.result = <-f.resultchan:\n f.isfinished = true\n return f.result\n }\n}\n\n// SetResult set return value\nfunc (f *Future) SetResult(result interface{}) {\n if f.isfinished == true {\n return\n }\n f.resultchan <- result\n close(f.resultchan)\n}\n\n// NewFuture init Future\nfunc NewFuture() *Future {\n return &Future{\n isfinished: false,\n result: nil,\n resultchan: make(chan interface{}, 1),\n }\n}\n```\n\n调用\n\n```golang\n/*\nFuture 是一个未来的任务的抽象。和python里的那个有点类似。\n在异步任务中SetResult,在GetResult的时候会等待result生成,或者超时。\n*/\n\ntasks := make([]*utils.Future, 0)\nfor i := 0; i < 10; i++ {\n future := utils.NewFuture()\n tasks = append(tasks, future)\n go func(result int) {\n time.Sleep(time.Second * time.Duration(rand.Int63n(10)))\n future.SetResult(result)\n }(i)\n}\n\nfor _, item := range tasks {\n ret, ok := item.GetResult().(int)\n if ok {\n fmt.Println(ret)\n } else {\n fmt.Println(\"failed\")\n }\n}\n```\n\n","source":"_posts/golang/golang future 实现.md","raw":"---\ntitle: golang future 实现\ndate: 2019-06-06 15:16:40\ntags: \n- golang\n- future\n\ncategories:\n- golang\n---\n\n实现如下\n\n```golang\npackage utils\n\nimport (\n \"sync\"\n \"time\"\n)\n\n// Future \ntype Future struct {\n isfinished bool\n result interface{}\n resultchan chan interface{}\n l sync.Mutex\n}\n\n// GetResult get return value\nfunc (f *Future) GetResult() interface{} {\n f.l.Lock()\n defer f.l.Unlock()\n if f.isfinished {\n return f.result\n }\n\n select {\n // timeout\n case <-time.Tick(time.Second * 6):\n f.isfinished = true\n f.result = nil\n return nil\n case f.result = <-f.resultchan:\n f.isfinished = true\n return f.result\n }\n}\n\n// SetResult set return value\nfunc (f *Future) SetResult(result interface{}) {\n if f.isfinished == true {\n return\n }\n f.resultchan <- result\n close(f.resultchan)\n}\n\n// NewFuture init Future\nfunc NewFuture() *Future {\n return &Future{\n isfinished: false,\n result: nil,\n resultchan: make(chan interface{}, 1),\n }\n}\n```\n\n调用\n\n```golang\n/*\nFuture 是一个未来的任务的抽象。和python里的那个有点类似。\n在异步任务中SetResult,在GetResult的时候会等待result生成,或者超时。\n*/\n\ntasks := make([]*utils.Future, 0)\nfor i := 0; i < 10; i++ {\n future := utils.NewFuture()\n tasks = append(tasks, future)\n go func(result int) {\n time.Sleep(time.Second * time.Duration(rand.Int63n(10)))\n future.SetResult(result)\n }(i)\n}\n\nfor _, item := range tasks {\n ret, ok := item.GetResult().(int)\n if ok {\n fmt.Println(ret)\n } else {\n fmt.Println(\"failed\")\n }\n}\n```\n\n","slug":"golang/golang future 实现","published":1,"updated":"2022-10-31T09:40:57.106Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpmv000aiysyft3b4yxi","content":"<p>实现如下</p>\n<pre><code class=\"golang\">package utils\n\nimport (\n "sync"\n "time"\n)\n\n// Future \ntype Future struct {\n isfinished bool\n result interface{}\n resultchan chan interface{}\n l sync.Mutex\n}\n\n// GetResult get return value\nfunc (f *Future) GetResult() interface{} {\n f.l.Lock()\n defer f.l.Unlock()\n if f.isfinished {\n return f.result\n }\n\n select {\n // timeout\n case <-time.Tick(time.Second * 6):\n f.isfinished = true\n f.result = nil\n return nil\n case f.result = <-f.resultchan:\n f.isfinished = true\n return f.result\n }\n}\n\n// SetResult set return value\nfunc (f *Future) SetResult(result interface{}) {\n if f.isfinished == true {\n return\n }\n f.resultchan <- result\n close(f.resultchan)\n}\n\n// NewFuture init Future\nfunc NewFuture() *Future {\n return &Future{\n isfinished: false,\n result: nil,\n resultchan: make(chan interface{}, 1),\n }\n}\n</code></pre>\n<p>调用</p>\n<pre><code class=\"golang\">/*\nFuture 是一个未来的任务的抽象。和python里的那个有点类似。\n在异步任务中SetResult,在GetResult的时候会等待result生成,或者超时。\n*/\n\ntasks := make([]*utils.Future, 0)\nfor i := 0; i < 10; i++ {\n future := utils.NewFuture()\n tasks = append(tasks, future)\n go func(result int) {\n time.Sleep(time.Second * time.Duration(rand.Int63n(10)))\n future.SetResult(result)\n }(i)\n}\n\nfor _, item := range tasks {\n ret, ok := item.GetResult().(int)\n if ok {\n fmt.Println(ret)\n } else {\n fmt.Println("failed")\n }\n}\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p>实现如下</p>\n<pre><code class=\"golang\">package utils\n\nimport (\n "sync"\n "time"\n)\n\n// Future \ntype Future struct {\n isfinished bool\n result interface{}\n resultchan chan interface{}\n l sync.Mutex\n}\n\n// GetResult get return value\nfunc (f *Future) GetResult() interface{} {\n f.l.Lock()\n defer f.l.Unlock()\n if f.isfinished {\n return f.result\n }\n\n select {\n // timeout\n case <-time.Tick(time.Second * 6):\n f.isfinished = true\n f.result = nil\n return nil\n case f.result = <-f.resultchan:\n f.isfinished = true\n return f.result\n }\n}\n\n// SetResult set return value\nfunc (f *Future) SetResult(result interface{}) {\n if f.isfinished == true {\n return\n }\n f.resultchan <- result\n close(f.resultchan)\n}\n\n// NewFuture init Future\nfunc NewFuture() *Future {\n return &Future{\n isfinished: false,\n result: nil,\n resultchan: make(chan interface{}, 1),\n }\n}\n</code></pre>\n<p>调用</p>\n<pre><code class=\"golang\">/*\nFuture 是一个未来的任务的抽象。和python里的那个有点类似。\n在异步任务中SetResult,在GetResult的时候会等待result生成,或者超时。\n*/\n\ntasks := make([]*utils.Future, 0)\nfor i := 0; i < 10; i++ {\n future := utils.NewFuture()\n tasks = append(tasks, future)\n go func(result int) {\n time.Sleep(time.Second * time.Duration(rand.Int63n(10)))\n future.SetResult(result)\n }(i)\n}\n\nfor _, item := range tasks {\n ret, ok := item.GetResult().(int)\n if ok {\n fmt.Println(ret)\n } else {\n fmt.Println("failed")\n }\n}\n</code></pre>\n"},{"title":"go-micro 使用问题记录","date":"2019-11-05T10:30:57.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n### 1. micro@v1.14 之后使用 consul 作为注册中心问题\nmicro@v1.14 之后更换了默认的注册中心,把 consul 换成了 etcd ,如果需要使用 consul 作为注册中心,\n需要重新编译 micro。\n\n1. clone 源码:\n\n```shell\ngit clone https://github.com/micro/micro.git \n```\n\n2. 切到源码目录并新增 plugins.go :\n\n```shell\ncd micro源码目录 vi plugins.go\n```\n\n```golang\npackage main\n\nimport (\n\t_ \"github.com/micro/go-plugins/registry/consul\"\n)\n```\n\n3. 编译\n\n```shell\ngo build -o mainWithConsul main.go plugins.go \n```\n\n4. 运行\n\n```shell\n ./mainWithConsul --registry=consul api \n```\n\n### 2. micro 用 gin 作为 api 问题\n用 gin 做为 gin-api 服务时, 通过 *micro new --type web* 创建一个服务出来,注意修改服务的 name,改为 *com.example.api.ServiceName*,\n且在 gin router 中创建:\n\n```golang\nrouter := gin.Default()\nr := router.Group(\"/ServiceName\")\n\n```\n```shell\nmicro api --namespace=com.example.api\n```\n\n启动 这里必须要加 api 不然转发不了\n\n### 3. 使用 micro new 生成模板\n\n使用 micro new 生成模板, RegisterSubscriber 的 topic 名称和 service name 一样,导致大概50%的概率出现请求失败问题.\n\n**解决方法:** 注释 RegisterSubscriber 部分代码,或者换一个 topic 名称。\n","source":"_posts/golang/go-micro 使用问题记录.md","raw":"---\ntitle: go-micro 使用问题记录\ndate: 2019-11-05 18:30:57\ntags:\n- golang\n- go-micro\n- 微服务\n- 注意事项\n\ncategories:\n- golang\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n### 1. micro@v1.14 之后使用 consul 作为注册中心问题\nmicro@v1.14 之后更换了默认的注册中心,把 consul 换成了 etcd ,如果需要使用 consul 作为注册中心,\n需要重新编译 micro。\n\n1. clone 源码:\n\n```shell\ngit clone https://github.com/micro/micro.git \n```\n\n2. 切到源码目录并新增 plugins.go :\n\n```shell\ncd micro源码目录 vi plugins.go\n```\n\n```golang\npackage main\n\nimport (\n\t_ \"github.com/micro/go-plugins/registry/consul\"\n)\n```\n\n3. 编译\n\n```shell\ngo build -o mainWithConsul main.go plugins.go \n```\n\n4. 运行\n\n```shell\n ./mainWithConsul --registry=consul api \n```\n\n### 2. micro 用 gin 作为 api 问题\n用 gin 做为 gin-api 服务时, 通过 *micro new --type web* 创建一个服务出来,注意修改服务的 name,改为 *com.example.api.ServiceName*,\n且在 gin router 中创建:\n\n```golang\nrouter := gin.Default()\nr := router.Group(\"/ServiceName\")\n\n```\n```shell\nmicro api --namespace=com.example.api\n```\n\n启动 这里必须要加 api 不然转发不了\n\n### 3. 使用 micro new 生成模板\n\n使用 micro new 生成模板, RegisterSubscriber 的 topic 名称和 service name 一样,导致大概50%的概率出现请求失败问题.\n\n**解决方法:** 注释 RegisterSubscriber 部分代码,或者换一个 topic 名称。\n","slug":"golang/go-micro 使用问题记录","published":1,"updated":"2022-11-01T02:42:47.276Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpmw000ciysy3tlpgv08","content":"<h3 id=\"1-109-x69-99-x72-x6f-64-118-49-x2e-49-52-之后使用-consul-作为注册中心问题\"><a href=\"#1-109-x69-99-x72-x6f-64-118-49-x2e-49-52-之后使用-consul-作为注册中心问题\" class=\"headerlink\" title=\"1. micro@v1.14 之后使用 consul 作为注册中心问题\"></a>1. <a href=\"mailto:micro@v1.14\">micro@v1.14</a> 之后使用 consul 作为注册中心问题</h3><p><a href=\"mailto:micro@v1.14\">micro@v1.14</a> 之后更换了默认的注册中心,把 consul 换成了 etcd ,如果需要使用 consul 作为注册中心,<br>需要重新编译 micro。</p>\n<ol>\n<li>clone 源码:</li>\n</ol>\n<pre><code class=\"shell\">git clone https://github.com/micro/micro.git \n</code></pre>\n<ol start=\"2\">\n<li>切到源码目录并新增 plugins.go :</li>\n</ol>\n<pre><code class=\"shell\">cd micro源码目录 vi plugins.go\n</code></pre>\n<pre><code class=\"golang\">package main\n\nimport (\n _ "github.com/micro/go-plugins/registry/consul"\n)\n</code></pre>\n<ol start=\"3\">\n<li>编译</li>\n</ol>\n<pre><code class=\"shell\">go build -o mainWithConsul main.go plugins.go \n</code></pre>\n<ol start=\"4\">\n<li>运行</li>\n</ol>\n<pre><code class=\"shell\"> ./mainWithConsul --registry=consul api \n</code></pre>\n<h3 id=\"2-micro-用-gin-作为-api-问题\"><a href=\"#2-micro-用-gin-作为-api-问题\" class=\"headerlink\" title=\"2. micro 用 gin 作为 api 问题\"></a>2. micro 用 gin 作为 api 问题</h3><p>用 gin 做为 gin-api 服务时, 通过 <em>micro new –type web</em> 创建一个服务出来,注意修改服务的 name,改为 <em>com.example.api.ServiceName</em>,<br>且在 gin router 中创建:</p>\n<pre><code class=\"golang\">router := gin.Default()\nr := router.Group("/ServiceName")\n</code></pre>\n<pre><code class=\"shell\">micro api --namespace=com.example.api\n</code></pre>\n<p>启动 这里必须要加 api 不然转发不了</p>\n<h3 id=\"3-使用-micro-new-生成模板\"><a href=\"#3-使用-micro-new-生成模板\" class=\"headerlink\" title=\"3. 使用 micro new 生成模板\"></a>3. 使用 micro new 生成模板</h3><p>使用 micro new 生成模板, RegisterSubscriber 的 topic 名称和 service name 一样,导致大概50%的概率出现请求失败问题.</p>\n<p><strong>解决方法:</strong> 注释 RegisterSubscriber 部分代码,或者换一个 topic 名称。</p>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"1-109-x69-99-x72-x6f-64-118-49-x2e-49-52-之后使用-consul-作为注册中心问题\"><a href=\"#1-109-x69-99-x72-x6f-64-118-49-x2e-49-52-之后使用-consul-作为注册中心问题\" class=\"headerlink\" title=\"1. micro@v1.14 之后使用 consul 作为注册中心问题\"></a>1. <a href=\"mailto:micro@v1.14\">micro@v1.14</a> 之后使用 consul 作为注册中心问题</h3><p><a href=\"mailto:micro@v1.14\">micro@v1.14</a> 之后更换了默认的注册中心,把 consul 换成了 etcd ,如果需要使用 consul 作为注册中心,<br>需要重新编译 micro。</p>\n<ol>\n<li>clone 源码:</li>\n</ol>\n<pre><code class=\"shell\">git clone https://github.com/micro/micro.git \n</code></pre>\n<ol start=\"2\">\n<li>切到源码目录并新增 plugins.go :</li>\n</ol>\n<pre><code class=\"shell\">cd micro源码目录 vi plugins.go\n</code></pre>\n<pre><code class=\"golang\">package main\n\nimport (\n _ "github.com/micro/go-plugins/registry/consul"\n)\n</code></pre>\n<ol start=\"3\">\n<li>编译</li>\n</ol>\n<pre><code class=\"shell\">go build -o mainWithConsul main.go plugins.go \n</code></pre>\n<ol start=\"4\">\n<li>运行</li>\n</ol>\n<pre><code class=\"shell\"> ./mainWithConsul --registry=consul api \n</code></pre>\n<h3 id=\"2-micro-用-gin-作为-api-问题\"><a href=\"#2-micro-用-gin-作为-api-问题\" class=\"headerlink\" title=\"2. micro 用 gin 作为 api 问题\"></a>2. micro 用 gin 作为 api 问题</h3><p>用 gin 做为 gin-api 服务时, 通过 <em>micro new –type web</em> 创建一个服务出来,注意修改服务的 name,改为 <em>com.example.api.ServiceName</em>,<br>且在 gin router 中创建:</p>\n<pre><code class=\"golang\">router := gin.Default()\nr := router.Group("/ServiceName")\n</code></pre>\n<pre><code class=\"shell\">micro api --namespace=com.example.api\n</code></pre>\n<p>启动 这里必须要加 api 不然转发不了</p>\n<h3 id=\"3-使用-micro-new-生成模板\"><a href=\"#3-使用-micro-new-生成模板\" class=\"headerlink\" title=\"3. 使用 micro new 生成模板\"></a>3. 使用 micro new 生成模板</h3><p>使用 micro new 生成模板, RegisterSubscriber 的 topic 名称和 service name 一样,导致大概50%的概率出现请求失败问题.</p>\n<p><strong>解决方法:</strong> 注释 RegisterSubscriber 部分代码,或者换一个 topic 名称。</p>\n"},{"title":"golang 函数调用链","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n\n\n[TOC]\n\n### 利用 defer 实现函数出入口的跟踪\n\n跟踪函数调用,我们首先想到的就是跟踪函数的出入口,而完成这一任务,当仁不让的就是利用defer\n\n```golang\nfunc trace() func() {\n pc, _, _, ok := runtime.Caller(1)\n if !ok {\n panic(\"not found caller\")\n }\n\n fn := runtime.FuncForPC(pc)\n name := fn.Name()\n\n fmt.Printf(\"enter: %s\\n\", name)\n return func() { fmt.Printf(\"exit: %s\\n\", name) } \n}\n\nfunc A1() {\n defer trace()()\n B1()\n}\n\nfunc B1() {\n defer trace()()\n C1()\n}\n\nfunc C1() {\n defer trace()()\n D()\n}\n\nfunc D() {\n defer trace()()\n}\n\nfunc main() {\n A1()\n}\n```\n\n我们看到:以 A1 实现为例,当执行流来带 defer 语句时,首先会对 defer 后面的表达式进行求值。trace 函数会执行,输出函数入口信息,并返回一个 “打印出口信息” 的匿名函数。该函数在此并不会执行,而是被注册到函数 A1 的 defer 函数栈中,待 A1 函数执行结束后才会被弹出执行。也就是在 A1 结束后,会有一条函数的出口信息被输出\n\n $go build\n $./functrace-demo\n enter: main.A1\n enter: main.B1\n enter: main.C1\n enter: main.D\n exit: main.D\n exit: main.C1\n exit: main.B1\n exit: main.A1","source":"_posts/golang/golang 函数调用链.md","raw":"---\ntitle: golang 函数调用链\ntags:\n - golang\ncategories:\n - golang\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n\n\n[TOC]\n\n### 利用 defer 实现函数出入口的跟踪\n\n跟踪函数调用,我们首先想到的就是跟踪函数的出入口,而完成这一任务,当仁不让的就是利用defer\n\n```golang\nfunc trace() func() {\n pc, _, _, ok := runtime.Caller(1)\n if !ok {\n panic(\"not found caller\")\n }\n\n fn := runtime.FuncForPC(pc)\n name := fn.Name()\n\n fmt.Printf(\"enter: %s\\n\", name)\n return func() { fmt.Printf(\"exit: %s\\n\", name) } \n}\n\nfunc A1() {\n defer trace()()\n B1()\n}\n\nfunc B1() {\n defer trace()()\n C1()\n}\n\nfunc C1() {\n defer trace()()\n D()\n}\n\nfunc D() {\n defer trace()()\n}\n\nfunc main() {\n A1()\n}\n```\n\n我们看到:以 A1 实现为例,当执行流来带 defer 语句时,首先会对 defer 后面的表达式进行求值。trace 函数会执行,输出函数入口信息,并返回一个 “打印出口信息” 的匿名函数。该函数在此并不会执行,而是被注册到函数 A1 的 defer 函数栈中,待 A1 函数执行结束后才会被弹出执行。也就是在 A1 结束后,会有一条函数的出口信息被输出\n\n $go build\n $./functrace-demo\n enter: main.A1\n enter: main.B1\n enter: main.C1\n enter: main.D\n exit: main.D\n exit: main.C1\n exit: main.B1\n exit: main.A1","slug":"golang/golang 函数调用链","published":1,"updated":"2022-11-01T03:06:02.582Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpmy000giysy64dcffye","content":"<p>[TOC]</p>\n<h3 id=\"利用-defer-实现函数出入口的跟踪\"><a href=\"#利用-defer-实现函数出入口的跟踪\" class=\"headerlink\" title=\"利用 defer 实现函数出入口的跟踪\"></a>利用 defer 实现函数出入口的跟踪</h3><p>跟踪函数调用,我们首先想到的就是跟踪函数的出入口,而完成这一任务,当仁不让的就是利用defer</p>\n<pre><code class=\"golang\">func trace() func() {\n pc, _, _, ok := runtime.Caller(1)\n if !ok {\n panic("not found caller")\n }\n\n fn := runtime.FuncForPC(pc)\n name := fn.Name()\n\n fmt.Printf("enter: %s\\n", name)\n return func() { fmt.Printf("exit: %s\\n", name) } \n}\n\nfunc A1() {\n defer trace()()\n B1()\n}\n\nfunc B1() {\n defer trace()()\n C1()\n}\n\nfunc C1() {\n defer trace()()\n D()\n}\n\nfunc D() {\n defer trace()()\n}\n\nfunc main() {\n A1()\n}\n</code></pre>\n<p>我们看到:以 A1 实现为例,当执行流来带 defer 语句时,首先会对 defer 后面的表达式进行求值。trace 函数会执行,输出函数入口信息,并返回一个 “打印出口信息” 的匿名函数。该函数在此并不会执行,而是被注册到函数 A1 的 defer 函数栈中,待 A1 函数执行结束后才会被弹出执行。也就是在 A1 结束后,会有一条函数的出口信息被输出</p>\n<pre><code>$go build\n$./functrace-demo\nenter: main.A1\nenter: main.B1\nenter: main.C1\nenter: main.D\nexit: main.D\nexit: main.C1\nexit: main.B1\nexit: main.A1\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p>[TOC]</p>\n<h3 id=\"利用-defer-实现函数出入口的跟踪\"><a href=\"#利用-defer-实现函数出入口的跟踪\" class=\"headerlink\" title=\"利用 defer 实现函数出入口的跟踪\"></a>利用 defer 实现函数出入口的跟踪</h3><p>跟踪函数调用,我们首先想到的就是跟踪函数的出入口,而完成这一任务,当仁不让的就是利用defer</p>\n<pre><code class=\"golang\">func trace() func() {\n pc, _, _, ok := runtime.Caller(1)\n if !ok {\n panic("not found caller")\n }\n\n fn := runtime.FuncForPC(pc)\n name := fn.Name()\n\n fmt.Printf("enter: %s\\n", name)\n return func() { fmt.Printf("exit: %s\\n", name) } \n}\n\nfunc A1() {\n defer trace()()\n B1()\n}\n\nfunc B1() {\n defer trace()()\n C1()\n}\n\nfunc C1() {\n defer trace()()\n D()\n}\n\nfunc D() {\n defer trace()()\n}\n\nfunc main() {\n A1()\n}\n</code></pre>\n<p>我们看到:以 A1 实现为例,当执行流来带 defer 语句时,首先会对 defer 后面的表达式进行求值。trace 函数会执行,输出函数入口信息,并返回一个 “打印出口信息” 的匿名函数。该函数在此并不会执行,而是被注册到函数 A1 的 defer 函数栈中,待 A1 函数执行结束后才会被弹出执行。也就是在 A1 结束后,会有一条函数的出口信息被输出</p>\n<pre><code>$go build\n$./functrace-demo\nenter: main.A1\nenter: main.B1\nenter: main.C1\nenter: main.D\nexit: main.D\nexit: main.C1\nexit: main.B1\nexit: main.A1\n</code></pre>\n"},{"title":"golang 启动一个简单 http 代理","date":"2020-05-08T03:30:57.000Z","_content":"\n## golang 转发 http 请求\n\n\n```golang\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n)\n\n//将request转发给 http://127.0.0.1:2003\nfunc proxyHandler(w http.ResponseWriter, r *http.Request) {\n\n\ttrueServer := \"http://127.0.0.1:15672\"\n\n\turl, err := url.Parse(trueServer)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\n\tproxy := httputil.NewSingleHostReverseProxy(url)\n\tproxy.ServeHTTP(w, r)\n\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/\", proxyHandler)\n\tlog.Fatal(http.ListenAndServe(\":2002\", nil))\n}\n\n```\n","source":"_posts/golang/golang 启动一个简单 http 代理.md","raw":"---\ntitle: golang 启动一个简单 http 代理\ndate: 2020-05-08 11:30:57\ntags: \n- golang\n- http\n\ncategories:\n- golang\n---\n\n## golang 转发 http 请求\n\n\n```golang\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n)\n\n//将request转发给 http://127.0.0.1:2003\nfunc proxyHandler(w http.ResponseWriter, r *http.Request) {\n\n\ttrueServer := \"http://127.0.0.1:15672\"\n\n\turl, err := url.Parse(trueServer)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\n\tproxy := httputil.NewSingleHostReverseProxy(url)\n\tproxy.ServeHTTP(w, r)\n\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/\", proxyHandler)\n\tlog.Fatal(http.ListenAndServe(\":2002\", nil))\n}\n\n```\n","slug":"golang/golang 启动一个简单 http 代理","published":1,"updated":"2022-10-31T09:40:57.106Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpmy000jiysyeskd11oe","content":"<h2 id=\"golang-转发-http-请求\"><a href=\"#golang-转发-http-请求\" class=\"headerlink\" title=\"golang 转发 http 请求\"></a>golang 转发 http 请求</h2><pre><code class=\"golang\">package main\n\nimport (\n "log"\n "net/http"\n "net/http/httputil"\n "net/url"\n)\n\n//将request转发给 http://127.0.0.1:2003\nfunc proxyHandler(w http.ResponseWriter, r *http.Request) {\n\n trueServer := "http://127.0.0.1:15672"\n\n url, err := url.Parse(trueServer)\n if err != nil {\n log.Println(err)\n return\n }\n\n proxy := httputil.NewSingleHostReverseProxy(url)\n proxy.ServeHTTP(w, r)\n\n}\n\nfunc main() {\n http.HandleFunc("/", proxyHandler)\n log.Fatal(http.ListenAndServe(":2002", nil))\n}\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"golang-转发-http-请求\"><a href=\"#golang-转发-http-请求\" class=\"headerlink\" title=\"golang 转发 http 请求\"></a>golang 转发 http 请求</h2><pre><code class=\"golang\">package main\n\nimport (\n "log"\n "net/http"\n "net/http/httputil"\n "net/url"\n)\n\n//将request转发给 http://127.0.0.1:2003\nfunc proxyHandler(w http.ResponseWriter, r *http.Request) {\n\n trueServer := "http://127.0.0.1:15672"\n\n url, err := url.Parse(trueServer)\n if err != nil {\n log.Println(err)\n return\n }\n\n proxy := httputil.NewSingleHostReverseProxy(url)\n proxy.ServeHTTP(w, r)\n\n}\n\nfunc main() {\n http.HandleFunc("/", proxyHandler)\n log.Fatal(http.ListenAndServe(":2002", nil))\n}\n</code></pre>\n"},{"title":"golang 正则表达式常用","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n# regex_const.go\n\n\n```golang\npackage check\n\nimport (\n\t\"regexp\"\n)s\n\nconst (\n\t// 中国大陆手机号码正则匹配, 不是那么太精细\n\t// 只要是 13,14,15,17,18,19 开头的 11 位数字就认为是中国手机号\n\tchinaMobilePattern = `^1[0-9]{10}$`\n\t// 用户昵称的正则匹配, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n\t// 字符 '_' 只能出现在中间且不能重复, 如 \"__\"\n\tnicknamePattern = `^[a-z0-9A-Z\\p{Han}]+(_[a-z0-9A-Z\\p{Han}]+)*?$`\n\t// 用户名的正则匹配, 合法的字符有 0-9, A-Z, a-z, _\n\t// 第一个字母不能为 _, 0-9\n\t// 最后一个字母不能为 _, 且 _ 不能连续\n\tusernamePattern = `^[a-zA-Z][a-z0-9A-Z]*(_[a-z0-9A-Z]+)*?$`\n\t// 电子邮箱的正则匹配, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n\t// 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n\t// 开头字母不能是 -, _, .\n\t// 结尾字母不能是 -, _, .\n\t// -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n\t// 邮箱的域名可以包含 0-9, A-Z, a-z, -\n\t// 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n\t// 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\n\tmailPattern = `^[a-z0-9A-Z]+([_\\-\\.][a-z0-9A-Z]+)*?@[a-z0-9A-Z]+([\\-\\.][a-z0-9A-Z]+)*?\\.[a-zA-Z]{2,}$`\n\n\tchineseNamePattern = \"^\\\\p{Han}+(\\u00B7\\\\p{Han}+)*?$\"\n\tchineseNameExPattern = \"^\\\\p{Han}+([\\u00B7\\u2022\\u2027\\u30FB\\u002E\\u0387\\u16EB\\u2219\\u22C5\\uFF65\\u05BC]\\\\p{Han}+)*?$\"\n)\n\nvar (\n\tchinaMobileRegexp = regexp.MustCompile(chinaMobilePattern)\n\tnicknameRegexp = regexp.MustCompile(nicknamePattern)\n\tusernameRegexp = regexp.MustCompile(usernamePattern)\n\tmailRegexp = regexp.MustCompile(mailPattern)\n\tchineseNameRegexp = regexp.MustCompile(chineseNamePattern)\n\tchineseNameExRegexp = regexp.MustCompile(chineseNameExPattern)\n)\n\n// 检验是否为合法的中国手机号, 不是那么太精细\n// 只要是 13,14,15,18 开头的 11 位数字就认为是中国手机号\nfunc IsChinaMobile(b []byte) bool {\n\tif len(b) != 11 {\n\t\treturn false\n\t}\n\treturn chinaMobileRegexp.Match(b)\n}\n\n// 同 func IsChinaMobile(b []byte) bool\nfunc IsChinaMobileString(s string) bool {\n\tif len(s) != 11 {\n\t\treturn false\n\t}\n\treturn chinaMobileRegexp.MatchString(s)\n}\n\n// 检验是否为合法的昵称, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n// 字符 '_' 只能出现在中间且不能重复, 如 \"__\"\nfunc IsNickname(b []byte) bool {\n\tif len(b) == 0 {\n\t\treturn false\n\t}\n\treturn nicknameRegexp.Match(b)\n}\n\n// 同 func IsNickname(b []byte) bool\nfunc IsNicknameString(s string) bool {\n\tif len(s) == 0 {\n\t\treturn false\n\t}\n\treturn nicknameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的用户名, 合法的字符有 0-9, A-Z, a-z, _\n// 第一个字母不能为 _, 0-9\n// 最后一个字母不能为 _, 且 _ 不能连续\nfunc IsUserName(b []byte) bool {\n\tif len(b) == 0 {\n\t\treturn false\n\t}\n\treturn usernameRegexp.Match(b)\n}\n\n// 同 func IsName(b []byte) bool\nfunc IsUserNameString(s string) bool {\n\tif len(s) == 0 {\n\t\treturn false\n\t}\n\treturn usernameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的电子邮箱, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n// 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n// 开头字母不能是 -, _, .\n// 结尾字母不能是 -, _, .\n// -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n// 邮箱的域名可以包含 0-9, A-Z, a-z, -\n// 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n// 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\nfunc IsMail(b []byte) bool {\n\tif len(b) < 6 { // x@x.xx\n\t\treturn false\n\t}\n\treturn mailRegexp.Match(b)\n}\n\n// 同 func IsMail(b []byte) bool\nfunc IsMailString(s string) bool {\n\tif len(s) < 6 { // x@x.xx\n\t\treturn false\n\t}\n\treturn mailRegexp.MatchString(s)\n}\n\n// IsChineseName 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四)\nfunc IsChineseName(b []byte) bool {\n\treturn chineseNameRegexp.Match(b)\n}\n\n// 同 IsChineseName(b []byte) bool\nfunc IsChineseNameString(s string) bool {\n\treturn chineseNameRegexp.MatchString(s)\n}\n\n// IsChineseNameEx 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四),\n// 主要功能和 IsChineseName 相同, 但是如果姓名中包含不规范的间隔符, 会自动修正为正确的间隔符 '\\u00B7', 并返回正确的结果.\nfunc IsChineseNameEx(b []byte) ([]byte, bool) {\n\tif chineseNameRegexp.Match(b) {\n\t\treturn b, true\n\t}\n\tif !chineseNameExRegexp.Match(b) {\n\t\treturn b, false\n\t}\n\tlist := []rune(string(b))\n\tfor i := 0; i < len(list); i++ {\n\t\tswitch list[i] {\n\t\tcase '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n\t\t\tlist[i] = '\\u00B7'\n\t\t}\n\t}\n\treturn []byte(string(list)), true\n}\n\n// 同 IsChineseNameEx(b []byte) ([]byte, bool)\nfunc IsChineseNameStringEx(s string) (string, bool) {\n\tif chineseNameRegexp.MatchString(s) {\n\t\treturn s, true\n\t}\n\tif !chineseNameExRegexp.MatchString(s) {\n\t\treturn s, false\n\t}\n\tlist := []rune(s)\n\tfor i := 0; i < len(list); i++ {\n\t\tswitch list[i] {\n\t\tcase '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n\t\t\tlist[i] = '\\u00B7'\n\t\t}\n\t}\n\treturn string(list), true\n}\n```","source":"_posts/golang/golang 正则表达式常用.md","raw":"---\ntitle: golang 正则表达式常用\ntags:\n - golang\n - regex\ncategories:\n - golang\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n# regex_const.go\n\n\n```golang\npackage check\n\nimport (\n\t\"regexp\"\n)s\n\nconst (\n\t// 中国大陆手机号码正则匹配, 不是那么太精细\n\t// 只要是 13,14,15,17,18,19 开头的 11 位数字就认为是中国手机号\n\tchinaMobilePattern = `^1[0-9]{10}$`\n\t// 用户昵称的正则匹配, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n\t// 字符 '_' 只能出现在中间且不能重复, 如 \"__\"\n\tnicknamePattern = `^[a-z0-9A-Z\\p{Han}]+(_[a-z0-9A-Z\\p{Han}]+)*?$`\n\t// 用户名的正则匹配, 合法的字符有 0-9, A-Z, a-z, _\n\t// 第一个字母不能为 _, 0-9\n\t// 最后一个字母不能为 _, 且 _ 不能连续\n\tusernamePattern = `^[a-zA-Z][a-z0-9A-Z]*(_[a-z0-9A-Z]+)*?$`\n\t// 电子邮箱的正则匹配, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n\t// 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n\t// 开头字母不能是 -, _, .\n\t// 结尾字母不能是 -, _, .\n\t// -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n\t// 邮箱的域名可以包含 0-9, A-Z, a-z, -\n\t// 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n\t// 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\n\tmailPattern = `^[a-z0-9A-Z]+([_\\-\\.][a-z0-9A-Z]+)*?@[a-z0-9A-Z]+([\\-\\.][a-z0-9A-Z]+)*?\\.[a-zA-Z]{2,}$`\n\n\tchineseNamePattern = \"^\\\\p{Han}+(\\u00B7\\\\p{Han}+)*?$\"\n\tchineseNameExPattern = \"^\\\\p{Han}+([\\u00B7\\u2022\\u2027\\u30FB\\u002E\\u0387\\u16EB\\u2219\\u22C5\\uFF65\\u05BC]\\\\p{Han}+)*?$\"\n)\n\nvar (\n\tchinaMobileRegexp = regexp.MustCompile(chinaMobilePattern)\n\tnicknameRegexp = regexp.MustCompile(nicknamePattern)\n\tusernameRegexp = regexp.MustCompile(usernamePattern)\n\tmailRegexp = regexp.MustCompile(mailPattern)\n\tchineseNameRegexp = regexp.MustCompile(chineseNamePattern)\n\tchineseNameExRegexp = regexp.MustCompile(chineseNameExPattern)\n)\n\n// 检验是否为合法的中国手机号, 不是那么太精细\n// 只要是 13,14,15,18 开头的 11 位数字就认为是中国手机号\nfunc IsChinaMobile(b []byte) bool {\n\tif len(b) != 11 {\n\t\treturn false\n\t}\n\treturn chinaMobileRegexp.Match(b)\n}\n\n// 同 func IsChinaMobile(b []byte) bool\nfunc IsChinaMobileString(s string) bool {\n\tif len(s) != 11 {\n\t\treturn false\n\t}\n\treturn chinaMobileRegexp.MatchString(s)\n}\n\n// 检验是否为合法的昵称, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n// 字符 '_' 只能出现在中间且不能重复, 如 \"__\"\nfunc IsNickname(b []byte) bool {\n\tif len(b) == 0 {\n\t\treturn false\n\t}\n\treturn nicknameRegexp.Match(b)\n}\n\n// 同 func IsNickname(b []byte) bool\nfunc IsNicknameString(s string) bool {\n\tif len(s) == 0 {\n\t\treturn false\n\t}\n\treturn nicknameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的用户名, 合法的字符有 0-9, A-Z, a-z, _\n// 第一个字母不能为 _, 0-9\n// 最后一个字母不能为 _, 且 _ 不能连续\nfunc IsUserName(b []byte) bool {\n\tif len(b) == 0 {\n\t\treturn false\n\t}\n\treturn usernameRegexp.Match(b)\n}\n\n// 同 func IsName(b []byte) bool\nfunc IsUserNameString(s string) bool {\n\tif len(s) == 0 {\n\t\treturn false\n\t}\n\treturn usernameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的电子邮箱, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n// 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n// 开头字母不能是 -, _, .\n// 结尾字母不能是 -, _, .\n// -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n// 邮箱的域名可以包含 0-9, A-Z, a-z, -\n// 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n// 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\nfunc IsMail(b []byte) bool {\n\tif len(b) < 6 { // x@x.xx\n\t\treturn false\n\t}\n\treturn mailRegexp.Match(b)\n}\n\n// 同 func IsMail(b []byte) bool\nfunc IsMailString(s string) bool {\n\tif len(s) < 6 { // x@x.xx\n\t\treturn false\n\t}\n\treturn mailRegexp.MatchString(s)\n}\n\n// IsChineseName 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四)\nfunc IsChineseName(b []byte) bool {\n\treturn chineseNameRegexp.Match(b)\n}\n\n// 同 IsChineseName(b []byte) bool\nfunc IsChineseNameString(s string) bool {\n\treturn chineseNameRegexp.MatchString(s)\n}\n\n// IsChineseNameEx 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四),\n// 主要功能和 IsChineseName 相同, 但是如果姓名中包含不规范的间隔符, 会自动修正为正确的间隔符 '\\u00B7', 并返回正确的结果.\nfunc IsChineseNameEx(b []byte) ([]byte, bool) {\n\tif chineseNameRegexp.Match(b) {\n\t\treturn b, true\n\t}\n\tif !chineseNameExRegexp.Match(b) {\n\t\treturn b, false\n\t}\n\tlist := []rune(string(b))\n\tfor i := 0; i < len(list); i++ {\n\t\tswitch list[i] {\n\t\tcase '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n\t\t\tlist[i] = '\\u00B7'\n\t\t}\n\t}\n\treturn []byte(string(list)), true\n}\n\n// 同 IsChineseNameEx(b []byte) ([]byte, bool)\nfunc IsChineseNameStringEx(s string) (string, bool) {\n\tif chineseNameRegexp.MatchString(s) {\n\t\treturn s, true\n\t}\n\tif !chineseNameExRegexp.MatchString(s) {\n\t\treturn s, false\n\t}\n\tlist := []rune(s)\n\tfor i := 0; i < len(list); i++ {\n\t\tswitch list[i] {\n\t\tcase '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n\t\t\tlist[i] = '\\u00B7'\n\t\t}\n\t}\n\treturn string(list), true\n}\n```","slug":"golang/golang 正则表达式常用","published":1,"updated":"2022-11-01T03:06:50.461Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn0000niysybk9e9cwc","content":"<h1 id=\"regex-const-go\"><a href=\"#regex-const-go\" class=\"headerlink\" title=\"regex_const.go\"></a>regex_const.go</h1><pre><code class=\"golang\">package check\n\nimport (\n "regexp"\n)s\n\nconst (\n // 中国大陆手机号码正则匹配, 不是那么太精细\n // 只要是 13,14,15,17,18,19 开头的 11 位数字就认为是中国手机号\n chinaMobilePattern = `^1[0-9]{10}$`\n // 用户昵称的正则匹配, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n // 字符 '_' 只能出现在中间且不能重复, 如 "__"\n nicknamePattern = `^[a-z0-9A-Z\\p{Han}]+(_[a-z0-9A-Z\\p{Han}]+)*?$`\n // 用户名的正则匹配, 合法的字符有 0-9, A-Z, a-z, _\n // 第一个字母不能为 _, 0-9\n // 最后一个字母不能为 _, 且 _ 不能连续\n usernamePattern = `^[a-zA-Z][a-z0-9A-Z]*(_[a-z0-9A-Z]+)*?$`\n // 电子邮箱的正则匹配, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n // 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n // 开头字母不能是 -, _, .\n // 结尾字母不能是 -, _, .\n // -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n // 邮箱的域名可以包含 0-9, A-Z, a-z, -\n // 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n // 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\n mailPattern = `^[a-z0-9A-Z]+([_\\-\\.][a-z0-9A-Z]+)*?@[a-z0-9A-Z]+([\\-\\.][a-z0-9A-Z]+)*?\\.[a-zA-Z]{2,}$`\n\n chineseNamePattern = "^\\\\p{Han}+(\\u00B7\\\\p{Han}+)*?$"\n chineseNameExPattern = "^\\\\p{Han}+([\\u00B7\\u2022\\u2027\\u30FB\\u002E\\u0387\\u16EB\\u2219\\u22C5\\uFF65\\u05BC]\\\\p{Han}+)*?$"\n)\n\nvar (\n chinaMobileRegexp = regexp.MustCompile(chinaMobilePattern)\n nicknameRegexp = regexp.MustCompile(nicknamePattern)\n usernameRegexp = regexp.MustCompile(usernamePattern)\n mailRegexp = regexp.MustCompile(mailPattern)\n chineseNameRegexp = regexp.MustCompile(chineseNamePattern)\n chineseNameExRegexp = regexp.MustCompile(chineseNameExPattern)\n)\n\n// 检验是否为合法的中国手机号, 不是那么太精细\n// 只要是 13,14,15,18 开头的 11 位数字就认为是中国手机号\nfunc IsChinaMobile(b []byte) bool {\n if len(b) != 11 {\n return false\n }\n return chinaMobileRegexp.Match(b)\n}\n\n// 同 func IsChinaMobile(b []byte) bool\nfunc IsChinaMobileString(s string) bool {\n if len(s) != 11 {\n return false\n }\n return chinaMobileRegexp.MatchString(s)\n}\n\n// 检验是否为合法的昵称, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n// 字符 '_' 只能出现在中间且不能重复, 如 "__"\nfunc IsNickname(b []byte) bool {\n if len(b) == 0 {\n return false\n }\n return nicknameRegexp.Match(b)\n}\n\n// 同 func IsNickname(b []byte) bool\nfunc IsNicknameString(s string) bool {\n if len(s) == 0 {\n return false\n }\n return nicknameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的用户名, 合法的字符有 0-9, A-Z, a-z, _\n// 第一个字母不能为 _, 0-9\n// 最后一个字母不能为 _, 且 _ 不能连续\nfunc IsUserName(b []byte) bool {\n if len(b) == 0 {\n return false\n }\n return usernameRegexp.Match(b)\n}\n\n// 同 func IsName(b []byte) bool\nfunc IsUserNameString(s string) bool {\n if len(s) == 0 {\n return false\n }\n return usernameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的电子邮箱, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n// 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n// 开头字母不能是 -, _, .\n// 结尾字母不能是 -, _, .\n// -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n// 邮箱的域名可以包含 0-9, A-Z, a-z, -\n// 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n// 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\nfunc IsMail(b []byte) bool {\n if len(b) < 6 { // x@x.xx\n return false\n }\n return mailRegexp.Match(b)\n}\n\n// 同 func IsMail(b []byte) bool\nfunc IsMailString(s string) bool {\n if len(s) < 6 { // x@x.xx\n return false\n }\n return mailRegexp.MatchString(s)\n}\n\n// IsChineseName 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四)\nfunc IsChineseName(b []byte) bool {\n return chineseNameRegexp.Match(b)\n}\n\n// 同 IsChineseName(b []byte) bool\nfunc IsChineseNameString(s string) bool {\n return chineseNameRegexp.MatchString(s)\n}\n\n// IsChineseNameEx 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四),\n// 主要功能和 IsChineseName 相同, 但是如果姓名中包含不规范的间隔符, 会自动修正为正确的间隔符 '\\u00B7', 并返回正确的结果.\nfunc IsChineseNameEx(b []byte) ([]byte, bool) {\n if chineseNameRegexp.Match(b) {\n return b, true\n }\n if !chineseNameExRegexp.Match(b) {\n return b, false\n }\n list := []rune(string(b))\n for i := 0; i < len(list); i++ {\n switch list[i] {\n case '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n list[i] = '\\u00B7'\n }\n }\n return []byte(string(list)), true\n}\n\n// 同 IsChineseNameEx(b []byte) ([]byte, bool)\nfunc IsChineseNameStringEx(s string) (string, bool) {\n if chineseNameRegexp.MatchString(s) {\n return s, true\n }\n if !chineseNameExRegexp.MatchString(s) {\n return s, false\n }\n list := []rune(s)\n for i := 0; i < len(list); i++ {\n switch list[i] {\n case '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n list[i] = '\\u00B7'\n }\n }\n return string(list), true\n}\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h1 id=\"regex-const-go\"><a href=\"#regex-const-go\" class=\"headerlink\" title=\"regex_const.go\"></a>regex_const.go</h1><pre><code class=\"golang\">package check\n\nimport (\n "regexp"\n)s\n\nconst (\n // 中国大陆手机号码正则匹配, 不是那么太精细\n // 只要是 13,14,15,17,18,19 开头的 11 位数字就认为是中国手机号\n chinaMobilePattern = `^1[0-9]{10}$`\n // 用户昵称的正则匹配, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n // 字符 '_' 只能出现在中间且不能重复, 如 "__"\n nicknamePattern = `^[a-z0-9A-Z\\p{Han}]+(_[a-z0-9A-Z\\p{Han}]+)*?$`\n // 用户名的正则匹配, 合法的字符有 0-9, A-Z, a-z, _\n // 第一个字母不能为 _, 0-9\n // 最后一个字母不能为 _, 且 _ 不能连续\n usernamePattern = `^[a-zA-Z][a-z0-9A-Z]*(_[a-z0-9A-Z]+)*?$`\n // 电子邮箱的正则匹配, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n // 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n // 开头字母不能是 -, _, .\n // 结尾字母不能是 -, _, .\n // -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n // 邮箱的域名可以包含 0-9, A-Z, a-z, -\n // 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n // 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\n mailPattern = `^[a-z0-9A-Z]+([_\\-\\.][a-z0-9A-Z]+)*?@[a-z0-9A-Z]+([\\-\\.][a-z0-9A-Z]+)*?\\.[a-zA-Z]{2,}$`\n\n chineseNamePattern = "^\\\\p{Han}+(\\u00B7\\\\p{Han}+)*?$"\n chineseNameExPattern = "^\\\\p{Han}+([\\u00B7\\u2022\\u2027\\u30FB\\u002E\\u0387\\u16EB\\u2219\\u22C5\\uFF65\\u05BC]\\\\p{Han}+)*?$"\n)\n\nvar (\n chinaMobileRegexp = regexp.MustCompile(chinaMobilePattern)\n nicknameRegexp = regexp.MustCompile(nicknamePattern)\n usernameRegexp = regexp.MustCompile(usernamePattern)\n mailRegexp = regexp.MustCompile(mailPattern)\n chineseNameRegexp = regexp.MustCompile(chineseNamePattern)\n chineseNameExRegexp = regexp.MustCompile(chineseNameExPattern)\n)\n\n// 检验是否为合法的中国手机号, 不是那么太精细\n// 只要是 13,14,15,18 开头的 11 位数字就认为是中国手机号\nfunc IsChinaMobile(b []byte) bool {\n if len(b) != 11 {\n return false\n }\n return chinaMobileRegexp.Match(b)\n}\n\n// 同 func IsChinaMobile(b []byte) bool\nfunc IsChinaMobileString(s string) bool {\n if len(s) != 11 {\n return false\n }\n return chinaMobileRegexp.MatchString(s)\n}\n\n// 检验是否为合法的昵称, 合法的字符有 0-9, A-Z, a-z, _, 汉字\n// 字符 '_' 只能出现在中间且不能重复, 如 "__"\nfunc IsNickname(b []byte) bool {\n if len(b) == 0 {\n return false\n }\n return nicknameRegexp.Match(b)\n}\n\n// 同 func IsNickname(b []byte) bool\nfunc IsNicknameString(s string) bool {\n if len(s) == 0 {\n return false\n }\n return nicknameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的用户名, 合法的字符有 0-9, A-Z, a-z, _\n// 第一个字母不能为 _, 0-9\n// 最后一个字母不能为 _, 且 _ 不能连续\nfunc IsUserName(b []byte) bool {\n if len(b) == 0 {\n return false\n }\n return usernameRegexp.Match(b)\n}\n\n// 同 func IsName(b []byte) bool\nfunc IsUserNameString(s string) bool {\n if len(s) == 0 {\n return false\n }\n return usernameRegexp.MatchString(s)\n}\n\n// 检验是否为合法的电子邮箱, 考虑到各个网站的 mail 要求不一样, 这里匹配比较宽松\n// 邮箱用户名可以包含 0-9, A-Z, a-z, -, _, .\n// 开头字母不能是 -, _, .\n// 结尾字母不能是 -, _, .\n// -, _, . 这三个连接字母任意两个不能连续, 如不能出现 --, __, .., -_, -., _.\n// 邮箱的域名可以包含 0-9, A-Z, a-z, -\n// 连接字符 - 只能出现在中间, 不能连续, 如不能 --\n// 支持多级域名, x@y.z, x@y.z.w, x@x.y.z.w.e\nfunc IsMail(b []byte) bool {\n if len(b) < 6 { // x@x.xx\n return false\n }\n return mailRegexp.Match(b)\n}\n\n// 同 func IsMail(b []byte) bool\nfunc IsMailString(s string) bool {\n if len(s) < 6 { // x@x.xx\n return false\n }\n return mailRegexp.MatchString(s)\n}\n\n// IsChineseName 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四)\nfunc IsChineseName(b []byte) bool {\n return chineseNameRegexp.Match(b)\n}\n\n// 同 IsChineseName(b []byte) bool\nfunc IsChineseNameString(s string) bool {\n return chineseNameRegexp.MatchString(s)\n}\n\n// IsChineseNameEx 检验是否为有效的中文姓名(比如 张三, 李四, 张三·李四),\n// 主要功能和 IsChineseName 相同, 但是如果姓名中包含不规范的间隔符, 会自动修正为正确的间隔符 '\\u00B7', 并返回正确的结果.\nfunc IsChineseNameEx(b []byte) ([]byte, bool) {\n if chineseNameRegexp.Match(b) {\n return b, true\n }\n if !chineseNameExRegexp.Match(b) {\n return b, false\n }\n list := []rune(string(b))\n for i := 0; i < len(list); i++ {\n switch list[i] {\n case '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n list[i] = '\\u00B7'\n }\n }\n return []byte(string(list)), true\n}\n\n// 同 IsChineseNameEx(b []byte) ([]byte, bool)\nfunc IsChineseNameStringEx(s string) (string, bool) {\n if chineseNameRegexp.MatchString(s) {\n return s, true\n }\n if !chineseNameExRegexp.MatchString(s) {\n return s, false\n }\n list := []rune(s)\n for i := 0; i < len(list); i++ {\n switch list[i] {\n case '\\u2022', '\\u2027', '\\u30FB', '\\u002E', '\\u0387', '\\u16EB', '\\u2219', '\\u22C5', '\\uFF65', '\\u05BC':\n list[i] = '\\u00B7'\n }\n }\n return string(list), true\n}\n</code></pre>\n"},{"title":"golang 生成随机字符串","date":"2019-06-13T07:41:22.000Z","_content":"\n### 随机字符串\n\n```go\n\n//RandomStr 随机生成字符串\nfunc RandomStr(length int) string {\n\tstr := \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\tbytes := []byte(str)\n\tresult := []byte{}\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\tfor i := 0; i < length; i++ {\n\t\tresult = append(result, bytes[r.Intn(len(bytes))])\n\t}\n\treturn string(result)\n}\n```\n\n### 生成定长字符串\n\n\n```go\n//获得定长字符串\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenString(str string, length int, char byte) string {\n\tif len(str) == 0 {\n\t\treturn \"\"\n\t}\n\n\tif len(str) == length {\n\t\treturn str\n\t}\n\n\t//超出切后面\n\tif len(str) > length {\n\t\treturn string(str[:length])\n\t}\n\n\t//缺少添加char\n\tif len(str) < length {\n\t\tslice := make([]byte, length-len(str))\n\t\tfor k := range slice {\n\t\t\tslice[k] = char\n\t\t}\n\t\treturn string(append(slice, []byte(str)...))\n\t}\n\n\treturn \"\"\n}\n\n```\n### 获得定长byte slice\n```go\n//获得定长byte slice\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenByte(b []byte, length int, char byte) (tb []byte) {\n\tif len(b) == 0 {\n\t\treturn\n\t}\n\n\tif len(b) == length {\n\t\treturn b\n\t}\n\n\t//超出切后面\n\tif len(b) > length {\n\t\treturn b[:length]\n\t}\n\n\t//缺少添加char\n\tif len(b) < length {\n\t\tslice := make([]byte, length-len(b))\n\t\tfor k := range slice {\n\t\t\tslice[k] = char\n\t\t}\n\t\treturn append(slice, []byte(b)...)\n\t}\n\n\treturn\n}\n```\n\n### 生成随机验证码\n\n```golang\npackage main\n \nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"time\"\n)\n \nfunc GenValidateCode(width int) string {\n\tnumeric := [10]byte{0,1,2,3,4,5,6,7,8,9}\n\tr := len(numeric)\n\trand.Seed(time.Now().UnixNano())\n \n\tvar sb strings.Builder\n\tfor i := 0; i < width; i++ {\n\t\tfmt.Fprintf(&sb, \"%d\", numeric[ rand.Intn(r) ])\n\t}\n\treturn sb.String()\n}\n \nfunc main() {\n\tfmt.Println( GenValidateCode(6) )\n}\n\n```\n\n","source":"_posts/golang/golang 生成随机字符串.md","raw":"---\ntitle: golang 生成随机字符串\ndate: 2019-06-13 15:41:22\ntags: \n- golang\n\ncategories:\n- golang\n\n---\n\n### 随机字符串\n\n```go\n\n//RandomStr 随机生成字符串\nfunc RandomStr(length int) string {\n\tstr := \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\tbytes := []byte(str)\n\tresult := []byte{}\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\tfor i := 0; i < length; i++ {\n\t\tresult = append(result, bytes[r.Intn(len(bytes))])\n\t}\n\treturn string(result)\n}\n```\n\n### 生成定长字符串\n\n\n```go\n//获得定长字符串\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenString(str string, length int, char byte) string {\n\tif len(str) == 0 {\n\t\treturn \"\"\n\t}\n\n\tif len(str) == length {\n\t\treturn str\n\t}\n\n\t//超出切后面\n\tif len(str) > length {\n\t\treturn string(str[:length])\n\t}\n\n\t//缺少添加char\n\tif len(str) < length {\n\t\tslice := make([]byte, length-len(str))\n\t\tfor k := range slice {\n\t\t\tslice[k] = char\n\t\t}\n\t\treturn string(append(slice, []byte(str)...))\n\t}\n\n\treturn \"\"\n}\n\n```\n### 获得定长byte slice\n```go\n//获得定长byte slice\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenByte(b []byte, length int, char byte) (tb []byte) {\n\tif len(b) == 0 {\n\t\treturn\n\t}\n\n\tif len(b) == length {\n\t\treturn b\n\t}\n\n\t//超出切后面\n\tif len(b) > length {\n\t\treturn b[:length]\n\t}\n\n\t//缺少添加char\n\tif len(b) < length {\n\t\tslice := make([]byte, length-len(b))\n\t\tfor k := range slice {\n\t\t\tslice[k] = char\n\t\t}\n\t\treturn append(slice, []byte(b)...)\n\t}\n\n\treturn\n}\n```\n\n### 生成随机验证码\n\n```golang\npackage main\n \nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"time\"\n)\n \nfunc GenValidateCode(width int) string {\n\tnumeric := [10]byte{0,1,2,3,4,5,6,7,8,9}\n\tr := len(numeric)\n\trand.Seed(time.Now().UnixNano())\n \n\tvar sb strings.Builder\n\tfor i := 0; i < width; i++ {\n\t\tfmt.Fprintf(&sb, \"%d\", numeric[ rand.Intn(r) ])\n\t}\n\treturn sb.String()\n}\n \nfunc main() {\n\tfmt.Println( GenValidateCode(6) )\n}\n\n```\n\n","slug":"golang/golang 生成随机字符串","published":1,"updated":"2022-10-31T09:00:20.493Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn1000piysy0rn17ftq","content":"<h3 id=\"随机字符串\"><a href=\"#随机字符串\" class=\"headerlink\" title=\"随机字符串\"></a>随机字符串</h3><pre><code class=\"go\">\n//RandomStr 随机生成字符串\nfunc RandomStr(length int) string {\n str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"\n bytes := []byte(str)\n result := []byte{}\n r := rand.New(rand.NewSource(time.Now().UnixNano()))\n for i := 0; i < length; i++ {\n result = append(result, bytes[r.Intn(len(bytes))])\n }\n return string(result)\n}\n</code></pre>\n<h3 id=\"生成定长字符串\"><a href=\"#生成定长字符串\" class=\"headerlink\" title=\"生成定长字符串\"></a>生成定长字符串</h3><pre><code class=\"go\">//获得定长字符串\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenString(str string, length int, char byte) string {\n if len(str) == 0 {\n return ""\n }\n\n if len(str) == length {\n return str\n }\n\n //超出切后面\n if len(str) > length {\n return string(str[:length])\n }\n\n //缺少添加char\n if len(str) < length {\n slice := make([]byte, length-len(str))\n for k := range slice {\n slice[k] = char\n }\n return string(append(slice, []byte(str)...))\n }\n\n return ""\n}\n</code></pre>\n<h3 id=\"获得定长byte-slice\"><a href=\"#获得定长byte-slice\" class=\"headerlink\" title=\"获得定长byte slice\"></a>获得定长byte slice</h3><pre><code class=\"go\">//获得定长byte slice\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenByte(b []byte, length int, char byte) (tb []byte) {\n if len(b) == 0 {\n return\n }\n\n if len(b) == length {\n return b\n }\n\n //超出切后面\n if len(b) > length {\n return b[:length]\n }\n\n //缺少添加char\n if len(b) < length {\n slice := make([]byte, length-len(b))\n for k := range slice {\n slice[k] = char\n }\n return append(slice, []byte(b)...)\n }\n\n return\n}\n</code></pre>\n<h3 id=\"生成随机验证码\"><a href=\"#生成随机验证码\" class=\"headerlink\" title=\"生成随机验证码\"></a>生成随机验证码</h3><pre><code class=\"golang\">package main\n \nimport (\n "fmt"\n "math/rand"\n "strings"\n "time"\n)\n \nfunc GenValidateCode(width int) string {\n numeric := [10]byte{0,1,2,3,4,5,6,7,8,9}\n r := len(numeric)\n rand.Seed(time.Now().UnixNano())\n \n var sb strings.Builder\n for i := 0; i < width; i++ {\n fmt.Fprintf(&sb, "%d", numeric[ rand.Intn(r) ])\n }\n return sb.String()\n}\n \nfunc main() {\n fmt.Println( GenValidateCode(6) )\n}\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"随机字符串\"><a href=\"#随机字符串\" class=\"headerlink\" title=\"随机字符串\"></a>随机字符串</h3><pre><code class=\"go\">\n//RandomStr 随机生成字符串\nfunc RandomStr(length int) string {\n str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"\n bytes := []byte(str)\n result := []byte{}\n r := rand.New(rand.NewSource(time.Now().UnixNano()))\n for i := 0; i < length; i++ {\n result = append(result, bytes[r.Intn(len(bytes))])\n }\n return string(result)\n}\n</code></pre>\n<h3 id=\"生成定长字符串\"><a href=\"#生成定长字符串\" class=\"headerlink\" title=\"生成定长字符串\"></a>生成定长字符串</h3><pre><code class=\"go\">//获得定长字符串\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenString(str string, length int, char byte) string {\n if len(str) == 0 {\n return ""\n }\n\n if len(str) == length {\n return str\n }\n\n //超出切后面\n if len(str) > length {\n return string(str[:length])\n }\n\n //缺少添加char\n if len(str) < length {\n slice := make([]byte, length-len(str))\n for k := range slice {\n slice[k] = char\n }\n return string(append(slice, []byte(str)...))\n }\n\n return ""\n}\n</code></pre>\n<h3 id=\"获得定长byte-slice\"><a href=\"#获得定长byte-slice\" class=\"headerlink\" title=\"获得定长byte slice\"></a>获得定长byte slice</h3><pre><code class=\"go\">//获得定长byte slice\n//str 填充字符串\n//length 获得定长的长度\n//char 不够长时填充的字符\nfunc GetFixedLenByte(b []byte, length int, char byte) (tb []byte) {\n if len(b) == 0 {\n return\n }\n\n if len(b) == length {\n return b\n }\n\n //超出切后面\n if len(b) > length {\n return b[:length]\n }\n\n //缺少添加char\n if len(b) < length {\n slice := make([]byte, length-len(b))\n for k := range slice {\n slice[k] = char\n }\n return append(slice, []byte(b)...)\n }\n\n return\n}\n</code></pre>\n<h3 id=\"生成随机验证码\"><a href=\"#生成随机验证码\" class=\"headerlink\" title=\"生成随机验证码\"></a>生成随机验证码</h3><pre><code class=\"golang\">package main\n \nimport (\n "fmt"\n "math/rand"\n "strings"\n "time"\n)\n \nfunc GenValidateCode(width int) string {\n numeric := [10]byte{0,1,2,3,4,5,6,7,8,9}\n r := len(numeric)\n rand.Seed(time.Now().UnixNano())\n \n var sb strings.Builder\n for i := 0; i < width; i++ {\n fmt.Fprintf(&sb, "%d", numeric[ rand.Intn(r) ])\n }\n return sb.String()\n}\n \nfunc main() {\n fmt.Println( GenValidateCode(6) )\n}\n</code></pre>\n"},{"title":"golang ants 协程池","auto_excerpt":{"enable":true,"length":150},"date":"2021-09-18T08:07:50.000Z","_content":"\n### ants 类图\n![direct](/img/puml/ants/ants.svg)\n","source":"_posts/golang/golang-ants-协程池.md","raw":"---\ntitle: golang ants 协程池\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-09-18 16:07:50\ntags:\n- golang\n- ants\n- goroutine\ncategories:\n- golang\n---\n\n### ants 类图\n![direct](/img/puml/ants/ants.svg)\n","slug":"golang/golang-ants-协程池","published":1,"updated":"2022-10-31T09:40:57.107Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn2000tiysyfx2lgah0","content":"<h3 id=\"ants-类图\"><a href=\"#ants-类图\" class=\"headerlink\" title=\"ants 类图\"></a>ants 类图</h3><p><img src=\"/img/puml/ants/ants.svg\" alt=\"direct\"></p>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"ants-类图\"><a href=\"#ants-类图\" class=\"headerlink\" title=\"ants 类图\"></a>ants 类图</h3><p><img src=\"/img/puml/ants/ants.svg\" alt=\"direct\"></p>\n"},{"title":"golang helloworld docker 启动","date":"2021-09-08T09:15:43.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n**golang的helloworld程序,利用docker-compose启动**\n\n创建项目目录并初始化go mod 项目\n\n```shell\nmkdir hellogo\ncd hellogo\n# 初始化 go mod 项目\ngo mod init hellogo\n```\n\n项目结构\n\n```shell\n➜ hellogo tree \n.\n├── docker-compose.yaml\n├── dockerfile\n├── go.mod\n└── main.go\n```\n\n`vim main.go`\n\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tfmt.Println(\"hello world\")\n\ti := 1\n\tfor {\n\t\ttime.Sleep(1 * time.Second)\n\t\tfmt.Printf(\"wait %d second\\n\", i)\n\t\ti++\n\t}\n}\n\n```\n\n`vim dockerfile`\n\n```dockerfile\n# build stage\nFROM golang:alpine AS builder\nRUN apk add --no-cache git gcc libc-dev\n\nRUN mkdir /app\nCOPY go.mod /app/go.mod\nCOPY main.go /app/main.go\nWORKDIR /app\nRUN go mod tidy\nRUN go build -o hellogo main.go\n\n# final stage\nFROM alpine:latest\nLABEL Name=hellogo Version=0.0.1\nRUN apk --no-cache add ca-certificates\nCOPY --from=builder /app/hellogo /app\nENTRYPOINT ./app\n# EXPOSE 80\n\n```\n\n`vim docker-compose.yaml`\n\n```docker-compose\nversion: \"3\"\nservices:\n hellogo:\n build: .\n image: hellogo\n network_mode: bridge\n container_name: hellogo\n\n```\n\n**运行 docker-compose**\n\n```shell\ndocker-compose -f docker-compose.yaml up --build\n```\n\n看到如下输出表示成功\n\n```shell\n...\nSuccessfully built 50cb8339209e\nSuccessfully tagged hellogo:latest\nStarting hellogo ... done\nAttaching to hellogo\nhellogo | hello world\nhellogo | wait 1 second\nhellogo | wait 2 second\nhellogo | wait 3 second\n```\n\n","source":"_posts/golang/golang-helloworld-docker-启动.md","raw":"---\ntitle: golang helloworld docker 启动\ndate: 2021-09-08 17:15:43\ntags:\n- golang\n- docker\n- docker-compose\n- helloworld\ncategories:\n- golang\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n**golang的helloworld程序,利用docker-compose启动**\n\n创建项目目录并初始化go mod 项目\n\n```shell\nmkdir hellogo\ncd hellogo\n# 初始化 go mod 项目\ngo mod init hellogo\n```\n\n项目结构\n\n```shell\n➜ hellogo tree \n.\n├── docker-compose.yaml\n├── dockerfile\n├── go.mod\n└── main.go\n```\n\n`vim main.go`\n\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tfmt.Println(\"hello world\")\n\ti := 1\n\tfor {\n\t\ttime.Sleep(1 * time.Second)\n\t\tfmt.Printf(\"wait %d second\\n\", i)\n\t\ti++\n\t}\n}\n\n```\n\n`vim dockerfile`\n\n```dockerfile\n# build stage\nFROM golang:alpine AS builder\nRUN apk add --no-cache git gcc libc-dev\n\nRUN mkdir /app\nCOPY go.mod /app/go.mod\nCOPY main.go /app/main.go\nWORKDIR /app\nRUN go mod tidy\nRUN go build -o hellogo main.go\n\n# final stage\nFROM alpine:latest\nLABEL Name=hellogo Version=0.0.1\nRUN apk --no-cache add ca-certificates\nCOPY --from=builder /app/hellogo /app\nENTRYPOINT ./app\n# EXPOSE 80\n\n```\n\n`vim docker-compose.yaml`\n\n```docker-compose\nversion: \"3\"\nservices:\n hellogo:\n build: .\n image: hellogo\n network_mode: bridge\n container_name: hellogo\n\n```\n\n**运行 docker-compose**\n\n```shell\ndocker-compose -f docker-compose.yaml up --build\n```\n\n看到如下输出表示成功\n\n```shell\n...\nSuccessfully built 50cb8339209e\nSuccessfully tagged hellogo:latest\nStarting hellogo ... done\nAttaching to hellogo\nhellogo | hello world\nhellogo | wait 1 second\nhellogo | wait 2 second\nhellogo | wait 3 second\n```\n\n","slug":"golang/golang-helloworld-docker-启动","published":1,"updated":"2022-11-01T02:42:56.887Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn3000viysy911w0yct","content":"<p><strong>golang的helloworld程序,利用docker-compose启动</strong></p>\n<p>创建项目目录并初始化go mod 项目</p>\n<pre><code class=\"shell\">mkdir hellogo\ncd hellogo\n# 初始化 go mod 项目\ngo mod init hellogo\n</code></pre>\n<p>项目结构</p>\n<pre><code class=\"shell\">➜ hellogo tree \n.\n├── docker-compose.yaml\n├── dockerfile\n├── go.mod\n└── main.go\n</code></pre>\n<p><code>vim main.go</code></p>\n<pre><code class=\"golang\">package main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc main() {\n fmt.Println("hello world")\n i := 1\n for {\n time.Sleep(1 * time.Second)\n fmt.Printf("wait %d second\\n", i)\n i++\n }\n}\n</code></pre>\n<p><code>vim dockerfile</code></p>\n<pre><code class=\"dockerfile\"># build stage\nFROM golang:alpine AS builder\nRUN apk add --no-cache git gcc libc-dev\n\nRUN mkdir /app\nCOPY go.mod /app/go.mod\nCOPY main.go /app/main.go\nWORKDIR /app\nRUN go mod tidy\nRUN go build -o hellogo main.go\n\n# final stage\nFROM alpine:latest\nLABEL Name=hellogo Version=0.0.1\nRUN apk --no-cache add ca-certificates\nCOPY --from=builder /app/hellogo /app\nENTRYPOINT ./app\n# EXPOSE 80\n</code></pre>\n<p><code>vim docker-compose.yaml</code></p>\n<pre><code class=\"docker-compose\">version: "3"\nservices:\n hellogo:\n build: .\n image: hellogo\n network_mode: bridge\n container_name: hellogo\n</code></pre>\n<p><strong>运行 docker-compose</strong></p>\n<pre><code class=\"shell\">docker-compose -f docker-compose.yaml up --build\n</code></pre>\n<p>看到如下输出表示成功</p>\n<pre><code class=\"shell\">...\nSuccessfully built 50cb8339209e\nSuccessfully tagged hellogo:latest\nStarting hellogo ... done\nAttaching to hellogo\nhellogo | hello world\nhellogo | wait 1 second\nhellogo | wait 2 second\nhellogo | wait 3 second\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p><strong>golang的helloworld程序,利用docker-compose启动</strong></p>\n<p>创建项目目录并初始化go mod 项目</p>\n<pre><code class=\"shell\">mkdir hellogo\ncd hellogo\n# 初始化 go mod 项目\ngo mod init hellogo\n</code></pre>\n<p>项目结构</p>\n<pre><code class=\"shell\">➜ hellogo tree \n.\n├── docker-compose.yaml\n├── dockerfile\n├── go.mod\n└── main.go\n</code></pre>\n<p><code>vim main.go</code></p>\n<pre><code class=\"golang\">package main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc main() {\n fmt.Println("hello world")\n i := 1\n for {\n time.Sleep(1 * time.Second)\n fmt.Printf("wait %d second\\n", i)\n i++\n }\n}\n</code></pre>\n<p><code>vim dockerfile</code></p>\n<pre><code class=\"dockerfile\"># build stage\nFROM golang:alpine AS builder\nRUN apk add --no-cache git gcc libc-dev\n\nRUN mkdir /app\nCOPY go.mod /app/go.mod\nCOPY main.go /app/main.go\nWORKDIR /app\nRUN go mod tidy\nRUN go build -o hellogo main.go\n\n# final stage\nFROM alpine:latest\nLABEL Name=hellogo Version=0.0.1\nRUN apk --no-cache add ca-certificates\nCOPY --from=builder /app/hellogo /app\nENTRYPOINT ./app\n# EXPOSE 80\n</code></pre>\n<p><code>vim docker-compose.yaml</code></p>\n<pre><code class=\"docker-compose\">version: "3"\nservices:\n hellogo:\n build: .\n image: hellogo\n network_mode: bridge\n container_name: hellogo\n</code></pre>\n<p><strong>运行 docker-compose</strong></p>\n<pre><code class=\"shell\">docker-compose -f docker-compose.yaml up --build\n</code></pre>\n<p>看到如下输出表示成功</p>\n<pre><code class=\"shell\">...\nSuccessfully built 50cb8339209e\nSuccessfully tagged hellogo:latest\nStarting hellogo ... done\nAttaching to hellogo\nhellogo | hello world\nhellogo | wait 1 second\nhellogo | wait 2 second\nhellogo | wait 3 second\n</code></pre>\n"},{"title":"golang 实现计算器","date":"2021-09-24T08:58:16.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"golang 实现一个计算的功能,并支持括号计算\n\ngithub地址:[https://github.com/huangxingx/go-utils.git](https://github.com/huangxingx/go-utils.git)\n\n#### 实现重点:后缀表达式\n一般的表达式都是中缀表达式,操作符在两个操作项之间。而后缀表达式的操作符在两个操作项后面\n\n#### 后缀表达式的实现\n利用栈 `stack` 和队列 `queue` 实现**后缀表达式**\n![后缀表达式流程图](/img/puml/suffix_express/suffix_express.svg)\n\n**流程:**\n> 1. 先把字符串转换为列表 比如 \"1+2+3\\*4\" 转换为 [\"1\", \"+\", \"2\", \"+\", \"3\", \"*\", \"4\"]\n> 2. 上面的字符串列表转换为后缀表达式\n> 3. 遍历后缀表达式进行计算\n\n`computer.go`\n\n```golang\npackage go_utils\n\nimport \"strconv\"\n\n// Computer 计算后缀表达式\nfunc Computer(expression string) float64 {\n\tmpnExpression := parse2mpn(expression)\n\trpnExpression := parse2rpn(mpnExpression)\n\tresultStack := NewStack()\n\tfor _, v := range rpnExpression {\n\t\tswitch v {\n\t\tcase \"+\", \"-\", \"*\", \"/\":\n\t\t\tv1, _ := resultStack.Pop().(float64)\n\t\t\tv2, _ := resultStack.Pop().(float64)\n\t\t\tswitch v {\n\t\t\tcase \"+\":\n\t\t\t\tresultStack.Push(v1 + v2)\n\t\t\tcase \"-\":\n\t\t\t\tresultStack.Push(v2 - v1)\n\t\t\tcase \"*\":\n\t\t\t\tresultStack.Push(v2 * v1)\n\t\t\tcase \"/\":\n\t\t\t\tresultStack.Push(v2 / v1)\n\t\t\t}\n\t\tdefault:\n\t\t\tfloat, err := strconv.ParseFloat(v, 64)\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tresultStack.Push(float)\n\t\t}\n\t}\n\treturn resultStack.Pop().(float64)\n}\n\n// 解析中缀表达式数组\nfunc parse2mpn(express string) []string {\n\t//compile, _ := regexp.Compile(\"[\\\\d\\\\+\\\\-\\\\*\\\\/\\\\(\\\\)]\")\n\t//return compile.FindAllString(express, -1)\n\tresult := make([]string, 0)\n\ts := \"\"\n\tfor i, v := range express {\n\t\tif v == 32 {\n\t\t\tcontinue\n\t\t}\n\t\tif v > 47 && v < 59 || v == 46 {\n\t\t\ts += string(v)\n\t\t\tif i == len(express)-1 {\n\t\t\t\tresult = append(result, s)\n\t\t\t}\n\t\t} else {\n\t\t\t// +-*/ ( )\n\t\t\tif s != \"\" {\n\t\t\t\tresult = append(result, s)\n\t\t\t\ts = \"\"\n\t\t\t}\n\t\t\tresult = append(result, string(v))\n\t\t}\n\t}\n\treturn result\n}\n\n// parse2rpn 解析中缀表达式-> 后缀表达式\nfunc parse2rpn(express []string) []string {\n\tstact := NewStack()\n\trpnList := make([]string, 0, len(express))\n\tvar s string\n\tfor i := 0; i < len(express); i++ {\n\t\ts = express[i]\n\t\tswitch s {\n\t\tcase \"+\", \"-\":\n\t\t\tfor {\n\t\t\t\tif stact.IsEmpty() || stact.Peek().(string) == \"(\" {\n\t\t\t\t\tstact.Push(s)\n\t\t\t\t\tbreak\n\t\t\t\t} else {\n\t\t\t\t\trpnList = append(rpnList, stact.Pop().(string))\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"*\", \"/\":\n\t\t\tfor {\n\t\t\t\tif stact.IsEmpty() || (stact.Peek().(string) != \"*\" && stact.Peek().(string) != \"/\") {\n\t\t\t\t\tstact.Push(s)\n\t\t\t\t\tbreak\n\t\t\t\t} else {\n\t\t\t\t\trpnList = append(rpnList, stact.Pop().(string))\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase \"(\":\n\t\t\tstact.Push(s)\n\n\t\tcase \")\":\n\n\t\t\tfor stact.Peek().(string) != \"(\" {\n\t\t\t\trpnList = append(rpnList, stact.Pop().(string))\n\t\t\t}\n\t\t\tstact.Pop()\n\t\tdefault:\n\t\t\trpnList = append(rpnList, s)\n\t\t}\n\t}\n\n\tfor stact.Size() > 0 {\n\t\trpnList = append(rpnList, stact.Pop().(string))\n\t}\n\treturn rpnList\n}\n\n```\n\n","source":"_posts/golang/golang-实现计算器.md","raw":"---\ntitle: golang 实现计算器\ndate: 2021-09-24 16:58:16\ntags:\n- golang\n- 算法\ncategories:\n- golang\n\nauto_excerpt:\n enable: true\n length: 150\n---\ngolang 实现一个计算的功能,并支持括号计算\n\ngithub地址:[https://github.com/huangxingx/go-utils.git](https://github.com/huangxingx/go-utils.git)\n\n#### 实现重点:后缀表达式\n一般的表达式都是中缀表达式,操作符在两个操作项之间。而后缀表达式的操作符在两个操作项后面\n\n#### 后缀表达式的实现\n利用栈 `stack` 和队列 `queue` 实现**后缀表达式**\n![后缀表达式流程图](/img/puml/suffix_express/suffix_express.svg)\n\n**流程:**\n> 1. 先把字符串转换为列表 比如 \"1+2+3\\*4\" 转换为 [\"1\", \"+\", \"2\", \"+\", \"3\", \"*\", \"4\"]\n> 2. 上面的字符串列表转换为后缀表达式\n> 3. 遍历后缀表达式进行计算\n\n`computer.go`\n\n```golang\npackage go_utils\n\nimport \"strconv\"\n\n// Computer 计算后缀表达式\nfunc Computer(expression string) float64 {\n\tmpnExpression := parse2mpn(expression)\n\trpnExpression := parse2rpn(mpnExpression)\n\tresultStack := NewStack()\n\tfor _, v := range rpnExpression {\n\t\tswitch v {\n\t\tcase \"+\", \"-\", \"*\", \"/\":\n\t\t\tv1, _ := resultStack.Pop().(float64)\n\t\t\tv2, _ := resultStack.Pop().(float64)\n\t\t\tswitch v {\n\t\t\tcase \"+\":\n\t\t\t\tresultStack.Push(v1 + v2)\n\t\t\tcase \"-\":\n\t\t\t\tresultStack.Push(v2 - v1)\n\t\t\tcase \"*\":\n\t\t\t\tresultStack.Push(v2 * v1)\n\t\t\tcase \"/\":\n\t\t\t\tresultStack.Push(v2 / v1)\n\t\t\t}\n\t\tdefault:\n\t\t\tfloat, err := strconv.ParseFloat(v, 64)\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tresultStack.Push(float)\n\t\t}\n\t}\n\treturn resultStack.Pop().(float64)\n}\n\n// 解析中缀表达式数组\nfunc parse2mpn(express string) []string {\n\t//compile, _ := regexp.Compile(\"[\\\\d\\\\+\\\\-\\\\*\\\\/\\\\(\\\\)]\")\n\t//return compile.FindAllString(express, -1)\n\tresult := make([]string, 0)\n\ts := \"\"\n\tfor i, v := range express {\n\t\tif v == 32 {\n\t\t\tcontinue\n\t\t}\n\t\tif v > 47 && v < 59 || v == 46 {\n\t\t\ts += string(v)\n\t\t\tif i == len(express)-1 {\n\t\t\t\tresult = append(result, s)\n\t\t\t}\n\t\t} else {\n\t\t\t// +-*/ ( )\n\t\t\tif s != \"\" {\n\t\t\t\tresult = append(result, s)\n\t\t\t\ts = \"\"\n\t\t\t}\n\t\t\tresult = append(result, string(v))\n\t\t}\n\t}\n\treturn result\n}\n\n// parse2rpn 解析中缀表达式-> 后缀表达式\nfunc parse2rpn(express []string) []string {\n\tstact := NewStack()\n\trpnList := make([]string, 0, len(express))\n\tvar s string\n\tfor i := 0; i < len(express); i++ {\n\t\ts = express[i]\n\t\tswitch s {\n\t\tcase \"+\", \"-\":\n\t\t\tfor {\n\t\t\t\tif stact.IsEmpty() || stact.Peek().(string) == \"(\" {\n\t\t\t\t\tstact.Push(s)\n\t\t\t\t\tbreak\n\t\t\t\t} else {\n\t\t\t\t\trpnList = append(rpnList, stact.Pop().(string))\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"*\", \"/\":\n\t\t\tfor {\n\t\t\t\tif stact.IsEmpty() || (stact.Peek().(string) != \"*\" && stact.Peek().(string) != \"/\") {\n\t\t\t\t\tstact.Push(s)\n\t\t\t\t\tbreak\n\t\t\t\t} else {\n\t\t\t\t\trpnList = append(rpnList, stact.Pop().(string))\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase \"(\":\n\t\t\tstact.Push(s)\n\n\t\tcase \")\":\n\n\t\t\tfor stact.Peek().(string) != \"(\" {\n\t\t\t\trpnList = append(rpnList, stact.Pop().(string))\n\t\t\t}\n\t\t\tstact.Pop()\n\t\tdefault:\n\t\t\trpnList = append(rpnList, s)\n\t\t}\n\t}\n\n\tfor stact.Size() > 0 {\n\t\trpnList = append(rpnList, stact.Pop().(string))\n\t}\n\treturn rpnList\n}\n\n```\n\n","slug":"golang/golang-实现计算器","published":1,"updated":"2022-10-31T09:40:57.108Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn4000ziysy7c598fq0","content":"<p>golang 实现一个计算的功能,并支持括号计算</p>\n<p>github地址:<a href=\"https://github.com/huangxingx/go-utils.git\">https://github.com/huangxingx/go-utils.git</a></p>\n<h4 id=\"实现重点:后缀表达式\"><a href=\"#实现重点:后缀表达式\" class=\"headerlink\" title=\"实现重点:后缀表达式\"></a>实现重点:后缀表达式</h4><p>一般的表达式都是中缀表达式,操作符在两个操作项之间。而后缀表达式的操作符在两个操作项后面</p>\n<h4 id=\"后缀表达式的实现\"><a href=\"#后缀表达式的实现\" class=\"headerlink\" title=\"后缀表达式的实现\"></a>后缀表达式的实现</h4><p>利用栈 <code>stack</code> 和队列 <code>queue</code> 实现<strong>后缀表达式</strong><br><img src=\"/img/puml/suffix_express/suffix_express.svg\" alt=\"后缀表达式流程图\"></p>\n<p><strong>流程:</strong></p>\n<blockquote>\n<ol>\n<li>先把字符串转换为列表 比如 “1+2+3*4” 转换为 [“1”, “+”, “2”, “+”, “3”, “*”, “4”]</li>\n<li>上面的字符串列表转换为后缀表达式</li>\n<li>遍历后缀表达式进行计算</li>\n</ol>\n</blockquote>\n<p><code>computer.go</code></p>\n<pre><code class=\"golang\">package go_utils\n\nimport "strconv"\n\n// Computer 计算后缀表达式\nfunc Computer(expression string) float64 {\n mpnExpression := parse2mpn(expression)\n rpnExpression := parse2rpn(mpnExpression)\n resultStack := NewStack()\n for _, v := range rpnExpression {\n switch v {\n case "+", "-", "*", "/":\n v1, _ := resultStack.Pop().(float64)\n v2, _ := resultStack.Pop().(float64)\n switch v {\n case "+":\n resultStack.Push(v1 + v2)\n case "-":\n resultStack.Push(v2 - v1)\n case "*":\n resultStack.Push(v2 * v1)\n case "/":\n resultStack.Push(v2 / v1)\n }\n default:\n float, err := strconv.ParseFloat(v, 64)\n if err != nil {\n panic(err)\n }\n resultStack.Push(float)\n }\n }\n return resultStack.Pop().(float64)\n}\n\n// 解析中缀表达式数组\nfunc parse2mpn(express string) []string {\n //compile, _ := regexp.Compile("[\\\\d\\\\+\\\\-\\\\*\\\\/\\\\(\\\\)]")\n //return compile.FindAllString(express, -1)\n result := make([]string, 0)\n s := ""\n for i, v := range express {\n if v == 32 {\n continue\n }\n if v > 47 && v < 59 || v == 46 {\n s += string(v)\n if i == len(express)-1 {\n result = append(result, s)\n }\n } else {\n // +-*/ ( )\n if s != "" {\n result = append(result, s)\n s = ""\n }\n result = append(result, string(v))\n }\n }\n return result\n}\n\n// parse2rpn 解析中缀表达式-> 后缀表达式\nfunc parse2rpn(express []string) []string {\n stact := NewStack()\n rpnList := make([]string, 0, len(express))\n var s string\n for i := 0; i < len(express); i++ {\n s = express[i]\n switch s {\n case "+", "-":\n for {\n if stact.IsEmpty() || stact.Peek().(string) == "(" {\n stact.Push(s)\n break\n } else {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n }\n case "*", "/":\n for {\n if stact.IsEmpty() || (stact.Peek().(string) != "*" && stact.Peek().(string) != "/") {\n stact.Push(s)\n break\n } else {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n }\n\n case "(":\n stact.Push(s)\n\n case ")":\n\n for stact.Peek().(string) != "(" {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n stact.Pop()\n default:\n rpnList = append(rpnList, s)\n }\n }\n\n for stact.Size() > 0 {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n return rpnList\n}\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p>golang 实现一个计算的功能,并支持括号计算</p>\n<p>github地址:<a href=\"https://github.com/huangxingx/go-utils.git\">https://github.com/huangxingx/go-utils.git</a></p>\n<h4 id=\"实现重点:后缀表达式\"><a href=\"#实现重点:后缀表达式\" class=\"headerlink\" title=\"实现重点:后缀表达式\"></a>实现重点:后缀表达式</h4><p>一般的表达式都是中缀表达式,操作符在两个操作项之间。而后缀表达式的操作符在两个操作项后面</p>\n<h4 id=\"后缀表达式的实现\"><a href=\"#后缀表达式的实现\" class=\"headerlink\" title=\"后缀表达式的实现\"></a>后缀表达式的实现</h4><p>利用栈 <code>stack</code> 和队列 <code>queue</code> 实现<strong>后缀表达式</strong><br><img src=\"/img/puml/suffix_express/suffix_express.svg\" alt=\"后缀表达式流程图\"></p>\n<p><strong>流程:</strong></p>\n<blockquote>\n<ol>\n<li>先把字符串转换为列表 比如 “1+2+3*4” 转换为 [“1”, “+”, “2”, “+”, “3”, “*”, “4”]</li>\n<li>上面的字符串列表转换为后缀表达式</li>\n<li>遍历后缀表达式进行计算</li>\n</ol>\n</blockquote>\n<p><code>computer.go</code></p>\n<pre><code class=\"golang\">package go_utils\n\nimport "strconv"\n\n// Computer 计算后缀表达式\nfunc Computer(expression string) float64 {\n mpnExpression := parse2mpn(expression)\n rpnExpression := parse2rpn(mpnExpression)\n resultStack := NewStack()\n for _, v := range rpnExpression {\n switch v {\n case "+", "-", "*", "/":\n v1, _ := resultStack.Pop().(float64)\n v2, _ := resultStack.Pop().(float64)\n switch v {\n case "+":\n resultStack.Push(v1 + v2)\n case "-":\n resultStack.Push(v2 - v1)\n case "*":\n resultStack.Push(v2 * v1)\n case "/":\n resultStack.Push(v2 / v1)\n }\n default:\n float, err := strconv.ParseFloat(v, 64)\n if err != nil {\n panic(err)\n }\n resultStack.Push(float)\n }\n }\n return resultStack.Pop().(float64)\n}\n\n// 解析中缀表达式数组\nfunc parse2mpn(express string) []string {\n //compile, _ := regexp.Compile("[\\\\d\\\\+\\\\-\\\\*\\\\/\\\\(\\\\)]")\n //return compile.FindAllString(express, -1)\n result := make([]string, 0)\n s := ""\n for i, v := range express {\n if v == 32 {\n continue\n }\n if v > 47 && v < 59 || v == 46 {\n s += string(v)\n if i == len(express)-1 {\n result = append(result, s)\n }\n } else {\n // +-*/ ( )\n if s != "" {\n result = append(result, s)\n s = ""\n }\n result = append(result, string(v))\n }\n }\n return result\n}\n\n// parse2rpn 解析中缀表达式-> 后缀表达式\nfunc parse2rpn(express []string) []string {\n stact := NewStack()\n rpnList := make([]string, 0, len(express))\n var s string\n for i := 0; i < len(express); i++ {\n s = express[i]\n switch s {\n case "+", "-":\n for {\n if stact.IsEmpty() || stact.Peek().(string) == "(" {\n stact.Push(s)\n break\n } else {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n }\n case "*", "/":\n for {\n if stact.IsEmpty() || (stact.Peek().(string) != "*" && stact.Peek().(string) != "/") {\n stact.Push(s)\n break\n } else {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n }\n\n case "(":\n stact.Push(s)\n\n case ")":\n\n for stact.Peek().(string) != "(" {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n stact.Pop()\n default:\n rpnList = append(rpnList, s)\n }\n }\n\n for stact.Size() > 0 {\n rpnList = append(rpnList, stact.Pop().(string))\n }\n return rpnList\n}\n</code></pre>\n"},{"title":"golang 并发赋值安全性","auto_excerpt":{"enable":true,"length":150},"date":"2021-09-30T11:10:48.000Z","_content":"\nGo 多协程并发的场景无处不在,并发对同一变量的赋值也是经常遇到。本文尝试探讨了 Go 中所有类型并发赋值的安全性。\n1. 由一条机器指令完成赋值的类型并发赋值是安全的,这些类型有:字节型,布尔型、整型、浮点型、字符型、指针、函数。\n2. 数组由一个或多个元素组成,大部分情况并发不安全。注意:当位宽不大于 64 位且是 2 的整数次幂(8,16,32,64),那么其并发赋值是安全的。\n3. struct 或底层是 struct 的类型并发赋值大部分情况并发不安全,这些类型有:复数、字符串、 数组、切片、字典、通道、接口。注意:当 struct 赋值时退化为单个字段由一个机器指令完成赋值时,并发赋值又是安全的。这种情况有:\n > 实部或虚部相同的复数的并发赋值;\n > 等长字符串的并发赋值;\n > 同长度同容量切片的并发赋值;\n > 同一种具体类型不同值并发赋给接口。\n\n","source":"_posts/golang/golang-并发赋值安全性.md","raw":"---\ntitle: golang 并发赋值安全性\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-09-30 19:10:48\ntags:\n- golang\n- concurrent\n\ncategories:\n- golang\n---\n\nGo 多协程并发的场景无处不在,并发对同一变量的赋值也是经常遇到。本文尝试探讨了 Go 中所有类型并发赋值的安全性。\n1. 由一条机器指令完成赋值的类型并发赋值是安全的,这些类型有:字节型,布尔型、整型、浮点型、字符型、指针、函数。\n2. 数组由一个或多个元素组成,大部分情况并发不安全。注意:当位宽不大于 64 位且是 2 的整数次幂(8,16,32,64),那么其并发赋值是安全的。\n3. struct 或底层是 struct 的类型并发赋值大部分情况并发不安全,这些类型有:复数、字符串、 数组、切片、字典、通道、接口。注意:当 struct 赋值时退化为单个字段由一个机器指令完成赋值时,并发赋值又是安全的。这种情况有:\n > 实部或虚部相同的复数的并发赋值;\n > 等长字符串的并发赋值;\n > 同长度同容量切片的并发赋值;\n > 同一种具体类型不同值并发赋给接口。\n\n","slug":"golang/golang-并发赋值安全性","published":1,"updated":"2022-11-01T02:42:47.276Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn50012iysy1fg69klu","content":"<p>Go 多协程并发的场景无处不在,并发对同一变量的赋值也是经常遇到。本文尝试探讨了 Go 中所有类型并发赋值的安全性。</p>\n<ol>\n<li>由一条机器指令完成赋值的类型并发赋值是安全的,这些类型有:字节型,布尔型、整型、浮点型、字符型、指针、函数。</li>\n<li>数组由一个或多个元素组成,大部分情况并发不安全。注意:当位宽不大于 64 位且是 2 的整数次幂(8,16,32,64),那么其并发赋值是安全的。</li>\n<li>struct 或底层是 struct 的类型并发赋值大部分情况并发不安全,这些类型有:复数、字符串、 数组、切片、字典、通道、接口。注意:当 struct 赋值时退化为单个字段由一个机器指令完成赋值时,并发赋值又是安全的。这种情况有:<blockquote>\n<p>实部或虚部相同的复数的并发赋值;<br>等长字符串的并发赋值;<br>同长度同容量切片的并发赋值;<br>同一种具体类型不同值并发赋给接口。</p>\n</blockquote>\n</li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<p>Go 多协程并发的场景无处不在,并发对同一变量的赋值也是经常遇到。本文尝试探讨了 Go 中所有类型并发赋值的安全性。</p>\n<ol>\n<li>由一条机器指令完成赋值的类型并发赋值是安全的,这些类型有:字节型,布尔型、整型、浮点型、字符型、指针、函数。</li>\n<li>数组由一个或多个元素组成,大部分情况并发不安全。注意:当位宽不大于 64 位且是 2 的整数次幂(8,16,32,64),那么其并发赋值是安全的。</li>\n<li>struct 或底层是 struct 的类型并发赋值大部分情况并发不安全,这些类型有:复数、字符串、 数组、切片、字典、通道、接口。注意:当 struct 赋值时退化为单个字段由一个机器指令完成赋值时,并发赋值又是安全的。这种情况有:<blockquote>\n<p>实部或虚部相同的复数的并发赋值;<br>等长字符串的并发赋值;<br>同长度同容量切片的并发赋值;<br>同一种具体类型不同值并发赋给接口。</p>\n</blockquote>\n</li>\n</ol>\n"},{"title":"gorm 中使用 json","date":"2019-06-11T08:55:56.000Z","_content":"\n### Json 类的实现\n\n```golang\npackage models\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"errors\"\n)\n\ntype JSON []byte\n\nfunc (j JSON) Value() (driver.Value, error) {\n\tif j.IsNull() {\n\t\treturn nil, nil\n\t}\n\treturn string(j), nil\n}\nfunc (j *JSON) Scan(value interface{}) error {\n\tif value == nil {\n\t\t*j = nil\n\t\treturn nil\n\t}\n\ts, ok := value.([]byte)\n\tif !ok {\n\t\terrors.New(\"Invalid Scan Source\")\n\t}\n\t*j = append((*j)[0:0], s...)\n\treturn nil\n}\nfunc (m JSON) MarshalJSON() ([]byte, error) {\n\tif m == nil {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn m, nil\n}\nfunc (m *JSON) UnmarshalJSON(data []byte) error {\n\tif m == nil {\n\t\treturn errors.New(\"null point exception\")\n\t}\n\t*m = append((*m)[0:0], data...)\n\treturn nil\n}\nfunc (j JSON) IsNull() bool {\n\treturn len(j) == 0 || string(j) == \"null\"\n}\nfunc (j JSON) Equals(j1 JSON) bool {\n\treturn bytes.Equal([]byte(j), []byte(j1))\n}\n\n```\n\n### 在自定义 Model 中使用 Json Type\n\n```golang\npackage models\ntype Model struct {\n ID int `gorm:\"primary_key\" json:\"id\"`\n CreatedAt int64 `json:\"createdAt\"`\n UpdatedAt int64 `json:\"updatedAt\"`\n Object JSON `sql:\"type:json\" json:\"object,omitempty\"`\n}\n```\n\n","source":"_posts/golang/gorm 中使用 json.md","raw":"---\ntitle: gorm 中使用 json\ndate: 2019-06-11 16:55:56\ntags:\n- golang\n- gorm\ncategories:\n- golang\n---\n\n### Json 类的实现\n\n```golang\npackage models\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"errors\"\n)\n\ntype JSON []byte\n\nfunc (j JSON) Value() (driver.Value, error) {\n\tif j.IsNull() {\n\t\treturn nil, nil\n\t}\n\treturn string(j), nil\n}\nfunc (j *JSON) Scan(value interface{}) error {\n\tif value == nil {\n\t\t*j = nil\n\t\treturn nil\n\t}\n\ts, ok := value.([]byte)\n\tif !ok {\n\t\terrors.New(\"Invalid Scan Source\")\n\t}\n\t*j = append((*j)[0:0], s...)\n\treturn nil\n}\nfunc (m JSON) MarshalJSON() ([]byte, error) {\n\tif m == nil {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn m, nil\n}\nfunc (m *JSON) UnmarshalJSON(data []byte) error {\n\tif m == nil {\n\t\treturn errors.New(\"null point exception\")\n\t}\n\t*m = append((*m)[0:0], data...)\n\treturn nil\n}\nfunc (j JSON) IsNull() bool {\n\treturn len(j) == 0 || string(j) == \"null\"\n}\nfunc (j JSON) Equals(j1 JSON) bool {\n\treturn bytes.Equal([]byte(j), []byte(j1))\n}\n\n```\n\n### 在自定义 Model 中使用 Json Type\n\n```golang\npackage models\ntype Model struct {\n ID int `gorm:\"primary_key\" json:\"id\"`\n CreatedAt int64 `json:\"createdAt\"`\n UpdatedAt int64 `json:\"updatedAt\"`\n Object JSON `sql:\"type:json\" json:\"object,omitempty\"`\n}\n```\n\n","slug":"golang/gorm 中使用 json","published":1,"updated":"2022-11-01T02:43:01.772Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn60017iysy49pi4h8o","content":"<h3 id=\"Json-类的实现\"><a href=\"#Json-类的实现\" class=\"headerlink\" title=\"Json 类的实现\"></a>Json 类的实现</h3><pre><code class=\"golang\">package models\n\nimport (\n "bytes"\n "database/sql/driver"\n "errors"\n)\n\ntype JSON []byte\n\nfunc (j JSON) Value() (driver.Value, error) {\n if j.IsNull() {\n return nil, nil\n }\n return string(j), nil\n}\nfunc (j *JSON) Scan(value interface{}) error {\n if value == nil {\n *j = nil\n return nil\n }\n s, ok := value.([]byte)\n if !ok {\n errors.New("Invalid Scan Source")\n }\n *j = append((*j)[0:0], s...)\n return nil\n}\nfunc (m JSON) MarshalJSON() ([]byte, error) {\n if m == nil {\n return []byte("null"), nil\n }\n return m, nil\n}\nfunc (m *JSON) UnmarshalJSON(data []byte) error {\n if m == nil {\n return errors.New("null point exception")\n }\n *m = append((*m)[0:0], data...)\n return nil\n}\nfunc (j JSON) IsNull() bool {\n return len(j) == 0 || string(j) == "null"\n}\nfunc (j JSON) Equals(j1 JSON) bool {\n return bytes.Equal([]byte(j), []byte(j1))\n}\n</code></pre>\n<h3 id=\"在自定义-Model-中使用-Json-Type\"><a href=\"#在自定义-Model-中使用-Json-Type\" class=\"headerlink\" title=\"在自定义 Model 中使用 Json Type\"></a>在自定义 Model 中使用 Json Type</h3><pre><code class=\"golang\">package models\ntype Model struct {\n ID int `gorm:"primary_key" json:"id"`\n CreatedAt int64 `json:"createdAt"`\n UpdatedAt int64 `json:"updatedAt"`\n Object JSON `sql:"type:json" json:"object,omitempty"`\n}\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"Json-类的实现\"><a href=\"#Json-类的实现\" class=\"headerlink\" title=\"Json 类的实现\"></a>Json 类的实现</h3><pre><code class=\"golang\">package models\n\nimport (\n "bytes"\n "database/sql/driver"\n "errors"\n)\n\ntype JSON []byte\n\nfunc (j JSON) Value() (driver.Value, error) {\n if j.IsNull() {\n return nil, nil\n }\n return string(j), nil\n}\nfunc (j *JSON) Scan(value interface{}) error {\n if value == nil {\n *j = nil\n return nil\n }\n s, ok := value.([]byte)\n if !ok {\n errors.New("Invalid Scan Source")\n }\n *j = append((*j)[0:0], s...)\n return nil\n}\nfunc (m JSON) MarshalJSON() ([]byte, error) {\n if m == nil {\n return []byte("null"), nil\n }\n return m, nil\n}\nfunc (m *JSON) UnmarshalJSON(data []byte) error {\n if m == nil {\n return errors.New("null point exception")\n }\n *m = append((*m)[0:0], data...)\n return nil\n}\nfunc (j JSON) IsNull() bool {\n return len(j) == 0 || string(j) == "null"\n}\nfunc (j JSON) Equals(j1 JSON) bool {\n return bytes.Equal([]byte(j), []byte(j1))\n}\n</code></pre>\n<h3 id=\"在自定义-Model-中使用-Json-Type\"><a href=\"#在自定义-Model-中使用-Json-Type\" class=\"headerlink\" title=\"在自定义 Model 中使用 Json Type\"></a>在自定义 Model 中使用 Json Type</h3><pre><code class=\"golang\">package models\ntype Model struct {\n ID int `gorm:"primary_key" json:"id"`\n CreatedAt int64 `json:"createdAt"`\n UpdatedAt int64 `json:"updatedAt"`\n Object JSON `sql:"type:json" json:"object,omitempty"`\n}\n</code></pre>\n"},{"title":"sync.Pool 对象池使用问题记录","auto_excerpt":{"enable":true,"length":150},"date":"2021-09-30T03:07:11.000Z","_content":"\n### sync.Pool 对象重置问题\n\n1. 方式1: get -> operate-obj -> reset -> put\n\n > 问题:get 的对象在操作时可能没有被reset 到\n\n2. 方式2: get -> reset -> operate-obj -> put\n\n > 获取对象后,先 reset 在使用是没有问题的\n >\n > gin 就是这样使用的\n >\n > ```go\n > func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n > \tc := engine.pool.Get().(*Context)\n > \tc.writermem.reset(w)\n > \tc.Request = req\n > \tc.reset() // 先reset \n > \n > \tengine.handleHTTPRequest(c) // 再使用\n > \n > \tengine.pool.Put(c) // 使用之后直接 put\n > }\n > \n > ```\n\n**`cat test_syncpool.go`**\n\n```go\npackage test\n\nimport (\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n)\n\ntype A struct {\n\tName string\n}\n\nfunc (a *A) putValue(i int) {\n\ta.Name = \"A+\" + strconv.Itoa(i)\n}\n\nfunc (a *A) reset() {\n\ta.Name = \"\"\n}\n\nfunc Benchmark_A(b *testing.B) {\n\tpool := &sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn new(A)\n\t\t},\n\t}\n pool2 := &sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn new(A)\n\t\t},\n\t}\n\twaitGroup := sync.WaitGroup{}\n\n\twaitGroup.Add(1)\n\tgo func() {\n\t\tdefer waitGroup.Done()\n\t\tfor i := 0; i < b.N; i++ {\n\n\t\t\ta := pool.Get().(*A)\n\n\t\t\t//a.reset() // 方式2 没有问题\n\n\t\t\tif a.Name != \"\" {\n\t\t\t\tpanic(\"name not empty\")\n\t\t\t}\n\t\t\ta.putValue(i) // a.Name = \"A+\" + strconv.Itoa(i)\n\t\t\tif a.Name == \"\" {\n\t\t\t\tpanic(\"name is empty\")\n\t\t\t}\n\n\t\t\ta.reset() // 方式1 有问题的方式\n\n\t\t\tpool.Put(a)\n\t\t}\n\t}()\n\n\twaitGroup.Add(1)\n\tgo func() {\n\t\tdefer waitGroup.Done()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\ta := pool.Get().(*A)\n\t\t\ta.reset()\n\t\t\tif a.Name != \"\" {\n\t\t\t\tpanic(\"name not empty\")\n\t\t\t}\n\t\t\ta.putValue(i)\n\t\t\tif a.Name == \"\" {\n\t\t\t\tpanic(\"name not empty\")\n\t\t\t}\n\t\t\tpool.Put(a)\n\t\t}\n\t}()\n\twaitGroup.Wait()\n}\n\n```\n\n修改点\n\n```go\nfunc (pool *taskPool) get() *Task {\n task := pool.bp.Get().(*Task)\n task.Reset()\n return task\n}\n\nfunc (pool *taskPool) put(obj *Task) {\n pool.bp.Put(obj)\n}\n```\n\n\n\n执行用例 BenchmarkTimeWheel\n\n```shell\n➜ go-timewheel (master) ✗ go test -v -bench . -test.bench='TimeWheel$' -test.run ^$\ngoos: darwin\ngoarch: amd64\npkg: github.com/rfyiamcool/go-timewheel\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmarkAdd-12 1990670 1090 ns/op\nBenchmarkTimeWheel/wheel-N-1m-12 1000000 1049 ns/op\nBenchmarkTimeWheel/wheel-N-5m-12 1223064 1061 ns/op\nBenchmarkTimeWheel/wheel-N-10m-12 1000000 1253 ns/op\nPASS\nok github.com/rfyiamcool/go-timewheel 100.504s\n```\n\n\n\n问题点\n\ntask.callback 有值,但是 task.callback == nil 分支却执行了,断点在 task.callback() 时去执行 task.callback 确实是有值的\n\n![image-20210930143647762](/img/syncpool/image-20210930143647762.png)\n\nTask.stop = true\n\n![image-20210930145240966](/img/syncpool/image-20210930145240966.png)\n\n![image-20210930145657424](/img/syncpool/image-20210930145657424.png)\n","source":"_posts/golang/sync-Pool-对象池使用问题记录.md","raw":"---\ntitle: sync.Pool 对象池使用问题记录\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-09-30 11:07:11\ntags:\n\n- golang\n- sync.Pool\n- concurrent\n\ncategories:\n- golang\n---\n\n### sync.Pool 对象重置问题\n\n1. 方式1: get -> operate-obj -> reset -> put\n\n > 问题:get 的对象在操作时可能没有被reset 到\n\n2. 方式2: get -> reset -> operate-obj -> put\n\n > 获取对象后,先 reset 在使用是没有问题的\n >\n > gin 就是这样使用的\n >\n > ```go\n > func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n > \tc := engine.pool.Get().(*Context)\n > \tc.writermem.reset(w)\n > \tc.Request = req\n > \tc.reset() // 先reset \n > \n > \tengine.handleHTTPRequest(c) // 再使用\n > \n > \tengine.pool.Put(c) // 使用之后直接 put\n > }\n > \n > ```\n\n**`cat test_syncpool.go`**\n\n```go\npackage test\n\nimport (\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n)\n\ntype A struct {\n\tName string\n}\n\nfunc (a *A) putValue(i int) {\n\ta.Name = \"A+\" + strconv.Itoa(i)\n}\n\nfunc (a *A) reset() {\n\ta.Name = \"\"\n}\n\nfunc Benchmark_A(b *testing.B) {\n\tpool := &sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn new(A)\n\t\t},\n\t}\n pool2 := &sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn new(A)\n\t\t},\n\t}\n\twaitGroup := sync.WaitGroup{}\n\n\twaitGroup.Add(1)\n\tgo func() {\n\t\tdefer waitGroup.Done()\n\t\tfor i := 0; i < b.N; i++ {\n\n\t\t\ta := pool.Get().(*A)\n\n\t\t\t//a.reset() // 方式2 没有问题\n\n\t\t\tif a.Name != \"\" {\n\t\t\t\tpanic(\"name not empty\")\n\t\t\t}\n\t\t\ta.putValue(i) // a.Name = \"A+\" + strconv.Itoa(i)\n\t\t\tif a.Name == \"\" {\n\t\t\t\tpanic(\"name is empty\")\n\t\t\t}\n\n\t\t\ta.reset() // 方式1 有问题的方式\n\n\t\t\tpool.Put(a)\n\t\t}\n\t}()\n\n\twaitGroup.Add(1)\n\tgo func() {\n\t\tdefer waitGroup.Done()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\ta := pool.Get().(*A)\n\t\t\ta.reset()\n\t\t\tif a.Name != \"\" {\n\t\t\t\tpanic(\"name not empty\")\n\t\t\t}\n\t\t\ta.putValue(i)\n\t\t\tif a.Name == \"\" {\n\t\t\t\tpanic(\"name not empty\")\n\t\t\t}\n\t\t\tpool.Put(a)\n\t\t}\n\t}()\n\twaitGroup.Wait()\n}\n\n```\n\n修改点\n\n```go\nfunc (pool *taskPool) get() *Task {\n task := pool.bp.Get().(*Task)\n task.Reset()\n return task\n}\n\nfunc (pool *taskPool) put(obj *Task) {\n pool.bp.Put(obj)\n}\n```\n\n\n\n执行用例 BenchmarkTimeWheel\n\n```shell\n➜ go-timewheel (master) ✗ go test -v -bench . -test.bench='TimeWheel$' -test.run ^$\ngoos: darwin\ngoarch: amd64\npkg: github.com/rfyiamcool/go-timewheel\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmarkAdd-12 1990670 1090 ns/op\nBenchmarkTimeWheel/wheel-N-1m-12 1000000 1049 ns/op\nBenchmarkTimeWheel/wheel-N-5m-12 1223064 1061 ns/op\nBenchmarkTimeWheel/wheel-N-10m-12 1000000 1253 ns/op\nPASS\nok github.com/rfyiamcool/go-timewheel 100.504s\n```\n\n\n\n问题点\n\ntask.callback 有值,但是 task.callback == nil 分支却执行了,断点在 task.callback() 时去执行 task.callback 确实是有值的\n\n![image-20210930143647762](/img/syncpool/image-20210930143647762.png)\n\nTask.stop = true\n\n![image-20210930145240966](/img/syncpool/image-20210930145240966.png)\n\n![image-20210930145657424](/img/syncpool/image-20210930145657424.png)\n","slug":"golang/sync-Pool-对象池使用问题记录","published":1,"updated":"2022-10-31T09:40:57.113Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn60019iysy16es8yu7","content":"<h3 id=\"sync-Pool-对象重置问题\"><a href=\"#sync-Pool-对象重置问题\" class=\"headerlink\" title=\"sync.Pool 对象重置问题\"></a>sync.Pool 对象重置问题</h3><ol>\n<li><p>方式1: get -> operate-obj -> reset -> put</p>\n<blockquote>\n<p>问题:get 的对象在操作时可能没有被reset 到</p>\n</blockquote>\n</li>\n<li><p>方式2: get -> reset -> operate-obj -> put</p>\n<blockquote>\n<p>获取对象后,先 reset 在使用是没有问题的</p>\n<p>gin 就是这样使用的</p>\n<pre><code class=\"go\">func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n c := engine.pool.Get().(*Context)\n c.writermem.reset(w)\n c.Request = req\n c.reset() // 先reset \n\n engine.handleHTTPRequest(c) // 再使用\n\n engine.pool.Put(c) // 使用之后直接 put\n}\n</code></pre>\n</blockquote>\n</li>\n</ol>\n<p><strong><code>cat test_syncpool.go</code></strong></p>\n<pre><code class=\"go\">package test\n\nimport (\n "strconv"\n "sync"\n "testing"\n)\n\ntype A struct {\n Name string\n}\n\nfunc (a *A) putValue(i int) {\n a.Name = "A+" + strconv.Itoa(i)\n}\n\nfunc (a *A) reset() {\n a.Name = ""\n}\n\nfunc Benchmark_A(b *testing.B) {\n pool := &sync.Pool{\n New: func() interface{} {\n return new(A)\n },\n }\n pool2 := &sync.Pool{\n New: func() interface{} {\n return new(A)\n },\n }\n waitGroup := sync.WaitGroup{}\n\n waitGroup.Add(1)\n go func() {\n defer waitGroup.Done()\n for i := 0; i < b.N; i++ {\n\n a := pool.Get().(*A)\n\n //a.reset() // 方式2 没有问题\n\n if a.Name != "" {\n panic("name not empty")\n }\n a.putValue(i) // a.Name = "A+" + strconv.Itoa(i)\n if a.Name == "" {\n panic("name is empty")\n }\n\n a.reset() // 方式1 有问题的方式\n\n pool.Put(a)\n }\n }()\n\n waitGroup.Add(1)\n go func() {\n defer waitGroup.Done()\n for i := 0; i < b.N; i++ {\n a := pool.Get().(*A)\n a.reset()\n if a.Name != "" {\n panic("name not empty")\n }\n a.putValue(i)\n if a.Name == "" {\n panic("name not empty")\n }\n pool.Put(a)\n }\n }()\n waitGroup.Wait()\n}\n</code></pre>\n<p>修改点</p>\n<pre><code class=\"go\">func (pool *taskPool) get() *Task {\n task := pool.bp.Get().(*Task)\n task.Reset()\n return task\n}\n\nfunc (pool *taskPool) put(obj *Task) {\n pool.bp.Put(obj)\n}\n</code></pre>\n<p>执行用例 BenchmarkTimeWheel</p>\n<pre><code class=\"shell\">➜ go-timewheel (master) ✗ go test -v -bench . -test.bench='TimeWheel$' -test.run ^$\ngoos: darwin\ngoarch: amd64\npkg: github.com/rfyiamcool/go-timewheel\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmarkAdd-12 1990670 1090 ns/op\nBenchmarkTimeWheel/wheel-N-1m-12 1000000 1049 ns/op\nBenchmarkTimeWheel/wheel-N-5m-12 1223064 1061 ns/op\nBenchmarkTimeWheel/wheel-N-10m-12 1000000 1253 ns/op\nPASS\nok github.com/rfyiamcool/go-timewheel 100.504s\n</code></pre>\n<p>问题点</p>\n<p>task.callback 有值,但是 task.callback == nil 分支却执行了,断点在 task.callback() 时去执行 task.callback 确实是有值的</p>\n<p><img src=\"/img/syncpool/image-20210930143647762.png\" alt=\"image-20210930143647762\"></p>\n<p>Task.stop = true</p>\n<p><img src=\"/img/syncpool/image-20210930145240966.png\" alt=\"image-20210930145240966\"></p>\n<p><img src=\"/img/syncpool/image-20210930145657424.png\" alt=\"image-20210930145657424\"></p>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"sync-Pool-对象重置问题\"><a href=\"#sync-Pool-对象重置问题\" class=\"headerlink\" title=\"sync.Pool 对象重置问题\"></a>sync.Pool 对象重置问题</h3><ol>\n<li><p>方式1: get -> operate-obj -> reset -> put</p>\n<blockquote>\n<p>问题:get 的对象在操作时可能没有被reset 到</p>\n</blockquote>\n</li>\n<li><p>方式2: get -> reset -> operate-obj -> put</p>\n<blockquote>\n<p>获取对象后,先 reset 在使用是没有问题的</p>\n<p>gin 就是这样使用的</p>\n<pre><code class=\"go\">func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n c := engine.pool.Get().(*Context)\n c.writermem.reset(w)\n c.Request = req\n c.reset() // 先reset \n\n engine.handleHTTPRequest(c) // 再使用\n\n engine.pool.Put(c) // 使用之后直接 put\n}\n</code></pre>\n</blockquote>\n</li>\n</ol>\n<p><strong><code>cat test_syncpool.go</code></strong></p>\n<pre><code class=\"go\">package test\n\nimport (\n "strconv"\n "sync"\n "testing"\n)\n\ntype A struct {\n Name string\n}\n\nfunc (a *A) putValue(i int) {\n a.Name = "A+" + strconv.Itoa(i)\n}\n\nfunc (a *A) reset() {\n a.Name = ""\n}\n\nfunc Benchmark_A(b *testing.B) {\n pool := &sync.Pool{\n New: func() interface{} {\n return new(A)\n },\n }\n pool2 := &sync.Pool{\n New: func() interface{} {\n return new(A)\n },\n }\n waitGroup := sync.WaitGroup{}\n\n waitGroup.Add(1)\n go func() {\n defer waitGroup.Done()\n for i := 0; i < b.N; i++ {\n\n a := pool.Get().(*A)\n\n //a.reset() // 方式2 没有问题\n\n if a.Name != "" {\n panic("name not empty")\n }\n a.putValue(i) // a.Name = "A+" + strconv.Itoa(i)\n if a.Name == "" {\n panic("name is empty")\n }\n\n a.reset() // 方式1 有问题的方式\n\n pool.Put(a)\n }\n }()\n\n waitGroup.Add(1)\n go func() {\n defer waitGroup.Done()\n for i := 0; i < b.N; i++ {\n a := pool.Get().(*A)\n a.reset()\n if a.Name != "" {\n panic("name not empty")\n }\n a.putValue(i)\n if a.Name == "" {\n panic("name not empty")\n }\n pool.Put(a)\n }\n }()\n waitGroup.Wait()\n}\n</code></pre>\n<p>修改点</p>\n<pre><code class=\"go\">func (pool *taskPool) get() *Task {\n task := pool.bp.Get().(*Task)\n task.Reset()\n return task\n}\n\nfunc (pool *taskPool) put(obj *Task) {\n pool.bp.Put(obj)\n}\n</code></pre>\n<p>执行用例 BenchmarkTimeWheel</p>\n<pre><code class=\"shell\">➜ go-timewheel (master) ✗ go test -v -bench . -test.bench='TimeWheel$' -test.run ^$\ngoos: darwin\ngoarch: amd64\npkg: github.com/rfyiamcool/go-timewheel\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmarkAdd-12 1990670 1090 ns/op\nBenchmarkTimeWheel/wheel-N-1m-12 1000000 1049 ns/op\nBenchmarkTimeWheel/wheel-N-5m-12 1223064 1061 ns/op\nBenchmarkTimeWheel/wheel-N-10m-12 1000000 1253 ns/op\nPASS\nok github.com/rfyiamcool/go-timewheel 100.504s\n</code></pre>\n<p>问题点</p>\n<p>task.callback 有值,但是 task.callback == nil 分支却执行了,断点在 task.callback() 时去执行 task.callback 确实是有值的</p>\n<p><img src=\"/img/syncpool/image-20210930143647762.png\" alt=\"image-20210930143647762\"></p>\n<p>Task.stop = true</p>\n<p><img src=\"/img/syncpool/image-20210930145240966.png\" alt=\"image-20210930145240966\"></p>\n<p><img src=\"/img/syncpool/image-20210930145657424.png\" alt=\"image-20210930145657424\"></p>\n"},{"title":"java mapstruct 详解","auto_excerpt":{"enable":true,"length":150},"date":"2021-10-25T01:58:50.000Z","_content":"\n官网:[https://mapstruct.org/](https://mapstruct.org/)\n\nGitHub: [https://github.com/mapstruct/mapstruct](https://github.com/mapstruct/mapstruct)\n\n### 关于 BeanUtil\n\n平时我经常使用Hutool中的BeanUtil类来实现对象转换,用多了之后就发现有些缺点:\n\n- 对象属性映射使用反射来实现,性能比较低;\n- 对于不同名称或不同类型的属性无法转换,还得单独写Getter、Setter方法;\n- 对于嵌套的子对象也需要转换的情况,也得自行处理;\n- 集合对象转换时,得使用循环,一个个拷贝。\n\n对于这些不足,MapStruct都能解决,不愧为一款功能强大的对象映射工具!\n\n### IDEA插件支持\n\n![](/img/mapstruct-plugins.png)\n\n### 项目集成\n\n在SpingBoot中集成MapStruct非常简单,仅续添加如下两个依赖即可,这里使用的是`1.4.2.Final`版本\n\n```xml\n<dependency>\n <!--MapStruct相关依赖-->\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct</artifactId>\n <version>${mapstruct.version}</version>\n </dependency>\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct-processor</artifactId>\n <version>${mapstruct.version}</version>\n <scope>compile</scope>\n </dependency>\n</dependencies\n```\n\n","source":"_posts/java/java-mapstruct-详解.md","raw":"---\ntitle: java mapstruct 详解\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-10-25 09:58:50\ntags:\n- java\n- mapstruct\n- plugins\ncategories:\n- java\n---\n\n官网:[https://mapstruct.org/](https://mapstruct.org/)\n\nGitHub: [https://github.com/mapstruct/mapstruct](https://github.com/mapstruct/mapstruct)\n\n### 关于 BeanUtil\n\n平时我经常使用Hutool中的BeanUtil类来实现对象转换,用多了之后就发现有些缺点:\n\n- 对象属性映射使用反射来实现,性能比较低;\n- 对于不同名称或不同类型的属性无法转换,还得单独写Getter、Setter方法;\n- 对于嵌套的子对象也需要转换的情况,也得自行处理;\n- 集合对象转换时,得使用循环,一个个拷贝。\n\n对于这些不足,MapStruct都能解决,不愧为一款功能强大的对象映射工具!\n\n### IDEA插件支持\n\n![](/img/mapstruct-plugins.png)\n\n### 项目集成\n\n在SpingBoot中集成MapStruct非常简单,仅续添加如下两个依赖即可,这里使用的是`1.4.2.Final`版本\n\n```xml\n<dependency>\n <!--MapStruct相关依赖-->\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct</artifactId>\n <version>${mapstruct.version}</version>\n </dependency>\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct-processor</artifactId>\n <version>${mapstruct.version}</version>\n <scope>compile</scope>\n </dependency>\n</dependencies\n```\n\n","slug":"java/java-mapstruct-详解","published":1,"updated":"2022-11-02T08:31:55.892Z","_id":"cl9zdnpn7001ciysy1cx5cazq","comments":1,"layout":"post","photos":[],"link":"","content":"<p>官网:<a href=\"https://mapstruct.org/\">https://mapstruct.org/</a></p>\n<p>GitHub: <a href=\"https://github.com/mapstruct/mapstruct\">https://github.com/mapstruct/mapstruct</a></p>\n<h3 id=\"关于-BeanUtil\"><a href=\"#关于-BeanUtil\" class=\"headerlink\" title=\"关于 BeanUtil\"></a>关于 BeanUtil</h3><p>平时我经常使用Hutool中的BeanUtil类来实现对象转换,用多了之后就发现有些缺点:</p>\n<ul>\n<li>对象属性映射使用反射来实现,性能比较低;</li>\n<li>对于不同名称或不同类型的属性无法转换,还得单独写Getter、Setter方法;</li>\n<li>对于嵌套的子对象也需要转换的情况,也得自行处理;</li>\n<li>集合对象转换时,得使用循环,一个个拷贝。</li>\n</ul>\n<p>对于这些不足,MapStruct都能解决,不愧为一款功能强大的对象映射工具!</p>\n<h3 id=\"IDEA插件支持\"><a href=\"#IDEA插件支持\" class=\"headerlink\" title=\"IDEA插件支持\"></a>IDEA插件支持</h3><p><img src=\"/img/mapstruct-plugins.png\"></p>\n<h3 id=\"项目集成\"><a href=\"#项目集成\" class=\"headerlink\" title=\"项目集成\"></a>项目集成</h3><p>在SpingBoot中集成MapStruct非常简单,仅续添加如下两个依赖即可,这里使用的是<code>1.4.2.Final</code>版本</p>\n<pre><code class=\"xml\"><dependency>\n <!--MapStruct相关依赖-->\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct</artifactId>\n <version>${mapstruct.version}</version>\n </dependency>\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct-processor</artifactId>\n <version>${mapstruct.version}</version>\n <scope>compile</scope>\n </dependency>\n</dependencies\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p>官网:<a href=\"https://mapstruct.org/\">https://mapstruct.org/</a></p>\n<p>GitHub: <a href=\"https://github.com/mapstruct/mapstruct\">https://github.com/mapstruct/mapstruct</a></p>\n<h3 id=\"关于-BeanUtil\"><a href=\"#关于-BeanUtil\" class=\"headerlink\" title=\"关于 BeanUtil\"></a>关于 BeanUtil</h3><p>平时我经常使用Hutool中的BeanUtil类来实现对象转换,用多了之后就发现有些缺点:</p>\n<ul>\n<li>对象属性映射使用反射来实现,性能比较低;</li>\n<li>对于不同名称或不同类型的属性无法转换,还得单独写Getter、Setter方法;</li>\n<li>对于嵌套的子对象也需要转换的情况,也得自行处理;</li>\n<li>集合对象转换时,得使用循环,一个个拷贝。</li>\n</ul>\n<p>对于这些不足,MapStruct都能解决,不愧为一款功能强大的对象映射工具!</p>\n<h3 id=\"IDEA插件支持\"><a href=\"#IDEA插件支持\" class=\"headerlink\" title=\"IDEA插件支持\"></a>IDEA插件支持</h3><p><img src=\"/img/mapstruct-plugins.png\"></p>\n<h3 id=\"项目集成\"><a href=\"#项目集成\" class=\"headerlink\" title=\"项目集成\"></a>项目集成</h3><p>在SpingBoot中集成MapStruct非常简单,仅续添加如下两个依赖即可,这里使用的是<code>1.4.2.Final</code>版本</p>\n<pre><code class=\"xml\"><dependency>\n <!--MapStruct相关依赖-->\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct</artifactId>\n <version>${mapstruct.version}</version>\n </dependency>\n <dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct-processor</artifactId>\n <version>${mapstruct.version}</version>\n <scope>compile</scope>\n </dependency>\n</dependencies\n</code></pre>\n"},{"title":"maven template","auto_excerpt":{"enable":true,"length":150},"date":"2021-11-01T16:00:00.000Z","_content":"\n\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-parent</artifactId>\n <version>2.6.11</version>\n <relativePath/> <!-- lookup parent from repository -->\n </parent>\n <groupId>com.example</groupId>\n <artifactId>spring-openfeign-demo</artifactId>\n <version>0.0.1-SNAPSHOT</version>\n <name>spring-openfeign-demo</name>\n <description>spring-openfeign-demo</description>\n <properties>\n <java.version>1.8</java.version>\n </properties>\n\n <dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-dependencies</artifactId>\n <scope>import</scope>\n <type>pom</type>\n <version>2021.0.0</version>\n </dependency>\n </dependencies>\n </dependencyManagement>\n\n <dependencies>\n <dependency>\n <!-- mybatis-plus -->\n <dependency>\n <groupId>com.baomidou</groupId>\n <artifactId>mybatis-plus-boot-starter</artifactId>\n <version>${mybatis-plus.version}</version>\n </dependency>\n <!-- redisson -->\n <dependency>\n <groupId>org.redisson</groupId>\n <artifactId>redisson-spring-boot-starter</artifactId>\n <version>${redisson.version}</version>\n </dependency>\n\n\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n </dependency>\n\n <dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-test</artifactId>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-openfeign</artifactId>\n </dependency>\n <!-- <dependency>-->\n <!-- <groupId>org.springframework.cloud</groupId>-->\n <!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>-->\n <!-- </dependency>-->\n\n <dependency>\n <groupId>cn.hutool</groupId>\n <artifactId>hutool-all</artifactId>\n </dependency>\n\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-classic</artifactId>\n </dependency>\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-core</artifactId>\n </dependency>\n <dependency>\n <groupId>org.projectlombok</groupId>\n <artifactId>lombok</artifactId>\n </dependency>\n </dependencies>\n\n\n <repositories>\n <repository>\n <id>central</id>\n <name>aliyun maven</name>\n <url>https://maven.aliyun.com/nexus/content/groups/public/</url>\n <layout>default</layout>\n <!-- 是否开启发布版构件下载 -->\n <releases>\n <enabled>true</enabled>\n </releases>\n <!-- 是否开启快照版构件下载 -->\n <snapshots>\n <enabled>true</enabled>\n </snapshots>\n </repository>\n </repositories>\n <build>\n <plugins>\n <plugin>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-maven-plugin</artifactId>\n </plugin>\n </plugins>\n </build>\n\n</project>\n\n\n```","source":"_posts/java/maven-template.md","raw":"---\ntitle: maven template\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-11-02\ntags:\n- java\n- maven\ncategories:\n- java\n---\n\n\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-parent</artifactId>\n <version>2.6.11</version>\n <relativePath/> <!-- lookup parent from repository -->\n </parent>\n <groupId>com.example</groupId>\n <artifactId>spring-openfeign-demo</artifactId>\n <version>0.0.1-SNAPSHOT</version>\n <name>spring-openfeign-demo</name>\n <description>spring-openfeign-demo</description>\n <properties>\n <java.version>1.8</java.version>\n </properties>\n\n <dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-dependencies</artifactId>\n <scope>import</scope>\n <type>pom</type>\n <version>2021.0.0</version>\n </dependency>\n </dependencies>\n </dependencyManagement>\n\n <dependencies>\n <dependency>\n <!-- mybatis-plus -->\n <dependency>\n <groupId>com.baomidou</groupId>\n <artifactId>mybatis-plus-boot-starter</artifactId>\n <version>${mybatis-plus.version}</version>\n </dependency>\n <!-- redisson -->\n <dependency>\n <groupId>org.redisson</groupId>\n <artifactId>redisson-spring-boot-starter</artifactId>\n <version>${redisson.version}</version>\n </dependency>\n\n\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n </dependency>\n\n <dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-test</artifactId>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-openfeign</artifactId>\n </dependency>\n <!-- <dependency>-->\n <!-- <groupId>org.springframework.cloud</groupId>-->\n <!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>-->\n <!-- </dependency>-->\n\n <dependency>\n <groupId>cn.hutool</groupId>\n <artifactId>hutool-all</artifactId>\n </dependency>\n\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-classic</artifactId>\n </dependency>\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-core</artifactId>\n </dependency>\n <dependency>\n <groupId>org.projectlombok</groupId>\n <artifactId>lombok</artifactId>\n </dependency>\n </dependencies>\n\n\n <repositories>\n <repository>\n <id>central</id>\n <name>aliyun maven</name>\n <url>https://maven.aliyun.com/nexus/content/groups/public/</url>\n <layout>default</layout>\n <!-- 是否开启发布版构件下载 -->\n <releases>\n <enabled>true</enabled>\n </releases>\n <!-- 是否开启快照版构件下载 -->\n <snapshots>\n <enabled>true</enabled>\n </snapshots>\n </repository>\n </repositories>\n <build>\n <plugins>\n <plugin>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-maven-plugin</artifactId>\n </plugin>\n </plugins>\n </build>\n\n</project>\n\n\n```","slug":"java/maven-template","published":1,"updated":"2022-11-02T08:32:04.176Z","_id":"cl9zdnpn8001giysybetvfzey","comments":1,"layout":"post","photos":[],"link":"","content":"<pre><code class=\"xml\"><?xml version="1.0" encoding="UTF-8"?>\n<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-parent</artifactId>\n <version>2.6.11</version>\n <relativePath/> <!-- lookup parent from repository -->\n </parent>\n <groupId>com.example</groupId>\n <artifactId>spring-openfeign-demo</artifactId>\n <version>0.0.1-SNAPSHOT</version>\n <name>spring-openfeign-demo</name>\n <description>spring-openfeign-demo</description>\n <properties>\n <java.version>1.8</java.version>\n </properties>\n\n <dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-dependencies</artifactId>\n <scope>import</scope>\n <type>pom</type>\n <version>2021.0.0</version>\n </dependency>\n </dependencies>\n </dependencyManagement>\n\n <dependencies>\n <dependency>\n <!-- mybatis-plus -->\n <dependency>\n <groupId>com.baomidou</groupId>\n <artifactId>mybatis-plus-boot-starter</artifactId>\n <version>${mybatis-plus.version}</version>\n </dependency>\n <!-- redisson -->\n <dependency>\n <groupId>org.redisson</groupId>\n <artifactId>redisson-spring-boot-starter</artifactId>\n <version>${redisson.version}</version>\n </dependency>\n\n\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n </dependency>\n\n <dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-test</artifactId>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-openfeign</artifactId>\n </dependency>\n <!-- <dependency>-->\n <!-- <groupId>org.springframework.cloud</groupId>-->\n <!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>-->\n <!-- </dependency>-->\n\n <dependency>\n <groupId>cn.hutool</groupId>\n <artifactId>hutool-all</artifactId>\n </dependency>\n\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-classic</artifactId>\n </dependency>\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-core</artifactId>\n </dependency>\n <dependency>\n <groupId>org.projectlombok</groupId>\n <artifactId>lombok</artifactId>\n </dependency>\n </dependencies>\n\n\n <repositories>\n <repository>\n <id>central</id>\n <name>aliyun maven</name>\n <url>https://maven.aliyun.com/nexus/content/groups/public/</url>\n <layout>default</layout>\n <!-- 是否开启发布版构件下载 -->\n <releases>\n <enabled>true</enabled>\n </releases>\n <!-- 是否开启快照版构件下载 -->\n <snapshots>\n <enabled>true</enabled>\n </snapshots>\n </repository>\n </repositories>\n <build>\n <plugins>\n <plugin>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-maven-plugin</artifactId>\n </plugin>\n </plugins>\n </build>\n\n</project>\n\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<pre><code class=\"xml\"><?xml version="1.0" encoding="UTF-8"?>\n<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">\n <modelVersion>4.0.0</modelVersion>\n <parent>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-parent</artifactId>\n <version>2.6.11</version>\n <relativePath/> <!-- lookup parent from repository -->\n </parent>\n <groupId>com.example</groupId>\n <artifactId>spring-openfeign-demo</artifactId>\n <version>0.0.1-SNAPSHOT</version>\n <name>spring-openfeign-demo</name>\n <description>spring-openfeign-demo</description>\n <properties>\n <java.version>1.8</java.version>\n </properties>\n\n <dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-dependencies</artifactId>\n <scope>import</scope>\n <type>pom</type>\n <version>2021.0.0</version>\n </dependency>\n </dependencies>\n </dependencyManagement>\n\n <dependencies>\n <dependency>\n <!-- mybatis-plus -->\n <dependency>\n <groupId>com.baomidou</groupId>\n <artifactId>mybatis-plus-boot-starter</artifactId>\n <version>${mybatis-plus.version}</version>\n </dependency>\n <!-- redisson -->\n <dependency>\n <groupId>org.redisson</groupId>\n <artifactId>redisson-spring-boot-starter</artifactId>\n <version>${redisson.version}</version>\n </dependency>\n\n\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n </dependency>\n\n <dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-test</artifactId>\n <scope>test</scope>\n </dependency>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-openfeign</artifactId>\n </dependency>\n <!-- <dependency>-->\n <!-- <groupId>org.springframework.cloud</groupId>-->\n <!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>-->\n <!-- </dependency>-->\n\n <dependency>\n <groupId>cn.hutool</groupId>\n <artifactId>hutool-all</artifactId>\n </dependency>\n\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-classic</artifactId>\n </dependency>\n <dependency>\n <groupId>ch.qos.logback</groupId>\n <artifactId>logback-core</artifactId>\n </dependency>\n <dependency>\n <groupId>org.projectlombok</groupId>\n <artifactId>lombok</artifactId>\n </dependency>\n </dependencies>\n\n\n <repositories>\n <repository>\n <id>central</id>\n <name>aliyun maven</name>\n <url>https://maven.aliyun.com/nexus/content/groups/public/</url>\n <layout>default</layout>\n <!-- 是否开启发布版构件下载 -->\n <releases>\n <enabled>true</enabled>\n </releases>\n <!-- 是否开启快照版构件下载 -->\n <snapshots>\n <enabled>true</enabled>\n </snapshots>\n </repository>\n </repositories>\n <build>\n <plugins>\n <plugin>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-maven-plugin</artifactId>\n </plugin>\n </plugins>\n </build>\n\n</project>\n\n</code></pre>\n"},{"title":"springboot 事务","date":"2021-09-13T12:18:56.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n[toc]\n\n## @Transactional 注解的属性介绍\n\n### value 和 transactionManager 属性\n\n它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。\n\n### propagation 属性\n\n事务的传播行为,默认值为 Propagation.REQUIRED。\n\n* REQUIRED\n\n支持事务,如果业务方法执行时在一个事务中,则加入当前事务,否则则重新开始一个事务。\n\n* REQUIRES_NEW\n\n支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。\n\n* NESTED\n\n支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。\n\n* SUPPORTS\n\n支持事务。当前有事务就加入当前事务。当前没有事务就算了,不会开启一个事物。\n\n* MANDATORY\n\n支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。\n\n* NOT_SUPPORTED\n\n不支持事务,如果业务方法执行时已经在一个事务中,则挂起当前事务,等方法执行完毕后,事务恢复进行。\n\n* NEVER\n\n不支持事务。如果当前已经在一个事务中了,抛出异常。数据回滚。\n\n### isolation 属性\n\n事务的隔离级别,默认值为 Isolation.DEFAULT。\n\n* Isolation.DEFAULT\n\n 使用底层数据库默认的隔离级别。\n\n* Isolation.READ_UNCOMMITTED\n\n* Isolation.READ_COMMITTED\n\n* Isolation.REPEATABLE_READ\n\n* Isolation.SERIALIZABLE\n\n### timeout 属性\n\n事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。\n\n### readOnly 属性\n\n指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。\n\n### rollbackFor 属性\n\n用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。\n\n### noRollbackFor 属性\n\n抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。\n\n## 事务不生效的情况\n\n### 1. 数据库不支持事物\n\nmysql 中 MyISAM 是不支持事物的。\n\n### 2. 类内部访问\n\n简单来讲就是指非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 访问 B\n\n### 3. 私有方法\n\n在私有方法上,添加`@Transactional`注解也不会生效,私有方法外部不能访问,所以只能内部访问。\n\n### 4. 异常不匹配\n\n`@Transactional`注解默认处理运行时异常,即只有抛出运行时异常时,才会触发事务回滚,否则并不会\n\n### 5. 多线程\n\n这个场景可能并不多见,在标记事务的方法内部,另起子线程执行 db 操作,此时事务同样不会生效\n\n### 6. 传播属性设置异常\n\n有些传播属性是不支持事物的,见上文中呢\n","source":"_posts/java/springboot-事务.md","raw":"---\ntitle: springboot 事务\ndate: 2021-09-13 20:18:56\ntags:\n- java\n- springboot\n- 事务\ncategories:\n- java\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n[toc]\n\n## @Transactional 注解的属性介绍\n\n### value 和 transactionManager 属性\n\n它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。\n\n### propagation 属性\n\n事务的传播行为,默认值为 Propagation.REQUIRED。\n\n* REQUIRED\n\n支持事务,如果业务方法执行时在一个事务中,则加入当前事务,否则则重新开始一个事务。\n\n* REQUIRES_NEW\n\n支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。\n\n* NESTED\n\n支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。\n\n* SUPPORTS\n\n支持事务。当前有事务就加入当前事务。当前没有事务就算了,不会开启一个事物。\n\n* MANDATORY\n\n支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。\n\n* NOT_SUPPORTED\n\n不支持事务,如果业务方法执行时已经在一个事务中,则挂起当前事务,等方法执行完毕后,事务恢复进行。\n\n* NEVER\n\n不支持事务。如果当前已经在一个事务中了,抛出异常。数据回滚。\n\n### isolation 属性\n\n事务的隔离级别,默认值为 Isolation.DEFAULT。\n\n* Isolation.DEFAULT\n\n 使用底层数据库默认的隔离级别。\n\n* Isolation.READ_UNCOMMITTED\n\n* Isolation.READ_COMMITTED\n\n* Isolation.REPEATABLE_READ\n\n* Isolation.SERIALIZABLE\n\n### timeout 属性\n\n事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。\n\n### readOnly 属性\n\n指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。\n\n### rollbackFor 属性\n\n用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。\n\n### noRollbackFor 属性\n\n抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。\n\n## 事务不生效的情况\n\n### 1. 数据库不支持事物\n\nmysql 中 MyISAM 是不支持事物的。\n\n### 2. 类内部访问\n\n简单来讲就是指非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 访问 B\n\n### 3. 私有方法\n\n在私有方法上,添加`@Transactional`注解也不会生效,私有方法外部不能访问,所以只能内部访问。\n\n### 4. 异常不匹配\n\n`@Transactional`注解默认处理运行时异常,即只有抛出运行时异常时,才会触发事务回滚,否则并不会\n\n### 5. 多线程\n\n这个场景可能并不多见,在标记事务的方法内部,另起子线程执行 db 操作,此时事务同样不会生效\n\n### 6. 传播属性设置异常\n\n有些传播属性是不支持事物的,见上文中呢\n","slug":"java/springboot-事务","published":1,"updated":"2022-11-01T02:43:19.954Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpn9001kiysy9aps3lzr","content":"<p>[toc]</p>\n<h2 id=\"Transactional-注解的属性介绍\"><a href=\"#Transactional-注解的属性介绍\" class=\"headerlink\" title=\"@Transactional 注解的属性介绍\"></a>@Transactional 注解的属性介绍</h2><h3 id=\"value-和-transactionManager-属性\"><a href=\"#value-和-transactionManager-属性\" class=\"headerlink\" title=\"value 和 transactionManager 属性\"></a>value 和 transactionManager 属性</h3><p>它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。</p>\n<h3 id=\"propagation-属性\"><a href=\"#propagation-属性\" class=\"headerlink\" title=\"propagation 属性\"></a>propagation 属性</h3><p>事务的传播行为,默认值为 Propagation.REQUIRED。</p>\n<ul>\n<li>REQUIRED</li>\n</ul>\n<p>支持事务,如果业务方法执行时在一个事务中,则加入当前事务,否则则重新开始一个事务。</p>\n<ul>\n<li>REQUIRES_NEW</li>\n</ul>\n<p>支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。</p>\n<ul>\n<li>NESTED</li>\n</ul>\n<p>支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。</p>\n<ul>\n<li>SUPPORTS</li>\n</ul>\n<p>支持事务。当前有事务就加入当前事务。当前没有事务就算了,不会开启一个事物。</p>\n<ul>\n<li>MANDATORY</li>\n</ul>\n<p>支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。</p>\n<ul>\n<li>NOT_SUPPORTED</li>\n</ul>\n<p>不支持事务,如果业务方法执行时已经在一个事务中,则挂起当前事务,等方法执行完毕后,事务恢复进行。</p>\n<ul>\n<li>NEVER</li>\n</ul>\n<p>不支持事务。如果当前已经在一个事务中了,抛出异常。数据回滚。</p>\n<h3 id=\"isolation-属性\"><a href=\"#isolation-属性\" class=\"headerlink\" title=\"isolation 属性\"></a>isolation 属性</h3><p>事务的隔离级别,默认值为 Isolation.DEFAULT。</p>\n<ul>\n<li><p>Isolation.DEFAULT</p>\n<p>使用底层数据库默认的隔离级别。</p>\n</li>\n<li><p>Isolation.READ_UNCOMMITTED</p>\n</li>\n<li><p>Isolation.READ_COMMITTED</p>\n</li>\n<li><p>Isolation.REPEATABLE_READ</p>\n</li>\n<li><p>Isolation.SERIALIZABLE</p>\n</li>\n</ul>\n<h3 id=\"timeout-属性\"><a href=\"#timeout-属性\" class=\"headerlink\" title=\"timeout 属性\"></a>timeout 属性</h3><p>事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。</p>\n<h3 id=\"readOnly-属性\"><a href=\"#readOnly-属性\" class=\"headerlink\" title=\"readOnly 属性\"></a>readOnly 属性</h3><p>指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。</p>\n<h3 id=\"rollbackFor-属性\"><a href=\"#rollbackFor-属性\" class=\"headerlink\" title=\"rollbackFor 属性\"></a>rollbackFor 属性</h3><p>用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。</p>\n<h3 id=\"noRollbackFor-属性\"><a href=\"#noRollbackFor-属性\" class=\"headerlink\" title=\"noRollbackFor 属性\"></a>noRollbackFor 属性</h3><p>抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。</p>\n<h2 id=\"事务不生效的情况\"><a href=\"#事务不生效的情况\" class=\"headerlink\" title=\"事务不生效的情况\"></a>事务不生效的情况</h2><h3 id=\"1-数据库不支持事物\"><a href=\"#1-数据库不支持事物\" class=\"headerlink\" title=\"1. 数据库不支持事物\"></a>1. 数据库不支持事物</h3><p>mysql 中 MyISAM 是不支持事物的。</p>\n<h3 id=\"2-类内部访问\"><a href=\"#2-类内部访问\" class=\"headerlink\" title=\"2. 类内部访问\"></a>2. 类内部访问</h3><p>简单来讲就是指非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 访问 B</p>\n<h3 id=\"3-私有方法\"><a href=\"#3-私有方法\" class=\"headerlink\" title=\"3. 私有方法\"></a>3. 私有方法</h3><p>在私有方法上,添加<code>@Transactional</code>注解也不会生效,私有方法外部不能访问,所以只能内部访问。</p>\n<h3 id=\"4-异常不匹配\"><a href=\"#4-异常不匹配\" class=\"headerlink\" title=\"4. 异常不匹配\"></a>4. 异常不匹配</h3><p><code>@Transactional</code>注解默认处理运行时异常,即只有抛出运行时异常时,才会触发事务回滚,否则并不会</p>\n<h3 id=\"5-多线程\"><a href=\"#5-多线程\" class=\"headerlink\" title=\"5. 多线程\"></a>5. 多线程</h3><p>这个场景可能并不多见,在标记事务的方法内部,另起子线程执行 db 操作,此时事务同样不会生效</p>\n<h3 id=\"6-传播属性设置异常\"><a href=\"#6-传播属性设置异常\" class=\"headerlink\" title=\"6. 传播属性设置异常\"></a>6. 传播属性设置异常</h3><p>有些传播属性是不支持事物的,见上文中呢</p>\n","site":{"data":{}},"excerpt":"","more":"<p>[toc]</p>\n<h2 id=\"Transactional-注解的属性介绍\"><a href=\"#Transactional-注解的属性介绍\" class=\"headerlink\" title=\"@Transactional 注解的属性介绍\"></a>@Transactional 注解的属性介绍</h2><h3 id=\"value-和-transactionManager-属性\"><a href=\"#value-和-transactionManager-属性\" class=\"headerlink\" title=\"value 和 transactionManager 属性\"></a>value 和 transactionManager 属性</h3><p>它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。</p>\n<h3 id=\"propagation-属性\"><a href=\"#propagation-属性\" class=\"headerlink\" title=\"propagation 属性\"></a>propagation 属性</h3><p>事务的传播行为,默认值为 Propagation.REQUIRED。</p>\n<ul>\n<li>REQUIRED</li>\n</ul>\n<p>支持事务,如果业务方法执行时在一个事务中,则加入当前事务,否则则重新开始一个事务。</p>\n<ul>\n<li>REQUIRES_NEW</li>\n</ul>\n<p>支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。</p>\n<ul>\n<li>NESTED</li>\n</ul>\n<p>支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。</p>\n<ul>\n<li>SUPPORTS</li>\n</ul>\n<p>支持事务。当前有事务就加入当前事务。当前没有事务就算了,不会开启一个事物。</p>\n<ul>\n<li>MANDATORY</li>\n</ul>\n<p>支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。</p>\n<ul>\n<li>NOT_SUPPORTED</li>\n</ul>\n<p>不支持事务,如果业务方法执行时已经在一个事务中,则挂起当前事务,等方法执行完毕后,事务恢复进行。</p>\n<ul>\n<li>NEVER</li>\n</ul>\n<p>不支持事务。如果当前已经在一个事务中了,抛出异常。数据回滚。</p>\n<h3 id=\"isolation-属性\"><a href=\"#isolation-属性\" class=\"headerlink\" title=\"isolation 属性\"></a>isolation 属性</h3><p>事务的隔离级别,默认值为 Isolation.DEFAULT。</p>\n<ul>\n<li><p>Isolation.DEFAULT</p>\n<p>使用底层数据库默认的隔离级别。</p>\n</li>\n<li><p>Isolation.READ_UNCOMMITTED</p>\n</li>\n<li><p>Isolation.READ_COMMITTED</p>\n</li>\n<li><p>Isolation.REPEATABLE_READ</p>\n</li>\n<li><p>Isolation.SERIALIZABLE</p>\n</li>\n</ul>\n<h3 id=\"timeout-属性\"><a href=\"#timeout-属性\" class=\"headerlink\" title=\"timeout 属性\"></a>timeout 属性</h3><p>事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。</p>\n<h3 id=\"readOnly-属性\"><a href=\"#readOnly-属性\" class=\"headerlink\" title=\"readOnly 属性\"></a>readOnly 属性</h3><p>指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。</p>\n<h3 id=\"rollbackFor-属性\"><a href=\"#rollbackFor-属性\" class=\"headerlink\" title=\"rollbackFor 属性\"></a>rollbackFor 属性</h3><p>用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。</p>\n<h3 id=\"noRollbackFor-属性\"><a href=\"#noRollbackFor-属性\" class=\"headerlink\" title=\"noRollbackFor 属性\"></a>noRollbackFor 属性</h3><p>抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。</p>\n<h2 id=\"事务不生效的情况\"><a href=\"#事务不生效的情况\" class=\"headerlink\" title=\"事务不生效的情况\"></a>事务不生效的情况</h2><h3 id=\"1-数据库不支持事物\"><a href=\"#1-数据库不支持事物\" class=\"headerlink\" title=\"1. 数据库不支持事物\"></a>1. 数据库不支持事物</h3><p>mysql 中 MyISAM 是不支持事物的。</p>\n<h3 id=\"2-类内部访问\"><a href=\"#2-类内部访问\" class=\"headerlink\" title=\"2. 类内部访问\"></a>2. 类内部访问</h3><p>简单来讲就是指非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 访问 B</p>\n<h3 id=\"3-私有方法\"><a href=\"#3-私有方法\" class=\"headerlink\" title=\"3. 私有方法\"></a>3. 私有方法</h3><p>在私有方法上,添加<code>@Transactional</code>注解也不会生效,私有方法外部不能访问,所以只能内部访问。</p>\n<h3 id=\"4-异常不匹配\"><a href=\"#4-异常不匹配\" class=\"headerlink\" title=\"4. 异常不匹配\"></a>4. 异常不匹配</h3><p><code>@Transactional</code>注解默认处理运行时异常,即只有抛出运行时异常时,才会触发事务回滚,否则并不会</p>\n<h3 id=\"5-多线程\"><a href=\"#5-多线程\" class=\"headerlink\" title=\"5. 多线程\"></a>5. 多线程</h3><p>这个场景可能并不多见,在标记事务的方法内部,另起子线程执行 db 操作,此时事务同样不会生效</p>\n<h3 id=\"6-传播属性设置异常\"><a href=\"#6-传播属性设置异常\" class=\"headerlink\" title=\"6. 传播属性设置异常\"></a>6. 传播属性设置异常</h3><p>有些传播属性是不支持事物的,见上文中呢</p>\n"},{"title":"mac zsh 终端","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","typora-root-url":"../..","_content":"\n## mac zsh 终端\n\n### 利用iTerm2+oh-my-zsh+Dracula主题打造我的Mac终端利器\n\n参考文章:https://blog.csdn.net/daiyuhe/article/details/88667875\n效果图\n\n![image](/img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png)\n\n### item2\n官网:https://www.iterm2.com/index.html\n\n\n### oh-my-zsh配置\n\n#### 安装oh-my-zsh\n```shell\nsh -c \"$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\"\n```\n\n#### 在oh-my-zsh中启用插件\n```\nplugins=(\ngit \nzsh-autosuggestions \nzsh-syntax-highlighting\nmvn\n)\n```","source":"_posts/mac/mac-item2-终端.md","raw":"---\ntitle: mac zsh 终端\ntags:\n - mac\ncategories:\n - mac\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\ntypora-root-url: ../..\n---\n\n## mac zsh 终端\n\n### 利用iTerm2+oh-my-zsh+Dracula主题打造我的Mac终端利器\n\n参考文章:https://blog.csdn.net/daiyuhe/article/details/88667875\n效果图\n\n![image](/img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png)\n\n### item2\n官网:https://www.iterm2.com/index.html\n\n\n### oh-my-zsh配置\n\n#### 安装oh-my-zsh\n```shell\nsh -c \"$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\"\n```\n\n#### 在oh-my-zsh中启用插件\n```\nplugins=(\ngit \nzsh-autosuggestions \nzsh-syntax-highlighting\nmvn\n)\n```","slug":"mac/mac-item2-终端","published":1,"updated":"2022-11-01T03:09:50.072Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpna001niysy1w3251mn","content":"<h2 id=\"mac-zsh-终端\"><a href=\"#mac-zsh-终端\" class=\"headerlink\" title=\"mac zsh 终端\"></a>mac zsh 终端</h2><h3 id=\"利用iTerm2-oh-my-zsh-Dracula主题打造我的Mac终端利器\"><a href=\"#利用iTerm2-oh-my-zsh-Dracula主题打造我的Mac终端利器\" class=\"headerlink\" title=\"利用iTerm2+oh-my-zsh+Dracula主题打造我的Mac终端利器\"></a>利用iTerm2+oh-my-zsh+Dracula主题打造我的Mac终端利器</h3><p>参考文章:<a href=\"https://blog.csdn.net/daiyuhe/article/details/88667875\">https://blog.csdn.net/daiyuhe/article/details/88667875</a><br>效果图</p>\n<p><img src=\"/img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png\" alt=\"image\"></p>\n<h3 id=\"item2\"><a href=\"#item2\" class=\"headerlink\" title=\"item2\"></a>item2</h3><p>官网:<a href=\"https://www.iterm2.com/index.html\">https://www.iterm2.com/index.html</a></p>\n<h3 id=\"oh-my-zsh配置\"><a href=\"#oh-my-zsh配置\" class=\"headerlink\" title=\"oh-my-zsh配置\"></a>oh-my-zsh配置</h3><h4 id=\"安装oh-my-zsh\"><a href=\"#安装oh-my-zsh\" class=\"headerlink\" title=\"安装oh-my-zsh\"></a>安装oh-my-zsh</h4><pre><code class=\"shell\">sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"\n</code></pre>\n<h4 id=\"在oh-my-zsh中启用插件\"><a href=\"#在oh-my-zsh中启用插件\" class=\"headerlink\" title=\"在oh-my-zsh中启用插件\"></a>在oh-my-zsh中启用插件</h4><pre><code>plugins=(\ngit \nzsh-autosuggestions \nzsh-syntax-highlighting\nmvn\n)\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"mac-zsh-终端\"><a href=\"#mac-zsh-终端\" class=\"headerlink\" title=\"mac zsh 终端\"></a>mac zsh 终端</h2><h3 id=\"利用iTerm2-oh-my-zsh-Dracula主题打造我的Mac终端利器\"><a href=\"#利用iTerm2-oh-my-zsh-Dracula主题打造我的Mac终端利器\" class=\"headerlink\" title=\"利用iTerm2+oh-my-zsh+Dracula主题打造我的Mac终端利器\"></a>利用iTerm2+oh-my-zsh+Dracula主题打造我的Mac终端利器</h3><p>参考文章:<a href=\"https://blog.csdn.net/daiyuhe/article/details/88667875\">https://blog.csdn.net/daiyuhe/article/details/88667875</a><br>效果图</p>\n<p><img src=\"/img/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaXl1aGU=,size_16,color_FFFFFF,t_70.png\" alt=\"image\"></p>\n<h3 id=\"item2\"><a href=\"#item2\" class=\"headerlink\" title=\"item2\"></a>item2</h3><p>官网:<a href=\"https://www.iterm2.com/index.html\">https://www.iterm2.com/index.html</a></p>\n<h3 id=\"oh-my-zsh配置\"><a href=\"#oh-my-zsh配置\" class=\"headerlink\" title=\"oh-my-zsh配置\"></a>oh-my-zsh配置</h3><h4 id=\"安装oh-my-zsh\"><a href=\"#安装oh-my-zsh\" class=\"headerlink\" title=\"安装oh-my-zsh\"></a>安装oh-my-zsh</h4><pre><code class=\"shell\">sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"\n</code></pre>\n<h4 id=\"在oh-my-zsh中启用插件\"><a href=\"#在oh-my-zsh中启用插件\" class=\"headerlink\" title=\"在oh-my-zsh中启用插件\"></a>在oh-my-zsh中启用插件</h4><pre><code>plugins=(\ngit \nzsh-autosuggestions \nzsh-syntax-highlighting\nmvn\n)\n</code></pre>\n"},{"title":"SQL中的in与not in、exists与not exists的区别以及性能分析","date":"2021-09-14T14:55:15.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n### 1、in和exists\n\nin是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为exists比in效率高的说法是不准确的。\n\n**如果查询的两个表大小相当,那么用in和exists差别不大;如果两个表中一个较小一个较大,则子查询表大的用exists,子查询表小的用in;**\n\n## **2、not in 和not exists**\n\nnot in 逻辑上不完全等同于not exists,如果你误用了not in,小心你的程序存在致命的BUG,请看下面的例子:\n\n```sql\ncreate table #t1(c1 int,c2 int);\n\ncreate table #t2(c1 int,c2 int);\n\ninsert into #t1 values(1,2);\n\ninsert into #t1 values(1,3);\n\ninsert into #t2 values(1,2);\n\ninsert into #t2 values(1,null);\n\n \n\nselect * from #t1 where c2 not in(select c2 from #t2); -->执行结果:无\n\nselect * from #t1 where not exists(select 1 from #t2 where #t2.c2=#t1.c2) -->执行结果:1 3\n```\n\n正如所看到的,not in出现了不期望的结果集,存在逻辑错误。如果看一下上述两个select 语句的执行计划,也会不同,后者使用了hash_aj,所以,请尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。\n\n如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录。如果子查询字段有非空限制,这时可以使用not in,并且可以通过提示让它用hasg_aj或merge_aj连接。\n\n如果查询语句使用了not in,那么对内外表都进行全表扫描,没有用到索引;而not exists的子查询依然能用到表上的索引。所以无论哪个表大,用not exists都比not in 要快。\n\n## **3、in 与 = 的区别**\n\n```sql\nselect name from student where name in('zhang','wang','zhao');\n```\n\n与\n\n```sql\nselect name from student where name='zhang' or name='wang' or name='zhao'\n```\n\n的结果是相同的。\n\n### 其他分析\n\n**对于in 和 exists的性能区别:**\n\n如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in,反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。\n\n其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了\n\n**对于not in 和 not exists的性能区别:**\n\nnot in 只有当子查询中,select 关键字后的字段有not null约束或者有这种暗示时用not in,另外如果主查询中表大,子查询中的表小但是记录多,则应当使用not in,并使用anti hash join.\n\n如果主查询表中记录少,子查询表中记录多,并有索引,可以使用not exists,另外not in最好也可以用`/*+ HASH_AJ */`或者外连接+is null\n\nNOT IN 在基于成本的应用中较好\n\n","source":"_posts/mysql/SQL中的in与not-in、exists与not-exists的区别以及性能分析.md","raw":"---\ntitle: SQL中的in与not in、exists与not exists的区别以及性能分析\ndate: 2021-09-14 22:55:15\ntags:\n- sql\n- 性能\ncategories:\n- mysql\nauto_excerpt:\n enable: true\n length: 150\n---\n\n### 1、in和exists\n\nin是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为exists比in效率高的说法是不准确的。\n\n**如果查询的两个表大小相当,那么用in和exists差别不大;如果两个表中一个较小一个较大,则子查询表大的用exists,子查询表小的用in;**\n\n## **2、not in 和not exists**\n\nnot in 逻辑上不完全等同于not exists,如果你误用了not in,小心你的程序存在致命的BUG,请看下面的例子:\n\n```sql\ncreate table #t1(c1 int,c2 int);\n\ncreate table #t2(c1 int,c2 int);\n\ninsert into #t1 values(1,2);\n\ninsert into #t1 values(1,3);\n\ninsert into #t2 values(1,2);\n\ninsert into #t2 values(1,null);\n\n \n\nselect * from #t1 where c2 not in(select c2 from #t2); -->执行结果:无\n\nselect * from #t1 where not exists(select 1 from #t2 where #t2.c2=#t1.c2) -->执行结果:1 3\n```\n\n正如所看到的,not in出现了不期望的结果集,存在逻辑错误。如果看一下上述两个select 语句的执行计划,也会不同,后者使用了hash_aj,所以,请尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。\n\n如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录。如果子查询字段有非空限制,这时可以使用not in,并且可以通过提示让它用hasg_aj或merge_aj连接。\n\n如果查询语句使用了not in,那么对内外表都进行全表扫描,没有用到索引;而not exists的子查询依然能用到表上的索引。所以无论哪个表大,用not exists都比not in 要快。\n\n## **3、in 与 = 的区别**\n\n```sql\nselect name from student where name in('zhang','wang','zhao');\n```\n\n与\n\n```sql\nselect name from student where name='zhang' or name='wang' or name='zhao'\n```\n\n的结果是相同的。\n\n### 其他分析\n\n**对于in 和 exists的性能区别:**\n\n如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in,反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。\n\n其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了\n\n**对于not in 和 not exists的性能区别:**\n\nnot in 只有当子查询中,select 关键字后的字段有not null约束或者有这种暗示时用not in,另外如果主查询中表大,子查询中的表小但是记录多,则应当使用not in,并使用anti hash join.\n\n如果主查询表中记录少,子查询表中记录多,并有索引,可以使用not exists,另外not in最好也可以用`/*+ HASH_AJ */`或者外连接+is null\n\nNOT IN 在基于成本的应用中较好\n\n","slug":"mysql/SQL中的in与not-in、exists与not-exists的区别以及性能分析","published":1,"updated":"2022-11-01T02:43:31.312Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpna001piysy9w38ewz3","content":"<h3 id=\"1、in和exists\"><a href=\"#1、in和exists\" class=\"headerlink\" title=\"1、in和exists\"></a>1、in和exists</h3><p>in是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为exists比in效率高的说法是不准确的。</p>\n<p><strong>如果查询的两个表大小相当,那么用in和exists差别不大;如果两个表中一个较小一个较大,则子查询表大的用exists,子查询表小的用in;</strong></p>\n<h2 id=\"2、not-in-和not-exists\"><a href=\"#2、not-in-和not-exists\" class=\"headerlink\" title=\"2、not in 和not exists\"></a><strong>2、not in 和not exists</strong></h2><p>not in 逻辑上不完全等同于not exists,如果你误用了not in,小心你的程序存在致命的BUG,请看下面的例子:</p>\n<pre><code class=\"sql\">create table #t1(c1 int,c2 int);\n\ncreate table #t2(c1 int,c2 int);\n\ninsert into #t1 values(1,2);\n\ninsert into #t1 values(1,3);\n\ninsert into #t2 values(1,2);\n\ninsert into #t2 values(1,null);\n\n \n\nselect * from #t1 where c2 not in(select c2 from #t2); -->执行结果:无\n\nselect * from #t1 where not exists(select 1 from #t2 where #t2.c2=#t1.c2) -->执行结果:1 3\n</code></pre>\n<p>正如所看到的,not in出现了不期望的结果集,存在逻辑错误。如果看一下上述两个select 语句的执行计划,也会不同,后者使用了hash_aj,所以,请尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。</p>\n<p>如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录。如果子查询字段有非空限制,这时可以使用not in,并且可以通过提示让它用hasg_aj或merge_aj连接。</p>\n<p>如果查询语句使用了not in,那么对内外表都进行全表扫描,没有用到索引;而not exists的子查询依然能用到表上的索引。所以无论哪个表大,用not exists都比not in 要快。</p>\n<h2 id=\"3、in-与-的区别\"><a href=\"#3、in-与-的区别\" class=\"headerlink\" title=\"3、in 与 = 的区别\"></a><strong>3、in 与 = 的区别</strong></h2><pre><code class=\"sql\">select name from student where name in('zhang','wang','zhao');\n</code></pre>\n<p>与</p>\n<pre><code class=\"sql\">select name from student where name='zhang' or name='wang' or name='zhao'\n</code></pre>\n<p>的结果是相同的。</p>\n<h3 id=\"其他分析\"><a href=\"#其他分析\" class=\"headerlink\" title=\"其他分析\"></a>其他分析</h3><p><strong>对于in 和 exists的性能区别:</strong></p>\n<p>如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in,反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。</p>\n<p>其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了</p>\n<p><strong>对于not in 和 not exists的性能区别:</strong></p>\n<p>not in 只有当子查询中,select 关键字后的字段有not null约束或者有这种暗示时用not in,另外如果主查询中表大,子查询中的表小但是记录多,则应当使用not in,并使用anti hash join.</p>\n<p>如果主查询表中记录少,子查询表中记录多,并有索引,可以使用not exists,另外not in最好也可以用<code>/*+ HASH_AJ */</code>或者外连接+is null</p>\n<p>NOT IN 在基于成本的应用中较好</p>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"1、in和exists\"><a href=\"#1、in和exists\" class=\"headerlink\" title=\"1、in和exists\"></a>1、in和exists</h3><p>in是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为exists比in效率高的说法是不准确的。</p>\n<p><strong>如果查询的两个表大小相当,那么用in和exists差别不大;如果两个表中一个较小一个较大,则子查询表大的用exists,子查询表小的用in;</strong></p>\n<h2 id=\"2、not-in-和not-exists\"><a href=\"#2、not-in-和not-exists\" class=\"headerlink\" title=\"2、not in 和not exists\"></a><strong>2、not in 和not exists</strong></h2><p>not in 逻辑上不完全等同于not exists,如果你误用了not in,小心你的程序存在致命的BUG,请看下面的例子:</p>\n<pre><code class=\"sql\">create table #t1(c1 int,c2 int);\n\ncreate table #t2(c1 int,c2 int);\n\ninsert into #t1 values(1,2);\n\ninsert into #t1 values(1,3);\n\ninsert into #t2 values(1,2);\n\ninsert into #t2 values(1,null);\n\n \n\nselect * from #t1 where c2 not in(select c2 from #t2); -->执行结果:无\n\nselect * from #t1 where not exists(select 1 from #t2 where #t2.c2=#t1.c2) -->执行结果:1 3\n</code></pre>\n<p>正如所看到的,not in出现了不期望的结果集,存在逻辑错误。如果看一下上述两个select 语句的执行计划,也会不同,后者使用了hash_aj,所以,请尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。</p>\n<p>如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录。如果子查询字段有非空限制,这时可以使用not in,并且可以通过提示让它用hasg_aj或merge_aj连接。</p>\n<p>如果查询语句使用了not in,那么对内外表都进行全表扫描,没有用到索引;而not exists的子查询依然能用到表上的索引。所以无论哪个表大,用not exists都比not in 要快。</p>\n<h2 id=\"3、in-与-的区别\"><a href=\"#3、in-与-的区别\" class=\"headerlink\" title=\"3、in 与 = 的区别\"></a><strong>3、in 与 = 的区别</strong></h2><pre><code class=\"sql\">select name from student where name in('zhang','wang','zhao');\n</code></pre>\n<p>与</p>\n<pre><code class=\"sql\">select name from student where name='zhang' or name='wang' or name='zhao'\n</code></pre>\n<p>的结果是相同的。</p>\n<h3 id=\"其他分析\"><a href=\"#其他分析\" class=\"headerlink\" title=\"其他分析\"></a>其他分析</h3><p><strong>对于in 和 exists的性能区别:</strong></p>\n<p>如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in,反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。</p>\n<p>其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了</p>\n<p><strong>对于not in 和 not exists的性能区别:</strong></p>\n<p>not in 只有当子查询中,select 关键字后的字段有not null约束或者有这种暗示时用not in,另外如果主查询中表大,子查询中的表小但是记录多,则应当使用not in,并使用anti hash join.</p>\n<p>如果主查询表中记录少,子查询表中记录多,并有索引,可以使用not exists,另外not in最好也可以用<code>/*+ HASH_AJ */</code>或者外连接+is null</p>\n<p>NOT IN 在基于成本的应用中较好</p>\n"},{"title":"mysql 距离函数 st_distance 使用","date":"2019-06-12T07:58:59.000Z","_content":"\n随着近几年各类移动终端的迅速普及,在手机移动定位app中,附近的人,附近的地点功能十分常见,基于地理位置的服务(LBS)和相关应用也越来越多,而支撑这些应用的最基础技术之一,就是基于地理位置信息的处理。\n\nst_distance 函数 \nst_distance 函数是从mysql5.6.1才加入的。 \nSET @g1 = POINT(1,1), @g2 = POINT(2,2); \nselect st_distance (@g1, @g2); \n输出结果:1.4142135623730951\n\n```sql\nSELECT st_distance (point (1, 1),point(2,2) ) * 111195 \n```\n\n// 输出结果:157253.47706807632 单位:米 \nst_distance 计算的结果单位是度,需要乘111195(地球半径6371000*PI/180)是将值转化为米。\n\n当然你也可以自定义距离函数: \n```sql\nCREATE FUNCTION slc ( \nlat1 DOUBLE, \nlon1 DOUBLE, \nlat2 DOUBLE, \nlon2 DOUBLE \n) RETURNS DOUBLE RETURN 6371 * acos( \ncos(radians(lat1)) * cos(radians(lat2)) * cos( \nradians(lon2) - radians(lon1) \n) + sin(radians(lat1)) * sin(radians(lat2)) \n);\n\nSELECT slc(1,1,2,2) from DUAL \n```\n// 输出结果:157.22543203804852 km\n\n#### 应用场景: \n\n假设我当时的坐标 117.069,35.86 需要查询我附近50KM内服务区,并按照距离由近及远排列 \n\n```sql\nSELECT \ns.id,s.name,s.lng,s.lat, \n(st_distance (point (lng, lat),point(117.069,35.86) ) *111195) AS distance \nFROM \nroad_servicearea s \nHAVING distance<50 \nORDER BY distance\n```\n#### 知识科普: \n\n地球是在不停地绕地轴旋转,在地球中腰画一个与地轴垂直的大圆圈,使圈上的每一点都和南北两极的距离相等,这个圆圈就叫作“赤道”。在赤道的南北两边,画出许多和赤道平行的圆圈,就是“纬圈”;构成这些圆圈的线段,叫做纬线。我们把赤道定为纬度零度,向南向北各为90度,在赤道以南的叫南纬,在赤道以北的叫北纬。北极就是北纬90度,南极就是南纬90度。纬度的高低也标志着气候的冷热,如赤道和低纬度地地区无冬,两极和高纬度地区无夏,中纬度地区四季分明。\n\n从北极点到南极点,可以画出许多南北方向的与地球赤道垂直的大圆圈,这叫作“经圈”;构成这些圆圈的线段,就叫经线。公元1884平面坐标图年,国际上规定以通过英国伦敦近郊的格林尼治天文台的经线作为计算经度的起点,即经度零度零分零秒,也称“本初子午线”。在它东面的为东经,共180度; \n\n在它西面的为西经,共180度。因为地球是圆的,所以东经180度和西经180度的经线是同一条经线。各国公定180度经线为“国际日期变更线”。为了避免同一地区使用两个不同的日期,国际日期变线在遇陆地时略有偏离。每一经度和纬度还可以再细分为60分,每一分再分为60秒以及秒的小数。利用经纬线,我们就可以确定地球上每一个地方的具体位置,并且把它在地图或地球仪上表示出来。\n","source":"_posts/mysql/mysql 距离函数 st_distance 使用.md","raw":"---\ntitle: mysql 距离函数 st_distance 使用\ndate: 2019-06-12 15:58:59\ntags:\n- mysql\n- function\n\ncategories:\n- mysql\n\n---\n\n随着近几年各类移动终端的迅速普及,在手机移动定位app中,附近的人,附近的地点功能十分常见,基于地理位置的服务(LBS)和相关应用也越来越多,而支撑这些应用的最基础技术之一,就是基于地理位置信息的处理。\n\nst_distance 函数 \nst_distance 函数是从mysql5.6.1才加入的。 \nSET @g1 = POINT(1,1), @g2 = POINT(2,2); \nselect st_distance (@g1, @g2); \n输出结果:1.4142135623730951\n\n```sql\nSELECT st_distance (point (1, 1),point(2,2) ) * 111195 \n```\n\n// 输出结果:157253.47706807632 单位:米 \nst_distance 计算的结果单位是度,需要乘111195(地球半径6371000*PI/180)是将值转化为米。\n\n当然你也可以自定义距离函数: \n```sql\nCREATE FUNCTION slc ( \nlat1 DOUBLE, \nlon1 DOUBLE, \nlat2 DOUBLE, \nlon2 DOUBLE \n) RETURNS DOUBLE RETURN 6371 * acos( \ncos(radians(lat1)) * cos(radians(lat2)) * cos( \nradians(lon2) - radians(lon1) \n) + sin(radians(lat1)) * sin(radians(lat2)) \n);\n\nSELECT slc(1,1,2,2) from DUAL \n```\n// 输出结果:157.22543203804852 km\n\n#### 应用场景: \n\n假设我当时的坐标 117.069,35.86 需要查询我附近50KM内服务区,并按照距离由近及远排列 \n\n```sql\nSELECT \ns.id,s.name,s.lng,s.lat, \n(st_distance (point (lng, lat),point(117.069,35.86) ) *111195) AS distance \nFROM \nroad_servicearea s \nHAVING distance<50 \nORDER BY distance\n```\n#### 知识科普: \n\n地球是在不停地绕地轴旋转,在地球中腰画一个与地轴垂直的大圆圈,使圈上的每一点都和南北两极的距离相等,这个圆圈就叫作“赤道”。在赤道的南北两边,画出许多和赤道平行的圆圈,就是“纬圈”;构成这些圆圈的线段,叫做纬线。我们把赤道定为纬度零度,向南向北各为90度,在赤道以南的叫南纬,在赤道以北的叫北纬。北极就是北纬90度,南极就是南纬90度。纬度的高低也标志着气候的冷热,如赤道和低纬度地地区无冬,两极和高纬度地区无夏,中纬度地区四季分明。\n\n从北极点到南极点,可以画出许多南北方向的与地球赤道垂直的大圆圈,这叫作“经圈”;构成这些圆圈的线段,就叫经线。公元1884平面坐标图年,国际上规定以通过英国伦敦近郊的格林尼治天文台的经线作为计算经度的起点,即经度零度零分零秒,也称“本初子午线”。在它东面的为东经,共180度; \n\n在它西面的为西经,共180度。因为地球是圆的,所以东经180度和西经180度的经线是同一条经线。各国公定180度经线为“国际日期变更线”。为了避免同一地区使用两个不同的日期,国际日期变线在遇陆地时略有偏离。每一经度和纬度还可以再细分为60分,每一分再分为60秒以及秒的小数。利用经纬线,我们就可以确定地球上每一个地方的具体位置,并且把它在地图或地球仪上表示出来。\n","slug":"mysql/mysql 距离函数 st_distance 使用","published":1,"updated":"2022-11-01T02:43:25.286Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnb001tiysycq0rgumb","content":"<p>随着近几年各类移动终端的迅速普及,在手机移动定位app中,附近的人,附近的地点功能十分常见,基于地理位置的服务(LBS)和相关应用也越来越多,而支撑这些应用的最基础技术之一,就是基于地理位置信息的处理。</p>\n<p>st_distance 函数<br>st_distance 函数是从mysql5.6.1才加入的。<br>SET @g1 = POINT(1,1), @g2 = POINT(2,2);<br>select st_distance (@g1, @g2);<br>输出结果:1.4142135623730951</p>\n<pre><code class=\"sql\">SELECT st_distance (point (1, 1),point(2,2) ) * 111195 \n</code></pre>\n<p>// 输出结果:157253.47706807632 单位:米<br>st_distance 计算的结果单位是度,需要乘111195(地球半径6371000*PI/180)是将值转化为米。</p>\n<p>当然你也可以自定义距离函数: </p>\n<pre><code class=\"sql\">CREATE FUNCTION slc ( \nlat1 DOUBLE, \nlon1 DOUBLE, \nlat2 DOUBLE, \nlon2 DOUBLE \n) RETURNS DOUBLE RETURN 6371 * acos( \ncos(radians(lat1)) * cos(radians(lat2)) * cos( \nradians(lon2) - radians(lon1) \n) + sin(radians(lat1)) * sin(radians(lat2)) \n);\n\nSELECT slc(1,1,2,2) from DUAL \n</code></pre>\n<p>// 输出结果:157.22543203804852 km</p>\n<h4 id=\"应用场景\"><a href=\"#应用场景\" class=\"headerlink\" title=\"应用场景:\"></a>应用场景:</h4><p>假设我当时的坐标 117.069,35.86 需要查询我附近50KM内服务区,并按照距离由近及远排列 </p>\n<pre><code class=\"sql\">SELECT \ns.id,s.name,s.lng,s.lat, \n(st_distance (point (lng, lat),point(117.069,35.86) ) *111195) AS distance \nFROM \nroad_servicearea s \nHAVING distance<50 \nORDER BY distance\n</code></pre>\n<h4 id=\"知识科普:\"><a href=\"#知识科普:\" class=\"headerlink\" title=\"知识科普:\"></a>知识科普:</h4><p>地球是在不停地绕地轴旋转,在地球中腰画一个与地轴垂直的大圆圈,使圈上的每一点都和南北两极的距离相等,这个圆圈就叫作“赤道”。在赤道的南北两边,画出许多和赤道平行的圆圈,就是“纬圈”;构成这些圆圈的线段,叫做纬线。我们把赤道定为纬度零度,向南向北各为90度,在赤道以南的叫南纬,在赤道以北的叫北纬。北极就是北纬90度,南极就是南纬90度。纬度的高低也标志着气候的冷热,如赤道和低纬度地地区无冬,两极和高纬度地区无夏,中纬度地区四季分明。</p>\n<p>从北极点到南极点,可以画出许多南北方向的与地球赤道垂直的大圆圈,这叫作“经圈”;构成这些圆圈的线段,就叫经线。公元1884平面坐标图年,国际上规定以通过英国伦敦近郊的格林尼治天文台的经线作为计算经度的起点,即经度零度零分零秒,也称“本初子午线”。在它东面的为东经,共180度; </p>\n<p>在它西面的为西经,共180度。因为地球是圆的,所以东经180度和西经180度的经线是同一条经线。各国公定180度经线为“国际日期变更线”。为了避免同一地区使用两个不同的日期,国际日期变线在遇陆地时略有偏离。每一经度和纬度还可以再细分为60分,每一分再分为60秒以及秒的小数。利用经纬线,我们就可以确定地球上每一个地方的具体位置,并且把它在地图或地球仪上表示出来。</p>\n","site":{"data":{}},"excerpt":"","more":"<p>随着近几年各类移动终端的迅速普及,在手机移动定位app中,附近的人,附近的地点功能十分常见,基于地理位置的服务(LBS)和相关应用也越来越多,而支撑这些应用的最基础技术之一,就是基于地理位置信息的处理。</p>\n<p>st_distance 函数<br>st_distance 函数是从mysql5.6.1才加入的。<br>SET @g1 = POINT(1,1), @g2 = POINT(2,2);<br>select st_distance (@g1, @g2);<br>输出结果:1.4142135623730951</p>\n<pre><code class=\"sql\">SELECT st_distance (point (1, 1),point(2,2) ) * 111195 \n</code></pre>\n<p>// 输出结果:157253.47706807632 单位:米<br>st_distance 计算的结果单位是度,需要乘111195(地球半径6371000*PI/180)是将值转化为米。</p>\n<p>当然你也可以自定义距离函数: </p>\n<pre><code class=\"sql\">CREATE FUNCTION slc ( \nlat1 DOUBLE, \nlon1 DOUBLE, \nlat2 DOUBLE, \nlon2 DOUBLE \n) RETURNS DOUBLE RETURN 6371 * acos( \ncos(radians(lat1)) * cos(radians(lat2)) * cos( \nradians(lon2) - radians(lon1) \n) + sin(radians(lat1)) * sin(radians(lat2)) \n);\n\nSELECT slc(1,1,2,2) from DUAL \n</code></pre>\n<p>// 输出结果:157.22543203804852 km</p>\n<h4 id=\"应用场景\"><a href=\"#应用场景\" class=\"headerlink\" title=\"应用场景:\"></a>应用场景:</h4><p>假设我当时的坐标 117.069,35.86 需要查询我附近50KM内服务区,并按照距离由近及远排列 </p>\n<pre><code class=\"sql\">SELECT \ns.id,s.name,s.lng,s.lat, \n(st_distance (point (lng, lat),point(117.069,35.86) ) *111195) AS distance \nFROM \nroad_servicearea s \nHAVING distance<50 \nORDER BY distance\n</code></pre>\n<h4 id=\"知识科普:\"><a href=\"#知识科普:\" class=\"headerlink\" title=\"知识科普:\"></a>知识科普:</h4><p>地球是在不停地绕地轴旋转,在地球中腰画一个与地轴垂直的大圆圈,使圈上的每一点都和南北两极的距离相等,这个圆圈就叫作“赤道”。在赤道的南北两边,画出许多和赤道平行的圆圈,就是“纬圈”;构成这些圆圈的线段,叫做纬线。我们把赤道定为纬度零度,向南向北各为90度,在赤道以南的叫南纬,在赤道以北的叫北纬。北极就是北纬90度,南极就是南纬90度。纬度的高低也标志着气候的冷热,如赤道和低纬度地地区无冬,两极和高纬度地区无夏,中纬度地区四季分明。</p>\n<p>从北极点到南极点,可以画出许多南北方向的与地球赤道垂直的大圆圈,这叫作“经圈”;构成这些圆圈的线段,就叫经线。公元1884平面坐标图年,国际上规定以通过英国伦敦近郊的格林尼治天文台的经线作为计算经度的起点,即经度零度零分零秒,也称“本初子午线”。在它东面的为东经,共180度; </p>\n<p>在它西面的为西经,共180度。因为地球是圆的,所以东经180度和西经180度的经线是同一条经线。各国公定180度经线为“国际日期变更线”。为了避免同一地区使用两个不同的日期,国际日期变线在遇陆地时略有偏离。每一经度和纬度还可以再细分为60分,每一分再分为60秒以及秒的小数。利用经纬线,我们就可以确定地球上每一个地方的具体位置,并且把它在地图或地球仪上表示出来。</p>\n"},{"title":"mysql 慢日志查询分析","date":"2021-09-17T01:58:36.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n### 慢查询日志相关参数\n\n- slow_query_log :是否开启慢查询日志,1表示开启,0表示关闭。\n\n- log-slow-queries :旧版(5.6以下版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log\n\n- slow-query-log-file:新版(5.6及以上版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log\n\n- long_query_time :慢查询阈值,当查询时间多于设定的阈值时,记录日志。\n\n- log_queries_not_using_indexes:未使用索引的查询也被记录到慢查询日志中(可选项)。\n\n- log_output:日志存储方式。log_output='FILE'表示将日志存入文件,默认值是'FILE'。log_output='TABLE'表示将日志存入数据库,这样日志信息就会被写入到mysql.slow_log表中。MySQL数据库支持同时两种日志存储方式,配置的时候以逗号隔开即可,如:log_output='FILE,TABLE'。日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,因此对于需要启用慢查询日志,又需要能够获得更高的系统性能,那么建议优先记录到文件。\n\n### **日志分析工具****mysqldumpslow**\n\n在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow\n\n查看mysqldumpslow的帮助信息:\n\n```shell\n[root@DB-Server ~]# mysqldumpslow --help\nUsage: mysqldumpslow [ OPTS... ] [ LOGS... ]\n \nParse and summarize the MySQL slow query log. Options are\n \n --verbose verbose\n --debug debug\n --help write this text to standard output\n \n -v verbose\n -d debug\n -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default\n al: average lock time\n ar: average rows sent\n at: average query time\n c: count\n l: lock time\n r: rows sent\n t: query time \n -r reverse the sort order (largest last instead of first)\n -t NUM just show the top n queries\n -a don't abstract all numbers to N and strings to 'S'\n -n NUM abstract numbers with at least n digits within names\n -g PATTERN grep: only consider stmts that include this string\n -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),\n default is '*', i.e. match all\n -i NAME name of server instance (if using mysql.server startup script)\n -l don't subtract lock time from total time\n```\n\n* -s, 是表示按照何种方式排序,\n\n> c: 访问计数\n>\n> l: 锁定时间\n>\n> r: 返回记录\n>\n> t: 查询时间\n>\n> al:平均锁定时间\n>\n> ar:平均返回记录数\n>\n> at:平均查询时间\n\n* -t, 是top n的意思,即为返回前面多少条的数据;\n\n* -g, 后边可以写一个正则匹配模式,大小写不敏感的;\n\n比如\n\n得到返回记录集最多的10个SQL。\n\nmysqldumpslow -s r -t 10 /database/mysql/mysql06_slow.log\n\n得到访问次数最多的10个SQL\n\nmysqldumpslow -s c -t 10 /database/mysql/mysql06_slow.log\n\n得到按照时间排序的前10条里面含有左连接的查询语句。\n\nmysqldumpslow -s t -t 10 -g “left join” /database/mysql/mysql06_slow.log\n\n另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。\n\nmysqldumpslow -s r -t 20 /mysqldata/mysql/mysql06-slow.log | more","source":"_posts/mysql/mysql-慢日志查询分析.md","raw":"---\ntitle: mysql 慢日志查询分析\ndate: 2021-09-17 09:58:36\ntags:\n- mysql\n- 慢日志\n- \ncategories:\n- mysql\n- \nauto_excerpt:\n enable: true\n length: 150\n---\n\n### 慢查询日志相关参数\n\n- slow_query_log :是否开启慢查询日志,1表示开启,0表示关闭。\n\n- log-slow-queries :旧版(5.6以下版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log\n\n- slow-query-log-file:新版(5.6及以上版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log\n\n- long_query_time :慢查询阈值,当查询时间多于设定的阈值时,记录日志。\n\n- log_queries_not_using_indexes:未使用索引的查询也被记录到慢查询日志中(可选项)。\n\n- log_output:日志存储方式。log_output='FILE'表示将日志存入文件,默认值是'FILE'。log_output='TABLE'表示将日志存入数据库,这样日志信息就会被写入到mysql.slow_log表中。MySQL数据库支持同时两种日志存储方式,配置的时候以逗号隔开即可,如:log_output='FILE,TABLE'。日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,因此对于需要启用慢查询日志,又需要能够获得更高的系统性能,那么建议优先记录到文件。\n\n### **日志分析工具****mysqldumpslow**\n\n在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow\n\n查看mysqldumpslow的帮助信息:\n\n```shell\n[root@DB-Server ~]# mysqldumpslow --help\nUsage: mysqldumpslow [ OPTS... ] [ LOGS... ]\n \nParse and summarize the MySQL slow query log. Options are\n \n --verbose verbose\n --debug debug\n --help write this text to standard output\n \n -v verbose\n -d debug\n -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default\n al: average lock time\n ar: average rows sent\n at: average query time\n c: count\n l: lock time\n r: rows sent\n t: query time \n -r reverse the sort order (largest last instead of first)\n -t NUM just show the top n queries\n -a don't abstract all numbers to N and strings to 'S'\n -n NUM abstract numbers with at least n digits within names\n -g PATTERN grep: only consider stmts that include this string\n -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),\n default is '*', i.e. match all\n -i NAME name of server instance (if using mysql.server startup script)\n -l don't subtract lock time from total time\n```\n\n* -s, 是表示按照何种方式排序,\n\n> c: 访问计数\n>\n> l: 锁定时间\n>\n> r: 返回记录\n>\n> t: 查询时间\n>\n> al:平均锁定时间\n>\n> ar:平均返回记录数\n>\n> at:平均查询时间\n\n* -t, 是top n的意思,即为返回前面多少条的数据;\n\n* -g, 后边可以写一个正则匹配模式,大小写不敏感的;\n\n比如\n\n得到返回记录集最多的10个SQL。\n\nmysqldumpslow -s r -t 10 /database/mysql/mysql06_slow.log\n\n得到访问次数最多的10个SQL\n\nmysqldumpslow -s c -t 10 /database/mysql/mysql06_slow.log\n\n得到按照时间排序的前10条里面含有左连接的查询语句。\n\nmysqldumpslow -s t -t 10 -g “left join” /database/mysql/mysql06_slow.log\n\n另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。\n\nmysqldumpslow -s r -t 20 /mysqldata/mysql/mysql06-slow.log | more","slug":"mysql/mysql-慢日志查询分析","published":1,"updated":"2022-10-31T09:40:57.112Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnc001viysygoi1gz0t","content":"<h3 id=\"慢查询日志相关参数\"><a href=\"#慢查询日志相关参数\" class=\"headerlink\" title=\"慢查询日志相关参数\"></a>慢查询日志相关参数</h3><ul>\n<li><p>slow_query_log :是否开启慢查询日志,1表示开启,0表示关闭。</p>\n</li>\n<li><p>log-slow-queries :旧版(5.6以下版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log</p>\n</li>\n<li><p>slow-query-log-file:新版(5.6及以上版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log</p>\n</li>\n<li><p>long_query_time :慢查询阈值,当查询时间多于设定的阈值时,记录日志。</p>\n</li>\n<li><p>log_queries_not_using_indexes:未使用索引的查询也被记录到慢查询日志中(可选项)。</p>\n</li>\n<li><p>log_output:日志存储方式。log_output=’FILE’表示将日志存入文件,默认值是’FILE’。log_output=’TABLE’表示将日志存入数据库,这样日志信息就会被写入到mysql.slow_log表中。MySQL数据库支持同时两种日志存储方式,配置的时候以逗号隔开即可,如:log_output=’FILE,TABLE’。日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,因此对于需要启用慢查询日志,又需要能够获得更高的系统性能,那么建议优先记录到文件。</p>\n</li>\n</ul>\n<h3 id=\"日志分析工具-mysqldumpslow\"><a href=\"#日志分析工具-mysqldumpslow\" class=\"headerlink\" title=\"日志分析工具****mysqldumpslow\"></a><strong>日志分析工具****mysqldumpslow</strong></h3><p>在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow</p>\n<p>查看mysqldumpslow的帮助信息:</p>\n<pre><code class=\"shell\">[root@DB-Server ~]# mysqldumpslow --help\nUsage: mysqldumpslow [ OPTS... ] [ LOGS... ]\n \nParse and summarize the MySQL slow query log. Options are\n \n --verbose verbose\n --debug debug\n --help write this text to standard output\n \n -v verbose\n -d debug\n -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default\n al: average lock time\n ar: average rows sent\n at: average query time\n c: count\n l: lock time\n r: rows sent\n t: query time \n -r reverse the sort order (largest last instead of first)\n -t NUM just show the top n queries\n -a don't abstract all numbers to N and strings to 'S'\n -n NUM abstract numbers with at least n digits within names\n -g PATTERN grep: only consider stmts that include this string\n -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),\n default is '*', i.e. match all\n -i NAME name of server instance (if using mysql.server startup script)\n -l don't subtract lock time from total time\n</code></pre>\n<ul>\n<li>-s, 是表示按照何种方式排序,</li>\n</ul>\n<blockquote>\n<p>c: 访问计数</p>\n<p>l: 锁定时间</p>\n<p>r: 返回记录</p>\n<p>t: 查询时间</p>\n<p>al:平均锁定时间</p>\n<p>ar:平均返回记录数</p>\n<p>at:平均查询时间</p>\n</blockquote>\n<ul>\n<li><p>-t, 是top n的意思,即为返回前面多少条的数据;</p>\n</li>\n<li><p>-g, 后边可以写一个正则匹配模式,大小写不敏感的;</p>\n</li>\n</ul>\n<p>比如</p>\n<p>得到返回记录集最多的10个SQL。</p>\n<p>mysqldumpslow -s r -t 10 /database/mysql/mysql06_slow.log</p>\n<p>得到访问次数最多的10个SQL</p>\n<p>mysqldumpslow -s c -t 10 /database/mysql/mysql06_slow.log</p>\n<p>得到按照时间排序的前10条里面含有左连接的查询语句。</p>\n<p>mysqldumpslow -s t -t 10 -g “left join” /database/mysql/mysql06_slow.log</p>\n<p>另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。</p>\n<p>mysqldumpslow -s r -t 20 /mysqldata/mysql/mysql06-slow.log | more</p>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"慢查询日志相关参数\"><a href=\"#慢查询日志相关参数\" class=\"headerlink\" title=\"慢查询日志相关参数\"></a>慢查询日志相关参数</h3><ul>\n<li><p>slow_query_log :是否开启慢查询日志,1表示开启,0表示关闭。</p>\n</li>\n<li><p>log-slow-queries :旧版(5.6以下版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log</p>\n</li>\n<li><p>slow-query-log-file:新版(5.6及以上版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log</p>\n</li>\n<li><p>long_query_time :慢查询阈值,当查询时间多于设定的阈值时,记录日志。</p>\n</li>\n<li><p>log_queries_not_using_indexes:未使用索引的查询也被记录到慢查询日志中(可选项)。</p>\n</li>\n<li><p>log_output:日志存储方式。log_output=’FILE’表示将日志存入文件,默认值是’FILE’。log_output=’TABLE’表示将日志存入数据库,这样日志信息就会被写入到mysql.slow_log表中。MySQL数据库支持同时两种日志存储方式,配置的时候以逗号隔开即可,如:log_output=’FILE,TABLE’。日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,因此对于需要启用慢查询日志,又需要能够获得更高的系统性能,那么建议优先记录到文件。</p>\n</li>\n</ul>\n<h3 id=\"日志分析工具-mysqldumpslow\"><a href=\"#日志分析工具-mysqldumpslow\" class=\"headerlink\" title=\"日志分析工具****mysqldumpslow\"></a><strong>日志分析工具****mysqldumpslow</strong></h3><p>在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow</p>\n<p>查看mysqldumpslow的帮助信息:</p>\n<pre><code class=\"shell\">[root@DB-Server ~]# mysqldumpslow --help\nUsage: mysqldumpslow [ OPTS... ] [ LOGS... ]\n \nParse and summarize the MySQL slow query log. Options are\n \n --verbose verbose\n --debug debug\n --help write this text to standard output\n \n -v verbose\n -d debug\n -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default\n al: average lock time\n ar: average rows sent\n at: average query time\n c: count\n l: lock time\n r: rows sent\n t: query time \n -r reverse the sort order (largest last instead of first)\n -t NUM just show the top n queries\n -a don't abstract all numbers to N and strings to 'S'\n -n NUM abstract numbers with at least n digits within names\n -g PATTERN grep: only consider stmts that include this string\n -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),\n default is '*', i.e. match all\n -i NAME name of server instance (if using mysql.server startup script)\n -l don't subtract lock time from total time\n</code></pre>\n<ul>\n<li>-s, 是表示按照何种方式排序,</li>\n</ul>\n<blockquote>\n<p>c: 访问计数</p>\n<p>l: 锁定时间</p>\n<p>r: 返回记录</p>\n<p>t: 查询时间</p>\n<p>al:平均锁定时间</p>\n<p>ar:平均返回记录数</p>\n<p>at:平均查询时间</p>\n</blockquote>\n<ul>\n<li><p>-t, 是top n的意思,即为返回前面多少条的数据;</p>\n</li>\n<li><p>-g, 后边可以写一个正则匹配模式,大小写不敏感的;</p>\n</li>\n</ul>\n<p>比如</p>\n<p>得到返回记录集最多的10个SQL。</p>\n<p>mysqldumpslow -s r -t 10 /database/mysql/mysql06_slow.log</p>\n<p>得到访问次数最多的10个SQL</p>\n<p>mysqldumpslow -s c -t 10 /database/mysql/mysql06_slow.log</p>\n<p>得到按照时间排序的前10条里面含有左连接的查询语句。</p>\n<p>mysqldumpslow -s t -t 10 -g “left join” /database/mysql/mysql06_slow.log</p>\n<p>另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。</p>\n<p>mysqldumpslow -s r -t 20 /mysqldata/mysql/mysql06-slow.log | more</p>\n"},{"title":"ssh","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n## ssh\n\n### 生成公钥\n\n```shell\nssh-keygen -t rsa\n```\n\n\\-t 指定算法\n\\-f 指定生成秘钥路径\n\\-N 指定密码\n\n### 拷贝公钥\n\n```shell\ncd ~/.ssh\nscp id_rsa.pub root@B:/root/.ssh/authorized_keys #此命令在A机器执行,目的将公钥发送至B机器\nscp id_rsa.pub root@A:/root/.ssh/authorized_keys #此命令在B机器执行,目的将公钥发送至B机器\n```\n将你的SSH公钥复制到远程主机,开启无密码登录 – 简单的方法\n```shell\nssh-copy-id username@hostname\n```\n\n如果发现设置免密登陆,还需要输入密码,那么检查一下/root .ssh authorized\\_keys目录和文件的权限。\n\n```shell\nchmod 600 authorized_keys \nchmod 700 .ssh\n```\n\n如果authorized\\_keys文件、𝐻𝑂𝑀𝐸/.ssh 目录或HOME目录让本用户之外的用户有写权限,那么 sshd 都会拒绝使用 \\~/.ssh/authorized\\_keys 文件中的key来进行认证的。","source":"_posts/linux/ssh.md","raw":"---\ntitle: ssh\ntags:\n - ssh\n - linux\ncategories:\n - linux\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n## ssh\n\n### 生成公钥\n\n```shell\nssh-keygen -t rsa\n```\n\n\\-t 指定算法\n\\-f 指定生成秘钥路径\n\\-N 指定密码\n\n### 拷贝公钥\n\n```shell\ncd ~/.ssh\nscp id_rsa.pub root@B:/root/.ssh/authorized_keys #此命令在A机器执行,目的将公钥发送至B机器\nscp id_rsa.pub root@A:/root/.ssh/authorized_keys #此命令在B机器执行,目的将公钥发送至B机器\n```\n将你的SSH公钥复制到远程主机,开启无密码登录 – 简单的方法\n```shell\nssh-copy-id username@hostname\n```\n\n如果发现设置免密登陆,还需要输入密码,那么检查一下/root .ssh authorized\\_keys目录和文件的权限。\n\n```shell\nchmod 600 authorized_keys \nchmod 700 .ssh\n```\n\n如果authorized\\_keys文件、𝐻𝑂𝑀𝐸/.ssh 目录或HOME目录让本用户之外的用户有写权限,那么 sshd 都会拒绝使用 \\~/.ssh/authorized\\_keys 文件中的key来进行认证的。","slug":"linux/ssh","published":1,"updated":"2022-11-01T03:00:59.190Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnd001ziysy5xnncfkp","content":"<h2 id=\"ssh\"><a href=\"#ssh\" class=\"headerlink\" title=\"ssh\"></a>ssh</h2><h3 id=\"生成公钥\"><a href=\"#生成公钥\" class=\"headerlink\" title=\"生成公钥\"></a>生成公钥</h3><pre><code class=\"shell\">ssh-keygen -t rsa\n</code></pre>\n<p>-t 指定算法<br>-f 指定生成秘钥路径<br>-N 指定密码</p>\n<h3 id=\"拷贝公钥\"><a href=\"#拷贝公钥\" class=\"headerlink\" title=\"拷贝公钥\"></a>拷贝公钥</h3><pre><code class=\"shell\">cd ~/.ssh\nscp id_rsa.pub root@B:/root/.ssh/authorized_keys #此命令在A机器执行,目的将公钥发送至B机器\nscp id_rsa.pub root@A:/root/.ssh/authorized_keys #此命令在B机器执行,目的将公钥发送至B机器\n</code></pre>\n<p>将你的SSH公钥复制到远程主机,开启无密码登录 – 简单的方法</p>\n<pre><code class=\"shell\">ssh-copy-id username@hostname\n</code></pre>\n<p>如果发现设置免密登陆,还需要输入密码,那么检查一下/root .ssh authorized_keys目录和文件的权限。</p>\n<pre><code class=\"shell\">chmod 600 authorized_keys \nchmod 700 .ssh\n</code></pre>\n<p>如果authorized_keys文件、𝐻𝑂𝑀𝐸/.ssh 目录或HOME目录让本用户之外的用户有写权限,那么 sshd 都会拒绝使用 ~/.ssh/authorized_keys 文件中的key来进行认证的。</p>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"ssh\"><a href=\"#ssh\" class=\"headerlink\" title=\"ssh\"></a>ssh</h2><h3 id=\"生成公钥\"><a href=\"#生成公钥\" class=\"headerlink\" title=\"生成公钥\"></a>生成公钥</h3><pre><code class=\"shell\">ssh-keygen -t rsa\n</code></pre>\n<p>-t 指定算法<br>-f 指定生成秘钥路径<br>-N 指定密码</p>\n<h3 id=\"拷贝公钥\"><a href=\"#拷贝公钥\" class=\"headerlink\" title=\"拷贝公钥\"></a>拷贝公钥</h3><pre><code class=\"shell\">cd ~/.ssh\nscp id_rsa.pub root@B:/root/.ssh/authorized_keys #此命令在A机器执行,目的将公钥发送至B机器\nscp id_rsa.pub root@A:/root/.ssh/authorized_keys #此命令在B机器执行,目的将公钥发送至B机器\n</code></pre>\n<p>将你的SSH公钥复制到远程主机,开启无密码登录 – 简单的方法</p>\n<pre><code class=\"shell\">ssh-copy-id username@hostname\n</code></pre>\n<p>如果发现设置免密登陆,还需要输入密码,那么检查一下/root .ssh authorized_keys目录和文件的权限。</p>\n<pre><code class=\"shell\">chmod 600 authorized_keys \nchmod 700 .ssh\n</code></pre>\n<p>如果authorized_keys文件、𝐻𝑂𝑀𝐸/.ssh 目录或HOME目录让本用户之外的用户有写权限,那么 sshd 都会拒绝使用 ~/.ssh/authorized_keys 文件中的key来进行认证的。</p>\n"},{"title":"python 正则表达式简介","date":"2017-11-13T09:21:05.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n\n### 1. 概念:\n正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。使用这个小型语言, 你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、email 地址、TeX命令或任何你想搞定的东西。然后你可以问诸如“这个字符串匹配该模式吗?”或“在这个字符串中是否有部分匹配该模式呢?”。你也可以使用 RE 以各种方式来修改或分割字符串。\n\n### 2. 在正则表达式中, 如下的字符是具有特殊含义的\n\t. (所有字符) ^ $ *(0-N次) +(1-N次) ? (0-1次) { } [ ] \\ | ( ) \n\t 1).\"[\" 和 \"]\"。它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集\n\t 2).其它地方的\"^\"只会简单匹配 \"^\"字符本身。例[^5] 将匹配除 \"5\" 之外的任意字符。\n\t 3).反斜杠后面可以加不同的字符以表示不同特殊意义。它也可以用于取消所有的元字符\n\n### 3. RE 函数用法:\n findall(rule , target [,flag] ) 在目标字符串中查找符合规则的字符串。\n match() 决定 RE 是否在字符串刚开始的位置匹配\n search() 扫描字符串,找到这个 RE 匹配的位置\n findall() 找到 RE 匹配的所有子串,并把它们作为一个列表返回\n finditer() 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回\n group() 返回被 RE 匹配的字符串\n start() 返回匹配开始的位置\n end() 返回匹配结束的位置\n span() 返回一个元组包含匹配 (开始,结束) 的位置\n compile( rule [,flag] )将正则规则编译成一个Pattern对象,以供接下来使用第一个参数是规则式,第二个参数是规则选项。(使用compile加速)\n\n### 4. 含义:\n\t预定义转义字符集: “\\d” “\\w” “\\s” 等等,它们是以字符’\\’开头,后面接一个特定\n\n字符的形式,用来指示一个预定义好的含义\n```\n\t‘^’ 和’$’ 匹配字符串开头和结尾\n\t‘.’ 匹配所有字符 除\\n以外\n\t‘\\d’ 匹配数字\n\t‘\\D’ 匹配非数字\n\t‘\\w’ 匹配字母和数字\n\t‘\\W’ 匹配非英文字母和数字\n\t‘\\s’ 匹配间隔符\n\t‘\\S’ 匹配非间隔符\n\t‘\\A’ 匹配字符串开头\n\t‘\\Z’ 匹配字符串结尾\n\t‘\\b’ 只用以匹配单词的词首和词尾。单词被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。(退格)\n\t‘\\B’,它正好同 \\b 相反,只在当前位置不在单词边界时匹配。\n```\n### 5. 前向界定与后向界定:\n```\n\t‘(?<=…)’ 前向界定:括号中’…’代表你希望匹配的字符串的前面应该出现的字符串。\n\t‘(?=…)’后向界定 :括号中的’…’代表你希望匹配的字符串后面应该出现的字符串\n\t‘(?<!..)’前向非界定 :只有当你希望的字符串前面不是’…’的内容时才匹配\n\t‘(?!...)’后向非界定 :只有当你希望的字符串后面不跟着’…’内容时才匹配。\n```\n### 6. 组的基本知识:\n```\n\t‘(‘’)’ 无命名组 [a-z]+(\\d+)[a-z]+\n\t‘(?P<name>…)’ 命名组 (?P<g1>[a-z]+)\\d+(?P=g1)\n\t‘(?P=name)’ 调用已匹配的命名组\n\t‘\\number’ 通过序号调用已匹配的组正则式中的每个组都有一个序号,序号是按组从左到右,从1开始的数字,你可以通过下面的形式来调用已匹配的组\n( r\"(\\d+)([a-z]+)(\\d+)(\\2)(\\1)\" ) \n```\n","source":"_posts/python/python 正则表达式简介.md","raw":"---\ntitle: python 正则表达式简介\ndate: 2017-11-13 17:21:05\ntags:\n- regex\n- python\n\ncategories:\n- python\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n\n### 1. 概念:\n正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。使用这个小型语言, 你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、email 地址、TeX命令或任何你想搞定的东西。然后你可以问诸如“这个字符串匹配该模式吗?”或“在这个字符串中是否有部分匹配该模式呢?”。你也可以使用 RE 以各种方式来修改或分割字符串。\n\n### 2. 在正则表达式中, 如下的字符是具有特殊含义的\n\t. (所有字符) ^ $ *(0-N次) +(1-N次) ? (0-1次) { } [ ] \\ | ( ) \n\t 1).\"[\" 和 \"]\"。它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集\n\t 2).其它地方的\"^\"只会简单匹配 \"^\"字符本身。例[^5] 将匹配除 \"5\" 之外的任意字符。\n\t 3).反斜杠后面可以加不同的字符以表示不同特殊意义。它也可以用于取消所有的元字符\n\n### 3. RE 函数用法:\n findall(rule , target [,flag] ) 在目标字符串中查找符合规则的字符串。\n match() 决定 RE 是否在字符串刚开始的位置匹配\n search() 扫描字符串,找到这个 RE 匹配的位置\n findall() 找到 RE 匹配的所有子串,并把它们作为一个列表返回\n finditer() 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回\n group() 返回被 RE 匹配的字符串\n start() 返回匹配开始的位置\n end() 返回匹配结束的位置\n span() 返回一个元组包含匹配 (开始,结束) 的位置\n compile( rule [,flag] )将正则规则编译成一个Pattern对象,以供接下来使用第一个参数是规则式,第二个参数是规则选项。(使用compile加速)\n\n### 4. 含义:\n\t预定义转义字符集: “\\d” “\\w” “\\s” 等等,它们是以字符’\\’开头,后面接一个特定\n\n字符的形式,用来指示一个预定义好的含义\n```\n\t‘^’ 和’$’ 匹配字符串开头和结尾\n\t‘.’ 匹配所有字符 除\\n以外\n\t‘\\d’ 匹配数字\n\t‘\\D’ 匹配非数字\n\t‘\\w’ 匹配字母和数字\n\t‘\\W’ 匹配非英文字母和数字\n\t‘\\s’ 匹配间隔符\n\t‘\\S’ 匹配非间隔符\n\t‘\\A’ 匹配字符串开头\n\t‘\\Z’ 匹配字符串结尾\n\t‘\\b’ 只用以匹配单词的词首和词尾。单词被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。(退格)\n\t‘\\B’,它正好同 \\b 相反,只在当前位置不在单词边界时匹配。\n```\n### 5. 前向界定与后向界定:\n```\n\t‘(?<=…)’ 前向界定:括号中’…’代表你希望匹配的字符串的前面应该出现的字符串。\n\t‘(?=…)’后向界定 :括号中的’…’代表你希望匹配的字符串后面应该出现的字符串\n\t‘(?<!..)’前向非界定 :只有当你希望的字符串前面不是’…’的内容时才匹配\n\t‘(?!...)’后向非界定 :只有当你希望的字符串后面不跟着’…’内容时才匹配。\n```\n### 6. 组的基本知识:\n```\n\t‘(‘’)’ 无命名组 [a-z]+(\\d+)[a-z]+\n\t‘(?P<name>…)’ 命名组 (?P<g1>[a-z]+)\\d+(?P=g1)\n\t‘(?P=name)’ 调用已匹配的命名组\n\t‘\\number’ 通过序号调用已匹配的组正则式中的每个组都有一个序号,序号是按组从左到右,从1开始的数字,你可以通过下面的形式来调用已匹配的组\n( r\"(\\d+)([a-z]+)(\\d+)(\\2)(\\1)\" ) \n```\n","slug":"python/python 正则表达式简介","published":1,"updated":"2022-11-01T02:43:38.057Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnd0021iysy6gep6h8o","content":"<h3 id=\"1-概念:\"><a href=\"#1-概念:\" class=\"headerlink\" title=\"1. 概念:\"></a>1. 概念:</h3><p>正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。使用这个小型语言, 你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、email 地址、TeX命令或任何你想搞定的东西。然后你可以问诸如“这个字符串匹配该模式吗?”或“在这个字符串中是否有部分匹配该模式呢?”。你也可以使用 RE 以各种方式来修改或分割字符串。</p>\n<h3 id=\"2-在正则表达式中,-如下的字符是具有特殊含义的\"><a href=\"#2-在正则表达式中,-如下的字符是具有特殊含义的\" class=\"headerlink\" title=\"2. 在正则表达式中, 如下的字符是具有特殊含义的\"></a>2. 在正则表达式中, 如下的字符是具有特殊含义的</h3><pre><code>. (所有字符) ^ $ *(0-N次) +(1-N次) ? (0-1次) { } [ ] \\ | ( ) \n 1)."[" 和 "]"。它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集\n 2).其它地方的"^"只会简单匹配 "^"字符本身。例[^5] 将匹配除 "5" 之外的任意字符。\n 3).反斜杠后面可以加不同的字符以表示不同特殊意义。它也可以用于取消所有的元字符\n</code></pre>\n<h3 id=\"3-RE-函数用法\"><a href=\"#3-RE-函数用法\" class=\"headerlink\" title=\"3. RE 函数用法:\"></a>3. RE 函数用法:</h3><pre><code> findall(rule , target [,flag] ) 在目标字符串中查找符合规则的字符串。\n match() 决定 RE 是否在字符串刚开始的位置匹配\n search() 扫描字符串,找到这个 RE 匹配的位置\n findall() 找到 RE 匹配的所有子串,并把它们作为一个列表返回\n finditer() 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回\n group() 返回被 RE 匹配的字符串\n start() 返回匹配开始的位置\n end() 返回匹配结束的位置\n span() 返回一个元组包含匹配 (开始,结束) 的位置\n compile( rule [,flag] )将正则规则编译成一个Pattern对象,以供接下来使用第一个参数是规则式,第二个参数是规则选项。(使用compile加速)\n</code></pre>\n<h3 id=\"4-含义\"><a href=\"#4-含义\" class=\"headerlink\" title=\"4. 含义:\"></a>4. 含义:</h3><pre><code>预定义转义字符集: “\\d” “\\w” “\\s” 等等,它们是以字符’\\’开头,后面接一个特定\n</code></pre>\n<p>字符的形式,用来指示一个预定义好的含义</p>\n<pre><code> ‘^’ 和’$’ 匹配字符串开头和结尾\n ‘.’ 匹配所有字符 除\\n以外\n ‘\\d’ 匹配数字\n ‘\\D’ 匹配非数字\n ‘\\w’ 匹配字母和数字\n ‘\\W’ 匹配非英文字母和数字\n ‘\\s’ 匹配间隔符\n ‘\\S’ 匹配非间隔符\n ‘\\A’ 匹配字符串开头\n ‘\\Z’ 匹配字符串结尾\n ‘\\b’ 只用以匹配单词的词首和词尾。单词被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。(退格)\n ‘\\B’,它正好同 \\b 相反,只在当前位置不在单词边界时匹配。\n</code></pre>\n<h3 id=\"5-前向界定与后向界定\"><a href=\"#5-前向界定与后向界定\" class=\"headerlink\" title=\"5. 前向界定与后向界定:\"></a>5. 前向界定与后向界定:</h3><pre><code> ‘(?<=…)’ 前向界定:括号中’…’代表你希望匹配的字符串的前面应该出现的字符串。\n ‘(?=…)’后向界定 :括号中的’…’代表你希望匹配的字符串后面应该出现的字符串\n ‘(?<!..)’前向非界定 :只有当你希望的字符串前面不是’…’的内容时才匹配\n ‘(?!...)’后向非界定 :只有当你希望的字符串后面不跟着’…’内容时才匹配。\n</code></pre>\n<h3 id=\"6-组的基本知识\"><a href=\"#6-组的基本知识\" class=\"headerlink\" title=\"6. 组的基本知识:\"></a>6. 组的基本知识:</h3><pre><code> ‘(‘’)’ 无命名组 [a-z]+(\\d+)[a-z]+\n ‘(?P<name>…)’ 命名组 (?P<g1>[a-z]+)\\d+(?P=g1)\n ‘(?P=name)’ 调用已匹配的命名组\n ‘\\number’ 通过序号调用已匹配的组正则式中的每个组都有一个序号,序号是按组从左到右,从1开始的数字,你可以通过下面的形式来调用已匹配的组\n( r"(\\d+)([a-z]+)(\\d+)(\\2)(\\1)" ) \n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"1-概念:\"><a href=\"#1-概念:\" class=\"headerlink\" title=\"1. 概念:\"></a>1. 概念:</h3><p>正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。使用这个小型语言, 你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、email 地址、TeX命令或任何你想搞定的东西。然后你可以问诸如“这个字符串匹配该模式吗?”或“在这个字符串中是否有部分匹配该模式呢?”。你也可以使用 RE 以各种方式来修改或分割字符串。</p>\n<h3 id=\"2-在正则表达式中,-如下的字符是具有特殊含义的\"><a href=\"#2-在正则表达式中,-如下的字符是具有特殊含义的\" class=\"headerlink\" title=\"2. 在正则表达式中, 如下的字符是具有特殊含义的\"></a>2. 在正则表达式中, 如下的字符是具有特殊含义的</h3><pre><code>. (所有字符) ^ $ *(0-N次) +(1-N次) ? (0-1次) { } [ ] \\ | ( ) \n 1)."[" 和 "]"。它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集\n 2).其它地方的"^"只会简单匹配 "^"字符本身。例[^5] 将匹配除 "5" 之外的任意字符。\n 3).反斜杠后面可以加不同的字符以表示不同特殊意义。它也可以用于取消所有的元字符\n</code></pre>\n<h3 id=\"3-RE-函数用法\"><a href=\"#3-RE-函数用法\" class=\"headerlink\" title=\"3. RE 函数用法:\"></a>3. RE 函数用法:</h3><pre><code> findall(rule , target [,flag] ) 在目标字符串中查找符合规则的字符串。\n match() 决定 RE 是否在字符串刚开始的位置匹配\n search() 扫描字符串,找到这个 RE 匹配的位置\n findall() 找到 RE 匹配的所有子串,并把它们作为一个列表返回\n finditer() 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回\n group() 返回被 RE 匹配的字符串\n start() 返回匹配开始的位置\n end() 返回匹配结束的位置\n span() 返回一个元组包含匹配 (开始,结束) 的位置\n compile( rule [,flag] )将正则规则编译成一个Pattern对象,以供接下来使用第一个参数是规则式,第二个参数是规则选项。(使用compile加速)\n</code></pre>\n<h3 id=\"4-含义\"><a href=\"#4-含义\" class=\"headerlink\" title=\"4. 含义:\"></a>4. 含义:</h3><pre><code>预定义转义字符集: “\\d” “\\w” “\\s” 等等,它们是以字符’\\’开头,后面接一个特定\n</code></pre>\n<p>字符的形式,用来指示一个预定义好的含义</p>\n<pre><code> ‘^’ 和’$’ 匹配字符串开头和结尾\n ‘.’ 匹配所有字符 除\\n以外\n ‘\\d’ 匹配数字\n ‘\\D’ 匹配非数字\n ‘\\w’ 匹配字母和数字\n ‘\\W’ 匹配非英文字母和数字\n ‘\\s’ 匹配间隔符\n ‘\\S’ 匹配非间隔符\n ‘\\A’ 匹配字符串开头\n ‘\\Z’ 匹配字符串结尾\n ‘\\b’ 只用以匹配单词的词首和词尾。单词被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。(退格)\n ‘\\B’,它正好同 \\b 相反,只在当前位置不在单词边界时匹配。\n</code></pre>\n<h3 id=\"5-前向界定与后向界定\"><a href=\"#5-前向界定与后向界定\" class=\"headerlink\" title=\"5. 前向界定与后向界定:\"></a>5. 前向界定与后向界定:</h3><pre><code> ‘(?<=…)’ 前向界定:括号中’…’代表你希望匹配的字符串的前面应该出现的字符串。\n ‘(?=…)’后向界定 :括号中的’…’代表你希望匹配的字符串后面应该出现的字符串\n ‘(?<!..)’前向非界定 :只有当你希望的字符串前面不是’…’的内容时才匹配\n ‘(?!...)’后向非界定 :只有当你希望的字符串后面不跟着’…’内容时才匹配。\n</code></pre>\n<h3 id=\"6-组的基本知识\"><a href=\"#6-组的基本知识\" class=\"headerlink\" title=\"6. 组的基本知识:\"></a>6. 组的基本知识:</h3><pre><code> ‘(‘’)’ 无命名组 [a-z]+(\\d+)[a-z]+\n ‘(?P<name>…)’ 命名组 (?P<g1>[a-z]+)\\d+(?P=g1)\n ‘(?P=name)’ 调用已匹配的命名组\n ‘\\number’ 通过序号调用已匹配的组正则式中的每个组都有一个序号,序号是按组从左到右,从1开始的数字,你可以通过下面的形式来调用已匹配的组\n( r"(\\d+)([a-z]+)(\\d+)(\\2)(\\1)" ) \n</code></pre>\n"},{"title":"uvicorn 初识","date":"2021-09-09T09:10:23.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n## uvicorn简介\n\n`uvicorn`是一个基于`asyncio`开发的一个轻量级高效的web服务器框架。\n\n官方地址: [http://www.uvicorn.org/](http://www.uvicorn.org/)\n\n`uvicorn` 设计的初衷是想要实现两个目标:\n\n- 使用[`uvloop`](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FMagicStack%2Fuvloop)和[`httptools`](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FMagicStack%2Fhttptools)实现一个极速的`asyncio`服务器。\n- 实现一个基于[`ASGI(异步服务器网关接口)`](https://link.juejin.cn?target=http%3A%2F%2Fchannels.readthedocs.io%2Fen%2Fstable%2Fasgi.html)的最小的应用程序接口。\n\n它目前支持`http`,`websockets`,`Pub/Sub` 广播,并且可以扩展到其他协议和消息类型。\n\n","source":"_posts/python/uvicorn-初识.md","raw":"---\ntitle: uvicorn 初识\n\ndate: 2021-09-09 17:10:23\ntags:\n- python\n- web\ncategories:\n- python\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n## uvicorn简介\n\n`uvicorn`是一个基于`asyncio`开发的一个轻量级高效的web服务器框架。\n\n官方地址: [http://www.uvicorn.org/](http://www.uvicorn.org/)\n\n`uvicorn` 设计的初衷是想要实现两个目标:\n\n- 使用[`uvloop`](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FMagicStack%2Fuvloop)和[`httptools`](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FMagicStack%2Fhttptools)实现一个极速的`asyncio`服务器。\n- 实现一个基于[`ASGI(异步服务器网关接口)`](https://link.juejin.cn?target=http%3A%2F%2Fchannels.readthedocs.io%2Fen%2Fstable%2Fasgi.html)的最小的应用程序接口。\n\n它目前支持`http`,`websockets`,`Pub/Sub` 广播,并且可以扩展到其他协议和消息类型。\n\n","slug":"python/uvicorn-初识","published":1,"updated":"2022-10-31T09:40:57.113Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnf0025iysy3ba17agn","content":"<h2 id=\"uvicorn简介\"><a href=\"#uvicorn简介\" class=\"headerlink\" title=\"uvicorn简介\"></a>uvicorn简介</h2><p><code>uvicorn</code>是一个基于<code>asyncio</code>开发的一个轻量级高效的web服务器框架。</p>\n<p>官方地址: <a href=\"http://www.uvicorn.org/\">http://www.uvicorn.org/</a></p>\n<p><code>uvicorn</code> 设计的初衷是想要实现两个目标:</p>\n<ul>\n<li>使用<a href=\"https://link.juejin.cn/?target=https://github.com/MagicStack/uvloop\"><code>uvloop</code></a>和<a href=\"https://link.juejin.cn/?target=https://github.com/MagicStack/httptools\"><code>httptools</code></a>实现一个极速的<code>asyncio</code>服务器。</li>\n<li>实现一个基于<a href=\"https://link.juejin.cn/?target=http://channels.readthedocs.io/en/stable/asgi.html\"><code>ASGI(异步服务器网关接口)</code></a>的最小的应用程序接口。</li>\n</ul>\n<p>它目前支持<code>http</code>,<code>websockets</code>,<code>Pub/Sub</code> 广播,并且可以扩展到其他协议和消息类型。</p>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"uvicorn简介\"><a href=\"#uvicorn简介\" class=\"headerlink\" title=\"uvicorn简介\"></a>uvicorn简介</h2><p><code>uvicorn</code>是一个基于<code>asyncio</code>开发的一个轻量级高效的web服务器框架。</p>\n<p>官方地址: <a href=\"http://www.uvicorn.org/\">http://www.uvicorn.org/</a></p>\n<p><code>uvicorn</code> 设计的初衷是想要实现两个目标:</p>\n<ul>\n<li>使用<a href=\"https://link.juejin.cn/?target=https://github.com/MagicStack/uvloop\"><code>uvloop</code></a>和<a href=\"https://link.juejin.cn/?target=https://github.com/MagicStack/httptools\"><code>httptools</code></a>实现一个极速的<code>asyncio</code>服务器。</li>\n<li>实现一个基于<a href=\"https://link.juejin.cn/?target=http://channels.readthedocs.io/en/stable/asgi.html\"><code>ASGI(异步服务器网关接口)</code></a>的最小的应用程序接口。</li>\n</ul>\n<p>它目前支持<code>http</code>,<code>websockets</code>,<code>Pub/Sub</code> 广播,并且可以扩展到其他协议和消息类型。</p>\n"},{"title":"redis 详解","auto_excerpt":{"enable":true,"length":150},"date":"2021-09-08T07:22:26.000Z","_content":"\n[toc]\n\n### redis 介绍\n\n官网地址:[https://redis.io/](https://redis.io/)\n\nRedis的全称是 **RE**mote **DI**ctionary **S**erver,是一个高效的内存键值数据库,常被用来做分布式的高速缓存,相比较我们常规使用的Mysql、MongoDB等数据库,Redis的最大特点在于数据读写全部在内存中进行,进而带来极大的效率优势。\n\n### redis 使用场景\n\n1. 作为缓存服务器(String, Hash)\n2. 作为消息队列(List)\n3. 数据排行榜(Sorted Set)\n4. 热点数据\n5. 计数器; 比如统计点击率、点赞率,Redis具有原子性,可以避免并发问题。\n\n以前只列出简单且常见的用途\n\n### redis 数据类型\n\n**String、List、Hash、Set、Sorted Set** 五种数据类型\n\n**String**\nString和我们常规理解的字符串基本一致,主要存储序列化后的字符串,支持写入原生字符串也支持写入数字类型。\n\n**List**\nList即为列表,List在Redis底层采用的是**双向链表**实现的,所以我们会发现Redis的List操作命令有左右之分,比如LPUSH、RPUSH,实际上就是双端列表左右两端的存取。\n\n**Hash**\nHash可以理解为我们常规使用的字典数据结构,Redis采用**散列表**来实现Hash, 一个Hash结构里面可以存在很多的key和value,Hash是Redis比较推荐使用的一种数据结构\n\n**Set**\nSet是集合,满足集合确定性、无序性、唯一性三个性质,可以用来进行元素的去重操作\n\n**Sorted Set**\nSorted Set是有序集合,满足集合唯一性的要求,同时也满足有序的性质。向Sorted Set中插入元素的时候需要同时指定一个Score,用于作为排序的标准, Sorted Set的底层实现采用的是**Skip List**\n\n### redis 命令\n\n---\n\n参考:\n\n1. [Redis详解(1)——为什么我们都需要了解Redis](https://zhuanlan.zhihu.com/p/94680529)\n\n---\n","source":"_posts/redis/redis-详解.md","raw":"---\ntitle: redis 详解\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-09-08 15:22:26\ntags:\ncategories:\n- redis\n---\n\n[toc]\n\n### redis 介绍\n\n官网地址:[https://redis.io/](https://redis.io/)\n\nRedis的全称是 **RE**mote **DI**ctionary **S**erver,是一个高效的内存键值数据库,常被用来做分布式的高速缓存,相比较我们常规使用的Mysql、MongoDB等数据库,Redis的最大特点在于数据读写全部在内存中进行,进而带来极大的效率优势。\n\n### redis 使用场景\n\n1. 作为缓存服务器(String, Hash)\n2. 作为消息队列(List)\n3. 数据排行榜(Sorted Set)\n4. 热点数据\n5. 计数器; 比如统计点击率、点赞率,Redis具有原子性,可以避免并发问题。\n\n以前只列出简单且常见的用途\n\n### redis 数据类型\n\n**String、List、Hash、Set、Sorted Set** 五种数据类型\n\n**String**\nString和我们常规理解的字符串基本一致,主要存储序列化后的字符串,支持写入原生字符串也支持写入数字类型。\n\n**List**\nList即为列表,List在Redis底层采用的是**双向链表**实现的,所以我们会发现Redis的List操作命令有左右之分,比如LPUSH、RPUSH,实际上就是双端列表左右两端的存取。\n\n**Hash**\nHash可以理解为我们常规使用的字典数据结构,Redis采用**散列表**来实现Hash, 一个Hash结构里面可以存在很多的key和value,Hash是Redis比较推荐使用的一种数据结构\n\n**Set**\nSet是集合,满足集合确定性、无序性、唯一性三个性质,可以用来进行元素的去重操作\n\n**Sorted Set**\nSorted Set是有序集合,满足集合唯一性的要求,同时也满足有序的性质。向Sorted Set中插入元素的时候需要同时指定一个Score,用于作为排序的标准, Sorted Set的底层实现采用的是**Skip List**\n\n### redis 命令\n\n---\n\n参考:\n\n1. [Redis详解(1)——为什么我们都需要了解Redis](https://zhuanlan.zhihu.com/p/94680529)\n\n---\n","slug":"redis/redis-详解","published":1,"updated":"2022-11-01T02:43:46.905Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpng0028iysy74hb8zx5","content":"<p>[toc]</p>\n<h3 id=\"redis-介绍\"><a href=\"#redis-介绍\" class=\"headerlink\" title=\"redis 介绍\"></a>redis 介绍</h3><p>官网地址:<a href=\"https://redis.io/\">https://redis.io/</a></p>\n<p>Redis的全称是 <strong>RE</strong>mote <strong>DI</strong>ctionary <strong>S</strong>erver,是一个高效的内存键值数据库,常被用来做分布式的高速缓存,相比较我们常规使用的Mysql、MongoDB等数据库,Redis的最大特点在于数据读写全部在内存中进行,进而带来极大的效率优势。</p>\n<h3 id=\"redis-使用场景\"><a href=\"#redis-使用场景\" class=\"headerlink\" title=\"redis 使用场景\"></a>redis 使用场景</h3><ol>\n<li>作为缓存服务器(String, Hash)</li>\n<li>作为消息队列(List)</li>\n<li>数据排行榜(Sorted Set)</li>\n<li>热点数据</li>\n<li>计数器; 比如统计点击率、点赞率,Redis具有原子性,可以避免并发问题。</li>\n</ol>\n<p>以前只列出简单且常见的用途</p>\n<h3 id=\"redis-数据类型\"><a href=\"#redis-数据类型\" class=\"headerlink\" title=\"redis 数据类型\"></a>redis 数据类型</h3><p><strong>String、List、Hash、Set、Sorted Set</strong> 五种数据类型</p>\n<p><strong>String</strong><br>String和我们常规理解的字符串基本一致,主要存储序列化后的字符串,支持写入原生字符串也支持写入数字类型。</p>\n<p><strong>List</strong><br>List即为列表,List在Redis底层采用的是<strong>双向链表</strong>实现的,所以我们会发现Redis的List操作命令有左右之分,比如LPUSH、RPUSH,实际上就是双端列表左右两端的存取。</p>\n<p><strong>Hash</strong><br>Hash可以理解为我们常规使用的字典数据结构,Redis采用<strong>散列表</strong>来实现Hash, 一个Hash结构里面可以存在很多的key和value,Hash是Redis比较推荐使用的一种数据结构</p>\n<p><strong>Set</strong><br>Set是集合,满足集合确定性、无序性、唯一性三个性质,可以用来进行元素的去重操作</p>\n<p><strong>Sorted Set</strong><br>Sorted Set是有序集合,满足集合唯一性的要求,同时也满足有序的性质。向Sorted Set中插入元素的时候需要同时指定一个Score,用于作为排序的标准, Sorted Set的底层实现采用的是<strong>Skip List</strong></p>\n<h3 id=\"redis-命令\"><a href=\"#redis-命令\" class=\"headerlink\" title=\"redis 命令\"></a>redis 命令</h3><hr>\n<p>参考:</p>\n<ol>\n<li><a href=\"https://zhuanlan.zhihu.com/p/94680529\">Redis详解(1)——为什么我们都需要了解Redis</a></li>\n</ol>\n<hr>\n","site":{"data":{}},"excerpt":"","more":"<p>[toc]</p>\n<h3 id=\"redis-介绍\"><a href=\"#redis-介绍\" class=\"headerlink\" title=\"redis 介绍\"></a>redis 介绍</h3><p>官网地址:<a href=\"https://redis.io/\">https://redis.io/</a></p>\n<p>Redis的全称是 <strong>RE</strong>mote <strong>DI</strong>ctionary <strong>S</strong>erver,是一个高效的内存键值数据库,常被用来做分布式的高速缓存,相比较我们常规使用的Mysql、MongoDB等数据库,Redis的最大特点在于数据读写全部在内存中进行,进而带来极大的效率优势。</p>\n<h3 id=\"redis-使用场景\"><a href=\"#redis-使用场景\" class=\"headerlink\" title=\"redis 使用场景\"></a>redis 使用场景</h3><ol>\n<li>作为缓存服务器(String, Hash)</li>\n<li>作为消息队列(List)</li>\n<li>数据排行榜(Sorted Set)</li>\n<li>热点数据</li>\n<li>计数器; 比如统计点击率、点赞率,Redis具有原子性,可以避免并发问题。</li>\n</ol>\n<p>以前只列出简单且常见的用途</p>\n<h3 id=\"redis-数据类型\"><a href=\"#redis-数据类型\" class=\"headerlink\" title=\"redis 数据类型\"></a>redis 数据类型</h3><p><strong>String、List、Hash、Set、Sorted Set</strong> 五种数据类型</p>\n<p><strong>String</strong><br>String和我们常规理解的字符串基本一致,主要存储序列化后的字符串,支持写入原生字符串也支持写入数字类型。</p>\n<p><strong>List</strong><br>List即为列表,List在Redis底层采用的是<strong>双向链表</strong>实现的,所以我们会发现Redis的List操作命令有左右之分,比如LPUSH、RPUSH,实际上就是双端列表左右两端的存取。</p>\n<p><strong>Hash</strong><br>Hash可以理解为我们常规使用的字典数据结构,Redis采用<strong>散列表</strong>来实现Hash, 一个Hash结构里面可以存在很多的key和value,Hash是Redis比较推荐使用的一种数据结构</p>\n<p><strong>Set</strong><br>Set是集合,满足集合确定性、无序性、唯一性三个性质,可以用来进行元素的去重操作</p>\n<p><strong>Sorted Set</strong><br>Sorted Set是有序集合,满足集合唯一性的要求,同时也满足有序的性质。向Sorted Set中插入元素的时候需要同时指定一个Score,用于作为排序的标准, Sorted Set的底层实现采用的是<strong>Skip List</strong></p>\n<h3 id=\"redis-命令\"><a href=\"#redis-命令\" class=\"headerlink\" title=\"redis 命令\"></a>redis 命令</h3><hr>\n<p>参考:</p>\n<ol>\n<li><a href=\"https://zhuanlan.zhihu.com/p/94680529\">Redis详解(1)——为什么我们都需要了解Redis</a></li>\n</ol>\n<hr>\n"},{"title":"http 状态码","date":"2018-04-18T09:21:05.000Z","updated":"2018-04-18T09:21:05.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n\nHTTP协议状态码表示的意思主要分为五类 ,大体是 :\n\n* 1×× 保留\n* 2×× 表示请求成功地接收\n* 3×× 为完成请求客户需进一步细化请求\n* 4×× 客户错误\n* 5×× 服务器错误\n\n#### 100 Continue\n指示客户端应该继续请求。回送用于通知客户端此次请求已经收到,并且没有被服务器拒绝。\n客户端应该继续发送剩下的请求数据或者请求已经完成,或者忽略回送数据。服务器必须发送\n最后的回送在请求之后。\n\n#### 101 Switching Protocols \n服务器依照客服端请求,通过Upgrade头信息,改变当前连接的应用协议。服务器将根据Upgrade头立刻改变协议\n\n在101回送以空行结束的时候。\n\n### Successful \n\n#### 200 OK\n指示客服端的请求已经成功收到,解析,接受。\n#### 201 Created \n请求已经完成并一个新的返回资源被创建。被创建的资源可能是一个URI资源,通常URI资源在Location头指定。回送应该包含一个实体数据\n并且包含资源特性以及location通过用户或者用户代理来选择合适的方法。实体数据格式通过煤体类型来指定即content-type头。最开始服务 器\n必须创建指定的资源在返回201状态码之前。如果行为没有被立刻执行,服务器应该返回202。\n#### 202 Accepted \n请求已经被接受用来处理。但是处理并没有完成。请求可能或者根本没有遵照执行,因为处理实际执行过程中可能被拒绝。\n#### 203 Non-Authoritative Information\n不是权威性信息。\n#### 204 No Content \n服务器已经接受请求并且没必要返回实体数据,可能需要返回更新信息。回送可能包含新的或更新信息由entity-headers呈现。\n#### 205 Reset Content \n服务器已经接受请求并且用户代理应该重新设置文档视图。\n#### 206 Partial Content \n服务器已经接受请求GET请求资源的部分。请求必须包含一个Range头信息以指示获取范围可能必须包含If-Range头信息以成立请求条件。\n### Redirection \n\n#### 300 Multiple Choices\n请求资源符合任何一个呈现方式。\n#### 301 Moved Permanently \n请求的资源已经被赋予一个新的URI。\n#### 302 Found \n通过不同的URI请求资源的临时文件。\n#### 303 See Other\n#### 304 Not Modified \n如果客服端已经完成一个有条件的请求并且请求是允许的,但是这个文档并没有改变,服务器应该返回304状态码。304\n状态码一定不能包含信息主体,从而通常通过一个头字段后的第一个空行结束。\n\n#### 305 Use Proxy\n请求的资源必须通过代理(由Location字段指定)来访问。Location资源给出了代理的URI。\n#### 306 Unused\n#### 307 Temporary Redirect\n临时重定向。\n### Client Error\n\n#### 400 Bad Request\n因为错误的语法导致服务器无法理解请求信息。\n#### 401 Unauthorized \n如果请求需要用户验证。回送应该包含一个WWW-Authenticate头字段用来指明请求资源的权限。\n#### 402 Payment Required \n保留状态码。\n#### 403 Forbidden \n服务器接受请求,但是被拒绝处理。\n#### 404 Not Found \n服务器已经找到任何匹配Request-URI的资源。\n#### 405 Menthod Not Allowed \nRequest-Line 请求的方法不被允许通过指定的URI。\n#### 406 Not Acceptable\n客户端浏览器不接受所请求页面的 MIME 类型。\n#### 407 Proxy Authentication Required\n要求进行代理身份验证。\n#### 408 Reqeust Timeout \n客服端没有提交任何请求在服务器等待处理时间内。\n#### 409 Conflict\n#### 410 Gone\n#### 411 Length Required \n服务器拒绝接受请求在没有定义Content-Length字段的情况下。\n#### 412 Precondition Failed\n前提条件失败。\n#### 413 Request Entity Too Large \n服务器拒绝处理请求因为请求数据超过服务器能够处理的范围。服务器可能关闭当前连接来阻止客服端继续请求。\n#### 414 Request-URI Too Long \n服务器拒绝服务当前请求因为URI的长度超过了服务器的解析范围。\n#### 415 Unsupported Media Type \n服务器拒绝服务当前请求因为请求数据格式并不被请求的资源支持。\n#### 416 Request Range Not Satisfialbe\n所请求的范围无法满足。\n#### 417 Expectation Failed\n执行失败。\n### Server Error \n\n#### 500 Internal Server Error\n服务器遭遇异常阻止了当前请求的执行\n#### 501 Not Implemented \n服务器没有相应的执行动作来完成当前请求。\n#### 502 Bad Gateway\nWeb 服务器用作网关或代理服务器时收到了无效响应。\n#### 503 Service Unavailable \n因为临时文件超载导致服务器不能处理当前请求。\n#### 504 Gateway Timeout\n网关访问超时。\n#### 505 Http Version Not Supported\nHTTP 版本不受支持。\n\n```\n\"100\" : Continue\n\"101\" : witching Protocols\n\"200\" : OK\n\"201\" : Created\n\"202\" : Accepted\n\"203\" : Non-Authoritative Information\n\"204\" : No Content\n\"205\" : Reset Content\n\"206\" : Partial Content\n\"300\" : Multiple Choices\n\"301\" : Moved Permanently\n\"302\" : Found\n\"303\" : See Other\n\"304\" : Not Modified\n\"305\" : Use Proxy\n\"307\" : Temporary Redirect\n\"400\" : Bad Request\n\"401\" : Unauthorized\n\"402\" : Payment Required\n\"403\" : Forbidden\n\"404\" : Not Found\n\"405\" : Method Not Allowed\n\"406\" : Not Acceptable\n\"407\" : Proxy Authentication Required\n\"408\" : Request Time-out\n\"409\" : Conflict\n\"410\" : Gone\n\"411\" : Length Required\n\"412\" : Precondition Failed\n\"413\" : Request Entity Too Large\n\"414\" : Request-URI Too Large\n\"415\" : Unsupported Media Type\n\"416\" : Requested range not satisfiable\n\"417\" : Expectation Failed\n\"500\" : Internal Server Error\n\"501\" : Not Implemented\n\"502\" : Bad Gateway\n\"503\" : Service Unavailable\n\"504\" : Gateway Time-out\n\"505\" : HTTP Version not supported\n```\n\n","source":"_posts/其他/http 状态码.md","raw":"---\ntitle: http 状态码\ndate: 2018-04-18 17:21:05\nupdated: 2018-04-18 17:21:05\ntags:\n- http\n- web\ncategories:\n- 其他\n\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n\nHTTP协议状态码表示的意思主要分为五类 ,大体是 :\n\n* 1×× 保留\n* 2×× 表示请求成功地接收\n* 3×× 为完成请求客户需进一步细化请求\n* 4×× 客户错误\n* 5×× 服务器错误\n\n#### 100 Continue\n指示客户端应该继续请求。回送用于通知客户端此次请求已经收到,并且没有被服务器拒绝。\n客户端应该继续发送剩下的请求数据或者请求已经完成,或者忽略回送数据。服务器必须发送\n最后的回送在请求之后。\n\n#### 101 Switching Protocols \n服务器依照客服端请求,通过Upgrade头信息,改变当前连接的应用协议。服务器将根据Upgrade头立刻改变协议\n\n在101回送以空行结束的时候。\n\n### Successful \n\n#### 200 OK\n指示客服端的请求已经成功收到,解析,接受。\n#### 201 Created \n请求已经完成并一个新的返回资源被创建。被创建的资源可能是一个URI资源,通常URI资源在Location头指定。回送应该包含一个实体数据\n并且包含资源特性以及location通过用户或者用户代理来选择合适的方法。实体数据格式通过煤体类型来指定即content-type头。最开始服务 器\n必须创建指定的资源在返回201状态码之前。如果行为没有被立刻执行,服务器应该返回202。\n#### 202 Accepted \n请求已经被接受用来处理。但是处理并没有完成。请求可能或者根本没有遵照执行,因为处理实际执行过程中可能被拒绝。\n#### 203 Non-Authoritative Information\n不是权威性信息。\n#### 204 No Content \n服务器已经接受请求并且没必要返回实体数据,可能需要返回更新信息。回送可能包含新的或更新信息由entity-headers呈现。\n#### 205 Reset Content \n服务器已经接受请求并且用户代理应该重新设置文档视图。\n#### 206 Partial Content \n服务器已经接受请求GET请求资源的部分。请求必须包含一个Range头信息以指示获取范围可能必须包含If-Range头信息以成立请求条件。\n### Redirection \n\n#### 300 Multiple Choices\n请求资源符合任何一个呈现方式。\n#### 301 Moved Permanently \n请求的资源已经被赋予一个新的URI。\n#### 302 Found \n通过不同的URI请求资源的临时文件。\n#### 303 See Other\n#### 304 Not Modified \n如果客服端已经完成一个有条件的请求并且请求是允许的,但是这个文档并没有改变,服务器应该返回304状态码。304\n状态码一定不能包含信息主体,从而通常通过一个头字段后的第一个空行结束。\n\n#### 305 Use Proxy\n请求的资源必须通过代理(由Location字段指定)来访问。Location资源给出了代理的URI。\n#### 306 Unused\n#### 307 Temporary Redirect\n临时重定向。\n### Client Error\n\n#### 400 Bad Request\n因为错误的语法导致服务器无法理解请求信息。\n#### 401 Unauthorized \n如果请求需要用户验证。回送应该包含一个WWW-Authenticate头字段用来指明请求资源的权限。\n#### 402 Payment Required \n保留状态码。\n#### 403 Forbidden \n服务器接受请求,但是被拒绝处理。\n#### 404 Not Found \n服务器已经找到任何匹配Request-URI的资源。\n#### 405 Menthod Not Allowed \nRequest-Line 请求的方法不被允许通过指定的URI。\n#### 406 Not Acceptable\n客户端浏览器不接受所请求页面的 MIME 类型。\n#### 407 Proxy Authentication Required\n要求进行代理身份验证。\n#### 408 Reqeust Timeout \n客服端没有提交任何请求在服务器等待处理时间内。\n#### 409 Conflict\n#### 410 Gone\n#### 411 Length Required \n服务器拒绝接受请求在没有定义Content-Length字段的情况下。\n#### 412 Precondition Failed\n前提条件失败。\n#### 413 Request Entity Too Large \n服务器拒绝处理请求因为请求数据超过服务器能够处理的范围。服务器可能关闭当前连接来阻止客服端继续请求。\n#### 414 Request-URI Too Long \n服务器拒绝服务当前请求因为URI的长度超过了服务器的解析范围。\n#### 415 Unsupported Media Type \n服务器拒绝服务当前请求因为请求数据格式并不被请求的资源支持。\n#### 416 Request Range Not Satisfialbe\n所请求的范围无法满足。\n#### 417 Expectation Failed\n执行失败。\n### Server Error \n\n#### 500 Internal Server Error\n服务器遭遇异常阻止了当前请求的执行\n#### 501 Not Implemented \n服务器没有相应的执行动作来完成当前请求。\n#### 502 Bad Gateway\nWeb 服务器用作网关或代理服务器时收到了无效响应。\n#### 503 Service Unavailable \n因为临时文件超载导致服务器不能处理当前请求。\n#### 504 Gateway Timeout\n网关访问超时。\n#### 505 Http Version Not Supported\nHTTP 版本不受支持。\n\n```\n\"100\" : Continue\n\"101\" : witching Protocols\n\"200\" : OK\n\"201\" : Created\n\"202\" : Accepted\n\"203\" : Non-Authoritative Information\n\"204\" : No Content\n\"205\" : Reset Content\n\"206\" : Partial Content\n\"300\" : Multiple Choices\n\"301\" : Moved Permanently\n\"302\" : Found\n\"303\" : See Other\n\"304\" : Not Modified\n\"305\" : Use Proxy\n\"307\" : Temporary Redirect\n\"400\" : Bad Request\n\"401\" : Unauthorized\n\"402\" : Payment Required\n\"403\" : Forbidden\n\"404\" : Not Found\n\"405\" : Method Not Allowed\n\"406\" : Not Acceptable\n\"407\" : Proxy Authentication Required\n\"408\" : Request Time-out\n\"409\" : Conflict\n\"410\" : Gone\n\"411\" : Length Required\n\"412\" : Precondition Failed\n\"413\" : Request Entity Too Large\n\"414\" : Request-URI Too Large\n\"415\" : Unsupported Media Type\n\"416\" : Requested range not satisfiable\n\"417\" : Expectation Failed\n\"500\" : Internal Server Error\n\"501\" : Not Implemented\n\"502\" : Bad Gateway\n\"503\" : Service Unavailable\n\"504\" : Gateway Time-out\n\"505\" : HTTP Version not supported\n```\n\n","slug":"其他/http 状态码","published":1,"comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnh002diysygp6b5rm1","content":"<p>HTTP协议状态码表示的意思主要分为五类 ,大体是 :</p>\n<ul>\n<li>1×× 保留</li>\n<li>2×× 表示请求成功地接收</li>\n<li>3×× 为完成请求客户需进一步细化请求</li>\n<li>4×× 客户错误</li>\n<li>5×× 服务器错误</li>\n</ul>\n<h4 id=\"100-Continue\"><a href=\"#100-Continue\" class=\"headerlink\" title=\"100 Continue\"></a>100 Continue</h4><p>指示客户端应该继续请求。回送用于通知客户端此次请求已经收到,并且没有被服务器拒绝。<br>客户端应该继续发送剩下的请求数据或者请求已经完成,或者忽略回送数据。服务器必须发送<br>最后的回送在请求之后。</p>\n<h4 id=\"101-Switching-Protocols\"><a href=\"#101-Switching-Protocols\" class=\"headerlink\" title=\"101 Switching Protocols\"></a>101 Switching Protocols</h4><p>服务器依照客服端请求,通过Upgrade头信息,改变当前连接的应用协议。服务器将根据Upgrade头立刻改变协议</p>\n<p>在101回送以空行结束的时候。</p>\n<h3 id=\"Successful\"><a href=\"#Successful\" class=\"headerlink\" title=\"Successful\"></a>Successful</h3><h4 id=\"200-OK\"><a href=\"#200-OK\" class=\"headerlink\" title=\"200 OK\"></a>200 OK</h4><p>指示客服端的请求已经成功收到,解析,接受。</p>\n<h4 id=\"201-Created\"><a href=\"#201-Created\" class=\"headerlink\" title=\"201 Created\"></a>201 Created</h4><p>请求已经完成并一个新的返回资源被创建。被创建的资源可能是一个URI资源,通常URI资源在Location头指定。回送应该包含一个实体数据<br>并且包含资源特性以及location通过用户或者用户代理来选择合适的方法。实体数据格式通过煤体类型来指定即content-type头。最开始服务 器<br>必须创建指定的资源在返回201状态码之前。如果行为没有被立刻执行,服务器应该返回202。</p>\n<h4 id=\"202-Accepted\"><a href=\"#202-Accepted\" class=\"headerlink\" title=\"202 Accepted\"></a>202 Accepted</h4><p>请求已经被接受用来处理。但是处理并没有完成。请求可能或者根本没有遵照执行,因为处理实际执行过程中可能被拒绝。</p>\n<h4 id=\"203-Non-Authoritative-Information\"><a href=\"#203-Non-Authoritative-Information\" class=\"headerlink\" title=\"203 Non-Authoritative Information\"></a>203 Non-Authoritative Information</h4><p>不是权威性信息。</p>\n<h4 id=\"204-No-Content\"><a href=\"#204-No-Content\" class=\"headerlink\" title=\"204 No Content\"></a>204 No Content</h4><p>服务器已经接受请求并且没必要返回实体数据,可能需要返回更新信息。回送可能包含新的或更新信息由entity-headers呈现。</p>\n<h4 id=\"205-Reset-Content\"><a href=\"#205-Reset-Content\" class=\"headerlink\" title=\"205 Reset Content\"></a>205 Reset Content</h4><p>服务器已经接受请求并且用户代理应该重新设置文档视图。</p>\n<h4 id=\"206-Partial-Content\"><a href=\"#206-Partial-Content\" class=\"headerlink\" title=\"206 Partial Content\"></a>206 Partial Content</h4><p>服务器已经接受请求GET请求资源的部分。请求必须包含一个Range头信息以指示获取范围可能必须包含If-Range头信息以成立请求条件。</p>\n<h3 id=\"Redirection\"><a href=\"#Redirection\" class=\"headerlink\" title=\"Redirection\"></a>Redirection</h3><h4 id=\"300-Multiple-Choices\"><a href=\"#300-Multiple-Choices\" class=\"headerlink\" title=\"300 Multiple Choices\"></a>300 Multiple Choices</h4><p>请求资源符合任何一个呈现方式。</p>\n<h4 id=\"301-Moved-Permanently\"><a href=\"#301-Moved-Permanently\" class=\"headerlink\" title=\"301 Moved Permanently\"></a>301 Moved Permanently</h4><p>请求的资源已经被赋予一个新的URI。</p>\n<h4 id=\"302-Found\"><a href=\"#302-Found\" class=\"headerlink\" title=\"302 Found\"></a>302 Found</h4><p>通过不同的URI请求资源的临时文件。</p>\n<h4 id=\"303-See-Other\"><a href=\"#303-See-Other\" class=\"headerlink\" title=\"303 See Other\"></a>303 See Other</h4><h4 id=\"304-Not-Modified\"><a href=\"#304-Not-Modified\" class=\"headerlink\" title=\"304 Not Modified\"></a>304 Not Modified</h4><p>如果客服端已经完成一个有条件的请求并且请求是允许的,但是这个文档并没有改变,服务器应该返回304状态码。304<br>状态码一定不能包含信息主体,从而通常通过一个头字段后的第一个空行结束。</p>\n<h4 id=\"305-Use-Proxy\"><a href=\"#305-Use-Proxy\" class=\"headerlink\" title=\"305 Use Proxy\"></a>305 Use Proxy</h4><p>请求的资源必须通过代理(由Location字段指定)来访问。Location资源给出了代理的URI。</p>\n<h4 id=\"306-Unused\"><a href=\"#306-Unused\" class=\"headerlink\" title=\"306 Unused\"></a>306 Unused</h4><h4 id=\"307-Temporary-Redirect\"><a href=\"#307-Temporary-Redirect\" class=\"headerlink\" title=\"307 Temporary Redirect\"></a>307 Temporary Redirect</h4><p>临时重定向。</p>\n<h3 id=\"Client-Error\"><a href=\"#Client-Error\" class=\"headerlink\" title=\"Client Error\"></a>Client Error</h3><h4 id=\"400-Bad-Request\"><a href=\"#400-Bad-Request\" class=\"headerlink\" title=\"400 Bad Request\"></a>400 Bad Request</h4><p>因为错误的语法导致服务器无法理解请求信息。</p>\n<h4 id=\"401-Unauthorized\"><a href=\"#401-Unauthorized\" class=\"headerlink\" title=\"401 Unauthorized\"></a>401 Unauthorized</h4><p>如果请求需要用户验证。回送应该包含一个WWW-Authenticate头字段用来指明请求资源的权限。</p>\n<h4 id=\"402-Payment-Required\"><a href=\"#402-Payment-Required\" class=\"headerlink\" title=\"402 Payment Required\"></a>402 Payment Required</h4><p>保留状态码。</p>\n<h4 id=\"403-Forbidden\"><a href=\"#403-Forbidden\" class=\"headerlink\" title=\"403 Forbidden\"></a>403 Forbidden</h4><p>服务器接受请求,但是被拒绝处理。</p>\n<h4 id=\"404-Not-Found\"><a href=\"#404-Not-Found\" class=\"headerlink\" title=\"404 Not Found\"></a>404 Not Found</h4><p>服务器已经找到任何匹配Request-URI的资源。</p>\n<h4 id=\"405-Menthod-Not-Allowed\"><a href=\"#405-Menthod-Not-Allowed\" class=\"headerlink\" title=\"405 Menthod Not Allowed\"></a>405 Menthod Not Allowed</h4><p>Request-Line 请求的方法不被允许通过指定的URI。</p>\n<h4 id=\"406-Not-Acceptable\"><a href=\"#406-Not-Acceptable\" class=\"headerlink\" title=\"406 Not Acceptable\"></a>406 Not Acceptable</h4><p>客户端浏览器不接受所请求页面的 MIME 类型。</p>\n<h4 id=\"407-Proxy-Authentication-Required\"><a href=\"#407-Proxy-Authentication-Required\" class=\"headerlink\" title=\"407 Proxy Authentication Required\"></a>407 Proxy Authentication Required</h4><p>要求进行代理身份验证。</p>\n<h4 id=\"408-Reqeust-Timeout\"><a href=\"#408-Reqeust-Timeout\" class=\"headerlink\" title=\"408 Reqeust Timeout\"></a>408 Reqeust Timeout</h4><p>客服端没有提交任何请求在服务器等待处理时间内。</p>\n<h4 id=\"409-Conflict\"><a href=\"#409-Conflict\" class=\"headerlink\" title=\"409 Conflict\"></a>409 Conflict</h4><h4 id=\"410-Gone\"><a href=\"#410-Gone\" class=\"headerlink\" title=\"410 Gone\"></a>410 Gone</h4><h4 id=\"411-Length-Required\"><a href=\"#411-Length-Required\" class=\"headerlink\" title=\"411 Length Required\"></a>411 Length Required</h4><p>服务器拒绝接受请求在没有定义Content-Length字段的情况下。</p>\n<h4 id=\"412-Precondition-Failed\"><a href=\"#412-Precondition-Failed\" class=\"headerlink\" title=\"412 Precondition Failed\"></a>412 Precondition Failed</h4><p>前提条件失败。</p>\n<h4 id=\"413-Request-Entity-Too-Large\"><a href=\"#413-Request-Entity-Too-Large\" class=\"headerlink\" title=\"413 Request Entity Too Large\"></a>413 Request Entity Too Large</h4><p>服务器拒绝处理请求因为请求数据超过服务器能够处理的范围。服务器可能关闭当前连接来阻止客服端继续请求。</p>\n<h4 id=\"414-Request-URI-Too-Long\"><a href=\"#414-Request-URI-Too-Long\" class=\"headerlink\" title=\"414 Request-URI Too Long\"></a>414 Request-URI Too Long</h4><p>服务器拒绝服务当前请求因为URI的长度超过了服务器的解析范围。</p>\n<h4 id=\"415-Unsupported-Media-Type\"><a href=\"#415-Unsupported-Media-Type\" class=\"headerlink\" title=\"415 Unsupported Media Type\"></a>415 Unsupported Media Type</h4><p>服务器拒绝服务当前请求因为请求数据格式并不被请求的资源支持。</p>\n<h4 id=\"416-Request-Range-Not-Satisfialbe\"><a href=\"#416-Request-Range-Not-Satisfialbe\" class=\"headerlink\" title=\"416 Request Range Not Satisfialbe\"></a>416 Request Range Not Satisfialbe</h4><p>所请求的范围无法满足。</p>\n<h4 id=\"417-Expectation-Failed\"><a href=\"#417-Expectation-Failed\" class=\"headerlink\" title=\"417 Expectation Failed\"></a>417 Expectation Failed</h4><p>执行失败。</p>\n<h3 id=\"Server-Error\"><a href=\"#Server-Error\" class=\"headerlink\" title=\"Server Error\"></a>Server Error</h3><h4 id=\"500-Internal-Server-Error\"><a href=\"#500-Internal-Server-Error\" class=\"headerlink\" title=\"500 Internal Server Error\"></a>500 Internal Server Error</h4><p>服务器遭遇异常阻止了当前请求的执行</p>\n<h4 id=\"501-Not-Implemented\"><a href=\"#501-Not-Implemented\" class=\"headerlink\" title=\"501 Not Implemented\"></a>501 Not Implemented</h4><p>服务器没有相应的执行动作来完成当前请求。</p>\n<h4 id=\"502-Bad-Gateway\"><a href=\"#502-Bad-Gateway\" class=\"headerlink\" title=\"502 Bad Gateway\"></a>502 Bad Gateway</h4><p>Web 服务器用作网关或代理服务器时收到了无效响应。</p>\n<h4 id=\"503-Service-Unavailable\"><a href=\"#503-Service-Unavailable\" class=\"headerlink\" title=\"503 Service Unavailable\"></a>503 Service Unavailable</h4><p>因为临时文件超载导致服务器不能处理当前请求。</p>\n<h4 id=\"504-Gateway-Timeout\"><a href=\"#504-Gateway-Timeout\" class=\"headerlink\" title=\"504 Gateway Timeout\"></a>504 Gateway Timeout</h4><p>网关访问超时。</p>\n<h4 id=\"505-Http-Version-Not-Supported\"><a href=\"#505-Http-Version-Not-Supported\" class=\"headerlink\" title=\"505 Http Version Not Supported\"></a>505 Http Version Not Supported</h4><p>HTTP 版本不受支持。</p>\n<pre><code>"100" : Continue\n"101" : witching Protocols\n"200" : OK\n"201" : Created\n"202" : Accepted\n"203" : Non-Authoritative Information\n"204" : No Content\n"205" : Reset Content\n"206" : Partial Content\n"300" : Multiple Choices\n"301" : Moved Permanently\n"302" : Found\n"303" : See Other\n"304" : Not Modified\n"305" : Use Proxy\n"307" : Temporary Redirect\n"400" : Bad Request\n"401" : Unauthorized\n"402" : Payment Required\n"403" : Forbidden\n"404" : Not Found\n"405" : Method Not Allowed\n"406" : Not Acceptable\n"407" : Proxy Authentication Required\n"408" : Request Time-out\n"409" : Conflict\n"410" : Gone\n"411" : Length Required\n"412" : Precondition Failed\n"413" : Request Entity Too Large\n"414" : Request-URI Too Large\n"415" : Unsupported Media Type\n"416" : Requested range not satisfiable\n"417" : Expectation Failed\n"500" : Internal Server Error\n"501" : Not Implemented\n"502" : Bad Gateway\n"503" : Service Unavailable\n"504" : Gateway Time-out\n"505" : HTTP Version not supported\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p>HTTP协议状态码表示的意思主要分为五类 ,大体是 :</p>\n<ul>\n<li>1×× 保留</li>\n<li>2×× 表示请求成功地接收</li>\n<li>3×× 为完成请求客户需进一步细化请求</li>\n<li>4×× 客户错误</li>\n<li>5×× 服务器错误</li>\n</ul>\n<h4 id=\"100-Continue\"><a href=\"#100-Continue\" class=\"headerlink\" title=\"100 Continue\"></a>100 Continue</h4><p>指示客户端应该继续请求。回送用于通知客户端此次请求已经收到,并且没有被服务器拒绝。<br>客户端应该继续发送剩下的请求数据或者请求已经完成,或者忽略回送数据。服务器必须发送<br>最后的回送在请求之后。</p>\n<h4 id=\"101-Switching-Protocols\"><a href=\"#101-Switching-Protocols\" class=\"headerlink\" title=\"101 Switching Protocols\"></a>101 Switching Protocols</h4><p>服务器依照客服端请求,通过Upgrade头信息,改变当前连接的应用协议。服务器将根据Upgrade头立刻改变协议</p>\n<p>在101回送以空行结束的时候。</p>\n<h3 id=\"Successful\"><a href=\"#Successful\" class=\"headerlink\" title=\"Successful\"></a>Successful</h3><h4 id=\"200-OK\"><a href=\"#200-OK\" class=\"headerlink\" title=\"200 OK\"></a>200 OK</h4><p>指示客服端的请求已经成功收到,解析,接受。</p>\n<h4 id=\"201-Created\"><a href=\"#201-Created\" class=\"headerlink\" title=\"201 Created\"></a>201 Created</h4><p>请求已经完成并一个新的返回资源被创建。被创建的资源可能是一个URI资源,通常URI资源在Location头指定。回送应该包含一个实体数据<br>并且包含资源特性以及location通过用户或者用户代理来选择合适的方法。实体数据格式通过煤体类型来指定即content-type头。最开始服务 器<br>必须创建指定的资源在返回201状态码之前。如果行为没有被立刻执行,服务器应该返回202。</p>\n<h4 id=\"202-Accepted\"><a href=\"#202-Accepted\" class=\"headerlink\" title=\"202 Accepted\"></a>202 Accepted</h4><p>请求已经被接受用来处理。但是处理并没有完成。请求可能或者根本没有遵照执行,因为处理实际执行过程中可能被拒绝。</p>\n<h4 id=\"203-Non-Authoritative-Information\"><a href=\"#203-Non-Authoritative-Information\" class=\"headerlink\" title=\"203 Non-Authoritative Information\"></a>203 Non-Authoritative Information</h4><p>不是权威性信息。</p>\n<h4 id=\"204-No-Content\"><a href=\"#204-No-Content\" class=\"headerlink\" title=\"204 No Content\"></a>204 No Content</h4><p>服务器已经接受请求并且没必要返回实体数据,可能需要返回更新信息。回送可能包含新的或更新信息由entity-headers呈现。</p>\n<h4 id=\"205-Reset-Content\"><a href=\"#205-Reset-Content\" class=\"headerlink\" title=\"205 Reset Content\"></a>205 Reset Content</h4><p>服务器已经接受请求并且用户代理应该重新设置文档视图。</p>\n<h4 id=\"206-Partial-Content\"><a href=\"#206-Partial-Content\" class=\"headerlink\" title=\"206 Partial Content\"></a>206 Partial Content</h4><p>服务器已经接受请求GET请求资源的部分。请求必须包含一个Range头信息以指示获取范围可能必须包含If-Range头信息以成立请求条件。</p>\n<h3 id=\"Redirection\"><a href=\"#Redirection\" class=\"headerlink\" title=\"Redirection\"></a>Redirection</h3><h4 id=\"300-Multiple-Choices\"><a href=\"#300-Multiple-Choices\" class=\"headerlink\" title=\"300 Multiple Choices\"></a>300 Multiple Choices</h4><p>请求资源符合任何一个呈现方式。</p>\n<h4 id=\"301-Moved-Permanently\"><a href=\"#301-Moved-Permanently\" class=\"headerlink\" title=\"301 Moved Permanently\"></a>301 Moved Permanently</h4><p>请求的资源已经被赋予一个新的URI。</p>\n<h4 id=\"302-Found\"><a href=\"#302-Found\" class=\"headerlink\" title=\"302 Found\"></a>302 Found</h4><p>通过不同的URI请求资源的临时文件。</p>\n<h4 id=\"303-See-Other\"><a href=\"#303-See-Other\" class=\"headerlink\" title=\"303 See Other\"></a>303 See Other</h4><h4 id=\"304-Not-Modified\"><a href=\"#304-Not-Modified\" class=\"headerlink\" title=\"304 Not Modified\"></a>304 Not Modified</h4><p>如果客服端已经完成一个有条件的请求并且请求是允许的,但是这个文档并没有改变,服务器应该返回304状态码。304<br>状态码一定不能包含信息主体,从而通常通过一个头字段后的第一个空行结束。</p>\n<h4 id=\"305-Use-Proxy\"><a href=\"#305-Use-Proxy\" class=\"headerlink\" title=\"305 Use Proxy\"></a>305 Use Proxy</h4><p>请求的资源必须通过代理(由Location字段指定)来访问。Location资源给出了代理的URI。</p>\n<h4 id=\"306-Unused\"><a href=\"#306-Unused\" class=\"headerlink\" title=\"306 Unused\"></a>306 Unused</h4><h4 id=\"307-Temporary-Redirect\"><a href=\"#307-Temporary-Redirect\" class=\"headerlink\" title=\"307 Temporary Redirect\"></a>307 Temporary Redirect</h4><p>临时重定向。</p>\n<h3 id=\"Client-Error\"><a href=\"#Client-Error\" class=\"headerlink\" title=\"Client Error\"></a>Client Error</h3><h4 id=\"400-Bad-Request\"><a href=\"#400-Bad-Request\" class=\"headerlink\" title=\"400 Bad Request\"></a>400 Bad Request</h4><p>因为错误的语法导致服务器无法理解请求信息。</p>\n<h4 id=\"401-Unauthorized\"><a href=\"#401-Unauthorized\" class=\"headerlink\" title=\"401 Unauthorized\"></a>401 Unauthorized</h4><p>如果请求需要用户验证。回送应该包含一个WWW-Authenticate头字段用来指明请求资源的权限。</p>\n<h4 id=\"402-Payment-Required\"><a href=\"#402-Payment-Required\" class=\"headerlink\" title=\"402 Payment Required\"></a>402 Payment Required</h4><p>保留状态码。</p>\n<h4 id=\"403-Forbidden\"><a href=\"#403-Forbidden\" class=\"headerlink\" title=\"403 Forbidden\"></a>403 Forbidden</h4><p>服务器接受请求,但是被拒绝处理。</p>\n<h4 id=\"404-Not-Found\"><a href=\"#404-Not-Found\" class=\"headerlink\" title=\"404 Not Found\"></a>404 Not Found</h4><p>服务器已经找到任何匹配Request-URI的资源。</p>\n<h4 id=\"405-Menthod-Not-Allowed\"><a href=\"#405-Menthod-Not-Allowed\" class=\"headerlink\" title=\"405 Menthod Not Allowed\"></a>405 Menthod Not Allowed</h4><p>Request-Line 请求的方法不被允许通过指定的URI。</p>\n<h4 id=\"406-Not-Acceptable\"><a href=\"#406-Not-Acceptable\" class=\"headerlink\" title=\"406 Not Acceptable\"></a>406 Not Acceptable</h4><p>客户端浏览器不接受所请求页面的 MIME 类型。</p>\n<h4 id=\"407-Proxy-Authentication-Required\"><a href=\"#407-Proxy-Authentication-Required\" class=\"headerlink\" title=\"407 Proxy Authentication Required\"></a>407 Proxy Authentication Required</h4><p>要求进行代理身份验证。</p>\n<h4 id=\"408-Reqeust-Timeout\"><a href=\"#408-Reqeust-Timeout\" class=\"headerlink\" title=\"408 Reqeust Timeout\"></a>408 Reqeust Timeout</h4><p>客服端没有提交任何请求在服务器等待处理时间内。</p>\n<h4 id=\"409-Conflict\"><a href=\"#409-Conflict\" class=\"headerlink\" title=\"409 Conflict\"></a>409 Conflict</h4><h4 id=\"410-Gone\"><a href=\"#410-Gone\" class=\"headerlink\" title=\"410 Gone\"></a>410 Gone</h4><h4 id=\"411-Length-Required\"><a href=\"#411-Length-Required\" class=\"headerlink\" title=\"411 Length Required\"></a>411 Length Required</h4><p>服务器拒绝接受请求在没有定义Content-Length字段的情况下。</p>\n<h4 id=\"412-Precondition-Failed\"><a href=\"#412-Precondition-Failed\" class=\"headerlink\" title=\"412 Precondition Failed\"></a>412 Precondition Failed</h4><p>前提条件失败。</p>\n<h4 id=\"413-Request-Entity-Too-Large\"><a href=\"#413-Request-Entity-Too-Large\" class=\"headerlink\" title=\"413 Request Entity Too Large\"></a>413 Request Entity Too Large</h4><p>服务器拒绝处理请求因为请求数据超过服务器能够处理的范围。服务器可能关闭当前连接来阻止客服端继续请求。</p>\n<h4 id=\"414-Request-URI-Too-Long\"><a href=\"#414-Request-URI-Too-Long\" class=\"headerlink\" title=\"414 Request-URI Too Long\"></a>414 Request-URI Too Long</h4><p>服务器拒绝服务当前请求因为URI的长度超过了服务器的解析范围。</p>\n<h4 id=\"415-Unsupported-Media-Type\"><a href=\"#415-Unsupported-Media-Type\" class=\"headerlink\" title=\"415 Unsupported Media Type\"></a>415 Unsupported Media Type</h4><p>服务器拒绝服务当前请求因为请求数据格式并不被请求的资源支持。</p>\n<h4 id=\"416-Request-Range-Not-Satisfialbe\"><a href=\"#416-Request-Range-Not-Satisfialbe\" class=\"headerlink\" title=\"416 Request Range Not Satisfialbe\"></a>416 Request Range Not Satisfialbe</h4><p>所请求的范围无法满足。</p>\n<h4 id=\"417-Expectation-Failed\"><a href=\"#417-Expectation-Failed\" class=\"headerlink\" title=\"417 Expectation Failed\"></a>417 Expectation Failed</h4><p>执行失败。</p>\n<h3 id=\"Server-Error\"><a href=\"#Server-Error\" class=\"headerlink\" title=\"Server Error\"></a>Server Error</h3><h4 id=\"500-Internal-Server-Error\"><a href=\"#500-Internal-Server-Error\" class=\"headerlink\" title=\"500 Internal Server Error\"></a>500 Internal Server Error</h4><p>服务器遭遇异常阻止了当前请求的执行</p>\n<h4 id=\"501-Not-Implemented\"><a href=\"#501-Not-Implemented\" class=\"headerlink\" title=\"501 Not Implemented\"></a>501 Not Implemented</h4><p>服务器没有相应的执行动作来完成当前请求。</p>\n<h4 id=\"502-Bad-Gateway\"><a href=\"#502-Bad-Gateway\" class=\"headerlink\" title=\"502 Bad Gateway\"></a>502 Bad Gateway</h4><p>Web 服务器用作网关或代理服务器时收到了无效响应。</p>\n<h4 id=\"503-Service-Unavailable\"><a href=\"#503-Service-Unavailable\" class=\"headerlink\" title=\"503 Service Unavailable\"></a>503 Service Unavailable</h4><p>因为临时文件超载导致服务器不能处理当前请求。</p>\n<h4 id=\"504-Gateway-Timeout\"><a href=\"#504-Gateway-Timeout\" class=\"headerlink\" title=\"504 Gateway Timeout\"></a>504 Gateway Timeout</h4><p>网关访问超时。</p>\n<h4 id=\"505-Http-Version-Not-Supported\"><a href=\"#505-Http-Version-Not-Supported\" class=\"headerlink\" title=\"505 Http Version Not Supported\"></a>505 Http Version Not Supported</h4><p>HTTP 版本不受支持。</p>\n<pre><code>"100" : Continue\n"101" : witching Protocols\n"200" : OK\n"201" : Created\n"202" : Accepted\n"203" : Non-Authoritative Information\n"204" : No Content\n"205" : Reset Content\n"206" : Partial Content\n"300" : Multiple Choices\n"301" : Moved Permanently\n"302" : Found\n"303" : See Other\n"304" : Not Modified\n"305" : Use Proxy\n"307" : Temporary Redirect\n"400" : Bad Request\n"401" : Unauthorized\n"402" : Payment Required\n"403" : Forbidden\n"404" : Not Found\n"405" : Method Not Allowed\n"406" : Not Acceptable\n"407" : Proxy Authentication Required\n"408" : Request Time-out\n"409" : Conflict\n"410" : Gone\n"411" : Length Required\n"412" : Precondition Failed\n"413" : Request Entity Too Large\n"414" : Request-URI Too Large\n"415" : Unsupported Media Type\n"416" : Requested range not satisfiable\n"417" : Expectation Failed\n"500" : Internal Server Error\n"501" : Not Implemented\n"502" : Bad Gateway\n"503" : Service Unavailable\n"504" : Gateway Time-out\n"505" : HTTP Version not supported\n</code></pre>\n"},{"title":"jetbrains 插件推荐","date":"2021-09-18T06:07:20.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n### PlantUML integration\n\n插件地址: [https://plugins.jetbrains.com/plugin/7017-plantuml-integration](https://plugins.jetbrains.com/plugin/7017-plantuml-integration)\n\nPlantUML diagramming tool integration. Now better and faster, with code navigation and highlighting.\n\n<img src=\"https://plugins.jetbrains.com/files/7017/screenshot_49c30caf-b0d3-4425-b31d-e13915ee1e98\">\n\n### CamelCase\n\n插件地址: [https://plugins.jetbrains.com/plugin/7160-camelcase](https://plugins.jetbrains.com/plugin/7160-camelcase)\n\nSwitch easily between kebab-case, SNAKE_CASE, PascalCase, camelCase, snake_case or space case. See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U. Allows to disable some conversions or change their order in the preferences.\n\n### GitToolBox\n\n插件地址:[https://plugins.jetbrains.com/plugin/7499-gittoolbox](https://plugins.jetbrains.com/plugin/7499-gittoolbox)\n\n**Extends Git Integration with additional features**\n\nStatus display, auto fetch, inline blame annotation, commit dialog completion, behind notifications and more...\n\nFor more see [Manual](https://github.com/zielu/GitToolBox/wiki/Manual)\n\n### Translation\n\nTranslation plugin for IntelliJ based IDEs.\n\n<div>\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18009.png\" style=\"zoom:75%;\">\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18932.png\">\n</div>\n\n\n","source":"_posts/其他/jetbrains-插件推荐.md","raw":"---\ntitle: jetbrains 插件推荐\ndate: 2021-09-18 14:07:20\n\ntags:\n- jetbrains\n- plugins\n\ncategories:\n- 其他\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n### PlantUML integration\n\n插件地址: [https://plugins.jetbrains.com/plugin/7017-plantuml-integration](https://plugins.jetbrains.com/plugin/7017-plantuml-integration)\n\nPlantUML diagramming tool integration. Now better and faster, with code navigation and highlighting.\n\n<img src=\"https://plugins.jetbrains.com/files/7017/screenshot_49c30caf-b0d3-4425-b31d-e13915ee1e98\">\n\n### CamelCase\n\n插件地址: [https://plugins.jetbrains.com/plugin/7160-camelcase](https://plugins.jetbrains.com/plugin/7160-camelcase)\n\nSwitch easily between kebab-case, SNAKE_CASE, PascalCase, camelCase, snake_case or space case. See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U. Allows to disable some conversions or change their order in the preferences.\n\n### GitToolBox\n\n插件地址:[https://plugins.jetbrains.com/plugin/7499-gittoolbox](https://plugins.jetbrains.com/plugin/7499-gittoolbox)\n\n**Extends Git Integration with additional features**\n\nStatus display, auto fetch, inline blame annotation, commit dialog completion, behind notifications and more...\n\nFor more see [Manual](https://github.com/zielu/GitToolBox/wiki/Manual)\n\n### Translation\n\nTranslation plugin for IntelliJ based IDEs.\n\n<div>\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18009.png\" style=\"zoom:75%;\">\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18932.png\">\n</div>\n\n\n","slug":"其他/jetbrains-插件推荐","published":1,"updated":"2022-11-01T03:10:44.745Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnh002fiysy5mrzcnpt","content":"<h3 id=\"PlantUML-integration\"><a href=\"#PlantUML-integration\" class=\"headerlink\" title=\"PlantUML integration\"></a>PlantUML integration</h3><p>插件地址: <a href=\"https://plugins.jetbrains.com/plugin/7017-plantuml-integration\">https://plugins.jetbrains.com/plugin/7017-plantuml-integration</a></p>\n<p>PlantUML diagramming tool integration. Now better and faster, with code navigation and highlighting.</p>\n<img src=\"https://plugins.jetbrains.com/files/7017/screenshot_49c30caf-b0d3-4425-b31d-e13915ee1e98\">\n\n<h3 id=\"CamelCase\"><a href=\"#CamelCase\" class=\"headerlink\" title=\"CamelCase\"></a>CamelCase</h3><p>插件地址: <a href=\"https://plugins.jetbrains.com/plugin/7160-camelcase\">https://plugins.jetbrains.com/plugin/7160-camelcase</a></p>\n<p>Switch easily between kebab-case, SNAKE_CASE, PascalCase, camelCase, snake_case or space case. See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U. Allows to disable some conversions or change their order in the preferences.</p>\n<h3 id=\"GitToolBox\"><a href=\"#GitToolBox\" class=\"headerlink\" title=\"GitToolBox\"></a>GitToolBox</h3><p>插件地址:<a href=\"https://plugins.jetbrains.com/plugin/7499-gittoolbox\">https://plugins.jetbrains.com/plugin/7499-gittoolbox</a></p>\n<p><strong>Extends Git Integration with additional features</strong></p>\n<p>Status display, auto fetch, inline blame annotation, commit dialog completion, behind notifications and more…</p>\n<p>For more see <a href=\"https://github.com/zielu/GitToolBox/wiki/Manual\">Manual</a></p>\n<h3 id=\"Translation\"><a href=\"#Translation\" class=\"headerlink\" title=\"Translation\"></a>Translation</h3><p>Translation plugin for IntelliJ based IDEs.</p>\n<div>\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18009.png\" style=\"zoom:75%;\">\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18932.png\">\n</div>\n\n\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"PlantUML-integration\"><a href=\"#PlantUML-integration\" class=\"headerlink\" title=\"PlantUML integration\"></a>PlantUML integration</h3><p>插件地址: <a href=\"https://plugins.jetbrains.com/plugin/7017-plantuml-integration\">https://plugins.jetbrains.com/plugin/7017-plantuml-integration</a></p>\n<p>PlantUML diagramming tool integration. Now better and faster, with code navigation and highlighting.</p>\n<img src=\"https://plugins.jetbrains.com/files/7017/screenshot_49c30caf-b0d3-4425-b31d-e13915ee1e98\">\n\n<h3 id=\"CamelCase\"><a href=\"#CamelCase\" class=\"headerlink\" title=\"CamelCase\"></a>CamelCase</h3><p>插件地址: <a href=\"https://plugins.jetbrains.com/plugin/7160-camelcase\">https://plugins.jetbrains.com/plugin/7160-camelcase</a></p>\n<p>Switch easily between kebab-case, SNAKE_CASE, PascalCase, camelCase, snake_case or space case. See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U. Allows to disable some conversions or change their order in the preferences.</p>\n<h3 id=\"GitToolBox\"><a href=\"#GitToolBox\" class=\"headerlink\" title=\"GitToolBox\"></a>GitToolBox</h3><p>插件地址:<a href=\"https://plugins.jetbrains.com/plugin/7499-gittoolbox\">https://plugins.jetbrains.com/plugin/7499-gittoolbox</a></p>\n<p><strong>Extends Git Integration with additional features</strong></p>\n<p>Status display, auto fetch, inline blame annotation, commit dialog completion, behind notifications and more…</p>\n<p>For more see <a href=\"https://github.com/zielu/GitToolBox/wiki/Manual\">Manual</a></p>\n<h3 id=\"Translation\"><a href=\"#Translation\" class=\"headerlink\" title=\"Translation\"></a>Translation</h3><p>Translation plugin for IntelliJ based IDEs.</p>\n<div>\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18009.png\" style=\"zoom:75%;\">\n <img src=\" https://plugins.jetbrains.com/files/8579/screenshot_18932.png\">\n</div>\n\n\n"},{"title":"状态机","auto_excerpt":{"enable":true,"length":150},"date":"2021-09-23T09:49:33.000Z","_content":"\n### 状态模式\n### 概念\n有限状态机(Finite State Machine):有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。有 3 个组成部分:状态(State)、事件(Event)、动作(Action),事件触发状态的转移及动作的执行。\n\n**状态模式:** 用来实现状态机的设计模式,常用在工作流引擎、工单等涉及到很多状态的场景。\n\n### 作用\n状态模式能解耦状态和状态转移(事件),让整个状态机系统逻辑能加清晰。\n\n#### todo\n1. 代码示例(订单状态转换)\n2. 状态转换图","source":"_posts/其他/状态机.md","raw":"---\ntitle: 状态机\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-09-23 17:49:33\ntags:\ncategories:\n- 其他\n---\n\n### 状态模式\n### 概念\n有限状态机(Finite State Machine):有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。有 3 个组成部分:状态(State)、事件(Event)、动作(Action),事件触发状态的转移及动作的执行。\n\n**状态模式:** 用来实现状态机的设计模式,常用在工作流引擎、工单等涉及到很多状态的场景。\n\n### 作用\n状态模式能解耦状态和状态转移(事件),让整个状态机系统逻辑能加清晰。\n\n#### todo\n1. 代码示例(订单状态转换)\n2. 状态转换图","slug":"其他/状态机","published":1,"updated":"2022-11-01T02:41:03.859Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpni002kiysydw14htax","content":"<h3 id=\"状态模式\"><a href=\"#状态模式\" class=\"headerlink\" title=\"状态模式\"></a>状态模式</h3><h3 id=\"概念\"><a href=\"#概念\" class=\"headerlink\" title=\"概念\"></a>概念</h3><p>有限状态机(Finite State Machine):有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。有 3 个组成部分:状态(State)、事件(Event)、动作(Action),事件触发状态的转移及动作的执行。</p>\n<p><strong>状态模式:</strong> 用来实现状态机的设计模式,常用在工作流引擎、工单等涉及到很多状态的场景。</p>\n<h3 id=\"作用\"><a href=\"#作用\" class=\"headerlink\" title=\"作用\"></a>作用</h3><p>状态模式能解耦状态和状态转移(事件),让整个状态机系统逻辑能加清晰。</p>\n<h4 id=\"todo\"><a href=\"#todo\" class=\"headerlink\" title=\"todo\"></a>todo</h4><ol>\n<li>代码示例(订单状态转换)</li>\n<li>状态转换图</li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<h3 id=\"状态模式\"><a href=\"#状态模式\" class=\"headerlink\" title=\"状态模式\"></a>状态模式</h3><h3 id=\"概念\"><a href=\"#概念\" class=\"headerlink\" title=\"概念\"></a>概念</h3><p>有限状态机(Finite State Machine):有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。有 3 个组成部分:状态(State)、事件(Event)、动作(Action),事件触发状态的转移及动作的执行。</p>\n<p><strong>状态模式:</strong> 用来实现状态机的设计模式,常用在工作流引擎、工单等涉及到很多状态的场景。</p>\n<h3 id=\"作用\"><a href=\"#作用\" class=\"headerlink\" title=\"作用\"></a>作用</h3><p>状态模式能解耦状态和状态转移(事件),让整个状态机系统逻辑能加清晰。</p>\n<h4 id=\"todo\"><a href=\"#todo\" class=\"headerlink\" title=\"todo\"></a>todo</h4><ol>\n<li>代码示例(订单状态转换)</li>\n<li>状态转换图</li>\n</ol>\n"},{"title":"markdown 语法简介","date":"2019-06-05T03:13:18.000Z","_content":"\n```markdown\n#一、标题写法:\n第一种方法:\n1、在文本下面加上 等于号 = ,那么上方的文本就变成了大标题。等于号的个数无限制,但一定要大于0个哦。。\n2、在文本下面加上 下划线 - ,那么上方的文本就变成了中标题,同样的 下划线个数无限制。\n3、要想输入=号,上面有文本而不让其转化为大标题,则需要在两者之间加一个空行。\n另一种方法:(推荐这种方法;注意⚠️中间需要有一个空格)\n关于标题还有等级表示法,分为六个等级,显示的文本大小依次减小。不同等级之间是以井号 # 的个数来标识的。一级标题有一个 #,二级标题有两个# ,以此类推。\n例如:\n# 一级标题 \n## 二级标题 \n### 三级标题 \n#### 四级标题 \n##### 五级标题 \n###### 六级标题 \n二、编辑基本语法 \n1、字体格式强调\n 我们可以使用下面的方式给我们的文本添加强调的效果\n*强调* (示例:斜体) \n _强调_ (示例:斜体) \n**加重强调** (示例:粗体) \n __加重强调__ (示例:粗体) \n***特别强调*** (示例:粗斜体) \n___特别强调___ (示例:粗斜体) \n2、代码 \n`<hello world>` \n\n3、代码块高亮\n(```)\n@Override\nprotected void onDestroy() {\n EventBus.getDefault().unregister(this);\n super.onDestroy();\n}\n(```)\n\n4、表格 (建议在表格前空一行,否则可能影响表格无法显示)\n \n 表头 | 表头 | 表头\n ---- | ----- | ------ \n 单元格内容 | 单元格内容 | 单元格内容 \n 单元格内容 | 单元格内容 | 单元格内容 \n \n5、其他引用\n图片 \n![图片名称](https://www.baidu.com/img/bd_logo1.png) \n链接 \n[链接名称](https://www.baidu.com/) \n6、列表 \n1. 项目1 \n2. 项目2 \n3. 项目3 \n * 项目1 (一个*号会显示为一个黑点,注意⚠️有空格,否则直接显示为*项目1) \n * 项目2 \n \n7、换行(建议直接在前一行后面补两个空格)\n直接回车不能换行, \n可以在上一行文本后面补两个空格, \n这样下一行的文本就换行了。\n或者就是在两行文本直接加一个空行。\n也能实现换行效果,不过这个行间距有点大。 \n \n8、引用\n> 第一行引用文字 \n> 第二行引用文字 \n\n```\n","source":"_posts/其他/markdown 语法简介.md","raw":"---\ntitle: markdown 语法简介\ndate: 2019-06-05 11:13:18\ntags: \n- markdown\n- api\n\ncategories:\n- 其他\n---\n\n```markdown\n#一、标题写法:\n第一种方法:\n1、在文本下面加上 等于号 = ,那么上方的文本就变成了大标题。等于号的个数无限制,但一定要大于0个哦。。\n2、在文本下面加上 下划线 - ,那么上方的文本就变成了中标题,同样的 下划线个数无限制。\n3、要想输入=号,上面有文本而不让其转化为大标题,则需要在两者之间加一个空行。\n另一种方法:(推荐这种方法;注意⚠️中间需要有一个空格)\n关于标题还有等级表示法,分为六个等级,显示的文本大小依次减小。不同等级之间是以井号 # 的个数来标识的。一级标题有一个 #,二级标题有两个# ,以此类推。\n例如:\n# 一级标题 \n## 二级标题 \n### 三级标题 \n#### 四级标题 \n##### 五级标题 \n###### 六级标题 \n二、编辑基本语法 \n1、字体格式强调\n 我们可以使用下面的方式给我们的文本添加强调的效果\n*强调* (示例:斜体) \n _强调_ (示例:斜体) \n**加重强调** (示例:粗体) \n __加重强调__ (示例:粗体) \n***特别强调*** (示例:粗斜体) \n___特别强调___ (示例:粗斜体) \n2、代码 \n`<hello world>` \n\n3、代码块高亮\n(```)\n@Override\nprotected void onDestroy() {\n EventBus.getDefault().unregister(this);\n super.onDestroy();\n}\n(```)\n\n4、表格 (建议在表格前空一行,否则可能影响表格无法显示)\n \n 表头 | 表头 | 表头\n ---- | ----- | ------ \n 单元格内容 | 单元格内容 | 单元格内容 \n 单元格内容 | 单元格内容 | 单元格内容 \n \n5、其他引用\n图片 \n![图片名称](https://www.baidu.com/img/bd_logo1.png) \n链接 \n[链接名称](https://www.baidu.com/) \n6、列表 \n1. 项目1 \n2. 项目2 \n3. 项目3 \n * 项目1 (一个*号会显示为一个黑点,注意⚠️有空格,否则直接显示为*项目1) \n * 项目2 \n \n7、换行(建议直接在前一行后面补两个空格)\n直接回车不能换行, \n可以在上一行文本后面补两个空格, \n这样下一行的文本就换行了。\n或者就是在两行文本直接加一个空行。\n也能实现换行效果,不过这个行间距有点大。 \n \n8、引用\n> 第一行引用文字 \n> 第二行引用文字 \n\n```\n","slug":"其他/markdown 语法简介","published":1,"updated":"2022-11-01T02:41:19.882Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnj002miysy5oso7r7t","content":"<pre><code class=\"markdown\">#一、标题写法:\n第一种方法:\n1、在文本下面加上 等于号 = ,那么上方的文本就变成了大标题。等于号的个数无限制,但一定要大于0个哦。。\n2、在文本下面加上 下划线 - ,那么上方的文本就变成了中标题,同样的 下划线个数无限制。\n3、要想输入=号,上面有文本而不让其转化为大标题,则需要在两者之间加一个空行。\n另一种方法:(推荐这种方法;注意⚠️中间需要有一个空格)\n关于标题还有等级表示法,分为六个等级,显示的文本大小依次减小。不同等级之间是以井号 # 的个数来标识的。一级标题有一个 #,二级标题有两个# ,以此类推。\n例如:\n# 一级标题 \n## 二级标题 \n### 三级标题 \n#### 四级标题 \n##### 五级标题 \n###### 六级标题 \n二、编辑基本语法 \n1、字体格式强调\n 我们可以使用下面的方式给我们的文本添加强调的效果\n*强调* (示例:斜体) \n _强调_ (示例:斜体) \n**加重强调** (示例:粗体) \n __加重强调__ (示例:粗体) \n***特别强调*** (示例:粗斜体) \n___特别强调___ (示例:粗斜体) \n2、代码 \n`<hello world>` \n\n3、代码块高亮\n(```)\n@Override\nprotected void onDestroy() {\n EventBus.getDefault().unregister(this);\n super.onDestroy();\n}\n(```)\n\n4、表格 (建议在表格前空一行,否则可能影响表格无法显示)\n \n 表头 | 表头 | 表头\n ---- | ----- | ------ \n 单元格内容 | 单元格内容 | 单元格内容 \n 单元格内容 | 单元格内容 | 单元格内容 \n \n5、其他引用\n图片 \n![图片名称](https://www.baidu.com/img/bd_logo1.png) \n链接 \n[链接名称](https://www.baidu.com/) \n6、列表 \n1. 项目1 \n2. 项目2 \n3. 项目3 \n * 项目1 (一个*号会显示为一个黑点,注意⚠️有空格,否则直接显示为*项目1) \n * 项目2 \n \n7、换行(建议直接在前一行后面补两个空格)\n直接回车不能换行, \n可以在上一行文本后面补两个空格, \n这样下一行的文本就换行了。\n或者就是在两行文本直接加一个空行。\n也能实现换行效果,不过这个行间距有点大。 \n \n8、引用\n> 第一行引用文字 \n> 第二行引用文字 \n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<pre><code class=\"markdown\">#一、标题写法:\n第一种方法:\n1、在文本下面加上 等于号 = ,那么上方的文本就变成了大标题。等于号的个数无限制,但一定要大于0个哦。。\n2、在文本下面加上 下划线 - ,那么上方的文本就变成了中标题,同样的 下划线个数无限制。\n3、要想输入=号,上面有文本而不让其转化为大标题,则需要在两者之间加一个空行。\n另一种方法:(推荐这种方法;注意⚠️中间需要有一个空格)\n关于标题还有等级表示法,分为六个等级,显示的文本大小依次减小。不同等级之间是以井号 # 的个数来标识的。一级标题有一个 #,二级标题有两个# ,以此类推。\n例如:\n# 一级标题 \n## 二级标题 \n### 三级标题 \n#### 四级标题 \n##### 五级标题 \n###### 六级标题 \n二、编辑基本语法 \n1、字体格式强调\n 我们可以使用下面的方式给我们的文本添加强调的效果\n*强调* (示例:斜体) \n _强调_ (示例:斜体) \n**加重强调** (示例:粗体) \n __加重强调__ (示例:粗体) \n***特别强调*** (示例:粗斜体) \n___特别强调___ (示例:粗斜体) \n2、代码 \n`<hello world>` \n\n3、代码块高亮\n(```)\n@Override\nprotected void onDestroy() {\n EventBus.getDefault().unregister(this);\n super.onDestroy();\n}\n(```)\n\n4、表格 (建议在表格前空一行,否则可能影响表格无法显示)\n \n 表头 | 表头 | 表头\n ---- | ----- | ------ \n 单元格内容 | 单元格内容 | 单元格内容 \n 单元格内容 | 单元格内容 | 单元格内容 \n \n5、其他引用\n图片 \n![图片名称](https://www.baidu.com/img/bd_logo1.png) \n链接 \n[链接名称](https://www.baidu.com/) \n6、列表 \n1. 项目1 \n2. 项目2 \n3. 项目3 \n * 项目1 (一个*号会显示为一个黑点,注意⚠️有空格,否则直接显示为*项目1) \n * 项目2 \n \n7、换行(建议直接在前一行后面补两个空格)\n直接回车不能换行, \n可以在上一行文本后面补两个空格, \n这样下一行的文本就换行了。\n或者就是在两行文本直接加一个空行。\n也能实现换行效果,不过这个行间距有点大。 \n \n8、引用\n> 第一行引用文字 \n> 第二行引用文字 \n</code></pre>\n"},{"title":"网站收藏","date":"2019-06-04T08:20:14.000Z","auto_excerpt":{"enable":true,"length":150},"_content":"\n[toc]\n\n### 官网\n\nhexo [[https://hexo.io](https://hexo.io)\n\nredis [https://redis.io/](https://redis.io/)\n\ngit [https://git-scm.com/book/zh/v2](https://git-scm.com/book/zh/v2)\n\ngolang [https://golang.google.cn/](https://golang.google.cn/)\n\nuvicorn Python ASGI 框架 [http://www.uvicorn.org/](http://www.uvicorn.org/)\n\n### API 网站\n\nhexo [https://hexo.io/api/](https://hexo.io/api/)\n\nredis [https://redis.io](https://redis.io)\n\nmarkdown [https://markdown.com.cn/](https://markdown.com.cn/)\n\n### golang 第三方库\n\nants 协程池 [https://github.com/panjf2000/ants/](https://github.com/panjf2000/ants/)\n\ngorm orm [https://gorm.io/zh_CN/](https://gorm.io/zh_CN/)\n\n### 工具网站\n\nexcalidraw 在线作图 [https://excalidraw.com/](https://excalidraw.com/)\n\noktools [https://oktools.net/](https://oktools.net/)\n\njson2go [https://oktools.net/json2go](https://oktools.net/json2go)\n\nprocesson 在线作图 [https://www.processon.com/](https://www.processon.com/)\n\n\n\n### 其他\n\nmacwk Mac 软件下载 [https://macwk.com/](https://macwk.com/)\n\n","source":"_posts/其他/网站收藏.md","raw":"---\ntitle: 网站收藏\ndate: 2019-06-04 16:20:14\ntags:\n- site\n- api\n\ncategories:\n- 其他\n\nauto_excerpt:\n enable: true\n length: 150\n---\n\n[toc]\n\n### 官网\n\nhexo [[https://hexo.io](https://hexo.io)\n\nredis [https://redis.io/](https://redis.io/)\n\ngit [https://git-scm.com/book/zh/v2](https://git-scm.com/book/zh/v2)\n\ngolang [https://golang.google.cn/](https://golang.google.cn/)\n\nuvicorn Python ASGI 框架 [http://www.uvicorn.org/](http://www.uvicorn.org/)\n\n### API 网站\n\nhexo [https://hexo.io/api/](https://hexo.io/api/)\n\nredis [https://redis.io](https://redis.io)\n\nmarkdown [https://markdown.com.cn/](https://markdown.com.cn/)\n\n### golang 第三方库\n\nants 协程池 [https://github.com/panjf2000/ants/](https://github.com/panjf2000/ants/)\n\ngorm orm [https://gorm.io/zh_CN/](https://gorm.io/zh_CN/)\n\n### 工具网站\n\nexcalidraw 在线作图 [https://excalidraw.com/](https://excalidraw.com/)\n\noktools [https://oktools.net/](https://oktools.net/)\n\njson2go [https://oktools.net/json2go](https://oktools.net/json2go)\n\nprocesson 在线作图 [https://www.processon.com/](https://www.processon.com/)\n\n\n\n### 其他\n\nmacwk Mac 软件下载 [https://macwk.com/](https://macwk.com/)\n\n","slug":"其他/网站收藏","published":1,"updated":"2022-11-01T02:40:51.532Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpnk002piysy1casf1jy","content":"<p>[toc]</p>\n<h3 id=\"官网\"><a href=\"#官网\" class=\"headerlink\" title=\"官网\"></a>官网</h3><p>hexo [<a href=\"https://hexo.io/\">https://hexo.io</a></p>\n<p>redis <a href=\"https://redis.io/\">https://redis.io/</a></p>\n<p>git <a href=\"https://git-scm.com/book/zh/v2\">https://git-scm.com/book/zh/v2</a></p>\n<p>golang <a href=\"https://golang.google.cn/\">https://golang.google.cn/</a></p>\n<p>uvicorn Python ASGI 框架 <a href=\"http://www.uvicorn.org/\">http://www.uvicorn.org/</a></p>\n<h3 id=\"API-网站\"><a href=\"#API-网站\" class=\"headerlink\" title=\"API 网站\"></a>API 网站</h3><p>hexo <a href=\"https://hexo.io/api/\">https://hexo.io/api/</a></p>\n<p>redis <a href=\"https://redis.io/\">https://redis.io</a></p>\n<p>markdown <a href=\"https://markdown.com.cn/\">https://markdown.com.cn/</a></p>\n<h3 id=\"golang-第三方库\"><a href=\"#golang-第三方库\" class=\"headerlink\" title=\"golang 第三方库\"></a>golang 第三方库</h3><p>ants 协程池 <a href=\"https://github.com/panjf2000/ants/\">https://github.com/panjf2000/ants/</a></p>\n<p>gorm orm <a href=\"https://gorm.io/zh_CN/\">https://gorm.io/zh_CN/</a></p>\n<h3 id=\"工具网站\"><a href=\"#工具网站\" class=\"headerlink\" title=\"工具网站\"></a>工具网站</h3><p>excalidraw 在线作图 <a href=\"https://excalidraw.com/\">https://excalidraw.com/</a></p>\n<p>oktools <a href=\"https://oktools.net/\">https://oktools.net/</a></p>\n<p>json2go <a href=\"https://oktools.net/json2go\">https://oktools.net/json2go</a></p>\n<p>processon 在线作图 <a href=\"https://www.processon.com/\">https://www.processon.com/</a></p>\n<h3 id=\"其他\"><a href=\"#其他\" class=\"headerlink\" title=\"其他\"></a>其他</h3><p>macwk Mac 软件下载 <a href=\"https://macwk.com/\">https://macwk.com/</a></p>\n","site":{"data":{}},"excerpt":"","more":"<p>[toc]</p>\n<h3 id=\"官网\"><a href=\"#官网\" class=\"headerlink\" title=\"官网\"></a>官网</h3><p>hexo [<a href=\"https://hexo.io/\">https://hexo.io</a></p>\n<p>redis <a href=\"https://redis.io/\">https://redis.io/</a></p>\n<p>git <a href=\"https://git-scm.com/book/zh/v2\">https://git-scm.com/book/zh/v2</a></p>\n<p>golang <a href=\"https://golang.google.cn/\">https://golang.google.cn/</a></p>\n<p>uvicorn Python ASGI 框架 <a href=\"http://www.uvicorn.org/\">http://www.uvicorn.org/</a></p>\n<h3 id=\"API-网站\"><a href=\"#API-网站\" class=\"headerlink\" title=\"API 网站\"></a>API 网站</h3><p>hexo <a href=\"https://hexo.io/api/\">https://hexo.io/api/</a></p>\n<p>redis <a href=\"https://redis.io/\">https://redis.io</a></p>\n<p>markdown <a href=\"https://markdown.com.cn/\">https://markdown.com.cn/</a></p>\n<h3 id=\"golang-第三方库\"><a href=\"#golang-第三方库\" class=\"headerlink\" title=\"golang 第三方库\"></a>golang 第三方库</h3><p>ants 协程池 <a href=\"https://github.com/panjf2000/ants/\">https://github.com/panjf2000/ants/</a></p>\n<p>gorm orm <a href=\"https://gorm.io/zh_CN/\">https://gorm.io/zh_CN/</a></p>\n<h3 id=\"工具网站\"><a href=\"#工具网站\" class=\"headerlink\" title=\"工具网站\"></a>工具网站</h3><p>excalidraw 在线作图 <a href=\"https://excalidraw.com/\">https://excalidraw.com/</a></p>\n<p>oktools <a href=\"https://oktools.net/\">https://oktools.net/</a></p>\n<p>json2go <a href=\"https://oktools.net/json2go\">https://oktools.net/json2go</a></p>\n<p>processon 在线作图 <a href=\"https://www.processon.com/\">https://www.processon.com/</a></p>\n<h3 id=\"其他\"><a href=\"#其他\" class=\"headerlink\" title=\"其他\"></a>其他</h3><p>macwk Mac 软件下载 <a href=\"https://macwk.com/\">https://macwk.com/</a></p>\n"},{"title":"时间轮","auto_excerpt":{"enable":true,"length":150},"date":"2021-09-30T01:45:10.000Z","_content":"\n## 时间轮 Timewheel\n\n### kafka 时间轮的实现 \n[Kafka Timer 实现源码](https://github.com/apache/kafka/tree/3cdc78e6bb1f83973a14ce1550fe3874f7348b05/core/src/main/scala/kafka/utils/timer) </br>\n\nKafka 的层级时间轮实现中,利用了 Java 内置的 DelayQueue 结构,将每一层时间轮中所有 “包含有定时任务的 bucket” 都加入到同一个 DelayQueue 中,然后 等到有 bucket 到期后再驱动时钟往前走,并逐个处理该 bucket 中的定时任务\n\n1. 往层级时间轮中添加一个定时任务 task1 后,会将该任务所属的 bucket2 的到期时间设置为 task1 的到期时间 expiration(= 当前时间 currentTime + 定时任务到期间隔 duration),并将这个 bucket2 添加(Offer)到 DelayQueue 中。\n2. DelayQueue(内部有一个线程)会等待 “到期时间最早(earliest)的 bucket” 到期,图中等到的是排在队首的 bucket2,于是经由 poll 返回并删除这个 bucket2;随后,时间轮会将当前时间 currentTime 往前移动到 bucket2 的 expiration 所指向的时间(图中是 1ms 所在的位置);最后,bucket2 中包含的 task1 会被删除并执行。\n\n### Golang timewheel 实现\n\n#### 参考 Kafka 时间轮实现的 golang 版本\n\ntimewheel : [https://github.com/RussellLuo/timingwheel.git]()\n\n1. PriorityQueue —— 从 NSQ 借用过来的 优先级队列(基于最小堆实现)。\n2. DelayQueue —— Offer(添加 bucket)和 Poll(获取并删除 bucket)的运作方式,跟 Golang Timer 运行时中 addtimerLocked 和 timerproc 的运作方式如出一辙,因此参考了其中的实现方式(参考 [原理介绍](https://blog.gopheracademy.com/advent-2016/go-timers/))。\n\n#### 基于 Timer 的版本\n\ngo-timewhee : [https://github.com/rfyiamcool/go-timewheel]()\n\n\n</br>\n\n**参考:**\n* [层级时间轮的 Golang 实现](http://russellluo.com/2018/10/golang-implementation-of-hierarchical-timing-wheels.html)\n\n","source":"_posts/算法/时间轮.md","raw":"---\ntitle: 时间轮\nauto_excerpt:\n enable: true\n length: 150\ndate: 2021-09-30 09:45:10\ntags:\n- 算法\n- timewheels\ncategories:\n- 算法\n---\n\n## 时间轮 Timewheel\n\n### kafka 时间轮的实现 \n[Kafka Timer 实现源码](https://github.com/apache/kafka/tree/3cdc78e6bb1f83973a14ce1550fe3874f7348b05/core/src/main/scala/kafka/utils/timer) </br>\n\nKafka 的层级时间轮实现中,利用了 Java 内置的 DelayQueue 结构,将每一层时间轮中所有 “包含有定时任务的 bucket” 都加入到同一个 DelayQueue 中,然后 等到有 bucket 到期后再驱动时钟往前走,并逐个处理该 bucket 中的定时任务\n\n1. 往层级时间轮中添加一个定时任务 task1 后,会将该任务所属的 bucket2 的到期时间设置为 task1 的到期时间 expiration(= 当前时间 currentTime + 定时任务到期间隔 duration),并将这个 bucket2 添加(Offer)到 DelayQueue 中。\n2. DelayQueue(内部有一个线程)会等待 “到期时间最早(earliest)的 bucket” 到期,图中等到的是排在队首的 bucket2,于是经由 poll 返回并删除这个 bucket2;随后,时间轮会将当前时间 currentTime 往前移动到 bucket2 的 expiration 所指向的时间(图中是 1ms 所在的位置);最后,bucket2 中包含的 task1 会被删除并执行。\n\n### Golang timewheel 实现\n\n#### 参考 Kafka 时间轮实现的 golang 版本\n\ntimewheel : [https://github.com/RussellLuo/timingwheel.git]()\n\n1. PriorityQueue —— 从 NSQ 借用过来的 优先级队列(基于最小堆实现)。\n2. DelayQueue —— Offer(添加 bucket)和 Poll(获取并删除 bucket)的运作方式,跟 Golang Timer 运行时中 addtimerLocked 和 timerproc 的运作方式如出一辙,因此参考了其中的实现方式(参考 [原理介绍](https://blog.gopheracademy.com/advent-2016/go-timers/))。\n\n#### 基于 Timer 的版本\n\ngo-timewhee : [https://github.com/rfyiamcool/go-timewheel]()\n\n\n</br>\n\n**参考:**\n* [层级时间轮的 Golang 实现](http://russellluo.com/2018/10/golang-implementation-of-hierarchical-timing-wheels.html)\n\n","slug":"算法/时间轮","published":1,"updated":"2022-10-31T09:40:57.114Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl9zdnpo1005kiysy5loufbpm","content":"<h2 id=\"时间轮-Timewheel\"><a href=\"#时间轮-Timewheel\" class=\"headerlink\" title=\"时间轮 Timewheel\"></a>时间轮 Timewheel</h2><h3 id=\"kafka-时间轮的实现\"><a href=\"#kafka-时间轮的实现\" class=\"headerlink\" title=\"kafka 时间轮的实现\"></a>kafka 时间轮的实现</h3><p><a href=\"https://github.com/apache/kafka/tree/3cdc78e6bb1f83973a14ce1550fe3874f7348b05/core/src/main/scala/kafka/utils/timer\">Kafka Timer 实现源码</a> </br></p>\n<p>Kafka 的层级时间轮实现中,利用了 Java 内置的 DelayQueue 结构,将每一层时间轮中所有 “包含有定时任务的 bucket” 都加入到同一个 DelayQueue 中,然后 等到有 bucket 到期后再驱动时钟往前走,并逐个处理该 bucket 中的定时任务</p>\n<ol>\n<li>往层级时间轮中添加一个定时任务 task1 后,会将该任务所属的 bucket2 的到期时间设置为 task1 的到期时间 expiration(= 当前时间 currentTime + 定时任务到期间隔 duration),并将这个 bucket2 添加(Offer)到 DelayQueue 中。</li>\n<li>DelayQueue(内部有一个线程)会等待 “到期时间最早(earliest)的 bucket” 到期,图中等到的是排在队首的 bucket2,于是经由 poll 返回并删除这个 bucket2;随后,时间轮会将当前时间 currentTime 往前移动到 bucket2 的 expiration 所指向的时间(图中是 1ms 所在的位置);最后,bucket2 中包含的 task1 会被删除并执行。</li>\n</ol>\n<h3 id=\"Golang-timewheel-实现\"><a href=\"#Golang-timewheel-实现\" class=\"headerlink\" title=\"Golang timewheel 实现\"></a>Golang timewheel 实现</h3><h4 id=\"参考-Kafka-时间轮实现的-golang-版本\"><a href=\"#参考-Kafka-时间轮实现的-golang-版本\" class=\"headerlink\" title=\"参考 Kafka 时间轮实现的 golang 版本\"></a>参考 Kafka 时间轮实现的 golang 版本</h4><p>timewheel : <a href=\"\">https://github.com/RussellLuo/timingwheel.git</a></p>\n<ol>\n<li>PriorityQueue —— 从 NSQ 借用过来的 优先级队列(基于最小堆实现)。</li>\n<li>DelayQueue —— Offer(添加 bucket)和 Poll(获取并删除 bucket)的运作方式,跟 Golang Timer 运行时中 addtimerLocked 和 timerproc 的运作方式如出一辙,因此参考了其中的实现方式(参考 <a href=\"https://blog.gopheracademy.com/advent-2016/go-timers/\">原理介绍</a>)。</li>\n</ol>\n<h4 id=\"基于-Timer-的版本\"><a href=\"#基于-Timer-的版本\" class=\"headerlink\" title=\"基于 Timer 的版本\"></a>基于 Timer 的版本</h4><p>go-timewhee : <a href=\"\">https://github.com/rfyiamcool/go-timewheel</a></p>\n</br>\n\n<p><strong>参考:</strong></p>\n<ul>\n<li><a href=\"http://russellluo.com/2018/10/golang-implementation-of-hierarchical-timing-wheels.html\">层级时间轮的 Golang 实现</a></li>\n</ul>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"时间轮-Timewheel\"><a href=\"#时间轮-Timewheel\" class=\"headerlink\" title=\"时间轮 Timewheel\"></a>时间轮 Timewheel</h2><h3 id=\"kafka-时间轮的实现\"><a href=\"#kafka-时间轮的实现\" class=\"headerlink\" title=\"kafka 时间轮的实现\"></a>kafka 时间轮的实现</h3><p><a href=\"https://github.com/apache/kafka/tree/3cdc78e6bb1f83973a14ce1550fe3874f7348b05/core/src/main/scala/kafka/utils/timer\">Kafka Timer 实现源码</a> </br></p>\n<p>Kafka 的层级时间轮实现中,利用了 Java 内置的 DelayQueue 结构,将每一层时间轮中所有 “包含有定时任务的 bucket” 都加入到同一个 DelayQueue 中,然后 等到有 bucket 到期后再驱动时钟往前走,并逐个处理该 bucket 中的定时任务</p>\n<ol>\n<li>往层级时间轮中添加一个定时任务 task1 后,会将该任务所属的 bucket2 的到期时间设置为 task1 的到期时间 expiration(= 当前时间 currentTime + 定时任务到期间隔 duration),并将这个 bucket2 添加(Offer)到 DelayQueue 中。</li>\n<li>DelayQueue(内部有一个线程)会等待 “到期时间最早(earliest)的 bucket” 到期,图中等到的是排在队首的 bucket2,于是经由 poll 返回并删除这个 bucket2;随后,时间轮会将当前时间 currentTime 往前移动到 bucket2 的 expiration 所指向的时间(图中是 1ms 所在的位置);最后,bucket2 中包含的 task1 会被删除并执行。</li>\n</ol>\n<h3 id=\"Golang-timewheel-实现\"><a href=\"#Golang-timewheel-实现\" class=\"headerlink\" title=\"Golang timewheel 实现\"></a>Golang timewheel 实现</h3><h4 id=\"参考-Kafka-时间轮实现的-golang-版本\"><a href=\"#参考-Kafka-时间轮实现的-golang-版本\" class=\"headerlink\" title=\"参考 Kafka 时间轮实现的 golang 版本\"></a>参考 Kafka 时间轮实现的 golang 版本</h4><p>timewheel : <a href=\"\">https://github.com/RussellLuo/timingwheel.git</a></p>\n<ol>\n<li>PriorityQueue —— 从 NSQ 借用过来的 优先级队列(基于最小堆实现)。</li>\n<li>DelayQueue —— Offer(添加 bucket)和 Poll(获取并删除 bucket)的运作方式,跟 Golang Timer 运行时中 addtimerLocked 和 timerproc 的运作方式如出一辙,因此参考了其中的实现方式(参考 <a href=\"https://blog.gopheracademy.com/advent-2016/go-timers/\">原理介绍</a>)。</li>\n</ol>\n<h4 id=\"基于-Timer-的版本\"><a href=\"#基于-Timer-的版本\" class=\"headerlink\" title=\"基于 Timer 的版本\"></a>基于 Timer 的版本</h4><p>go-timewhee : <a href=\"\">https://github.com/rfyiamcool/go-timewheel</a></p>\n</br>\n\n<p><strong>参考:</strong></p>\n<ul>\n<li><a href=\"http://russellluo.com/2018/10/golang-implementation-of-hierarchical-timing-wheels.html\">层级时间轮的 Golang 实现</a></li>\n</ul>\n"},{"title":"Java双亲委派机制及用法","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-02T16:00:00.000Z","typora-root-url":"../..","_content":"\n# Java双亲委派机制及用法\n\n## 一、JVM类加载过程\n\n整个生命周期包括如下7个阶段\n\n1. 加载(loading)也称为装载\n2. 验证、准备、解析3个部分统称为链接(Linking)\n3. 初始化\n4. 使用\n5. 销毁\n\n![java类加载过程](/img/java类加载过程.png)\n\n### 二、类加载器介绍\n\nJVM中有内置了三个初始的类加载器。\n\n1. **BootstrapClassLoader(引导类、启动类加载器)**,顶层类加载器,C++实现。加载%JAVA_HOME%lib目录下jar包和类或者或被 `-Xbootclasspath`参数指定的路径中的所有类,加载java.*。\n2. **ExtensionClassLoader(扩展类加载器)** :负责加载目录 `%JRE_HOME%/lib/ext` 目录下的jar包和类,或被 `java.ext.dirs` 系统变量所指定的路径下的jar包。\n3. **AppClassLoader(应用程序类加载器)** :加载当前应用classpath下的所有jar包和类(在spring加载中起到了重要作用)。\n\n### 三、双亲委派模型\n\n一个类加载的时候先交由父类加载器去加载,如果父类加载器也存在父类加载器,继续交由父类加载,以此递归,如果父类加载无法加载再由自己去加载。\n\n![双亲委派模型](/img/java双亲委派模型.png)\n\n**如果想打破双亲委派机制可以自定义一个类加载器继承java.lang.ClassLoader,重写loadClass方法,不交由父类去加载。**\n\n**如果只是想自定义加载class的方法,比如读取二进制流后进行解密等。只需要重写findClass就行了。**\n\n**loadClass方法负责加载流程,findClass方法负责具体加载细节。**\n\n### 四、双亲委派模式的特点\n\n- 避免重复加载`class`,保证数据安全。\n- 保护核心class无法修改。\n- 不同加载器加载同一个class不是同一个class对象,保证class执行安全。","source":"_posts/java/java双亲委派机制及用法.md","raw":"---\ntitle: Java双亲委派机制及用法\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-03\ntags:\n- java\ncategories:\n- java\ntypora-root-url: ../..\n---\n\n# Java双亲委派机制及用法\n\n## 一、JVM类加载过程\n\n整个生命周期包括如下7个阶段\n\n1. 加载(loading)也称为装载\n2. 验证、准备、解析3个部分统称为链接(Linking)\n3. 初始化\n4. 使用\n5. 销毁\n\n![java类加载过程](/img/java类加载过程.png)\n\n### 二、类加载器介绍\n\nJVM中有内置了三个初始的类加载器。\n\n1. **BootstrapClassLoader(引导类、启动类加载器)**,顶层类加载器,C++实现。加载%JAVA_HOME%lib目录下jar包和类或者或被 `-Xbootclasspath`参数指定的路径中的所有类,加载java.*。\n2. **ExtensionClassLoader(扩展类加载器)** :负责加载目录 `%JRE_HOME%/lib/ext` 目录下的jar包和类,或被 `java.ext.dirs` 系统变量所指定的路径下的jar包。\n3. **AppClassLoader(应用程序类加载器)** :加载当前应用classpath下的所有jar包和类(在spring加载中起到了重要作用)。\n\n### 三、双亲委派模型\n\n一个类加载的时候先交由父类加载器去加载,如果父类加载器也存在父类加载器,继续交由父类加载,以此递归,如果父类加载无法加载再由自己去加载。\n\n![双亲委派模型](/img/java双亲委派模型.png)\n\n**如果想打破双亲委派机制可以自定义一个类加载器继承java.lang.ClassLoader,重写loadClass方法,不交由父类去加载。**\n\n**如果只是想自定义加载class的方法,比如读取二进制流后进行解密等。只需要重写findClass就行了。**\n\n**loadClass方法负责加载流程,findClass方法负责具体加载细节。**\n\n### 四、双亲委派模式的特点\n\n- 避免重复加载`class`,保证数据安全。\n- 保护核心class无法修改。\n- 不同加载器加载同一个class不是同一个class对象,保证class执行安全。","slug":"java/java双亲委派机制及用法","published":1,"updated":"2022-11-03T03:24:00.607Z","_id":"cla0hyr6z0000mksydy3z686i","comments":1,"layout":"post","photos":[],"link":"","content":"<h1 id=\"Java双亲委派机制及用法\"><a href=\"#Java双亲委派机制及用法\" class=\"headerlink\" title=\"Java双亲委派机制及用法\"></a>Java双亲委派机制及用法</h1><h2 id=\"一、JVM类加载过程\"><a href=\"#一、JVM类加载过程\" class=\"headerlink\" title=\"一、JVM类加载过程\"></a>一、JVM类加载过程</h2><p>整个生命周期包括如下7个阶段</p>\n<ol>\n<li>加载(loading)也称为装载</li>\n<li>验证、准备、解析3个部分统称为链接(Linking)</li>\n<li>初始化</li>\n<li>使用</li>\n<li>销毁</li>\n</ol>\n<p><img src=\"/img/java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.png\" alt=\"java类加载过程\"></p>\n<h3 id=\"二、类加载器介绍\"><a href=\"#二、类加载器介绍\" class=\"headerlink\" title=\"二、类加载器介绍\"></a>二、类加载器介绍</h3><p>JVM中有内置了三个初始的类加载器。</p>\n<ol>\n<li>**BootstrapClassLoader(引导类、启动类加载器)**,顶层类加载器,C++实现。加载%JAVA_HOME%lib目录下jar包和类或者或被 <code>-Xbootclasspath</code>参数指定的路径中的所有类,加载java.*。</li>\n<li><strong>ExtensionClassLoader(扩展类加载器)</strong> :负责加载目录 <code>%JRE_HOME%/lib/ext</code> 目录下的jar包和类,或被 <code>java.ext.dirs</code> 系统变量所指定的路径下的jar包。</li>\n<li><strong>AppClassLoader(应用程序类加载器)</strong> :加载当前应用classpath下的所有jar包和类(在spring加载中起到了重要作用)。</li>\n</ol>\n<h3 id=\"三、双亲委派模型\"><a href=\"#三、双亲委派模型\" class=\"headerlink\" title=\"三、双亲委派模型\"></a>三、双亲委派模型</h3><p>一个类加载的时候先交由父类加载器去加载,如果父类加载器也存在父类加载器,继续交由父类加载,以此递归,如果父类加载无法加载再由自己去加载。</p>\n<p><img src=\"/img/java%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B.png\" alt=\"双亲委派模型\"></p>\n<p><strong>如果想打破双亲委派机制可以自定义一个类加载器继承java.lang.ClassLoader,重写loadClass方法,不交由父类去加载。</strong></p>\n<p><strong>如果只是想自定义加载class的方法,比如读取二进制流后进行解密等。只需要重写findClass就行了。</strong></p>\n<p><strong>loadClass方法负责加载流程,findClass方法负责具体加载细节。</strong></p>\n<h3 id=\"四、双亲委派模式的特点\"><a href=\"#四、双亲委派模式的特点\" class=\"headerlink\" title=\"四、双亲委派模式的特点\"></a>四、双亲委派模式的特点</h3><ul>\n<li>避免重复加载<code>class</code>,保证数据安全。</li>\n<li>保护核心class无法修改。</li>\n<li>不同加载器加载同一个class不是同一个class对象,保证class执行安全。</li>\n</ul>\n","site":{"data":{}},"excerpt":"","more":"<h1 id=\"Java双亲委派机制及用法\"><a href=\"#Java双亲委派机制及用法\" class=\"headerlink\" title=\"Java双亲委派机制及用法\"></a>Java双亲委派机制及用法</h1><h2 id=\"一、JVM类加载过程\"><a href=\"#一、JVM类加载过程\" class=\"headerlink\" title=\"一、JVM类加载过程\"></a>一、JVM类加载过程</h2><p>整个生命周期包括如下7个阶段</p>\n<ol>\n<li>加载(loading)也称为装载</li>\n<li>验证、准备、解析3个部分统称为链接(Linking)</li>\n<li>初始化</li>\n<li>使用</li>\n<li>销毁</li>\n</ol>\n<p><img src=\"/img/java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.png\" alt=\"java类加载过程\"></p>\n<h3 id=\"二、类加载器介绍\"><a href=\"#二、类加载器介绍\" class=\"headerlink\" title=\"二、类加载器介绍\"></a>二、类加载器介绍</h3><p>JVM中有内置了三个初始的类加载器。</p>\n<ol>\n<li>**BootstrapClassLoader(引导类、启动类加载器)**,顶层类加载器,C++实现。加载%JAVA_HOME%lib目录下jar包和类或者或被 <code>-Xbootclasspath</code>参数指定的路径中的所有类,加载java.*。</li>\n<li><strong>ExtensionClassLoader(扩展类加载器)</strong> :负责加载目录 <code>%JRE_HOME%/lib/ext</code> 目录下的jar包和类,或被 <code>java.ext.dirs</code> 系统变量所指定的路径下的jar包。</li>\n<li><strong>AppClassLoader(应用程序类加载器)</strong> :加载当前应用classpath下的所有jar包和类(在spring加载中起到了重要作用)。</li>\n</ol>\n<h3 id=\"三、双亲委派模型\"><a href=\"#三、双亲委派模型\" class=\"headerlink\" title=\"三、双亲委派模型\"></a>三、双亲委派模型</h3><p>一个类加载的时候先交由父类加载器去加载,如果父类加载器也存在父类加载器,继续交由父类加载,以此递归,如果父类加载无法加载再由自己去加载。</p>\n<p><img src=\"/img/java%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B.png\" alt=\"双亲委派模型\"></p>\n<p><strong>如果想打破双亲委派机制可以自定义一个类加载器继承java.lang.ClassLoader,重写loadClass方法,不交由父类去加载。</strong></p>\n<p><strong>如果只是想自定义加载class的方法,比如读取二进制流后进行解密等。只需要重写findClass就行了。</strong></p>\n<p><strong>loadClass方法负责加载流程,findClass方法负责具体加载细节。</strong></p>\n<h3 id=\"四、双亲委派模式的特点\"><a href=\"#四、双亲委派模式的特点\" class=\"headerlink\" title=\"四、双亲委派模式的特点\"></a>四、双亲委派模式的特点</h3><ul>\n<li>避免重复加载<code>class</code>,保证数据安全。</li>\n<li>保护核心class无法修改。</li>\n<li>不同加载器加载同一个class不是同一个class对象,保证class执行安全。</li>\n</ul>\n"},{"title":"Arthas","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-02T16:00:00.000Z","_content":"\n# Arthas\n\n## 快速入门\n\nGitHub:https://github.com/alibaba/arthas\n\n文档地址:https://arthas.aliyun.com/\n\n下载并启动arthas\n\n```shell\ncurl -O https://arthas.aliyun.com/arthas-boot.jar\njava -jar arthas-boot.jar\n```\n\n**使用`as.sh`**\n\nArthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 `回车` 执行即可:\n\n```shell\ncurl -L https://arthas.aliyun.com/install.sh | sh\t\t\n```\n\n## 常用命令\n\n### logger\n\n更新 logger lever\n\n```shell\nlogger --name ROOT --level debug\n```\n\n### jad\n\n反编译指定已加载类的源码\n\n使用参考 https://arthas.aliyun.com/doc/jad.html\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| --------------------: | :----------------------------------------- |\n| *class-pattern* | 类名表达式匹配 |\n| `[c:]` | 类所属 ClassLoader 的 hashcode |\n| `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name |\n| [E] | 开启正则表达式匹配,默认为通配符匹配 |\n\n### tt\n\n方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测\n\n```shell\ntt -t demo.MathGame primeFactors\n```\n\n**表格字段说明**\n\n| 表格字段 | 字段解释 |\n| --------- | ------------------------------------------------------------ |\n| INDEX | 时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。 |\n| TIMESTAMP | 方法执行的本机时间,记录了这个时间片段所发生的本机时间 |\n| COST(ms) | 方法执行的耗时 |\n| IS-RET | 方法是否以正常返回的形式结束 |\n| IS-EXP | 方法是否以抛异常的形式结束 |\n| OBJECT | 执行对象的`hashCode()`,注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体 |\n| CLASS | 执行的类名 |\n| METHOD | 执行的方法名 |\n\n### trace\n\n`trace` 命令能主动搜索 `class-pattern`/`method-pattern` 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| ------------------: | :----------------------------------- |\n| *class-pattern* | 类名表达式匹配 |\n| *method-pattern* | 方法名表达式匹配 |\n| *condition-express* | 条件表达式 |\n| [E] | 开启正则表达式匹配,默认为通配符匹配 |\n| `[n:]` | 命令执行次数 |\n| `#cost` | 方法执行耗时 |\n\n```shell\ntrace demo.MathGame run -n 10 '#cost > 10'\n```\n\n\n\n### watch\n\n让你能方便的观察到指定函数的调用情况。能观察到的范围为:`返回值`、`抛出异常`、`入参`,通过编写 OGNL 表达式进行对应变量的查看。\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| ------------------: | :------------------------------------------------ |\n| *class-pattern* | 类名表达式匹配 |\n| *method-pattern* | 函数名表达式匹配 |\n| *express* | 观察表达式,默认值:`{params, target, returnObj}` |\n| *condition-express* | 条件表达式 |\n| [b] | 在**函数调用之前**观察 |\n| [e] | 在**函数异常之后**观察 |\n| [s] | 在**函数返回之后**观察 |\n| [f] | 在**函数结束之后**(正常返回和异常返回)观察 |\n| [E] | 开启正则表达式匹配,默认为通配符匹配 |\n| [x:] | 指定输出结果的属性遍历深度,默认为 1,最大值是 4 |\n\n```shell\nwatch demo.MathGame primeFactors -x 2\n```\n\n### dashboard\n\n当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| -------: | :--------------------------------------- |\n| [i:] | 刷新实时数据的时间间隔 (ms),默认 5000ms |\n| [n:] | 刷新实时数据的次数 |\n\n","source":"_posts/java/arthas.md","raw":"---\ntitle: Arthas\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-03\ntags:\n- java\ncategories:\n- java\n---\n\n# Arthas\n\n## 快速入门\n\nGitHub:https://github.com/alibaba/arthas\n\n文档地址:https://arthas.aliyun.com/\n\n下载并启动arthas\n\n```shell\ncurl -O https://arthas.aliyun.com/arthas-boot.jar\njava -jar arthas-boot.jar\n```\n\n**使用`as.sh`**\n\nArthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 `回车` 执行即可:\n\n```shell\ncurl -L https://arthas.aliyun.com/install.sh | sh\t\t\n```\n\n## 常用命令\n\n### logger\n\n更新 logger lever\n\n```shell\nlogger --name ROOT --level debug\n```\n\n### jad\n\n反编译指定已加载类的源码\n\n使用参考 https://arthas.aliyun.com/doc/jad.html\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| --------------------: | :----------------------------------------- |\n| *class-pattern* | 类名表达式匹配 |\n| `[c:]` | 类所属 ClassLoader 的 hashcode |\n| `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name |\n| [E] | 开启正则表达式匹配,默认为通配符匹配 |\n\n### tt\n\n方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测\n\n```shell\ntt -t demo.MathGame primeFactors\n```\n\n**表格字段说明**\n\n| 表格字段 | 字段解释 |\n| --------- | ------------------------------------------------------------ |\n| INDEX | 时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。 |\n| TIMESTAMP | 方法执行的本机时间,记录了这个时间片段所发生的本机时间 |\n| COST(ms) | 方法执行的耗时 |\n| IS-RET | 方法是否以正常返回的形式结束 |\n| IS-EXP | 方法是否以抛异常的形式结束 |\n| OBJECT | 执行对象的`hashCode()`,注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体 |\n| CLASS | 执行的类名 |\n| METHOD | 执行的方法名 |\n\n### trace\n\n`trace` 命令能主动搜索 `class-pattern`/`method-pattern` 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| ------------------: | :----------------------------------- |\n| *class-pattern* | 类名表达式匹配 |\n| *method-pattern* | 方法名表达式匹配 |\n| *condition-express* | 条件表达式 |\n| [E] | 开启正则表达式匹配,默认为通配符匹配 |\n| `[n:]` | 命令执行次数 |\n| `#cost` | 方法执行耗时 |\n\n```shell\ntrace demo.MathGame run -n 10 '#cost > 10'\n```\n\n\n\n### watch\n\n让你能方便的观察到指定函数的调用情况。能观察到的范围为:`返回值`、`抛出异常`、`入参`,通过编写 OGNL 表达式进行对应变量的查看。\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| ------------------: | :------------------------------------------------ |\n| *class-pattern* | 类名表达式匹配 |\n| *method-pattern* | 函数名表达式匹配 |\n| *express* | 观察表达式,默认值:`{params, target, returnObj}` |\n| *condition-express* | 条件表达式 |\n| [b] | 在**函数调用之前**观察 |\n| [e] | 在**函数异常之后**观察 |\n| [s] | 在**函数返回之后**观察 |\n| [f] | 在**函数结束之后**(正常返回和异常返回)观察 |\n| [E] | 开启正则表达式匹配,默认为通配符匹配 |\n| [x:] | 指定输出结果的属性遍历深度,默认为 1,最大值是 4 |\n\n```shell\nwatch demo.MathGame primeFactors -x 2\n```\n\n### dashboard\n\n当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。\n\n**参数说明**\n\n| 参数名称 | 参数说明 |\n| -------: | :--------------------------------------- |\n| [i:] | 刷新实时数据的时间间隔 (ms),默认 5000ms |\n| [n:] | 刷新实时数据的次数 |\n\n","slug":"java/arthas","published":1,"updated":"2022-11-03T12:03:35.643Z","_id":"cla10lblf000048sy07z887qk","comments":1,"layout":"post","photos":[],"link":"","content":"<h1 id=\"Arthas\"><a href=\"#Arthas\" class=\"headerlink\" title=\"Arthas\"></a>Arthas</h1><h2 id=\"快速入门\"><a href=\"#快速入门\" class=\"headerlink\" title=\"快速入门\"></a>快速入门</h2><p>GitHub:<a href=\"https://github.com/alibaba/arthas\">https://github.com/alibaba/arthas</a></p>\n<p>文档地址:<a href=\"https://arthas.aliyun.com/\">https://arthas.aliyun.com/</a></p>\n<p>下载并启动arthas</p>\n<pre><code class=\"shell\">curl -O https://arthas.aliyun.com/arthas-boot.jar\njava -jar arthas-boot.jar\n</code></pre>\n<p><strong>使用<code>as.sh</code></strong></p>\n<p>Arthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 <code>回车</code> 执行即可:</p>\n<pre><code class=\"shell\">curl -L https://arthas.aliyun.com/install.sh | sh \n</code></pre>\n<h2 id=\"常用命令\"><a href=\"#常用命令\" class=\"headerlink\" title=\"常用命令\"></a>常用命令</h2><h3 id=\"logger\"><a href=\"#logger\" class=\"headerlink\" title=\"logger\"></a>logger</h3><p>更新 logger lever</p>\n<pre><code class=\"shell\">logger --name ROOT --level debug\n</code></pre>\n<h3 id=\"jad\"><a href=\"#jad\" class=\"headerlink\" title=\"jad\"></a>jad</h3><p>反编译指定已加载类的源码</p>\n<p>使用参考 <a href=\"https://arthas.aliyun.com/doc/jad.html\">https://arthas.aliyun.com/doc/jad.html</a></p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\"><em>class-pattern</em></td>\n<td align=\"left\">类名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><code>[c:]</code></td>\n<td align=\"left\">类所属 ClassLoader 的 hashcode</td>\n</tr>\n<tr>\n<td align=\"right\"><code>[classLoaderClass:]</code></td>\n<td align=\"left\">指定执行表达式的 ClassLoader 的 class name</td>\n</tr>\n<tr>\n<td align=\"right\">[E]</td>\n<td align=\"left\">开启正则表达式匹配,默认为通配符匹配</td>\n</tr>\n</tbody></table>\n<h3 id=\"tt\"><a href=\"#tt\" class=\"headerlink\" title=\"tt\"></a>tt</h3><p>方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测</p>\n<pre><code class=\"shell\">tt -t demo.MathGame primeFactors\n</code></pre>\n<p><strong>表格字段说明</strong></p>\n<table>\n<thead>\n<tr>\n<th>表格字段</th>\n<th>字段解释</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>INDEX</td>\n<td>时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。</td>\n</tr>\n<tr>\n<td>TIMESTAMP</td>\n<td>方法执行的本机时间,记录了这个时间片段所发生的本机时间</td>\n</tr>\n<tr>\n<td>COST(ms)</td>\n<td>方法执行的耗时</td>\n</tr>\n<tr>\n<td>IS-RET</td>\n<td>方法是否以正常返回的形式结束</td>\n</tr>\n<tr>\n<td>IS-EXP</td>\n<td>方法是否以抛异常的形式结束</td>\n</tr>\n<tr>\n<td>OBJECT</td>\n<td>执行对象的<code>hashCode()</code>,注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体</td>\n</tr>\n<tr>\n<td>CLASS</td>\n<td>执行的类名</td>\n</tr>\n<tr>\n<td>METHOD</td>\n<td>执行的方法名</td>\n</tr>\n</tbody></table>\n<h3 id=\"trace\"><a href=\"#trace\" class=\"headerlink\" title=\"trace\"></a>trace</h3><p><code>trace</code> 命令能主动搜索 <code>class-pattern</code>/<code>method-pattern</code> 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。</p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\"><em>class-pattern</em></td>\n<td align=\"left\">类名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>method-pattern</em></td>\n<td align=\"left\">方法名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>condition-express</em></td>\n<td align=\"left\">条件表达式</td>\n</tr>\n<tr>\n<td align=\"right\">[E]</td>\n<td align=\"left\">开启正则表达式匹配,默认为通配符匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><code>[n:]</code></td>\n<td align=\"left\">命令执行次数</td>\n</tr>\n<tr>\n<td align=\"right\"><code>#cost</code></td>\n<td align=\"left\">方法执行耗时</td>\n</tr>\n</tbody></table>\n<pre><code class=\"shell\">trace demo.MathGame run -n 10 '#cost > 10'\n</code></pre>\n<h3 id=\"watch\"><a href=\"#watch\" class=\"headerlink\" title=\"watch\"></a>watch</h3><p>让你能方便的观察到指定函数的调用情况。能观察到的范围为:<code>返回值</code>、<code>抛出异常</code>、<code>入参</code>,通过编写 OGNL 表达式进行对应变量的查看。</p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\"><em>class-pattern</em></td>\n<td align=\"left\">类名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>method-pattern</em></td>\n<td align=\"left\">函数名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>express</em></td>\n<td align=\"left\">观察表达式,默认值:<code>{params, target, returnObj}</code></td>\n</tr>\n<tr>\n<td align=\"right\"><em>condition-express</em></td>\n<td align=\"left\">条件表达式</td>\n</tr>\n<tr>\n<td align=\"right\">[b]</td>\n<td align=\"left\">在<strong>函数调用之前</strong>观察</td>\n</tr>\n<tr>\n<td align=\"right\">[e]</td>\n<td align=\"left\">在<strong>函数异常之后</strong>观察</td>\n</tr>\n<tr>\n<td align=\"right\">[s]</td>\n<td align=\"left\">在<strong>函数返回之后</strong>观察</td>\n</tr>\n<tr>\n<td align=\"right\">[f]</td>\n<td align=\"left\">在<strong>函数结束之后</strong>(正常返回和异常返回)观察</td>\n</tr>\n<tr>\n<td align=\"right\">[E]</td>\n<td align=\"left\">开启正则表达式匹配,默认为通配符匹配</td>\n</tr>\n<tr>\n<td align=\"right\">[x:]</td>\n<td align=\"left\">指定输出结果的属性遍历深度,默认为 1,最大值是 4</td>\n</tr>\n</tbody></table>\n<pre><code class=\"shell\">watch demo.MathGame primeFactors -x 2\n</code></pre>\n<h3 id=\"dashboard\"><a href=\"#dashboard\" class=\"headerlink\" title=\"dashboard\"></a>dashboard</h3><p>当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。</p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\">[i:]</td>\n<td align=\"left\">刷新实时数据的时间间隔 (ms),默认 5000ms</td>\n</tr>\n<tr>\n<td align=\"right\">[n:]</td>\n<td align=\"left\">刷新实时数据的次数</td>\n</tr>\n</tbody></table>\n","site":{"data":{}},"excerpt":"","more":"<h1 id=\"Arthas\"><a href=\"#Arthas\" class=\"headerlink\" title=\"Arthas\"></a>Arthas</h1><h2 id=\"快速入门\"><a href=\"#快速入门\" class=\"headerlink\" title=\"快速入门\"></a>快速入门</h2><p>GitHub:<a href=\"https://github.com/alibaba/arthas\">https://github.com/alibaba/arthas</a></p>\n<p>文档地址:<a href=\"https://arthas.aliyun.com/\">https://arthas.aliyun.com/</a></p>\n<p>下载并启动arthas</p>\n<pre><code class=\"shell\">curl -O https://arthas.aliyun.com/arthas-boot.jar\njava -jar arthas-boot.jar\n</code></pre>\n<p><strong>使用<code>as.sh</code></strong></p>\n<p>Arthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 <code>回车</code> 执行即可:</p>\n<pre><code class=\"shell\">curl -L https://arthas.aliyun.com/install.sh | sh \n</code></pre>\n<h2 id=\"常用命令\"><a href=\"#常用命令\" class=\"headerlink\" title=\"常用命令\"></a>常用命令</h2><h3 id=\"logger\"><a href=\"#logger\" class=\"headerlink\" title=\"logger\"></a>logger</h3><p>更新 logger lever</p>\n<pre><code class=\"shell\">logger --name ROOT --level debug\n</code></pre>\n<h3 id=\"jad\"><a href=\"#jad\" class=\"headerlink\" title=\"jad\"></a>jad</h3><p>反编译指定已加载类的源码</p>\n<p>使用参考 <a href=\"https://arthas.aliyun.com/doc/jad.html\">https://arthas.aliyun.com/doc/jad.html</a></p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\"><em>class-pattern</em></td>\n<td align=\"left\">类名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><code>[c:]</code></td>\n<td align=\"left\">类所属 ClassLoader 的 hashcode</td>\n</tr>\n<tr>\n<td align=\"right\"><code>[classLoaderClass:]</code></td>\n<td align=\"left\">指定执行表达式的 ClassLoader 的 class name</td>\n</tr>\n<tr>\n<td align=\"right\">[E]</td>\n<td align=\"left\">开启正则表达式匹配,默认为通配符匹配</td>\n</tr>\n</tbody></table>\n<h3 id=\"tt\"><a href=\"#tt\" class=\"headerlink\" title=\"tt\"></a>tt</h3><p>方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测</p>\n<pre><code class=\"shell\">tt -t demo.MathGame primeFactors\n</code></pre>\n<p><strong>表格字段说明</strong></p>\n<table>\n<thead>\n<tr>\n<th>表格字段</th>\n<th>字段解释</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>INDEX</td>\n<td>时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。</td>\n</tr>\n<tr>\n<td>TIMESTAMP</td>\n<td>方法执行的本机时间,记录了这个时间片段所发生的本机时间</td>\n</tr>\n<tr>\n<td>COST(ms)</td>\n<td>方法执行的耗时</td>\n</tr>\n<tr>\n<td>IS-RET</td>\n<td>方法是否以正常返回的形式结束</td>\n</tr>\n<tr>\n<td>IS-EXP</td>\n<td>方法是否以抛异常的形式结束</td>\n</tr>\n<tr>\n<td>OBJECT</td>\n<td>执行对象的<code>hashCode()</code>,注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体</td>\n</tr>\n<tr>\n<td>CLASS</td>\n<td>执行的类名</td>\n</tr>\n<tr>\n<td>METHOD</td>\n<td>执行的方法名</td>\n</tr>\n</tbody></table>\n<h3 id=\"trace\"><a href=\"#trace\" class=\"headerlink\" title=\"trace\"></a>trace</h3><p><code>trace</code> 命令能主动搜索 <code>class-pattern</code>/<code>method-pattern</code> 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。</p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\"><em>class-pattern</em></td>\n<td align=\"left\">类名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>method-pattern</em></td>\n<td align=\"left\">方法名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>condition-express</em></td>\n<td align=\"left\">条件表达式</td>\n</tr>\n<tr>\n<td align=\"right\">[E]</td>\n<td align=\"left\">开启正则表达式匹配,默认为通配符匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><code>[n:]</code></td>\n<td align=\"left\">命令执行次数</td>\n</tr>\n<tr>\n<td align=\"right\"><code>#cost</code></td>\n<td align=\"left\">方法执行耗时</td>\n</tr>\n</tbody></table>\n<pre><code class=\"shell\">trace demo.MathGame run -n 10 '#cost > 10'\n</code></pre>\n<h3 id=\"watch\"><a href=\"#watch\" class=\"headerlink\" title=\"watch\"></a>watch</h3><p>让你能方便的观察到指定函数的调用情况。能观察到的范围为:<code>返回值</code>、<code>抛出异常</code>、<code>入参</code>,通过编写 OGNL 表达式进行对应变量的查看。</p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\"><em>class-pattern</em></td>\n<td align=\"left\">类名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>method-pattern</em></td>\n<td align=\"left\">函数名表达式匹配</td>\n</tr>\n<tr>\n<td align=\"right\"><em>express</em></td>\n<td align=\"left\">观察表达式,默认值:<code>{params, target, returnObj}</code></td>\n</tr>\n<tr>\n<td align=\"right\"><em>condition-express</em></td>\n<td align=\"left\">条件表达式</td>\n</tr>\n<tr>\n<td align=\"right\">[b]</td>\n<td align=\"left\">在<strong>函数调用之前</strong>观察</td>\n</tr>\n<tr>\n<td align=\"right\">[e]</td>\n<td align=\"left\">在<strong>函数异常之后</strong>观察</td>\n</tr>\n<tr>\n<td align=\"right\">[s]</td>\n<td align=\"left\">在<strong>函数返回之后</strong>观察</td>\n</tr>\n<tr>\n<td align=\"right\">[f]</td>\n<td align=\"left\">在<strong>函数结束之后</strong>(正常返回和异常返回)观察</td>\n</tr>\n<tr>\n<td align=\"right\">[E]</td>\n<td align=\"left\">开启正则表达式匹配,默认为通配符匹配</td>\n</tr>\n<tr>\n<td align=\"right\">[x:]</td>\n<td align=\"left\">指定输出结果的属性遍历深度,默认为 1,最大值是 4</td>\n</tr>\n</tbody></table>\n<pre><code class=\"shell\">watch demo.MathGame primeFactors -x 2\n</code></pre>\n<h3 id=\"dashboard\"><a href=\"#dashboard\" class=\"headerlink\" title=\"dashboard\"></a>dashboard</h3><p>当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。</p>\n<p><strong>参数说明</strong></p>\n<table>\n<thead>\n<tr>\n<th align=\"right\">参数名称</th>\n<th align=\"left\">参数说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td align=\"right\">[i:]</td>\n<td align=\"left\">刷新实时数据的时间间隔 (ms),默认 5000ms</td>\n</tr>\n<tr>\n<td align=\"right\">[n:]</td>\n<td align=\"left\">刷新实时数据的次数</td>\n</tr>\n</tbody></table>\n"},{"title":"Java 知识图谱","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-02T16:00:00.000Z","_content":"\n\n\n","source":"_posts/java/Java-知识图谱.md","raw":"---\ntitle: Java 知识图谱\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-03\ntags:\n- java\n- 知识图谱\ncategories:\n- java\n---\n\n\n\n","slug":"java/Java-知识图谱","published":1,"updated":"2022-11-03T12:10:46.912Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cla1y95wr0000cvsycr4486fy","content":"","site":{"data":{}},"excerpt":"","more":""},{"title":"todo","auto_excerpt":{"enable":true,"length":150},"date":"2023-02-24T02:27:41.000Z","_content":"1. Jetty 架构\n2. spring redis factory 实现\n","source":"_posts/todo.md","raw":"---\ntitle: todo\nauto_excerpt:\n enable: true\n length: 150\ndate: 2023-02-24 10:27:41\ntags:\ncategories:\n---\n1. Jetty 架构\n2. spring redis factory 实现\n","slug":"todo","published":1,"updated":"2023-03-09T03:01:41.751Z","comments":1,"layout":"post","photos":[],"link":"","_id":"clfhsb9tm0000cesy6r3dcw6c","content":"<ol>\n<li>Jetty 架构</li>\n<li>spring redis factory 实现</li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<ol>\n<li>Jetty 架构</li>\n<li>spring redis factory 实现</li>\n</ol>\n"},{"title":"consul 常用命令","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-01T07:43:04.000Z","_content":"\n## consul 常用命令\n\n#### 查询\n\n```shell\n# 通过健康检查的服务\ncurl http://localhost:8500/v1/health/service/:service_id?passing\n# token\ncurl --header \"X-Consul-Token: {token}\" http://127.0.0.1:8500/v1/catalog/services\n\ncurl http://localhost:8500/v1/catalog/service/:service_id\n```\n\n#### 注册\n\n```shell\n# 注销\ncurl -XPUT http://localhost:8500/v1/agent/service/deregister/:service_id\t\n\ncurl -XPUT http://localhost:18500/v1/agent/service/deregister/enterprise\t\n```\n\n","source":"_posts/consul/consul 常用命令.md","raw":"---\ntitle: consul 常用命令\ntags:\n - consul\ncategories:\n - consul\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-01 15:43:04\n---\n\n## consul 常用命令\n\n#### 查询\n\n```shell\n# 通过健康检查的服务\ncurl http://localhost:8500/v1/health/service/:service_id?passing\n# token\ncurl --header \"X-Consul-Token: {token}\" http://127.0.0.1:8500/v1/catalog/services\n\ncurl http://localhost:8500/v1/catalog/service/:service_id\n```\n\n#### 注册\n\n```shell\n# 注销\ncurl -XPUT http://localhost:8500/v1/agent/service/deregister/:service_id\t\n\ncurl -XPUT http://localhost:18500/v1/agent/service/deregister/enterprise\t\n```\n\n","slug":"consul/consul 常用命令","published":1,"updated":"2023-03-21T04:58:41.893Z","_id":"clfhsb9tq0001cesycnoyai7o","comments":1,"layout":"post","photos":[],"link":"","content":"<h2 id=\"consul-常用命令\"><a href=\"#consul-常用命令\" class=\"headerlink\" title=\"consul 常用命令\"></a>consul 常用命令</h2><h4 id=\"查询\"><a href=\"#查询\" class=\"headerlink\" title=\"查询\"></a>查询</h4><pre><code class=\"shell\"># 通过健康检查的服务\ncurl http://localhost:8500/v1/health/service/:service_id?passing\n# token\ncurl --header "X-Consul-Token: {token}" http://127.0.0.1:8500/v1/catalog/services\n\ncurl http://localhost:8500/v1/catalog/service/:service_id\n</code></pre>\n<h4 id=\"注册\"><a href=\"#注册\" class=\"headerlink\" title=\"注册\"></a>注册</h4><pre><code class=\"shell\"># 注销\ncurl -XPUT http://localhost:8500/v1/agent/service/deregister/:service_id \n\ncurl -XPUT http://localhost:18500/v1/agent/service/deregister/enterprise \n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"consul-常用命令\"><a href=\"#consul-常用命令\" class=\"headerlink\" title=\"consul 常用命令\"></a>consul 常用命令</h2><h4 id=\"查询\"><a href=\"#查询\" class=\"headerlink\" title=\"查询\"></a>查询</h4><pre><code class=\"shell\"># 通过健康检查的服务\ncurl http://localhost:8500/v1/health/service/:service_id?passing\n# token\ncurl --header "X-Consul-Token: {token}" http://127.0.0.1:8500/v1/catalog/services\n\ncurl http://localhost:8500/v1/catalog/service/:service_id\n</code></pre>\n<h4 id=\"注册\"><a href=\"#注册\" class=\"headerlink\" title=\"注册\"></a>注册</h4><pre><code class=\"shell\"># 注销\ncurl -XPUT http://localhost:8500/v1/agent/service/deregister/:service_id \n\ncurl -XPUT http://localhost:18500/v1/agent/service/deregister/enterprise \n</code></pre>\n"},{"title":"Caffeine 使用","auto_excerpt":{"enable":true,"length":150},"date":"2023-03-19T16:00:00.000Z","_content":"\n坐标\n\n```xml\n<dependency>\n <groupId>com.github.ben-manes.caffeine</groupId>\n <artifactId>caffeine</artifactId>\n <version>3.1.5</version>\n</dependency>\n```\n\n","source":"_posts/java/caffeine 使用.md","raw":"---\ntitle: Caffeine 使用\nauto_excerpt:\n enable: true\n length: 150\ndate: 2023-03-20\ntags:\n- java\n- caffeine \ncategories:\n- java\n---\n\n坐标\n\n```xml\n<dependency>\n <groupId>com.github.ben-manes.caffeine</groupId>\n <artifactId>caffeine</artifactId>\n <version>3.1.5</version>\n</dependency>\n```\n\n","slug":"java/caffeine 使用","published":1,"updated":"2023-03-20T08:19:01.055Z","comments":1,"layout":"post","photos":[],"link":"","_id":"clfhsb9u10004cesyeloq930q","content":"<p>坐标</p>\n<pre><code class=\"xml\"><dependency>\n <groupId>com.github.ben-manes.caffeine</groupId>\n <artifactId>caffeine</artifactId>\n <version>3.1.5</version>\n</dependency>\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<p>坐标</p>\n<pre><code class=\"xml\"><dependency>\n <groupId>com.github.ben-manes.caffeine</groupId>\n <artifactId>caffeine</artifactId>\n <version>3.1.5</version>\n</dependency>\n</code></pre>\n"},{"title":"PowerJob 快速开始","auto_excerpt":{"enable":true,"length":150},"date":"2023-03-16T07:43:04.000Z","_content":"\n#### 下载项目\n\n```shell\ngit clone --depth=1 https://github.com/PowerJob/PowerJob.git\n```\n\n#### 运行 docker\n\n```shell\ncd PowerJob\n\n# 前台运行(初次运行时,推荐使用该方式,方便实时查看日志,排查问题)\ndocker-compose up\n\n# 后台运行\ndocker-compose up -d\n```\n\n运行成功后,浏览器访问 http://127.0.0.1:7700/ \n\n应用名称:powerjob-worker-samples\n\n密码:powerjob123\n\n\n\n---\n\n\n\ndocker-compose方式运行,会创建3个容器:\n\n1. **powerjob-mysql**:存储 PowerJob 服务端运行时数据,启动时会自动创建`powerjob-daily`数据库,用户名:`root`,密码:`No1Bug2Please3!`;\n2. powerjob-server:`PowerJob`服务端,源码路径:PowerJob/powerjob-server/powerjob-server-starter;\n3. powerjob-worker-samples:PowerJob 提供的 Worker 示例,源码路径:PowerJob/powerjob-worker-samples。","source":"_posts/powerjob/快速开始.md","raw":"---\ntitle: PowerJob 快速开始\ntags:\n - powerjob\ncategories:\n - powerjob\nauto_excerpt:\n enable: true\n length: 150\ndate: 2023-03-16 15:43:04\n---\n\n#### 下载项目\n\n```shell\ngit clone --depth=1 https://github.com/PowerJob/PowerJob.git\n```\n\n#### 运行 docker\n\n```shell\ncd PowerJob\n\n# 前台运行(初次运行时,推荐使用该方式,方便实时查看日志,排查问题)\ndocker-compose up\n\n# 后台运行\ndocker-compose up -d\n```\n\n运行成功后,浏览器访问 http://127.0.0.1:7700/ \n\n应用名称:powerjob-worker-samples\n\n密码:powerjob123\n\n\n\n---\n\n\n\ndocker-compose方式运行,会创建3个容器:\n\n1. **powerjob-mysql**:存储 PowerJob 服务端运行时数据,启动时会自动创建`powerjob-daily`数据库,用户名:`root`,密码:`No1Bug2Please3!`;\n2. powerjob-server:`PowerJob`服务端,源码路径:PowerJob/powerjob-server/powerjob-server-starter;\n3. powerjob-worker-samples:PowerJob 提供的 Worker 示例,源码路径:PowerJob/powerjob-worker-samples。","slug":"powerjob/快速开始","published":1,"updated":"2023-03-16T10:52:05.116Z","comments":1,"layout":"post","photos":[],"link":"","_id":"clfhsb9u20005cesybz315qz8","content":"<h4 id=\"下载项目\"><a href=\"#下载项目\" class=\"headerlink\" title=\"下载项目\"></a>下载项目</h4><pre><code class=\"shell\">git clone --depth=1 https://github.com/PowerJob/PowerJob.git\n</code></pre>\n<h4 id=\"运行-docker\"><a href=\"#运行-docker\" class=\"headerlink\" title=\"运行 docker\"></a>运行 docker</h4><pre><code class=\"shell\">cd PowerJob\n\n# 前台运行(初次运行时,推荐使用该方式,方便实时查看日志,排查问题)\ndocker-compose up\n\n# 后台运行\ndocker-compose up -d\n</code></pre>\n<p>运行成功后,浏览器访问 <a href=\"http://127.0.0.1:7700/\">http://127.0.0.1:7700/</a> </p>\n<p>应用名称:powerjob-worker-samples</p>\n<p>密码:powerjob123</p>\n<hr>\n<p>docker-compose方式运行,会创建3个容器:</p>\n<ol>\n<li><strong>powerjob-mysql</strong>:存储 PowerJob 服务端运行时数据,启动时会自动创建<code>powerjob-daily</code>数据库,用户名:<code>root</code>,密码:<code>No1Bug2Please3!</code>;</li>\n<li>powerjob-server:<code>PowerJob</code>服务端,源码路径:PowerJob/powerjob-server/powerjob-server-starter;</li>\n<li>powerjob-worker-samples:PowerJob 提供的 Worker 示例,源码路径:PowerJob/powerjob-worker-samples。</li>\n</ol>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"下载项目\"><a href=\"#下载项目\" class=\"headerlink\" title=\"下载项目\"></a>下载项目</h4><pre><code class=\"shell\">git clone --depth=1 https://github.com/PowerJob/PowerJob.git\n</code></pre>\n<h4 id=\"运行-docker\"><a href=\"#运行-docker\" class=\"headerlink\" title=\"运行 docker\"></a>运行 docker</h4><pre><code class=\"shell\">cd PowerJob\n\n# 前台运行(初次运行时,推荐使用该方式,方便实时查看日志,排查问题)\ndocker-compose up\n\n# 后台运行\ndocker-compose up -d\n</code></pre>\n<p>运行成功后,浏览器访问 <a href=\"http://127.0.0.1:7700/\">http://127.0.0.1:7700/</a> </p>\n<p>应用名称:powerjob-worker-samples</p>\n<p>密码:powerjob123</p>\n<hr>\n<p>docker-compose方式运行,会创建3个容器:</p>\n<ol>\n<li><strong>powerjob-mysql</strong>:存储 PowerJob 服务端运行时数据,启动时会自动创建<code>powerjob-daily</code>数据库,用户名:<code>root</code>,密码:<code>No1Bug2Please3!</code>;</li>\n<li>powerjob-server:<code>PowerJob</code>服务端,源码路径:PowerJob/powerjob-server/powerjob-server-starter;</li>\n<li>powerjob-worker-samples:PowerJob 提供的 Worker 示例,源码路径:PowerJob/powerjob-worker-samples。</li>\n</ol>\n"},{"title":"redis 知识图谱","auto_excerpt":{"enable":true,"length":150},"date":"2022-11-03T16:00:00.000Z","_content":"\n# redis 知识图谱\n\n## redis 数据结构\n\n## redis 集群\n\n### redis 集群 Cluster\n\n### redis 主从模式\n\n### redis 哨兵模式\n\n#### 哨兵脑裂问题","source":"_posts/redis/redis 知识图谱.md","raw":"---\ntitle: redis 知识图谱\nauto_excerpt:\n enable: true\n length: 150\ndate: 2022-11-04\ntags:\n- redis\n- 知识图谱\ncategories:\n- redis\n---\n\n# redis 知识图谱\n\n## redis 数据结构\n\n## redis 集群\n\n### redis 集群 Cluster\n\n### redis 主从模式\n\n### redis 哨兵模式\n\n#### 哨兵脑裂问题","slug":"redis/redis 知识图谱","published":1,"updated":"2022-11-04T10:07:39.025Z","comments":1,"layout":"post","photos":[],"link":"","_id":"clfhsb9u30006cesy1cyk2vwh","content":"<h1 id=\"redis-知识图谱\"><a href=\"#redis-知识图谱\" class=\"headerlink\" title=\"redis 知识图谱\"></a>redis 知识图谱</h1><h2 id=\"redis-数据结构\"><a href=\"#redis-数据结构\" class=\"headerlink\" title=\"redis 数据结构\"></a>redis 数据结构</h2><h2 id=\"redis-集群\"><a href=\"#redis-集群\" class=\"headerlink\" title=\"redis 集群\"></a>redis 集群</h2><h3 id=\"redis-集群-Cluster\"><a href=\"#redis-集群-Cluster\" class=\"headerlink\" title=\"redis 集群 Cluster\"></a>redis 集群 Cluster</h3><h3 id=\"redis-主从模式\"><a href=\"#redis-主从模式\" class=\"headerlink\" title=\"redis 主从模式\"></a>redis 主从模式</h3><h3 id=\"redis-哨兵模式\"><a href=\"#redis-哨兵模式\" class=\"headerlink\" title=\"redis 哨兵模式\"></a>redis 哨兵模式</h3><h4 id=\"哨兵脑裂问题\"><a href=\"#哨兵脑裂问题\" class=\"headerlink\" title=\"哨兵脑裂问题\"></a>哨兵脑裂问题</h4>","site":{"data":{}},"excerpt":"","more":"<h1 id=\"redis-知识图谱\"><a href=\"#redis-知识图谱\" class=\"headerlink\" title=\"redis 知识图谱\"></a>redis 知识图谱</h1><h2 id=\"redis-数据结构\"><a href=\"#redis-数据结构\" class=\"headerlink\" title=\"redis 数据结构\"></a>redis 数据结构</h2><h2 id=\"redis-集群\"><a href=\"#redis-集群\" class=\"headerlink\" title=\"redis 集群\"></a>redis 集群</h2><h3 id=\"redis-集群-Cluster\"><a href=\"#redis-集群-Cluster\" class=\"headerlink\" title=\"redis 集群 Cluster\"></a>redis 集群 Cluster</h3><h3 id=\"redis-主从模式\"><a href=\"#redis-主从模式\" class=\"headerlink\" title=\"redis 主从模式\"></a>redis 主从模式</h3><h3 id=\"redis-哨兵模式\"><a href=\"#redis-哨兵模式\" class=\"headerlink\" title=\"redis 哨兵模式\"></a>redis 哨兵模式</h3><h4 id=\"哨兵脑裂问题\"><a href=\"#哨兵脑裂问题\" class=\"headerlink\" title=\"哨兵脑裂问题\"></a>哨兵脑裂问题</h4>"},{"title":"spring-boot-consul 配置中心","auto_excerpt":{"enable":true,"length":150},"date":"2023-03-14T06:10:14.000Z","_content":"\n## Spring Boot Consul 配置中心\n\n文档:https://www.springcloud.cc/spring-cloud-consul.html\n\n#### 依赖引入\n\nspring boot parent\n\n```xml\n<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.1.3.RELEASE</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n```\n\nspring cloud 依赖\n\n```xml\n<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>${spring-cloud-release.version}</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n```\n\nConsul 依赖\n\n```xml\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-consul-config</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-consul-discovery</artifactId>\n\t\t</dependency>\n```\n\n#### 配置文件说明\n\n```yaml\nspring:\n application:\n name: order-service # 应用名称\n profiles:\n active: dev # 指定环境,默认加载 default 环境\n cloud:\n consul:\n # Consul 服务器地址\n host: localhost\n port: 8500\n # 配置中心相关配置\n config:\n # 是否启用配置中心,默认值 true 开启\n enabled: true\n # 设置配置的基本文件夹,默认值 config 可以理解为配置文件所在的最外层文件夹\n prefix: config\n # 设置应用的文件夹名称,默认值 application 一般建议设置为微服务应用名称\n default-context: orderService\n # 配置环境分隔符,默认值 \",\" 和 default-context 配置项搭配\n # 例如应用 orderService 分别有环境 default、dev、test、prod\n # 只需在 config 文件夹下创建 orderService、orderService-dev、orderService-test、orderService-prod 文件夹即可\n profile-separator: '-'\n # 指定配置格式为 yaml\n format: YAML\n # Consul 的 Key/Values 中的 Key,Value 对应整个配置文件\n data-key: orderServiceConfig\n # 以上配置可以理解为:加载 config/orderService/ 文件夹下 Key 为 orderServiceConfig 的 Value 对应的配置信息\n watch:\n # 是否开启自动刷新,默认值 true 开启\n enabled: true\n # 刷新频率,单位:毫秒,默认值 1000\n delay: 1000\n # 服务发现相关配置\n discovery:\n register: true # 是否需要注册\n instance-id: ${spring.application.name}-01 # 注册实例 id(必须唯一)\n service-name: ${spring.application.name} # 服务名称\n port: ${server.port} # 服务端口\n prefer-ip-address: true # 是否使用 ip 地址注册\n ip-address: ${spring.cloud.client.ip-address} # 服务请求 ip\n```\n\n注意需要添加 `@RefreshScope` 注解用于重新刷新作用域实现属性值自动刷新。\n\n#### 配置优先级\n\n配置中心 > 命令行参数 > 本地application.yml > 本地bootstrap.yml\n\n它的设计者认为,配置中心就是最高优先级的,不允许外部修改。\n\n如果想要覆盖,可在远程配置中加下面配置\n\n```yaml\nspring:\n cloud:\n config:\n allowOverride: true\n overrideNone: true\n overrideSystemProperties: false\n```\n\n","source":"_posts/java/spring-boot-consul-配置中心.md","raw":"---\ntitle: spring-boot-consul 配置中心\nauto_excerpt:\n enable: true\n length: 150\ndate: 2023-03-14 14:10:14\ntags:\n- java\n- spring boot\n- consul\ncategories:\n- java\n---\n\n## Spring Boot Consul 配置中心\n\n文档:https://www.springcloud.cc/spring-cloud-consul.html\n\n#### 依赖引入\n\nspring boot parent\n\n```xml\n<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>2.1.3.RELEASE</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n```\n\nspring cloud 依赖\n\n```xml\n<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t\t<artifactId>spring-cloud-dependencies</artifactId>\n\t\t\t\t<version>${spring-cloud-release.version}</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n```\n\nConsul 依赖\n\n```xml\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-consul-config</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.cloud</groupId>\n\t\t\t<artifactId>spring-cloud-starter-consul-discovery</artifactId>\n\t\t</dependency>\n```\n\n#### 配置文件说明\n\n```yaml\nspring:\n application:\n name: order-service # 应用名称\n profiles:\n active: dev # 指定环境,默认加载 default 环境\n cloud:\n consul:\n # Consul 服务器地址\n host: localhost\n port: 8500\n # 配置中心相关配置\n config:\n # 是否启用配置中心,默认值 true 开启\n enabled: true\n # 设置配置的基本文件夹,默认值 config 可以理解为配置文件所在的最外层文件夹\n prefix: config\n # 设置应用的文件夹名称,默认值 application 一般建议设置为微服务应用名称\n default-context: orderService\n # 配置环境分隔符,默认值 \",\" 和 default-context 配置项搭配\n # 例如应用 orderService 分别有环境 default、dev、test、prod\n # 只需在 config 文件夹下创建 orderService、orderService-dev、orderService-test、orderService-prod 文件夹即可\n profile-separator: '-'\n # 指定配置格式为 yaml\n format: YAML\n # Consul 的 Key/Values 中的 Key,Value 对应整个配置文件\n data-key: orderServiceConfig\n # 以上配置可以理解为:加载 config/orderService/ 文件夹下 Key 为 orderServiceConfig 的 Value 对应的配置信息\n watch:\n # 是否开启自动刷新,默认值 true 开启\n enabled: true\n # 刷新频率,单位:毫秒,默认值 1000\n delay: 1000\n # 服务发现相关配置\n discovery:\n register: true # 是否需要注册\n instance-id: ${spring.application.name}-01 # 注册实例 id(必须唯一)\n service-name: ${spring.application.name} # 服务名称\n port: ${server.port} # 服务端口\n prefer-ip-address: true # 是否使用 ip 地址注册\n ip-address: ${spring.cloud.client.ip-address} # 服务请求 ip\n```\n\n注意需要添加 `@RefreshScope` 注解用于重新刷新作用域实现属性值自动刷新。\n\n#### 配置优先级\n\n配置中心 > 命令行参数 > 本地application.yml > 本地bootstrap.yml\n\n它的设计者认为,配置中心就是最高优先级的,不允许外部修改。\n\n如果想要覆盖,可在远程配置中加下面配置\n\n```yaml\nspring:\n cloud:\n config:\n allowOverride: true\n overrideNone: true\n overrideSystemProperties: false\n```\n\n","slug":"java/spring-boot-consul-配置中心","published":1,"updated":"2023-03-15T05:15:33.040Z","comments":1,"layout":"post","photos":[],"link":"","_id":"clfhsb9u5000acesyhjt43a8y","content":"<h2 id=\"Spring-Boot-Consul-配置中心\"><a href=\"#Spring-Boot-Consul-配置中心\" class=\"headerlink\" title=\"Spring Boot Consul 配置中心\"></a>Spring Boot Consul 配置中心</h2><p>文档:<a href=\"https://www.springcloud.cc/spring-cloud-consul.html\">https://www.springcloud.cc/spring-cloud-consul.html</a></p>\n<h4 id=\"依赖引入\"><a href=\"#依赖引入\" class=\"headerlink\" title=\"依赖引入\"></a>依赖引入</h4><p>spring boot parent</p>\n<pre><code class=\"xml\"><parent>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-parent</artifactId>\n <version>2.1.3.RELEASE</version>\n <relativePath /> <!-- lookup parent from repository -->\n </parent>\n</code></pre>\n<p>spring cloud 依赖</p>\n<pre><code class=\"xml\"><dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-dependencies</artifactId>\n <version>${spring-cloud-release.version}</version>\n <type>pom</type>\n <scope>import</scope>\n </dependency>\n </dependencies>\n </dependencyManagement>\n</code></pre>\n<p>Consul 依赖</p>\n<pre><code class=\"xml\"> <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-consul-config</artifactId>\n </dependency>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-consul-discovery</artifactId>\n </dependency>\n</code></pre>\n<h4 id=\"配置文件说明\"><a href=\"#配置文件说明\" class=\"headerlink\" title=\"配置文件说明\"></a>配置文件说明</h4><pre><code class=\"yaml\">spring:\n application:\n name: order-service # 应用名称\n profiles:\n active: dev # 指定环境,默认加载 default 环境\n cloud:\n consul:\n # Consul 服务器地址\n host: localhost\n port: 8500\n # 配置中心相关配置\n config:\n # 是否启用配置中心,默认值 true 开启\n enabled: true\n # 设置配置的基本文件夹,默认值 config 可以理解为配置文件所在的最外层文件夹\n prefix: config\n # 设置应用的文件夹名称,默认值 application 一般建议设置为微服务应用名称\n default-context: orderService\n # 配置环境分隔符,默认值 "," 和 default-context 配置项搭配\n # 例如应用 orderService 分别有环境 default、dev、test、prod\n # 只需在 config 文件夹下创建 orderService、orderService-dev、orderService-test、orderService-prod 文件夹即可\n profile-separator: '-'\n # 指定配置格式为 yaml\n format: YAML\n # Consul 的 Key/Values 中的 Key,Value 对应整个配置文件\n data-key: orderServiceConfig\n # 以上配置可以理解为:加载 config/orderService/ 文件夹下 Key 为 orderServiceConfig 的 Value 对应的配置信息\n watch:\n # 是否开启自动刷新,默认值 true 开启\n enabled: true\n # 刷新频率,单位:毫秒,默认值 1000\n delay: 1000\n # 服务发现相关配置\n discovery:\n register: true # 是否需要注册\n instance-id: ${spring.application.name}-01 # 注册实例 id(必须唯一)\n service-name: ${spring.application.name} # 服务名称\n port: ${server.port} # 服务端口\n prefer-ip-address: true # 是否使用 ip 地址注册\n ip-address: ${spring.cloud.client.ip-address} # 服务请求 ip\n</code></pre>\n<p>注意需要添加 <code>@RefreshScope</code> 注解用于重新刷新作用域实现属性值自动刷新。</p>\n<h4 id=\"配置优先级\"><a href=\"#配置优先级\" class=\"headerlink\" title=\"配置优先级\"></a>配置优先级</h4><p>配置中心 > 命令行参数 > 本地application.yml > 本地bootstrap.yml</p>\n<p>它的设计者认为,配置中心就是最高优先级的,不允许外部修改。</p>\n<p>如果想要覆盖,可在远程配置中加下面配置</p>\n<pre><code class=\"yaml\">spring:\n cloud:\n config:\n allowOverride: true\n overrideNone: true\n overrideSystemProperties: false\n</code></pre>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"Spring-Boot-Consul-配置中心\"><a href=\"#Spring-Boot-Consul-配置中心\" class=\"headerlink\" title=\"Spring Boot Consul 配置中心\"></a>Spring Boot Consul 配置中心</h2><p>文档:<a href=\"https://www.springcloud.cc/spring-cloud-consul.html\">https://www.springcloud.cc/spring-cloud-consul.html</a></p>\n<h4 id=\"依赖引入\"><a href=\"#依赖引入\" class=\"headerlink\" title=\"依赖引入\"></a>依赖引入</h4><p>spring boot parent</p>\n<pre><code class=\"xml\"><parent>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-parent</artifactId>\n <version>2.1.3.RELEASE</version>\n <relativePath /> <!-- lookup parent from repository -->\n </parent>\n</code></pre>\n<p>spring cloud 依赖</p>\n<pre><code class=\"xml\"><dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-dependencies</artifactId>\n <version>${spring-cloud-release.version}</version>\n <type>pom</type>\n <scope>import</scope>\n </dependency>\n </dependencies>\n </dependencyManagement>\n</code></pre>\n<p>Consul 依赖</p>\n<pre><code class=\"xml\"> <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-consul-config</artifactId>\n </dependency>\n <dependency>\n <groupId>org.springframework.cloud</groupId>\n <artifactId>spring-cloud-starter-consul-discovery</artifactId>\n </dependency>\n</code></pre>\n<h4 id=\"配置文件说明\"><a href=\"#配置文件说明\" class=\"headerlink\" title=\"配置文件说明\"></a>配置文件说明</h4><pre><code class=\"yaml\">spring:\n application:\n name: order-service # 应用名称\n profiles:\n active: dev # 指定环境,默认加载 default 环境\n cloud:\n consul:\n # Consul 服务器地址\n host: localhost\n port: 8500\n # 配置中心相关配置\n config:\n # 是否启用配置中心,默认值 true 开启\n enabled: true\n # 设置配置的基本文件夹,默认值 config 可以理解为配置文件所在的最外层文件夹\n prefix: config\n # 设置应用的文件夹名称,默认值 application 一般建议设置为微服务应用名称\n default-context: orderService\n # 配置环境分隔符,默认值 "," 和 default-context 配置项搭配\n # 例如应用 orderService 分别有环境 default、dev、test、prod\n # 只需在 config 文件夹下创建 orderService、orderService-dev、orderService-test、orderService-prod 文件夹即可\n profile-separator: '-'\n # 指定配置格式为 yaml\n format: YAML\n # Consul 的 Key/Values 中的 Key,Value 对应整个配置文件\n data-key: orderServiceConfig\n # 以上配置可以理解为:加载 config/orderService/ 文件夹下 Key 为 orderServiceConfig 的 Value 对应的配置信息\n watch:\n # 是否开启自动刷新,默认值 true 开启\n enabled: true\n # 刷新频率,单位:毫秒,默认值 1000\n delay: 1000\n # 服务发现相关配置\n discovery:\n register: true # 是否需要注册\n instance-id: ${spring.application.name}-01 # 注册实例 id(必须唯一)\n service-name: ${spring.application.name} # 服务名称\n port: ${server.port} # 服务端口\n prefer-ip-address: true # 是否使用 ip 地址注册\n ip-address: ${spring.cloud.client.ip-address} # 服务请求 ip\n</code></pre>\n<p>注意需要添加 <code>@RefreshScope</code> 注解用于重新刷新作用域实现属性值自动刷新。</p>\n<h4 id=\"配置优先级\"><a href=\"#配置优先级\" class=\"headerlink\" title=\"配置优先级\"></a>配置优先级</h4><p>配置中心 > 命令行参数 > 本地application.yml > 本地bootstrap.yml</p>\n<p>它的设计者认为,配置中心就是最高优先级的,不允许外部修改。</p>\n<p>如果想要覆盖,可在远程配置中加下面配置</p>\n<pre><code class=\"yaml\">spring:\n cloud:\n config:\n allowOverride: true\n overrideNone: true\n overrideSystemProperties: false\n</code></pre>\n"}],"PostAsset":[],"PostCategory":[{"post_id":"cl9zdnpmt0005iysye7gu9qj1","category_id":"cl9zdnpmr0002iysy4yexey3j","_id":"cl9zdnpmw000diysyaz7gddj3"},{"post_id":"cl9zdnpml0000iysy0qlahgkn","category_id":"cl9zdnpmr0002iysy4yexey3j","_id":"cl9zdnpmy000hiysy29b9anat"},{"post_id":"cl9zdnpmp0001iysy32vof5gp","category_id":"cl9zdnpmu0007iysye3nd4aze","_id":"cl9zdnpn0000liysy4ripewcg"},{"post_id":"cl9zdnpms0004iysycvwk73p6","category_id":"cl9zdnpmr0002iysy4yexey3j","_id":"cl9zdnpn1000qiysyb9iuedf4"},{"post_id":"cl9zdnpn0000niysybk9e9cwc","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn3000wiysy0ajjeiu6"},{"post_id":"cl9zdnpmu0006iysyho6jg8yw","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn40010iysycnk4hfjk"},{"post_id":"cl9zdnpn1000piysy0rn17ftq","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn50013iysy3ebl265z"},{"post_id":"cl9zdnpn2000tiysyfx2lgah0","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn60018iysyaqeqfa2j"},{"post_id":"cl9zdnpmv000aiysyft3b4yxi","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn7001aiysy2qr7fczc"},{"post_id":"cl9zdnpn3000viysy911w0yct","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn7001diysyh789410z"},{"post_id":"cl9zdnpn4000ziysy7c598fq0","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn8001hiysy1l3ug3nq"},{"post_id":"cl9zdnpmw000ciysy3tlpgv08","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpn9001liysy3r6kdppv"},{"post_id":"cl9zdnpn50012iysy1fg69klu","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpna001oiysy1clg0m9e"},{"post_id":"cl9zdnpn60017iysy49pi4h8o","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpnb001riysy71zh468h"},{"post_id":"cl9zdnpmy000giysy64dcffye","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpnc001uiysycitmexr8"},{"post_id":"cl9zdnpn60019iysy16es8yu7","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpnc001xiysy8ekj1et3"},{"post_id":"cl9zdnpmy000jiysyeskd11oe","category_id":"cl9zdnpn0000kiysy1qgcdw23","_id":"cl9zdnpnd0020iysybx2f1clz"},{"post_id":"cl9zdnpn7001ciysy1cx5cazq","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"cl9zdnpnf0022iysy5lb664ab"},{"post_id":"cl9zdnpn9001kiysy9aps3lzr","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"cl9zdnpng0026iysyb3eqeln4"},{"post_id":"cl9zdnpna001niysy1w3251mn","category_id":"cl9zdnpnc001wiysybfjxhcgy","_id":"cl9zdnpng0029iysy404v8x5s"},{"post_id":"cl9zdnpna001piysy9w38ewz3","category_id":"cl9zdnpnf0023iysyaxg2cqbm","_id":"cl9zdnpni002hiysy09a04bbz"},{"post_id":"cl9zdnpnb001tiysycq0rgumb","category_id":"cl9zdnpnf0023iysyaxg2cqbm","_id":"cl9zdnpnk002oiysy5zqj4pd6"},{"post_id":"cl9zdnpnc001viysygoi1gz0t","category_id":"cl9zdnpnf0023iysyaxg2cqbm","_id":"cl9zdnpnl002siysyeh58ffjo"},{"post_id":"cl9zdnpnd001ziysy5xnncfkp","category_id":"cl9zdnpnk002niysy5f9ob47k","_id":"cl9zdnpnm002xiysy4oum241u"},{"post_id":"cl9zdnpnd0021iysy6gep6h8o","category_id":"cl9zdnpnl002tiysy1ss4756s","_id":"cl9zdnpnm0032iysy7m5857lt"},{"post_id":"cl9zdnpnf0025iysy3ba17agn","category_id":"cl9zdnpnl002tiysy1ss4756s","_id":"cl9zdnpno0035iysy1l9z9d6w"},{"post_id":"cl9zdnpng0028iysy74hb8zx5","category_id":"cl9zdnpnm0031iysy2g0uhd9n","_id":"cl9zdnpno003aiysy3etjholt"},{"post_id":"cl9zdnpnh002diysygp6b5rm1","category_id":"cl9zdnpno0036iysy7p4tfmc8","_id":"cl9zdnpnp003fiysyc1s5ggoo"},{"post_id":"cl9zdnpnh002fiysy5mrzcnpt","category_id":"cl9zdnpno0036iysy7p4tfmc8","_id":"cl9zdnpnq003kiysy63fm1pie"},{"post_id":"cl9zdnpni002kiysydw14htax","category_id":"cl9zdnpno0036iysy7p4tfmc8","_id":"cl9zdnpnr003piysy7zjb89yk"},{"post_id":"cl9zdnpnj002miysy5oso7r7t","category_id":"cl9zdnpno0036iysy7p4tfmc8","_id":"cl9zdnpns003tiysye7khgwdx"},{"post_id":"cl9zdnpnk002piysy1casf1jy","category_id":"cl9zdnpno0036iysy7p4tfmc8","_id":"cl9zdnpns003xiysy2npxhurq"},{"post_id":"cl9zdnpo1005kiysy5loufbpm","category_id":"cl9zdnpo2005liysycnw77fgz","_id":"cl9zdnpo2005piysyeyzn3j4g"},{"post_id":"cl9zdnpn8001giysybetvfzey","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"cl9zdpmea0001kwsy7kq45zvf"},{"post_id":"cla0hyr6z0000mksydy3z686i","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"cla0hyr730002mksy9819d6jx"},{"post_id":"cla10lblf000048sy07z887qk","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"cla10lbll000248sygylj3tci"},{"post_id":"cla1y95wr0000cvsycr4486fy","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"cla1y95x20002cvsy0msg1int"},{"post_id":"clfhsb9u10004cesyeloq930q","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"clfhsb9u5000bcesy3e0mgzvh"},{"post_id":"clfhsb9tq0001cesycnoyai7o","category_id":"clfhsb9tt0002cesyhypx4yd7","_id":"clfhsb9u6000ccesy48lkh44a"},{"post_id":"clfhsb9u30006cesy1cyk2vwh","category_id":"cl9zdnpnm0031iysy2g0uhd9n","_id":"clfhsb9u7000ecesya0kvfpn4"},{"post_id":"clfhsb9u5000acesyhjt43a8y","category_id":"cl9zdnpn9001iiysy27vi0lrd","_id":"clfhsb9u7000gcesy0773cwbr"},{"post_id":"clfhsb9u20005cesybz315qz8","category_id":"clfhsb9u40008cesyg3he52mo","_id":"clfhsb9u7000jcesy6ldbfijt"}],"PostTag":[{"post_id":"cl9zdnpmt0005iysye7gu9qj1","tag_id":"cl9zdnpms0003iysyhzxa50qh","_id":"cl9zdnpmv0009iysy82vu5hcw"},{"post_id":"cl9zdnpml0000iysy0qlahgkn","tag_id":"cl9zdnpms0003iysyhzxa50qh","_id":"cl9zdnpmw000biysyfczseq0c"},{"post_id":"cl9zdnpmp0001iysy32vof5gp","tag_id":"cl9zdnpmu0008iysy9s4436pc","_id":"cl9zdnpmy000iiysybp5fefmj"},{"post_id":"cl9zdnpms0004iysycvwk73p6","tag_id":"cl9zdnpms0003iysyhzxa50qh","_id":"cl9zdnpn1000oiysyfut2ec0t"},{"post_id":"cl9zdnpn1000piysy0rn17ftq","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpn3000uiysyckhj1taz"},{"post_id":"cl9zdnpmu0006iysyho6jg8yw","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpn50011iysy4ia8b6hm"},{"post_id":"cl9zdnpmu0006iysyho6jg8yw","tag_id":"cl9zdnpn2000siysye476gct9","_id":"cl9zdnpn50015iysy6g37bhv0"},{"post_id":"cl9zdnpmv000aiysyft3b4yxi","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpn8001fiysy4snzbn4z"},{"post_id":"cl9zdnpmv000aiysyft3b4yxi","tag_id":"cl9zdnpn50016iysy6ms09siz","_id":"cl9zdnpn9001jiysydcrhhbok"},{"post_id":"cl9zdnpmw000ciysy3tlpgv08","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpng0027iysyb0k4fjqm"},{"post_id":"cl9zdnpmw000ciysy3tlpgv08","tag_id":"cl9zdnpn9001miysycdoa7yhj","_id":"cl9zdnpnh002biysyhf69an4i"},{"post_id":"cl9zdnpmw000ciysy3tlpgv08","tag_id":"cl9zdnpnb001siysy9g9lesur","_id":"cl9zdnpnh002eiysy10ba2qw0"},{"post_id":"cl9zdnpmw000ciysy3tlpgv08","tag_id":"cl9zdnpnc001yiysyddv9d1x8","_id":"cl9zdnpni002iiysyg1l8b73f"},{"post_id":"cl9zdnpmy000giysy64dcffye","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpnj002liysy9ro17jih"},{"post_id":"cl9zdnpmy000jiysyeskd11oe","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpnl002riysyaly20tq6"},{"post_id":"cl9zdnpmy000jiysyeskd11oe","tag_id":"cl9zdnpni002jiysydf65b4kh","_id":"cl9zdnpnl002uiysy2yrb3bn8"},{"post_id":"cl9zdnpn0000niysybk9e9cwc","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpnl002wiysy60zlex4y"},{"post_id":"cl9zdnpn0000niysybk9e9cwc","tag_id":"cl9zdnpnl002qiysyewxkaptm","_id":"cl9zdnpnm002ziysy6k2g1b4f"},{"post_id":"cl9zdnpn2000tiysyfx2lgah0","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpno0034iysy2s5jdyw4"},{"post_id":"cl9zdnpn2000tiysyfx2lgah0","tag_id":"cl9zdnpnl002viysyhdl0hd3n","_id":"cl9zdnpno0037iysy78tl75mv"},{"post_id":"cl9zdnpn2000tiysyfx2lgah0","tag_id":"cl9zdnpnm0030iysyhjozb02n","_id":"cl9zdnpno0039iysygfj42o87"},{"post_id":"cl9zdnpn3000viysy911w0yct","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpnp003diysyete8gh73"},{"post_id":"cl9zdnpn3000viysy911w0yct","tag_id":"cl9zdnpmu0008iysy9s4436pc","_id":"cl9zdnpnq003giysy8gm35lce"},{"post_id":"cl9zdnpn3000viysy911w0yct","tag_id":"cl9zdnpnm0033iysy6xp120x1","_id":"cl9zdnpnq003iiysyhls1gxy9"},{"post_id":"cl9zdnpn3000viysy911w0yct","tag_id":"cl9zdnpno0038iysy7lhcb6lu","_id":"cl9zdnpnr003liysygfkg0may"},{"post_id":"cl9zdnpn4000ziysy7c598fq0","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpnr003niysyftucbli7"},{"post_id":"cl9zdnpn4000ziysy7c598fq0","tag_id":"cl9zdnpnp003ciysygzst74pz","_id":"cl9zdnpnr003qiysy7d6890he"},{"post_id":"cl9zdnpn50012iysy1fg69klu","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpns003siysydhfa458c"},{"post_id":"cl9zdnpn50012iysy1fg69klu","tag_id":"cl9zdnpnq003hiysy816i8ud1","_id":"cl9zdnpns003uiysyakzsaj8t"},{"post_id":"cl9zdnpn60017iysy49pi4h8o","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpns003viysy31ka25ya"},{"post_id":"cl9zdnpn60017iysy49pi4h8o","tag_id":"cl9zdnpnr003miysy0x7m8npu","_id":"cl9zdnpns003yiysy8yodcrbe"},{"post_id":"cl9zdnpn60019iysy16es8yu7","tag_id":"cl9zdnpn0000miysy6wjx5ier","_id":"cl9zdnpnt0040iysy6z80cmb8"},{"post_id":"cl9zdnpn60019iysy16es8yu7","tag_id":"cl9zdnpnr003riysy1p424k66","_id":"cl9zdnpnt0041iysydyqh8qnc"},{"post_id":"cl9zdnpn60019iysy16es8yu7","tag_id":"cl9zdnpnq003hiysy816i8ud1","_id":"cl9zdnpnu0043iysyfybp0pfd"},{"post_id":"cl9zdnpn7001ciysy1cx5cazq","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"cl9zdnpnu0046iysycco6eedz"},{"post_id":"cl9zdnpn7001ciysy1cx5cazq","tag_id":"cl9zdnpnt0042iysyfuohe9wf","_id":"cl9zdnpnu0047iysyaa2u5arm"},{"post_id":"cl9zdnpn7001ciysy1cx5cazq","tag_id":"cl9zdnpnu0044iysyh3nechvu","_id":"cl9zdnpnv0049iysy1qco4x8x"},{"post_id":"cl9zdnpn9001kiysy9aps3lzr","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"cl9zdnpnv004ciysyg9kzaf69"},{"post_id":"cl9zdnpn9001kiysy9aps3lzr","tag_id":"cl9zdnpnu0048iysy32k98taa","_id":"cl9zdnpnv004diysy0zf68abh"},{"post_id":"cl9zdnpn9001kiysy9aps3lzr","tag_id":"cl9zdnpnv004aiysy2eh5e0k1","_id":"cl9zdnpnv004fiysygrdsabdf"},{"post_id":"cl9zdnpna001niysy1w3251mn","tag_id":"cl9zdnpnv004biysy5gfj1c3j","_id":"cl9zdnpnv004giysy6ro8gd2u"},{"post_id":"cl9zdnpna001piysy9w38ewz3","tag_id":"cl9zdnpnv004eiysy2m4w9bx2","_id":"cl9zdnpnw004jiysy1zcwfain"},{"post_id":"cl9zdnpna001piysy9w38ewz3","tag_id":"cl9zdnpnv004hiysy8jx58oli","_id":"cl9zdnpnw004kiysy2w4x3bgc"},{"post_id":"cl9zdnpnb001tiysycq0rgumb","tag_id":"cl9zdnpnw004iiysy1u82h61o","_id":"cl9zdnpnw004niysyc8h48sz8"},{"post_id":"cl9zdnpnb001tiysycq0rgumb","tag_id":"cl9zdnpnw004liysy6fe6byd8","_id":"cl9zdnpnw004oiysy4r0k7h91"},{"post_id":"cl9zdnpnc001viysygoi1gz0t","tag_id":"cl9zdnpnw004iiysy1u82h61o","_id":"cl9zdnpnx004riysy1kn9grkc"},{"post_id":"cl9zdnpnc001viysygoi1gz0t","tag_id":"cl9zdnpnw004piysyhsyo41gu","_id":"cl9zdnpnx004siysyeq8x12sk"},{"post_id":"cl9zdnpnd001ziysy5xnncfkp","tag_id":"cl9zdnpnx004qiysy0jne9way","_id":"cl9zdnpnx004viysycsc97clp"},{"post_id":"cl9zdnpnd001ziysy5xnncfkp","tag_id":"cl9zdnpnx004tiysya8nf6ojc","_id":"cl9zdnpnx004wiysy8qcyh0m0"},{"post_id":"cl9zdnpnd0021iysy6gep6h8o","tag_id":"cl9zdnpnl002qiysyewxkaptm","_id":"cl9zdnpny004ziysydljuf1hq"},{"post_id":"cl9zdnpnd0021iysy6gep6h8o","tag_id":"cl9zdnpnx004xiysyda8kfmkm","_id":"cl9zdnpny0050iysy6tlxfg75"},{"post_id":"cl9zdnpnf0025iysy3ba17agn","tag_id":"cl9zdnpnx004xiysyda8kfmkm","_id":"cl9zdnpny0053iysyazi97kw9"},{"post_id":"cl9zdnpnf0025iysy3ba17agn","tag_id":"cl9zdnpny0051iysya01bgd91","_id":"cl9zdnpny0054iysy4s568weh"},{"post_id":"cl9zdnpnh002diysygp6b5rm1","tag_id":"cl9zdnpni002jiysydf65b4kh","_id":"cl9zdnpnz0057iysy7j6s5bza"},{"post_id":"cl9zdnpnh002diysygp6b5rm1","tag_id":"cl9zdnpny0051iysya01bgd91","_id":"cl9zdnpnz0058iysyh7mb1pv8"},{"post_id":"cl9zdnpnh002fiysy5mrzcnpt","tag_id":"cl9zdnpnz0056iysy3nr1bxtq","_id":"cl9zdnpnz005biysy8b8jbnkh"},{"post_id":"cl9zdnpnh002fiysy5mrzcnpt","tag_id":"cl9zdnpnu0044iysyh3nechvu","_id":"cl9zdnpnz005ciysyc2g8263k"},{"post_id":"cl9zdnpnj002miysy5oso7r7t","tag_id":"cl9zdnpnz005aiysye26qd4l7","_id":"cl9zdnpo0005fiysy4gst2n5l"},{"post_id":"cl9zdnpnj002miysy5oso7r7t","tag_id":"cl9zdnpnz005diysy27qhbm5d","_id":"cl9zdnpo0005giysy5vcwc5dc"},{"post_id":"cl9zdnpnk002piysy1casf1jy","tag_id":"cl9zdnpnz005eiysyh0f26oyd","_id":"cl9zdnpo0005iiysy45byhxxs"},{"post_id":"cl9zdnpnk002piysy1casf1jy","tag_id":"cl9zdnpnz005diysy27qhbm5d","_id":"cl9zdnpo0005jiysybig2hpmt"},{"post_id":"cl9zdnpo1005kiysy5loufbpm","tag_id":"cl9zdnpnp003ciysygzst74pz","_id":"cl9zdnpo2005niysy72a6asw9"},{"post_id":"cl9zdnpo1005kiysy5loufbpm","tag_id":"cl9zdnpo2005miysy4vhr1vn7","_id":"cl9zdnpo2005oiysy56ccanjo"},{"post_id":"cl9zdnpn8001giysybetvfzey","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"cl9zdpmeb0002kwsy10fxerte"},{"post_id":"cl9zdnpn8001giysybetvfzey","tag_id":"cl9zdpme90000kwsyhjlja3tp","_id":"cl9zdpmeb0003kwsy4lmj1aw6"},{"post_id":"cla0hyr6z0000mksydy3z686i","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"cla0hyr730001mksy1o9k6ybz"},{"post_id":"cla10lblf000048sy07z887qk","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"cla10lblk000148syau4vfjka"},{"post_id":"cla1y95wr0000cvsycr4486fy","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"cla1y95x20003cvsy6wns2gt7"},{"post_id":"cla1y95wr0000cvsycr4486fy","tag_id":"cla1y95wu0001cvsyem364jvt","_id":"cla1y95x20004cvsy9r072ms8"},{"post_id":"clfhsb9tq0001cesycnoyai7o","tag_id":"clfhsb9u00003cesy6aticpy1","_id":"clfhsb9u50009cesybwk21omb"},{"post_id":"clfhsb9u10004cesyeloq930q","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"clfhsb9u7000fcesy9uamh82x"},{"post_id":"clfhsb9u10004cesyeloq930q","tag_id":"clfhsb9u30007cesy3gwi3mu9","_id":"clfhsb9u7000hcesybere40e6"},{"post_id":"clfhsb9u20005cesybz315qz8","tag_id":"clfhsb9u6000dcesy3qx9fasa","_id":"clfhsb9u7000kcesy2vkv4wgl"},{"post_id":"clfhsb9u30006cesy1cyk2vwh","tag_id":"clfhsb9u7000icesy8kqo75o7","_id":"clfhsb9u8000mcesy4q2ahkdd"},{"post_id":"clfhsb9u30006cesy1cyk2vwh","tag_id":"cla1y95wu0001cvsyem364jvt","_id":"clfhsb9u8000ncesyg4tv9oyt"},{"post_id":"clfhsb9u5000acesyhjt43a8y","tag_id":"cl9zdnpnt003ziysy9jlr8b8t","_id":"clfhsb9u8000ocesy5x8dbt4t"},{"post_id":"clfhsb9u5000acesyhjt43a8y","tag_id":"clfhsb9u7000lcesybn5m80ov","_id":"clfhsb9u8000pcesyelu44ayq"},{"post_id":"clfhsb9u5000acesyhjt43a8y","tag_id":"clfhsb9u00003cesy6aticpy1","_id":"clfhsb9u8000qcesyhbtx48ey"}],"Tag":[{"name":"git","_id":"cl9zdnpms0003iysyhzxa50qh"},{"name":"docker","_id":"cl9zdnpmu0008iysy9s4436pc"},{"name":"golang","_id":"cl9zdnpn0000miysy6wjx5ier"},{"name":"gin","_id":"cl9zdnpn2000siysye476gct9"},{"name":"future","_id":"cl9zdnpn50016iysy6ms09siz"},{"name":"go-micro","_id":"cl9zdnpn9001miysycdoa7yhj"},{"name":"微服务","_id":"cl9zdnpnb001siysy9g9lesur"},{"name":"注意事项","_id":"cl9zdnpnc001yiysyddv9d1x8"},{"name":"http","_id":"cl9zdnpni002jiysydf65b4kh"},{"name":"regex","_id":"cl9zdnpnl002qiysyewxkaptm"},{"name":"ants","_id":"cl9zdnpnl002viysyhdl0hd3n"},{"name":"goroutine","_id":"cl9zdnpnm0030iysyhjozb02n"},{"name":"docker-compose","_id":"cl9zdnpnm0033iysy6xp120x1"},{"name":"helloworld","_id":"cl9zdnpno0038iysy7lhcb6lu"},{"name":"算法","_id":"cl9zdnpnp003ciysygzst74pz"},{"name":"concurrent","_id":"cl9zdnpnq003hiysy816i8ud1"},{"name":"gorm","_id":"cl9zdnpnr003miysy0x7m8npu"},{"name":"sync.Pool","_id":"cl9zdnpnr003riysy1p424k66"},{"name":"java","_id":"cl9zdnpnt003ziysy9jlr8b8t"},{"name":"mapstruct","_id":"cl9zdnpnt0042iysyfuohe9wf"},{"name":"plugins","_id":"cl9zdnpnu0044iysyh3nechvu"},{"name":"springboot","_id":"cl9zdnpnu0048iysy32k98taa"},{"name":"事务","_id":"cl9zdnpnv004aiysy2eh5e0k1"},{"name":"mac","_id":"cl9zdnpnv004biysy5gfj1c3j"},{"name":"sql","_id":"cl9zdnpnv004eiysy2m4w9bx2"},{"name":"性能","_id":"cl9zdnpnv004hiysy8jx58oli"},{"name":"mysql","_id":"cl9zdnpnw004iiysy1u82h61o"},{"name":"function","_id":"cl9zdnpnw004liysy6fe6byd8"},{"name":"慢日志","_id":"cl9zdnpnw004piysyhsyo41gu"},{"name":"ssh","_id":"cl9zdnpnx004qiysy0jne9way"},{"name":"linux","_id":"cl9zdnpnx004tiysya8nf6ojc"},{"name":"python","_id":"cl9zdnpnx004xiysyda8kfmkm"},{"name":"web","_id":"cl9zdnpny0051iysya01bgd91"},{"name":"jetbrains","_id":"cl9zdnpnz0056iysy3nr1bxtq"},{"name":"markdown","_id":"cl9zdnpnz005aiysye26qd4l7"},{"name":"api","_id":"cl9zdnpnz005diysy27qhbm5d"},{"name":"site","_id":"cl9zdnpnz005eiysyh0f26oyd"},{"name":"timewheels","_id":"cl9zdnpo2005miysy4vhr1vn7"},{"name":"maven","_id":"cl9zdpme90000kwsyhjlja3tp"},{"name":"知识图谱","_id":"cla1y95wu0001cvsyem364jvt"},{"name":"consul","_id":"clfhsb9u00003cesy6aticpy1"},{"name":"caffeine","_id":"clfhsb9u30007cesy3gwi3mu9"},{"name":"powerjob","_id":"clfhsb9u6000dcesy3qx9fasa"},{"name":"redis","_id":"clfhsb9u7000icesy8kqo75o7"},{"name":"spring boot","_id":"clfhsb9u7000lcesybn5m80ov"}]}}