diff --git a/css/main.css b/css/main.css index 7b73cf2d7b..e07e1b10ba 100644 --- a/css/main.css +++ b/css/main.css @@ -1,3 +1,3 @@ -/* build time:Mon Jun 19 2023 18:25:04 GMT+0800 (China Standard Time)*/ -html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}::selection{background:#262a30;color:#fff}body{position:relative;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif;font-size:14px;line-height:2;color:#555;background:#fff}@media (max-width:767px){body{padding-right:0!important}}@media (min-width:768px) and (max-width:991px){body{padding-right:0!important}}@media (min-width:1600px){body{font-size:16px}}h1,h2,h3,h4,h5,h6{margin:0;padding:0;font-weight:700;line-height:1.5;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif}h2,h3,h4,h5,h6{margin:20px 0 15px}h1{font-size:24px}@media (max-width:767px){h1{font-size:20px}}h2{font-size:22px}@media (max-width:767px){h2{font-size:18px}}h3{font-size:20px}@media (max-width:767px){h3{font-size:16px}}h4{font-size:18px}@media (max-width:767px){h4{font-size:14px}}h5{font-size:16px}@media (max-width:767px){h5{font-size:12px}}h6{font-size:14px}@media (max-width:767px){h6{font-size:10px}}p{margin:0 0 25px 0}a{color:#555;text-decoration:none;border-bottom:1px solid #999;word-wrap:break-word}a:hover{color:#222;border-bottom-color:#222}ul{list-style:none}blockquote{margin:0;padding:0}img{display:block;margin:auto;max-width:100%;height:auto}hr{margin:40px 0;height:3px;border:none;background-color:#ddd;background-image:repeating-linear-gradient(-45deg,#fff,#fff 4px,transparent 4px,transparent 8px)}blockquote{padding:0 15px;color:#666;border-left:4px solid #ddd}blockquote cite::before{content:"-";padding:0 5px}dt{font-weight:700}dd{margin:0;padding:0}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.center-block{display:block;margin-left:auto;margin-right:auto}.clearfix:after,.clearfix:before{content:" ";display:table}.clearfix:after{clear:both}.pullquote{width:45%}.pullquote.left{float:left;margin-left:5px;margin-right:10px}.pullquote.right{float:right;margin-left:10px;margin-right:5px}.affix.affix.affix{position:fixed}.translation{margin-top:-20px;font-size:14px;color:#999}.scrollbar-measure{width:100px;height:100px;overflow:scroll;position:absolute;top:-9999px}.use-motion .motion-element{opacity:0}#local-search-input{padding:3px;border:none;text-indent:14px;border-radius:0;width:140px;outline:0;border-bottom:1px solid #999;background:inherit;opacity:.5}#local-search-input:focus{opacity:1}.search-icon{position:absolute;top:9px}table{margin:20px 0;width:100%;border-collapse:collapse;border-spacing:0;border:1px solid #ddd;font-size:14px;table-layout:fixed;word-wrap:break-all}table>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}table>tbody>tr:hover{background-color:#f5f5f5}caption,td,th{padding:8px;text-align:left;vertical-align:middle;font-weight:400}td,th{border-bottom:3px solid #ddd;border-right:1px solid #eee}th{padding-bottom:10px;font-weight:700}td{border-bottom-width:1px}body,html{height:100%}.container{position:relative;min-height:100%}.header-inner{margin:0 auto;padding:100px 0 70px;width:700px}@media (min-width:1600px){.container .header-inner{width:900px}}.main{padding-bottom:150px}.main-inner{margin:0 auto;width:700px}@media (min-width:1600px){.container .main-inner{width:900px}}.footer{position:absolute;left:0;bottom:0;width:100%;min-height:50px}.footer-inner{box-sizing:border-box;margin:20px auto;width:700px}@media (min-width:1600px){.container .footer-inner{width:900px}}.highlight,pre{overflow:auto;margin:20px 0;padding:0;font-size:13px;color:#c5c8c6;background:#1d1f21;line-height:1.6}code,pre{font-family:consolas,Menlo,"PingFang SC","Microsoft YaHei",monospace}code{padding:2px 4px;word-wrap:break-word;color:#c5c8c6;background:#1d1f21;border-radius:3px;font-size:13px}pre code{padding:0;color:#c5c8c6;background:0 0;text-shadow:none}.highlight{border-radius:1px}.highlight pre{border:none;margin:0;padding:10px 0}.highlight table{margin:0;width:auto;border:none}.highlight td{border:none;padding:0}.highlight figcaption{font-size:1em;color:#c5c8c6;line-height:1em;margin-bottom:1em}.highlight figcaption:after,.highlight figcaption:before{content:" ";display:table}.highlight figcaption:after{clear:both}.highlight figcaption a{float:right;color:#c5c8c6}.highlight figcaption a:hover{border-bottom-color:#c5c8c6}.highlight .gutter pre{padding-left:10px;padding-right:10px;color:#888f96;text-align:right;background-color:#000}.highlight .code pre{padding-left:10px;padding-right:10px;background-color:#1d1f21}.highlight .line{height:20px}.gutter{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.gist table{width:auto}.gist table td{border:none}pre .deletion{background:green}pre .addition{background:maroon}pre .meta{color:#b294bb}pre .comment{color:#969896}pre .attribute,pre .css .class,pre .css .id,pre .css .pseudo,pre .html .doctype,pre .regexp,pre .ruby .constant,pre .tag,pre .variable,pre .xml .doctype,pre .xml .pi,pre .xml .tag .title{color:#c66}pre .built_in,pre .command,pre .constant,pre .literal,pre .number,pre .params,pre .preprocessor{color:#de935f}pre .css .rules .attribute,pre .formula,pre .header,pre .inheritance,pre .number,pre .ruby .class .title,pre .ruby .symbol,pre .special,pre .string,pre .value,pre .xml .cdata{color:#b5bd68}pre .css .hexcolor,pre .title{color:#8abeb7}pre .coffeescript .title,pre .function,pre .javascript .title,pre .perl .sub,pre .python .decorator,pre .python .title,pre .ruby .function .title,pre .ruby .title .keyword{color:#81a2be}pre .javascript .function,pre .keyword{color:#b294bb}.full-image.full-image.full-image{border:none;max-width:100%;width:auto;margin:20px auto}@media (min-width:992px){.full-image.full-image.full-image{max-width:none;width:110%;margin:25px -5%}}.blockquote-center,.page-home .post-type-quote blockquote,.page-post-detail .post-type-quote blockquote{position:relative;margin:40px 0;padding:0;border-left:none;text-align:center}.blockquote-center::after,.blockquote-center::before,.page-home .post-type-quote blockquote::after,.page-home .post-type-quote blockquote::before,.page-post-detail .post-type-quote blockquote::after,.page-post-detail .post-type-quote blockquote::before{position:absolute;content:' ';display:block;width:100%;height:24px;opacity:.2;background-repeat:no-repeat;background-position:0 -6px;background-size:22px 22px}.blockquote-center::before,.page-home .post-type-quote blockquote::before,.page-post-detail .post-type-quote blockquote::before{top:-20px;background-image:url(../images/quote-l.svg);border-top:1px solid #ccc}.blockquote-center::after,.page-home .post-type-quote blockquote::after,.page-post-detail .post-type-quote blockquote::after{bottom:-20px;background-image:url(../images/quote-r.svg);border-bottom:1px solid #ccc;background-position:100% 8px}.blockquote-center div,.blockquote-center p,.page-home .post-type-quote blockquote div,.page-home .post-type-quote blockquote p,.page-post-detail .post-type-quote blockquote div,.page-post-detail .post-type-quote blockquote p{text-align:center}.post .post-body .group-picture img{box-sizing:border-box;padding:0 3px;border:none}.post .group-picture-row{overflow:hidden;margin-top:6px}.post .group-picture-row:first-child{margin-top:0}.post .group-picture-column{float:left}.page-post-detail .post-body .group-picture-column{float:none;margin-top:10px;width:auto!important}.page-post-detail .post-body .group-picture-column img{margin:0 auto}.page-archive .group-picture-container{overflow:hidden}.page-archive .group-picture-row{float:left}.page-archive .group-picture-row:first-child{margin-top:6px}.page-archive .group-picture-column{max-width:150px;max-height:150px}.note{padding:20px;margin:20px 0;border:1px solid #eee;border-left-width:5px;border-radius:3px}.note h2,.note h3,.note h4,.note h5,.note h6{margin-top:0;margin-bottom:5px}.note p:last-child{margin-bottom:0}.note code{border-radius:3px}.note+.note{margin-top:-5px}.default{border-left-color:#777}.default h2,.default h3,.default h4,.default h5,.default h6{color:#777}.primary{border-left-color:#428bca}.primary h2,.primary h3,.primary h4,.primary h5,.primary h6{color:#428bca}.success{border-left-color:#5cb85c}.success h2,.success h3,.success h4,.success h5,.success h6{color:#5cb85c}.danger{border-left-color:#d9534f}.danger h2,.danger h3,.danger h4,.danger h5,.danger h6{color:#d9534f}.warning{border-left-color:#f0ad4e}.warning h2,.warning h3,.warning h4,.warning h5,.warning h6{color:#f0ad4e}.info{border-left-color:#5bc0de}.info h2,.info h3,.info h4,.info h5,.info h6{color:#5bc0de}.btn{display:inline-block;padding:0 20px;font-size:14px;color:#fff;background:#222;border:2px solid #222;text-decoration:none;border-radius:0;transition-property:background-color;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.btn:hover,.post-button .btn:hover{border-color:#222;color:#222;background:#fff}.btn-bar{display:block;width:22px;height:2px;background:#555;border-radius:1px}.btn-bar+.btn-bar{margin-top:4px}.pagination{margin:120px 0 40px;text-align:center;border-top:1px solid #eee}.page-number-basic,.pagination .next,.pagination .page-number,.pagination .prev,.pagination .space{display:inline-block;position:relative;top:-1px;margin:0 10px;padding:0 10px;line-height:30px}@media (max-width:767px){.page-number-basic,.pagination .next,.pagination .page-number,.pagination .prev,.pagination .space{margin:0 5px}}.pagination .next,.pagination .page-number,.pagination .prev{border-bottom:0;border-top:1px solid #eee;transition-property:border-color;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.pagination .next:hover,.pagination .page-number:hover,.pagination .prev:hover{border-top-color:#222}.pagination .space{padding:0;margin:0}.pagination .prev{margin-left:0}.pagination .next{margin-right:0}.pagination .page-number.current{color:#fff;background:#ccc;border-top-color:#ccc}@media (max-width:767px){.pagination{border-top:none}.pagination .next,.pagination .page-number,.pagination .prev{margin-bottom:10px;border-top:0;border-bottom:1px solid #eee}.pagination .next:hover,.pagination .page-number:hover,.pagination .prev:hover{border-bottom-color:#222}}.comments{margin:60px 20px 0}.tag-cloud{text-align:center}.tag-cloud a{display:inline-block;margin:10px}.back-to-top{box-sizing:border-box;position:fixed;bottom:-100px;right:50px;z-index:1050;padding:0 6px;width:25px;background:#222;font-size:12px;opacity:1;color:#fff;cursor:pointer;text-align:center;-webkit-transform:translateZ(0);transition-property:bottom;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}@media (max-width:767px){.back-to-top{display:none}}@media (min-width:768px) and (max-width:991px){.back-to-top{display:none}}.back-to-top.back-to-top-on{bottom:19px}.header{background:#fff}.header-inner{position:relative}.headband{height:3px;background:#222}.site-meta{margin:0;text-align:left}@media (max-width:767px){.site-meta{text-align:center}}.brand{position:relative;display:inline-block;padding:0 40px;color:#222;background:#222;border-bottom:none}.brand:hover{color:#222}.logo{display:inline-block;margin-right:5px;line-height:36px;vertical-align:top}.site-title{display:inline-block;vertical-align:top;line-height:36px;font-size:20px;font-weight:400;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif}.site-subtitle{margin-top:10px;font-size:13px;color:#999}.use-motion .brand{opacity:0}.use-motion .logo,.use-motion .site-subtitle,.use-motion .site-title{opacity:0;position:relative;top:-10px}.site-nav-toggle{display:none;position:absolute;top:10px;left:10px}@media (max-width:767px){.site-nav-toggle{display:block}}.site-nav-toggle button{margin-top:2px;padding:9px 10px;background:0 0;border:none}@media (max-width:767px){.site-nav{display:none;margin:0 -10px;padding:0 10px;clear:both;border-top:1px solid #ddd}}@media (min-width:768px) and (max-width:991px){.site-nav{display:block!important}}@media (min-width:992px){.site-nav{display:block!important}}.menu{margin-top:20px;padding-left:0;text-align:center}.menu .menu-item{display:inline-block;margin:0 10px}@media screen and (max-width:767px){.menu .menu-item{margin-top:10px}}.menu .menu-item a{display:block;font-size:13px;text-transform:capitalize;line-height:inherit;border-bottom:1px solid transparent;transition-property:border-color;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.menu .menu-item a:hover{border-bottom-color:#222}.menu .menu-item .fa{margin-right:5px}.use-motion .menu-item{opacity:0}.post-body{font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif}@media (max-width:767px){.post-body{word-break:break-word}}.post-body .fancybox img{display:block!important;margin:0 auto;cursor:pointer;cursor:zoom-in;cursor:-webkit-zoom-in}.post-body .figure .caption,.post-body .image-caption{margin:10px auto 15px;text-align:center;font-size:14px;color:#999;font-weight:700;line-height:1}.post-sticky-flag{display:inline-block;font-size:16px;-ms-transform:rotate(30deg);-webkit-transform:rotate(30deg);-moz-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}.posts-expand{padding-top:40px}@media (max-width:767px){.posts-expand{margin:0 20px}.post-body .highlight,.post-body pre{padding:10px}.post-body .highlight .gutter pre,.post-body pre .gutter pre{padding-right:10px}}@media (min-width:992px){.posts-expand .post-body{text-align:justify}}.posts-expand .post-body h2,.posts-expand .post-body h3,.posts-expand .post-body h4,.posts-expand .post-body h5,.posts-expand .post-body h6{padding-top:10px}.posts-expand .post-body h2 .header-anchor,.posts-expand .post-body h3 .header-anchor,.posts-expand .post-body h4 .header-anchor,.posts-expand .post-body h5 .header-anchor,.posts-expand .post-body h6 .header-anchor{float:right;margin-left:10px;color:#ccc;border-bottom-style:none;visibility:hidden}.posts-expand .post-body h2 .header-anchor:hover,.posts-expand .post-body h3 .header-anchor:hover,.posts-expand .post-body h4 .header-anchor:hover,.posts-expand .post-body h5 .header-anchor:hover,.posts-expand .post-body h6 .header-anchor:hover{color:inherit}.posts-expand .post-body h2:hover .header-anchor,.posts-expand .post-body h3:hover .header-anchor,.posts-expand .post-body h4:hover .header-anchor,.posts-expand .post-body h5:hover .header-anchor,.posts-expand .post-body h6:hover .header-anchor{visibility:visible}.posts-expand .post-body ul li{list-style:circle}.posts-expand .post-body img{box-sizing:border-box;margin:auto;padding:3px;border:1px solid #ddd}.posts-expand .fancybox img{margin:0 auto}@media (max-width:767px){.posts-collapse{margin:0 20px}.posts-collapse .post-meta,.posts-collapse .post-title{display:block;width:auto;text-align:left}}.posts-collapse{position:relative;z-index:1010;margin-left:0}.posts-collapse::after{content:" ";position:absolute;top:20px;left:0;margin-left:-2px;width:4px;height:100%;background:#f5f5f5;z-index:-1}@media (max-width:767px){.posts-collapse{margin:0 20px}}.posts-collapse .collection-title{position:relative;margin:60px 0}.posts-collapse .collection-title h2{margin-left:20px}.posts-collapse .collection-title small{color:#bbb}.posts-collapse .collection-title::before{content:" ";position:absolute;left:0;top:50%;margin-left:-4px;margin-top:-4px;width:8px;height:8px;background:#bbb;border-radius:50%}.posts-collapse .post{margin:30px 0}.posts-collapse .post-header{position:relative;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s;transition-property:border;border-bottom:1px dashed #ccc}.posts-collapse .post-header::before{content:" ";position:absolute;left:0;top:12px;width:6px;height:6px;margin-left:-4px;background:#bbb;border-radius:50%;border:1px solid #fff;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s;transition-property:background}.posts-collapse .post-header:hover{border-bottom-color:#666}.posts-collapse .post-header:hover::before{background:#222}.posts-collapse .post-meta{position:absolute;font-size:12px;left:20px;top:5px}.posts-collapse .post-comments-count{display:none}.posts-collapse .post-title{margin-left:60px;font-size:16px;font-weight:400;line-height:inherit}.posts-collapse .post-title::after{margin-left:3px;opacity:.6}.posts-collapse .post-title a{color:#666;border-bottom:none}.page-home .post-type-quote .post-header,.page-home .post-type-quote .post-tags,.page-post-detail .post-type-quote .post-header,.page-post-detail .post-type-quote .post-tags{display:none}.posts-expand .post-title{font-size:26px;text-align:center;word-break:break-word;font-weight:400}@media (max-width:767px){.posts-expand .post-title{font-size:22px}}.posts-expand .post-title-link{display:inline-block;position:relative;color:#555;border-bottom:none;line-height:1.2;vertical-align:top}.posts-expand .post-title-link::before{content:"";position:absolute;width:100%;height:2px;bottom:0;left:0;background-color:#000;visibility:hidden;-webkit-transform:scaleX(0);-moz-transform:scaleX(0);-ms-transform:scaleX(0);-o-transform:scaleX(0);transform:scaleX(0);transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.posts-expand .post-title-link:hover::before{visibility:visible;-webkit-transform:scaleX(1);-moz-transform:scaleX(1);-ms-transform:scaleX(1);-o-transform:scaleX(1);transform:scaleX(1)}.posts-expand .post-title-link .fa{font-size:16px}.posts-expand .post-meta{margin:3px 0 60px 0;color:#999;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif;font-size:12px;text-align:center}.posts-expand .post-meta .post-category-list{display:inline-block;margin:0;padding:3px}.posts-expand .post-meta .post-category-list-link{color:#999}.posts-expand .post-meta .post-description{font-size:14px;margin-top:2px}.post-meta-divider{margin:0 .5em}.post-meta-item-icon{margin-right:3px}@media (min-width:768px) and (max-width:991px){.post-meta-item-icon{display:inline-block}}@media (max-width:767px){.post-meta-item-icon{display:inline-block}}@media (min-width:768px) and (max-width:991px){.post-meta-item-text{display:none}}@media (max-width:767px){.post-meta-item-text{display:none}}@media (max-width:767px){.posts-expand .post-comments-count{display:none}}.social-like{font-size:14px;height:20px;text-align:center;border-top:1px solid #eee;padding-top:9px;margin-top:45px;display:flex;justify-content:center}.vk_like{width:85px;height:21px;padding-top:7px;align-self:center}.fb_like{height:30px;align-self:center}.post-button{margin-top:50px}.post-button .btn{color:#555;font-size:14px;background:0 0;border-radius:0;line-height:2;margin:0 4px 8px 4px}.post-button .fa-fw{width:1.285714285714286em;text-align:left}.posts-expand .post-tags{margin-top:40px;text-align:center}.posts-expand .post-tags a{display:inline-block;margin-right:10px;font-size:13px}.post-nav{display:table;margin-top:15px;width:100%;border-top:1px solid #eee}.post-nav-divider{display:table-cell;width:10%}.post-nav-item{display:table-cell;padding:10px 0 0 0;width:45%;vertical-align:top}.post-nav-item a{position:relative;display:block;line-height:25px;font-size:14px;color:#555;border-bottom:none}.post-nav-item a:hover{color:#222;border-bottom:none}.post-nav-item a:active{top:2px}.post-nav-item .fa{position:absolute;top:8px;left:0;font-size:12px}.post-nav-next a{padding-left:15px}.post-nav-prev{text-align:right}.post-nav-prev a{padding-right:15px}.post-nav-prev .fa{right:0;left:auto}.posts-expand .post-eof{display:block;margin:80px auto 60px;width:8%;height:1px;background:#ccc;text-align:center}.post:last-child .post-eof.post-eof.post-eof{display:none}.post-gallery{display:table;table-layout:fixed;width:100%;border-collapse:separate}.post-gallery-row{display:table-row}.post-gallery .post-gallery-img{display:table-cell;text-align:center;vertical-align:middle;border:none}.post-gallery .post-gallery-img img{max-width:100%;max-height:100%;border:none}.fancybox-close,.fancybox-close:hover{border:none}.sidebar{position:fixed;right:0;top:0;bottom:0;width:0;z-index:1040;box-shadow:inset 0 2px 6px #000;background:#222;-webkit-transform:translateZ(0)}.sidebar a{color:#999;border-bottom-color:#555}.sidebar a:hover{color:#eee}@media (min-width:768px) and (max-width:991px){.sidebar{display:none!important}}@media (max-width:767px){.sidebar{display:none!important}}.sidebar-inner{position:relative;padding:20px 10px;color:#999;text-align:center}.sidebar-toggle{position:fixed;right:50px;bottom:45px;width:15px;height:15px;padding:5px;background:#222;line-height:0;z-index:1050;cursor:pointer;-webkit-transform:translateZ(0)}@media (min-width:768px) and (max-width:991px){.sidebar-toggle{display:none}}@media (max-width:767px){.sidebar-toggle{display:none}}.sidebar-toggle-line{position:relative;display:inline-block;vertical-align:top;height:2px;width:100%;background:#fff;margin-top:3px}.sidebar-toggle-line:first-child{margin-top:0}.site-author-image{display:block;margin:0 auto;padding:2px;max-width:96px;height:auto;border:2px solid #333}.site-author-name{margin:5px 0 0;text-align:center;color:#f5f5f5;font-weight:400}.site-description{margin-top:5px;text-align:center;font-size:14px;color:#999}.site-state{overflow:hidden;line-height:1.4;white-space:nowrap;text-align:center}.site-state-item{display:inline-block;padding:0 15px;border-left:1px solid #333}.site-state-item:first-child{border-left:none}.site-state-item a{border-bottom:none}.site-state-item-count{display:block;text-align:center;color:inherit;font-weight:600;font-size:18px}.site-state-item-name{font-size:13px;color:inherit}.feed-link{margin-top:20px}.feed-link a{display:inline-block;padding:0 15px;color:#fc6423;border:1px solid #fc6423;border-radius:4px}.feed-link a i{color:#fc6423;font-size:14px}.feed-link a:hover{color:#fff;background:#fc6423}.feed-link a:hover i{color:#fff}.links-of-author{margin-top:20px}.links-of-author a{display:inline-block;vertical-align:middle;margin-right:10px;margin-bottom:10px;border-bottom-color:#555;font-size:13px}.links-of-author a:before{display:inline-block;vertical-align:middle;margin-right:3px;content:" ";width:4px;height:4px;border-radius:50%;background:#53c778}.links-of-blogroll{font-size:13px}.links-of-blogroll-title{margin-top:20px;font-size:14px;font-weight:600}.links-of-blogroll-list{margin:0;padding:0}.links-of-blogroll-item{padding:2px 10px}.sidebar-nav{margin:0 0 20px;padding-left:0}.sidebar-nav li{display:inline-block;cursor:pointer;border-bottom:1px solid transparent;font-size:14px;color:#555}.sidebar-nav li:hover{color:#f5f5f5}.page-post-detail .sidebar-nav-toc{padding:0 5px}.page-post-detail .sidebar-nav-overview{margin-left:10px}.sidebar-nav .sidebar-nav-active{color:#87daff;border-bottom-color:#87daff}.sidebar-nav .sidebar-nav-active:hover{color:#87daff}.sidebar-panel{display:none}.sidebar-panel-active{display:block}.post-toc-empty{font-size:14px;color:#666}.post-toc-wrap{overflow:hidden}.post-toc{overflow:auto}.post-toc ol{margin:0;padding:0 2px 5px 10px;text-align:left;list-style:none;font-size:14px}.post-toc ol>ol{padding-left:0}.post-toc ol a{transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s;transition-property:all;color:#999;border-bottom-color:#555}.post-toc ol a:hover{color:#ccc;border-bottom-color:#ccc}.post-toc .nav-item{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.8}.post-toc .nav .nav-child{display:none}.post-toc .nav .active>.nav-child{display:block}.post-toc .nav .active-current>.nav-child{display:block}.post-toc .nav .active-current>.nav-child>.nav-item{display:block}.post-toc .nav .active>a{color:#87daff;border-bottom-color:#87daff}.post-toc .nav .active-current>a{color:#87daff}.post-toc .nav .active-current>a:hover{color:#87daff}.footer{font-size:14px;color:#999}.footer img{border:none}.footer-inner{text-align:center}.with-love{display:inline-block;margin:0 5px}.powered-by,.theme-info{display:inline-block}.powered-by{margin-right:10px}.powered-by::after{content:"|";padding-left:10px}.cc-license{margin-top:10px;text-align:center}.cc-license .cc-opacity{opacity:.7;border-bottom:none}.cc-license .cc-opacity:hover{opacity:.9}.cc-license img{display:inline-block}.cloud-tie-wrapper img{display:inline-block}.cloud-tie-wrapper .total-txt{font-size:1em!important}.cloud-tie-join-count .join-count{color:#555!important;font-size:inherit!important;margin:0!important}.post-spread{margin-top:20px;text-align:center}.jiathis_style{display:inline-block}.jiathis_style a{border:none}.post-spread{margin-top:20px;text-align:center}.bdshare-slide-button-box a{border:none}.bdsharebuttonbox{display:inline-block}.bdsharebuttonbox a{border:none}ul.search-result-list{padding-left:0;margin:0 5px 0 8px}p.search-result{border-bottom:1px dashed #ccc;padding:5px 0}a.search-result-title{font-weight:700}a.search-result{border-bottom:transparent;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-keyword{border-bottom:1px dashed red;font-size:14px;font-weight:700;color:red}#local-search-result{height:88%;overflow:auto}.popup{display:none;position:fixed;top:10%;left:50%;width:700px;height:80%;margin-left:-350px;padding:3px 0 0 10px;background:#fff;color:#333;z-index:9999;border-radius:5px}@media (max-width:767px){.popup{padding:3px;top:0;left:0;margin:0;width:100%;height:100%;border-radius:0}}.popoverlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:2080;background-color:rgba(0,0,0,.3)}#local-search-input{margin-bottom:10px;padding:10px;width:97%;font-size:18px}.popup .fa-search{padding-top:8px}.popup-btn-close{position:absolute;top:6px;right:14px;color:#4ebd79;font-size:14px;font-weight:700;text-transform:uppercase;cursor:pointer}#no-result{position:absolute;left:44%;top:42%;color:#ccc}.use-motion .post{opacity:0}.page-archive .archive-page-counter{position:relative;top:3px;left:20px}@media (max-width:767px){.page-archive .archive-page-counter{top:5px}}.page-archive .posts-collapse .archive-move-on{position:absolute;top:11px;left:0;margin-left:-6px;width:10px;height:10px;opacity:.5;background:#555;border:1px solid #fff;border-radius:50%}.category-all-page .category-all-title{text-align:center}.category-all-page .category-all{margin-top:20px}.category-all-page .category-list{margin:0;padding:0;list-style:none}.category-all-page .category-list-item{margin:5px 10px}.category-all-page .category-list-count{color:#bbb}.category-all-page .category-list-count:before{display:inline;content:" ("}.category-all-page .category-list-count:after{display:inline;content:") "}.category-all-page .category-list-child{padding-left:10px}#schedule ul#event-list{padding-left:30px}#schedule ul#event-list hr{margin:20px 0 45px 0!important;background:#222}#schedule ul#event-list hr:after{display:inline-block;content:'NOW';background:#222;color:#fff;font-weight:700;text-align:right;padding:0 5px}#schedule ul#event-list li.event{margin:20px 0;background:#f9f9f9;padding-left:10px;min-height:40px}#schedule ul#event-list li.event h2.event-summary{margin:0;padding-bottom:3px}#schedule ul#event-list li.event h2.event-summary:before{display:inline-block;font-family:FontAwesome;font-size:8px;content:'\f111';vertical-align:middle;margin-right:25px;color:#bbb}#schedule ul#event-list li.event span.event-relative-time{display:inline-block;font-size:12px;font-weight:400;padding-left:12px;color:#bbb}#schedule ul#event-list li.event span.event-details{display:block;color:#bbb;margin-left:56px;padding-top:3px;padding-bottom:6px;text-indent:-24px;line-height:18px}#schedule ul#event-list li.event span.event-details:before{text-indent:0;display:inline-block;width:14px;font-family:FontAwesome;text-align:center;margin-right:9px;color:#bbb}#schedule ul#event-list li.event span.event-details.event-location:before{content:'\f041'}#schedule ul#event-list li.event span.event-details.event-duration:before{content:'\f017'}#schedule ul#event-list li.event-past{background:#fcfcfc}#schedule ul#event-list li.event-past>*{opacity:.6}#schedule ul#event-list li.event-past h2.event-summary{color:#bbb}#schedule ul#event-list li.event-past h2.event-summary:before{color:#dfdfdf}#schedule ul#event-list li.event-now{background:#222;color:#fff;padding:15px 0 15px 10px}#schedule ul#event-list li.event-now h2.event-summary:before{-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-ms-transform:scale(1.2);-o-transform:scale(1.2);transform:scale(1.2);color:#fff;animation:dot-flash 1s alternate infinite ease-in-out}#schedule ul#event-list li.event-now *{color:#fff!important}@-moz-keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-webkit-keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-o-keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}.page-post-detail .sidebar-toggle-line{background:#87daff}.page-post-detail .comments{overflow:hidden}h1,h2,h3,h4,h5,h6{margin:20px 0 10px}p{margin:0 0 25px 0}a{border-bottom-color:#ccc}hr{margin:20px 0;height:2px}.main-inner{margin-top:80px}.header{background:#f5f5f5}.header-inner{padding:25px 0 20px}.header-inner:after,.header-inner:before{content:" ";display:table}.header-inner:after{clear:both}@media (max-width:767px){.header-inner{width:auto;margin-bottom:50px;padding:10px}}.site-meta{float:left;margin-left:-20px;line-height:normal}@media (max-width:767px){.site-meta{margin-left:10px}}.site-meta .brand{padding:2px 1px;background:0 0}@media (max-width:767px){.site-meta .brand{display:block}}.site-meta .logo{display:none}.site-meta .site-title{font-size:22px;font-weight:bolder}@media (max-width:767px){.site-meta .site-title{line-height:34px}}.logo-line-after,.logo-line-before{display:block;overflow:hidden;margin:0 auto;width:75%}@media (max-width:767px){.logo-line-after,.logo-line-before{display:none}}.logo-line-after i,.logo-line-before i{position:relative;display:block;height:2px;background:#222}@media (max-width:767px){.logo-line-after i,.logo-line-before i{height:3px}}.use-motion .logo-line-before i{left:-100%}.use-motion .logo-line-after i{right:-100%}.site-subtitle{display:none}.site-nav-toggle{position:static;float:right}.menu{float:right;margin:8px 0 0 0}@media (max-width:767px){.menu{margin:20px 0 0 0;padding:0}}.menu br{display:none}.menu .menu-item{margin:0}@media (max-width:767px){.menu .menu-item{display:block}}.menu .menu-item a{padding:0 10px;background:0 0;border:none;border-radius:2px;transition-property:background}@media (max-width:767px){.menu .menu-item a{text-align:left}}.menu .menu-item a:hover{background:#e1e1e1}.menu a::before{display:none}@media (max-width:767px){.menu a::before{display:block}}@media (max-width:767px){.menu{float:none}}.site-search form{display:none}.posts-expand{padding-top:0}.posts-expand .post-meta,.posts-expand .post-title{text-align:left}@media (max-width:767px){.posts-expand .post-meta,.posts-expand .post-title{text-align:center}}.posts-expand .post-eof{display:none}.posts-expand .post{margin-top:120px}.posts-expand .post:first-child{margin-top:0}.posts-expand .post-meta{margin-top:5px;margin-bottom:20px}.posts-expand .post-title{position:relative;font-size:26px;font-weight:400}@media (max-width:767px){.posts-expand .post-title{font-size:20px}}@media (min-width:1600px){.posts-expand .post-title{font-size:26px}}.posts-expand .post-title:hover:before{background:#222}.posts-expand .post-body img{margin:0}.posts-expand .post-tags{text-align:left}.posts-expand .post-tags a{padding:1px 5px;background:#f5f5f5;border-bottom:none}.posts-expand .post-tags a:hover{background:#ccc}.posts-expand .post-nav{margin-top:40px}.post-button{margin-top:20px;text-align:left}.post-button a{margin:0 8px 8px 0!important;padding:0;font-size:14px;color:#666;background:0 0;border:none;border-bottom:2px solid #666;transition-property:border}@media (max-width:767px){.post-button a{font-size:12px}}@media (min-width:1600px){.post-button a{font-size:16px}}.post-button a:hover{border-bottom-color:#222}.links-of-blogroll-inline .links-of-blogroll-item{display:inline-block}.btn{padding:0 10px;border-width:2px;border-radius:0}.headband{display:none}.site-search{position:relative;float:right;margin-top:5px;padding-top:3px}@media (max-width:767px){.site-search{float:none;padding:0 10px}}@media (max-width:767px){.container .main-inner{width:auto}}.page-post-detail .post-meta,.page-post-detail .post-title{text-align:center}.page-post-detail .post-title:before{display:none}.page-post-detail .post-meta{margin-bottom:60px}.pagination{margin:120px 0 0;text-align:left}@media (max-width:767px){.pagination{margin:80px 10px 0;text-align:center}}.footer{margin-top:80px;padding:10px 0;background:#f5f5f5;color:#666}.footer-inner{margin:0 auto;text-align:left}@media (max-width:767px){.footer-inner{width:auto;text-align:center}} +/* build time:Thu Aug 10 2023 15:47:26 GMT+0800 (China Standard Time)*/ +html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}::selection{background:#262a30;color:#fff}body{position:relative;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif;font-size:14px;line-height:2;color:#555;background:#fff}@media (max-width:767px){body{padding-right:0!important}}@media (min-width:768px) and (max-width:991px){body{padding-right:0!important}}@media (min-width:1600px){body{font-size:16px}}h1,h2,h3,h4,h5,h6{margin:0;padding:0;font-weight:700;line-height:1.5;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif}h2,h3,h4,h5,h6{margin:20px 0 15px}h1{font-size:24px}@media (max-width:767px){h1{font-size:20px}}h2{font-size:22px}@media (max-width:767px){h2{font-size:18px}}h3{font-size:20px}@media (max-width:767px){h3{font-size:16px}}h4{font-size:18px}@media (max-width:767px){h4{font-size:14px}}h5{font-size:16px}@media (max-width:767px){h5{font-size:12px}}h6{font-size:14px}@media (max-width:767px){h6{font-size:10px}}p{margin:0 0 25px 0}a{color:#555;text-decoration:none;border-bottom:1px solid #999;word-wrap:break-word}a:hover{color:#222;border-bottom-color:#222}ul{list-style:none}blockquote{margin:0;padding:0}img{display:block;margin:auto;max-width:100%;height:auto}hr{margin:40px 0;height:3px;border:none;background-color:#ddd;background-image:repeating-linear-gradient(-45deg,#fff,#fff 4px,transparent 4px,transparent 8px)}blockquote{padding:0 15px;color:#666;border-left:4px solid #ddd}blockquote cite::before{content:"-";padding:0 5px}dt{font-weight:700}dd{margin:0;padding:0}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.center-block{display:block;margin-left:auto;margin-right:auto}.clearfix:after,.clearfix:before{content:" ";display:table}.clearfix:after{clear:both}.pullquote{width:45%}.pullquote.left{float:left;margin-left:5px;margin-right:10px}.pullquote.right{float:right;margin-left:10px;margin-right:5px}.affix.affix.affix{position:fixed}.translation{margin-top:-20px;font-size:14px;color:#999}.scrollbar-measure{width:100px;height:100px;overflow:scroll;position:absolute;top:-9999px}.use-motion .motion-element{opacity:0}#local-search-input{padding:3px;border:none;text-indent:14px;border-radius:0;width:140px;outline:0;border-bottom:1px solid #999;background:inherit;opacity:.5}#local-search-input:focus{opacity:1}.search-icon{position:absolute;top:9px}table{margin:20px 0;width:100%;border-collapse:collapse;border-spacing:0;border:1px solid #ddd;font-size:14px;table-layout:fixed;word-wrap:break-all}table>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}table>tbody>tr:hover{background-color:#f5f5f5}caption,td,th{padding:8px;text-align:left;vertical-align:middle;font-weight:400}td,th{border-bottom:3px solid #ddd;border-right:1px solid #eee}th{padding-bottom:10px;font-weight:700}td{border-bottom-width:1px}body,html{height:100%}.container{position:relative;min-height:100%}.header-inner{margin:0 auto;padding:100px 0 70px;width:700px}@media (min-width:1600px){.container .header-inner{width:900px}}.main{padding-bottom:150px}.main-inner{margin:0 auto;width:700px}@media (min-width:1600px){.container .main-inner{width:900px}}.footer{position:absolute;left:0;bottom:0;width:100%;min-height:50px}.footer-inner{box-sizing:border-box;margin:20px auto;width:700px}@media (min-width:1600px){.container .footer-inner{width:900px}}.highlight,pre{overflow:auto;margin:20px 0;padding:0;font-size:13px;color:#c5c8c6;background:#1d1f21;line-height:1.6}code,pre{font-family:consolas,Menlo,"PingFang SC","Microsoft YaHei",monospace}code{padding:2px 4px;word-wrap:break-word;color:#c5c8c6;background:#1d1f21;border-radius:3px;font-size:13px}pre code{padding:0;color:#c5c8c6;background:0 0;text-shadow:none}.highlight{border-radius:1px}.highlight pre{border:none;margin:0;padding:10px 0}.highlight table{margin:0;width:auto;border:none}.highlight td{border:none;padding:0}.highlight figcaption{font-size:1em;color:#c5c8c6;line-height:1em;margin-bottom:1em}.highlight figcaption:after,.highlight figcaption:before{content:" ";display:table}.highlight figcaption:after{clear:both}.highlight figcaption a{float:right;color:#c5c8c6}.highlight figcaption a:hover{border-bottom-color:#c5c8c6}.highlight .gutter pre{padding-left:10px;padding-right:10px;color:#888f96;text-align:right;background-color:#000}.highlight .code pre{padding-left:10px;padding-right:10px;background-color:#1d1f21}.highlight .line{height:20px}.gutter{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.gist table{width:auto}.gist table td{border:none}pre .deletion{background:green}pre .addition{background:maroon}pre .meta{color:#b294bb}pre .comment{color:#969896}pre .attribute,pre .css .class,pre .css .id,pre .css .pseudo,pre .html .doctype,pre .regexp,pre .ruby .constant,pre .tag,pre .variable,pre .xml .doctype,pre .xml .pi,pre .xml .tag .title{color:#c66}pre .built_in,pre .command,pre .constant,pre .literal,pre .number,pre .params,pre .preprocessor{color:#de935f}pre .css .rules .attribute,pre .formula,pre .header,pre .inheritance,pre .number,pre .ruby .class .title,pre .ruby .symbol,pre .special,pre .string,pre .value,pre .xml .cdata{color:#b5bd68}pre .css .hexcolor,pre .title{color:#8abeb7}pre .coffeescript .title,pre .function,pre .javascript .title,pre .perl .sub,pre .python .decorator,pre .python .title,pre .ruby .function .title,pre .ruby .title .keyword{color:#81a2be}pre .javascript .function,pre .keyword{color:#b294bb}.full-image.full-image.full-image{border:none;max-width:100%;width:auto;margin:20px auto}@media (min-width:992px){.full-image.full-image.full-image{max-width:none;width:110%;margin:25px -5%}}.blockquote-center,.page-home .post-type-quote blockquote,.page-post-detail .post-type-quote blockquote{position:relative;margin:40px 0;padding:0;border-left:none;text-align:center}.blockquote-center::after,.blockquote-center::before,.page-home .post-type-quote blockquote::after,.page-home .post-type-quote blockquote::before,.page-post-detail .post-type-quote blockquote::after,.page-post-detail .post-type-quote blockquote::before{position:absolute;content:' ';display:block;width:100%;height:24px;opacity:.2;background-repeat:no-repeat;background-position:0 -6px;background-size:22px 22px}.blockquote-center::before,.page-home .post-type-quote blockquote::before,.page-post-detail .post-type-quote blockquote::before{top:-20px;background-image:url(../images/quote-l.svg);border-top:1px solid #ccc}.blockquote-center::after,.page-home .post-type-quote blockquote::after,.page-post-detail .post-type-quote blockquote::after{bottom:-20px;background-image:url(../images/quote-r.svg);border-bottom:1px solid #ccc;background-position:100% 8px}.blockquote-center div,.blockquote-center p,.page-home .post-type-quote blockquote div,.page-home .post-type-quote blockquote p,.page-post-detail .post-type-quote blockquote div,.page-post-detail .post-type-quote blockquote p{text-align:center}.post .post-body .group-picture img{box-sizing:border-box;padding:0 3px;border:none}.post .group-picture-row{overflow:hidden;margin-top:6px}.post .group-picture-row:first-child{margin-top:0}.post .group-picture-column{float:left}.page-post-detail .post-body .group-picture-column{float:none;margin-top:10px;width:auto!important}.page-post-detail .post-body .group-picture-column img{margin:0 auto}.page-archive .group-picture-container{overflow:hidden}.page-archive .group-picture-row{float:left}.page-archive .group-picture-row:first-child{margin-top:6px}.page-archive .group-picture-column{max-width:150px;max-height:150px}.note{padding:20px;margin:20px 0;border:1px solid #eee;border-left-width:5px;border-radius:3px}.note h2,.note h3,.note h4,.note h5,.note h6{margin-top:0;margin-bottom:5px}.note p:last-child{margin-bottom:0}.note code{border-radius:3px}.note+.note{margin-top:-5px}.default{border-left-color:#777}.default h2,.default h3,.default h4,.default h5,.default h6{color:#777}.primary{border-left-color:#428bca}.primary h2,.primary h3,.primary h4,.primary h5,.primary h6{color:#428bca}.success{border-left-color:#5cb85c}.success h2,.success h3,.success h4,.success h5,.success h6{color:#5cb85c}.danger{border-left-color:#d9534f}.danger h2,.danger h3,.danger h4,.danger h5,.danger h6{color:#d9534f}.warning{border-left-color:#f0ad4e}.warning h2,.warning h3,.warning h4,.warning h5,.warning h6{color:#f0ad4e}.info{border-left-color:#5bc0de}.info h2,.info h3,.info h4,.info h5,.info h6{color:#5bc0de}.btn{display:inline-block;padding:0 20px;font-size:14px;color:#fff;background:#222;border:2px solid #222;text-decoration:none;border-radius:0;transition-property:background-color;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.btn:hover,.post-button .btn:hover{border-color:#222;color:#222;background:#fff}.btn-bar{display:block;width:22px;height:2px;background:#555;border-radius:1px}.btn-bar+.btn-bar{margin-top:4px}.pagination{margin:120px 0 40px;text-align:center;border-top:1px solid #eee}.page-number-basic,.pagination .next,.pagination .page-number,.pagination .prev,.pagination .space{display:inline-block;position:relative;top:-1px;margin:0 10px;padding:0 10px;line-height:30px}@media (max-width:767px){.page-number-basic,.pagination .next,.pagination .page-number,.pagination .prev,.pagination .space{margin:0 5px}}.pagination .next,.pagination .page-number,.pagination .prev{border-bottom:0;border-top:1px solid #eee;transition-property:border-color;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.pagination .next:hover,.pagination .page-number:hover,.pagination .prev:hover{border-top-color:#222}.pagination .space{padding:0;margin:0}.pagination .prev{margin-left:0}.pagination .next{margin-right:0}.pagination .page-number.current{color:#fff;background:#ccc;border-top-color:#ccc}@media (max-width:767px){.pagination{border-top:none}.pagination .next,.pagination .page-number,.pagination .prev{margin-bottom:10px;border-top:0;border-bottom:1px solid #eee}.pagination .next:hover,.pagination .page-number:hover,.pagination .prev:hover{border-bottom-color:#222}}.comments{margin:60px 20px 0}.tag-cloud{text-align:center}.tag-cloud a{display:inline-block;margin:10px}.back-to-top{box-sizing:border-box;position:fixed;bottom:-100px;right:50px;z-index:1050;padding:0 6px;width:25px;background:#222;font-size:12px;opacity:1;color:#fff;cursor:pointer;text-align:center;-webkit-transform:translateZ(0);transition-property:bottom;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}@media (max-width:767px){.back-to-top{display:none}}@media (min-width:768px) and (max-width:991px){.back-to-top{display:none}}.back-to-top.back-to-top-on{bottom:19px}.header{background:#fff}.header-inner{position:relative}.headband{height:3px;background:#222}.site-meta{margin:0;text-align:left}@media (max-width:767px){.site-meta{text-align:center}}.brand{position:relative;display:inline-block;padding:0 40px;color:#222;background:#222;border-bottom:none}.brand:hover{color:#222}.logo{display:inline-block;margin-right:5px;line-height:36px;vertical-align:top}.site-title{display:inline-block;vertical-align:top;line-height:36px;font-size:20px;font-weight:400;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif}.site-subtitle{margin-top:10px;font-size:13px;color:#999}.use-motion .brand{opacity:0}.use-motion .logo,.use-motion .site-subtitle,.use-motion .site-title{opacity:0;position:relative;top:-10px}.site-nav-toggle{display:none;position:absolute;top:10px;left:10px}@media (max-width:767px){.site-nav-toggle{display:block}}.site-nav-toggle button{margin-top:2px;padding:9px 10px;background:0 0;border:none}@media (max-width:767px){.site-nav{display:none;margin:0 -10px;padding:0 10px;clear:both;border-top:1px solid #ddd}}@media (min-width:768px) and (max-width:991px){.site-nav{display:block!important}}@media (min-width:992px){.site-nav{display:block!important}}.menu{margin-top:20px;padding-left:0;text-align:center}.menu .menu-item{display:inline-block;margin:0 10px}@media screen and (max-width:767px){.menu .menu-item{margin-top:10px}}.menu .menu-item a{display:block;font-size:13px;text-transform:capitalize;line-height:inherit;border-bottom:1px solid transparent;transition-property:border-color;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.menu .menu-item a:hover{border-bottom-color:#222}.menu .menu-item .fa{margin-right:5px}.use-motion .menu-item{opacity:0}.post-body{font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif}@media (max-width:767px){.post-body{word-break:break-word}}.post-body .fancybox img{display:block!important;margin:0 auto;cursor:pointer;cursor:zoom-in;cursor:-webkit-zoom-in}.post-body .figure .caption,.post-body .image-caption{margin:10px auto 15px;text-align:center;font-size:14px;color:#999;font-weight:700;line-height:1}.post-sticky-flag{display:inline-block;font-size:16px;-ms-transform:rotate(30deg);-webkit-transform:rotate(30deg);-moz-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}.posts-expand{padding-top:40px}@media (max-width:767px){.posts-expand{margin:0 20px}.post-body .highlight,.post-body pre{padding:10px}.post-body .highlight .gutter pre,.post-body pre .gutter pre{padding-right:10px}}@media (min-width:992px){.posts-expand .post-body{text-align:justify}}.posts-expand .post-body h2,.posts-expand .post-body h3,.posts-expand .post-body h4,.posts-expand .post-body h5,.posts-expand .post-body h6{padding-top:10px}.posts-expand .post-body h2 .header-anchor,.posts-expand .post-body h3 .header-anchor,.posts-expand .post-body h4 .header-anchor,.posts-expand .post-body h5 .header-anchor,.posts-expand .post-body h6 .header-anchor{float:right;margin-left:10px;color:#ccc;border-bottom-style:none;visibility:hidden}.posts-expand .post-body h2 .header-anchor:hover,.posts-expand .post-body h3 .header-anchor:hover,.posts-expand .post-body h4 .header-anchor:hover,.posts-expand .post-body h5 .header-anchor:hover,.posts-expand .post-body h6 .header-anchor:hover{color:inherit}.posts-expand .post-body h2:hover .header-anchor,.posts-expand .post-body h3:hover .header-anchor,.posts-expand .post-body h4:hover .header-anchor,.posts-expand .post-body h5:hover .header-anchor,.posts-expand .post-body h6:hover .header-anchor{visibility:visible}.posts-expand .post-body ul li{list-style:circle}.posts-expand .post-body img{box-sizing:border-box;margin:auto;padding:3px;border:1px solid #ddd}.posts-expand .fancybox img{margin:0 auto}@media (max-width:767px){.posts-collapse{margin:0 20px}.posts-collapse .post-meta,.posts-collapse .post-title{display:block;width:auto;text-align:left}}.posts-collapse{position:relative;z-index:1010;margin-left:0}.posts-collapse::after{content:" ";position:absolute;top:20px;left:0;margin-left:-2px;width:4px;height:100%;background:#f5f5f5;z-index:-1}@media (max-width:767px){.posts-collapse{margin:0 20px}}.posts-collapse .collection-title{position:relative;margin:60px 0}.posts-collapse .collection-title h2{margin-left:20px}.posts-collapse .collection-title small{color:#bbb}.posts-collapse .collection-title::before{content:" ";position:absolute;left:0;top:50%;margin-left:-4px;margin-top:-4px;width:8px;height:8px;background:#bbb;border-radius:50%}.posts-collapse .post{margin:30px 0}.posts-collapse .post-header{position:relative;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s;transition-property:border;border-bottom:1px dashed #ccc}.posts-collapse .post-header::before{content:" ";position:absolute;left:0;top:12px;width:6px;height:6px;margin-left:-4px;background:#bbb;border-radius:50%;border:1px solid #fff;transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s;transition-property:background}.posts-collapse .post-header:hover{border-bottom-color:#666}.posts-collapse .post-header:hover::before{background:#222}.posts-collapse .post-meta{position:absolute;font-size:12px;left:20px;top:5px}.posts-collapse .post-comments-count{display:none}.posts-collapse .post-title{margin-left:60px;font-size:16px;font-weight:400;line-height:inherit}.posts-collapse .post-title::after{margin-left:3px;opacity:.6}.posts-collapse .post-title a{color:#666;border-bottom:none}.page-home .post-type-quote .post-header,.page-home .post-type-quote .post-tags,.page-post-detail .post-type-quote .post-header,.page-post-detail .post-type-quote .post-tags{display:none}.posts-expand .post-title{font-size:26px;text-align:center;word-break:break-word;font-weight:400}@media (max-width:767px){.posts-expand .post-title{font-size:22px}}.posts-expand .post-title-link{display:inline-block;position:relative;color:#555;border-bottom:none;line-height:1.2;vertical-align:top}.posts-expand .post-title-link::before{content:"";position:absolute;width:100%;height:2px;bottom:0;left:0;background-color:#000;visibility:hidden;-webkit-transform:scaleX(0);-moz-transform:scaleX(0);-ms-transform:scaleX(0);-o-transform:scaleX(0);transform:scaleX(0);transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s}.posts-expand .post-title-link:hover::before{visibility:visible;-webkit-transform:scaleX(1);-moz-transform:scaleX(1);-ms-transform:scaleX(1);-o-transform:scaleX(1);transform:scaleX(1)}.posts-expand .post-title-link .fa{font-size:16px}.posts-expand .post-meta{margin:3px 0 60px 0;color:#999;font-family:Lato,"PingFang SC","Microsoft YaHei",sans-serif;font-size:12px;text-align:center}.posts-expand .post-meta .post-category-list{display:inline-block;margin:0;padding:3px}.posts-expand .post-meta .post-category-list-link{color:#999}.posts-expand .post-meta .post-description{font-size:14px;margin-top:2px}.post-meta-divider{margin:0 .5em}.post-meta-item-icon{margin-right:3px}@media (min-width:768px) and (max-width:991px){.post-meta-item-icon{display:inline-block}}@media (max-width:767px){.post-meta-item-icon{display:inline-block}}@media (min-width:768px) and (max-width:991px){.post-meta-item-text{display:none}}@media (max-width:767px){.post-meta-item-text{display:none}}@media (max-width:767px){.posts-expand .post-comments-count{display:none}}.social-like{font-size:14px;height:20px;text-align:center;border-top:1px solid #eee;padding-top:9px;margin-top:45px;display:flex;justify-content:center}.vk_like{width:85px;height:21px;padding-top:7px;align-self:center}.fb_like{height:30px;align-self:center}.post-button{margin-top:50px}.post-button .btn{color:#555;font-size:14px;background:0 0;border-radius:0;line-height:2;margin:0 4px 8px 4px}.post-button .fa-fw{width:1.285714285714286em;text-align:left}.posts-expand .post-tags{margin-top:40px;text-align:center}.posts-expand .post-tags a{display:inline-block;margin-right:10px;font-size:13px}.post-nav{display:table;margin-top:15px;width:100%;border-top:1px solid #eee}.post-nav-divider{display:table-cell;width:10%}.post-nav-item{display:table-cell;padding:10px 0 0 0;width:45%;vertical-align:top}.post-nav-item a{position:relative;display:block;line-height:25px;font-size:14px;color:#555;border-bottom:none}.post-nav-item a:hover{color:#222;border-bottom:none}.post-nav-item a:active{top:2px}.post-nav-item .fa{position:absolute;top:8px;left:0;font-size:12px}.post-nav-next a{padding-left:15px}.post-nav-prev{text-align:right}.post-nav-prev a{padding-right:15px}.post-nav-prev .fa{right:0;left:auto}.posts-expand .post-eof{display:block;margin:80px auto 60px;width:8%;height:1px;background:#ccc;text-align:center}.post:last-child .post-eof.post-eof.post-eof{display:none}.post-gallery{display:table;table-layout:fixed;width:100%;border-collapse:separate}.post-gallery-row{display:table-row}.post-gallery .post-gallery-img{display:table-cell;text-align:center;vertical-align:middle;border:none}.post-gallery .post-gallery-img img{max-width:100%;max-height:100%;border:none}.fancybox-close,.fancybox-close:hover{border:none}.sidebar{position:fixed;right:0;top:0;bottom:0;width:0;z-index:1040;box-shadow:inset 0 2px 6px #000;background:#222;-webkit-transform:translateZ(0)}.sidebar a{color:#999;border-bottom-color:#555}.sidebar a:hover{color:#eee}@media (min-width:768px) and (max-width:991px){.sidebar{display:none!important}}@media (max-width:767px){.sidebar{display:none!important}}.sidebar-inner{position:relative;padding:20px 10px;color:#999;text-align:center}.sidebar-toggle{position:fixed;right:50px;bottom:45px;width:15px;height:15px;padding:5px;background:#222;line-height:0;z-index:1050;cursor:pointer;-webkit-transform:translateZ(0)}@media (min-width:768px) and (max-width:991px){.sidebar-toggle{display:none}}@media (max-width:767px){.sidebar-toggle{display:none}}.sidebar-toggle-line{position:relative;display:inline-block;vertical-align:top;height:2px;width:100%;background:#fff;margin-top:3px}.sidebar-toggle-line:first-child{margin-top:0}.site-author-image{display:block;margin:0 auto;padding:2px;max-width:96px;height:auto;border:2px solid #333}.site-author-name{margin:5px 0 0;text-align:center;color:#f5f5f5;font-weight:400}.site-description{margin-top:5px;text-align:center;font-size:14px;color:#999}.site-state{overflow:hidden;line-height:1.4;white-space:nowrap;text-align:center}.site-state-item{display:inline-block;padding:0 15px;border-left:1px solid #333}.site-state-item:first-child{border-left:none}.site-state-item a{border-bottom:none}.site-state-item-count{display:block;text-align:center;color:inherit;font-weight:600;font-size:18px}.site-state-item-name{font-size:13px;color:inherit}.feed-link{margin-top:20px}.feed-link a{display:inline-block;padding:0 15px;color:#fc6423;border:1px solid #fc6423;border-radius:4px}.feed-link a i{color:#fc6423;font-size:14px}.feed-link a:hover{color:#fff;background:#fc6423}.feed-link a:hover i{color:#fff}.links-of-author{margin-top:20px}.links-of-author a{display:inline-block;vertical-align:middle;margin-right:10px;margin-bottom:10px;border-bottom-color:#555;font-size:13px}.links-of-author a:before{display:inline-block;vertical-align:middle;margin-right:3px;content:" ";width:4px;height:4px;border-radius:50%;background:#4260ff}.links-of-blogroll{font-size:13px}.links-of-blogroll-title{margin-top:20px;font-size:14px;font-weight:600}.links-of-blogroll-list{margin:0;padding:0}.links-of-blogroll-item{padding:2px 10px}.sidebar-nav{margin:0 0 20px;padding-left:0}.sidebar-nav li{display:inline-block;cursor:pointer;border-bottom:1px solid transparent;font-size:14px;color:#555}.sidebar-nav li:hover{color:#f5f5f5}.page-post-detail .sidebar-nav-toc{padding:0 5px}.page-post-detail .sidebar-nav-overview{margin-left:10px}.sidebar-nav .sidebar-nav-active{color:#87daff;border-bottom-color:#87daff}.sidebar-nav .sidebar-nav-active:hover{color:#87daff}.sidebar-panel{display:none}.sidebar-panel-active{display:block}.post-toc-empty{font-size:14px;color:#666}.post-toc-wrap{overflow:hidden}.post-toc{overflow:auto}.post-toc ol{margin:0;padding:0 2px 5px 10px;text-align:left;list-style:none;font-size:14px}.post-toc ol>ol{padding-left:0}.post-toc ol a{transition-duration:.2s;transition-timing-function:ease-in-out;transition-delay:0s;transition-property:all;color:#999;border-bottom-color:#555}.post-toc ol a:hover{color:#ccc;border-bottom-color:#ccc}.post-toc .nav-item{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.8}.post-toc .nav .nav-child{display:none}.post-toc .nav .active>.nav-child{display:block}.post-toc .nav .active-current>.nav-child{display:block}.post-toc .nav .active-current>.nav-child>.nav-item{display:block}.post-toc .nav .active>a{color:#87daff;border-bottom-color:#87daff}.post-toc .nav .active-current>a{color:#87daff}.post-toc .nav .active-current>a:hover{color:#87daff}.footer{font-size:14px;color:#999}.footer img{border:none}.footer-inner{text-align:center}.with-love{display:inline-block;margin:0 5px}.powered-by,.theme-info{display:inline-block}.powered-by{margin-right:10px}.powered-by::after{content:"|";padding-left:10px}.cc-license{margin-top:10px;text-align:center}.cc-license .cc-opacity{opacity:.7;border-bottom:none}.cc-license .cc-opacity:hover{opacity:.9}.cc-license img{display:inline-block}.cloud-tie-wrapper img{display:inline-block}.cloud-tie-wrapper .total-txt{font-size:1em!important}.cloud-tie-join-count .join-count{color:#555!important;font-size:inherit!important;margin:0!important}.post-spread{margin-top:20px;text-align:center}.jiathis_style{display:inline-block}.jiathis_style a{border:none}.post-spread{margin-top:20px;text-align:center}.bdshare-slide-button-box a{border:none}.bdsharebuttonbox{display:inline-block}.bdsharebuttonbox a{border:none}ul.search-result-list{padding-left:0;margin:0 5px 0 8px}p.search-result{border-bottom:1px dashed #ccc;padding:5px 0}a.search-result-title{font-weight:700}a.search-result{border-bottom:transparent;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-keyword{border-bottom:1px dashed red;font-size:14px;font-weight:700;color:red}#local-search-result{height:88%;overflow:auto}.popup{display:none;position:fixed;top:10%;left:50%;width:700px;height:80%;margin-left:-350px;padding:3px 0 0 10px;background:#fff;color:#333;z-index:9999;border-radius:5px}@media (max-width:767px){.popup{padding:3px;top:0;left:0;margin:0;width:100%;height:100%;border-radius:0}}.popoverlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:2080;background-color:rgba(0,0,0,.3)}#local-search-input{margin-bottom:10px;padding:10px;width:97%;font-size:18px}.popup .fa-search{padding-top:8px}.popup-btn-close{position:absolute;top:6px;right:14px;color:#4ebd79;font-size:14px;font-weight:700;text-transform:uppercase;cursor:pointer}#no-result{position:absolute;left:44%;top:42%;color:#ccc}.use-motion .post{opacity:0}.page-archive .archive-page-counter{position:relative;top:3px;left:20px}@media (max-width:767px){.page-archive .archive-page-counter{top:5px}}.page-archive .posts-collapse .archive-move-on{position:absolute;top:11px;left:0;margin-left:-6px;width:10px;height:10px;opacity:.5;background:#555;border:1px solid #fff;border-radius:50%}.category-all-page .category-all-title{text-align:center}.category-all-page .category-all{margin-top:20px}.category-all-page .category-list{margin:0;padding:0;list-style:none}.category-all-page .category-list-item{margin:5px 10px}.category-all-page .category-list-count{color:#bbb}.category-all-page .category-list-count:before{display:inline;content:" ("}.category-all-page .category-list-count:after{display:inline;content:") "}.category-all-page .category-list-child{padding-left:10px}#schedule ul#event-list{padding-left:30px}#schedule ul#event-list hr{margin:20px 0 45px 0!important;background:#222}#schedule ul#event-list hr:after{display:inline-block;content:'NOW';background:#222;color:#fff;font-weight:700;text-align:right;padding:0 5px}#schedule ul#event-list li.event{margin:20px 0;background:#f9f9f9;padding-left:10px;min-height:40px}#schedule ul#event-list li.event h2.event-summary{margin:0;padding-bottom:3px}#schedule ul#event-list li.event h2.event-summary:before{display:inline-block;font-family:FontAwesome;font-size:8px;content:'\f111';vertical-align:middle;margin-right:25px;color:#bbb}#schedule ul#event-list li.event span.event-relative-time{display:inline-block;font-size:12px;font-weight:400;padding-left:12px;color:#bbb}#schedule ul#event-list li.event span.event-details{display:block;color:#bbb;margin-left:56px;padding-top:3px;padding-bottom:6px;text-indent:-24px;line-height:18px}#schedule ul#event-list li.event span.event-details:before{text-indent:0;display:inline-block;width:14px;font-family:FontAwesome;text-align:center;margin-right:9px;color:#bbb}#schedule ul#event-list li.event span.event-details.event-location:before{content:'\f041'}#schedule ul#event-list li.event span.event-details.event-duration:before{content:'\f017'}#schedule ul#event-list li.event-past{background:#fcfcfc}#schedule ul#event-list li.event-past>*{opacity:.6}#schedule ul#event-list li.event-past h2.event-summary{color:#bbb}#schedule ul#event-list li.event-past h2.event-summary:before{color:#dfdfdf}#schedule ul#event-list li.event-now{background:#222;color:#fff;padding:15px 0 15px 10px}#schedule ul#event-list li.event-now h2.event-summary:before{-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-ms-transform:scale(1.2);-o-transform:scale(1.2);transform:scale(1.2);color:#fff;animation:dot-flash 1s alternate infinite ease-in-out}#schedule ul#event-list li.event-now *{color:#fff!important}@-moz-keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-webkit-keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-o-keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@keyframes dot-flash{from{opacity:1;-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}to{opacity:0;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}.page-post-detail .sidebar-toggle-line{background:#87daff}.page-post-detail .comments{overflow:hidden}h1,h2,h3,h4,h5,h6{margin:20px 0 10px}p{margin:0 0 25px 0}a{border-bottom-color:#ccc}hr{margin:20px 0;height:2px}.main-inner{margin-top:80px}.header{background:#f5f5f5}.header-inner{padding:25px 0 20px}.header-inner:after,.header-inner:before{content:" ";display:table}.header-inner:after{clear:both}@media (max-width:767px){.header-inner{width:auto;margin-bottom:50px;padding:10px}}.site-meta{float:left;margin-left:-20px;line-height:normal}@media (max-width:767px){.site-meta{margin-left:10px}}.site-meta .brand{padding:2px 1px;background:0 0}@media (max-width:767px){.site-meta .brand{display:block}}.site-meta .logo{display:none}.site-meta .site-title{font-size:22px;font-weight:bolder}@media (max-width:767px){.site-meta .site-title{line-height:34px}}.logo-line-after,.logo-line-before{display:block;overflow:hidden;margin:0 auto;width:75%}@media (max-width:767px){.logo-line-after,.logo-line-before{display:none}}.logo-line-after i,.logo-line-before i{position:relative;display:block;height:2px;background:#222}@media (max-width:767px){.logo-line-after i,.logo-line-before i{height:3px}}.use-motion .logo-line-before i{left:-100%}.use-motion .logo-line-after i{right:-100%}.site-subtitle{display:none}.site-nav-toggle{position:static;float:right}.menu{float:right;margin:8px 0 0 0}@media (max-width:767px){.menu{margin:20px 0 0 0;padding:0}}.menu br{display:none}.menu .menu-item{margin:0}@media (max-width:767px){.menu .menu-item{display:block}}.menu .menu-item a{padding:0 10px;background:0 0;border:none;border-radius:2px;transition-property:background}@media (max-width:767px){.menu .menu-item a{text-align:left}}.menu .menu-item a:hover{background:#e1e1e1}.menu a::before{display:none}@media (max-width:767px){.menu a::before{display:block}}@media (max-width:767px){.menu{float:none}}.site-search form{display:none}.posts-expand{padding-top:0}.posts-expand .post-meta,.posts-expand .post-title{text-align:left}@media (max-width:767px){.posts-expand .post-meta,.posts-expand .post-title{text-align:center}}.posts-expand .post-eof{display:none}.posts-expand .post{margin-top:120px}.posts-expand .post:first-child{margin-top:0}.posts-expand .post-meta{margin-top:5px;margin-bottom:20px}.posts-expand .post-title{position:relative;font-size:26px;font-weight:400}@media (max-width:767px){.posts-expand .post-title{font-size:20px}}@media (min-width:1600px){.posts-expand .post-title{font-size:26px}}.posts-expand .post-title:hover:before{background:#222}.posts-expand .post-body img{margin:0}.posts-expand .post-tags{text-align:left}.posts-expand .post-tags a{padding:1px 5px;background:#f5f5f5;border-bottom:none}.posts-expand .post-tags a:hover{background:#ccc}.posts-expand .post-nav{margin-top:40px}.post-button{margin-top:20px;text-align:left}.post-button a{margin:0 8px 8px 0!important;padding:0;font-size:14px;color:#666;background:0 0;border:none;border-bottom:2px solid #666;transition-property:border}@media (max-width:767px){.post-button a{font-size:12px}}@media (min-width:1600px){.post-button a{font-size:16px}}.post-button a:hover{border-bottom-color:#222}.links-of-blogroll-inline .links-of-blogroll-item{display:inline-block}.btn{padding:0 10px;border-width:2px;border-radius:0}.headband{display:none}.site-search{position:relative;float:right;margin-top:5px;padding-top:3px}@media (max-width:767px){.site-search{float:none;padding:0 10px}}@media (max-width:767px){.container .main-inner{width:auto}}.page-post-detail .post-meta,.page-post-detail .post-title{text-align:center}.page-post-detail .post-title:before{display:none}.page-post-detail .post-meta{margin-bottom:60px}.pagination{margin:120px 0 0;text-align:left}@media (max-width:767px){.pagination{margin:80px 10px 0;text-align:center}}.footer{margin-top:80px;padding:10px 0;background:#f5f5f5;color:#666}.footer-inner{margin:0 auto;text-align:left}@media (max-width:767px){.footer-inner{width:auto;text-align:center}} /* rebuild by neat */ \ No newline at end of file diff --git a/lib/fancybox/source/helpers/jquery.fancybox-buttons.css b/lib/fancybox/source/helpers/jquery.fancybox-buttons.css index 1f93dd9775..8619b9174a 100644 --- a/lib/fancybox/source/helpers/jquery.fancybox-buttons.css +++ b/lib/fancybox/source/helpers/jquery.fancybox-buttons.css @@ -1,3 +1,3 @@ -/* build time:Mon Jun 19 2023 18:25:04 GMT+0800 (China Standard Time)*/ +/* build time:Thu Aug 10 2023 15:47:26 GMT+0800 (China Standard Time)*/ #fancybox-buttons{position:fixed;left:0;width:100%;z-index:8050}#fancybox-buttons.top{top:10px}#fancybox-buttons.bottom{bottom:10px}#fancybox-buttons ul{display:block;width:166px;height:30px;margin:0 auto;padding:0;list-style:none;border:1px solid #111;border-radius:3px;-webkit-box-shadow:inset 0 0 0 1px rgba(255,255,255,.05);-moz-box-shadow:inset 0 0 0 1px rgba(255,255,255,.05);box-shadow:inset 0 0 0 1px rgba(255,255,255,.05);background:#323232;background:-moz-linear-gradient(top,#444 0,#343434 50%,#292929 50%,#333 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#444),color-stop(50%,#343434),color-stop(50%,#292929),color-stop(100%,#333));background:-webkit-linear-gradient(top,#444 0,#343434 50%,#292929 50%,#333 100%);background:-o-linear-gradient(top,#444 0,#343434 50%,#292929 50%,#333 100%);background:-ms-linear-gradient(top,#444 0,#343434 50%,#292929 50%,#333 100%);background:linear-gradient(top,#444 0,#343434 50%,#292929 50%,#333 100%)}#fancybox-buttons ul li{float:left;margin:0;padding:0}#fancybox-buttons a{display:block;width:30px;height:30px;text-indent:-9999px;background-color:transparent;background-image:url(fancybox_buttons.png);background-repeat:no-repeat;outline:0;opacity:.8}#fancybox-buttons a:hover{opacity:1}#fancybox-buttons a.btnPrev{background-position:5px 0}#fancybox-buttons a.btnNext{background-position:-33px 0;border-right:1px solid #3e3e3e}#fancybox-buttons a.btnPlay{background-position:0 -30px}#fancybox-buttons a.btnPlayOn{background-position:-30px -30px}#fancybox-buttons a.btnToggle{background-position:3px -60px;border-left:1px solid #111;border-right:1px solid #3e3e3e;width:35px}#fancybox-buttons a.btnToggleOn{background-position:-27px -60px}#fancybox-buttons a.btnClose{border-left:1px solid #111;width:35px;background-position:-56px 0}#fancybox-buttons a.btnDisabled{opacity:.4;cursor:default} /* rebuild by neat */ \ No newline at end of file diff --git a/lib/fancybox/source/helpers/jquery.fancybox-thumbs.css b/lib/fancybox/source/helpers/jquery.fancybox-thumbs.css index 0ea776fcf3..a21a0823e2 100644 --- a/lib/fancybox/source/helpers/jquery.fancybox-thumbs.css +++ b/lib/fancybox/source/helpers/jquery.fancybox-thumbs.css @@ -1,3 +1,3 @@ -/* build time:Mon Jun 19 2023 18:25:04 GMT+0800 (China Standard Time)*/ +/* build time:Thu Aug 10 2023 15:47:26 GMT+0800 (China Standard Time)*/ #fancybox-thumbs{position:fixed;left:0;width:100%;overflow:hidden;z-index:8050}#fancybox-thumbs.bottom{bottom:2px}#fancybox-thumbs.top{top:2px}#fancybox-thumbs ul{position:relative;list-style:none;margin:0;padding:0}#fancybox-thumbs ul li{float:left;padding:1px;opacity:.5}#fancybox-thumbs ul li.active{opacity:.75;padding:0;border:1px solid #fff}#fancybox-thumbs ul li:hover{opacity:1}#fancybox-thumbs ul li a{display:block;position:relative;overflow:hidden;border:1px solid #222;background:#111;outline:0}#fancybox-thumbs ul li img{display:block;position:relative;border:0;padding:0;max-width:none} /* rebuild by neat */ \ No newline at end of file diff --git a/lib/fancybox/source/jquery.fancybox.css b/lib/fancybox/source/jquery.fancybox.css index d8e8e1fd60..b16c2d8176 100644 --- a/lib/fancybox/source/jquery.fancybox.css +++ b/lib/fancybox/source/jquery.fancybox.css @@ -1,3 +1,3 @@ -/* build time:Mon Jun 19 2023 18:25:04 GMT+0800 (China Standard Time)*/ +/* build time:Thu Aug 10 2023 15:47:26 GMT+0800 (China Standard Time)*/ /*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */.fancybox-image,.fancybox-inner,.fancybox-nav,.fancybox-nav span,.fancybox-outer,.fancybox-skin,.fancybox-tmp,.fancybox-wrap,.fancybox-wrap iframe,.fancybox-wrap object{padding:0;margin:0;border:0;outline:0;vertical-align:top}.fancybox-wrap{position:absolute;top:0;left:0;z-index:8020}.fancybox-skin{position:relative;background:#f9f9f9;color:#444;text-shadow:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fancybox-opened{z-index:8030}.fancybox-opened .fancybox-skin{-webkit-box-shadow:0 10px 25px rgba(0,0,0,.5);-moz-box-shadow:0 10px 25px rgba(0,0,0,.5);box-shadow:0 10px 25px rgba(0,0,0,.5)}.fancybox-inner,.fancybox-outer{position:relative}.fancybox-inner{overflow:hidden}.fancybox-type-iframe .fancybox-inner{-webkit-overflow-scrolling:touch}.fancybox-error{color:#444;font:14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;margin:0;padding:15px;white-space:nowrap}.fancybox-iframe,.fancybox-image{display:block;width:100%;height:100%}.fancybox-image{max-width:100%;max-height:100%}#fancybox-loading,.fancybox-close,.fancybox-next span,.fancybox-prev span{background-image:url(fancybox_sprite.png)}#fancybox-loading{position:fixed;top:50%;left:50%;margin-top:-22px;margin-left:-22px;background-position:0 -108px;opacity:.8;cursor:pointer;z-index:8060}#fancybox-loading div{width:44px;height:44px;background:url(fancybox_loading.gif) center center no-repeat}.fancybox-close{position:absolute;top:-18px;right:-18px;width:36px;height:36px;cursor:pointer;z-index:8040}.fancybox-nav{position:absolute;top:0;width:40%;height:100%;cursor:pointer;text-decoration:none;background:transparent url(blank.gif);-webkit-tap-highlight-color:transparent;z-index:8040}.fancybox-prev{left:0}.fancybox-next{right:0}.fancybox-nav span{position:absolute;top:50%;width:36px;height:34px;margin-top:-18px;cursor:pointer;z-index:8040;visibility:hidden}.fancybox-prev span{left:10px;background-position:0 -36px}.fancybox-next span{right:10px;background-position:0 -72px}.fancybox-nav:hover span{visibility:visible}.fancybox-tmp{position:absolute;top:-99999px;left:-99999px;visibility:hidden;max-width:99999px;max-height:99999px;overflow:visible!important}.fancybox-lock{overflow:hidden!important;width:auto}.fancybox-lock body{overflow:hidden!important}.fancybox-lock-test{overflow-y:hidden!important}.fancybox-overlay{position:absolute;top:0;left:0;overflow:hidden;display:none;z-index:8010;background:url(fancybox_overlay.png)}.fancybox-overlay-fixed{position:fixed;bottom:0;right:0}.fancybox-lock .fancybox-overlay{overflow:auto;overflow-y:scroll}.fancybox-title{visibility:hidden;font:normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;position:relative;text-shadow:none;z-index:8050}.fancybox-opened .fancybox-title{visibility:visible}.fancybox-title-float-wrap{position:absolute;bottom:0;right:50%;margin-bottom:-35px;z-index:8050;text-align:center}.fancybox-title-float-wrap .child{display:inline-block;margin-right:-100%;padding:2px 20px;background:0 0;background:rgba(0,0,0,.8);-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;text-shadow:0 1px 2px #222;color:#fff;font-weight:700;line-height:24px;white-space:nowrap}.fancybox-title-outside-wrap{position:relative;margin-top:10px;color:#fff}.fancybox-title-inside-wrap{padding-top:10px}.fancybox-title-over-wrap{position:absolute;bottom:0;left:0;color:#fff;padding:10px;background:#000;background:rgba(0,0,0,.8)}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min--moz-device-pixel-ratio:1.5),only screen and (min-device-pixel-ratio:1.5){#fancybox-loading,.fancybox-close,.fancybox-next span,.fancybox-prev span{background-image:url(fancybox_sprite@2x.png);background-size:44px 152px}#fancybox-loading div{background-image:url(fancybox_loading@2x.gif);background-size:24px 24px}} /* rebuild by neat */ \ No newline at end of file diff --git a/lib/font-awesome/css/font-awesome.css b/lib/font-awesome/css/font-awesome.css index 9a48413e5a..232896c62e 100644 --- a/lib/font-awesome/css/font-awesome.css +++ b/lib/font-awesome/css/font-awesome.css @@ -1,4 +1,4 @@ -/* build time:Mon Jun 19 2023 18:25:04 GMT+0800 (China Standard Time)*/ +/* build time:Thu Aug 10 2023 15:47:26 GMT+0800 (China Standard Time)*/ /*! * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) diff --git a/search.xml b/search.xml index 5288a807c6..23575666f3 100644 --- a/search.xml +++ b/search.xml @@ -1,6408 +1,2825 @@ - - - - - - 字节码增强 - 初识链路追踪 - ByteBuddy - - /20230226-Bytecode_enhancement_part_1/ - - 1. 简单介绍

1.1 背景

  • 不能通过 -javaagent 方式启动
  • 需要增强非业务代码( Spring AOP 不够用)
  • 业务方尽量少改动代码

1.2 效果

1
2
3
4
5
6
7
[Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@87f383f on sun.instrument.InstrumentationImpl@4eb7f003
[Byte Buddy] INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@87f383f on sun.instrument.InstrumentationImpl@4eb7f003
[Byte Buddy] TRANSFORM com.hisen.agent.util.Hisen [sun.misc.Launcher$AppClassLoader@18b4aac2, null, Thread[main,5,main], loaded=false]
name before:hisen
hello: hisen1677417020466
public static void com.hisen.agent.util.Hisen.hello(java.lang.String): took 0 millisecond
HisenAdvice exit. time use:0

2. 代码逻辑

2.1 启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author hisenyuan
* @date 2023/2/6 21:31
*/
public class Start {
public static void main(String[] args) {
startTraceAgent();
Hisen.hello("hisen");
}

private static void startTraceAgent() {
Instrumentation install = ByteBuddyAgent.install();
HisenInterceptor.init(install);
new HisenInstrumentation().init(install);
}
}

2.2 拦截方式

会改变程序执行的堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* @author hisenyuan
* @date 2023/2/8 21:17
*/
public class HisenInterceptor {
public static void init(Instrumentation instrumentation) {
new AgentBuilder.Default()
.type(ElementMatchers.nameEndsWith("Hisen"))
.transform((builder, type, classLoader, module, protectionDomain) -> builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(TimeInterceptor.class)))
.installOn(instrumentation);
}

public static class TimeInterceptor {
@RuntimeType
public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable) throws Exception {
long start = System.currentTimeMillis();
try {
return callable.call();
} catch (Exception e) {
// 进行异常信息上报
System.out.println("方法执行发生异常" + e.getMessage());
throw e;
} finally {
System.out.println(method + ": took " + (System.currentTimeMillis() - start) + " millisecond");
}
}
}
}

2.3 代码注入

不会改变代码堆栈流
这种方式也可以增强系统类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* @author hisenyuan
* @date 2023/2/8 20:16
*/
public class HisenInstrumentation {
public void init(Instrumentation instrumentation) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RETRANSFORMATION)
// Make sure we see helpful logs
.with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
.with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
.ignore(none())
// Ignore Byte Buddy and JDK classes we are not interested in
.ignore(
nameStartsWith("net.bytebuddy.")
.or(nameStartsWith("jdk.internal.reflect."))
.or(nameStartsWith("java.lang.invoke."))
.or(nameStartsWith("com.sun.proxy."))
)
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.type(nameEndsWith("Hisen"))
.transform(
(builder, type, classLoader, transformer, module) ->
builder.visit(Advice.to(HisenAdvice.class).on(isMethod().and(named("hello")))))
.installOn(instrumentation);
}

public static class HisenAdvice {
@Advice.OnMethodEnter
static long enter(@Advice.Argument(value = 0, typing = DYNAMIC, readOnly = false) String name) {
System.out.println("name before:" + name);
// change name
name += System.currentTimeMillis();
return System.currentTimeMillis();
}

@Advice.OnMethodExit(onThrowable = RuntimeException.class)
static void exit(
@Advice.Enter
long startTime) {
System.out.println("HisenAdvice exit. time use:" + (System.currentTimeMillis() - startTime));
}
}
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 给群晖 NAS DS918+ 添加 RTL-8156B 2.5G 无线网卡 - - /20221119-synology-DS918-add-2500M-RTL8156B-usb-network-cart/ - - 0. 前言

就这个群晖的网卡,困扰了我两天。
因为按照之后没法在系统控制面板里面找到对应的网卡。
就连 GitHub 上驱动的作者都说不支持 RTL-8156B(详见:GitHub-issue) 的外置网卡。
但是我想那么多人都买了这种网卡,并且成功了,于是周末到处搜,最终找到了办法。

可能这是 DS918+ 之类才会遇到的问题

1. 获取 root 权限

搜索引擎很多

2. 安装驱动

2.1 查询群晖架构

群晖官网查询NASCPU架构

2.2 下载对应驱动

https://github.com/bb-qq/r8152/releases
我这里是 DM7 的系统,下载的最新版本
下载到 PC 上即可,不用下载到 NAS

2.3 安装

下载完成,在群晖套件中心,手动安装,选择刚刚下载的驱动文件安装
第一次安装会失败(GitHub 上也有说明),需要执行如下命令

1
sudo install -m 4755 -o root -D /var/packages/r8152/target/r8152/spk_su /opt/sbin/spk_su

修改之后,再次安装即可完成。

3. 修改配置

由于群晖918+等型号对 lan 口限制了最大 2 个,这里需要修改两个配置文件。
都是找到 maxlanport=“2” 修改数字为大于 2 的数字即可。

3.1 其一

1
2
3
vi /etc.defaults/synoinfo.conf
# 我这里的修改
maxlanport=“4”

3.2 其二

1
2
3
vi /etc/synoinfo.conf
# 这个文件有点大,好好找找,我的是在 77% 左右
maxlanport="4"

4. 信息中心-网络

群晖 DS918+,USB 外置网卡安装好驱动,设置好 maxlan 之后,
系统命令行可以识别网卡,并且可以获取 IPv6 地址,但是没有 IPv4 的地址

1
2
3
4
5
6
7
8
9
10
root@HiSEN-DS:~# ifconfig
eth2 Link encap:Ethernet HWaddr 00:xx:xx:xx:xx:03
inet6 addr: fd7e:a5c4:268d:0:2e0:ffff:fe68:80/64 Scope:Global
inet6 addr: fe80::2e0:ffff:fe68:80/64 Scope:Link
inet6 addr: 2408:8207:ffff:6700:2e0:ffff:fe68:80/64 Scope:Global
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:4 errors:0 dropped:0 overruns:0 frame:0
TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1175 (1.1 KiB) TX bytes:1446 (1.4 KiB)

5. 控制面板-网络-网络界面

设置局域网 3 (外置网卡) 为自动获取 IP 后,网络正常,系统控制面积显示正常

1
2
3
4
5
6
7
8
9
10
11
root@DS:~# ifconfig
eth2 Link encap:Ethernet HWaddr 00:xx:xx:xx:xx:03
inet addr:10.0.0.xxx Bcast:10.0.0.255 Mask:255.255.255.0
inet6 addr: fd7e:a5c4:268d:0:2e0:ffff:fe68:80/64 Scope:Global
inet6 addr: fe80::2e0:ffff:fe68:80/64 Scope:Link
inet6 addr: 2408:8207:ffff:6700:2e0:ffff:fe68:80/64 Scope:Global
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1419 errors:0 dropped:0 overruns:0 frame:0
TX packets:312 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:465252 (454.3 KiB) TX bytes:71449 (69.7 KiB)

6. 参考

群晖DSM系统“套件法”安装8156B芯片网卡驱动
升級2.5G網卡的一些疑難雜症

]]>
- - - - - homelab - - - - - - - NAS - - - -
- - - - - 通过 OpenWrt 利用 Cloudflare tunnel 回家 - 连上家庭内网 - - /20221114-go_home_by_cloudflare_via_openwrt/ - - 1. 写在前面

需要注册 Cloudflare 账号
这个服务目前是可以免费使用

速度的话一般,勉强可以接受。
后续再折腾看看是什么问题。

2. 主要操作步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装客户端
opkg install cloudflared
# 登录, 返回一个 cloudflare 官方连接,点击会生成密钥
cloudflared tunnel login
# 创建一条隧道,<NAME> 替换成你喜欢的名字
cloudflared tunnel create <NAME>
# 创建一个域名解析
cloudflared tunnel route dns hisen home.hisenyuan.xyz
# 配置 xxx.yml
url: http://192.168.0.10 # 内网的网关,注意做好鉴权
tunnel: d42a48c9-26da-4734-b249-1b9eee69ba8c
credentials-file: /root/.cloudflared/d42a48c9-26da-4734-b249-1b9eee69ba8c.json
# 启动一个内网穿透服务
cloudflared tunnel --config xxx.yml run

3. 参考文档

]]>
- - - - - homelab - - - - - - - OpenWrt - - - -
- - - - - 网件 R8000 利用U盘扩展 OpenWrt overlay 空间 - - /20221030-OpenWrt_NETGEAR_R8000_use_USB_flash_disk_to_expand_overlay/ - - 一、问题

OpenWrt 官方关于 netgear r8000 的信息
https://openwrt.org/toh/netgear/r8000
“Second data partition (79 MiB) not available in OpenWrt”

128M 的空间,安装 OpenWrt 之后就剩下 21M 的空间可以安装插件
那自然是不够用的了,于是乎就找了些扩大空间的办法,最靠谱的就是利用U盘挂载

二、解决

2.1 获取U盘信息

插上 U盘 至 USB3 插口
登录 OpenWrt 控制台,查看 USB 在 OpenWrt 中的名称
也就是 /dev/sda

1
2
3
4
5
6
7
8
9
10
11
root@OpenWrt:~# cat /proc/scsi/usb-storage/0
Host scsi0: usb-storage
Vendor: SanDisk
Product: Extreme
Serial Number: AA010316152154386775
Protocol: Transparent SCSI
Transport: Bulk
Quirks: SANE_SENSE

root@OpenWrt:~# ls /dev/sda*
/dev/sda

2.2 安装所需软件

安装相关软件
因为需要格式化 U盘 至 ext4 的格式
以及挂载U盘空间至 overlay

1
root@OpenWrt:~# opkg update && opkg install block-mount e2fsprogs kmod-fs-ext4 kmod-usb-storage kmod-usb2 kmod-usb3

2.3 格式化U盘

格式化 U盘 至 ext4

1
2
3
4
5
root@OpenWrt:~# mkfs.ext4 /dev/sda
mke2fs 1.46.5 (30-Dec-2021)
/dev/sda contains a ext4 file system
created on Sat Oct 29 08:03:58 2022
Proceed anyway? (y,N) y

2.3 挂载 U盘

挂载 U盘 至 overlay
重启之后,就大功告成
放心地去安装各种插件吧

1
2
3
4
5
6
7
8
9
10
11
root@OpenWrt:~# mount /dev/sda /mnt
root@OpenWrt:~# mkdir /tmp/root
root@OpenWrt:~# mount -o bind / /tmp/root
root@OpenWrt:~# cp /tmp/root/* /mnt -a
root@OpenWrt:~# umount /tmp/root
root@OpenWrt:~# umount /mnt
root@OpenWrt:~# block detect > /etc/config/fstab
root@OpenWrt:~# uci set fstab.@mount[0].target='/overlay'
root@OpenWrt:~# uci set fstab.@mount[0].enabled='1'
root@OpenWrt:~# uci commit fstab
root@OpenWrt:~# reboot

三、参考

OpenWrt挂载U盘或SD卡作为根文件系统

]]>
- - - - - homelab - - - - - - - OpenWrt - - - -
- - - - - OpenWrt 新版本安装 passwall - - /20221030-OpenWrt_22.03_install_passwall/ - - 一、背景

最近看了不少软路由的东西
于是折腾了一波 OpenWrt
奈何网件 R8000 配置一般,跑起来体验不好。
后续估计是会上 x86 主机了,虽然可能会性能过剩。

二、问题

在官方原版的 OpenWrt 22.03 版本中
如果直接在 software 中安装 luci_app_passwall
那么安装后会提示没法实现透明代理,还需要安装一些额外的软件才行

三、解决问题

1
opkg install ipset ipt2socks iptables iptables-mod-conntrack-extra iptables-mod-iprange iptables-mod-socket iptables-mod-tproxy kmod-ipt-nat

安装完成之后,再按教程操作即可.
原因就是新版本的系统中默认不包含上述模块。
问题解决参考自 GitHub issue.

四、参考

]]>
- - - - - homelab - - - - - - - OpenWrt - - - -
- - - - - 关于数据一致性 - - /20220717-about-data-consistency/ - - 1. 背景

电商场景下的订单系统
往往会有很多查询需求
单体数据库无法满足大量数据存储、各种复杂查询

2. 方案

待更新

3. 总结

待更新

]]>
- - - - - database - - - - - - - db - - - -
- - - - - 水族入门 - - /20220612-introduction-to-aquarium/ - - 0. 概览

0.1 简介

入坑水族几年,和年纪无关…
修生养性,培养一个兴趣爱好,毕竟生命在于折腾
下班回家,看着鱼儿在水中游,喂喂乌龟,也蛮有趣

最近几天在折腾过滤,
进一步了解了一下过滤系统,
也有朋友在问养鱼养龟方面的问题,
于是就想着写一篇博客简单的记录一下。

不懂的多百度,设备什么的购物平台搜搜。
有兴趣可以逛逛:南美水族论坛、乌龟吧(百度贴吧),等水族相关内容。

0.2 预防针

需要有一定的金钱投入,更重要的是精力投入,长期维护。

0.3 我的水族

  • 鱼缸
    • 动物:宝莲灯、孔雀鱼、苹果螺、杀手螺、黑壳虾
    • 植物:小水兰、珍珠草
    • 过滤:伊罕滤桶
    • 灯具:LED 水草灯
  • 乌龟缸
    • 动物:鳄鱼龟(互动性不错)、苹果螺、黑壳虾
    • 植物:石菖蒲、水竹、紫芋
    • 过滤:侧滤 + 沼泽 + 滴滤
    • 灯具:LED 水草灯

1. 动物选择

养宠物之前
一定要先了解清楚
自己喜欢什么,适合什么
先了解学习一些经验,不要着急入手

鳄鱼龟:互动性不错,有点危险,长的很快.
宝莲灯:个人比较喜欢的热带灯科鱼,小型的.

2. 植物选择

鱼缸

  • 阴性草
    • 优点:简单好养,不需要水草灯、二氧化氮设备
    • 缺点:不太好看
  • 阳性草
    • 优点:好看
    • 缺点:需要一定的养护技巧,还有相关的设备

龟缸
主要考虑过滤使用
选择根系强大的挺水植物

3. 容器选择

选择透明的玻璃容器,观赏性比较好
鱼缸

  • 选择 5 面超白长方体鱼缸,尽量避免异形鱼缸
  • 尺寸(cm 长宽高):60x45x45、90x45x45、120x45x45、等等常规尺寸

龟缸

  • 和鱼缸类似
  • 入门也可以考虑乌龟周转箱

4. 过滤选择

俗话说,养鱼先养水。
要想养好水,就需要过滤器材。

关于自来水
开缸尽量不要用自来水,
使用自来水最好是晾晒 2 天,
水质稳定之后,换水的时候也可以直接注入少量自来水。

4.1 建议

使用过滤器

  • 上滤:安全、方便,不美观
  • 底滤:有点麻烦,美观
  • 侧滤:有点麻烦,不美观
  • 滤桶:安全,美观,不够强大

尽量增大水体体积
尽量增大水与滤材的接触面积(过滤效果好)

  • 装更多的滤材
  • 使用接触面积更大的滤材

滤材表面积大利于细菌进行生长繁殖,可保持干净稳定的水。
滤材选择:表面积越大越好,一般比较贵;便宜的可以以量取胜。

4.2 背景知识

异养细菌,把食物残渣、动物粪便等,转化成无机物,氨气(对动物有害)等。
硝化细菌,把氨气转换为硝酸盐(对动物无害),可以供植物吸收。

水循环大概过程:
『有机物(残渣、粪便)』 一异养细菌一> 『氨(有害)等其它无害物质』 一硝化细菌一> 『硝酸』 一> 『植物吸收』

4.2.1 详细解释

硝化细菌:属于生产者,包括亚硝酸细菌(将氨氧化成亚硝酸),硝酸细菌(将亚硝酸氧化成硝酸),
硝化细菌(英语:nitrifying bacteria)是一群好氧的化能自养生物之统称,属于生产者,
细菌能通过食用无机氮化合物生长。硝化细菌以二氧化碳为碳源,通过代谢将氨或铵盐氧化成硝酸盐。

自养生物(英语:autotroph)也称为生产者(producer)
指食物链底端可以利用阳光(光合作用)或无机物氧化配以地热能(化能合成)
将空气和周边环境中的二氧化碳、水以及无机盐等制造成有机物来提供代谢底物并存储化学能的生物,
主要包括绿色植物、海藻和少数微生物(浮游植物)。

异养生物(英语:heterotroph),也称为消费者( consumer)
指不能直接利用无机物或有机物维生,必须摄取现成的养分来维持生存机能的生物。

5. 参考

]]>
- - - - - 其它 - - - - - - - 水族 - - - -
- - - - - 北京联通-光猫改桥接-并开启IPv6 - - /20220604-bj-unicom-optical-modem-change-to-bridge-and-set-ipv6/ - - 0. 背景

现有的长城 100M 宽带太寒酸了
之前的住户说想升级光纤都没办法
于是乎咨询了下联通可否装光纤
答案是可以,换个套餐就行

桥接的好处就是,光猫只做光猫该做的事情。
其它事情由自己的路由器进行设置并且管理。
这样即使你搬家什么的,路由器搬走,
任何设备都不用动,包括固定内网 IP 等
我主要的点是固定内网 IP,因为我由 NAS

1. 操作

1.1 改为桥接

这一步是需要联系安装宽带的师傅
他们远程可以直接修改
如果改为桥接,那么原有网络无法使用

重点

  • 联系师傅索要宽带账号密码
  • 要安装师傅修改光猫为桥接
  • 修改之后进光猫后台较麻烦(不过用不到,都在路由器操作了)

1.2 路由器拨号

我的路由器是网件 R8000,输入账号密码等配置生效。
完事之后就可以上午,但是,这时候 IPv6 是不好使的。

1.3 IPv6

找到 IPv6 相关设置(高级—高级设置—IPv6)
选择 PPPoE,使用 IPv4 一样的账号密码拨号
等待配置生效,即可发现 IPv6 正常
IPv6 测试网址:test-ipv6.com

至于为什么要 IPv6,那只能说懂得都懂,啊哈哈。

2. 参考

桥接上网
这篇文章的一些观点我可能不太认同,
最重要的是,里面提到可以联系客服修改为桥接
这就很方便了,网上很多都是各种破解,挺麻烦。

电信光猫用桥接模式好还是路由模式好?

]]>
- - - - - 其它 - - - - - - - IPv6 - - ip - - - -
- - - - - shell & expect 实现 MySQL 命令行自动登录 - - /20220603-shell-and-expect-for-mysql-auto-login/ - - 0. 背景

由于目前经常需要登录数据库查询相关数据
每次进行登录一系列操作,有点费劲
于是乎想着看看怎么自动化

限制:需要在堡垒机进行操作

1. 知识

1.1 expcet

expect是一个自动化交互套件,
主要应用于执行命令和程序时,
系统以交互形式要求输入指定字符串,实现交互通信。

1.2 awk

Awk是一种便于使用且表达能力强的程序设计语言,
可应用于各种计算和数据处理任务。
入门指南

2. 脚本

2.1 自动登录脚本

说明:基于 expect
文件:mysqlLogin.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr//bin/expect -f
set ip [lindex $argv 0]
set port [lindex $argv 1]
set user [lindex $argv 2]
set password [lindex $argv 3]

spawn mysql -h$ip -u$user -P$port -p
set timeout 1000
expect "Enter password:"
send "$password\r"
expect ">"
send "use xxx_database\r"
interact

2.2 获取信息并登录

说明:通过 shell 动态获取登录信息
文件:autologin.sh

1
2
3
4
5
6
7
8
9
#!/usr/bin/bash
ips=`get_single_ip_by_domain hisen.me`
ip=($(echo $ips | awk '{print $2}'))
port=($(echo $ips | awk '{print $3}'))
echo "+---------------------------+"
echo "| Login to MySQL offline |"
echo "+---------------------------+"
# 调用上面的脚本,包含 4 个参数
expect /root/soft/login/mysqlLogin.sh $ip $port user pwd

2.3 快捷命令设置

设置完成之后,输入 ma 命令,即可自动登录到 MySQL 客户端

1
2
cat ~/.bashrc
alias ma='sh /root/soft/login/autologin.sh'

3. 参考文档

]]>
- - - - - shell - - - - - - - mysql - - shell - - - -
- - - - - 百度地图 API - 地址 转 经纬度 - - /20220528-baidu-map-api-address-to-longitude-and-latitude/ - - 0. 背景

某做科研的朋友
需要对一些地点的坐标
然后在 WGS-84 坐标系的底图上呈现相关内容

BD09 vs WGS84
北京韩美林艺术馆,BD09:116.68347847243588,39.88148624000483
北京韩美林艺术馆,WGS84:116.67097966259838,39.87446583754102

1. 准备

1.1 寻找成品

找了几个网址,反馈说之前用过坐标不准确

1.2 使用百度地图 API

1.2.1 注册百度地图开放平台

服务免费
需要实名认证
网址:lbsyun.baidu.com

1.2.2 创建应用

我没有选择 IP 白名单
选择的是 SN 签名
获取 AK、SK

1.2.3 文档

接口文档
之前 V2 版本的接口已经停用了
地址:地理编码文档
SN 生成
注意文档的生成方式还是 V2 的例子
地址:SN 生成文档

2. 代码

简单说明
地址文件格式一行一个
转换后会输出文件:地址,精度,纬度

CoordinateTransformUtil 在参考文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
* Baidu.com Inc.
* Copyright (c) 2022 All Rights Reserved.
*/
package com.hisen.api.baidu.map;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.joda.time.DateTime;
import org.springframework.util.DigestUtils;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* @author hisenyuan
* @date 2022/5/28 10:55
*/
public class SnCal {
private static final String URL = "https://api.map.baidu.com";
private static final String PATH = "/geocoding/v3/?";
/**
* 百度地图 AK
*/
private static final String AK = "your ak";
/**
* 百度地图 SK
*/
private static final String SK = "your sk";
private static final String LINE_BREAK = "\n";
private static final String FORMAT = "yyyyMMdd_HH_mm_ss_SSS";

public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 需要转换的文件,一行一个
String file = "/Users/hisenyuan/Downloads/address.txt";
// 结果输出文件夹
String resDir = "/Users/hisenyuan/Downloads/";
File resFile = getResFile(resDir);
// 读取文件
List<String> addressList = getAddressList(file);
for (int i = 0; i < addressList.size(); i++) {
convertLocation(resFile, addressList, i);
System.out.println("current line:" + (i + 1));
}
long use = System.currentTimeMillis() - start;
// 打印耗时
System.out.println("time us(ms):" + use);
}

private static void convertLocation(File resFile, List<String> addressList, int index) throws IOException {
String address = addressList.get(index);
String reqUrl = getReqUrl(address);
// 返回结果
String res = sendUrlGet(reqUrl);
double[] doubles = getWgs84(res);
String wgsRes = address + "," + doubles[0] + "," + doubles[1];
Files.append(wgsRes + LINE_BREAK, resFile, Charsets.UTF_8);
}

private static List<String> getAddressList(String file) throws IOException {
List<String> addressList = Files.readLines(new File(file), StandardCharsets.UTF_8);
System.out.println("sum line:" + addressList.size());
return addressList;
}

private static File getResFile(String resDir) throws IOException {
DateTime dateTime = new DateTime();
File resFile = new File(resDir + "处理结果_" + dateTime.toString(FORMAT) + ".txt");
if (!resFile.exists()) {
boolean newFile = resFile.createNewFile();
System.out.println("create file, result:" + (newFile ? "success" : "fail"));
}
return resFile;
}

private static double[] getWgs84(String res) {
JSONObject jsonObject = JSON.parseObject(res);
JSONObject result = jsonObject.getJSONObject("result");
JSONObject location = result.getJSONObject("location");
Double lng = location.getDouble("lng");
Double lat = location.getDouble("lat");
return CoordinateTransformUtil.bd09towgs84(lng, lat);
}

private static String getReqUrl(String address) throws UnsupportedEncodingException {
Map<String, String> paramsMap = new LinkedHashMap<>();
paramsMap.put("address", address);
paramsMap.put("output", "json");
paramsMap.put("ak", AK);
// 参数值转为 UTF-8
String paramsStr = toQueryString(paramsMap);
// 参与 MD5 计算的参数
String wholeStr = PATH + paramsStr + SK;
// UTF-8
String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
// Md5 摘要( SN 计算)
String md5 = md5(tempStr);
// 请求地址
return URL + PATH + paramsStr + "&sn=" + md5;
}

/**
* 值 UTF-8
*/
public static String toQueryString(Map<?, ?> data)
throws UnsupportedEncodingException {
StringBuilder queryString = new StringBuilder();
for (Map.Entry<?, ?> pair : data.entrySet()) {
queryString.append(pair.getKey()).append("=");
queryString
.append(URLEncoder.encode((String) pair.getValue(), StandardCharsets.UTF_8.name()))
.append("&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
return queryString.toString();
}

/**
* md5 摘要
*/
public static String md5(String str) {
return DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8));
}

/**
* 发起 http 请求
*
* @param url 请求地址
* @return 返回数据
*/
public static String sendUrlGet(String url) {
String responseContent = null;
try {
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = client.execute(httpGet);
HttpEntity resEntity = response.getEntity();
responseContent = EntityUtils.toString(resEntity, StandardCharsets.UTF_8);
// close resources
response.close();
client.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return responseContent;
}
}

3. 参考

注意,第一个文档的接口已经过时,新申请的用户无法使用

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Redis 开发与运维 - - /20220523-redis_dev_ops/ - - 零、背景

鉴于目前 Redis 使用广泛
虽然数据结构、API 比较简单
但是想使用好,还是有一定难度
建议了解下《Redis 开发与运维》
以达到知其然且知其所以然的境界

一、使用场景

大规模互联网在线应用
流量比较大,响应时间要求高
Redis 作为一款流行的高性能数据库

对于读多写少的场景,一般作为缓存使用
某大型电商订单系统读写比大概在 10:1
并且订单读取 90% 的流量都是创建订单当天查询

对于可容忍丢数据,但是对性能有极致要求,
比如优惠券发放,流量高,这种情况下当做 DB 用也挺好。

二、问题与建议

2.1 常见问题

  • 网络抖动导致 redis 操作失败
  • 定时任务清理过期 key 导致 IOPS 高
    • redis 做了二次开发,更激进的惰性删除( 针对大面积过期场景 )
  • 热 key 导致流量倾斜
    • 考虑 Server 端旁路监听,做统计,然后推送到客户端做内存缓存
    • 京东有开源热 Key 方案
  • redis slot 分配不均匀,导致某节点提前内存告警
    • 建议分配内存按 slot 分配,而不是节点

2.2 建议

  • 缓存时间动态可配
    • 上线后根据业务灵活调整,节约资源
    • 大促过程中,扩容来不及,可以根据趋势缩短缓存时间,避免淘汰
  • 使用 ProtoStuff 之类的高效序列化工具
  • 使用 snappy 等高效压缩算法

三、阅读摘抄

《Redis开发与运维》20 1121~1122

dbsize 获取当前数据库中键总数,复杂度 O(1)

keys * 复杂度 O(n)

type key 返回 redis 外部数据结构

object encoding key 返回 redis 内部编码实现

redis 为单线程模型,所有命令都进队列。

redis 单线程为何还这么快?

  1. 纯内存访问,内存响应约 100 ns,快的基础
  2. 非阻塞 I/O,事件驱动
  3. 单线程避免了线程切换和竞态产生的消耗

锁和线程切换通常是性能杀手。

单线程可以简化数据结构和算法。

由于是单线程,所以对每个命令执行时间有要求。
redis 是面向快速执行场景的数据库。

mget 结果是按照传入键的顺序返回。

redis 中每个中文占 3 个字节
随说:utf-8 汉字 3 ,ascii unicode 2

字符串类型的内部编码有 3 种:

  1. int:8 字节的长整形
  2. embstr:≤ 39 字节的字符串
  3. raw:> 39 字节的字符串

list 的 lrange 操作获取指定索引范围的元素。

  • 索引下标从左到右是 0~N-1
  • 索引下标从右到左是 -1~-N
  • 并且 end 包含了自身,很多编程语言不包含

对于字符串类型的键,再次执行 set 命令会将上次的过期时间去掉。

纯内存存储、I/O多路复用技术、单线程架构是 redis 高性能的三个因素。

了解每个命令的时间复杂度在

redis 命令真正执行的时间通常在微妙级别,所以才会有 redis 性能瓶颈是网络的说法。

原生批命令与 pipeline 的区别:

  1. 原生批命令是原子的,pipeline 是非原子的
  2. 原生一命令多 key,pipeline 支持多命令
  3. 原生是 redis 服务端实现,pipeline 客户端+服务端 实现

pipeline 只能操作一个 redis 实例,但即使在分布式 redis 场景中,也可以作为批量操作的重要优化手段。

redis 3.2 版本提供了 geo 功能,支持 LBS 服务。
geo 功能是 redis 借鉴了 ardb 功能实现,ardb 的作者来自中国。

输出缓冲区由两部分组成:

  1. 固定缓冲区 16KB,返回比较小的执行结果
  2. 动态缓冲区,列表实现,返回较大的结果

RESP(Redis Serialization Protocol Redis),协议。
保证客户端与服务端的正常通信,是各种编程语言开发客户端的基础。

jedis 没有内置序列化工具,需要自己选用。
page 121 有使用 protostuff (protobuf的Java客户端)进行操作的例子。
随说:这块 21 年出实际场景测试过,时间、空间复杂度优化幅度都蛮大

理解 redis 通信原理和建立完善的监控系统对快速定位客户端常见问题非常有帮助。

redis 默认采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小。

正常情况下,fork 操作耗时应该是每 GB 消耗 20 毫秒左右。

redis 是 CPU 密集型服务,不要做绑定单核 CPU 操作。
由于子进程非常消耗 CPU,会和父进程产生单核资源竞争。

子进程通过 fork 操作产生,理论上需要两倍的内存来完成持久化,
但 Linux 有写时复制机制(copy-on-write),
父子进程会共享相同的物理内存页,
当父进程处理写请求时会把要修改的页创建副本,
而子进程在 fork 操作过程中共享整个父进程内存快照。

复制功能是高可用 redis 的基础。

主从复制延迟,redis 提供了 repl-disable-tcp-nodelay 参数控制是否关闭,
TCP_NODELAY,默认为 no,即开启 TCP_NODELAY 功能,
这时主节点会合并较小的 TCP 数据包节省带宽但是增大了主从之间的延迟,
适用于跨机房部署。如果为 yes,那么会立即发送,适合同机房网络。

对于写并发量较高的场景,
多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,
同时也加重了主节点的负载影响服务稳定性。

psync 2.8 以上支持,有全量复制,部分复制
需要组件支持:

  1. 主从节点各自复制偏移量
  2. 主节点复制积压缓冲区
  3. 主节点运行 id

redis 支持无盘复制,repl-diskless-sync,试验阶段,
rdb 文件不保存到硬盘而直接通过网络发送给从节点。

为了降低主从延迟,
一般把 redis 主从部署在同机房 / 同城机房,
避免网络延迟和网络分区造成的心跳中断等情况。

对于读写分离,会造成从节点数据延迟,
可以编写外部监控程序监听主从节点的复制偏移量,
当延迟较大时出发报警或者通知客户端避免读取延迟过高的从节点。

为了保证复制一致性,从节点自身永远不会主动删除超时数据。

建议做读写分离之前,
可以考虑使用 redis cluster 等分布式解决方案,
这样不止扩展了读性能还可以扩展写性能和可支撑数据规模,
并且一致性和故障转移也可以得到保证,
对客户端的维护逻辑也相对容易。读写分离成本太高

redis-cli –bigkeys
把历史扫描过的最大对象统计出来,分析优化

带宽瓶颈通常出现在以下几个方面:

  1. 机器网卡带宽
  2. 机架交换机带宽
  3. 房之间专线带宽

网络快慢:同物理机 > 同机架 > 跨机架 > 同机房 > 同城机房 > 异地机房
随说:但它们的容灾性正好相反

redis 进程的内存消耗主要包括:自身内存 + 对象内存 + 缓冲内存 + 内存碎片。

对象内存是 redis 内存占用最大的一块,存储着用户所有的数据。

输入输出缓冲区在大流量的场景中容易失控,造成 redis 内存的不稳定,需要重点关注。

由于进程内保存大量的键,
维护每个键精准的过期删除机制会导致消耗大量的 CPU,
对于单线程的 redis 来说成本过高,
因此 redis 采用惰性删除和定时任务删除机制实现过期键的内存回收。

在高并发放场景下,建议字符串长度控制在 39 字节以内,
以减少 redisObject 内存分配次数,从而提高性能。

尽量减少字符串频繁修改操作如 append、setrange,
改为直接使用 set 修改字符串,降低预分配带来的内存浪费和内存碎片化。

redis sentinel 有一套合理的监控机来判断节点不可达,有三个定时任务:

  1. 每 10s 向主节点发送 info 信息获取最新拓扑
  2. 每 2s 向 sentinel 频道发送对主节点的判断以及自身的信息
  3. 每 1s sentinel 向主节点、从节点、其余 sentinel 发送 ping 做心跳检测,来确认节点是否可达。

redis 使用 raft 算法实现领导者的选举。

redis sentinel 是 redis 的高可用方案实现:故障发现、故障自动转移、配置中心、客户端通知

redis sentinel 模式下,客户端初始化连接的是 sentinel 节点集合,
不再是具体的 redis 节点,但 sentinel 只是配置中心不是代理。

redis cluster 是 redis 的分布式解决方案。当遇到单机内存、并发、流量等瓶颈时,
可以采用 cluster 架构方案达到负载均衡的目的。

redis cluster 之前的分布式方案:

  1. 客户端分片,客户端比较重,需要处理路由、高可用、故障转移等问题。
  2. 代理方案,部署麻烦,性能损耗。

数据分区是分布式存储的核心,
理解和灵活运用数据分区规则对于掌握 redis cluster 非常有帮助。

redis cluster 限制:

  1. 批量操作支持有限,目前 mget、mset 只支持具有相同 slot 值的 key 执行批量操作。
  2. key 事务操作支持有限
  3. key为数据分区最小力度,hash list 在单机
  4. 不支持多数据库空间
  5. 复制结构只支持一层,不支持嵌套树状

在分布式存储中需要提供维护节点元数据信息的机制,
所谓元数据是指:节点负责哪些数据,是否出现故障等状态信息。

常见的元数据维护方式分为:集中式、P2P方式

redis cluster 采用 P2P 的 Gossip(流言) 协议,
Gossip 协议工作原理就是节点彼此不断通信交换信息,
一段时间之后所有节点都会知道集群完整信息。

虽然 Gossip 协议的信息交换机制具有天然的分布式特性,但他是有成本的。
redis 集群内节点通信频率为每秒 10次。单次通信消息 > 2KB,
消息体携带的数据量根集群节点数息息相关,更大的集群代表更大的通信成本。
因此对于 redis 集群来说并不是大而全的集群更好,需要对集群的规模做限制。
随说:大规模集群可以考虑使用哨兵模式、或者拆分 redis cluster

集群内 Gossip 消息通信本身会消耗带宽,官方建议集群最大规模在 1000 以内。

redis cluster 可以实现对节点的灵活上下线控制。
其中原理可以抽象为槽和对应数据在不同节点之间的灵活移动。

集群伸缩=槽和数据在节点之间移动。

MOVED 重定向:在集群模式下 redis 接收 key 相关命令先计算对应的槽,再根据槽找到节点,
如果节点是自身,则处理命令,否则返回 MOVED 重定向错误,通知客户端请求正确的节点。

键使用大括号包含的内容又叫做 hash_tag,它提供不同的键可以具备相同的 slot 功能,
常用于 redis I/O 优化。在集群模式下实现 mget mset pipeline 等操作。

smart 客户端通过在内部维护
slot → node 的映射关系,本地就可以实现
key → node 的查找,从而保证 I/O 效率低最大化,而 MOVED 重定向负责协助 smart 客户端更新 slot → node 映射。

jediscluster 解析 cluster slots 结果缓存到本地,为每个节点创建唯一的 jedispool 连接池。

cluster slots 风暴:

  1. 重试机制导致 I/O 通信放大问题
  2. 个别节点异常导致频繁获取 slots 信息
  3. 频繁触发更新本地 slots 缓存操作,内部用了写锁,阻塞对集群所有的命令调用。

针对以上问题,jedis 2.8.2 版本做了改进:

  1. 当接收到 JedisConnectionException 时不再轻易初始化 slots 缓存,大幅降低内部 I/O 次数。
  2. 当更新 slots 缓存时,不再使用 ping 检测节点活跃度,并且使用 redis covering 变量保证同一时刻只有一个线程更新 slots 缓存,其它线程忽略,优化了写锁阻塞和 cluster slots 调用次数

jedis cluster 中由于 key 分布在不同的节点上,会造成无法实现 mget、mset 等功能。
但是可以利用 CRC16 计算出 key → slot,以及 smart 客户端保存 slot → node 的特性,
将属于同一个 redis 节点的 key 进行归类,
然后分别对每个节点对应的子 key 列表执行 mget 或者 pipeline 操作。

ASK 与 MOVED 都是对客户端重定向控制,ASK 表示集群正在进行 slot 迁移,
不知道什么时候完成。MOVED 明确表示 slot 对应的节点,因此需要更新 slot 缓存。

配置纪元的应用场景有:

  1. 新节点加入
  2. 槽节点映射冲突检测
  3. 从节点投票选举冲突检测

配置纪元的主要作用:

  1. 标示集群内每个主节点的不同版本和当前集群最大版本。
  2. 每次集群发生重要事件,都会递增纪元
  3. 大的纪元表示更新的状态

唯品会开发的 redis-migrate-tool 支持在线迁移,
采用多线程加速,提供数据校验和查看迁移状态等功能。
随说:在 github 开源了

缓存更新策略最佳实践

  1. 低一致性业务建议配置最大内存和淘汰策略
  2. 高一致性业务结合 expire 和主动更新

缓存粒度问题

  1. 全部字段缓存通用易维护,报文大性能低
  2. 部分字段缓存通用性低,维护复杂,高效
    随说:考虑使用 hash 全量存,按需取

使用布隆过滤器减少缓存击穿,page 351
随说:订单业务很难做到,特别是订单量大

无底洞:投入越多不一定产出越多

  1. 客户端一次批量操作会涉及多次网络开销,节点越多越耗时
  2. 网络连接数变多,对性能也会有影响

无底洞优化

  1. 命令本身的优化,例如优化 SQL 语句
  2. 减少网络通信次数
  3. 降低接入成本,使用连接池 / NIO 等
  4. 客户端 n 次 get,n 次网络 + n 次 get 命令
  5. 客户端 1 次 pipeline get,1 网络 + n get
  6. 客户端 1 次 mget,1 网络 + 1 mget
    随说:以上针对单个 redis 节点,非 cluster

NTP 是一种保证不同时钟一致性的服务。

redis cluster | sentinel 如果多个节点时间不一致,
会影响日志排查,但不会影响功能,节点依赖各自的时钟。

redis 公网无密码情况下,黑客可以写入公钥,通过设置 rdb 文件目录到 /root/.ssh 目录,
并且文件名设置为 authorized_keys 即可实现免密登录。爆破主机。

redis bind 参数指定的是 redis 和哪个网卡绑定(建议绑定内网),和客户端什么网段没有关系。

bigkey,一般字符串类型 value 超过 10KB 就是 bigkey 了,但这个值和具体的 OPS 相关。

redis 查看 key 的大小:debug object key
看其中的 serializedlength 即可。

]]>
- - - - - database - - - - - - - java - - redis - - - -
- - - - - 初识 Spring AOP 与 BeanPostProcess - - /20220326-Get-to-know-spring-AOP-and-BeanPostProcess/ - - 零、背景

最近在做重构的项目
进入阶段性收尾阶段
总结记录下相关的内容
方便大家遇到类似问题可以想起有某个地方可以参考

一、初识 AOP

目前的 AOP 应用,由于公司生态体系不够完善
利用 AOP + ThreadLocal(transmittable-thread-local,ttl)
做一部分链路追踪的事情( 耗时打印,traceId 处理 )
这部分倒是很简单,只是之前用的很少

PS:链路追踪蛮重要,针对排查问题,性能监控等大有帮助,多关注开源协议/实现( 如 CNCF )

二、初识 BeanPostProcess

2.1 应用场景

目前做的项目当中,有一个 IDC 负载均衡的工具
有设置当前机器所属机房的方法,但是没有提供从配置文件读取的能力
因为 IDC 路由不生效,会造成跨机房访问,导致访问延迟偏高( 回头再写性能优化相关内容 )

当时发现这个问题的时候已经上线,以稳定性为主,不想升级 jar 版本
于是乎当时想通过 AOP 或者字节码的方式去做一个增强

当然,由于知识浅薄,AOP 写对了,但是不生效,工具初始化的先于 AOP 的作用时间。

接下来就通过搜索引起各种找,偶然发现 BeanPostProcess (之前也通过一些 spring 回调做应用预热)

于是就开始一番操作,果然给解决问题了。

2.2 代码概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Idc implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ClientFactory) {
ClientFactory clientFactory = (ClientFactory) bean;
String idc = System.getenv("IDC");
if (StringUtils.isNotBlank(idc)) {
clientFactory.setIdc(idc);
}
clientFactory.setIdc("nj");
}
return bean;
}
}

2.2 BeanPostProcess 文档

BeanPostProcess API

三、总结

之前一直在做业务开发
也很少做从零到一的项目
即使看过一些 spring 的书籍和文档
但是还是没有理解太深,没有太注意
果然是书到用时方恨少哇,以后还是万事深挖一步

spring 生命周期,启动流程,各种回调,有必要看看,跑 demo 试试。

四、参考文档

]]>
- - - - - java - - - - - - - java - - spring - - - -
- - - - - 优雅地分桶 - 数据分片 - list 拆分 - - /20220218-divide-barrels-gracefully/ - - 零、背景

最近在做数据迁移
为了加速迁移速度
其中就需要把查询到的数据( max 100 条)
拆分成 5 份,然后执行 5 个子任务,加速处理

一、代码

1.1 常规做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static <T> List<List<T>> split2(List<T> lists, int subCount) {
if (CollectionUtils.isEmpty(lists) || subCount < 1) {
return null;
}

List<List<T>> result = new ArrayList<>();

int size = lists.size();
int count = (size + subCount - 1) / subCount;

for (int i = 0; i < count; i++) {
List<T> subList = lists.subList(i * subCount, (Math.min((i + 1) * subCount, size)));
result.add(subList);
}
return result;
}

1.2 新颖做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static <T> List<List<T>> split(List<T> lists, int subCount) {
if (CollectionUtils.isEmpty(lists) || subCount < 1) {
return null;
}
// 简约的计算分桶次数,避免判断是否有余数,再+1
int splitTimes = (lists.size() + subCount - 1) / subCount;
return Stream
// 每次递增指定子列表个数
.iterate(0, n -> n + subCount)
// 限制循环次数
.limit(splitTimes)
// 转换成想要的结果
.map(item -> lists.stream()
// 跳过之前的下标
.skip(item)
// 限制每次的个数为子表个数
.limit(subCount)
// 转换为子列表
.collect(Collectors.toList()))
// 转换为主列表
.collect(Collectors.toList());
}

三、性能

3.1 结果

从结果可以看出,一味地追求新颖也不是好事,需要知其然
java 8 的 stream 并不占优势,性能相差好几个数量级…

1
2
3
Benchmark              Mode  Cnt       Score       Error  Units
ListSplit.split2Test thrpt 10 306050.155 ± 24646.689 ops/s
ListSplit.splitTest thrpt 10 353.048 ± 122.423 ops/s

3.3 压测代码

压测教程:JMH 使用参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import com.google.api.client.util.Lists;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* @author hisenyuan
* @date 2022/2/18 20:05
*/
// 默认的 State,每个测试线程分配一个实例
@State(Scope.Thread)
// 如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。
@Fork(1)
// 预热的次数 3 次基准测试(不是执行三次方法)
@Warmup(iterations = 1)
// 基准执行次数 10 次(参数含义同上)
@Measurement(iterations = 10)
@BenchmarkMode(Mode.Throughput)
public class ListSplit {
private static final List<Integer> INTEGERS = Lists.newArrayList();
@Setup
public void init() {
List<Integer> list = Stream.iterate(0, n -> n + 1)
.limit(1000)
.collect(Collectors.toList());
INTEGERS.addAll(list);
}

@Benchmark
public void splitTest(){
List<List<Integer>> listList = split(INTEGERS, 3);
}
@Benchmark
public void split2Test(){
List<List<Integer>> listList = split2(INTEGERS, 3);
}

public static <T> List<List<T>> split(List<T> lists, int subCount) {
// 简约的计算分桶次数,避免判断是否有余数,再+1
int splitTimes = (lists.size() + subCount - 1) / subCount;
return Stream
// 每次递增指定子列表个数
.iterate(0, n -> n + subCount)
// 限制循环次数
.limit(splitTimes)
// 转换成想要的结果
.map(item -> lists.stream()
// 跳过之前的下标
.skip(item)
// 限制每次的个数为子表个数
.limit(subCount)
// 转换为子列表
.collect(Collectors.toList()))
// 转换为主列表
.collect(Collectors.toList());
}

public static <T> List<List<T>> split2(List<T> list, int len) {
if (CollectionUtils.isEmpty(list) || len < 1) {
return null;
}

List<List<T>> result = new ArrayList<>();

int size = list.size();
int count = (size + len - 1) / len;

for (int i = 0; i < count; i++) {
List<T> subList = list.subList(i * len, (Math.min((i + 1) * len, size)));
result.add(subList);
}
return result;
}

四、后话

其中不错的一点我觉得是

1
int splitTimes = (lists.size() + subCount - 1) / subCount;

这样巧妙的计算,避免了先取模,后判断是否有余数的繁杂。
这个不是我想到的,也不知道怎么通过数学证明,但是这样就是对的 ==

ps:是谁说数学没用的 ????

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 关于学习 Rust - - /20220206-about-learning-rust/ - - 零、背景

过年期间,看到 github 关注的人的动态当中
有人 star 了这个项目 Rust语言圣经
这些年也听说过 Rust,一直没有特意去了解
当我看到大佬也关注了这个课程的时候
感觉应该是一个不错的学习资料

前面介绍学习的好处让我心动了
就跟着学习了前面 4 节课
目前感觉良好
有空继续学

不得不说,环境搭建特别方便!

一、实践

1.1 code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
fn greet_world(){
let southern_germany = "Grüß Gott!";
let chinese = "世界,你好";
let english = "World, hello";
let regions = [southern_germany, chinese, english];
for region in regions.iter() {
println!("{}", &region);
}
}

fn more_than_hello(){
let penguin_data = "\
common name,length (cm)
Little penguin,33
Yellow-eyed penguin,65
Fiordland penguin,60
Invalid,data
";

let records = penguin_data.lines();

for (i, record) in records.enumerate() {
if i == 0 || record.trim().len() == 0 {
continue;
}

let fields: Vec<_> = record
.split(",")
.map(|field| field.trim())
.collect();

if cfg!(debug_assertions) {
eprintln!("debug: {:?} -> {:?}", record, fields);
}

let name = fields[0];
if let Ok(length) = fields[1].parse::<f32>() {
println!("{}, {}cm", name, length);
}
}
}

fn main() {
greet_world();
more_than_hello();
}

1.2 out put

1
2
3
4
5
6
7
8
9
$ cargo run --release
Finished release [optimized] target(s) in 0.02s
Running `target/release/world_hello`
Grüß Gott!
世界,你好
World, hello
Little penguin, 33cm
Yellow-eyed penguin, 65cm
Fiordland penguin, 60cm

二、资源

]]>
- - - - - rust - - - - - - - rust - - - -
- - - - - 回顾 2021,展望 2022 - - /20220102-Looking-back-to-2021-looking-forward-to-2022/ - - 零、摘要

回顾 2021

  1. 【完成】换了一份不错的工作;
  2. 【完成】坚持阅读,追求质量;
  3. 【待办】运动量不够,锻炼偏少;

展望 2022

  1. 体重控制在 65Kg 以内,学会自由泳;
  2. 持续阅读,技术与非技术书籍交替看,5 本基础书;
  3. 深入理解公司内部技术栈、深入了解 2-3 条业务线;
  4. 重新学习 Dubbo,学习设计理念,以及分布式系统架构;

一、成长

技术方面今年感觉成长不是太明显
有所改变的是会深入洞察需求
逐步完善设计之后开始 coding,而不是着急开始。

对于技术底层也有一定地涉猎,更多地可能是在钻电商业务。

二、阅读

本年度阅读图书 38 本
由于上半年部分时间在准备换工作
以及 6 月份换了工作之后花在工作的时间比较多
所以今年的阅读量下降地厉害,但是还是会坚持每天都翻几页

收获较多的为以下几本:

  • 《置身事内》
  • 《幕后产品》
  • 《商业的本质和互联网》
  • 《曾国藩的正面与侧面》
  • 《分析与思考:黄奇帆复旦经济课》

全部书单

三、生活

唯一的变化可能是多了 2 只鳄鱼龟。
夜深人静之时,回到家,给乌龟喂食互动下也挺有意思。

四、习惯

下半年由于工作原因,晚睡较多,很少早起。
需要找准自己的节奏,调节步伐,逐步早起。

五、随说

  1. 做个正常人,不做圣人;
  2. 力所能及地帮助身边人;
  3. 多关注家乡的教育和经济,有机会可以出一把力;

六、推荐

这些年能坚持下来的也就只有下面两个了~
其它的可能不太适合长时间跟踪

  1. 科技爱好者周刊
  2. GitHub 月度流行项目
]]>
- - - - - 随说 - - - - - - - 随说 - - - -
- - - - - 关于 Java static - - /20211112-about-static/ - - 零、背景

同事分享《Effective Java》
其中第十章,并发部分例子有争议
变量是否需要(代码如下) static?
几个大佬说需要加,我众目睽睽下反驳不需要,略尴尬

// 是否需要加 static?才能保证单例正确
private volatile Singleton singleton;

一、程序关键代码

1.1 原程序(错误)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
private volatile Singleton singleton;

public Singleton() {
}

private Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

1.2 正确程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public class Singleton {
private static volatile Singleton singleton;

public Singleton() {
}

private Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

二、验证

2.1 验证程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Singleton {
private static final AtomicInteger COUNT = new AtomicInteger(0);
// 通过volatile关键字来确保安全
private volatile Singleton singleton;

public Singleton() {
}

private Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
System.out.println("new:" + COUNT.addAndGet(1));
singleton = new Singleton();
}
}
}
return singleton;
}

@Test
public void test() {
int taskCount = 700;
// 锁住所有线程,等待并发执行
final CountDownLatch begin = new CountDownLatch(1);

final ExecutorService exec = Executors.newFixedThreadPool(taskCount);

for (int index = 0; index < taskCount; index++) {
submitTask(begin, exec);
}
System.out.println("开始执行");
// begin 减1 ,开始并发执行
begin.countDown();
//关闭执行
exec.shutdown();
}

private void submitTask(CountDownLatch begin, ExecutorService exec) {
Runnable run = () -> {
try {
// 等待,所有一起执行
begin.await();
//开始模拟等待。。。
Singleton singleton = new Singleton();
Singleton instance = singleton.getInstance();
System.out.println(Objects.isNull(instance));
} catch (InterruptedException e) {
e.printStackTrace();
}
};
exec.submit(run);
}
}

2.2 验证结果

带 static 的结果符合预期
对象只创建一次,并且返回结果均不为 null

2.2.1 不带 static

开始执行
new:1
new:2
new:3
false
true
省略几十行….

2.2.2 带 static

开始执行
new:1
false

三、原因分析

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

关键:static 变量在内存中只有一个副本,由所有对象共享。

四、参考

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 陆奇 | 年轻人的榜样 - - /20211109-about-luqi/ - - 零、背景

不知在哪听说过陆奇的传说
对于一位地位如此之高的华人
甚是敬仰,奈何相关资料甚少,很难深入了解
不像李开复、吴军那样,出过一些书,了解可以多些

榜样的力量或许很虚,关键看自己能悟多少,是否坚持行动。共勉!

一、工程领导(Engineering Leadership)

  • Believe in 技术
    未来任何一个工业都会变成软件工业。
  • 站在巨人的肩膀上做创新
    你所写的每一行代码是否值得?
  • 追求 Engineering Excellence
    追求工程技术的卓越的能力。
  • 每天学习
    每天学习,每天都争取变地更好。
    学经济、学产品、懂商业、懂生态。
  • Ownership
    把公司的事业,当成是自己的事业,own everything。从我做起,从身边的每一件事做起。

二、关于陆奇的文章

2.1 摘要

  • 20岁:做让自己走得快的事情
  • 30岁:做让自己走得远的事情:搭建系统
    • 身体系统
    • 家庭系统
    • 人脉系统
  • 40岁:找一个可以让你淋漓尽致发挥的舞台

2.2 原文

2.2.1 介绍

大部分人还不了解陆奇,但他值得让我们好好了解一下:

这几年陆奇被格外关注,始于2017年1月17日他被百度任命为百度总裁。这次任命份量极高,到今天百度创立19年,李彦宏给出如此高的权力,陆奇是唯一一个。

但陆奇值得。

陆奇在硅谷非常有名,拥有不错的人脉和江湖地位,凡接触过陆奇的人提起他,几乎是众口一词地称赞。

1998年陆奇加入雅虎,2007年任雅虎执行副总裁。

2008年他辞职时,杨致远当场洒泪,告别会上所有工程师穿上统一的T恤,T恤上印着“我曾与陆奇一起工作,你呢?”,以此纪念陆奇在雅虎的日子。

2008年加入微软,任全球执行副总裁,这是有史以来,华人在科技领域获得的最高职位。

2016年9月离职时,比尔·盖茨极力挽留甚至承诺:“你想要做什么业务,我们去搞个业务给你。或者你先休假一年两年,然后再回来当首席技术官,我们等着你就是了。”

微软现任 CEO 纳德拉曾对微软员工说:“五个人,对微软贡献巨大。一是创始人比尔·盖茨,二是CEO史蒂夫·鲍尔默,三是董事会主席约翰·汤普森,四是诗人奥斯卡·王尔德,最后一个就是陆奇。”

其实早在2005年前后,李彦宏就曾试图说服陆奇加入百度,只是当时并未如愿。

2017年陆奇加入百度后,李彦宏曾公开给了他很高的评价:陆奇上上下下有口皆碑,大家都很喜欢他,他有非常强的技术能力,又有很强的管理能力,并且工作极其玩命。

陆奇身上有太多值得我们学习的地方。

2.2.2 采访

前段时间《晚点》采访时就问陆奇:

你对20、30、40岁的年轻人各有什么建议?

陆奇:

20岁需要做让你可以走得很快的事情,快速学、快速失败。30岁你要让自己可以走得远,建立一个核心支撑体系能让你走得很远。这个体系包括你的身体、你厚实的家庭基础和几个志同道合你可以信任的朋友。

一个人到了35岁,到了打造产品的黄金时段,我已前很关注这个年龄阶段的人才,因为他/她已经犯过不少错,他/她最需要做一个好产品让他/她的职业生涯有一个本质提升。

40岁后,理想情况是找到一个可以让你淋漓尽致去发挥的舞台,一个人的才华和一个公司的才华只有在真正被释放的情况下才能实现它的价值。如果这个舞台是你自己的最好。

这个回答很经典,我反复看了很多遍。但要真的看懂看透,我们还必须要放在陆奇的整个人生系统里看,我一口气翻看了所有关于陆奇重要的中文报道,从中得到了一个更完整清晰的答案。

20岁:做让自己走得快的事情

陆奇:“20岁需要做让你可以走得很快的事情,快速学、快速失败。”

1、20多岁,习得什么能力最重要?

陆奇认为有两个:

学习能力:在这个变化速度越来越快的世界里,拥有学习和持续学习的能力,不断提高自己在某一个专业领域以及在企业内开发产品和业务的认知能力是基础。

原因很简单,因为创新,世界变化的速度越来越快。唯一的应对,是与它共跑甚至赶超。方法就是学习,要持续不断的学习,让自己拥有更多更深的专业知识和技能,更强的在企业内开发产品和业务的能力。

交流能力:这是一个人与人之间愈发紧密联结的世界,通过各种数字通讯服务与工具,社交网络,人们能够以文字、图片乃至视频,与更多的人保持交流。

从长远角度看,一个人越擅长结识他人,表达自己,形成相互学习并共同完成某些理想的关系和友谊,对这个人就越好。

2、20多岁,去创业公司还是大公司?

陆奇认为,要看个人追求。

如果你的长远理想是自己开公司,那加入创业公司,甚至直接创业,都是最好的选择。加入创业公司或自己创业会愈发成为年轻人学习并拥有真正创新能力的重要途径。因为在创业企业,每天做的事就是为了生存而战斗,一个人的真实作战能力测试来的很快。创新或死亡,是创业企业每天面临的现实。就学习和人才发展角度,一个人能获得这样的学习机会是非常宝贵的。

如果你的长远理想是成为一家公司某个职能的高管,那就进入一家可以提供你学习和成长机会的大企业。

但陆奇认为,以上仅限于少部分清楚的知道自己长远目标的人,他对女儿说:大部分人在25岁之前,对于人生想要做什么,其实只有模糊的感觉。

所以,20多岁,要学会快速失败,尝试,反馈,改变。把自己的时间投资在更大的学习发展空间机会中,可能为未来创造更高的收入。

3、陆奇的20多岁,在做什么?

读书,以及继续读书。

1978年,全国恢复高考,17岁的陆奇埋头狠狠啃了两年书,考入复旦大学计算机专业。读书期间,陆奇特别用功。他的同学们回忆说:

“他瘦瘦小小的身影,每天穿行在教室和图书馆之间,夜里图书馆熄灯,他才穿过农田,回到另一区的寝室。”

“他背着大书包在校园里穿梭,在林荫道上反反复复背着单词。他是全年级最瘦小的男生,但扛着全年级最大的书包。”

凭着超凡的努力,陆奇顺利考取复旦的研究生。凭着读研期间优异的成绩,他毕业后留校任教。

勤奋学习和扎实的专业知识带给陆奇的远不止一份稳定而又体面的工作,还包括更多的人生选项。

1989 年,卡耐基梅隆大学教授克拉克到复旦交流讲学,讲学时间选在了周末。不巧地是,当天的天气很不给力,暴雨倾盆,周末加坏天气使得来听讲座的学生寥寥无几。校方为了不让教授尴尬,就安排周末留校的师生去听讲座,因为暴雨取消回家计划的陆奇就是其中之一。

克拉克教授讲完后,让在场师生提问。陆奇抓住这个机会,接连向教授提了几个专业问题,克拉克听完,立刻就对这个提问的复旦老师的学术水平刮目相看。讲座过后,他专门翻阅了陆奇写的论文,看过之后,克拉克就邀请陆奇去卡耐基梅隆大学读博。

那年陆奇28岁,有点犹豫:“在大学当老师已经是很不错的职业了”。

克拉克只回了一句:“你是鹰,不应该局限在笼子里。”

陆奇去了美国。

走上卡耐基梅隆大学这个更大的平台,才有了陆奇对计算机科学更深入的研究与理解、才有了陆奇与李开复的相识以及后面的开挂人生。

4、59岁的陆奇还在试图重构知识体系,20多岁怎能不学?

2016年陆奇在硅谷接受采访时说:“最近几年我重新觉悟——你必须要重新学习,以前学的东西不光过时了,而且现在很多理论包括物理学都已经有了全新的认识,所有你要从根本上重新构建知识、认识世界。”

陆奇从微软离职原因之一是练习倒骑自行车摔伤了腿,那辆车是他与同事改造的、反向骑行的自行车,骑车时人和身体反应全部是倒置,所以要忘记过去学习到的全部经验。

就连之前选择加入YC,其中一个原因也是学习:我还希望我的工作不能只利用过往的经验,而是要每天学新东西。不学新东西的话,就没什么乐趣。

这几年陆奇观察到人才市场里一个非常重要的宏观趋势,整体的需求从原来的技能驱动型人才,越来越往知识和创新驱动型人才转移。所以他认为,一个人的能否快速学习,并将学到的知识应用于创造新的价值也变得愈发重要。

现在他仍旧保持着每天学习英语、阅读计算机领域最前沿论文的习惯。他说:“我把自己想象成一个软件,今天的版本一定要比昨天的版本好,明天的版本一定要比今天的版本好。”

年近60岁,陆奇还试图重构知识体系,20多岁的我们又怎么能不学习?

30岁:做让自己走得远的事情:搭建系统

陆奇说:30岁你要让自己可以走得远。建立一个核心支撑体系,能让你走得很远。这个体系包括你的身体、你厚实的家庭基础和几个志同道合、可以信任的朋友。

陆奇是一个寻求最优解的人,方式就是建立系统。

任何一个问题他都有系统的固定方式去解决——就是把它变成一个非常理性化、可以拆解成任务的方程。

比如,陆奇的人生里几乎看不冒险,因为他只把机会成本的8%用来冒险,就算这8%,他还是会建立一个决策系统,理性决定——快速反馈——如果方向不对,立即掉头甚至放弃。

比如,看待自己在微软的贡献,他不计较几个产品的得失,还是要回到系统上:比起一个产品的成败,帮助企业建造长期的创新的生命力才是最重要的。

我们再看一下他上面着重提到的三个系统。

1、身体系统

陆奇极度自律,坚持了十几年早上4点起床。

过去十几年他是这样的:“4点起来先弄Email,弄完了以后跑步,跑30分钟,洗个澡,去办公室。跑步第一对呼吸系统是很好的锻炼,第二可以出一身汗,第三是我可以边跑边听书不浪费时间,有时候我会把PPT放在跑步机上翻着看。”

从微软离开后有所变化,他现在对自己的生活效率很不满意,因为骑反向自行车把腿摔伤后,跑步根本不行了,目前他正在找一个让自己感觉每天都很顺畅的方式。

不过他依然精力旺盛,百度期间,同事说他每日长时间工作,无论周末、假期,无论在北京还是在国外,只要开会,他总是准时出现在视频会议的另外一端。他有依然有自己严格的作息制度,有自己严格的生活习惯,也有非常严格的饮食习惯。

陆奇认为,每个人有不同的身体状况,他自己的做法不建议其他人学习。但是他有一套核心的方法论可以介绍给大家:

找到问题的核心,即:无论创业还是在大企业里从业,在工作中如何进行时间管理来让产出最大化,同时也能获得自我满足?

再找到核心的解决办法,即:

设计一个“马拉松快步跑”的时间管理方法和心态。

第一:要意识到这是一场马拉松,不是一场短跑。

第二:这场马拉松的速度需要很快,因为现实世界中,任何高价值的东西——创业公司、大企业的好岗位等——都会有非常激烈的竞争,你需要保持速度并持续领先。

设计这样一个工作节奏和时间管理方式,很类似在高速公路开车。

陆奇说:“你需要保持一个均匀的高速,然后时不时的加速一下,再回到之前均匀的高速。你要避免过度频繁的加速、减速。就像一辆车,如果一直都是高速前进,只是偶尔减速一下,这对与一辆车的损耗是很低的。但如果一辆车过度频繁的突然加速、减速,会对这辆车带来巨大损耗,不用多久车就可能垮掉。

因此,你需要设计属于你自己的一个工作和生活节奏,这种节奏是你可以保持住的高速,而这个高速可以给你带来最大的效率。同时,你也需要设计这个日程节奏,让它可以应对突发变化,可以时不时的冲刺一下,比如偶尔过度加班让工作在截止日期前完成,然后迅速回归之前的速度。必须避免经常性的透支,经常性的拼命追赶截止日期,经常性的处于疲累状态。身体和精神上偶尔透支可以补回,不可长期透支。”

跑一个高效率、可持续、并且可以应对临时突发状况的马拉松才是关键。

2、家庭系统

陆奇的家庭很少出现在媒体中,公开信息绝少,但陆奇在讲搭建系统时特意提到“厚实的家庭基础”,说明他的个人成功受“家庭系统”支持颇多。

2018年5月8日,百度宣布陆奇因“个人和家庭原因”离开百度。

这其实是拿陆奇最不可能的原因当了最常见的公关话术,陆奇的家庭一直是全力支持陆奇做他想做的事业,百度的一位高层说,陆奇就职微软期间,其长期在美国西雅图工作,家人为了陆奇就定居硅谷,“他早就把家庭的事情处理好了”。

陆奇回国加入百度后,为了照顾陆奇的饮食起居,陆奇家人曾一度迁居北京,百度还为陆奇的夫人配备了出入百度大厦的证件。

2018年8月16日,YC媒体沟通会上,主持人官宣陆奇加盟YC担任YC中国首任CEO。陆奇和太太、女儿都在现场。

陆奇的太太说:“我们有一半的时间在美国,他去哪里,我们就跟到哪里。”

陆奇现场致辞的最后,也特别感谢了他的太太和家人。而且说希望在美国、中国多花时间,为了家庭。

写这部分内容时,我突然想起小晚的《晚点》团队采访陆奇时曾问过这么一个问题:你是否经常会太过相信自己的力量?

陆奇说:会。我太太一直觉得我太过于自信了。她老觉得我自以为是。

这就是最好的支持吧,即便有时认为你的理想“不现实”,但只要你想做,我愿意陪你。这就是陆奇所说的“厚实的家庭基础”对一个人事业的重要性吧,如果你的梦想和成长能得到最亲近的人的支持,应该是最幸福的事了。

3、人脉系统

陆奇的人脉有多强?

陆奇的人脉有多好,我相信在开头介绍中,那些顶级大佬的评价就是最好的答案,接触过的人都愿意帮他,合作过的人都愿意捧他。

这里再讲几个点。

1996年,陆奇博士毕业,摆在他面前的有三个选项:回国;华尔街;硅谷。

他不知道该如何选择,就去请教李开复自己该去哪里、进哪个公司工作,李开复给出了自己的建议:“去硅谷吧,找一家技术类的公司。”

于是陆奇就去了IBM的Almaden研究实验室,在那里工作了两年。

之后,陆奇跳槽到雅虎,李开复又一次提醒他:“雅虎的股价不会一直在这个水平上,它要么会上涨5倍,要么下跌5倍。”

陆奇职业生涯的两次关键时刻,李开复都给出了自己的建议和判断,一次让他看清了自己的优势和发展方向,一次让他看清了一家公司的发展空间。

2008年,微软收购雅虎未果,当时已在微软任职的师兄沈向阳向鲍尔默引荐了陆奇,于是有了之后鲍尔默与陆奇长达6个小时的会谈。就这样,微软成功挖到了雅虎搜索引擎业务第一人——陆奇,陆奇也开启了他在微软的执行副总裁生涯。

沈向阳的引荐,让陆奇成为“硅谷最有权势的华人”。

再到2019年,YC撤出中国,陆奇独立运营新基金奇绩创坛,很快首期美元基金募集完成约1亿美金,出资人都是谁呢?

陆奇的朋友们,阵容相当豪华:比尔盖茨、孙正义、红杉中国、高瓴资本、北极光创投等。

陆奇如何看待人脉?

中国人常说,做成一件事就是:天时地利人和。

人和就是人脉。陆奇喜欢那句“机会总是留给有准备的人”,但他喜欢那句“机会是留给广结良友的人。”

陆奇说:我个人的经验里,除了准备充分,你所拥有的人脉的宽度和广度也很重要,要通过交流结识更多人,最好是拥有不同职业背景的人,并跟他们保持关系)。美国有一句谚语,你获得一份工作不是因为你知道什么,而是因为你认识谁。这句话在某种角度上其实很正确,至少在我个人的经验里,这句话很正确。

陆奇为什么有如此好的人脉资源?

这句按照查理芒格的说法,当然是陆奇配得上。

他做了什么让自己配得上?

我们来分析几点。

陆奇的人品。

小晚的团队采访他时,多次试图让他聊百度,我们看陆奇的表现。

比如问“回头来看,加入百度是一个错误的选择吗?”,陆奇回答“不好意思,我真的不想讲百度”。

记者追问“你可以谈微软,但是避免谈百度,为什么?”,陆奇回答“是职业道德,我不想为它带来任何Distraction(干扰)”。

我看完这句,佩服。

谈及老东家YC,陆奇说:我个人很感激YC,我一定要强调这一点,他们送我们到了他们能送到的最远的地方。

记者问:看着雅虎一步步走向衰落,为什么没有更早的离开?

陆奇答:因为我答应杨致远做10年。当时雅虎进入了一个危机,杨致远找我说,中国有一个传统,朋友有难的时候不应该离开。我说那我就不离开了。

膜拜。

陆奇的情商。

小晚团队问:你心中优秀的CEO是什么样子?你的榜样是谁?

陆奇答:最强的人愿意为你而来,这是我觉得最好的CEO。在中国我觉得马云做得不错。

后面的一个问题中,他特意提及了张小龙,“张小龙是我非常敬仰的人。中国的移动生态走得很远,很大因素是微信,微信可能是当代做得最出色的一个产品”。

赞美阿里的组织,赞美腾讯的产品。

陆奇的品质。

一个浑身闪耀着发光品质的人怎么会没朋友?

陆奇身上的优秀品质太多,略举一二。

比如准时。自媒体人辉哥曾和陆奇在百度共事,他说某次出差,他们先到楼下大堂等,距离预定出发的时间还有1分钟时,他有一些着急,问要不要给陆奇打个电话。熟悉他的人说“他会准时的”,话音刚落,电梯门打开,陆奇出现,一分不差他极度守时。

比如来信必回。无论是邮件还是微信,你只要发给他,他一定会回复,时间不确定,有时候是凌晨1-2点,有时是早晨5-6点左右。

比如永远正向。辉哥说,工作中永远难免会遇到困难,但陆奇任何时候都保持正向的态度。无论是在公司开会,还是做大会的 MC,或是回邮件,永远是用简短有力,但充满了力量和鼓舞人上进的精神。大家最累最困难的时候,只要陆奇在那里振臂一挥,大家顿时又像打了鸡血。

40岁:找一个可以让你淋漓尽致发挥的舞台

陆奇:“40岁后,理想情况是找到一个可以让你淋漓尽致去发挥的舞台,一个人的才华和一个公司的才华只有在真正被释放的情况下才能实现它的价值。如果这个舞台是你自己的最好。”

陆奇说过一句广为流传的话:

“人生不是线性的,不要以为一班车就能把你从现在的位置带到你自己所期望的位置。”

什么阶段做什么,40岁以后,就要找到一个可以让你淋漓尽致发挥的舞台。

为什么不是20岁?

前文也说了,陆奇认为那个阶段,绝大部分人对于人生想要做什么,其实只有模糊的感觉。

而且陆奇认为,20多岁你要考虑一些现实因素,比如财务方面,工作收入需要满足自己生活的需求以及其他潜在财务责任,比如资助父母兄妹等,理想情况下,还可以有一些存款。

20多岁多学多做多试,30多岁搭建系统,正是为了40岁以后真正有属于自己的舞台。

微软属于他的舞台,但不够,所以他离开,回到中国,找更合适的舞台。

比尔·盖茨一直挽留他,说:百度能给你什么,我都给你。

陆奇说:你不能给我中国。

中国是他现在更好的舞台,一个更大意义上的舞台,他要借助一家合适的平台在这个舞台上跳舞。很可惜,百度没能最终成为那个合适的舞台。后来他加入YC,很可惜YC也没最终成为那个合适的舞台。

后来陆奇认为:属于自己的舞台,如果真的“属于自己”,那是最好的。

“如果你想真正大规模改变世界,那你必须是这个企业的创始人,否则你永远受限于你的雇主。当这个平台是你亲手建造,那你可以发挥的能量和范围是最充分的。”

陆奇的的签名是:“Do more, know more, be more”。

这大概就是追求“be more”吧,40岁以后,自我更重要了。

这个问题,《晚点》和陆奇的对话很精彩:

《晚点》:甘地说,be the change you want to see in the world。成为更好的自我和建立一个更好的世界,后者难道不会推动人走得更远吗?

陆奇:自我才会让人更永久。赚很多钱、建设一个成功公司,甚至建造一个国家、世界,都是外在目标。如果一个目标是外在的,你永远会在达到目标之后变得一片空虚。

《晚点》:有一些商人,他们的目标就是赢,他们乐此不疲,似乎并不空虚。

陆奇:你的追求是建立在别人输的基础之上,为什么世界上一定要有人输你才觉得你的生命是有价值的?我认为人的目标是你自己而不是任何外在的因素。

如今他终于有了属于自己的舞台,奇绩创坛,也真的“属于自己”。

三、参考

]]>
- - - - - 成长 - - - - - - - cs - - - -
- - - - - 介绍 | RASP - - /20211105-RASP-introduce/ - - 一、背景

翻看书签的时候偶然发现 RASP 两个收藏
脑子里毫无相关内容,细看之下,收获不小
ps:是不是看看收藏夹,能有意外收获

安全无小事,从点滴做起。要有安全意识
安全投入的比例,可以根据公司所处的行业公司规模进行相应调整。
安全投入包括:软件安全设计、招人、买设备、买服务、做评估。

二、介绍

2.1 RASP 是什么

RASP( Runtime Application Self-Protection )是一种在运行时检测攻击并且进行自我保护的一种技术。
OpenRASP 架构

2.2 RASP 优缺点

化繁为简,抓住事情的本质,这就是优势。

做一件事,方式方法有很多,最终的目的都一样。
对于安全来说,攻击方式千变万化,但是”攻击动作”万变不离其宗。
RASP 通过监控”攻击动作”进行自我保护,而不是对攻击方式的识别与防御。

2.2.1 优势

  • 通过 hook,深度监控应用执行。
  • 运行时自动识别处理用户输入威胁。
  • 以 Agent 方式运行,不侵入应用程序。

攻击动作

  • 文件上传
  • 文件读取
  • 文件写入
  • 文件重命名
  • 文件遍历
  • 命令执行
  • 反序列化
  • …更多请看

2.2.2 劣势

  • 性能有一定影响,单次请求影响 1~7 ms

2.3 RASP 与 WAF、IDS

当发生 SQL 注入攻击时,WAF 和 IDS 只能看到 HTTP 请求。
而 RASP 不但能看到完整 SQL 语句,还可以和 HTTP 请求关联,
并结合语义引擎、用户输入识别等能力,实现对 SQL 注入的检测。
ps:WAF 可以理解为安全网关,门卫。IDS 入侵检测系统。

三、参考

]]>
- - - - - 安全 - - - - - - - java - - soft - - - -
- - - - - Java JVM 工具与知识 | JVM 调优 - - /20211009-jvm-tool-and-knowledge/ - - 一、背景

曾经整理过一个帖子,不过是在公司内网。
今天突然想起,博客上零散的 jvm 相关内容,
但未系统整理相关知识和工具,遂写一篇文章。

学习的过程需要不断发现好的资源,深入钻研某个领域。

二、知识

2.1 图书

  • 《深入理解 Java 虚拟机》
  • 《Java 性能优化权威指南》
  • 《性能之巅》

2.2 文档

2.3 文章

三、工具

]]>
- - - - - java - - - - - - - java - - jvm - - - -
- - - - - Java 文件读取,部分中文乱码 - 分析与解决 - - /20210731-File-reading-part-of-Chinese-garbled-code/ - - 一、背景

最近做项目有一个地址库文件需要放在后端
由于文件在 jar 包中的问题,一些读取文件的姿势失效(方便的 Guava Files)
最后通过 getResourceAsStream 解决

接下来遇到了一件奇怪的事情,部分汉字乱码了,
调整编码,重新编辑汉字都试过了,无法解决。

最后求助于百度搜索,得到了一些有效的信息。
汉字是两个字节的,如果每次读固定个字节,可能会把汉字截断,造成乱码。
再次印证了基础知识的重要性!

二、相关代码

2.1 罪魁祸首

利用缓冲区读取文件,会出现边界情况下把汉字分割成两次来读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static String getAddressJson(String path) throws IOException {
log.info("cacheFile start. PATH:{}", path);
InputStream resourceAsStream = Address.class.getClassLoader().getResourceAsStream(path);
StringBuilder sb = new StringBuilder();
byte[] buf = new byte[10240];
int length;

while ((length = Objects.requireNonNull(resourceAsStream).read(buf)) != -1) {
// 此处 new String 放进去了一半中文字符,导致乱码
sb.append(new String(buf, 0, length, StandardCharsets.UTF_8));
}
resourceAsStream.close();

return sb.toString();
}

2.2 完美运行

据说现在很多人没法纯手写通过流读取文件了…(说的就是我!)

1
2
3
4
5
6
7
8
9
10
11
12
13
private static String getAddressJson(String path) throws IOException {
log.info("cacheFile start. PATH:{}", path);
InputStream resourceAsStream = Address.class.getClassLoader().getResourceAsStream(path);
StringBuilder sb = new StringBuilder();
InputStreamReader isr = new InputStreamReader(Objects.requireNonNull(resourceAsStream));
BufferedReader br = new BufferedReader(isr);
String newLine;
while ((newLine = br.readLine()) != null) {
sb.append(newLine);
}
resourceAsStream.close();
return sb.toString();
}

三、参考文章

CSDN

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 二进制的应用 - - /20210726-use-of-binary/ - - 一、背景

之前京东组里有同事使用二进制优化支付密码打标性能(大促 QPS 数百万),节省内存资源。
随说:存二进制报文小,传输快,反序列化快(之前存 JSON 对象),节省缓存。

目前公司遇到个套餐打标,也通过二进制实现简单高效得解决掉了。
随说:目前倒不是要求性能,只是这么设计扩展性好,操作简单。

知识点:二进制、与运算

二、设计

打标,无非就是识别某个东西是不是包含某些属性。
那么有什么好的办法能做到通用与高效?
如果固定映射,扩展性不好,查询逻辑费劲,存储成本偏高。

目前相对较好的方案是通过二进制位来做标记,再结合与运算,快速找出数据。
随说:Java MySQL 均支持与运算

套餐VIP1VIP2VIP3标记值
A1117
B1106
C1004

如上表所示,相应套餐的购买资格标记。

A 套餐所有会有均可购买
B 套餐 VIP3 不能购买
C 套餐仅 VIP1 可购买

对相关标记进行入库处理:A=7,B=6,C=4;

正常思维,需要存三个字段,没有扩展性,性能还差。
使用二进制,结合与运算,降本(计算、传输、匹配)增效(性能提升)。

三、代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
Integer a = 7;
Integer b = 6;
Integer c = 4;
List<Integer> suit = Lists.newArrayList(a, b, c);
System.out.printf("支持 VIP1 (0100=4)的套餐:" + Arrays.toString(suit.stream().filter(e -> (4 & e) != 0).toArray()));
System.out.printf("支持 VIP2 (0010=2)的套餐:" + Arrays.toString(suit.stream().filter(e -> (2 & e) != 0).toArray()));
System.out.printf("支持 VIP3 (0001=1)的套餐:" + Arrays.toString(suit.stream().filter(e -> (1 & e) != 0).toArray()));

// 支持 VIP1 (0100=4)的套餐:[7, 6, 4]
// 支持 VIP2 (0010=2)的套餐:[7, 6]
// 支持 VIP3 (0001=1)的套餐:[7]
}

四、总结

系统流量小的时候
粗糙烂制也不是不能用
但是当系统流量大了就得想办法优化:CPU、传输、存储
很多时候往往利用简单的原理解决大的问题,只是很多时候限于认知不知道可以这么用。

说到底还是要知其然,更要知其所以然。

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 关于互联网医疗 - - /20210718-about-internet-medicine/ - - 零、背景

由于近期换工作,停下了技术书籍,去了解行业
《移动健康和智慧医疗》算是互联网医疗的科普资料
前面部分的内容已经后面部分国际案例,了解之用足够
里面提到的『量化自我』,如果把世界量化分析,岂不是美哉?
随说:前提是大家相信分析出来的结论,以及按建议行事。

一、微博读后感

《移动健康和智慧医疗》0711~0717
过去人口红利式的告诉发展逐渐降速
老龄化突显使疾病预防和控制更重要

医疗信息化建设提升医疗系统效率
医疗数据收集与分析改进医疗方案
多维度健康数据分析建议促进健康

减少医疗信息不对称
降低患者再次入院率
早运动早发现早治疗
有效地减少医疗支出

互联网医疗典型方向:

  1. 促进健康
  2. 慢性病管理
  3. 诊断治疗(非急症性疾病)
  4. 院外康复指导和干预

二、摘抄(医疗相关)

《移动健康和智慧医疗》0711~0717
过去中国高速发展,目前人口红利逐渐消失,老龄化日益突显,使得以传染病为主,转向非传染性疾病的预防和控制 。

利用现代技术加强相关人员交流和互动

  1. 加强医疗保健机构的信息化程度,降本增效
  2. 从 cure 为主转化为 cure + care,减少病患
  3. 从生到死一条龙健康医护服务,预防为主

慢性病管理
血糖控制,通过监测行为特征,配合辅助功能,加强用户掌控能力,医生,家人远程支持与提醒。
随说:收集足够信息,给出专业建议

骨科手术后居家康复指导,尽快出院,获取足够信息与支持。
随说:给予高质量内容,提升自我恢复能力。

服务设计、实现、患者使用过程,均反应出一系列信息技术和预防、临床医学(流程、知识)之间的相互作用和支持,提现了互联网+多学科交叉,跨行业深度融合的思想。
随说:降本增效,沉淀数据,线上线下无缝对接

互联网医疗典型方向:健康促进、慢性病管理、诊断治疗、术后康复。

健康医疗APP重要因素:专业性、相关性、有效性、趣味性、社交化。
随说:社交?相互鼓励,交流心得

医疗云平台:数据汇集分发、电子健康档案、业务管理、安全体系、运维系统。

对多元异构多模态大数据进行处理、分析、挖掘,从中获取新知识和洞察,优化经营、管理,提升患者体验,提取最佳临床路径,辅助临床决策,实行计算机自动筛查和诊断,为其它利益相关方提供未知的信息资源等,这是互联网医疗追求的理想和目标。

研究发现我国近2/3接受糖尿病治疗的患者未能适当控制血糖,因此会出现各种并发症,如:心脏病、中风、失明、肾功能衰竭等。

2014年,慢性疾病导致的死亡占中国总死亡人数的 85%,导致的疾病负担占总疾病负担的 70%。

虽然中国经济的增长速度赢得了世界的瞩目,但是如何提高慢性病是综合防治能力,减少慢性病的发病率、致残率和死亡率,降低费用支出,仍是健康医疗服务需要应对的巨大挑战。

著名卫生经济学家普林斯顿大学教授 WR 提出,各国医疗体系的本质在于国家价值观和国家性格决定其系统如何运行。

低功耗可穿戴设备使得随时随地采集健康医疗数据成为可能。这些大量的、连续的、包含上下文情境的健康医疗数据,为健康医疗提供决策依据、促进健康生活方式的养成、改善疾病监护、诊治状况。

移动互联网具有用户身份、位置可识别,随时交互、多元数据可采集、用户可高度参与等一系列技术特征,使得移动互联网与其他行业融合时带来新的解决方案、服务模式和发展机遇。

医护路径全流程服务:促进健康、预防/慢性病管理、院前急救、诊断治疗、院外康复/干预。

院前急救:伤者历史数据、辅助诊断、车载数据同步医院,医院提前准备。

远程问诊成本低,灵活性强,出现一些早期症状可以及时获得指导。
随说:小毛病挂专家号,会诊几分钟…

量化自我:2007年凯文凯利提出,通过设备和技术持续跟踪、采集自己的生理心理特征,形成生活日志,探索身体健康的奥秘。

可穿戴设备的数据如果作为对患者实施诊断、治疗等环节的重要依据,则属于医疗器械,需要监管。

智能服装才是可穿戴设备的终极形式。

单点登录的目的是为了让多个相关联的应用使用相同的登录过程,代码复用,提升体验。

CAS 是开源的企业级单点登录解决方案。

根据密钥的职责和重要性,一般分为:主密钥、二级密钥、初级密钥。初级密钥是直接使用的密钥。高级密钥对低一级密钥做加密,保护低一级的密钥。

不同级别的密钥应该分开存放,最好是异地、异设备。

密钥不以明文形式存储在数据库或传输媒介中:避免密钥被截获后直接用来解密数据,增强安全性。

HIS:医院信息系统
PACS:影像系统
LIS:检验信息系统
RIS:放射信息系统
EMR:电子病历
HIMSS:美国医疗信息和管理协会
EMRAM:电子病历采纳模型
CHA:康体佳健康联盟

流式计算的数据来自一个最近的时间窗口,数据延迟短但精度可能较低。

一个完全测序的人类基因组包含 100~1000G的数据。

精准医疗:根据每个人的基因、生活环境、生活方式等的不同,提供相适应的个性化疾病治疗和预防

健康医疗大数据的收集、分析、整理和挖掘对于患者健康医护路径的不同环节都有非常重要的价值。

任何疾病最有效的治疗方式是预防保健。

每年心脑血管医疗费用占比搞,且再次入院率高,如何有效管理心血管病、节省医疗开支、降低出院患者再次入院率已经成为社会各界关注的重点。

美国的分级诊疗制度、医生多点职业、医疗信息化、商业保险、医院集团管理等一系列成熟体系,为美国移动健康医疗的发展提供了肥沃土壤。

远程医疗服务提供简单、方便、低成本的方式为患者远程诊治感冒、流感、喉咙痛或其它简单非急症性疾病。

2014 年 5 月,阿里旗下的支付宝推出“未来医院计划”,支付宝对医疗机构开放自己的平台能力,包括:账户体系、移动平台、支付及金融解决方案、云计算能力、大数据平台等,旨在优化患者在医院的就医流程,提高就医体验,提升医院的管理效率。

2015年阿里健康业务

  1. 药品零售
  2. 医疗服务(远程医疗平台,家庭医生等)
  3. 药监码(电子监管平台)
  4. 健康保险

北京大学人民医院是中国最具实力的三级甲等研究型医院之一,积极推进医疗信息化建设,2014 年通过了国际上衡量医院信息化水准的 HIMSS EMRAM 最高等级(7级)的评审,成为亚洲第二,中国第一家。

]]>
- - - - - read - - - - - - - read - - - -
- - - - - 读后感:《凤凰架构》 - - /20210613-reaction-of-the-fenix-project/ - - 一、简短感受

简单介绍,《凤凰架构》作者,周志明。
他最出名的书籍非《深入理解Java虚拟机》莫属了
书中了解到他对技术的态度,以及持续奋战一线值得学习,《程序员之路》值得一看(底部有链接)。

阅读时间:0531~0612
构建凤凰磐涅般的系统
介绍了一整套技术体系
穿插着技术的来龙去脉
着实是一部不错的书籍
架构的前提是足够了解
综合实际情况做出权衡
给人感觉不太像架构书
总的来说还是值得一看

用输出倒逼输入
这就是写博客等其它输出手段的作用

目前的软件没有烟囱式的,都是金字塔类型,所以底层基础要牢固!

二、部分摘抄

四层负载均衡的优势是性能高
主要工作在
数据链路层(2层),改写Mac地址
网络层(3层),改写 IP 地址

七层负载均衡的优势是功能强

将简单的校验交给 bean validation,
而把复杂校验留给自己,简直就是买株还珠。
可以使用自定义注解,优雅地解决校验问题。
检查校验项预置好默认提示信息。
基础校验直接放在 bean 属性上,业务的单开类。

能够使用确定的操作,促使状态间产生确定的转移结果的计算模型,在计算机科学中被称为状态机。

状态机特性:任何初始状态一样的状态机,如果执行的命令序列一样,则最终达到的状态也一样。(可用于分布式协商…)

广播指令、状态机复制。

让各个系统节点不受局部网络分区、机器崩溃、执行性能、其它因素影响,都能最终表现出整体一致的过程,被称为各个节点的协商共识。

一致性是指数据不同副本之间的差异,
共识是指达成一致性的方法与过程。

足可见技术圈里即使再有本事,也需要好好包装一下。paxos 论文发表三次后,被谷歌实现之后,凭着谷歌大拿的高度评价,获得了极大的关注。

操作转移模型
状态转移模型

达成共识的三步

  1. 如何选主
  2. 如何把数据复制到各个节点
  3. 如何保证过程是安全的

网关 = 路由器(基础职能) + 过滤器(可选职能)

打饭解释各种 I/O 模型
异步 I/O :下完外卖订单后干别的去,骑手送上门
同步 I/O
阻塞:打饭发现饭还没好,就一直干等着
非阻塞:打饭发现饭还没好,per 3min 看好了没
多路复用:同上,只是一个人可以处理多个请求
信号驱动:发现饭没好,让厨师好了通知你

BFF网关:backends for frontends,针对网关这种边缘节点,对同样的后端集群,裁剪、适配、聚合出不一样的前端服务,有助于后端的稳定,也有助于前端的赋能。

由于服务随时都有可能崩溃,因此快速的失败检测和自动恢复就显得至关重要。

lstio(mesh) 在 1.5 之前是使用微服务架构开发
模块如下

  1. mixer:鉴权策略与遥测
  2. pilot:对接 envoy 的数据平面,xds 策略分发
  3. galley:配置管理,提供外部配置感知能离
  4. citadel:安全加密,RABC 权限控制

微服务的目的是有效拆分应用,实现敏捷开发和部署。

凡事总该现有目的,有预期收益再谈行动才显得合理。

《没有银弹》:硬件的成本能够持续稳定地下降,而软件开发的成本则不可能。

系统的架构趋同于系统设计团队的沟通解构。
康威定律核心观点:沟通决定设计

大厂研发收到追捧,出除了企业本身的光环外(来自于哪里?),有大型系统浸染的经验,更有可能属于技术专家,也是其中的原因。

微服务对普通业务研发友好,但是对架构要求极高。

微服务的前提

  1. 决策者与执行者都能意识到康威定律在软件设计中的关键作用
  2. 组织中具备对微服务有充分理解、有一定实战经验的技术专家
  3. 追求以自治为目标的监控、度量能力
  4. 复杂性已经成为制约生产力的主要矛盾

需要想清楚做一件事的目的是什么?考虑 ROI

长期来看,多数服务端结局都是报废而非演进–Martin Fowler。
随说:良好设计的系统,应该是能够报废的,而非一味追求一步到位。

微服务拆分粒度
下界:独立(发布 / 部署 / 运行 / 测试),内聚(本地事务)
上界:2 披萨团队 (6~12)在一个研发周期完成全部需求。

治理,系统复杂性下的产物。

软件研发的认知负荷,本质上是来自技术的认知复杂度。

分布式系统早已放弃 Unix 所追求的简单性设计哲学。

治理架构腐化唯一有效的办法就是演进式设计。
开发过程中少妥协,否则会形成破窗效应。

Architect 架构师一词从建筑行业借鉴而来,让人容易误解架构师类似于给建筑设计骨架、绘制图纸的建筑架构师。演进式设计更像是“换房子”而不是“造房子”。

先进的生产力都伴随着更高的复杂性,需要有与生产力符合的生产关系来匹配,敏锐地捕捉到生产力的变化,随时调整生产关系,这才是架构师治理复杂性的终极方法。

1992 Java write once,run anywhere。
2018 graal vm,run programs faster anywhere。

目前 graal vm
优势:启动时间快,打包小,内存消耗少
劣势:无法无缝支持动态代理技术,延迟、吞吐量、可监控方面略差,性能劣于 hotspot jit 优化。毕竟是运行时优化,各种分支预测~

将思考具象化。
如果不把自己思考的内容输出给他人,
我们很容易就被自己所欺骗,
误以为自己已经理解得足够完备了。
随说:用输出倒逼输入。

三、图书资源

纸书作者应该还在初版中,目前看的是 PDF.
这本书属于开源书籍,开源地址:《凤凰架构》
凤凰架构附录的一篇文章不错:《程序员之路》

]]>
- - - - - read - - - - - - - read - - - -
- - - - - 关于学习英语 - - /20210605-About-learning-English/ - - 一、背景

作为一个软件开发工程师,日常很多机会和英文打交道。
特别是上一份工作,做全球支付项目,需要用到英文与国际友人沟通。
奈何自己的英语水平捉襟见肘,于是经常会有意地去收集相关的文章。

今天心血来潮,再次看了一下之前收藏的相关文章,感觉收获不少。
于是就想写一篇文章归集一下相关的内容,方便日后翻阅,顺便分享给有需要的人。

二、资源

复旦大学中文系教授严峰

程序员圈”名”人王垠 :

  • 解谜英语语法
    语法、动词比较重要
    推荐:练习造句。分析句子。
    语法树的概念蛮好理解,我让我明白了什么叫宾补~

  • 英语学习的一些经验
    语法和句子结构是关键,其次才是词汇量。
    如果你的词汇量足够阅读技术文档,那就可以开始看了。
    遇到不懂的单词,查询之,使用英英字典,记录在本子上,加强记忆。

GitHub 上的成功人士:

三、后话

还是得坚持看文档,背单词。

]]>
- - - - - 成长 - - - - - - - English - - - -
- - - - - Redis Cluster slot 分布不均匀问题排查 - - /20210518-troubleshooting-unbalanced-of-redis-cluster-slots/ - - 零、背景

在压测过程中发现有部分 redis cluster 节点内存占用比其它节点高(来自监控)

内存倾斜的隐患

  1. 更早达到容量瓶颈,触发淘汰策略
  2. 承担更多的负载( 读 / 写 ),导致节点压力增大,可能触发宕机。

一、问题

redis cluster slot 分配不均匀
redis cluster 集群内存分配算法的缺陷
问题算法:单节点内存 = 集群总内存 / 节点数

合理算法:单节点内存 = (集群总内容 / slot 数量) * 当前节点 slot 数量

二、排查

2.1 key 分布问题?

key 根据 crc16 计算之后比较均匀,排除 key 分布不均
在 key 均匀的情况下,考虑 slot 分配问题

2.2 slot 分布问题?

查找 slot 分布
redis 控制台执行:cluster slots
取最后一个节点的 rely 日志如下(日志含义见参考链接)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
(0)(0)10137
(1)10463
(2)(0)192.25.238.97
(1)2048
(2)1ab1735417e34e1fb38aa19958e2375498158be8
(3)(0)192.25.239.118
(1)2049
(2)2b444bb8ece6edcf4881cfacb60def2eb3bfba85
(1)(0)6540
(1)6866
(2)(0)192.168.184.118
(1)2048
(2)464c059fd4354b72cd277a7aaf6159a2682e47ff
(3)(0)192.168.198.232
(1)2048
(2)c4ae0536b5a9157b43516e26c4c77ca9e431005a
(2)(0)1308
(1)1634
(2)(0)192.168.197.41
(1)2048
(2)bb0b682b4eed16f773ed7b0d2cf430ac4e776624
(3)(0)192.25.222.250
(1)2048
(2)098251c97fb147b7aee8143ced13c742ec3115a9
(3)(0)8502
(1)8828
(2)(0)192.168.184.111
(1)2048
(2)3680ab0d0961e4dda6f9a35eb4c72cbcc04bba3b
(3)(0)192.168.176.75
(1)2048
(2)99d64ca4bf375460537a296ce0bdab2374c8487c
(4)(0)14388
(1)14714
(2)(0)192.25.239.116
(1)2048
(2)37d438fc43097bab896e04589b10659c2898c56b
(3)(0)192.168.184.113
(1)2048
(2)2697f554dac4c6b6452239b5f3f192052504431f
(5)(0)1962
(1)2288
(2)(0)192.168.198.241
(1)2049
(2)7c323af2d42df59cb172c1f2989fdb47972c4b6b
(3)(0)192.168.184.112
(1)2048
(2)94f67ff376efbecf3d84905dbfe6d9e8a5c9cf83
(6)(0)0
(1)326
(2)(0)192.168.197.9
(1)2048
(2)510edbf404ed2b24aeeff53f9d8f43c6404d62c0
(3)(0)192.168.198.249
(1)5010
(2)a150d1f3671d8a5e42c238e900a23031693ddd32
(7)(0)12426
(1)12752
(2)(0)192.168.198.240
(1)5010
(2)cb2c653239757df27da883dc024e87a8f11890f0
(3)(0)192.168.197.41
(1)2049
(2)ed4a2925e913bc2773cd3a4c13d48deac7f423fa
(8)(0)4251
(1)4577
(2)(0)192.25.239.105
(1)2048
(2)f2eb3bb1afc0d9345173d1eb2aaa675161f5d675
(3)(0)192.168.198.242
(1)2048
(2)67a2926615eb6e4e4f850f59f78f670010ef573a
(9)(0)9156
(1)9482
(2)(0)192.25.239.107
(1)5010
(2)4806e385ba1b193b48c0c6a8dd35e5d8a5738257
(3)(0)192.25.239.105
(1)2048
(2)c1490e0c9963628972be63339f032c63191f1897
(10)(0)6867
(1)7193
(2)(0)192.168.184.115
(1)2048
(2)2339d951c551c45daf84c147acaf6e6854f66f8c
(3)(0)192.25.222.250
(1)2049
(2)cd6e7d09828b8d795afe671c39a0312b3d6859fe
(11)(0)2616
(1)2942
(2)(0)192.168.198.234
(1)2048
(2)e5bfd81c7a0df2ec29b3ac4ce188795881b2bc1e
(3)(0)192.25.238.96
(1)2049
(2)513edbe433f180924500db83ef155426e7f2f040
(12)(0)5559
(1)5885
(2)(0)192.168.198.252
(1)2048
(2)515990729e830f0f75e3dc8179438d43c6c7c9b7
(3)(0)192.25.238.92
(1)2048
(2)5328403d6ecd5272c51250977cc1af7de3f16407
(13)(0)15369
(1)15695
(2)(0)192.168.176.78
(1)2049
(2)a140264d0aefdbb9b99c961c0c223266fa90129d
(3)(0)192.168.197.32
(1)2049
(2)5fbc13a478470e14006500e38fa032472ec0da50
(14)(0)12753
(1)13079
(2)(0)192.168.198.249
(1)2049
(2)120c04813a4f2db04bfc890874a5cf4e7ce47e15
(3)(0)192.168.197.9
(1)2048
(2)e8ca0053b9a99684f683202d68994cf8b28f26e0
(15)(0)4905
(1)5231
(2)(0)192.25.239.120
(1)2048
(2)e1a5f230ad1a7716c4e04bc785fcb001e7841b7f
(3)(0)192.168.176.76
(1)2049
(2)c370b87def3a5533ca436ac17ed22d2d5ef64cf7
(16)(0)15696
(1)16022
(2)(0)192.168.197.24
(1)2048
(2)ebad5e02b596b81431e7e35d80687b94eb4f495b
(3)(0)192.168.176.77
(1)5013
(2)96771bd26c4f0f3af5a1cb3de4de6467e07f1a6d
(17)(0)11118
(1)11444
(2)(0)192.168.198.147
(1)2049
(2)5e16b0661d43a03254fe0c9a97a21beada5665eb
(3)(0)192.168.184.109
(1)5010
(2)c132bdd8ab5c38d4cce4bee4ffc1a298e75d5538
(18)(0)16023
(1)16383
(2)(0)192.168.176.76
(1)2048
(2)e9b9b9354d6c77a980c0da35bef89b26042b716d
(3)(0)192.168.198.146
(1)2048
(2)1ca744d6e5c24d3f2c64eca1dec65eea8545ce8b
(19)(0)4578
(1)4904
(2)(0)192.25.239.104
(1)2048
(2)0aea965323c78cdefb3e8b4e03955b9651c719b6
(3)(0)192.168.198.240
(1)2049
(2)365350cfa7621749e2c696d84d32d2228122fdc6
(20)(0)10464
(1)10790
(2)(0)192.168.198.232
(1)2048
(2)2fe68c5065aefc24ddcc747071dd65c6cadd427b
(3)(0)192.168.184.118
(1)2049
(2)bdc2191de10b7d825f359922e3e31ce328c0a972
(21)(0)14061
(1)14387
(2)(0)192.168.198.231
(1)2049
(2)7a47f437951b03a95460924ffd349b3ecfb05bc7
(3)(0)192.168.184.114
(1)2048
(2)bfebcd3837ccf9df6989be91951087cb7351404c
(22)(0)9810
(1)10136
(2)(0)192.168.184.116
(1)2048
(2)3caff203db4214bea1f2bcbc2935bb3e2273ac72
(3)(0)192.25.223.2
(1)2048
(2)d2fe724704165a35eef6541f75cb2ba5245472fa
(23)(0)327
(1)653
(2)(0)192.25.238.90
(1)2049
(2)6254a81f003cce2cb66222dc312c0ad4ee0a900a
(3)(0)192.25.223.2
(1)2049
(2)7781d87ca77b51fcd07c8d0956ab350e3db3ba1c
(24)(0)9483
(1)9809
(2)(0)192.168.198.254
(1)2048
(2)a0acf25a9487c3fa6fc74af8ba57cc50d1b1a95c
(3)(0)192.25.239.119
(1)2048
(2)e05f23f53bad3f8ccc7ae4f9080426e289350557
(25)(0)12099
(1)12425
(2)(0)192.168.184.110
(1)2048
(2)39d6559849aa2d00b560cb554cfa79a30ee3ede1
(3)(0)192.168.198.247
(1)5010
(2)fe6696fa4333b4776ed5ee38b93380a21755eaa0
(26)(0)3924
(1)4250
(2)(0)192.168.198.180
(1)2048
(2)ba944b0643a2ad68033d129d78c1dee805d2191a
(3)(0)192.25.239.121
(1)2049
(2)b5b020b3105be1e4a49c57d54647da6797a0a3cc
(27)(0)14715
(1)15041
(2)(0)192.168.198.147
(1)2048
(2)937e58683236b2708fdc6ef77dd08e66ef7e7ad5
(3)(0)192.25.239.120
(1)2048
(2)274760f1e7e84bd14593b43f95965546f4cb34f5
(28)(0)1635
(1)1961
(2)(0)192.168.198.239
(1)2048
(2)ae4dc5d4d544dde49f177554c89e2001727d99e4
(3)(0)192.168.198.250
(1)2048
(2)bea5260033bac6fb7ab67a444f9a1d0e8c9f93b9
(29)(0)5886
(1)6212
(2)(0)192.25.239.116
(1)2049
(2)7c9f26573b5a79b77d9089ed7e5783f47f6d030d
(3)(0)192.25.239.104
(1)2048
(2)5c999bc681e6751ec576049db73c3bee2717f8b6
(30)(0)3270
(1)3596
(2)(0)192.168.198.179
(1)2048
(2)addeda3c397b1d6ca21996be33435f4a65ca7686
(3)(0)192.168.198.231
(1)5010
(2)1d78a81025acdc123f04680a30a80cff6258e909
(31)(0)2289
(1)2615
(2)(0)192.25.239.117
(1)2049
(2)b8ff018e1810697710963e0c4a17faca2b3b6421
(3)(0)192.168.184.111
(1)2049
(2)f7cc831c676ec19560f6ea779f86971bd227ba7f
(32)(0)6213
(1)6539
(2)(0)192.168.184.113
(1)2048
(2)ee943d8aec010e34fe1b573826d65cc9e1f14fce
(3)(0)192.168.198.242
(1)2049
(2)bfa424b2f9d4e476c3985025f5bf8bdb6625b74a
(33)(0)654
(1)980
(2)(0)192.25.239.106
(1)5010
(2)fa13eedd26af4e0364f56b1604cef9865afde98d
(3)(0)192.168.198.251
(1)2049
(2)ea87dd7cbf8b8cbeee37a3d482493b5fc41a755b
(34)(0)2943
(1)3269
(2)(0)192.25.239.118
(1)5010
(2)150932a7e4f0020b99c50312c8cec9d07d7aabcf
(3)(0)192.25.239.121
(1)5010
(2)816d511dcab3f7d9e2068401ba5958e01f2212dd
(35)(0)8175
(1)8501
(2)(0)192.168.198.148
(1)2048
(2)34d51673796ab652da7e312cff119296dae1d3bb
(3)(0)192.168.197.28
(1)2048
(2)e1f48544722bbb62d1fb8910a6a4b00d9ccbd09b
(36)(0)981
(1)1307
(2)(0)192.168.197.32
(1)5010
(2)41ea02b87652650c3ba2f085849fb6ac2169d897
(3)(0)192.168.197.28
(1)2049
(2)9717393eb89ab32126263741fd250198e378023c
(37)(0)5232
(1)5558
(2)(0)192.168.184.109
(1)2049
(2)96a728eece26f6f200b26ff9ddecae3fb5b8df11
(3)(0)192.25.239.117
(1)2048
(2)69f84dd28d8c254732a4f0d5e5fd56c60c58b7fa
(38)(0)3597
(1)3923
(2)(0)192.168.198.146
(1)2048
(2)526a0bc43f480738c704ea745f5377258c0e3cdb
(3)(0)192.168.176.78
(1)5010
(2)f54b7898236acf827c53277f22fc3dcadcd37602
(39)(0)15042
(1)15368
(2)(0)192.168.197.37
(1)2048
(2)57ceee2905d66bd5863acae5422bf9e39afa971d
(3)(0)192.168.198.179
(1)2048
(2)55192b8693c15df7f11cffc788f2043275886002
(40)(0)10791
(1)11117
(2)(0)192.25.222.249
(1)2048
(2)3c9b5f682360324951435b987489fbc2e4e5bfc3
(3)(0)192.168.198.251
(1)2048
(2)2dcf8603ecbdf43d18112dcaf06d3f9321bc2e0c
(41)(0)7194
(1)7520
(2)(0)192.168.184.112
(1)2048
(2)8a77d41c2bb0a0672654baecfdee52592bb04050
(3)(0)192.25.238.96
(1)2048
(2)8632629188cb269a900677ec0dc395cb292c1e2e
(42)(0)8829
(1)9155
(2)(0)192.168.198.241
(1)2048
(2)37684e5201b0fb50eaf184235a87f2e6a359dd01
(3)(0)192.168.184.115
(1)2048
(2)4684a38fa0b4df0b21f0d777b91300afcace551a
(43)(0)7848
(1)8174
(2)(0)192.168.184.114
(1)2048
(2)a6f9c01f4b3b2df4b4993f5745b78d1307d6ea81
(3)(0)192.168.197.37
(1)2049
(2)51e3568c115be10067d9e808d094f71f96c92647
(44)(0)13734
(1)14060
(2)(0)192.168.198.182
(1)2048
(2)6acca7f88ea98f8ba073f423707d7692592053f3
(3)(0)192.25.238.90
(1)2048
(2)84965ce2c21634a7ce9405374e36e80fa0e4521b
(45)(0)13407
(1)13733
(2)(0)192.168.184.117
(1)2048
(2)2fad26dc888fc776dec4fd33e167c860bd76d64e
(3)(0)192.25.239.181
(1)5090
(2)c91e8c9f8aea8424683258e18c5aa93ec45bb216
(46)(0)11772
(1)12098
(2)(0)192.168.199.1
(1)2048
(2)77c4678d6eddc2f8eb328fa02705d99a6c27a1ca
(3)(0)192.25.239.176
(1)5074
(2)21106c8a361da9c3cf494857e168c5b640eca234
(47)(0)7521
(1)7847
(2)(0)192.25.223.1
(1)2048
(2)ef990477dd7eb4b3c9915bf31b300e87d8fa9b29
(3)(0)192.25.239.106
(1)2049
(2)b0a2975c5cc56a69b3bc63b348a88252fa09ec1a
(48)(0)13080
(1)13406
(2)(0)192.168.198.182
(1)2049
(2)01baf618cc0945c5a379a37ff92e4b1e1eeefac8
(3)(0)192.168.184.116
(1)2048
(2)041464f26f3e7bdb277d0624ba7003bb447f227b
(49)(0)11445
(1)11771
(2)(0)192.25.239.107
(1)2049
(2)0495711f467db7ef747eeb9a24f16b7a5ce0c4ff
(3)(0)192.168.198.234
(1)2048
(2)34d285e446013db478a030f3172fce256b6d4ba8
reply from: 192.168.197.41:2048

通过 awk 计算每个节点管理的槽 ( slot ) 数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 把上面的日志粘贴到 slots 文件中
$ vi /tmp/slots
# 计算每个节点的槽数量并且排序,输出结果
$ cat /tmp/slots | awk '{if((NR % 8 == 1) || (NR % 8 == 2)) print $0}' | awk '{if(NR % 2 == 1) print $0,getline,$0}' | sed 's/)/ /g' | awk '{print $6,$3,$6-$3}' | sort -k 3 -r | head -n 3
16383 16023 360
9809 9483 326
980 654 326

# 根据槽号反向查找
$ cat /tmp/slots| grep 16023 -A8
(18)(0)16023
(1)16383
(2)(0)192.168.176.76
(1)2049
(2)e9b9b9354d6c77a980c0da35bef89b26042b716d
(3)(0)192.168.198.146
(1)2048
(2)1ca744d6e5c24d3f2c64eca1dec65eea8545ce8b
(19)(0)4578

由于本案例当中,大部分节点都是包含 326 个 slot,问题节点明显偏多 360

2.2.1 相关命令解释

awk NR 代表行号
awk getline 获取下一行,并且赋值到 $0 (本意是代表当前行数据)。
sort -k 3 -r,以第三列排序(-k 3),倒序输出(-r)
head -n 3,输出前三行

三、结论

根据上述假设,经过验证,发现确实是由于 slot 分配不均匀导致

四、改进

人工手动调整 slot 较多节点的内存,使之达到与其它节点内存占用水平。
提示:redis 可以通过修改参数( maxmemory,单位 byte )调整最大内存。

五、参考

]]>
- - - - - database - - - - - - - redis - - - -
- - - - - JMH使用案例-日期格式化工具性能对比 - - /20210511-JMH-use-cases-date-format-performance/ - - 一、背景

最近在看代码,发现一个 Date 格式化为 String 的方法。

1
2
3
public String dateFormatString() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(date);
}

看到这个方法想到

  • 每次都 new 一个 format 会快么(非线程安全,得每次都 new)?
  • SimpleDateFormat 格式化快么?
  • “yyyy-MM-dd HH:mm:ss.SSS” 改成静态常量会不会快点?

带着以上三个疑问,就想着做个对比测试。
恰巧最近在 perfma 社区看 jvm 相关内容时,刷到了『性能调优必备利器之 JMH』

优点:不用自己写相关统计代码,而且统计方式有多种

二、结论

性能从低到高

  • java.text.SimpleDateFormat
  • org.apache.commons.lang.time.DateFormatUtils
  • org.joda.time.DateTime 字符串静态常量影响
  • 是否静态常量几乎没有影响
  • 使用常量反而性能有所下降(???为何)
1
2
3
4
5
6
Benchmark                                             Mode  Cnt        Score        Error  Units
DateConvertTest.dateFormatString thrpt 10 867333.631 ± 9510.553 ops/s
DateConvertTest.dateFormatStringApache thrpt 10 2158217.955 ± 89012.866 ops/s
DateConvertTest.dateFormatStringApacheStaticPattern thrpt 10 2141167.550 ± 93715.889 ops/s
DateConvertTest.dateFormatStringJoda thrpt 10 2802803.925 ± 121600.833 ops/s
DateConvertTest.dateFormatStringJodaStaticPattern thrpt 10 2744918.391 ± 131925.235 ops/s

说明:
Error 列是空的,看 Score 和 Units 即可
ops/s:一秒钟执行多少次操作

三、代码

3.1 引入相关 jar

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.23</version>
</dependency>

3.2 IDEA 安装 JMH 插件

插件中心搜索:JMH
安装量最高的那个就是

3.3 编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import org.apache.commons.lang.time.DateFormatUtils;
import org.joda.time.DateTime;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @author hisenyuan
* @date 2021/5/10 18:39
*/
// 默认的 State,每个测试线程分配一个实例
@State(Scope.Thread)
// 如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。
@Fork(1)
// 预热的次数 3 次基准测试(不是执行三次方法)
@Warmup(iterations = 3)
// 基准执行次数 10 次(参数含义同上)
@Measurement(iterations = 10)
public class DateConvertTest {
private final static Date date = new Date();
private final static String PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";

@Benchmark
public String dateFormatString() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(date);
}

@Benchmark
public String dateFormatStringJoda() {
return new DateTime(date).toString("yyyy-MM-dd HH:mm:ss.SSS");
}

@Benchmark
public String dateFormatStringJodaStaticPattern() {
return new DateTime(date).toString(PATTERN);
}

@Benchmark
public String dateFormatStringApache() {
return DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss.SSS");
}

@Benchmark
public String dateFormatStringApacheStaticPattern() {
return DateFormatUtils.format(date, PATTERN);
}
}

3.4 执行

安装了 JMH 插件之后,直接对着类名右键,选择运行即可。
JMH 会默认执行当前类下面的所有的基准测试。

四、执行结果

ETA 00:10:50 代表整个测试需要近11分钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# JMH version: 1.23
# VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatString

# Run progress: 0.00% complete, ETA 00:10:50
# Fork: 1 of 1
# Warmup Iteration 1: 698627.190 ops/s
# Warmup Iteration 2: 873835.360 ops/s
# Warmup Iteration 3: 870972.646 ops/s
Iteration 1: 875650.632 ops/s
Iteration 2: 872407.599 ops/s
Iteration 3: 864390.061 ops/s
Iteration 4: 872456.756 ops/s
Iteration 5: 870538.248 ops/s
Iteration 6: 861600.599 ops/s
Iteration 7: 868469.312 ops/s
Iteration 8: 868930.572 ops/s
Iteration 9: 864686.616 ops/s
Iteration 10: 854205.911 ops/s


Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatString":
867333.631 ±(99.9%) 9510.553 ops/s [Average]
(min, avg, max) = (854205.911, 867333.631, 875650.632), stdev = 6290.642
CI (99.9%): [857823.077, 876844.184] (assumes normal distribution)


# JMH version: 1.23
# VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApache

# Run progress: 20.00% complete, ETA 00:08:42
# Fork: 1 of 1
# Warmup Iteration 1: 1922497.366 ops/s
# Warmup Iteration 2: 2121425.254 ops/s
# Warmup Iteration 3: 2142439.041 ops/s
Iteration 1: 2174791.808 ops/s
Iteration 2: 2101868.007 ops/s
Iteration 3: 2174003.026 ops/s
Iteration 4: 2202522.277 ops/s
Iteration 5: 2039234.570 ops/s
Iteration 6: 2105338.449 ops/s
Iteration 7: 2205933.974 ops/s
Iteration 8: 2175865.041 ops/s
Iteration 9: 2167544.736 ops/s
Iteration 10: 2235077.658 ops/s


Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApache":
2158217.955 ±(99.9%) 89012.866 ops/s [Average]
(min, avg, max) = (2039234.570, 2158217.955, 2235077.658), stdev = 58876.499
CI (99.9%): [2069205.089, 2247230.821] (assumes normal distribution)


# JMH version: 1.23
# VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApacheStaticPattern

# Run progress: 40.00% complete, ETA 00:06:31
# Fork: 1 of 1
# Warmup Iteration 1: 1964208.975 ops/s
# Warmup Iteration 2: 2138997.654 ops/s
# Warmup Iteration 3: 2164194.409 ops/s
Iteration 1: 2221565.433 ops/s
Iteration 2: 2231776.725 ops/s
Iteration 3: 2210494.794 ops/s
Iteration 4: 2057487.199 ops/s
Iteration 5: 2111366.133 ops/s
Iteration 6: 2150355.985 ops/s
Iteration 7: 2116673.562 ops/s
Iteration 8: 2140376.847 ops/s
Iteration 9: 2099801.246 ops/s
Iteration 10: 2071777.579 ops/s


Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApacheStaticPattern":
2141167.550 ±(99.9%) 93715.889 ops/s [Average]
(min, avg, max) = (2057487.199, 2141167.550, 2231776.725), stdev = 61987.258
CI (99.9%): [2047451.661, 2234883.440] (assumes normal distribution)


# JMH version: 1.23
# VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJoda

# Run progress: 60.00% complete, ETA 00:04:21
# Fork: 1 of 1
# Warmup Iteration 1: 2767967.530 ops/s
# Warmup Iteration 2: 2839342.269 ops/s
# Warmup Iteration 3: 2824726.959 ops/s
Iteration 1: 2859527.645 ops/s
Iteration 2: 2799624.402 ops/s
Iteration 3: 2876558.678 ops/s
Iteration 4: 2862478.579 ops/s
Iteration 5: 2882872.177 ops/s
Iteration 6: 2860788.081 ops/s
Iteration 7: 2742885.180 ops/s
Iteration 8: 2779727.212 ops/s
Iteration 9: 2719266.181 ops/s
Iteration 10: 2644311.112 ops/s


Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJoda":
2802803.925 ±(99.9%) 121600.833 ops/s [Average]
(min, avg, max) = (2644311.112, 2802803.925, 2882872.177), stdev = 80431.422
CI (99.9%): [2681203.092, 2924404.757] (assumes normal distribution)


# JMH version: 1.23
# VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJodaStaticPattern

# Run progress: 80.00% complete, ETA 00:02:10
# Fork: 1 of 1
# Warmup Iteration 1: 2711358.480 ops/s
# Warmup Iteration 2: 2823432.725 ops/s
# Warmup Iteration 3: 2745545.759 ops/s
Iteration 1: 2752446.099 ops/s
Iteration 2: 2792047.450 ops/s
Iteration 3: 2828389.094 ops/s
Iteration 4: 2799921.909 ops/s
Iteration 5: 2670924.332 ops/s
Iteration 6: 2533779.871 ops/s
Iteration 7: 2733321.639 ops/s
Iteration 8: 2738839.592 ops/s
Iteration 9: 2802775.869 ops/s
Iteration 10: 2796738.055 ops/s


Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJodaStaticPattern":
2744918.391 ±(99.9%) 131925.235 ops/s [Average]
(min, avg, max) = (2533779.871, 2744918.391, 2828389.094), stdev = 87260.375
CI (99.9%): [2612993.156, 2876843.626] (assumes normal distribution)


# Run complete. Total time: 00:10:52

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark Mode Cnt Score Error Units
DateConvertTest.dateFormatString thrpt 10 867333.631 ± 9510.553 ops/s
DateConvertTest.dateFormatStringApache thrpt 10 2158217.955 ± 89012.866 ops/s
DateConvertTest.dateFormatStringApacheStaticPattern thrpt 10 2141167.550 ± 93715.889 ops/s
DateConvertTest.dateFormatStringJoda thrpt 10 2802803.925 ± 121600.833 ops/s
DateConvertTest.dateFormatStringJodaStaticPattern thrpt 10 2744918.391 ± 131925.235 ops/s

五、JMH 介绍

]]>
- - - - - java - - - - - - - java - - - -
- - - - - JVM系列-MetaSpace(元空间) - - /20210430-jvm-series-metaspace/ - - 一、结论

JDK8 因未指定 MetaSpace 大小,程序启动过程中元空间不够用,触发 full gc。

详细如下:
JDK8 因未指定 MetaSpace 大小,默认初始大小约 21M
程序启动,元空间大小占用稳定在 90M
因为超过了默认元空间大小,导致元空间扩容(每次扩容会 full gc)
从 GC 日志来看,每次元空间扩容都是增加 20M 左右,所以程序启动时 full gc 4 次

二、问题

应用启动时出现 full gc;

gc日志重点:GC (Metadata GC Threshold) [PSYoungGen: 354024K->15340K(1376256K)

三、排查过程

启动过程中,出现了 JVM Full GC 告警。
由于启动过程中之前没有遇到这种情况,找不到其它原因。
于是修改 JVM 参数,增加 GC 日志,于是就看到了『二』中的内容。

看原因是元空间内存不够大导致的
通过 GC 日志可以看到元空间实际占用大小,100M
于是调整 JVM 元空间大小至 128M,问题解决。

问题是解决了,但是原因是什么呢?
在结论部分已经讲了,这里不重复。

所谓知其然,更要知其所以然。
只有这样才能明白问题的本质,学到知识。

四、背景知识

Metaspace整体介绍

]]>
- - - - - java - - - - - - - java - - jvm - - - -
- - - - - 关于中断 - - /20210306-about-interrupt/ - - 一、中断的解释

网络

中断(Interrupt)是指 处理器接收到来自硬件或软件的信号,提示发生了某个事件,应该被注意,这种情况就称为中断。

软中断 (form 《UNIX 操作系统设计》)

内核在收到软中断信号的进程上下文中处理软中断信号,因此进程必须运行以便处理信号。
处理软中断信号的方式:

  1. 进程忽略软中断信号;
  2. 进程收到软中断信号后退出;
  3. 进程收到信号后执行一个特殊的(用户)函数;

二、Java 的中断

Java API 中线程相关的方法主要有三个:

1
2
3
4
5
6
// 中断当前线程,仅设置中断标识位。
public void interrupt()
// 线程的中断标识位是否被标记
public static boolean interrupted()
// 类似 interrupted,无论之前是否被中断,都不会清空中断标识位
public boolean isInterrupted()

Java 中不推荐使用抢断式中断,倡导:

一个线程的生命不应该由其他线程终止,应当由它自己选择是否停止。

1
2
3
4
5
try {
// 业务代码
} catch (InterruptedException e) {
// 可选:退出 | 忽略 | 执行相关逻辑
}

三、扩展

  • 中断有优先级,在处理高优先级中断时,会屏蔽低优先级的中断。

四、参考

]]>
- - - - - 计算机科学 - - - - - - - cs - - - -
- - - - - 计算机科学的自我修炼 - - /20210219-teach-or-improve-yourself-computer-science/ - - 零、背景

在近两年高并发系统 DevOps 的过程中,
遇到了很多底层的问你题,eg: 网络、硬件、虚拟机等,
有些现象虽然知其然,但是不知其所以然,书到用时方恨少!
抱着深入学习的心态这两年看了一些相关书籍:

《计算机网络:自顶向下方法》
《操作系统精髓与设计原理》
《Java性能优化权威指南》
《Redis运维与开发》
《性能之巅》

收获甚大 所以想继续深入学习
误打误撞,看到了之前在 GitHub 关注的一个『自学计算机科学』仓库,很赞同下面这个观点

软件工程师分为两种:

  1. 一种充分理解了计算机科学,从而有能力应对充满挑战的创造性工作;
  2. 另一种仅仅凭着对一些高级工具的熟悉而勉强应付。

这两种人都自称软件工程师,都能在职业生涯早期挣到差不多的工资。
然而,随着时间流逝,第一种工程师不断成长,所做的事情将会越来越有意义且更为高薪,
不论是有价值的商业工作、突破性的开源项目、技术上的领导力或者高质量的个人贡献。

一、资源

1.1 摘要

科目为何要学最佳书籍最佳视频
编程不要做一个“永远没彻底搞懂”诸如递归等概念的程序员。《计算机程序的构造和解释》Brian Harvey’s Berkeley CS 61A
计算机架构如果你对于计算机如何工作没有具体的概念,那么你所做出的所有高级抽象都是空中楼阁。《深入理解计算机系统》Berkeley CS 61C
算法与数据结构如果你不懂得如何使用栈、队列、树、图等常见数据结构,遇到有难度的问题时,你将束手无策。《算法设计手册》Steven Skiena’s lectures
数学知识计算机科学基本上是应用数学的一个“跑偏的”分支,因此学习数学将会给你带来竞争优势。《计算机科学中的数学》Tom Leighton’s MIT 6.042J
操作系统你所写的代码,基本上都由操作系统来运行,因此你应当了解其运作的原理。《操作系统导论》Berkeley CS 162
计算机网络互联网已然势不可挡:理解工作原理才能解锁全部潜力。《计算机网络:自顶向下方法》Stanford CS 144
数据库对于多数重要程序,数据是其核心,然而很少人理解数据库系统的工作原理。《Readings in Database Systems》 (暂无中译本)Joe Hellerstein’s Berkeley CS 186
编程语言与编译器若你懂得编程语言和编译器如何工作,你就能写出更好的代码,更轻松地学习新的编程语言。《Crafting Interpreters》Alex Aiken’s course on Lagunita
分布式系统如今,多数 系统都是分布式的。《数据密集型应用系统设计》MIT 6.824

1.2 详情

内容值得一看,真心建议多花功夫学好底层知识。
原文:Teach Yourself Computer Science
翻译:自学计算机科学

来自亚马逊 CTO 的博文也值得一看,操作系统经典书籍
The OS Classics

三、相关资源

软件工程师面试指引

]]>
- - - - - 成长 - - 计算机科学 - - - - - - - cs - - - -
- - - - - 春节期间的收获 | 记第一次在外过年 - - /20210218-harvest-during-chinese-spring-festival/ - - 零、摘要

响应国家就地过年的号召,今年第一次在外过年。
弹指一挥间,12 天的假期已经成为过去。
期间还是有不少的收获,最主要的是看了 4 本书。
以及在微博上面看到了不少的人和事,甚是触动。

找到自己的兴趣,追求精进,坚持做对的事情。

一、阅读

1.1 《 UML 和模式应用》0113~0207

这本书是 leader 推荐给大家的
基于职责去做设计的理念确实很棒,值得观摩实践。

1.2 《段永平投资问答录(商业逻辑篇)》0120~0214

春节之前断断续续看了一部分,后面主要是春节在看。
关键是:要做对的事情,把事情做对。(前者更重要)
创始人对公司的文化影响很大,文化又对公司影响大。

有自己清晰的不为清单,发现错了立马纠正,因为这时候止损成本最低。

段永平的投资问答录其实很多时候也适合于人生建议,都是相通的。
很幸运有如此成功的人愿意与普通人进行交流,传授经验。

书本当中也能接触到不少成功人士,正所谓近朱者赤,
还有所谓的圈子,多看多想多学,总是会进步的。

1.3 《代码精进之路:从码农到工匠》0216~0217

实践出真知,作者成就非凡
其中的很多理念深度赞同
是一本值得翻阅的书,收益颇多

eg:

  1. 写好代码的技艺不是一蹴而就的,它是一个系统化的工程,不是看几本书、写几年代码就能轻松习得的,而需要我们对自己的思维习惯、学习方法和工程实践进行彻底的反省和重构。
  2. 在计算机科学中有两件难事:缓存失效和命名。
  3. 通常,如果你无法想出一个合适的名字,很可能意味着代码“坏味道”、设计有问题。
  4. 写好代码、追求卓越和工匠精神是每个程序员都应该具备的优秀品质。
  5. 管理者的一个很重要的使命就是帮助团队成长,包括制定规范和技术传承。
  6. 没有抽象思维,就没有人类今天灿烂的文明。
  7. 分治和抽象一样,都是人类进化过程中形成的伟大智慧,也是我们解决复杂问题的不二选择。
  8. 在学习之前,我们一定要问自己,这次学习的目标是什么?
  9. 技术 leader 要以德服人+以技服人

1.4 《硅谷钢铁侠:埃隆·马斯克的冒险人生》0217~0218

看完对马斯克以及他的企业和野心有更多的了解
羡慕他精力旺盛、超强的学习能力、以及追求梦想的执着

二、微博

触动最大的就是@纯银V 一位产品经理的微博
特别是关于一个 95 后实习生的故事
也很佩服纯银从一个法警做到如此高阶的产品经理
羡慕他把看产品的思路套用在看公司上,然后 2020 投资中概股收益颇丰

感悟就是:积极、主动、为自己工作,找到喜欢的事情,持续精进。

三、生活

和家里亲戚去郊区玩了几天,还是比较放松。
有时候不管是家人之间的沟通,还是朋友之间,可能更多的时候需要的是倾听,而不是讲道理。
犹如《代码精进之路》里讲的”不过在家里,我依然是输多赢少,后来我才发现,原来家不是一个讲逻辑的地方。”

安排好自己的时间,加强执行力!

]]>
- - - - - 随说 - - - - - - - 随说 - - - -
- - - - - 如何做一个让人讨厌的产品经理 - 《人人都是产品经理 2.0》读后感 - - /20210130-how-to-be-a-disgusting-product-manager/ - - 零、背景

本文灵感来自《人人都是产品经理 2.0》
位置:7.4.2 如何做一个让 Ta 们讨厌的人

作为一个研发,工作过程中如果能及时发现如下场景,
及时给对方负反馈,否则受伤的是整个团队。
看了这本书之后,感觉对产品有新的认知,
知道他们在做什么,怎么做,后续可以更好的与他们沟通。
而且里面的内容对于研发来讲也是适用的。

一、开始实施之前

1.1 不说清需求价值

技术问”为什么要做”时:
1、时支支吾吾
2、这是老板(XXX)要的,假装自己是个传话筒
3、我接的是二手需求,什么都不知道
随说:其实正确的做法是追溯这个需求的初衷,有利于评估 ROI (投入产出比),以及排优先级,以及增进对业务的理解。

1.2 不去想细节功能

技术问细节(业务细节,非技术细节)时:
1、装作自己完全没有想过
2、那就这样做
3、肯能那样做也可以
4、要不你来定吧
随说:这些表现其实都是不负责的!没有明确的需要,后期容易扯皮,也可能做出来的东西有问题。

1.3 帮技术评估工作量

1、这不是很简单嘛,就改个 XXX,几行代码就搞定
2、这些我都评估过了,都能做
3、不要偷懒,不要忽悠我,我抖动
随说:评估真是个高难度的活,往往只见树木不见森林

1.4 逼着技术团队承诺

1、任何时候只知道公事公办,技术承诺了却做不到,自己就没有责任了。
随说:互联网企业更多的只是一个”预测”,而非”承诺”,因为变化太快,承诺是技术对产品的责任,而预测是产研一起担责。

二、实施过程中

2.1 做了一半改需求

1、经常在某个迭代周期内做”非受迫的需求变更”,这招杀伤力很大,技术同学一般受不了。
随说:非受迫,意味着是产品没有想清楚,然后导致研发无谓劳动。万万不可取。

2.2 开发过程中消失

1、可以想各种办法隔绝与研发的联系。
随说:一般都需要多次沟通,反复确认细节。

2.3 过度关注细节

1、帮技术确定各种技术方案,这个应该这样实现…
随说:这种一般少见,研发转岗产品的多一些

三、产品发布之后

3.1 没有发布后的反馈

1、发布之后,犹如石沉大海,不告诉大家任何结果,甚至庆功宴都不叫 Ta 们,紧接着继续安排他们干活。
随说:技术人员也需要从市场、用户那里获得反馈,从而对自己做的事情产生价值感和成就感。

3.2 任务无节奏感

1、让研发一阵忙一阵闲,发布之后再开始研究接下来做什么。
2、一会儿让研发有着天天通宵的高强度,给 deadline,下死命令,做完之后不知道做什么。
随说:保持合理的安排,让研发有个合理的预期比较好。而不是等你们上了大学就自由了这种…

四、全程适用

4.1 优柔挂断

1、你定吧,你说往哪儿走我们照办
2、啊…那个…方案各有利弊,我也不知道怎么办,你们有什么好想法
随说:自己左手和右手互搏,思考清楚之后给大家一个明确的结果。

4.2 报喜不报忧

1、藏着噎着一些坏消息,eg:老板正考虑干掉这个项目等,大家只能通过小道消息得知
随说:这样很破坏信任感

4.3 不把 Ta 们当人看

1、只关注结果,不关注人的成长,永远把合作伙伴当”资源”
随说:结果导向没有问题,但是没有成长,留不住人。

]]>
- - - - - 随说 - - - - - - - 随说 - - - -
- - - - - Shell 批量解压文件并重命名 - 解决文件名冲突 - - /20201107-Shell-batch-decompression-and-rename/ - - 一、背景

由于某种原因,需要手工处理错误日志提取某些信息。
下载下来的日志文件是压缩包

1.1 文件信息

1
2
3
4
system_error.log.2020-11-01.20201105200433.zip
system_error.log.2020-11-01.20201105195953.zip
system_error.log.2020-11-01.20201105200830.zip
...省略

1.2 压缩包信息

1
2
3
4
5
6
7
$ unzip -v system_error.log.2020-11-01.20201105200830.zip
Archive: system_error.log.2020-11-01.20201105200830.zip
Length Method Size Cmpr Date Time CRC-32 Name
-------- ------ ------- ---- ---------- ----- -------- ----
4495560 Defl:N 78526 98% 11-01-2020 23:43 504551c6 system_error.log.2020-11-01
-------- ------- --- -------
4495560 78526 98% 1 file

压缩包文件名不一样,但是压缩包中的文件有一样的名字。
例如:system_error.log.2020-11-01

尝试使用如下方式,提示有重复,需要挨个选择如何处理,极度不便。
于是想使用 shell 来解决( 之前没写过 shell )

1.2 尝试过程

提示有文件重复,需要处理。

1
2
3
$ unzip '*.zip'
Archive: system_error.log.2020-10-15.20201105200943.zip
replace system_error.log.2020-10-15? [y]es, [n]o, [A]ll, [N]one, [r]ename:

尝试使用 -n 参数,不覆盖

1
2
3
4
$ unzip -n '*.zip'
Archive: system_error.log.2020-10-15.20201105200943.zip
...省略
100 archives were successfully processed.

-n 表示不覆盖,有重名的直接跳过,结果就是 100 个文件只解压的 10 个。

1.3 怎么解压并且重命名?

1
2
mv A B
# 把 A 改名为 B

怎么拿到压缩包内的文件名?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 输出压缩包内的信息
$ unzip -v system_error.log.2020-11-01.20201105200830.zip
Archive: system_error.log.2020-11-01.20201105200830.zip
Length Method Size Cmpr Date Time CRC-32 Name
-------- ------ ------- ---- ---------- ----- -------- ----
4495560 Defl:N 78526 98% 11-01-2020 23:43 504551c6 system_error.log.2020-11-01
-------- ------- --- -------
4495560 78526 98% 1 file

# 使用 awk 截取相关信息
# NR 行号,NF 最后一列
# unzip -v 的输出方式可能有变动,需要自己修改位置,以得到正确的结果
$ unzip -v system_error.log.2020-11-01.20201105200830.zip | awk '{if(NR==4) print $NF}'
system_error.log.2020-11-01

二、脚本信息

1.3 的问题解决了就可以开始写脚本

2.1 脚本

文件名 uf.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh
# 压缩包目录
srcDir=$1
# 解压后文件存放目录
dstDir=$2
# 文件名序号
seq=1
for file in ${srcDir}/*
do
# 判断是否为文件
if test -f $file
then
# 获取压缩包名称,例如:system_error.log.2020-11-01.20201105200830.zip
filename=$(basename $file)
# 解压文件,移动文件,并且改名(这里使用的是序号,可以截取原文件名拼接时间戳等避免重名)
unzip $filename && mv `unzip -v $filename | awk '{if(NR==4){print $8}}'` ${dstDir}/$((seq++))
fi
done

2.2 使用方法

注意,目前在目标目录下才能正确运行

1
2
chmod 775 uf.sh
sh uf.sh ./src ./dst

然后解决问题,解压所有 100 个压缩包,并且得到 100 个文件(文件名为 1~100)。

三、总结

不得不说解决问题是最快的学习方式
第一次写脚本相对耗时,也是一个不错的尝试过程。
对linux系统相关的命令/函数不熟悉,导致到处查资料,有必要系统化的看看书。

四、参考信息

]]>
- - - - - linux - - - - - - - shell - - - -
- - - - - 计算机存储单位 - 以及常识 - - /20201030-understanding-computer-storage-unit/ - - 一、常见单位
单位英文全称中文全称转换
bbit-
BByte字节1B=8b
KBKilo Byte千字节1KB=1024B
MBMega Byte兆字节1MB=1024KB
GBGiga Byte千兆1GB=1024MB
TBTrillion Byte万亿字节1TB=1024GB
PBPeta Byte千万亿字节1PB=1024TB
EBExa Byte百亿亿字节1EB=1024PB
ZBZetta Byte十万亿亿字节1ZB=1024EB
YBYotta Byte一亿亿亿字节1YB=1024ZB
BBBronto Byte一千亿亿亿字节1BB=1024YB
NBNona Byte1NB=1024BB
DBDogga Byte1DB=1024NB
CBCorydon Byte1CB=1024DB

进制除了 Byte 与 bit 之间是 8,其它的都是 1024,但是目前很多时候习惯用 1000,比如 1T ≈ 1000G;

二、带宽/网速

2.1 带宽

运营商(ISP)带宽宣传常见的有:50M、100M、500M、1000M…
注意:这是传输速率,而不是下载速度。

它们的单位其实是:bps (全称 bit per second,b/s)
50M 中 M 的含义是:million 即 百万。
翻译过来就是:50Mbps = 50,000,000 b/s;

2.2 网速

各种下载软件,比如迅雷会现在下载速度:12M/s
这个 12M 的单位是 byte 也就是字节。
由表常见单位可知,1byte = 8bit

所以由带宽传输速率转换为下载速度一般需要除以 8
即 100M 带宽,理论上最大的下载速度为:100Mbps / 8 = 12.5Mbyte / s(俗称网速 12.5M);

三、字节占用

关于字节的占用,最明显的就是文本编辑器,写入文本后保存,在文件管理器中能看到具体占用了多少存储空间。
问:一个英文(或标点)占用多少字节?一个汉字(或标点)占用多少字节?
答:与所使用的编码有关系,具体如下表:

编码英文汉字
ASCII1byte2byte
UTF-81byte3byte
Unicode2byte2byte
]]>
- - - - - java - - - - - - - java - - - -
- - - - - 理解 Linux Load Averages - - /20201026-Understanding-Linux-Load-Averages/ - - 零、背景

最近做压力测试,不同的系统的机器监控数据差异明显
A 系统:CPU 高 load 低
B 系统:CPU 低 load 高

那么是什么导致 A B 系统出现这种情况?
CPU 高了系统肯定跑不动了,那么 load 多高代表系统跑不动呢?

一、解释

  1. A 系统的原因
    CPU 很忙,没有等待其它资源,瓶颈在 CPU。
  2. B 系统的原因
    等待磁盘 I/O 完成的进程过多,导致进程队列长度过大,
    但是 CPU 运行的进程却很少,这样就导致负载过大,但 CPU 使用率低,瓶颈不在 CPU,可能在 I/O。

二、load averages 知识

  1. Linux 系统下代表的是 system load averages。
  2. Linux load averages track not just runnable tasks, but also tasks in the uninterruptible sleep state.
    Linux 平均负载不仅跟踪可运行的任务,还跟踪处于不可中断睡眠状态的任务。

  3. On Linux, load averages are (or try to be) “system load averages”, for the system as a whole, measuring the number of threads that are working and waiting to work (CPU, disk, uninterruptible locks). Put differently, it measures the number of threads that aren’t completely idle. Advantage: includes demand for different resources.
    在 Linux 上,负载平均值是(或试图是)“系统负载平均值” ,对于整个系统来说,测量正在工作和等待工作的线程数(CPU、磁盘、不可中断锁)。换句话说,它测量的是没有完全空闲的线程数量。优势: 包括对不同资源的需求。

  4. In 1993, a Linux engineer found a nonintuitive case with load averages, and with a three-line patch changed them forever from “CPU load averages” to what one might call “system load averages.” His change included tasks in the uninterruptible state, so that load averages reflected demand for disk resources and not just CPUs.
    1993年,一位 Linux 工程师发现了一个与平均负载不直观的案例,一个三行补丁永远地将它们从“ CPU 负载平均值”改变为人们可能称之为“系统负载平均值”他的更改包括处于不可中断状态的任务,因此平均负载反映了对磁盘资源的需求,而不仅仅是 cpu。

三、总结

  1. linux load averages 数字大,代表系统压力大;
  2. load 高可能的原因有(CPU、I/O、不可中断锁);
  3. load 1min load 5min load 15min,从左到右递增说明负载在增加,递减说明负载在降低;
  4. 负载多大才算高,没有定论,得看是否影响业务,如果业务正常那多半是没有问题的;

四、load 优化

  1. 优化算法,减少 CPU 计算;
  2. 减少 I/O 交互的次数,以及大小;
  3. 分布式系统水平扩展更多的机器;

五、参考

  1. Linux Load Averages: Solving the Mystery (发表于 2017 年)
  2. 理解Linux系统负荷 (发表于 2011 年)
  3. Linux中CPU使用率低负载高
]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Java HotSpot 虚拟机 CMS、G1 参数优化对比记录 - - /20201019-java-hotspot-vm-cms-g1-setting-log/ - - 零、背景说明

目前测试机器为 4C8G
两台机器完全处理一样的工作
大部分时间对象朝生夕死,很少进入老年代
CMS 指定了新生代最大 1536M,略微有点浪费
于是设置 G1 自动调节各个区域大小,看看能否有所改善
也因为最近重温了两本 JVM 相关的书籍,找机会进行实践看看

一、G1 设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-Xms4096M
-Xmx4096M
-XX:+UseG1GC
-XX:+UnlockDiagnosticVMOptions
-XX:SurvivorRatio=8
-XX:+ParallelRefProcEnabled
-XX:MaxTenuringThreshold=6
-XX:ParGCCardsPerStrideChunk=4096
-XX:MaxGCPauseMillis=100
-XX:MaxMetaspaceSize=256M
-XX:MetaspaceSize=256M
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-Xloggc:/home/hisen/gc.log

二、CMS 设置

最大最小设置成一致的值,是为了不让堆大小进行收缩(缩的过程会 GC)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 初始堆大小
-Xms4096M
# 最大堆大小
-Xmx4096M
# 年轻代的大小
-Xmn1536M
# eden 区比例,此处为 8:1:1
-XX:SurvivorRatio=8
# 最大元空间大小
-XX:MaxMetaspaceSize=256M
# 最小元空间大小
-XX:MetaspaceSize=256M
# 使用 CMS 垃圾收集器
-XX:+UseConcMarkSweepGC
# 只使用占用率作为启动 CMS GC 的标准,配合 CMSInitiatingOccupancyFraction 使用
-XX:+UseCMSInitiatingOccupancyOnly
# CMS垃圾收集器,当老年代达一定比例,触发CMS垃圾回收。此处为 70%
-XX:CMSInitiatingOccupancyFraction=70
# 默认为 false,并行的处理 Reference 对象,如 WeakReference,除非在 GC log 里出现 Reference 处理时间较长的日志,否则效果不会很明显。
-XX:+ParallelRefProcEnabled
# 开启或关闭在CMS重新标记阶段之前的 YGC 尝试
-XX:+CMSScavengeBeforeRemark
# 晋升老年代的阈值
-XX:MaxTenuringThreshold=6
# 解锁诊断参数,否则 ParGCCardsPerStrideChunk 不生效
-XX:+UnlockDiagnosticVMOptions
# 每个线程扫描 old gen 的 CardTable (一定大小的内存块) 个数
-XX:ParGCCardsPerStrideChunk=4096
# 打印 GC 的时间戳
-XX:+PrintGCDateStamps
# 打印 GC 明细日志
-XX:+PrintGCDetails
# 存放 GC 日志的路径
-Xloggc:/home/hisen/gc.log

三、结果对比

TypeDurationXmnYGC countYGC avgInterval Time
G172 hrs 21 min 8 sec1.66G759619.6 ms34 sec 294 ms
CMS70 hrs 24 min 51 sec1.5G702914.4 ms36 sec 68 ms

目前应用堆响应时间比较敏感,追求低延迟。
就上表来看,G1 新生代的大小确实上去了,但并不尽如人意。
但是随着而来的是 G1 『更频繁的YGC』以及『更长的停顿时间』
在《深入理解JVM虚拟机》第三版看到,
由于 G1 处理跨 region 需要耗费额外 10% ~ 20% 的资源,小堆 (堆大小<8G) 上没有优势。

JVM 新生代大小与老年代大小默认为 1:2,
大部分应用按这个设置是 OK 的,
虽然有时候看着老年代一直很小,
设置那么大浪费内存,但是当调用量大的时候,就能派上用场,减少 Full GC。

先占个坑,后续继续填内容。

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 英语文档:how-to-manage-a-redis-database.pdf - - /20201010-how-to-manage-a-redis-database/ - - 当初这个文档应该是在『科技爱好者周刊』上还是哪里发现的
看这个文档本来的目的是每天坚持阅读英文文档
技术文档一般都比较简洁易懂,英语是一个长期的过程,还得继续坚持阅读~

看的过程中发现,结合一定的场景,和示例
和官方文档相比,没有那么枯燥,也更好理解
甚至有一些之前未曾关注的用法让我感到惊艳

所以推荐一下

下载地址:how-to-manage-a-redis-database.pdf

]]>
- - - - - database - - - - - - - redis - - database - - - -
- - - - - BeanUtils 对象拷贝 - 仅拷贝指定属性 - 接口性能优化 - - /20200926-BeanUtils-copyLimitedProperties-for-better-performance-of-response/ - - 零、背景

很多时候我们调用接口并不是需要接口返回的所有信息;
就像查询数据库很少使用 select * from table; 一样;

为了使现有的存储结构以及代码逻辑改动最少
想办法在最外层的接口对返回的对象进行精简

目的使为了提高接口性能:减少 RPC 传输时间、节省网络带宽、节省序列化开销

一、方案

  1. 直接创建一个对象,根据入参传入的所需字段,写一堆 if else 进行 get set;
  2. 使用序列化工具,转为类似 Map 结构,取值拼装;
  3. 使用 BeanWrapper 操作对象;

前面两种比较呆板,属于定制化开发;

二、实践

Spring BeanWrapper 方案处理非集合对象比较完美,输出对象小不少;
基本数据类型有默认值,无法去除,不过这种对大小影响不大;
完整代码:github.com

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
Order order = getOrder();
Order smallerOrder = new Order();
List<String> keepFields = Lists.newArrayList();
keepFields.add("orderId");
keepFields.add("address.province");
// i want get all names, not only index 1, how to do ?
keepFields.add("productInfos[1].name");
copyLimitedProperties(order, smallerOrder, keepFields);
Gson gson = new Gson();
System.out.println("src:" + gson.toJson(order));
System.out.println("des" + gson.toJson(smallerOrder));
}

输出 src:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"address": {
"province": "B",
"city": "A",
"county": "C"
},
"productInfos": [
{
"productId": 0,
"name": "name0",
"price": 1,
"num": 0,
"imgUrl": "url1"
},
{
"productId": 0,
"name": "name1",
"price": 2,
"num": 0,
"imgUrl": "url1"
},
{
"productId": 0,
"name": "name2",
"price": 3,
"num": 0,
"imgUrl": "url1"
}
]
}

输出dst

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"address": {
"province": "B"
},
"productInfos": [
{
"productId": 0,
"num": 0
},
{
"productId": 0,
"name": "name1",
"num": 0
}
]
}

三、缺点

目前 Spring BeanWrapper 处理集合类型比较费劲,需要告知一个个具体的路径
比如:productInfos[1].name
如果能做到 productInfos[any].name 这种,然后取出所有 index 的属性,就比较完美
(目前解决方法:通过自定义处理,遇到 any 标识,自动补全所有 index 的 key 即可,只是不那么优雅)

缺陷原因:内部逻辑直接用 key 转换为 index ,没有 any 逻辑。

1
2
3
4
5
6
else if (value instanceof List) {
index = Integer.parseInt(key);
List<Object> list = (List)value;
this.growCollectionIfNecessary(list, index, indexedPropertyName, ph, i + 1);
value = list.get(index);
}

代码位置:org.springframework.beans.AbstractNestablePropertyAccessor#getPropertyValue

四、参考

Spring 官方文档:Bean Manipulation and the BeanWrapper

]]>
- - - - - java - - - - - - - java - - - -
- - - - - MySQL查询违反最左匹配原则,但 explain 显示走索引的疑惑 - - /20200915-the-puzzle-of-mysql-using-index-which-violation-of-leftmost-matching-principle/ - - 零、本文背景

有个朋友抛出一个问题,明显不符合最左匹配原则的 SQL,居然走索引了
兜兜转转,嘀咕了好几天,期间也和几个朋友讨论了一下
都没有结果,最后还是在 MySQL 的官方文档中找到了原因
记录下,也算是一次不错的探索。

一、问题描述

1.1 表结构

1
2
3
4
5
6
7
8
CREATE TABLE `people_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`last_name` varchar(255) NOT NULL,
`first_name` varchar(255) NOT NULL,
`bob` date NOT NULL,
PRIMARY KEY (`id`),
KEY `index_union` (`last_name`,`first_name`,`bob`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='人员-新'

1.2 数据

1
2
3
4
5
6
7
mysql> select * from people_new;
+----+-----------+------------+------------+
| id | last_name | first_name | bob |
+----+-----------+------------+------------+
| 1 | hisen | yuan | 2008-08-08 |
+----+-----------+------------+------------+
1 row in set (0.00 sec)

1.3 SQL 分析

可以看到 Using index
但是 possible_keys null 而 key 显示 index_union

1
2
3
4
5
6
7
mysql> explain select * from people_new  where bob = '2008-08-08' and first_name = 'yuan';
+----+-------------+------------+------------+-------+---------------+-------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------+-------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | people_new | NULL | index | NULL | index_union | 1537 | NULL | 1 | 100.00 | Using where; Using index |
+----+-------------+------------+------------+-------+---------------+-------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

二、原因

1.1 的表结构显示,除了 id,其余三个属性都在 联合索引
所以通过任何字段查询,返回的字段都被索引覆盖了,构成 覆盖索引
由于扫描全部索引会快于全表扫描,所以这时候 sql 不管是不是最左匹配都会走索引(应该是『全索引扫描』)。

3.1 的表结构中,除了 id ,还有 area 不在联合索引当中,此时破坏了 覆盖索引 ,故不走索引。

1
2
3
If the index is a covering index for the queries and can be used to satisfy all data required from the table,
only the index tree is scanned. In this case, the Extra column says Using index.
An index-only scan usually is faster than ALL because the size of the index usually is smaller than the table data.

详情:dev.mysql.com

三、验证

3.1 修改后的表结构

1
2
3
4
5
6
7
8
9
CREATE TABLE `people_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`last_name` varchar(255) NOT NULL,
`first_name` varchar(255) NOT NULL,
`bob` date NOT NULL,
`area` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_union` (`last_name`,`first_name`,`bob`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='人员-新'

3.2 修改表结构后的数据

1
2
3
4
5
6
7
mysql> select * from people_new;
+----+-----------+------------+------------+------+
| id | last_name | first_name | bob | area |
+----+-----------+------------+------------+------+
| 1 | hisen | yuan | 2008-08-08 | NULL |
+----+-----------+------------+------------+------+
1 row in set (0.00 sec)

3.2 SQL 分析

1
2
3
4
5
6
7
mysql> explain select * from people_new  where bob = '2008-08-08' and first_name = 'yuan';
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | people_new | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

四、总结

有时候多和同行交流也能学到很多。
带着问题去看官方文档收获会比较大。
遇到问题,找到原因,解决问题,印象会深刻。

]]>
- - - - - 数据库 - - - - - - - mysql - - index - - - -
- - - - - 软件工程师面试指引 - - /20200805-software-engineer-interview-guide/ - - 零、关于面试

面试决定因子:70% 能力、20% 缘分、10% 行情。
软件工程师是条不归路,每天进步一点点,早日走上人生巅峰。

一、面经/知识点

仓库的内容更多是抛砖引玉,提供通用与重要的技术,
真正掌握还得贴合自身需要,花时间持续深入地学习。

基础指引:https://github.com/Snailclimb/JavaGuide
进阶之路:https://github.com/doocs/advanced-java
编程书籍:https://github.com/jobbole/awesome-programming-books
算法小抄:https://labuladong.gitbook.io/algo/
大厂试题:https://github.com/0voice/interview_internal_reference
简历打磨:https://github.com/geekcompany/ResumeSample/blob/master/java.md

后续持续更新,多交流,共同成长。

二、相关内容

计算机科学的自我修炼
技术人的自我修炼(书单)

三、更新记录

2020-12-18

  1. 新增『算法小抄』,很佩服作者的计划、执行能力。关于作者部分很认同。

2021-02-18

  1. 新增『计算机科学的自我修炼』相关链接
]]>
- - - - - 成长 - - - - - - - java - - 面试 - - - -
- - - - - 王慧文(美团):很少人知道自己在愚昧之巅 - - /20200723-Few-people-know-that-they-are-ignorance/ - - 零、写在前面

早几天在领导 JG 的朋友圈看到这篇文章
简单几个字的标题,让我产生了兴趣
一来这个标题是挺吸引人,典型的自媒体文章
二来JG 之前推过一些书很不错
三来一般领导朋友圈都不会推垃圾信息

全文看来其实感觉还是挺切合我的认知:

  1. 人很难认清自己,很难找到方向,成长路上有他人指点会很棒
  2. 除了遇到的人合读过的书,十年之后你还是你
  3. 多与他人交流,多帮助他人,利人利己
  4. 运动、学习、反馈、重塑大脑

离开学校
大部分时间都是围绕着工作
有些人可能觉得当前的工作不满意
然后想着学点什么高大上的东西为以后的工作做准备
极端情况下这种选择是对的,大部分情况会让自己进退两难
正确的选择应该是:过好当下,瞭望远方,持续学习,总结反思,慢慢进步
架构师同事 SGLS 说过:做好当下的事情才是最重要的,不管现在是做什么项目,设计和实现是我们自己可以控制的。

以下为原文

1
2
3
审视是一个特别好的词。当一个人开始审视世界、审视经历、审视自己的时候,更容易做出有担当的选择。
作为美团“二号首长”,王慧文选择在2020年底正式“退休”,这不可分说地成为了焦点——
王慧文,是怎么成为“王慧文”的?

一、“不自我设限”,造就无边界的王慧文

什么是促使人成长的本质原因?
王慧文一直在渴求突破,也一直在探究这个问题。

1. 人要接受变化,有动荡才有进化

王慧文修行的开端,就是校内网。
2006年3月,王慧文、王兴和另外四名同伴绞尽脑汁、黑白颠倒,终于让校内网收获了一百多万用户。
王兴开始四处找投资,但在多数投资人眼里看来,他们仍是群“生瓜蛋子”、“杂牌军”,校内网不过是几个大学生做的小打小闹的玩意儿。
但在这时,千橡集团董事长陈一舟出现了,他愿意拿出一千多万人民币,收购校内网。《九败一胜:美团创始人王兴创业十年》中这样描述

1
2
3
4
5
6
7
陈一舟说,如果你们不卖,我们可以拿这钱砸到市场上推广。当然,校内网的这群狂妄的小子被这句话激怒了,拒绝跟陈一舟谈。
回绝了陈一舟后,王兴的融资依旧不顺利,陈一舟却与王兴他们保持接触,认准了校内网这条大鱼,准备放长线,持久诱惑。
2006年9月,陈一舟再次抬高了收购校内网的价码,校内网召开了五人的股东会。
王慧文觉得校内网的机会难得,做成的希望很大,而且是自己一手做起来的产品,感情已经很深,所以坚决反对卖掉。王兴态度中立,但倾向于不卖。赖斌强则考虑的方面更多,他支持卖掉,以免大家背着一身巨债创业。
除了他们三个大股东,还有另两名小股东,杨俊和付栋平。他们加入较晚,股份也较少,觉得卖不卖都行,比起被收购,他们更担心王慧文和赖斌强由于意见不合而导致团队破裂。
这次争论后不久,陈一舟就就又开出了更高的收购价格。几人意识到,如果继续争论不休,团队的裂痕势必更深。万般无奈之下,校内网不得不同意陈一舟的条件。
那一晚,大家都不说话,一起去吃夜宵的时候,王兴、王慧文、赖斌强全喝醉了,所有人大哭了一场。

不论校内网是成功的,还是失败的,在王慧文和王兴的心里,它永远都是遗憾的。
回首往事,王慧文已经不再执着,反而觉得这段遗憾对自己的人生是件好事。他说:
如果校内网做成了,我会变成让别人非常讨厌的人,刚愎、自负,因为挫折,所以我才懂得反省自己,能更理智客观冷静地看待自己。
还有人问王慧文,如果当初不卖的话,校内网能不能做得成?王慧文答:
这取决于我们遇到什么样的投资商,如果投资商能够在很多地方帮助我们,是可能做成的;如果投完钱就不怎么管,让我们自己随便搞,那就不行,因为我们还不够成熟。
管理者的自我反思,会反馈在他的战略、领导力、管理、业务实操等能力上。
人必须反思,是因为事物是时刻变化的,不断反思,才能不断跟上变化。
王慧文在一次采访中说:美团的持续高速增长,不是我们在推动着业务发展,而是业务快速发展,我们努力地跟上,不是我们做了什么事情,只是我们跟上了,没掉队。
在一个快速发展的行业里,在一个快速变化的时代里, 创业者、管理者如果没有自我反思,是很难生存下来的。
在第一次创业修行中,王慧文就悟出一个道理:人,把事做成不易,不断成长、突破自己边界更难。

2. 不设限的人生,成长机会无限

“君子不器”是王慧文的人生原则之一。
2019年,在极客公园创新大会上,极客公园创始人、总裁张鹏问王慧文:你觉得美团的基因是什么?
王慧文当时就说出了“君子不器”四个字,他面对张鹏侃侃而谈:孔子说,君子不器,君子不是一个器皿,人不要搞自我定义,自我定义就是自我设限,我不倾向于用美团的基因来自我设限。
他还用“基因突变”举例,基因变化变错的概率比变对的概率高几百万倍,所以大家都很恐惧基因变化,抵触变化。
但王慧文认为变化不可怕,自我设限才最可怕,他还幽默地说:一旦变对了呢?一旦长出翅膀了呢?
这种不自我设限,就是“美团无边界,专注而不专一”的根源。
君子不器,有两个含义:
一是人想要成长就不能有固化思维,事物是变化的,人也要具备动态发展的眼光,反思过去,展望未来;
二是人不能像器皿那样自我设限,器皿只有某一方面的用途和才能,而人应该博学多识。
孔子的这两点要求,王慧文都做到了,他的生活和工作就是如此。

《财经天下》周刊记者朱晓培这样形容“百变的王慧文”

1
在王慧文的朋友圈里,总能看见他忽然想到一个问题,比如:是不是20世纪在唯心主义和唯物主义领域都没有出现过大师?如果地球上没有美洲,哥伦布的船直接西行到了亚洲,今天的世界格局又会是什么样子?为什么射雕英雄传的五绝里只有反派欧阳锋没有广招门徒?

正如王慧文在内部信中所说,他个人的兴趣“散乱不稳定”。
当年创业时,王慧文和王兴都不会敲代码,但他们都觉得:没事,我们可以学。同样,后来在美团时,有一段时间王慧文要研究零售,他几乎每天都看一本关于零售的书,并且跟很多人谈论自己的心得。

1
2
王慧文敬佩爱因斯坦,爱因斯坦曾说:科学和艺术一样,都让我们的世界更加绚丽多彩。
爱因斯坦格外推崇西方古典音乐。这位“相对论”的创始人经常和“量子论”的创始人普朗克一起演奏贝多芬的作品,爱因斯坦拉小提琴,普朗克弹钢琴。相对论和量子论共同构成了21世纪物理学科的两大支柱,而它们的创始人不仅都对科学抱有偏执的热情,也都对艺术也怀有高涨的热爱,这真的很奇妙。

王慧文从中体会到:爱因斯坦经常从艺术和审美的视角去探讨科学命题,人如果永远只站在一个视角来看待人生,人生就会变得特别狭隘,不够丰富,而站在不同视角来看待问题,不设限地长大,才非常重要。
不自我定义、不自我设限、不抗拒变化、不沉溺过去的王慧文,与“无边界”的美团极为匹配。

1
2
3
4
2012年底,美团实现盈亏平衡后,王慧文认为团购领域大局已定,又开始把目光聚焦在了外卖领域。
2015年10月,早已在“千团大战”中站稳脚跟的美团,开始跟大众点评整合,从T型战略逐步转向餐饮、酒旅、综合三大业务架构。几次调整后,到店餐饮事业群、外卖配送事业群和餐饮生态平台被统一归进餐饮平台,总负责人仍然是王慧文。
2017年12月,美团再次调整架构,王慧文担任大零售事业群总裁,统筹生鲜零售、外卖、配送、餐饮B2B等业务。同时,王慧文还负责出行事业部,正式和滴滴展开角逐。
至此,美团点评已经构建了新到店事业群、大零售事业群、酒店旅游事业群和出行事业部四大业务体系,王慧文是其中两大事业群的统帅。

如果管理者认为自己就是一个器皿,那么你就会认为自己的状态永远不会改变,也没有改变的必要。如果王慧文这样想,就不会为美团的基业立下赫赫战功。
只有当你明白“君子不器”的道理时,才会意识到成长的必要性,也才会清醒地看到自己过去的弱点。
不自我设限,多元思考人生,始终相信自己有更多机会,这样的管理者才能持续地自我突破,才能在激烈的竞争中拼杀出属于自己的战场。

二、“练强兵,结硬寨,打呆仗”,

造就能征善战的王慧文
王慧文信奉曾国藩的九字箴言:练强兵,结硬寨,打呆仗。

1. 有担当的管理者,麾下自有万千雄兵

“练强兵”很容易理解,当时清朝腐败,太平天国武装起义,清朝的正规军竟无力抵抗,只好调动地方军事力量,多亏有曾国藩统率的湘军,才能击败太平军。此后,湘军成为了清朝军事上强有力的骨干。
没有强兵,则无法作战。但强兵如何练成?
曾国藩治理湘军,主要凭思想纪律,而不是战术技巧。
王慧文“练强兵”,看重三点:人聪明,接地气,学习能力强(不是考试能力强)。
其次,王慧文主张:管理者的一个重要责任,就是——担当。
王慧文有一句话曾经火遍全网:
有担当的管理者,要把下属从“愚昧之巅”推向“绝望之谷”,至于下属能否爬上“开悟之坡”,看个人造化。
王慧文解释,这句话的核心词是“担当”。
他向来不喜欢说“责任”,因为他认为:责任可以逃避,只有有担当的管理者才不会逃避责任。
那么,什么是“愚昧之巅”、“绝望之谷”、“开悟之坡”?
这些词语源于 达克效应 ,王慧文说:我思考我个人的成长和周围同事的成长,每个人都希望自己最后成为智者(大师),但是,我们要承认一个事实,大部分人没有走上去。

愚昧之巅——人的智慧极低,但是自信度极高;
绝望之谷——认识到自己的不足,智慧有所增长,自信度大幅降低;
开悟之坡——智慧继续增长,自信度开始反弹增长;
持续平稳高原——智慧达到顶级,自信心也持续平稳,走上大师之路。

王慧文说:大部分人没能从愚昧之巅走到绝望之谷,大部分人都在这个部分遇到了困难。
王慧文借用“达克效应”理论,不是为了拆穿别人的智慧不足,而是为了强调管理者的担当,他认为:

1
2
3
大部分人都知道别人处在愚昧之巅,但是很少有人知道自己处在愚昧之巅,这就产生了非常大的信息不对称。
为什么人不知道自己在愚昧之巅?就是因为每一个人在自己成长的过程中,没有得到有效反馈,没有人告诉他“你现在在愚昧之巅”。因此,有效反馈变得非常重要和稀缺。
但是再进一步思考,为什么很多人没有得到这个反馈?因为别人没有这个责任反馈给你,而且戳破真相是有风险的。

王慧文觉得他们在离开美团的环境后,很可能更没希望得到他人的有效反馈了,所以他有好几次,在同事或下属离职时,告诉了对方正处于愚昧之巅的真相,但是转眼就被拉黑了。
尽管王慧文承认他们是一个好人、正直的人,也是一个想成长的、对美团有过贡献的人,将来也会成为对社会有贡献的人,王慧文希望对方成长得更好,才戳破真相,但是要戳破真相、给他人有效反馈,就必须承担得罪人的风险。

也正因此,管理者才更需要担当。
王慧文说:你给别人反馈的时候,也可能你自己是一个傻×,可能我自己是在愚昧之巅,其实人家是对的,这个可能性也是很大的,通过这个行为反而证明了你可能在愚昧之巅的事实。
管理者能不能从善意出发,帮下属敲响警钟?又能不能在被误解时,甘愿承受来自下属的伤害?或者是在发现自己比下属更愚昧的时候,还能不能调整心态,通过努力学习来提升自己的智慧和自信?
回答这些问题的最好方式,就是努力向一个有担当的管理者去逼近。
担当,是管理者们最稀缺的能力,但却是“练强兵”必不可少的素质。

2. 优秀管理者,都在暗下笨功夫

“结硬寨,打呆仗”,是曾国藩的兵法内核。字面意思是,把军营扎得坚如磐石,打仗时不讲究技巧而是追求笨方法。
曾国藩带兵打仗有个规矩,他到任何地方安营扎寨之后,不论当时是刮风还是下雨,首先命令士兵们挖掘战壕,壕深约两米,而且还要筑墙,墙高约八尺高,墙外再挖一道沟。这就确保整个营盘固若金汤。
湘军总是要挖沟、筑墙,行军速度非常慢。由于这种打法显得特别笨,所以才叫“结硬寨,打呆仗”。
王慧文的工作速度并不慢,但他也爱下笨功夫,一个细节一个细节得打磨,确保每个业务流程都能稳扎稳打。
美团前员工这样评价王慧文:老王可能是我见过在互联网行业,大佬级别中所谓“比你聪明还更拼搏”,并且会关注很多业务细节的人。
工作起来,王慧文就像变成了拼命三郎,让很多比他年轻的人都心生畏惧。
据美团前员工回忆:每周六下午,是大团队的业务管理会,每个分支业务的负责人要详细地讲解业务进度,用数据说话,以结果为导向,绝对不让周报变成“流水账”;
在这个过程中,王慧文会非常认真地听每个人的讲解,绝不走过场,绝不讲废话;
某个员工在周报中提及“某个数据变化属于正常波动”,这是产品负责人没有特别认真思考或经验欠缺、全局感不强而惯用的一个理由,而此时王慧文则会一针见血地指出是因为什么问题、什么原因导致的数据变化,让人心服口服。
王兴在内部信里这样评价王慧文:

1
2
美团精神,老王身体力行、堪称典范。回顾老王过去九年多的工作,既有冲锋在前的勇猛,又有安营扎寨的稳健;既有舍我其谁的担当,又有功成不必在我的潇洒;既有“天下兴亡,匹夫有责”的责任感,又有“我们什么都没有,但是我们有兄弟和勇气”的真性情;既长期有耐心地保持战略定力,又坚持时不我待、只争朝夕地忘情投入。
美团人经常加班到凌晨两三点,但转天早上九点又准时坐到办公室里开会,这让很多人对美团这家公司佩服不已。

2013年6月,美团收购猛买网,猛买网创始人张智勇在加入美团之后,曾发微博说:牛x都是苦x堆出来的,你看美团网牛x,没看到的都是苦x。原来公司没有比它做得好,就是我不够苦x,是因为我管理不好。在这里,每月每周都能学到东西,蛮好的。辛苦没关系,能学到东西就行,最怕加班没结果。

对王慧文来说,周六全天开会,周日半天高管会,每周留给自己的时间或许只有一个下午而已。但王慧文向外界和美团内部员工展现的,永远是一副精力充沛、充满激情的样子。
这就像将军带兵打仗,“跟我上”和“给我上”,一字之差,但全军的战斗力却会有天壤之别。
前者能把团队里每个人的心思都拧成一股绳,无往不胜;后者却永远苦于团队像一盘散沙。

三、“不宜贪天之功,知止不殆”,

造就未来不可限量的王慧文
王慧文在今年年初的内部信里,把自己在美团拼搏的17年激情燃烧岁月,轻描淡写地说成了“我运气实在太好”。
低调、收敛,不是王慧文今天才有的风格,而是他始终具备的性格底色。
王慧文经常开玩笑地说,我是农民的孩子,只会拼命工作。玩笑的背后,是他专注做事的态度,以及不居功自傲的格局和胸襟。
管理者要常常怀有“清零思维”,过去的错误要变成未来的前车之鉴,不能用来惩罚自己;过去的成就要变成未来的经验和警示,不能用来炫耀自己、骄傲自满。
如果管理者觉得自己“满了”,也就意味着,你认为自己的成长之路到头了。
清零思维也是一种包容心。《九败一胜:美团创始人王兴创业十年》写道:王慧文曾去过无锡,见到88米高的灵山大佛,从远处看大佛,跟山一样高,走近大佛,人还没有大佛的脚趾头高。
王慧文心想,如果这尊大佛有脚臭的话,那么来观光的人就只能闻到大佛的脚臭味,而看不见整个大佛的巍巍如山。只有包容的人,心胸宽广,眼界开阔,才看得到大佛的宝相庄严。
该书的作者李志刚说:也只有包容的CEO,才能听到下面的真实心声,兼听则明,才能有各种奇人异士来帮你做事。
王慧文这17年来,已经为美团培养了大批人才,也为人才梯度建设付出了贡献,他离职后,自然也不需要人才来帮自己做事了,但是这种包容心会始终伴随他,让他的未来无法限量。
王慧文也包容对手,他和王兴都不认为美团是在跟同行们“争”,美团是在“竞”,他说:我们不认为千团大战是我们打赢了别人,而是我们跟上了行业节奏,他们没有跟上,这个认知是跟打仗不一样的;同向为竞,相向为争。

包容,就是不眷恋。

王慧文不眷恋名利荣誉,他说“我运气实在太好,不宜继续贪天之功,知止不殆”;
王慧文不眷恋自己的能力,他说“感谢我的家人,承受很多困难,无怨无尽地给我支持和包容”,还感谢了时代、王兴和所有同事;
王慧文不眷恋他的江湖,他说“美团十年,豪兴不浅。他日江湖相逢,再当杯酒言欢”。

金庸先生曾写:天上白云,散了又聚,聚了又散,人生离合,亦复如斯。

今天,我们预测王慧文的人生“下半场”会有怎样的风景,已经是徒劳,因为他早已选择了一条“不确定”的人生旅程。
王慧文说过,固定靶比移动靶更难打中。人生就像一个移动靶,我们谁也不知道自己的未来将穿梭至何处,也几乎不可能帮别人预测。
我们只能希望,当自己修炼出清零思维、包容心、不眷恋的人生态度时,能够跟上时代变化莫测的脚步,不要掉队。
不论人生的下半场将会如何,至少王慧文已经可以潇洒地说出一句——“这十年激烈精彩,不负年华”。

四、参考

美团王慧文:很少人知道自己在愚昧之巅

]]>
- - - - - 随说 - - - - - - - 王慧文 - - - -
- - - - - MySQL分页查询 - - /20200712-mysql-page-query/ - - 零、分页查询

分页查询一般都会想到 offset limit

问题1

当不停机需要全量同步数据时
有可能会漏掉或者重复处理很多数据
因为中途会有数据改动
每次分页内的数据与预期的会有出入

问题2

offset 会进行全表扫描

一、推荐方案

通过不变的字段进行排序
最好是使用递增的主键索引

1
2
3
4
5
select *
from hisen
where id > ${lastId}
order by id
limit ${pageSize};

二、参考

请不要将OFFSET和LIMIT用于分页

]]>
- - - - - mysql - - - - - - - mysql - - - -
- - - - - 关于部署软系统的思考 - - /20200627-thinking-of-deploy-soft-system/ - - 在 敏捷 + devops 盛行的年代
软件部署的频率一般都比较高,一周一迭代、一周两迭代
这就需要有良好的工具来支持,更需要良好的机制来避免人为犯错

工作当中会遇到很多部署导致的问题
其中大部分的问题原因是配置问题

关于部署

  1. 开发的时候记录大致改动点
  2. 上线之前按检查列表(团队或个人定义一些常规检查项目)挨个检查
  3. 上线之前写上线步骤列表,部署时做一个人肉机器人,按步骤操作即可
  4. 上线之前写好验证步骤,验证需要的各种权限
  5. 以上做完了最好找相关人员核对一遍,以免遗漏(互备很重要)
  6. 灰度发布(应用启动期间性能抖动问题值得研究)

互备很重要,哪怕对方没有介入开发,简单的按正常流程问你几个问题,也许就会发现漏了什么。

]]>
- - - - - soft - - - - - - - soft - - - -
- - - - - 赢得财富:用独到的知识/责任感/杠杆 - - /20200517-specific-knowledge-accountability-leverage/ - - 零、读后感

美国风险投资家 Naval Ravikant 通过一个很长的推特阐明了他的商业观,感觉挺精辟。
积累独到的知识(无法批量复制的),为社会提供他们需要的。
财富不是靠出卖时间获得,应该通过杠杆(资本、人力、一本万利的工具<写书等>)去积累。
选择一个可以长期从事的行业,寻找一批可以长期共事的人。

一、原文(及翻译)

  1. Seek wealth, not money or status. Wealth is having assets that earn while you sleep. Money is how we transfer time and wealth. Status is your place in the social hierarchy.

    去寻求财富,而非金钱或地位。财富就是你拥有资产,而资产在你睡觉的时候都还在为你赚钱;金钱是我们转换时间和财富的工具;身份是你在社会等级体系里所处的位置。

  2. Understand that ethical wealth creation is possible. If you secretly despise wealth, it will elude you.

    要明白一件事:一个人完全可以不靠坑蒙拐骗站着赚取财富。如果你在暗中鄙视财富,那么财富也会躲着你。

  3. Ignore people playing status games. They gain status by attacking people playing wealth creation games.

    别去理会那些热衷于玩身份游戏的人,他们通过攻击那些创造财富的人以获得自己的身份。

  4. You’re not going to get rich renting out your time. You must own equity — a piece of a business — to gain your financial freedom.

    你不会通过出租自己的时间而变得富有。你必须拥有产权,也就是生意的一部分,以此才能赢得个人财务自由。

  1. You will get rich by giving society what it wants but does not yet know how to get. At scale.

    提供社会大众想要但是他们还不知道如何获取的东西,你就会因此而致富。但有一点:你必须规模化地供应社会。

  2. Pick an industry where you can play long term games with long term people.

    选择一个你可以长期从事的产业,寻找一批可以一起长期共事的人。

  3. The Internet has massively broadened the possible space of careers. Most people haven’t figured this out yet.

    互联网极大拓展了一个人职业生涯的可能性。绝大多数人对此毫无认知。

  4. Play iterated games. All the returns in life, whether in wealth, relationships, or knowledge, come from compound interest.

    玩就玩复利游戏。无论是财富,人际关系或者是知识,所有你人生里获得的回报,都来自复利。

  5. Pick business partners with high intelligence, energy, and, above all, integrity.

    在选择商业合作伙伴的时候,选择那些高智商、精力旺盛的家伙,但在这一切之上,他应该是个正直诚实的人。

  6. Don’t partner with cynics and pessimists. Their beliefs are self-fulfilling.

    不要和愤世嫉俗者和悲观主义者合作,因为他们会任由坏事发生,以此证明他们的负面看法是正确的。

  7. Learn to sell. Learn to build. If you can do both, you will be unstoppable.

    学会如何销售,学会如何创建。如果你同时能做到这两件事,你的成功将无可阻挡。

  8. Arm yourself with specific knowledge, accountability, and leverage.

    用独到知识,责任感和杠杆武装自己。

  9. Specific knowledge is knowledge that you cannot be trained for. If society can train you, it can train someone else, and replace you.

    独到知识是那种不可以通过培训而获得的知识。这是因为,如果这种知识可以经由培训而得,那么其他人同样也可以,并且以此取代你。

  10. Specific knowledge is found by pursuing your genuine curiosity and passion rather than whatever is hot right now.

    在真正的好奇心和热情驱使你前进的路上,你更有可能获得独到知识,而不是在追逐潮流热点的闻风起舞脚步里。

  11. Building specific knowledge will feel like play to you but will look like work to others.

    创建独到知识的过程对于你就像是在玩,而对于别人则像是工作。

  12. When specific knowledge is taught, it’s through apprenticeships, not schools.

    不能通过学校教育教会一个人独到知识,它只能通过学徒制口传身教。

  13. Specific knowledge is often highly technical or creative. It cannot be outsourced or automated.

    独到知识通常极富技术性和创造性,因此它不能被外包或自动实现。

  14. Embrace accountability, and take business risks under your own name. Society will reward you with responsibility, equity, and leverage.

    拥抱责任感,押上自己的声誉以承担商业风险。社会也会以责任,产权和杠杆作为回报。

  15. The most accountable people have singular, public, and risky brands: Oprah, Trump, Kanye, Elon.

    最具责任感的人都具有独一无二的、世人皆知的、敢于冒险的个性特征,如奥普拉、川普、坎耶、埃隆。

  16. “Give me a lever long enough, and a place to stand, and I will move the earth.” — Archimedes

    只要给我一根足够长的杠杆,一处可以立足的地方,我就能撬起地球。——阿基米德

  17. Fortunes require leverage. Business leverage comes from capital, people, and products with no marginal cost of replication (code and media).

    财富增长需要使用杠杆。商业杠杆有三个来源:1、资本;2、人力;3、复制起来边际成本为零的产品(如:代码和媒体)。

  18. Capital means money. To raise money, apply your specific knowledge, with accountability, and show resulting good judgment.

    资本的意思就是钱。想要融资,那就运用你的独到知识,配合你责任感,展示出你良好的判断力。

  19. Labor means people working for you. It’s the oldest and most fought-over form of leverage. Labor leverage will impress your parents, but don’t waste your life chasing it.

    人力指的就是为你干活的人,它是最古老也是争夺最激烈的杠杆。人力杠杆会让你父母因为你手下有许多人为你工作而感到骄傲,但你不要浪费生命去追求这一点。

  20. Capital and labor are permissioned leverage. Everyone is chasing capital, but someone has to give it to you. Everyone is trying to lead, but someone has to follow you.

    资本和劳动力是需要征得许可才能使用的杠杆。每个人都在追逐资本,但总得有个什么人给你才行;每个人都想要领导其它人,但总得有什么人愿意跟着你才行。

  21. Code and media are permissionless leverage. They’re the leverage behind the newly rich. You can create software and media that works for you while you sleep.

    代码和媒体是无需要许可即可使用的杠杆。它们是新贵人群背后的杠杆,你可以通过自己创建的软件和媒体,在睡觉时仍然为你干活。

  22. An army of robots is freely available — it’s just packed in data centers for heat and space efficiency. Use it.

    一支机器人军团已经集结待命,只是为了节约空间和热效能,它们被打包放进数据中心。去用吧。

  23. If you can’t code, write books and blogs, record videos and podcasts.

    如果你不会编程,那你还可以写书和博客,或者做视频或者音频节目。

  24. Leverage is a force multiplier for your judgement.

    杠杆能够成倍地放大你的判断力(所产生的效能)。

  25. Judgement requires experience, but can be built faster by learning foundational skills.

    判断力需要经验,但它可以通过学习基本技能的方法更快速地建立起来。

  26. There is no skill called “business.” Avoid business magazines and business classes.

    并不存在一种叫做“商业”的能力。尽量避开商业杂志和商业课程。

  27. Study microeconomics, game theory, psychology, persuasion, ethics, mathematics, and computers.

    去学习微观经济学、博弈论、心理学、说服术、伦理学、数学和计算机科学。

  28. Reading is faster than listening. Doing is faster than watching.

    读比听快,做比看快。

  29. You should be too busy to “do coffee,” while still keeping an uncluttered calendar.

    你应该忙得没有社交的时间才对,与此同时你应该始终保证日程安排井井有条。

  30. Set and enforce an aspirational personal hourly rate. If fixing a problem will save less than your hourly rate, ignore it. If outsourcing a task will cost less than your hourly rate, outsource it.

    你应该为自己设定一个有抱负的个人时薪数,并且坚持执行。如果解决一个问题所能节省下来的成本低于你的个人时薪,那就忽略这个问题好了;如果一项任务的外包成本低于你的个人时薪,就把它外包出去。

  31. Work as hard as you can. Even though who you work with and what you work on are more important than how hard you work.

    尽管你跟谁一起工作、做什么工作,要远比你的努力程度更加重要。但还是要倾尽全力去工作。

  32. Become the best in the world at what you do. Keep redefining what you do until this is true.

    你所做的事情,要努力做到世界最好。不断重新定义你在做什么,直到真的做到世界最好。

  33. There are no get rich quick schemes. That’s just someone else getting rich off you.

    这个世界上并没有快速赚钱致富的方法,如果你想要找寻这种方法,那它只会让别人从你身上赚钱致富。

  34. Apply specific knowledge, with leverage, and eventually you will get what you deserve.

    运用你的独到知识,配合上杠杆,最终你会得到你应该得到的东西。

  35. When you’re finally wealthy, you’ll realize that it wasn’t what you were seeking in the first place. But that’s for another day.

    终有一天当你变得富有,你会发现那一切并不是你最开始想要的东西。但是那就是另外一回事了。

二、译者注释

  1. 财富就是你睡着觉,你的资产也在为你继续赚钱。这是一个越来越被广泛接受的定义。Naval Ravikant 是硅谷狂热的数字货币支持者,所以,他的话另有所指。从前后文来看,他所谓的资产并不等于是传统意义上的房产、股票、收藏,而是偏向于他反复提及的:软件和媒体。

  2. 出租时间概念,许多人理解为打工,认为打工就是出租自己的时间以换取金钱。其实并非如此,Naval 所指的出租时间概念,指的是一个人的财富增长,是否直接关系到他的时间。一个小卖部的老板,他并不为谁打工,但是他的财富增长需要他长时间守在店里,因此,他依然是出租时间换钱。但一个淘宝点卡店老板则不同,他的点卡销售是全自动的,不需要 24 小时守着,而且也不需要只做这一样生意。这就是Naval所谓互联网拓宽了个人职业生涯的一个例子。

  3. equity 我翻译为产权,不是一个很好的翻法。但是 Naval 前文提到 assets,很明显,作为投资人他非常清楚地知道这两个字眼之间的区别。equity 无论是翻译为股票、权益或者是资产,原文说“You must own equity — a piece of a business — to gain your financial freedom.”,这是和出租时间概念做对应的。出租时间的人,在商业链条里作为生产资料出现,不拥有任何产权,也就无法通过商业行为获利,所以,我这里勉强翻译为产权。

  4. specific knowledge 我翻译为独到知识,没有翻译为特定知识、专业知识或者是特殊知识。原因是在我的理解中,specific knowledge 不是书本知识,也不是学校教授的知识,更不可能在网上免费获取。一方面,它只能提供自己实践来获取;另一方面,它只能通过前人口耳相传。这种知识是做成一件事情的关键,属于知识体系中不共的那一部分。所以,我翻译为独到知识。

  5. “Give me a lever long enough, and a place to stand, and I will move the earth.” — Archimedes 这话不像是阿基米德说的。更像是一次抬杠的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    “给我一个支点,我就能撬起地球!”

    “那么,您站在哪儿呢?”

    “好吧,给我一个支点,再给我一个站立的地方,我就能撬起地球。”

    “那么,您用空气就能撬起地球了?”

    “好吧,给我一根足够长的杠杆,一处可以立足的地方,我就可以翘起地球!”

    “那么,阿基米德先生,支点又不需要了吗?”

    “滚!”
  6. accountability 我本想翻译为“靠谱程度”,想想还是算了。

  7. 号称是“四十条语录”,但是我就找见了 39 条。

  8. 结合上下文看,Leverage 一词始终翻译为“杠杆”其实也不大对头。Naval 一再强调代码、博客、播客、视频节目,我觉得 Leverage 在他那里,有些时候应该相当于是个人影响力的代名词,或者可以简单理解为放大器。

三、参考:

  1. 科技爱好者周刊-107期
  2. 如何不靠运气致富(和菜头翻译)
]]>
- - - - - 随说 - - - - - - - 财富 - - - -
- - - - - Java 反序列化漏洞 - - /20200510-java-deserialize-security-hole/ - - 微博目前关注了好几个做安全的大佬
不要问我为什么关注
问就是想观摩下有钱人的微博!

其中有一个经常发 Java 相关的内容
今天看到一个稍微熟悉一点关于 FastJson 漏洞

窃以为Fastjson入门的最佳篇章非Mi1k7ea这个系列莫属,郑重推荐: Mi1k7ea
共有5篇,从第3篇开始,成体系地由旧到新展示了1.2.25之后各版本的补丁绕过,
直至思路惊艳的1.2.47绕过方案及1.2.48的修补方案,相当完备。
可以看出此君是动手实践派、整理归纳派,要我说,新学东西时就得这套路,无它。

今天看了这两篇文章,不得不说对 Java 是异常的熟悉。
想做好安全,想必要对用的东西了如指掌,知道坑在哪里,怎么填坑。

后记:
很多时候大部分人都只关注自己工作职责内的那一亩三分地
其实很多时候横向观察一下会发现很多靓丽的风景
至今没有如此深入的去看过底层甚是愧疚
用最多的时间去做最正确的事情!

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 开源画图工具(各种图):draw.io 支持 Web、macOS、Windows、Linux - - /20200406-draw.io-open-source-drawing-tools-support-web-mac-windows-linux/ - - draw.io 一个很棒的开源画图工具,可以导出 xml、pdf、html、png 等各种格式
其实重点是开源,目前好几个地方看到有在用,之前用网页版,现在用桌面版多些

网页版:draw.io
桌面版:github 下载支持:Windows、macOS、Linux、Google Chrome OS

]]>
- - - - - soft - - - - - - - soft - - - -
- - - - - Java 日志配置文件 - self4j 之 logback 和 log4j - - /20200406-self4j-logback-log4j-log-properties/ - - 写这种博客总感觉很低级
但是不得不承认自己从来没有认真的了解过打日志这件事
这里开个头,先从常见的日志配置文件开始,后续再深入了解

“I wish i had” vs “I’m glad i did”
人生会不会留下遗憾,在 20 年之后的这两句话中就能看出。
希望 20 年后的自己,能够很自豪的说:I’m glad i did.

一、self4j

这是一个只定义了标准的日志组件

二、logback

常见配置:logback.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="APP" value="hisenyuan-log-project"/>
<!--文件输出-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--存储路径-->
<file>/export/log/${APP}/${APP}_detail.log</file>
<!--按天分割-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>/www/log/${APP}/${APP}_detail.%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0} %X{trace-id} - %m%n</pattern>
</encoder>
</appender>
<!--控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0} %X{trace-id} - %m%n</pattern>
</encoder>
</appender>

<!--你想排除xxx包下面的日志-->
<logger name="kafka.utils.Logging" additivity="false">
<appender-ref ref="FILE"/>
</logger>
<!--允许WARN以及以上的打印-->
<logger name="org.apache.http" additivity="false">
<level value="WARN" />
<appender-ref ref="FILE"/>
</logger>

<!--日志级别-->
<root level="INFO">
<!--启用日志文件打印日志-->
<appender-ref ref="FILE"/>
<!---->
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

三、log4j

常见配置:log4j.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE log4j:configuration SYSTEM
"http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration>
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{1} - %m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="INFO"/>
<param name="LevelMax" value="ERROR"/>
</filter>
</appender>

<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
<param name="Append" value="true"/>
<param name="file" value="/www/log/hisenyuan-log-project.log"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{1} - %m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="INFO"/>
<param name="LevelMax" value="ERROR"/>
</filter>
</appender>

<logger name="kafka.utils.Logging" additivity="false">
<appender-ref ref="FILE"/>
</logger>

<logger name="org.apache.http" additivity="false">
<appender-ref ref="FILE"/>
</logger>

<root>
<priority value="INFO"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>

</log4j:configuration>

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 找到好的学习方法 - - /20200226-find-a-good-way-to-learn/ - - 时间是公平的,对每个人都一样,不同之处在于每个人单位时间的产出值会有差异。
一直在寻找适合于自己的学习方法以提高时间的利用效率,以便不影响诗和远方==
奈何目前也没有太多满意的答案,或许生命的意义就是在于探索未知而又刺激的世界。
分享一些文字,提到了一些好的学习方法,抛砖引玉,共勉。

《贫穷的本质》告诉我们,贫穷的本质是认知。其实很多时候真的是认知,方法论决定了一个人可以走多远。

《刻意练习》原地踏步的练习是没意义的,大众眼里一万小时定律基本上是错误的,需要的是有进步练习;

《学习之道》十个不错的方法:运用回想、知识组块、简化类比、方法交替、间隔重复、注意休息、心理对照、自我测试、保持专注、困难先行;

《如何阅读一本书》里面有提到主题阅读,不同的人对同一主题会有不同的看法,可以对比吸收;

《程序员的职业素养》有提到卡塔练习,提高我们的肌肉反应,让写代码像聊天打字一遍的顺畅;

早些天看到一篇不错的帖子
对我感触最深的就是:4
标题:如果高效学习有什么秘诀的话,那就都在这里了:)

1)不要完美主义!
2)不要过度“学习路径依赖”,学习要冲着自己的目标去。
3)不要迷信权威的“好”教材。
4)不要看不起“薄薄”的“傻”教材,这些你看不起的学习材料,可能是你入门某个领域的关键。
5)不要迷信单一教材。
6)实践!
7)debug非常非常重要。
8)量变到质变。
9)最后,一定要相信时间的力量。

参考:

  1. 《贫穷的本质》
  2. 《刻意练习》
  3. 《学习之道》
  4. 《如果阅读一本书》
  5. 如果高效学习有什么秘诀的话,那就都在这里了:)
]]>
- - - - - 随说 - - - - - - - 随说 - - - -
- - - - - 我是怎样爱上阅读的 - - /20200201-how-i-fell-in-love-with-reading/ - - 零、概览

2015 年刚毕业的我懵懵懂懂看书都想睡觉
很开心来到北京遇到了许多美好的人和事
受到环境的影响一路走来成长加速收获颇丰

从 2016 开始慢慢喜欢上了阅读
从开始看书就想睡觉到现在一个6层书架都放不下的纸质书
从开始的『kindle voyage』到现在的『kindle oasis』第三代
从开始的只是在北京地铁5号线看下电子书到现在有点时间就看
慢慢地养成了阅读的习惯,生活也没有那么无聊,工作慢慢步入正轨
非技术书籍喜欢用 kindle 看,技术书还是纸书比较合适翻阅

简单统计
2020 51本
2019 71本
2018 38本
2017 48本(2017以及之前)

一、常见问题

Q:看书想睡觉怎么办?
A:先找有兴趣/好奇的主题

Q:看书记不住怎么办?
A:能不能记住无所谓,看的过程中有思考即可,好的书多看几遍

Q:哪来这么多时间?
A:合理安排时间,少浪费时间即可(通勤+早晚+中午,最少有3个小时时间)

二、为什么要阅读

读书使人充实,读史使人明智,读诗使人灵秀,
数学使人周密,科学使人深刻,伦理使人庄重,逻辑修辞学使人善辩

古人说开卷有益(无营养的除外)
通过阅读我们可以了解他人的经历
在他人的经历中我们可以得到经验
在他人的经验当中我们可以得到思想
一个人毕竟阅历有限,站在巨人的肩膀上你可以看的更高

科普性的书很多时候会恍然大悟
技术性的书能获得很多优秀的设计和实践以及学习新的技能
人文历史地里这些基本上是扩展知识面、提高个人综合素养、探索人生存在的意义

三、怎么开始阅读

从我的书单(hisen.me/booklist/)当中可以看出
刚开始的时候基本上都是兴趣导向
俗话说兴趣是最好的老师,有些好书恨不得立马就看完,
接触很多人说看书就想睡觉,其实那不是我们的错,
是书没有写好,换一本就好了,如果还是不行,那就继续换。

当找到感兴趣的主题,看了一段时间之后
会发现阅读速度会有所提升,也会有很强的满足感
毕竟看的过程中总会有所收获,有所思考

兴趣是最好的老师,但是兴趣不能当饭吃
这个时候应该开始找一些通俗易懂的技术书,而不是那些很难啃的枕头
还是以有趣,通俗易懂为主
坚持一段时间,技术上也会有所收获
慢慢的就可以驰骋技术书籍的战场了
如果有时候发现看不下去了
那么就切换其它类型的书籍(我一般都是一本技术一本非技术)

建议看看《如何阅读一本书》

四、怎么沉迷于阅读

当通过阅读,名利双收的时候,我想没有几个人会不沉迷于阅读
想做到名利双收,需要养成好的习惯,挑选好的书籍
建议看看相关领域大佬分享的书单,或者直接咨询
书籍看多了实践也得跟上
有条件可以合志同道合的人一起看同一本书,看的时候多交流探讨,会有意想不到的效果

五、去哪儿找书单

  1. hisen.me/booklist/
  2. 去问身边比自己强的人
  3. 查看业界大佬的书单(比如他们的博客/世界读书日分享等)
  4. 电子商务网站是看相关领域的排行榜
  5. GitHub 上各种学习类型的仓库之推荐书籍

六、小结

贫穷的本质是认知的差异
阅读、反馈、实践慢慢迭代认知
有长远的目标,不负韶华,坚持学习,努力提高

]]>
- - - - - 成长 - - - - - - - read - - - -
- - - - - 回顾2019,展望2020 - - /20191229-Looking-back-to-2019-looking-forward-to-2020/ - - 零、摘要

2019
1.【完成】换一份合适的工作
2.【完成】信息系统项目管理师
3.【完成】坚持阅读,数量质量超过2018

2020
1.羽毛球/骑行/等锻炼项目
2.通过一项含金量较高的考试项目
3.持续阅读/5本华章CS/其它不限
4.深入了解公司内部组件/知其所以然

2019预料之中夹着惊喜,正反馈激励我前行。
2020充实得度过每一天,有所追求有所进步。

一、成长

自己想要什么样的生活?

最近几年一直在思考这两个问题
如今已经慢慢勾勒出了自己想要的生活
大概就是舒服的活着,向身边人学习,帮助他人成长,体现自己的社会价值(马斯洛需求层次理论的4~5之间)

想清楚自己想要什么样的生活之后
接下来就是给自己设定一系列目标
例如:长期目标、5年规划、3年规划

抛砖引玉:

  1. 接触牛人,多和牛人交流取经/询问适当的建议,以之为榜样
  2. 保持好奇,对于自己不懂的技术或者其它任何事,各个角度去了解学习
  3. 管理精力,提高单位时间的效率,乐于锻炼,锻炼的人一般都精力充沛
  4. 持续学习,只有持续的投入系统的学习和总结方能保持竞争力
  5. 保持敬畏,认真负责得处理好份内得每一件事,再考虑锦上添花的事情

二、阅读

全部书单
从17年开始阅读量稍微上来一点到如今已经走过了快3个年头
在阅读中观摩了不少他人的经历、接触了各种不同的思维方式、看了很多恍然大悟的见解、领略了技术的魅力
有一句话说的不错:书是一面镜子,折射出一个人的方方面面
翻越自己的书单,也能看到一个变化的过程,从兴趣开始,逐步转向个人提升以及对精神世界的追求
目前发现不少的书籍内容有部分的”重叠”,所以有些书还是看的比较快,有些书值得重复阅读,特别是技术书

2019 Top8 (非权威排行)
《贫穷的本质》
《重塑大脑,重塑人生》
《领域驱动设计》(系列)
《代码简洁之道:程序员的职业素养》
《枪炮、病菌与钢铁:人类社会的命运》
《可伸缩服务架构:框架与中间件》
《Java并发编程的艺术》
《深入理解Java虚拟机》

三、生活

关键字:绿植、养鱼、骑行、羽毛球、辣椒

绿植和养鱼
看书累了/下班回家浇浇水赏赏鱼放空下自己挺好
偶尔还会收获成长的喜悦、以及把控感

骑行和羽毛球
运动方面实际行动比较多的今年是羽毛球
技术不咋地,野路子,打算2020培训下
骑行看季节看队友,春秋时节郊区遛遛挺好

辣椒
作为一个江西人,应该是无辣不欢的体质,无惧新型社交绝症

四、习惯

2019
起床:06:30
午休:20分钟
入睡:01:00

2020
起床:05:00
午休:20分钟
入睡:23:30

起床热身,煮早餐,基本上八点半左右出门
步行过程中耳机听互联网广播(目前是:36氪随身听)
地铁上耳机切换至音乐模式看书(kindle/纸书)
2020走养生路线,早睡早起!
中午看20分钟的书
早上看约1~3小时
晚上看书0~1小时

关注周围的事情,了解动态,吸取营养,输出影响力

五、随说

  1. 每天花点时间冥想放松效果很好(多次)
  2. 写字好处多,整理思绪、调整心情、集中注意力
  3. 多给予正反馈、多微笑
  4. 减少无效的时间开支(短视频/垃圾新闻/娱乐等性价比低的活动)
  5. 做任何事情始于兴趣/好奇/压力,持续于正反馈(得到自己想要的/有益的)
  6. 把自己所见所学所想进行归纳总结,寻求内在的统一是一个很有趣的过程
  7. 感谢遇到的人和事,做一个高度容错的系统,并且在错误中捞金
  8. 想办法优化自己的思维方式(阅读/取经/经历)
  9. 写博客/发微博是一个自我总结和展示的有效途径(不一定要写多好)

六、推荐

  1. 科技爱好者周刊
  2. 王垠
  3. 彭博商业周刊(刊物)
  4. 中国国家地里(刊物)
  5. 36氪随身听(广播)
]]>
- - - - - 随说 - - - - - - - 随说 - - - -
- - - - - 全球国家和地区列表,中英文国名、国际代码、电话区号(SQL & 文本) - - /20191120-Global-country-and-region-information-list/ - - 零、说明

包含全球200+国家和地区的信息
本列表属于之前有外国人整理过
利用有道翻译API翻译了国家名
涉及到全球的业务应该会用的上

一、SQL

sql如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `sms_country`
-- ----------------------------
DROP TABLE IF EXISTS `sms_country`;
CREATE TABLE `sms_country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`iso` char(2) NOT NULL,
`iso3` char(3) DEFAULT NULL,
`name` varchar(80) NOT NULL,
`name_zh` varchar(80) DEFAULT NULL,
`nicename` varchar(80) NOT NULL,
`numcode` smallint(6) DEFAULT NULL,
`phonecode` int(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=240 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sms_country
-- ----------------------------
INSERT INTO `sms_country` VALUES ('1', 'AF', 'AFG', 'AFGHANISTAN', '阿富汗', 'Afghanistan', '4', '93');
INSERT INTO `sms_country` VALUES ('2', 'AL', 'ALB', 'ALBANIA', '阿尔巴尼亚', 'Albania', '8', '355');
INSERT INTO `sms_country` VALUES ('3', 'DZ', 'DZA', 'ALGERIA', '阿尔及利亚', 'Algeria', '12', '213');
INSERT INTO `sms_country` VALUES ('4', 'AS', 'ASM', 'AMERICAN SAMOA', '美属萨摩亚', 'American Samoa', '16', '1684');
INSERT INTO `sms_country` VALUES ('5', 'AD', 'AND', 'ANDORRA', '安道尔', 'Andorra', '20', '376');
INSERT INTO `sms_country` VALUES ('6', 'AO', 'AGO', 'ANGOLA', '安哥拉', 'Angola', '24', '244');
INSERT INTO `sms_country` VALUES ('7', 'AI', 'AIA', 'ANGUILLA', '安圭拉岛', 'Anguilla', '660', '1264');
INSERT INTO `sms_country` VALUES ('8', 'AQ', null, 'ANTARCTICA', '南极洲', 'Antarctica', null, '0');
INSERT INTO `sms_country` VALUES ('9', 'AG', 'ATG', 'ANTIGUA AND BARBUDA', '安提瓜岛和巴布达', 'Antigua and Barbuda', '28', '1268');
INSERT INTO `sms_country` VALUES ('10', 'AR', 'ARG', 'ARGENTINA', '阿根廷', 'Argentina', '32', '54');
INSERT INTO `sms_country` VALUES ('11', 'AM', 'ARM', 'ARMENIA', '亚美尼亚', 'Armenia', '51', '374');
INSERT INTO `sms_country` VALUES ('12', 'AW', 'ABW', 'ARUBA', '阿鲁巴岛', 'Aruba', '533', '297');
INSERT INTO `sms_country` VALUES ('13', 'AU', 'AUS', 'AUSTRALIA', '澳大利亚', 'Australia', '36', '61');
INSERT INTO `sms_country` VALUES ('14', 'AT', 'AUT', 'AUSTRIA', '奥地利', 'Austria', '40', '43');
INSERT INTO `sms_country` VALUES ('15', 'AZ', 'AZE', 'AZERBAIJAN', '阿塞拜疆', 'Azerbaijan', '31', '994');
INSERT INTO `sms_country` VALUES ('16', 'BS', 'BHS', 'BAHAMAS', '巴哈马群岛', 'Bahamas', '44', '1242');
INSERT INTO `sms_country` VALUES ('17', 'BH', 'BHR', 'BAHRAIN', '巴林', 'Bahrain', '48', '973');
INSERT INTO `sms_country` VALUES ('18', 'BD', 'BGD', 'BANGLADESH', '孟加拉国', 'Bangladesh', '50', '880');
INSERT INTO `sms_country` VALUES ('19', 'BB', 'BRB', 'BARBADOS', '巴巴多斯', 'Barbados', '52', '1246');
INSERT INTO `sms_country` VALUES ('20', 'BY', 'BLR', 'BELARUS', '白俄罗斯', 'Belarus', '112', '375');
INSERT INTO `sms_country` VALUES ('21', 'BE', 'BEL', 'BELGIUM', '比利时', 'Belgium', '56', '32');
INSERT INTO `sms_country` VALUES ('22', 'BZ', 'BLZ', 'BELIZE', '伯利兹', 'Belize', '84', '501');
INSERT INTO `sms_country` VALUES ('23', 'BJ', 'BEN', 'BENIN', '贝宁', 'Benin', '204', '229');
INSERT INTO `sms_country` VALUES ('24', 'BM', 'BMU', 'BERMUDA', '百慕大', 'Bermuda', '60', '1441');
INSERT INTO `sms_country` VALUES ('25', 'BT', 'BTN', 'BHUTAN', '不丹', 'Bhutan', '64', '975');
INSERT INTO `sms_country` VALUES ('26', 'BO', 'BOL', 'BOLIVIA', '玻利维亚', 'Bolivia', '68', '591');
INSERT INTO `sms_country` VALUES ('27', 'BA', 'BIH', 'BOSNIA AND HERZEGOVINA', '波斯尼亚和黑塞哥维那', 'Bosnia and Herzegovina', '70', '387');
INSERT INTO `sms_country` VALUES ('28', 'BW', 'BWA', 'BOTSWANA', '博茨瓦纳', 'Botswana', '72', '267');
INSERT INTO `sms_country` VALUES ('29', 'BV', null, 'BOUVET ISLAND', '布维岛', 'Bouvet Island', null, '0');
INSERT INTO `sms_country` VALUES ('30', 'BR', 'BRA', 'BRAZIL', '巴西', 'Brazil', '76', '55');
INSERT INTO `sms_country` VALUES ('31', 'IO', null, 'BRITISH INDIAN OCEAN TERRITORY', '英属印度洋领地', 'British Indian Ocean Territory', null, '246');
INSERT INTO `sms_country` VALUES ('32', 'BN', 'BRN', 'BRUNEI DARUSSALAM', '文莱达鲁萨兰国', 'Brunei Darussalam', '96', '673');
INSERT INTO `sms_country` VALUES ('33', 'BG', 'BGR', 'BULGARIA', '保加利亚', 'Bulgaria', '100', '359');
INSERT INTO `sms_country` VALUES ('34', 'BF', 'BFA', 'BURKINA FASO', '布吉纳法索', 'Burkina Faso', '854', '226');
INSERT INTO `sms_country` VALUES ('35', 'BI', 'BDI', 'BURUNDI', '布隆迪', 'Burundi', '108', '257');
INSERT INTO `sms_country` VALUES ('36', 'KH', 'KHM', 'CAMBODIA', '柬埔寨', 'Cambodia', '116', '855');
INSERT INTO `sms_country` VALUES ('37', 'CM', 'CMR', 'CAMEROON', '喀麦隆', 'Cameroon', '120', '237');
INSERT INTO `sms_country` VALUES ('38', 'CA', 'CAN', 'CANADA', '加拿大', 'Canada', '124', '1');
INSERT INTO `sms_country` VALUES ('39', 'CV', 'CPV', 'CAPE VERDE', '佛得角', 'Cape Verde', '132', '238');
INSERT INTO `sms_country` VALUES ('40', 'KY', 'CYM', 'CAYMAN ISLANDS', '开曼群岛', 'Cayman Islands', '136', '1345');
INSERT INTO `sms_country` VALUES ('41', 'CF', 'CAF', 'CENTRAL AFRICAN REPUBLIC', '中非共和国', 'Central African Republic', '140', '236');
INSERT INTO `sms_country` VALUES ('42', 'TD', 'TCD', 'CHAD', '乍得', 'Chad', '148', '235');
INSERT INTO `sms_country` VALUES ('43', 'CL', 'CHL', 'CHILE', '智利', 'Chile', '152', '56');
INSERT INTO `sms_country` VALUES ('44', 'CN', 'CHN', 'CHINA', '中国', 'China', '156', '86');
INSERT INTO `sms_country` VALUES ('45', 'CX', null, 'CHRISTMAS ISLAND', '圣诞岛', 'Christmas Island', null, '61');
INSERT INTO `sms_country` VALUES ('46', 'CC', null, 'COCOS (KEELING) ISLANDS', 'COCOS(KEELING)岛', 'Cocos (Keeling) Islands', null, '672');
INSERT INTO `sms_country` VALUES ('47', 'CO', 'COL', 'COLOMBIA', '哥伦比亚', 'Colombia', '170', '57');
INSERT INTO `sms_country` VALUES ('48', 'KM', 'COM', 'COMOROS', '科摩罗', 'Comoros', '174', '269');
INSERT INTO `sms_country` VALUES ('49', 'CG', 'COG', 'CONGO', '刚果', 'Congo', '178', '242');
INSERT INTO `sms_country` VALUES ('50', 'CD', 'COD', 'CONGO, THE DEMOCRATIC REPUBLIC OF THE', '刚果民主共和国的', 'Congo, the Democratic Republic of the', '180', '242');
INSERT INTO `sms_country` VALUES ('51', 'CK', 'COK', 'COOK ISLANDS', '库克群岛', 'Cook Islands', '184', '682');
INSERT INTO `sms_country` VALUES ('52', 'CR', 'CRI', 'COSTA RICA', '哥斯达黎加', 'Costa Rica', '188', '506');
INSERT INTO `sms_country` VALUES ('53', 'CI', 'CIV', 'COTE D\'IVOIRE', '科特迪瓦', 'Cote D\'Ivoire', '384', '225');
INSERT INTO `sms_country` VALUES ('54', 'HR', 'HRV', 'CROATIA', '克罗地亚', 'Croatia', '191', '385');
INSERT INTO `sms_country` VALUES ('55', 'CU', 'CUB', 'CUBA', '古巴', 'Cuba', '192', '53');
INSERT INTO `sms_country` VALUES ('56', 'CY', 'CYP', 'CYPRUS', '塞浦路斯', 'Cyprus', '196', '357');
INSERT INTO `sms_country` VALUES ('57', 'CZ', 'CZE', 'CZECH REPUBLIC', '捷克共和国', 'Czech Republic', '203', '420');
INSERT INTO `sms_country` VALUES ('58', 'DK', 'DNK', 'DENMARK', '丹麦', 'Denmark', '208', '45');
INSERT INTO `sms_country` VALUES ('59', 'DJ', 'DJI', 'DJIBOUTI', '吉布提', 'Djibouti', '262', '253');
INSERT INTO `sms_country` VALUES ('60', 'DM', 'DMA', 'DOMINICA', '多米尼加', 'Dominica', '212', '1767');
INSERT INTO `sms_country` VALUES ('61', 'DO', 'DOM', 'DOMINICAN REPUBLIC', '多米尼加共和国', 'Dominican Republic', '214', '1809');
INSERT INTO `sms_country` VALUES ('62', 'EC', 'ECU', 'ECUADOR', '厄瓜多尔', 'Ecuador', '218', '593');
INSERT INTO `sms_country` VALUES ('63', 'EG', 'EGY', 'EGYPT', '埃及', 'Egypt', '818', '20');
INSERT INTO `sms_country` VALUES ('64', 'SV', 'SLV', 'EL SALVADOR', '萨尔瓦多', 'El Salvador', '222', '503');
INSERT INTO `sms_country` VALUES ('65', 'GQ', 'GNQ', 'EQUATORIAL GUINEA', '赤道几内亚', 'Equatorial Guinea', '226', '240');
INSERT INTO `sms_country` VALUES ('66', 'ER', 'ERI', 'ERITREA', '厄立特里亚', 'Eritrea', '232', '291');
INSERT INTO `sms_country` VALUES ('67', 'EE', 'EST', 'ESTONIA', '爱沙尼亚', 'Estonia', '233', '372');
INSERT INTO `sms_country` VALUES ('68', 'ET', 'ETH', 'ETHIOPIA', '埃塞俄比亚', 'Ethiopia', '231', '251');
INSERT INTO `sms_country` VALUES ('69', 'FK', 'FLK', 'FALKLAND ISLANDS (MALVINAS)', '福克兰群岛(马尔维纳斯)', 'Falkland Islands (Malvinas)', '238', '500');
INSERT INTO `sms_country` VALUES ('70', 'FO', 'FRO', 'FAROE ISLANDS', '法罗群岛', 'Faroe Islands', '234', '298');
INSERT INTO `sms_country` VALUES ('71', 'FJ', 'FJI', 'FIJI', '斐济', 'Fiji', '242', '679');
INSERT INTO `sms_country` VALUES ('72', 'FI', 'FIN', 'FINLAND', '芬兰', 'Finland', '246', '358');
INSERT INTO `sms_country` VALUES ('73', 'FR', 'FRA', 'FRANCE', '法国', 'France', '250', '33');
INSERT INTO `sms_country` VALUES ('74', 'GF', 'GUF', 'FRENCH GUIANA', '法属圭亚那', 'French Guiana', '254', '594');
INSERT INTO `sms_country` VALUES ('75', 'PF', 'PYF', 'FRENCH POLYNESIA', '法属波利尼西亚', 'French Polynesia', '258', '689');
INSERT INTO `sms_country` VALUES ('76', 'TF', null, 'FRENCH SOUTHERN TERRITORIES', '法国南部地区', 'French Southern Territories', null, '0');
INSERT INTO `sms_country` VALUES ('77', 'GA', 'GAB', 'GABON', '加蓬', 'Gabon', '266', '241');
INSERT INTO `sms_country` VALUES ('78', 'GM', 'GMB', 'GAMBIA', '冈比亚', 'Gambia', '270', '220');
INSERT INTO `sms_country` VALUES ('79', 'GE', 'GEO', 'GEORGIA', '乔治亚州', 'Georgia', '268', '995');
INSERT INTO `sms_country` VALUES ('80', 'DE', 'DEU', 'GERMANY', '德国', 'Germany', '276', '49');
INSERT INTO `sms_country` VALUES ('81', 'GH', 'GHA', 'GHANA', '加纳', 'Ghana', '288', '233');
INSERT INTO `sms_country` VALUES ('82', 'GI', 'GIB', 'GIBRALTAR', '直布罗陀', 'Gibraltar', '292', '350');
INSERT INTO `sms_country` VALUES ('83', 'GR', 'GRC', 'GREECE', '希腊', 'Greece', '300', '30');
INSERT INTO `sms_country` VALUES ('84', 'GL', 'GRL', 'GREENLAND', '格陵兰岛', 'Greenland', '304', '299');
INSERT INTO `sms_country` VALUES ('85', 'GD', 'GRD', 'GRENADA', '格林纳达', 'Grenada', '308', '1473');
INSERT INTO `sms_country` VALUES ('86', 'GP', 'GLP', 'GUADELOUPE', '瓜德罗普岛', 'Guadeloupe', '312', '590');
INSERT INTO `sms_country` VALUES ('87', 'GU', 'GUM', 'GUAM', '关岛', 'Guam', '316', '1671');
INSERT INTO `sms_country` VALUES ('88', 'GT', 'GTM', 'GUATEMALA', '危地马拉', 'Guatemala', '320', '502');
INSERT INTO `sms_country` VALUES ('89', 'GN', 'GIN', 'GUINEA', '几内亚', 'Guinea', '324', '224');
INSERT INTO `sms_country` VALUES ('90', 'GW', 'GNB', 'GUINEA-BISSAU', '几内亚比绍', 'Guinea-Bissau', '624', '245');
INSERT INTO `sms_country` VALUES ('91', 'GY', 'GUY', 'GUYANA', '圭亚那', 'Guyana', '328', '592');
INSERT INTO `sms_country` VALUES ('92', 'HT', 'HTI', 'HAITI', '海地', 'Haiti', '332', '509');
INSERT INTO `sms_country` VALUES ('93', 'HM', null, 'HEARD ISLAND AND MCDONALD ISLANDS', '听到岛和麦当劳的岛屿', 'Heard Island and Mcdonald Islands', null, '0');
INSERT INTO `sms_country` VALUES ('94', 'VA', 'VAT', 'HOLY SEE (VATICAN CITY STATE)', '教廷(梵蒂冈)', 'Holy See (Vatican City State)', '336', '39');
INSERT INTO `sms_country` VALUES ('95', 'HN', 'HND', 'HONDURAS', '洪都拉斯', 'Honduras', '340', '504');
INSERT INTO `sms_country` VALUES ('96', 'HK', 'HKG', 'HONG KONG', '香港', 'Hong Kong', '344', '852');
INSERT INTO `sms_country` VALUES ('97', 'HU', 'HUN', 'HUNGARY', '匈牙利', 'Hungary', '348', '36');
INSERT INTO `sms_country` VALUES ('98', 'IS', 'ISL', 'ICELAND', '冰岛', 'Iceland', '352', '354');
INSERT INTO `sms_country` VALUES ('99', 'IN', 'IND', 'INDIA', '印度', 'India', '356', '91');
INSERT INTO `sms_country` VALUES ('100', 'ID', 'IDN', 'INDONESIA', '印尼', 'Indonesia', '360', '62');
INSERT INTO `sms_country` VALUES ('101', 'IR', 'IRN', 'IRAN, ISLAMIC REPUBLIC OF', '伊朗伊斯兰共和国', 'Iran, Islamic Republic of', '364', '98');
INSERT INTO `sms_country` VALUES ('102', 'IQ', 'IRQ', 'IRAQ', '伊拉克', 'Iraq', '368', '964');
INSERT INTO `sms_country` VALUES ('103', 'IE', 'IRL', 'IRELAND', '爱尔兰', 'Ireland', '372', '353');
INSERT INTO `sms_country` VALUES ('104', 'IL', 'ISR', 'ISRAEL', '以色列', 'Israel', '376', '972');
INSERT INTO `sms_country` VALUES ('105', 'IT', 'ITA', 'ITALY', '意大利', 'Italy', '380', '39');
INSERT INTO `sms_country` VALUES ('106', 'JM', 'JAM', 'JAMAICA', '牙买加', 'Jamaica', '388', '1876');
INSERT INTO `sms_country` VALUES ('107', 'JP', 'JPN', 'JAPAN', '日本', 'Japan', '392', '81');
INSERT INTO `sms_country` VALUES ('108', 'JO', 'JOR', 'JORDAN', '约旦', 'Jordan', '400', '962');
INSERT INTO `sms_country` VALUES ('109', 'KZ', 'KAZ', 'KAZAKHSTAN', '哈萨克斯坦', 'Kazakhstan', '398', '7');
INSERT INTO `sms_country` VALUES ('110', 'KE', 'KEN', 'KENYA', '肯尼亚', 'Kenya', '404', '254');
INSERT INTO `sms_country` VALUES ('111', 'KI', 'KIR', 'KIRIBATI', '基里巴斯', 'Kiribati', '296', '686');
INSERT INTO `sms_country` VALUES ('112', 'KP', 'PRK', 'KOREA, DEMOCRATIC PEOPLE\'S REPUBLIC OF', '朝鲜民主主义人民共和国', 'Korea, Democratic People\'s Republic of', '408', '850');
INSERT INTO `sms_country` VALUES ('113', 'KR', 'KOR', 'KOREA, REPUBLIC OF', '朝鲜共和国', 'Korea, Republic of', '410', '82');
INSERT INTO `sms_country` VALUES ('114', 'KW', 'KWT', 'KUWAIT', '科威特', 'Kuwait', '414', '965');
INSERT INTO `sms_country` VALUES ('115', 'KG', 'KGZ', 'KYRGYZSTAN', '吉尔吉斯斯坦', 'Kyrgyzstan', '417', '996');
INSERT INTO `sms_country` VALUES ('116', 'LA', 'LAO', 'LAO PEOPLE\'S DEMOCRATIC REPUBLIC', '老挝人民民主共和国', 'Lao People\'s Democratic Republic', '418', '856');
INSERT INTO `sms_country` VALUES ('117', 'LV', 'LVA', 'LATVIA', '拉脱维亚', 'Latvia', '428', '371');
INSERT INTO `sms_country` VALUES ('118', 'LB', 'LBN', 'LEBANON', '黎巴嫩', 'Lebanon', '422', '961');
INSERT INTO `sms_country` VALUES ('119', 'LS', 'LSO', 'LESOTHO', '莱索托', 'Lesotho', '426', '266');
INSERT INTO `sms_country` VALUES ('120', 'LR', 'LBR', 'LIBERIA', '利比里亚', 'Liberia', '430', '231');
INSERT INTO `sms_country` VALUES ('121', 'LY', 'LBY', 'LIBYAN ARAB JAMAHIRIYA', '阿拉伯利比亚民众国', 'Libyan Arab Jamahiriya', '434', '218');
INSERT INTO `sms_country` VALUES ('122', 'LI', 'LIE', 'LIECHTENSTEIN', '列支敦斯登', 'Liechtenstein', '438', '423');
INSERT INTO `sms_country` VALUES ('123', 'LT', 'LTU', 'LITHUANIA', '立陶宛', 'Lithuania', '440', '370');
INSERT INTO `sms_country` VALUES ('124', 'LU', 'LUX', 'LUXEMBOURG', '卢森堡', 'Luxembourg', '442', '352');
INSERT INTO `sms_country` VALUES ('125', 'MO', 'MAC', 'MACAO', '澳门', 'Macao', '446', '853');
INSERT INTO `sms_country` VALUES ('126', 'MK', 'MKD', 'MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF', '前南斯拉夫马其顿共和国', 'Macedonia, the Former Yugoslav Republic of', '807', '389');
INSERT INTO `sms_country` VALUES ('127', 'MG', 'MDG', 'MADAGASCAR', '马达加斯加', 'Madagascar', '450', '261');
INSERT INTO `sms_country` VALUES ('128', 'MW', 'MWI', 'MALAWI', '马拉维', 'Malawi', '454', '265');
INSERT INTO `sms_country` VALUES ('129', 'MY', 'MYS', 'MALAYSIA', '马来西亚', 'Malaysia', '458', '60');
INSERT INTO `sms_country` VALUES ('130', 'MV', 'MDV', 'MALDIVES', '马尔代夫', 'Maldives', '462', '960');
INSERT INTO `sms_country` VALUES ('131', 'ML', 'MLI', 'MALI', '马里', 'Mali', '466', '223');
INSERT INTO `sms_country` VALUES ('132', 'MT', 'MLT', 'MALTA', '马耳他', 'Malta', '470', '356');
INSERT INTO `sms_country` VALUES ('133', 'MH', 'MHL', 'MARSHALL ISLANDS', '马绍尔群岛', 'Marshall Islands', '584', '692');
INSERT INTO `sms_country` VALUES ('134', 'MQ', 'MTQ', 'MARTINIQUE', '马提尼克岛', 'Martinique', '474', '596');
INSERT INTO `sms_country` VALUES ('135', 'MR', 'MRT', 'MAURITANIA', '毛利塔尼亚', 'Mauritania', '478', '222');
INSERT INTO `sms_country` VALUES ('136', 'MU', 'MUS', 'MAURITIUS', '毛里求斯', 'Mauritius', '480', '230');
INSERT INTO `sms_country` VALUES ('137', 'YT', null, 'MAYOTTE', '马约特岛', 'Mayotte', null, '269');
INSERT INTO `sms_country` VALUES ('138', 'MX', 'MEX', 'MEXICO', '墨西哥', 'Mexico', '484', '52');
INSERT INTO `sms_country` VALUES ('139', 'FM', 'FSM', 'MICRONESIA, FEDERATED STATES OF', '密克罗尼西亚联邦', 'Micronesia, Federated States of', '583', '691');
INSERT INTO `sms_country` VALUES ('140', 'MD', 'MDA', 'MOLDOVA, REPUBLIC OF', '摩尔多瓦共和国', 'Moldova, Republic of', '498', '373');
INSERT INTO `sms_country` VALUES ('141', 'MC', 'MCO', 'MONACO', '摩纳哥', 'Monaco', '492', '377');
INSERT INTO `sms_country` VALUES ('142', 'MN', 'MNG', 'MONGOLIA', '蒙古', 'Mongolia', '496', '976');
INSERT INTO `sms_country` VALUES ('143', 'MS', 'MSR', 'MONTSERRAT', '蒙特塞拉特', 'Montserrat', '500', '1664');
INSERT INTO `sms_country` VALUES ('144', 'MA', 'MAR', 'MOROCCO', '摩洛哥', 'Morocco', '504', '212');
INSERT INTO `sms_country` VALUES ('145', 'MZ', 'MOZ', 'MOZAMBIQUE', 'MOZAMBIQUE', 'Mozambique', '508', '258');
INSERT INTO `sms_country` VALUES ('146', 'MM', 'MMR', 'MYANMAR', '缅甸', 'Myanmar', '104', '95');
INSERT INTO `sms_country` VALUES ('147', 'NA', 'NAM', 'NAMIBIA', '纳米比亚', 'Namibia', '516', '264');
INSERT INTO `sms_country` VALUES ('148', 'NR', 'NRU', 'NAURU', '瑙鲁', 'Nauru', '520', '674');
INSERT INTO `sms_country` VALUES ('149', 'NP', 'NPL', 'NEPAL', '尼泊尔', 'Nepal', '524', '977');
INSERT INTO `sms_country` VALUES ('150', 'NL', 'NLD', 'NETHERLANDS', '荷兰', 'Netherlands', '528', '31');
INSERT INTO `sms_country` VALUES ('151', 'AN', 'ANT', 'NETHERLANDS ANTILLES', '荷属安的列斯群岛', 'Netherlands Antilles', '530', '599');
INSERT INTO `sms_country` VALUES ('152', 'NC', 'NCL', 'NEW CALEDONIA', '新喀里多尼亚', 'New Caledonia', '540', '687');
INSERT INTO `sms_country` VALUES ('153', 'NZ', 'NZL', 'NEW ZEALAND', '新西兰', 'New Zealand', '554', '64');
INSERT INTO `sms_country` VALUES ('154', 'NI', 'NIC', 'NICARAGUA', '尼加拉瓜', 'Nicaragua', '558', '505');
INSERT INTO `sms_country` VALUES ('155', 'NE', 'NER', 'NIGER', '尼日尔', 'Niger', '562', '227');
INSERT INTO `sms_country` VALUES ('156', 'NG', 'NGA', 'NIGERIA', '尼日利亚', 'Nigeria', '566', '234');
INSERT INTO `sms_country` VALUES ('157', 'NU', 'NIU', 'NIUE', '纽埃岛', 'Niue', '570', '683');
INSERT INTO `sms_country` VALUES ('158', 'NF', 'NFK', 'NORFOLK ISLAND', '诺福克岛', 'Norfolk Island', '574', '672');
INSERT INTO `sms_country` VALUES ('159', 'MP', 'MNP', 'NORTHERN MARIANA ISLANDS', '北马里亚纳群岛', 'Northern Mariana Islands', '580', '1670');
INSERT INTO `sms_country` VALUES ('160', 'NO', 'NOR', 'NORWAY', '挪威', 'Norway', '578', '47');
INSERT INTO `sms_country` VALUES ('161', 'OM', 'OMN', 'OMAN', '阿曼', 'Oman', '512', '968');
INSERT INTO `sms_country` VALUES ('162', 'PK', 'PAK', 'PAKISTAN', '巴基斯坦', 'Pakistan', '586', '92');
INSERT INTO `sms_country` VALUES ('163', 'PW', 'PLW', 'PALAU', '帕劳', 'Palau', '585', '680');
INSERT INTO `sms_country` VALUES ('164', 'PS', null, 'PALESTINIAN TERRITORY, OCCUPIED', '巴勒斯坦的领土,占领', 'Palestinian Territory, Occupied', null, '970');
INSERT INTO `sms_country` VALUES ('165', 'PA', 'PAN', 'PANAMA', '巴拿马', 'Panama', '591', '507');
INSERT INTO `sms_country` VALUES ('166', 'PG', 'PNG', 'PAPUA NEW GUINEA', '巴布新几内亚', 'Papua New Guinea', '598', '675');
INSERT INTO `sms_country` VALUES ('167', 'PY', 'PRY', 'PARAGUAY', '巴拉圭', 'Paraguay', '600', '595');
INSERT INTO `sms_country` VALUES ('168', 'PE', 'PER', 'PERU', '秘鲁', 'Peru', '604', '51');
INSERT INTO `sms_country` VALUES ('169', 'PH', 'PHL', 'PHILIPPINES', '菲律宾', 'Philippines', '608', '63');
INSERT INTO `sms_country` VALUES ('170', 'PN', 'PCN', 'PITCAIRN', '皮特克恩', 'Pitcairn', '612', '0');
INSERT INTO `sms_country` VALUES ('171', 'PL', 'POL', 'POLAND', '波兰', 'Poland', '616', '48');
INSERT INTO `sms_country` VALUES ('172', 'PT', 'PRT', 'PORTUGAL', '葡萄牙', 'Portugal', '620', '351');
INSERT INTO `sms_country` VALUES ('173', 'PR', 'PRI', 'PUERTO RICO', '波多黎各', 'Puerto Rico', '630', '1787');
INSERT INTO `sms_country` VALUES ('174', 'QA', 'QAT', 'QATAR', '卡塔尔', 'Qatar', '634', '974');
INSERT INTO `sms_country` VALUES ('175', 'RE', 'REU', 'REUNION', '团聚', 'Reunion', '638', '262');
INSERT INTO `sms_country` VALUES ('176', 'RO', 'ROM', 'ROMANIA', '罗马尼亚', 'Romania', '642', '40');
INSERT INTO `sms_country` VALUES ('177', 'RU', 'RUS', 'RUSSIAN FEDERATION', '俄罗斯联邦', 'Russian Federation', '643', '70');
INSERT INTO `sms_country` VALUES ('178', 'RW', 'RWA', 'RWANDA', '卢旺达', 'Rwanda', '646', '250');
INSERT INTO `sms_country` VALUES ('179', 'SH', 'SHN', 'SAINT HELENA', '圣赫勒拿', 'Saint Helena', '654', '290');
INSERT INTO `sms_country` VALUES ('180', 'KN', 'KNA', 'SAINT KITTS AND NEVIS', '圣基茨和尼维斯', 'Saint Kitts and Nevis', '659', '1869');
INSERT INTO `sms_country` VALUES ('181', 'LC', 'LCA', 'SAINT LUCIA', '圣卢西亚岛', 'Saint Lucia', '662', '1758');
INSERT INTO `sms_country` VALUES ('182', 'PM', 'SPM', 'SAINT PIERRE AND MIQUELON', '圣皮埃尔和MIQUELON', 'Saint Pierre and Miquelon', '666', '508');
INSERT INTO `sms_country` VALUES ('183', 'VC', 'VCT', 'SAINT VINCENT AND THE GRENADINES', '圣文森特和格林纳丁斯', 'Saint Vincent and the Grenadines', '670', '1784');
INSERT INTO `sms_country` VALUES ('184', 'WS', 'WSM', 'SAMOA', '萨摩亚', 'Samoa', '882', '684');
INSERT INTO `sms_country` VALUES ('185', 'SM', 'SMR', 'SAN MARINO', '圣马力诺', 'San Marino', '674', '378');
INSERT INTO `sms_country` VALUES ('186', 'ST', 'STP', 'SAO TOME AND PRINCIPE', '圣多美和王子', 'Sao Tome and Principe', '678', '239');
INSERT INTO `sms_country` VALUES ('187', 'SA', 'SAU', 'SAUDI ARABIA', '沙特阿拉伯', 'Saudi Arabia', '682', '966');
INSERT INTO `sms_country` VALUES ('188', 'SN', 'SEN', 'SENEGAL', '塞内加尔', 'Senegal', '686', '221');
INSERT INTO `sms_country` VALUES ('189', 'CS', null, 'SERBIA AND MONTENEGRO', '塞尔维亚和黑山', 'Serbia and Montenegro', null, '381');
INSERT INTO `sms_country` VALUES ('190', 'SC', 'SYC', 'SEYCHELLES', '塞舌尔', 'Seychelles', '690', '248');
INSERT INTO `sms_country` VALUES ('191', 'SL', 'SLE', 'SIERRA LEONE', '塞拉利昂', 'Sierra Leone', '694', '232');
INSERT INTO `sms_country` VALUES ('192', 'SG', 'SGP', 'SINGAPORE', '新加坡', 'Singapore', '702', '65');
INSERT INTO `sms_country` VALUES ('193', 'SK', 'SVK', 'SLOVAKIA', '斯洛伐克', 'Slovakia', '703', '421');
INSERT INTO `sms_country` VALUES ('194', 'SI', 'SVN', 'SLOVENIA', '斯洛文尼亚', 'Slovenia', '705', '386');
INSERT INTO `sms_country` VALUES ('195', 'SB', 'SLB', 'SOLOMON ISLANDS', '所罗门群岛', 'Solomon Islands', '90', '677');
INSERT INTO `sms_country` VALUES ('196', 'SO', 'SOM', 'SOMALIA', '索马里', 'Somalia', '706', '252');
INSERT INTO `sms_country` VALUES ('197', 'ZA', 'ZAF', 'SOUTH AFRICA', '南非', 'South Africa', '710', '27');
INSERT INTO `sms_country` VALUES ('198', 'GS', null, 'SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS', '南乔治亚岛和南桑威奇群岛', 'South Georgia and the South Sandwich Islands', null, '0');
INSERT INTO `sms_country` VALUES ('199', 'ES', 'ESP', 'SPAIN', '西班牙', 'Spain', '724', '34');
INSERT INTO `sms_country` VALUES ('200', 'LK', 'LKA', 'SRI LANKA', '斯里兰卡', 'Sri Lanka', '144', '94');
INSERT INTO `sms_country` VALUES ('201', 'SD', 'SDN', 'SUDAN', '苏丹', 'Sudan', '736', '249');
INSERT INTO `sms_country` VALUES ('202', 'SR', 'SUR', 'SURINAME', '苏里南', 'Suriname', '740', '597');
INSERT INTO `sms_country` VALUES ('203', 'SJ', 'SJM', 'SVALBARD AND JAN MAYEN', '斯瓦尔巴群岛和扬马延岛', 'Svalbard and Jan Mayen', '744', '47');
INSERT INTO `sms_country` VALUES ('204', 'SZ', 'SWZ', 'SWAZILAND', '斯威士兰', 'Swaziland', '748', '268');
INSERT INTO `sms_country` VALUES ('205', 'SE', 'SWE', 'SWEDEN', '瑞典', 'Sweden', '752', '46');
INSERT INTO `sms_country` VALUES ('206', 'CH', 'CHE', 'SWITZERLAND', '瑞士', 'Switzerland', '756', '41');
INSERT INTO `sms_country` VALUES ('207', 'SY', 'SYR', 'SYRIAN ARAB REPUBLIC', '阿拉伯叙利亚共和国', 'Syrian Arab Republic', '760', '963');
INSERT INTO `sms_country` VALUES ('208', 'TW', 'TWN', 'TAIWAN, PROVINCE OF CHINA', '台湾,中国的省份', 'Taiwan, Province of China', '158', '886');
INSERT INTO `sms_country` VALUES ('209', 'TJ', 'TJK', 'TAJIKISTAN', '塔吉克斯坦', 'Tajikistan', '762', '992');
INSERT INTO `sms_country` VALUES ('210', 'TZ', 'TZA', 'TANZANIA, UNITED REPUBLIC OF', '坦桑尼亚联合共和国', 'Tanzania, United Republic of', '834', '255');
INSERT INTO `sms_country` VALUES ('211', 'TH', 'THA', 'THAILAND', '泰国', 'Thailand', '764', '66');
INSERT INTO `sms_country` VALUES ('212', 'TL', null, 'TIMOR-LESTE', '东帝汶', 'Timor-Leste', null, '670');
INSERT INTO `sms_country` VALUES ('213', 'TG', 'TGO', 'TOGO', '多哥', 'Togo', '768', '228');
INSERT INTO `sms_country` VALUES ('214', 'TK', 'TKL', 'TOKELAU', '托克劳', 'Tokelau', '772', '690');
INSERT INTO `sms_country` VALUES ('215', 'TO', 'TON', 'TONGA', '汤加', 'Tonga', '776', '676');
INSERT INTO `sms_country` VALUES ('216', 'TT', 'TTO', 'TRINIDAD AND TOBAGO', '特立尼达和多巴哥', 'Trinidad and Tobago', '780', '1868');
INSERT INTO `sms_country` VALUES ('217', 'TN', 'TUN', 'TUNISIA', '突尼斯', 'Tunisia', '788', '216');
INSERT INTO `sms_country` VALUES ('218', 'TR', 'TUR', 'TURKEY', '土耳其', 'Turkey', '792', '90');
INSERT INTO `sms_country` VALUES ('219', 'TM', 'TKM', 'TURKMENISTAN', '土库曼斯坦', 'Turkmenistan', '795', '7370');
INSERT INTO `sms_country` VALUES ('220', 'TC', 'TCA', 'TURKS AND CAICOS ISLANDS', '特克斯和凯科斯群岛', 'Turks and Caicos Islands', '796', '1649');
INSERT INTO `sms_country` VALUES ('221', 'TV', 'TUV', 'TUVALU', '图瓦卢', 'Tuvalu', '798', '688');
INSERT INTO `sms_country` VALUES ('222', 'UG', 'UGA', 'UGANDA', '乌干达', 'Uganda', '800', '256');
INSERT INTO `sms_country` VALUES ('223', 'UA', 'UKR', 'UKRAINE', '乌克兰', 'Ukraine', '804', '380');
INSERT INTO `sms_country` VALUES ('224', 'AE', 'ARE', 'UNITED ARAB EMIRATES', '阿拉伯联合酋长国', 'United Arab Emirates', '784', '971');
INSERT INTO `sms_country` VALUES ('225', 'GB', 'GBR', 'UNITED KINGDOM', '联合王国', 'United Kingdom', '826', '44');
INSERT INTO `sms_country` VALUES ('226', 'US', 'USA', 'UNITED STATES', '美国', 'United States', '840', '1');
INSERT INTO `sms_country` VALUES ('227', 'UM', null, 'UNITED STATES MINOR OUTLYING ISLANDS', '美国小离岛', 'United States Minor Outlying Islands', null, '1');
INSERT INTO `sms_country` VALUES ('228', 'UY', 'URY', 'URUGUAY', '乌拉圭', 'Uruguay', '858', '598');
INSERT INTO `sms_country` VALUES ('229', 'UZ', 'UZB', 'UZBEKISTAN', '乌兹别克斯坦', 'Uzbekistan', '860', '998');
INSERT INTO `sms_country` VALUES ('230', 'VU', 'VUT', 'VANUATU', '瓦努阿图', 'Vanuatu', '548', '678');
INSERT INTO `sms_country` VALUES ('231', 'VE', 'VEN', 'VENEZUELA', '委内瑞拉', 'Venezuela', '862', '58');
INSERT INTO `sms_country` VALUES ('232', 'VN', 'VNM', 'VIET NAM', '越南', 'Viet Nam', '704', '84');
INSERT INTO `sms_country` VALUES ('233', 'VG', 'VGB', 'VIRGIN ISLANDS, BRITISH', '维尔京群岛,英国', 'Virgin Islands, British', '92', '1284');
INSERT INTO `sms_country` VALUES ('234', 'VI', 'VIR', 'VIRGIN ISLANDS, U.S.', '维尔京群岛,美国', 'Virgin Islands, U.s.', '850', '1340');
INSERT INTO `sms_country` VALUES ('235', 'WF', 'WLF', 'WALLIS AND FUTUNA', '瓦利斯群岛和富图纳群岛', 'Wallis and Futuna', '876', '681');
INSERT INTO `sms_country` VALUES ('236', 'EH', 'ESH', 'WESTERN SAHARA', '西撒哈拉', 'Western Sahara', '732', '212');
INSERT INTO `sms_country` VALUES ('237', 'YE', 'YEM', 'YEMEN', '也门', 'Yemen', '887', '967');
INSERT INTO `sms_country` VALUES ('238', 'ZM', 'ZMB', 'ZAMBIA', '赞比亚', 'Zambia', '894', '260');
INSERT INTO `sms_country` VALUES ('239', 'ZW', 'ZWE', 'ZIMBABWE', '津巴布韦', 'Zimbabwe', '716', '263');

二、文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
1,AF,AFG,AFGHANISTAN,阿富汗,Afghanistan,4,93
2,AL,ALB,ALBANIA,阿尔巴尼亚,Albania,8,355
3,DZ,DZA,ALGERIA,阿尔及利亚,Algeria,12,213
4,AS,ASM,AMERICAN SAMOA,美属萨摩亚,American Samoa,16,1684
5,AD,AND,ANDORRA,安道尔,Andorra,20,376
6,AO,AGO,ANGOLA,安哥拉,Angola,24,244
7,AI,AIA,ANGUILLA,安圭拉岛,Anguilla,660,1264
8,AQ,,ANTARCTICA,南极洲,Antarctica,,0
9,AG,ATG,ANTIGUA AND BARBUDA,安提瓜岛和巴布达,Antigua and Barbuda,28,1268
10,AR,ARG,ARGENTINA,阿根廷,Argentina,32,54
11,AM,ARM,ARMENIA,亚美尼亚,Armenia,51,374
12,AW,ABW,ARUBA,阿鲁巴岛,Aruba,533,297
13,AU,AUS,AUSTRALIA,澳大利亚,Australia,36,61
14,AT,AUT,AUSTRIA,奥地利,Austria,40,43
15,AZ,AZE,AZERBAIJAN,阿塞拜疆,Azerbaijan,31,994
16,BS,BHS,BAHAMAS,巴哈马群岛,Bahamas,44,1242
17,BH,BHR,BAHRAIN,巴林,Bahrain,48,973
18,BD,BGD,BANGLADESH,孟加拉国,Bangladesh,50,880
19,BB,BRB,BARBADOS,巴巴多斯,Barbados,52,1246
20,BY,BLR,BELARUS,白俄罗斯,Belarus,112,375
21,BE,BEL,BELGIUM,比利时,Belgium,56,32
22,BZ,BLZ,BELIZE,伯利兹,Belize,84,501
23,BJ,BEN,BENIN,贝宁,Benin,204,229
24,BM,BMU,BERMUDA,百慕大,Bermuda,60,1441
25,BT,BTN,BHUTAN,不丹,Bhutan,64,975
26,BO,BOL,BOLIVIA,玻利维亚,Bolivia,68,591
27,BA,BIH,BOSNIA AND HERZEGOVINA,波斯尼亚和黑塞哥维那,Bosnia and Herzegovina,70,387
28,BW,BWA,BOTSWANA,博茨瓦纳,Botswana,72,267
29,BV,,BOUVET ISLAND,布维岛,Bouvet Island,,0
30,BR,BRA,BRAZIL,巴西,Brazil,76,55
31,IO,,BRITISH INDIAN OCEAN TERRITORY,英属印度洋领地,British Indian Ocean Territory,,246
32,BN,BRN,BRUNEI DARUSSALAM,文莱达鲁萨兰国,Brunei Darussalam,96,673
33,BG,BGR,BULGARIA,保加利亚,Bulgaria,100,359
34,BF,BFA,BURKINA FASO,布吉纳法索,Burkina Faso,854,226
35,BI,BDI,BURUNDI,布隆迪,Burundi,108,257
36,KH,KHM,CAMBODIA,柬埔寨,Cambodia,116,855
37,CM,CMR,CAMEROON,喀麦隆,Cameroon,120,237
38,CA,CAN,CANADA,加拿大,Canada,124,1
39,CV,CPV,CAPE VERDE,佛得角,Cape Verde,132,238
40,KY,CYM,CAYMAN ISLANDS,开曼群岛,Cayman Islands,136,1345
41,CF,CAF,CENTRAL AFRICAN REPUBLIC,中非共和国,Central African Republic,140,236
42,TD,TCD,CHAD,乍得,Chad,148,235
43,CL,CHL,CHILE,智利,Chile,152,56
44,CN,CHN,CHINA,中国,China,156,86
45,CX,,CHRISTMAS ISLAND,圣诞岛,Christmas Island,,61
46,CC,,COCOS (KEELING) ISLANDS,COCOS(KEELING)岛,Cocos (Keeling) Islands,,672
47,CO,COL,COLOMBIA,哥伦比亚,Colombia,170,57
48,KM,COM,COMOROS,科摩罗,Comoros,174,269
49,CG,COG,CONGO,刚果,Congo,178,242
50,CD,COD,CONGO, THE DEMOCRATIC REPUBLIC OF THE,刚果民主共和国的,Congo, the Democratic Republic of the,180,242
51,CK,COK,COOK ISLANDS,库克群岛,Cook Islands,184,682
52,CR,CRI,COSTA RICA,哥斯达黎加,Costa Rica,188,506
53,CI,CIV,COTE D'IVOIRE,科特迪瓦,Cote D'Ivoire,384,225
54,HR,HRV,CROATIA,克罗地亚,Croatia,191,385
55,CU,CUB,CUBA,古巴,Cuba,192,53
56,CY,CYP,CYPRUS,塞浦路斯,Cyprus,196,357
57,CZ,CZE,CZECH REPUBLIC,捷克共和国,Czech Republic,203,420
58,DK,DNK,DENMARK,丹麦,Denmark,208,45
59,DJ,DJI,DJIBOUTI,吉布提,Djibouti,262,253
60,DM,DMA,DOMINICA,多米尼加,Dominica,212,1767
61,DO,DOM,DOMINICAN REPUBLIC,多米尼加共和国,Dominican Republic,214,1809
62,EC,ECU,ECUADOR,厄瓜多尔,Ecuador,218,593
63,EG,EGY,EGYPT,埃及,Egypt,818,20
64,SV,SLV,EL SALVADOR,萨尔瓦多,El Salvador,222,503
65,GQ,GNQ,EQUATORIAL GUINEA,赤道几内亚,Equatorial Guinea,226,240
66,ER,ERI,ERITREA,厄立特里亚,Eritrea,232,291
67,EE,EST,ESTONIA,爱沙尼亚,Estonia,233,372
68,ET,ETH,ETHIOPIA,埃塞俄比亚,Ethiopia,231,251
69,FK,FLK,FALKLAND ISLANDS (MALVINAS),福克兰群岛(马尔维纳斯),Falkland Islands (Malvinas),238,500
70,FO,FRO,FAROE ISLANDS,法罗群岛,Faroe Islands,234,298
71,FJ,FJI,FIJI,斐济,Fiji,242,679
72,FI,FIN,FINLAND,芬兰,Finland,246,358
73,FR,FRA,FRANCE,法国,France,250,33
74,GF,GUF,FRENCH GUIANA,法属圭亚那,French Guiana,254,594
75,PF,PYF,FRENCH POLYNESIA,法属波利尼西亚,French Polynesia,258,689
76,TF,,FRENCH SOUTHERN TERRITORIES,法国南部地区,French Southern Territories,,0
77,GA,GAB,GABON,加蓬,Gabon,266,241
78,GM,GMB,GAMBIA,冈比亚,Gambia,270,220
79,GE,GEO,GEORGIA,乔治亚州,Georgia,268,995
80,DE,DEU,GERMANY,德国,Germany,276,49
81,GH,GHA,GHANA,加纳,Ghana,288,233
82,GI,GIB,GIBRALTAR,直布罗陀,Gibraltar,292,350
83,GR,GRC,GREECE,希腊,Greece,300,30
84,GL,GRL,GREENLAND,格陵兰岛,Greenland,304,299
85,GD,GRD,GRENADA,格林纳达,Grenada,308,1473
86,GP,GLP,GUADELOUPE,瓜德罗普岛,Guadeloupe,312,590
87,GU,GUM,GUAM,关岛,Guam,316,1671
88,GT,GTM,GUATEMALA,危地马拉,Guatemala,320,502
89,GN,GIN,GUINEA,几内亚,Guinea,324,224
90,GW,GNB,GUINEA-BISSAU,几内亚比绍,Guinea-Bissau,624,245
91,GY,GUY,GUYANA,圭亚那,Guyana,328,592
92,HT,HTI,HAITI,海地,Haiti,332,509
93,HM,,HEARD ISLAND AND MCDONALD ISLANDS,听到岛和麦当劳的岛屿,Heard Island and Mcdonald Islands,,0
94,VA,VAT,HOLY SEE (VATICAN CITY STATE),教廷(梵蒂冈),Holy See (Vatican City State),336,39
95,HN,HND,HONDURAS,洪都拉斯,Honduras,340,504
96,HK,HKG,HONG KONG,香港,Hong Kong,344,852
97,HU,HUN,HUNGARY,匈牙利,Hungary,348,36
98,IS,ISL,ICELAND,冰岛,Iceland,352,354
99,IN,IND,INDIA,印度,India,356,91
100,ID,IDN,INDONESIA,印尼,Indonesia,360,62
101,IR,IRN,IRAN, ISLAMIC REPUBLIC OF,伊朗伊斯兰共和国,Iran, Islamic Republic of,364,98
102,IQ,IRQ,IRAQ,伊拉克,Iraq,368,964
103,IE,IRL,IRELAND,爱尔兰,Ireland,372,353
104,IL,ISR,ISRAEL,以色列,Israel,376,972
105,IT,ITA,ITALY,意大利,Italy,380,39
106,JM,JAM,JAMAICA,牙买加,Jamaica,388,1876
107,JP,JPN,JAPAN,日本,Japan,392,81
108,JO,JOR,JORDAN,约旦,Jordan,400,962
109,KZ,KAZ,KAZAKHSTAN,哈萨克斯坦,Kazakhstan,398,7
110,KE,KEN,KENYA,肯尼亚,Kenya,404,254
111,KI,KIR,KIRIBATI,基里巴斯,Kiribati,296,686
112,KP,PRK,KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF,朝鲜民主主义人民共和国,Korea, Democratic People's Republic of,408,850
113,KR,KOR,KOREA, REPUBLIC OF,朝鲜共和国,Korea, Republic of,410,82
114,KW,KWT,KUWAIT,科威特,Kuwait,414,965
115,KG,KGZ,KYRGYZSTAN,吉尔吉斯斯坦,Kyrgyzstan,417,996
116,LA,LAO,LAO PEOPLE'S DEMOCRATIC REPUBLIC,老挝人民民主共和国,Lao People's Democratic Republic,418,856
117,LV,LVA,LATVIA,拉脱维亚,Latvia,428,371
118,LB,LBN,LEBANON,黎巴嫩,Lebanon,422,961
119,LS,LSO,LESOTHO,莱索托,Lesotho,426,266
120,LR,LBR,LIBERIA,利比里亚,Liberia,430,231
121,LY,LBY,LIBYAN ARAB JAMAHIRIYA,阿拉伯利比亚民众国,Libyan Arab Jamahiriya,434,218
122,LI,LIE,LIECHTENSTEIN,列支敦斯登,Liechtenstein,438,423
123,LT,LTU,LITHUANIA,立陶宛,Lithuania,440,370
124,LU,LUX,LUXEMBOURG,卢森堡,Luxembourg,442,352
125,MO,MAC,MACAO,澳门,Macao,446,853
126,MK,MKD,MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF,前南斯拉夫马其顿共和国,Macedonia, the Former Yugoslav Republic of,807,389
127,MG,MDG,MADAGASCAR,马达加斯加,Madagascar,450,261
128,MW,MWI,MALAWI,马拉维,Malawi,454,265
129,MY,MYS,MALAYSIA,马来西亚,Malaysia,458,60
130,MV,MDV,MALDIVES,马尔代夫,Maldives,462,960
131,ML,MLI,MALI,马里,Mali,466,223
132,MT,MLT,MALTA,马耳他,Malta,470,356
133,MH,MHL,MARSHALL ISLANDS,马绍尔群岛,Marshall Islands,584,692
134,MQ,MTQ,MARTINIQUE,马提尼克岛,Martinique,474,596
135,MR,MRT,MAURITANIA,毛利塔尼亚,Mauritania,478,222
136,MU,MUS,MAURITIUS,毛里求斯,Mauritius,480,230
137,YT,,MAYOTTE,马约特岛,Mayotte,,269
138,MX,MEX,MEXICO,墨西哥,Mexico,484,52
139,FM,FSM,MICRONESIA, FEDERATED STATES OF,密克罗尼西亚联邦,Micronesia, Federated States of,583,691
140,MD,MDA,MOLDOVA, REPUBLIC OF,摩尔多瓦共和国,Moldova, Republic of,498,373
141,MC,MCO,MONACO,摩纳哥,Monaco,492,377
142,MN,MNG,MONGOLIA,蒙古,Mongolia,496,976
143,MS,MSR,MONTSERRAT,蒙特塞拉特,Montserrat,500,1664
144,MA,MAR,MOROCCO,摩洛哥,Morocco,504,212
145,MZ,MOZ,MOZAMBIQUE,MOZAMBIQUE,Mozambique,508,258
146,MM,MMR,MYANMAR,缅甸,Myanmar,104,95
147,NA,NAM,NAMIBIA,纳米比亚,Namibia,516,264
148,NR,NRU,NAURU,瑙鲁,Nauru,520,674
149,NP,NPL,NEPAL,尼泊尔,Nepal,524,977
150,NL,NLD,NETHERLANDS,荷兰,Netherlands,528,31
151,AN,ANT,NETHERLANDS ANTILLES,荷属安的列斯群岛,Netherlands Antilles,530,599
152,NC,NCL,NEW CALEDONIA,新喀里多尼亚,New Caledonia,540,687
153,NZ,NZL,NEW ZEALAND,新西兰,New Zealand,554,64
154,NI,NIC,NICARAGUA,尼加拉瓜,Nicaragua,558,505
155,NE,NER,NIGER,尼日尔,Niger,562,227
156,NG,NGA,NIGERIA,尼日利亚,Nigeria,566,234
157,NU,NIU,NIUE,纽埃岛,Niue,570,683
158,NF,NFK,NORFOLK ISLAND,诺福克岛,Norfolk Island,574,672
159,MP,MNP,NORTHERN MARIANA ISLANDS,北马里亚纳群岛,Northern Mariana Islands,580,1670
160,NO,NOR,NORWAY,挪威,Norway,578,47
161,OM,OMN,OMAN,阿曼,Oman,512,968
162,PK,PAK,PAKISTAN,巴基斯坦,Pakistan,586,92
163,PW,PLW,PALAU,帕劳,Palau,585,680
164,PS,,PALESTINIAN TERRITORY, OCCUPIED,巴勒斯坦的领土,占领,Palestinian Territory, Occupied,,970
165,PA,PAN,PANAMA,巴拿马,Panama,591,507
166,PG,PNG,PAPUA NEW GUINEA,巴布新几内亚,Papua New Guinea,598,675
167,PY,PRY,PARAGUAY,巴拉圭,Paraguay,600,595
168,PE,PER,PERU,秘鲁,Peru,604,51
169,PH,PHL,PHILIPPINES,菲律宾,Philippines,608,63
170,PN,PCN,PITCAIRN,皮特克恩,Pitcairn,612,0
171,PL,POL,POLAND,波兰,Poland,616,48
172,PT,PRT,PORTUGAL,葡萄牙,Portugal,620,351
173,PR,PRI,PUERTO RICO,波多黎各,Puerto Rico,630,1787
174,QA,QAT,QATAR,卡塔尔,Qatar,634,974
175,RE,REU,REUNION,团聚,Reunion,638,262
176,RO,ROM,ROMANIA,罗马尼亚,Romania,642,40
177,RU,RUS,RUSSIAN FEDERATION,俄罗斯联邦,Russian Federation,643,70
178,RW,RWA,RWANDA,卢旺达,Rwanda,646,250
179,SH,SHN,SAINT HELENA,圣赫勒拿,Saint Helena,654,290
180,KN,KNA,SAINT KITTS AND NEVIS,圣基茨和尼维斯,Saint Kitts and Nevis,659,1869
181,LC,LCA,SAINT LUCIA,圣卢西亚岛,Saint Lucia,662,1758
182,PM,SPM,SAINT PIERRE AND MIQUELON,圣皮埃尔和MIQUELON,Saint Pierre and Miquelon,666,508
183,VC,VCT,SAINT VINCENT AND THE GRENADINES,圣文森特和格林纳丁斯,Saint Vincent and the Grenadines,670,1784
184,WS,WSM,SAMOA,萨摩亚,Samoa,882,684
185,SM,SMR,SAN MARINO,圣马力诺,San Marino,674,378
186,ST,STP,SAO TOME AND PRINCIPE,圣多美和王子,Sao Tome and Principe,678,239
187,SA,SAU,SAUDI ARABIA,沙特阿拉伯,Saudi Arabia,682,966
188,SN,SEN,SENEGAL,塞内加尔,Senegal,686,221
189,CS,,SERBIA AND MONTENEGRO,塞尔维亚和黑山,Serbia and Montenegro,,381
190,SC,SYC,SEYCHELLES,塞舌尔,Seychelles,690,248
191,SL,SLE,SIERRA LEONE,塞拉利昂,Sierra Leone,694,232
192,SG,SGP,SINGAPORE,新加坡,Singapore,702,65
193,SK,SVK,SLOVAKIA,斯洛伐克,Slovakia,703,421
194,SI,SVN,SLOVENIA,斯洛文尼亚,Slovenia,705,386
195,SB,SLB,SOLOMON ISLANDS,所罗门群岛,Solomon Islands,90,677
196,SO,SOM,SOMALIA,索马里,Somalia,706,252
197,ZA,ZAF,SOUTH AFRICA,南非,South Africa,710,27
198,GS,,SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS,南乔治亚岛和南桑威奇群岛,South Georgia and the South Sandwich Islands,,0
199,ES,ESP,SPAIN,西班牙,Spain,724,34
200,LK,LKA,SRI LANKA,斯里兰卡,Sri Lanka,144,94
201,SD,SDN,SUDAN,苏丹,Sudan,736,249
202,SR,SUR,SURINAME,苏里南,Suriname,740,597
203,SJ,SJM,SVALBARD AND JAN MAYEN,斯瓦尔巴群岛和扬马延岛,Svalbard and Jan Mayen,744,47
204,SZ,SWZ,SWAZILAND,斯威士兰,Swaziland,748,268
205,SE,SWE,SWEDEN,瑞典,Sweden,752,46
206,CH,CHE,SWITZERLAND,瑞士,Switzerland,756,41
207,SY,SYR,SYRIAN ARAB REPUBLIC,阿拉伯叙利亚共和国,Syrian Arab Republic,760,963
208,TW,TWN,TAIWAN, PROVINCE OF CHINA,台湾,中国的省份,Taiwan, Province of China,158,886
209,TJ,TJK,TAJIKISTAN,塔吉克斯坦,Tajikistan,762,992
210,TZ,TZA,TANZANIA, UNITED REPUBLIC OF,坦桑尼亚联合共和国,Tanzania, United Republic of,834,255
211,TH,THA,THAILAND,泰国,Thailand,764,66
212,TL,,TIMOR-LESTE,东帝汶,Timor-Leste,,670
213,TG,TGO,TOGO,多哥,Togo,768,228
214,TK,TKL,TOKELAU,托克劳,Tokelau,772,690
215,TO,TON,TONGA,汤加,Tonga,776,676
216,TT,TTO,TRINIDAD AND TOBAGO,特立尼达和多巴哥,Trinidad and Tobago,780,1868
217,TN,TUN,TUNISIA,突尼斯,Tunisia,788,216
218,TR,TUR,TURKEY,土耳其,Turkey,792,90
219,TM,TKM,TURKMENISTAN,土库曼斯坦,Turkmenistan,795,7370
220,TC,TCA,TURKS AND CAICOS ISLANDS,特克斯和凯科斯群岛,Turks and Caicos Islands,796,1649
221,TV,TUV,TUVALU,图瓦卢,Tuvalu,798,688
222,UG,UGA,UGANDA,乌干达,Uganda,800,256
223,UA,UKR,UKRAINE,乌克兰,Ukraine,804,380
224,AE,ARE,UNITED ARAB EMIRATES,阿拉伯联合酋长国,United Arab Emirates,784,971
225,GB,GBR,UNITED KINGDOM,联合王国,United Kingdom,826,44
226,US,USA,UNITED STATES,美国,United States,840,1
227,UM,,UNITED STATES MINOR OUTLYING ISLANDS,美国小离岛,United States Minor Outlying Islands,,1
228,UY,URY,URUGUAY,乌拉圭,Uruguay,858,598
229,UZ,UZB,UZBEKISTAN,乌兹别克斯坦,Uzbekistan,860,998
230,VU,VUT,VANUATU,瓦努阿图,Vanuatu,548,678
231,VE,VEN,VENEZUELA,委内瑞拉,Venezuela,862,58
232,VN,VNM,VIET NAM,越南,Viet Nam,704,84
233,VG,VGB,VIRGIN ISLANDS, BRITISH,维尔京群岛,英国,Virgin Islands, British,92,1284
234,VI,VIR,VIRGIN ISLANDS, U.S.,维尔京群岛,美国,Virgin Islands, U.s.,850,1340
235,WF,WLF,WALLIS AND FUTUNA,瓦利斯群岛和富图纳群岛,Wallis and Futuna,876,681
236,EH,ESH,WESTERN SAHARA,西撒哈拉,Western Sahara,732,212
237,YE,YEM,YEMEN,也门,Yemen,887,967
238,ZM,ZMB,ZAMBIA,赞比亚,Zambia,894,260
239,ZW,ZWE,ZIMBABWE,津巴布韦,Zimbabwe,716,263

三、参考信息

  1. https://www.iban.com/currency-codes
  2. https://country-code.cl/
]]>
- - - - - 基础数据 - - - - - - - 基础数据 - - - -
- - - - - ValidSudoku - leetCode - 算法 - - /20191103-ValidSudoku%20-%20leetCode%20-%20%E7%AE%97%E6%B3%95/ - - 一、题目

Valid Sudoku链接

题目要求:
Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

  • Each row must contain the digits 1-9 without repetition.
  • Each column must contain the digits 1-9 without repetition.
  • Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition.

每一行都不能重复1-9
每一列都不能重复1-9
每个33的小格子(99分为9个3*3)不能重复

二、解题分析

  • 每一行每一列的数据,我们可以通过遍历二维数组解决;
  • 每个33的怎么解决呢?还是遍历二维数组,循环大(99)的二维数组时,符合条件再循环小(3*3)数组;
  • 怎么判断没有重复?方法很多,这里通过数组下标计数,数组初始值为0,根据下标+1,+1之后如果发现>1那么返回错误;

开始解题的时候可以一个大循环解决一个小问题,后续再把循环合并即可;
开始的时候我是写了三个双重for循环:

  1. 解决行重复判断问题;
  2. 解决列重复判断问题;
  3. 解决子矩阵重复判断问题;

当发现每一步都可行之后,尝试着合并,以减少时间复杂度;

三、代码样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static boolean isValidSudoku(char[][] board) {
// 处理 9 * 9
for (int i = 0; i < board.length; i++) {
int[] rowFlag = new int[10];
int[] columnFlag = new int[10];
for (int j = 0; j < board.length; j++) {
char row = board[i][j];
char column = board[j][i];
if (check(rowFlag, row)) {
return false;
}
if (check(columnFlag, column)) {
return false;
}
// 处理 3 * 3
if (i % 3 == 0 && j % 3 == 0) {
int[] smallFlag = new int[10];
for (int k = i; k < i + 3; k++) {
for (int l = j; l < j + 3; l++) {
char now = board[k][l];
if (check(smallFlag, now)) {
return false;
}
}
}
}
}
}
return true;
}

四、代码详情

可运行demo

github地址:ValidSudoku.java

]]>
- - - - - java - - - - - - - leetCode - - - -
- - - - - 各系统查询数据时间分布情况统计-日志处理 - - /20191025-%E5%90%84%E7%B3%BB%E7%BB%9F%E6%9F%A5%E8%AF%A2%E6%95%B0%E6%8D%AE%E6%97%B6%E9%97%B4%E5%88%86%E5%B8%83%E6%83%85%E5%86%B5%E7%BB%9F%E8%AE%A1-%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/ - - 一、说明

做这件事的目的是为了了解一条数据库记录从创建到使用的一个情况;
查询分布时间计算方式采用Top Percentile方式,就是按一定排序的数据,前面xx%的最大值是多少;
TP999 1ms 代表某接口99.9%的响应都在1ms之内;

最终的目的也就是为了知道数据多久之后可以打入冷宫,使用廉价存储;
冷热数据分级处理有利于在性能和成本上达到一定的平衡;
如把内存缓存时间设置为tp90所处的时间,那么90%的数据都能快速返回,其它少量数据回源处理;

关键字
java格式化输出
java8
stream
parallelStream
分组
排序
DoubleSummaryStatistics数据分析
TP999

二、效果

日志源数据预览

1
19-10-24.14:47:12.721 [THREAD-22000-18-T-17] INFO  FacadeImpl        - response yw:jiaoyi, orderId:123456, time:2019-10-24T14:46:44

ywcountmin(ms)max(ms)tp50(ms)tp90(ms)tp99(ms)tp999(ms)
hisen1000110000206090130
hisen-1200216000165070110

ps 输出是格式化的数据,并不是表格,可以通过:

world->粘贴输出文本->插入->表格->文本转换成表格->空格

即可完成文字到表格转换

这种过程可能比较low,但是也需要时间去处理,过程中还得配合linux命令等整合文本;

三、代码

完整:github-CallerAnalyze.java
摘要如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* @author hisenyuan
* @date 2019-10-25 23:20
*/
public class CallerAnalyze {
public static void main(String[] args) throws IOException {
String filePath = "/Users/hisenyuan/yw/yw.log";
ArrayList<CallerTimeVo> callerTimeVos = getCallerTimeVos(filePath);

System.out.println("size:" + callerTimeVos.size());
System.out.printf("%-20s%-20s%-20s%-20s%-20s%-20s%-20s%-20s", "yw", "count", "min(ms)", "max(ms)", "tp50(ms)", "tp90(ms)", "tp99(ms)", "tp999(ms)");
System.out.println();


Map<String, List<CallerTimeVo>> callerMap = callerTimeVos.parallelStream().collect(Collectors.groupingBy(CallerTimeVo::getCaller));

callerMap.entrySet()
.stream()
.sorted(Comparator.comparingInt(value -> value.getValue().size()))
.forEach(stringListEntry -> {
String caller = stringListEntry.getKey();
System.out.printf("%-20s", caller);
List<Long> sorted = stringListEntry.getValue()
.parallelStream()
.map(CallerTimeVo::getDuration)
.sorted(Long::compareTo)
.collect(Collectors.toList());
calTime(sorted);
});
}
private static ArrayList<CallerTimeVo> getCallerTimeVos(String filePath) throws IOException {
ArrayList<CallerTimeVo> callerTimeVos = Lists.newArrayList();
Files.asCharSource(new File(filePath), Charset.forName("UTF-8")).readLines(new LineProcessor<String>() {
@Override
public boolean processLine(String line) {
// 处理每一行
CallerTimeVo vo = getCreateTimeVo(line);
callerTimeVos.add(vo);
// false 会中断操作
return true;
}

@Override
public String getResult() {
return null;
}
});
return callerTimeVos;
}
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 业务安全的资源层攻防介绍 - - /20191023-%E4%B8%9A%E5%8A%A1%E5%AE%89%E5%85%A8%E7%9A%84%E8%B5%84%E6%BA%90%E5%B1%82%E6%94%BB%E9%98%B2%E4%BB%8B%E7%BB%8D/ - - 一些简单的黑产攻防介绍

  1. 修改前置摄像头,直接播放本地视频或者图片(色情诈骗、网约车司机人脸识别);
  2. 恶意注册靶机系统:黑卡猫池、群控、云控、箱控(12个主板,模拟120台手机);
  3. 模拟点击脚本在黑产中使用比例逐年增高,主要是效果好很难区分是否为真实用户;
  4. ROS软路由(可管理250个IP)、秒拨(每次拨号IP改变);
  5. 黑产变现的渠道主要有:羊毛、引流(注册账号发布信息);
  6. 很多黑产行为在单个平台来讲都是一次性活动;
  7. 改机工具:类沙箱,可以更改大量手机信息,提供快照功能,方便上下游复现现场环境,权限高,欺骗效果好;
  8. 针对改机工具一般都进行恶意环境检测,指令集中是否有hook、cpu架构、文件目录等;
  9. 防护方针:增加黑产的量化攻击成本(时间、资金);
  10. 给出模糊的错误提示,而不是准确的,否则黑产很容易试出策略;
  11. 不要在注册环节就大量拦截,要考虑用户体验,也要收集黑产行为信息,杀量化攻击;
  12. 忘记密码可能会被利用,找回密码的机制要严格审核(匹配通讯录等);
  13. 黑产资源成本高,全网来讲资源重复利用率高;
  14. IP资源、定位、手机、脚本、改机工具、云控等方便规模化,精细化运作;

从中不难看出源头是黑卡、被利用的宽带账号、也有可能是路由器、物联网设备等;
运营商加强实名认证
宽带拨号频率限制
网络设备安全加固,关闭特殊权限端口,禁用弱密码

打击网络黑产交易,防止黑产规模化,产业化趋势;

蜜罐系统收集黑产IP
提供IP代理给黑产使用了解对方信息

]]>
- - - - - biz - - - - - - - 安全 - - - -
- - - - - 如何成为专业的技术从业者 - 《程序员的职业素养》 - Bob大叔出品 - - /20191008-%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E4%B8%93%E4%B8%9A%E7%9A%84%E6%8A%80%E6%9C%AF%E4%BB%8E%E4%B8%9A%E8%80%85%20-%20%E3%80%8A%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84%E8%81%8C%E4%B8%9A%E7%B4%A0%E5%85%BB%E3%80%8B/ - - 很不错的一本书,作为程序员都值得去看一看,170+页周末一天可以看完, 还包括做笔记

医学专业已经建立起一套严密的辅导体系
软件行业建立一种包含学徒期、实习期、和长期指引的机制已是迫在眉睫

有人指导大多数人都可以快速的成长,节省很多走弯路的时间
当然,事在人为,只是说掌握了书中的那些要领,成为专业人员的几率更高,做更好的自己
如果从小学开始就一直有人引路并且自己也愿意跟着走的话,应该会很棒,现在也不晚,抓住时间就好

主要内容:
专业主义
学会说“不”,学会说“是”
编码的正确姿势
TDD
卡塔练习很重要,肌肉反应
验收测试(各方都一致同一的检验方式)
测试策略,自动化测试是趋势
时间管理,番茄工作法,注意力点数
预估的概念以及方法
压力,避免与面对
协作,学会与人交流
团队与项目,有凝聚力的团队战斗力强
软件开发如医生一样培训更佳
合适的工具事半功倍

豆瓣链接:《代码简洁之道:程序员的职业素养》

]]>
- - - - - read - - - - - - - read - - - -
- - - - - Thread Pool Monitor - 线程池监控 - - /20190905-Thread-Pool-Monitor/ - - 一、背景

业务当中多处用到线程池进行异步处理;
为了得知线程池设置是否合理,故需要增加线程池监控;
常见的实现方式:

  1. org.springframework.scheduling.concurrent.ScheduledExecutorTask
  2. org.springframework.scheduling.annotation.Scheduled

本文使用1的方式实现,主要是方便进行配置,可以托管多个任务;

二、效果预览

1
2
3
taskName:pool1-monitor. taskCount:820, completedTaskCount:820, largestPoolSize:30, poolSize:30, activeCount:0, corePoolSize:30, maximumPoolSize:50, queueSize:0
taskName:pool2-monitor. taskCount:1703, completedTaskCount:1703, largestPoolSize:30, poolSize:30, activeCount:0, corePoolSize:30, maximumPoolSize:50, queueSize:0
taskName:pool3-monitor. taskCount:820, completedTaskCount:448, largestPoolSize:30, poolSize:30, activeCount:30, corePoolSize:30, maximumPoolSize:50, queueSize:342

三、监控逻辑

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.hisen.thread.monitor.threadpool;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

/**
* @author hisenyuan
* @date 2019-09-05 19:45
*/
public class ThreadPoolMonitor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitor.class);

// xml中注入
private List<ThreadPoolTaskExecutor> pools;

@Override
public void run() {
monitorHandle(pools);
}

private void monitorHandle(List<ThreadPoolTaskExecutor> pools) {
pools.forEach(this::handleExecutor);
}

private void handleExecutor(ThreadPoolTaskExecutor p) {
ThreadPoolExecutor threadPoolExecutor = p.getThreadPoolExecutor();
// 线程池需要执行的任务数
long taskCount = threadPoolExecutor.getTaskCount();

// 线程池在运行过程中已完成的任务数
long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();

// 曾经创建过的最大线程数
long largestPoolSize = threadPoolExecutor.getLargestPoolSize();

// 线程池里的线程数量
long poolSize = threadPoolExecutor.getPoolSize();

// 线程池里活跃的线程数量
long activeCount = threadPoolExecutor.getActiveCount();

// 配置的核心线程数
int corePoolSize = threadPoolExecutor.getCorePoolSize();

// 配置的最大线程数
int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();

// 当前线程池队列的个数
int queueSize = threadPoolExecutor.getQueue().size();

// 线程池前缀名称
String threadNamePrefix = p.getThreadNamePrefix();

LOGGER.info("taskName:{}monitor. taskCount:{}, completedTaskCount:{}, largestPoolSize:{}, poolSize:{}, activeCount:{}, corePoolSize:{}, maximumPoolSize:{}, queueSize:{}"
, threadNamePrefix
, taskCount
, completedTaskCount
, largestPoolSize
, poolSize
, activeCount
, corePoolSize
, maximumPoolSize
, queueSize);
}

public List<ThreadPoolTaskExecutor> getPools() {
return pools;
}

public void setPools(List<ThreadPoolTaskExecutor> pools) {
this.pools = pools;
}
}

四、spring xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!-- ### 线程池监控 开始 ### -->
<bean id="threadPoolMonitorRunnable" class="com.hisen.thread.monitor.threadpool.ThreadPoolMonitor">
<property name="pools">
<list>
<ref bean="pool1" />
<ref bean="pool2" />
<ref bean="pool3" />
</list>
</property>
</bean>

<!-- 定时执行的线程池 -->
<bean id="threadPoolMonitorTask" class="org.springframework.scheduling.concurrent.ScheduledExecutorTask">
<property name="runnable" ref="threadPoolMonitorRunnable"/>
<!-- 延迟时间,单位ms -->
<property name="delay" value="150000"/>
<!-- 间隔时间,单位ms -->
<property name="period" value="5000"/>
</bean>

<!-- 任务调度 -->
<bean id="scheduledExecutorFactoryBean" class="org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean">
<!-- 执行任务的线程池个数 -->
<property name="poolSize" value="1" />
<!-- 任务列表 -->
<property name="scheduledExecutorTasks">
<list>
<ref bean="threadPoolMonitorTask"/>
</list>
</property>
</bean>
<!-- ### 线程池监控 结束 ### -->
]]>
- - - - - java - - - - - - - java - - - -
- - - - - 《重塑大脑,重塑人生》读后感 + 摘抄 - - /20190903-%E3%80%8A%E9%87%8D%E5%A1%91%E5%A4%A7%E8%84%91%EF%BC%8C%E9%87%8D%E5%A1%91%E4%BA%BA%E7%94%9F%E3%80%8B%E8%AF%BB%E5%90%8E%E6%84%9F%20+%20%E6%91%98%E6%8A%84/ - - 一、内容简介

这是一本人类大脑可塑性研究先驱与翘楚的故事书,正是让我们用触觉看到世界的巴赫-利塔这类先驱,使得我们正在成为来自地球的神。

二、读后感

这本书对我来讲还是很棒的,刷新了我对大脑的认知;
以前还停留在左右脑分工上,殊不知这是日本鬼子整出来的不严谨的概念;
看完书,感觉对个人技术、学习方面都有帮助,增加信心,因为之前错过了学校的大把学习时间;
运动和学习是互补的,前者产生新的神经干细胞,后者使它们的寿命延长;

这本书我是很推荐看的;
犹如最近看完的reids英文版文档;
每天看一点点,持续的刺激大脑相关区域,收获还是可以;
之前也咨询过英语比较好的同事;
反馈说目前想提高,就拿着领域内的文档硬看,坚持下来会有收获的;

每次看完书都会发个微博,避开朋友圈的尴尬,扩大散播范围,与更多的同好交流;

三、内容摘要

《重塑大脑,重塑人生》0822~0901
在持续不断噪声环境中长大的孩子都好动和吵闹,白噪声对大脑发育也有影响。

大脑重塑性和多巴胺有关,多巴胺可以使达成目标的那个行为的神经回路固话,连接得更紧。

有人说A片提供的是健康的快乐,使人从性的紧张中解放,其实A片提供的是上瘾、耐药性,他会降低快乐的感受。

所以学习没有一蹴而就之事,它是要下苦功的,我们的每一个经验都在改变大脑的连联结。

一定要指出一点:台湾地区一直受日本的影响,社会上流行着日本人说的右脑革命、右脑开发的谬论。婴儿发展初期,大脑两边很相似,只是后期专职分化了。因此绝对没有日本人七田真说的“右脑先发展到三岁才长出脑梁到左脑”

上学太早对身心情绪发展不好。

大脑每做一次不同的活动时,这些活动都改变了大脑的结构,每次练习都改变了大脑神经回路,使他更适合手边的作业,假如某些部件坏掉了,其他部件有时可以接管这项工作。(有时候头皮有反应、头胀、有点不舒服的感觉就是这个原因?)

大脑比我们能想象的还更开放。

大脑练习:找出弱点区域然后强化这个区域的功能。

一个认知能力的测试可以帮人们更加了解自己的大脑。

他们发现小猫的大脑有关键期:从3~8周,在这期间接受刺激才会正常地发展。

人的大脑也有关键期,例如语言发展有关键期,始于出生,终止于8岁到青春期之间。

当大脑在分配处理的资源时,大脑地图遵循的法则是竞争。资源不足时,大家会抢珍贵的资源,用进废退是唯一的法则。

当我们年龄越大,我们使用母语的频率就越高,母语占据我们语言地图的空间就越大,这也是因为我们的大脑有可塑性,我们学新语言才这么难。

使用双语的孩子两种语言的语音都共享一个大的语言地图。

但事实上,当我们学会一个坏习惯时,它占据了大脑地铁的空间,每次我们重复这个坏习惯,它又占据多点,让好习惯难以立足,这是为什么戒掉一个坏习惯比学它时难10倍,也是为什么同年的教育这么重要:最好一开始就教对,不要等到坏习惯已经做大有竞争优势了再去拔除它。

训练让神经元效率更高。

当动物有动机要学习时,大脑会弹性地对学习的需求作出反应。

一心多用不会使你的大脑地图产生永久改变。

多巴胺增强回馈报酬
乙酰胆碱帮助大脑加深印象,增强记忆。

都是噪声惹的祸:越靠近机场、公路的孩子智商越低。(持续的背景噪声对听觉皮质有很大的刺激)

把20~20000Hz的声音集合起来就是所谓的白噪声,昼夜播放白噪声会使人失去理智,进而发疯。

打开成年人的关键期:关键是要有激励,多巴胺、乙酰胆碱。

有些人只有在看色情影片时才会勃起,他们很少会想不举根他们爱看色情影片有关系。

恋人可以通过以下方式突破耐受性:浪漫的度个假,尝试新奇的活动,穿新的活动,想办法让对方惊喜。

我们的大脑就是演化来对新奇的东西起反应了。

当一个人成为父亲时,会分泌血管压缩素,会改变我们的大脑使你变得适合做父亲。

催产素通常是在草原田鼠交配时分泌(人性交也会),这使它们白头偕老,不会花心。

没有安全感的男人在做完爱后,会迅速离开,因为他害怕留下来,会被她影响;女人比较容易爱上与她性交的男人。

爱情的“去学习”也使我们改变了对自己形象的看法。在爱上一个很有权利欲、喜欢操控和贬低别人以抬高自我的人后,会失去所有的自我,变成自我怀疑、对自己没有信心的人。

《洛丽塔》

把猴子一只手神经切断,另外一只手绑起来,结果后面猴子用切断了神经的手进食。

“大量练习”(两周内集中训练)是为了引发启动大脑的可塑性改变,帮助大脑重新组织。

利用大脑可塑性停止忧虑、偏执想法、强迫性行为和坏习惯。

强迫症是:
你越做,就越想做;
越不做就越不会去想做。

幻肢是来自大脑的重组,脸和手的神经比较靠近。

有些女性的耳朵、锁骨和胸骨受到刺激会感到性兴奋,这三者的大脑地图都跟乳头的大脑地图紧邻;有些因阴茎癌而把阴茎切除的男性不但体验到幻阴茎,而且阴茎还会勃起。(性器官的大脑地图是在脚的旁边)

成功切除幻肢,通过镜子让患者看到幻肢,然后就会有信号传回大脑。

控制阀门理论,用电流刺激抑制痛的神经元,帮助疼痛阀门关闭。这个理论让西方科学家比较能接受针灸。

仅仅用想象和视觉错觉来重建构造大脑地图,没有打针、吃药、电流刺激,就将其痛苦、难以忍受的长期痛苦减轻或治愈了

经颅磁刺激可以无痛刺激脑神经。

实验表明,心智练习(靠想象)是用最少的实际练习来学习新肢体技术的有效方式。

用进废退的大脑需要外界刺激来维持它的地图。(心智下棋的例子最多)

专家不存储答案,但是存储重要的事实和策略,用长期记忆来解决问题是很多领域专家的共同特点。

从神经科学的观点来看,想象一个动作和执行其实没有很大差别。

一个人想象他在使用自己的肌肉可以增加肌肉的强度。

要发展一个新的回路,就必须阻挡或管制它的竞争者,即那些通常最常使用这个回路的信息。例如;想提高触觉等能力,就蒙住眼睛。

通常我们会重组我们的记忆以符合新的环境。

孩子要了解情绪,调节情绪,而产生社会化联结,必须在关键期经历几百次互动(母亲通过触觉 听觉 视觉 表现情绪)

两三岁以前靠内隐记忆,非语言的,例如骑自行车。
两三岁以后外显记忆开始发展,收集各种事实,事件,需要语言支持。

有几十个研究显示睡眠帮助我们巩固学习和记忆,而这影响大脑的可塑性改变。

婴儿时期的快速眼动睡眠是大脑可塑性发展的必要条件。

我们常常不自觉得被过去重要人际关系的魅影缠绕而影响现在的人际关系。

计算机听觉训练程序可以帮助老年人训练大脑。

不要因为小事而钻牛角尖,因为压力会产生皮质激素,而这会杀死海马回的神经细胞,海马回有助于短期记忆转为长期记忆

海马回中有神经干细胞。

在刺激丰富的环境中(球类、跑步机等各种玩具)生长的老鼠海马回容积增大15%,神经元数目增加了15%。长期丰富刺激环境对老年人的大脑神经再生有巨大影响。

为了使大脑保持最佳状态,我们必须学习新东西,而不是每天重复已经做的很熟练的事情,终身学习是很有必要的。

运动和学习是互补的,前者产生新的神经干细胞,后者使它们的寿命延长。

学一种新乐器、玩桥牌、打麻将、阅读和跳舞都可以帮助神经元的活化(需要全神贯注的活动才行)。

大脑最怕的就是人呆在相同的环境不动,这样会使大脑萎缩加速,单调不动会减少多巴胺的分泌,破坏维持大脑可塑性的注意力系统。

只有做自己想做的事情才会产生强烈的动机。

半个大脑也可以活的很好。

洗脑遵循了神经可塑性原则,可以用奖励、严厉惩罚以及大量训练的方式达到制约目的。

刚会走路的婴儿每天看电视的时间每增加一小时,他们在7岁时有注意力缺失问题的概率就会增加10%

]]>
- - - - - read - - - - - - - 读书 - - - -
- - - - - resteasy fastjson版本冲突问题 - - /20190804-resteasy%20fastjson%E7%89%88%E6%9C%AC%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/ - - 零、相关介绍

关键错误信息:
java.lang.RuntimeException: RESTeasy Provider Factory is null, do you have the ResteasyBootstrap listener configured?
java.lang.RuntimeException: Illegal to inject a message body into a singleton into public com.alibaba.fastjson.support.jaxrs.FastJsonProvider(java.lang.String)

resteasy:JBoss的一个开源项目,提供一套完整的框架帮助开发人员构建RESTful Web Service和RESTful Java应用程序。
fastjson:由阿里开发的一个性能很好的Java JSON 解析器和生成器。

引起错误的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version><!--改为:1.1.34.sec01相安无事-->
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.2.1.GA</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>2.2.1.GA</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-spring</artifactId>
<version>2.2.1.GA</version>
</dependency>

一、错误日志

本地启动Server报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2019-08-03 11:51:22,491 ERROR - [RMI TCP Connection(2)-127.0.0.1] - Context initialization failed
java.lang.RuntimeException: RESTeasy Provider Factory is null, do you have the ResteasyBootstrap listener configured?
at org.jboss.resteasy.plugins.spring.SpringContextLoaderSupport.customizeContext(SpringContextLoaderSupport.java:53)
at org.jboss.resteasy.plugins.spring.SpringContextLoader.customizeContext(SpringContextLoader.java:30)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:382)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
at org.jboss.resteasy.plugins.spring.SpringContextLoaderListener.contextInitialized(SpringContextLoaderListener.java:44)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5197)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5720)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1018)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1899)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:619)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:566)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at java.security.AccessController.doPrivileged(Native Method)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1427)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$2.run(Transport.java:202)
at sun.rmi.transport.Transport$2.run(Transport.java:199)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)

tomcat localhost long

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
八月 03, 2019 11:51:22 上午 org.apache.catalina.core.ApplicationContext log
信息: No Spring WebApplicationInitializer types detected on classpath
八月 03, 2019 11:51:22 上午 org.apache.catalina.core.StandardContext listenerStart
严重: Exception sending context initialized event to listener instance of class org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
java.lang.RuntimeException: java.lang.RuntimeException: Unable to instantiate MessageBodyReader
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:35)
at org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:211)
at org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap.contextInitialized(ResteasyBootstrap.java:28)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5197)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5720)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1018)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1899)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:619)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:566)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at java.security.AccessController.doPrivileged(Native Method)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1427)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$2.run(Transport.java:202)
at sun.rmi.transport.Transport$2.run(Transport.java:199)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.RuntimeException: Unable to instantiate MessageBodyReader
at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:760)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.registerProviders(RegisterBuiltin.java:70)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:31)
... 51 more
Caused by: java.lang.RuntimeException: Illegal to inject a message body into a singleton into public com.alibaba.fastjson.support.jaxrs.FastJsonProvider(java.lang.String)
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:209)
at org.jboss.resteasy.core.ConstructorInjectorImpl.injectableArguments(ConstructorInjectorImpl.java:63)
at org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:129)
at org.jboss.resteasy.spi.ResteasyProviderFactory.getProviderInstance(ResteasyProviderFactory.java:1038)
at org.jboss.resteasy.spi.ResteasyProviderFactory.addMessageBodyReader(ResteasyProviderFactory.java:478)
at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:756)
... 53 more

八月 03, 2019 11:51:22 上午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring root WebApplicationContext
八月 03, 2019 11:51:22 上午 org.apache.catalina.core.StandardContext listenerStart
严重: Exception sending context initialized event to listener instance of class org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
java.lang.RuntimeException: RESTeasy Provider Factory is null, do you have the ResteasyBootstrap listener configured?
at org.jboss.resteasy.plugins.spring.SpringContextLoaderSupport.customizeContext(SpringContextLoaderSupport.java:53)
at org.jboss.resteasy.plugins.spring.SpringContextLoader.customizeContext(SpringContextLoader.java:30)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:382)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
at org.jboss.resteasy.plugins.spring.SpringContextLoaderListener.contextInitialized(SpringContextLoaderListener.java:44)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5197)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5720)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1018)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1899)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:619)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:566)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at java.security.AccessController.doPrivileged(Native Method)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1427)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$2.run(Transport.java:202)
at sun.rmi.transport.Transport$2.run(Transport.java:199)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)

三、解决办法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.34.sec01</version><!--改为:1.1.34.sec01相安无事-->
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.2.1.GA</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>2.2.1.GA</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-spring</artifactId>
<version>2.2.1.GA</version>
</dependency>

四、细节说明

暂时不知道引起的原因,后期补充

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 性能指标的含义与计算方式:TP50、TP90、TP99、TP999 - - /20190801-%E6%80%A7%E8%83%BD%E6%8C%87%E6%A0%87%E7%9A%84%E5%90%AB%E4%B9%89%E4%B8%8E%E8%AE%A1%E7%AE%97%E6%96%B9%E5%BC%8F:TP50%E3%80%81TP90%E3%80%81TP99%E3%80%81TP999/ - - 零、本文背景

一个接口的关键指标应该就是响应速度,要想提高响应速度,结果在缓存中最好;
所以通过查询时间与缓存中订单的时间进行对比,得出缓存过期时间的最佳值,以平衡性能与成本;
近期在做日志分析,找到比较理想的一个缓存过期时间,使90%的查询都能被缓存覆盖;

ps:提取日志的关键是要日志规范,方便切割,才好算出调用时间(适用于暴利分析,而不是通过调用链等方式);

一、性能指标含义

常见指标:TP50、TP90、TP99、TP999
正式解释:TP=Top Percentile,Top百分数,是一个统计学里的术语,与平均数、中位数都是一类;
通俗理解:TP99 100ms,99%的查询都能在100ms内返回;

二、性能指标计算

计算方式:拿100次调用的耗时,排序,取第99个的耗时,这就是TP99的值;

具体的代码方式就是,先算出耗时,放入List,然后排序,按指定的下标取值即可;

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void calTime(List<Long> timeDurations) {
timeDurations.sort(Long::compareTo);
double tp50 = timeDurations.size() * 0.5;
double tp90 = timeDurations.size() * 0.9;
double tp99 = timeDurations.size() * 0.99;
double tp999 = timeDurations.size() * 0.999;

// 基础统计,包含max、min、count、avg信息;
DoubleSummaryStatistics doubleSummaryStatistics = timeDurations.stream().mapToDouble(Long::longValue).summaryStatistics();
System.out.println(JSON.toJSONString(doubleSummaryStatistics));

// Math.ceil() 向上取整
System.out.println("tp50:" + timeDurations.get((int) Math.ceil(tp50)));
System.out.println("tp90:" + timeDurations.get((int) Math.ceil(tp90)));
System.out.println("tp99:" + timeDurations.get((int) Math.ceil(tp99)));
System.out.println("tp999:" + timeDurations.get((int) Math.ceil(tp999)));
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Autowire、Resource的区别 | Java注解 - - /20190722-Autowire%E3%80%81Resource%E7%9A%84%E5%8C%BA%E5%88%AB%20%7C%20Java%E6%B3%A8%E8%A7%A3/ - - 零、本文背景

项目中看到有一个缓存接口存在多个实现类,
但是在代码中使用@Resource注解注入,
之前有了解过@Autowire @Resource的区别,
于是就尝试着搜索@@Resource,于是就有本文的总结了。

一、两个注解

1.1 @Autowire

1.1.1 Spring开发;
1.1.2 按照type来注入;

1.2 @Resource

1.2.1 JDK开发;
1.2.2 按照名称注入,若无,则按type来注入(未指定name的情况下);

二、后记

  1. 做事情要有计划,得主动;
  2. 需要想清楚自己想要什么样的生活,然后朝着目标奋斗;
  3. 使用现成的代码尽量搞清楚来龙去脉,可以学习知识,更能避免被坑;
  4. 每天的学习时间需要保证,坚持很重要;
]]>
- - - - - java - - - - - - - java - - - -
- - - - - 读取文件路径相关问题 - - /20190716-%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/ - - 零、本文由来

感觉从业这么久以来,读取文件路径相关问题一直是一个痛
用的很少,之前也解决过相关的问题
但是得用这种路径

1
src/test/resources/test/test.txt

上面这种路径如果在代码中的话,src/test/resources是不会存在的,会出现问题
本次学到了一个新的方法,Class.getResourceAsStream();
需要的路径在资源文件夹下面即可,填写路径:/test/test.txt
打包最终的路径会在:WEB-INF/classes/test/test.txt

ps:因maven打包配置不同,最终resources资源文件夹下的路径是不同的,注意src的坑即可

一、文件结构

1
2
3
4
5
6
7
│   └── test
│   ├── java
│   │   └── mq
│   │   └── Test.java
│   └── resources
│   ├── test
│   │   └── test.txt

二、读取代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/spring-config.xml"})
public class Test {
@Test
public void test() throws IOException {
InputStream inputStream = Test.class.getResourceAsStream("/test/test.txt");
byte[] buffer = new byte[1024];
int len;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
String res = bos.toString();
System.out.println(res);
}
}
]]>
- - - - - java - - - - - - - java - - - -
- - - - - 浅谈volatile - - /20190612-%E6%B5%85%E8%B0%88volatile/ - - 一、volatile是什么

volatile是Java提供的一种轻量级的同步机制
保证变量的修改其它线程立马可见,解决部分并发问题

二、volatile局限性

无法解决复合操作,例如 i++ i–这种操作
原因:i++操作分三步

  1. 读取i=1;
  2. i=i+1=2;
  3. i=2写入主存
    有可能在3写入之前,其它线程已经对i进行了修改,比如改为100了,结果覆盖写入了2

三、volatile原理

volatile底层依靠指令重排序来实现内存可见性的
具体的规则如下

  1. 当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序
  2. 当第一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序
  3. 当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序

四、 参考

https://www.cnblogs.com/chengxiao/p/6528109.html

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 缓存那些事儿 - 从组件到实践 - - /20190429-things-about-caching/ - - 一、为什么用缓存

1.1 空间换时间:

缓存是针对读多写少的场景典型的以空间换时间的操作
空间:内存
时间:读内存速度快(相对于读磁盘)

1.2 局部性原理:

这个世界很多事情都符合 2/8 原则
把热点数据缓存起来就大大提高系统效率

二、缓存组件介绍

2.1 Ehcache

  1. 快速,针对大型高并发系统场景,Ehcache 的多线程机制有相应的优化改善;
  2. 简单,很小的 jar 包,简单配置就可直接使用,单机场景下无需过多的其他服务依赖;
  3. 支持多种的缓存策略,灵活;
  4. 缓存数据有两级:内存和磁盘,与一般的本地内存缓存相比,有了磁盘的存储空间,将可以支持更大量的数据缓存需求;
  5. 具有缓存和缓存管理器的侦听接口,能更简单方便的进行缓存实例的监控管理;
  6. 支持多缓存管理器实例,以及一个实例的多个缓存区域;

2.2 Guava

  1. 自动将 entry 节点加载进缓存结构中;
  2. 当缓存的数据超过设置的最大值时,使用 LRU 算法移除;
  3. 具备根据 entry 节点上次被访问或者写入时间计算它的过期机制;
  4. 缓存的 key 被封装在 WeakReference 引用内;
  5. 缓存的 Value 被封装在W eakReference 或 SoftReference 引用内;
  6. 统计缓存使用过程中命中率、异常率、未命中率等统计数据;

2.3 Memcache

  1. memcache 使用预分配内存池的方式管理内存;
  2. 所有数据存储在物理内存里;
  3. 非阻塞 I/O 复用模型,纯 KV 存取操作;
  4. 多线程,效率高,会遇到锁等,上下文切换问题;
  5. 只支持简单 KV 数据类型;
  6. 数据不支持持久化;

2.4 Redis

  1. 临时申请空间,可能导致碎片;
  2. 有 VM 机制,能存储更多数据,超过内存空间后会导致 swap,降低效率;
  3. 非阻塞 I/O 复用模型,支持额外 CPU 计算:排序、聚合,会影响 I/O 性能;
  4. 单线程,无锁,无上下文切换,单实例无法利用多核性能;
  5. 支持多种数据类型:string / hash / list / set / sorted set
  6. 数据支持持久化:AOF(语句增量) / RDB(fork全量)
  7. 天然支持高可用分布式方案 sentinel + cluster (故障自动转移+集群)

三、正确使用缓存

3.1 读场景

先读缓存、再读DB
如果是并发读缓存失效,使用分布式锁只允许单次查询,其它等待,超时返回失败

3.2 写场景

cache 指的是删除『缓存』
db 指的是『更新/删除数据库』

3.2.1 先 cache 后 db

删除缓存成功,更新数据库失败,不影响数据准确性。
如果在 cache 与 db 短暂的时间内
有访问查询动作(先查缓存,后查 db 并且设置缓存)
那么还缓存中还是会存在过期的数据

3.2.2 先 db 后 cache

如果 db 成功,cache 失败,会导致数据不一致。
可以让 db cache 在一个事务,cache 失败回滚 db,保证一致性。

3.2.3 方案总结

其实本质上就是个分布式事务问题
怎么保证两个操作同时成功/失败
通过本地事务 / 补偿机制 实现会比较好

四、缓存问题集合

4.1 缓存穿透

解决方案:当查询到某数据不存在时,缓存假数据,例如:(key,key#);

4.2 缓存雪崩

缓存高可用(集群+主备)
循环一致性 Hash,节点组成一个环,如果一个节点挂了,顺着往下走;

五、参考链接

  1. 架构师之路18年精选100篇 | 缓存部分 - 58沈剑
  2. 缓存那些事 | 美团技术博客
]]>
- - - - - java - - - - - - - java - - cache - - - -
- - - - - Java GC - 理论与实践 - 附优化案例 - - /20190429-Java%20GC%20-%20%E7%90%86%E8%AE%BA%E4%B8%8E%E5%AE%9E%E8%B7%B5%20-%20%E9%99%84%E4%BC%98%E5%8C%96%E6%A1%88%E4%BE%8B/ - - 一、理论知识

常见参数:

  1. -Xms 堆初始化 例如:-Xms256m
  2. -Xmx 堆最大值 例如:-Xmx512m
  3. -Xmn 堆新生代 例如:-Xmn100m
  4. -XX:NewRatio 新生代与老年代的比例
  5. -XX:SurvivorRatio 新生代区域比例,默认8,代表Eden:From Survivor:To Survivor = 8:1:1

垃圾回收器:

  1. Serial/Serial Old 新生代/老年代,古老,单线程,暂停所有用户线程,复制算法/标记整理算法
  2. ParNew 1的多线程版本
  3. Parallel Scavenge 新生代,多线程,不需要暂停用户线程,复制算法
  4. Parallel Old 老年代,多线程,不需要暂停用户线程,标记整理算法
  5. CMS(Current Mark Sweep,详情)老年代,与ParNew配合使用,牺牲吞吐量获得最短停顿,标记整理算法
  6. G1 并行与并发收集器,可预测的停顿时间

二、实践案例

  1. Full GC 之前进行 Minor GC 避免扫描过多的对象, 配置:CMSScavengeBeforeRemark
  2. Xms和Xmx设置为相同,这样可以减少内存自动扩容和收缩带来的性能损失
  3. JVM调优是最后的稻草,进行JVM调优之前应该先优化架构和代码
  4. 调优是一个复杂的过程,根据具体的场景结合对垃圾回收器的深入理解进行调优,才可能事半功倍。

各个区大小比例建议

1
2
3
4
5
# 活跃空间大小:Full GC后堆中老年代占用空间的大小
空间 倍数
总大小 3-4倍活跃空间大小
新生代 1-1.5倍活跃空间大小
老年代 2-3倍活跃空间大小

三、参考连接

1.1 https://www.cnblogs.com/honey01/p/9475726.html
1.2 https://www.cnblogs.com/dolphin0520/p/3783345.html
2.1 https://tech.meituan.com/2017/12/29/jvm-optimize.html

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Java受检异常和非受检异常 - uncheckedException checkedException - - /20190429-java%20-%20%E5%8F%97%E6%A3%80%E5%BC%82%E5%B8%B8%E5%92%8C%E9%9D%9E%E5%8F%97%E6%A3%80%E5%BC%82%E5%B8%B8%20-%20uncheckedException%20checkedException/ - - 一、简单介绍

除了runtimeException以外的异常,都属于checkedException。

CheckedException(受检异常):编译器会检查这类异常,需要强制捕获,否则无法编译通过。
UnCheckedException(非受检异常):编译器不会检查这类异常,可以不用捕获,可以编译通过。

二、常见异常

受检:IOException、SQLException、NumberFormatException、IllegalArgumentException
非受检:OutOfMemoryError、StackOverflowError、NullPointerException、IndexOutOfBoundsException

点击查看所有jdk8 api文档
进去可以看到java.lang.RuntimeException下有很多子类异常

暂时写这些,日后再完善

]]>
- - - - - java - - - - - - - java - - 异常 - - - -
- - - - - Curator - 封装分布式锁等 | ZooKeeper目前最好用的客户端 - - /20190425-Curator%20-%20ZooKeeper%E7%9B%AE%E5%89%8D%E6%9C%80%E5%A5%BD%E7%94%A8%E7%9A%84%E5%AE%A2%E6%88%B7%E7%AB%AF/ - - 一、什么是Curator

Apache Curator is a Java/JVM client library for Apache ZooKeeper, a distributed coordination service.
It includes a highlevel API framework and utilities to make using Apache ZooKeeper much easier and more reliable.
It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL.

特别说明:Guava is to Java What Curator is to ZooKeeper

二、常见用法

后期补上自己的一些demo,其实官方文档已经介绍很全了
http://curator.apache.org/getting-started.html

三、其它说明

了解这个客户端是在《从Paxos到Zookeeper:分布式一致性原理与实战》这本书里面看到的(书单)
Curator号称是世界上最好用的zk客户端,相比zkClinet来说拥有更好的封装
让我想起Redisson和Jedis的模样

昨天一个做移动端的前同事截图问我那些代码什么意思,用的就是Curator封装的分布式锁

相比于Redis分布式存在超时问题,zookeeper分布式锁利用临时节点可以避免

目前dubbo master上使用的是Curator 4.0.1

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Guice Demo | solve NoSuchMethodError - - /20190424-Guice%20Demo%20%7C%20solve%20NoSuchMethodError/ - - 一、Guice简介

Google公司的Bob lee开发的轻量级IoC容器,其特点是:

  1. 速度快,号称是spring的100倍速度
  2. 无配置文件,实用JDK5.0的annotation描述组件依赖,简单,而且有编译器检查和重构支持
  3. 简单,代码量很少

二、简单样例

  1. 详细代码:https://github.com/hisenyuan/IDEAPractice/tree/master/src/main/java/com/hisen/jars/guice
  2. 依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.2.2</version>
    </dependency>
  3. 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class HelloApp extends BaseServer {
    @Inject
    private HelloServiceImpl hello;

    @Test
    public void testSayHello() {
    // 方式一
    Injector injector = Guice.createInjector();
    HelloService helloService = injector.getInstance(HelloService.class);
    helloService.sayHello("hisen");

    // 方式二 其实是在BaseServer中做了方式1的事情 【类似Spring的方式】
    hello.sayHello("1");
    }
    }

三、解决问题

1
java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V

github有人遇到同样的问题:https://github.com/SeleniumHQ/selenium/issues/3880

把本地的guava版本由19.0改为21.0成功解决问题

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 认识Java I/O | BIO NIO AIO | 同步与异步 | 阻塞与非阻塞 | I/O多路复用 - - /20190413-%E8%AE%A4%E8%AF%86Java%20IO%20%7C%20BIO-NIO-AIO/ - - 一、名词解释

1.1 同步与异步

同步与异步主要是针对CPU来说的

  1. 同步:CPU必须等着结果返回,这期间不能干别的;
  2. 异步:CPU不必等着结果返回,这期间可以干别的;

1.2 阻塞与非阻塞

阻塞与非阻塞主要是针对I/O来说的

  1. 阻塞:I/O线程会挂起,需等待结果;
  2. 非阻塞:I/O线程不必挂起,不等待结果;

1.3 伪异步I/O

本质上还是同步阻塞I/O
不过是在服务器把socket链接封装成Task提交给线程池处理
因为有队列,所以可以突破C:S=1:1的比例

1.4 I/O多路复用

通过把多个I/O的阻塞复用到一个阻塞上,从而使得系统在单线程情况下可以处理多个客户端的请求。
类似于linux的epoll、select

1.5 多路复用器

Selector,核心是通过Selector来轮询注册在其上的Channel
当发现有Channel就绪就返回Channel的选择键集合,进行I/O操作;

二、不同I/O模型对比表格

对比项同步阻塞I/O(BIO)伪异步IO非阻塞I/O(NIO)异步I/O(AIO)
客户端个数:I/O线程数1:1M:N(M>=N)M:1M:O
I/O类型(是否同步)同步同步同步(I/O多路复用)异步
I/O类型(是否阻塞)阻塞阻塞阻塞非阻塞
API使用难度简单简单非常复杂复杂
调试难度简单简单复杂复杂
可靠性非常差
吞吐量

三、参考

  1. 《Netty权威指南 2th》
]]>
- - - - - java - - - - - - - java - - - -
- - - - - 聊聊MySQL的隔离级别 | MySQL隔离级别原理 - - /20190413-%E8%81%8A%E8%81%8AMySQL%E7%9A%84%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB/ - - 这是之前的一个帖子:oracle - mysql - 数据库事务隔离级别介绍
写的不全面, 按现在的理解,重新写一个;

一、名词解释

脏读:在一个查询事务过程中,读到了其它事务没有提交的数据;
不可重复读:一个事务查询过程中,多次查询得到了不一致的结果,原因是:有别的更新事务提交了;
幻读:一个事务查询过程中,多次查询得到了不一致的结果,原因是:有别的删除事务/插入事务提交了;

二、数据库隔离级别

name名称脏读不可重复读幻读加锁读
Read uncommitted读未提交YesYesYesNo
Read committed读已提交NoYesYesNo
Repeatable read可重复读NoNoYesNo
Serializable序列化NoNoNoYes

默认的隔离级别为:RR,原因:5.1之后版本,如果Binlogog开启语句级别,必须为RR,RC可能会导致Binlog数据错误(详情);

三、控制方式

读未提交:每次都是读数据最新的版本(包括事务未提交的数据);
读已提交:MVCC控制;
可重复读:MVCC控制;
序列化:在读取的每一行上加锁,只能按顺序进行读写;

四、InnoDB MVCC(多版本并发控制)原理

InnoDB引擎下的MVCC,是通过在每一行上增加两个隐藏列实现的;
每发生一个新的事务,系统版本都会递增;
当UPDATE数据的时候,都是先copy出来一行操作,不影响原表数据,提交之后才影响;

一个保存创建时间(非时间值,而是系统版本号);
一个保存删除时间(非时间值,而是系统版本号);

4.1 SELECT

当前查询事务系统ID:5
结果条件:

  1. CREATE VERSION <= 5(RR级别为 <= 5,RC级别应该是直接取最大值);
  2. DELETE VERSION == null or DELETE VERSION > 5 (RR级别应该只有为空,RC级别可能有 > 5的情况);

1可以保证读取到的行,在事务开始之前就已经存在,要么是事务自身插入或者修改的;
2可以保证读取到的行,在事务开始之前未被删除;

4.2 INSERT

为新插入的每一行保存当前系统版本号作为CREATE VERSION;

4.3 DELETE

为删除的每一行保存当前系统版本号作为DELETE VERSION;

4.4 UPDATE

插入一行新的记录,保存当前系统版本号作为CREATE VERSION;
同事保存当前系统版本号到原来的行上作为DELETE VERSION;

五、参考:

  1. 《高性能MySQL》
  2. MySQL的四种事务隔离级别(ps:此处有测试截图,比较直观)
]]>
- - - - - sql - - - - - - - mysql - - - -
- - - - - Linux 日志分析出现次数前10的数据 | awk sort uniq - - /20190410-Linux-log-analyze-top-10/ - - 一、命令组合
1
2
3
4
$ more sort.log | awk '{print $1}' | sort | uniq -c | sort -k1nr | head -3
4 5
3 1
2 2

二、命令详解

more:一次读取少量的数据,避免一次性载入大文件,比cat好些
awk ‘{print $1}’:以awk默认的分隔符,并且打印第一列
sort:把上一步的结果按ASCII排序
uniq -c:如果重复那么计数,uniq命令可以组合很多其它参数
sort -k1nr:根据第一列的数据进行排序,n是按数值大小排序,r是倒序排序
head -3:显示前面三行数据

三、原始数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat sort.log
1
2
3
4
5
6
1
1
2
3
4
5
5
5
6
]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Unix文件超过大小限制移动到某目录 - - /20190408-Unix%E6%96%87%E4%BB%B6%E8%B6%85%E8%BF%87%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6%E7%A7%BB%E5%8A%A8%E5%88%B0%E6%9F%90%E7%9B%AE%E5%BD%95/ - - 某个文件夹下面有n个文件,需要移动大于10M的文件到/tmp/目录下

实现命令

1
find . -type f -size +100M -exec mv {} /tmp/ \;

find 查找
. 当前目录
-type 文件类型:f 文件,d 目录
-size 文件大小:+100M +:大于 -:小于 空:等于
-exec 管道命令,将前面的查询结果传递给后面的命令
{} 指前面传递过来的的查询结果
\; 结束管道命令

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Java对象的一生 | 从new到被回收 - - /20190408-Java%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%B8%80%E7%94%9F/ - - 一、出生过程

这里讲述的是第一次出生的过程,即之前class没有被加载。

1.1 类初始化

1.1.1 加载

1.1.1.1 通过类的全限定名获取定义此类的二进制字节流(可以从zip包、网络、运行时动态生成);
1.1.1.2 将这个字节流所代表的静态存储结构转化为方法去运行时数据结构;
1.1.1.3 在内存(方法区)中生成一个代表这个类的java.lang.Class对象,作为方法区这个类各种数据访问的入口;

1.1.2 验证

1.1.2.1 文件格式验证

1.1.2.1.1 魔数是否以0xCAFEEBABE开头(咖啡宝贝)
1.1.2.1.2 主、次版本号是否在当前虚拟机处理的范围之内(不同的jdk版本编译出来的版本不一致,可向前兼容)
1.1.2.1.3 常量池是否有不被支持的类型(检查常量tag标志)
1.1.2.1.4 指向常量的各种索引值是否指向不存在或者不符合类型的常量
1.1.2.1.5 CONSTANT_Utf8_info型的常量中是否有不符合UTF-8编码的数据
1.1.2.1.6 Class文件中各个部分以及文件本身是否有被删除或者附加的其它信息

这些操作是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身安全

1.1.2.2 元数据验证

1.1.2.2.1 是否有父类(Object除外)
1.1.2.2.2 是否继续了不允许继承的类(被final修饰的)
1.1.2.2.3 如果当前不是抽象类,是否实现了其父类或接口中要求实现的所有方法
1.1.2.2.4 类中的字段、方法是否与父类产生矛盾(如覆盖父类final字段、不合法的重载)
这些操作是对字节码进行语义分析,确保符合Java语言规范要求

1.1.2.3 字节码验证

1.1.2.3.1 确保操作数栈的数据类型与指令代码序列能完美配合(反例:操作数栈为int,使用的时候按long加载)
1.1.2.3.2 确保跳转指令不会跳转到方法体以外的字节码指令上
1.1.2.3.3 确保方法体重点类型转换是有效的
这些操作主要是通过数据流和控制流分析,确定程序的语义是合法的、符合逻辑的。
JDK1.6之后有一个优化,利用StackMapTable来验证是否合法

1.1.2.4 符号引用验证

1.1.2.4.1 符号引用中通过字符串描述的全限定名是否能找到对应的类
1.1.2.4.2 符号引用中的类、字段、方法是否可以被当前类访问
在将符号引用转化为直接引用的时候触发符号引用验证

1.1.3 准备

1.1.3.1 仅为类变量分配内存,并且赋值为初始值,例如int为0(被static修饰的)
1.1.3.2 特殊情况,被final修饰的static变量会直接赋值为代码给定的值
准备阶段是正式为类变量分配内存并设置类变量初始值,这些变量所使用的内存都将在方法区中进行分配。

1.1.4 解析

虚拟机将常量池中的符号引用替换为直接引用
符号引用:一组用来描述所引用目标的符号,所引用的目标不一定在内存中
直接引用:直接指向目标的指针、相对偏移量、能间接定位到句柄,直接引用的目标必须在内存中存在

1.1.5 初始化

<client>()方法:由编译器自动收集类中所有变量的赋值动作和静态语句块(static{}块)中的语句合并产生
按顺序收集。父类的此方法先执行,虚拟机会保证线程安全
真正开始执行类中定义的Java代码,

1.2 类实例化(New)

1.2.1 分配内存:在类初始化完成之后,能知道所需要的内存大小

指针碰撞:内存规整的,中间指针划分已用、空闲,那么分配就是指针向空闲一方移动对象大小相等的距离
空闲列表:内存不规整,用列表维护一个可用内存地址,从列表中找出一个合适大小的空间分配给实例

1.2.2 初始化为0值

1.2.3 设置对象头信息

1.2.4 执行<init>()方法,按照程序员的意思给对象赋值

<init>()方法:实例构造器

类的初始化是指类加载过程中的初始化阶段对类变量按照程序猿的意图进行赋值的过程;
类的实例化是指在类完全加载到内存中后创建对象的过程。

二、生平事迹

听候线程的指令,执行相关方法

三、驾鹤西去

可达性分析算法:从GC Root节点开始向下搜索,搜索所走过的引用链为引用链,当没有任何引用链时说明对象不可达
经历过两次不可达标记才会被标记为可回收
堆内存回收主要发生在堆上的新生代,为Minor GC,使用的是复制算法
新生代分为:Eden、to Survivor、from Survivor,后两者为Survivor,三者的比例为8:1:1

在Minor GC之前,to Survivor为空,对象存在:Eden、from Survivor
在Minor GC执行
Eden存活着的对象拷贝到to Survivor,同一时候对象年龄+1

from Survivor区中的幸存对象会考虑对象年龄
假设年龄没达到阈值,对象依旧拷贝到to survivor中
假设对象达到阈值那么将被移到老年代

复制阶段完毕后,Eden和from幸存区中仅仅保存死对象,能够视为清空
假设在复制过程中to幸存区被填满了,剩余的对象将被放到老年代

在Minor GC之后
from survivor和to survivor会调换一下名字,下次Minor GC时,to survivor变为from Survivor

四、参考连接

  1. 《深入理解Java虚拟机》
  2. 一个Java Class自述短暂的一生
  3. 一切皆对象:论对象的一生一世
  4. Java对象的生命周期与垃圾回收以及四种引用
  5. Java new一个对象的过程中发生了什么

目前写的不是太完善,有机会写的透彻明白一些。

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 数组中只有一个数出现奇数次 | 利用异或最高效 - - /20190404-%E6%95%B0%E7%BB%84%E4%B8%AD%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E6%95%B0%E5%87%BA%E7%8E%B0%E5%A5%87%E6%95%B0%E6%AC%A1%20%7C%20%E5%88%A9%E7%94%A8%E5%BC%82%E6%88%96%E6%9C%80%E9%AB%98%E6%95%88/ - - 一、背景

题意:一个数组有奇数个元素,其中只有一个元素出现一次,其它都出现了2次

方案A:利用hashMap存储出现的次数,然后遍历,复杂度O(3/2N)=O(N);
方案B:利用bitmap,根据数组元素的大小确定位置,做取反(0->1->0);
方案C:利用异或的特性快速找出,最简单,高效;

二、异或

a^a=0
0^a=a
异或支持:交换率、结合率
因此:
a^b^c^b^c=a^(b^b)^(c^c)=a

三、代码

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
int[] mayDup = {1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1};
int res = mayDup[0];
for (int i = 1; i < mayDup.length; i++) {
System.out.print("res^mayDup[i]: " + res + "^" + mayDup[i]);
res ^= mayDup[i];
System.out.println("=" + res);
}
System.out.println(res);
}

四、后记

这只是一个场景的妙用,实际当中很难遇到这种情况。
所以不管什么时候还是得多去刷题,没事刷几道,熟然生巧。

]]>
- - - - - java - - - - - - - java - - 技巧 - - - -
- - - - - Agent CGlib VS JDK | 动态代理比较 - - /20190330-Agent%20CGlib%20VS%20JDK%20%7C%20%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E6%AF%94%E8%BE%83/ - - 一、代理简介

1.1 代理是什么

举个例子:快到吃饭的点了,你有两种选择:1)自己做,2)叫外卖
1.1.1 自己做你嫌麻烦,那就叫外卖,只管收外卖其它不关注(解耦);
1.1.2 自己做的不好吃,那厨师上门,厨师给你增强菜的味道(增强);

官方定义:对其他对象提供一种代理以控制对这个对象的访问。

1.2 使用场景是什么

1.2.1 设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。

1.2.2 我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。

1.2.3 Spring的AOP机制就是采用动态代理的机制来实现切面编程。

二、CGlib VS JDK 两种方式实现代理

2.1 差异

2.1.1 JDK:只能代理接口
2.1.2 CGlib:直接代理类

2.2 简单demo

完整代码:https://github.com/hisenyuan/IDEAPractice/tree/master/src/main/java/com/hisen/jdk/agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public static void main(String[] args) {
System.out.println("===CGlibAgentTest===");
cGlibAgentTest();

System.out.println("\n===JDKDynamicAgentTest===");
jdkDynamicAgentTest();
}

private static void cGlibAgentTest() {
CGlibAgent cGlibAgent = new CGlibAgent();
Apple apple = (Apple) cGlibAgent.getInstance(Apple.class);
apple.show();

System.out.println();

Orange orange = (Orange) cGlibAgent.getInstance(Orange.class);
orange.show();
}

private static void jdkDynamicAgentTest() {
// must be return interface
Fruit apple = (Fruit) DynamicAgent.agent(Fruit.class, new Apple());
apple.show();

System.out.println();

Fruit orange = (Fruit) DynamicAgent.agent(Fruit.class, new Orange());
orange.show();
}
//===CGlibAgentTest===
//-> before invoking
//Apple show method is invoked
//-> after invoking
//
//-> before invoking
// Orange show method is invoked
//-> after invoking
//
//===JDKDynamicAgentTest===
//-> before invoking
// Apple show method is invoked
//-> after invoking
//
//-> before invoking
// Orange show method is invoked
//-> after invoking

三、参考连接

http://www.cnblogs.com/puyangsky/p/6218925.html
https://blog.csdn.net/u011784767/article/details/78281384

]]>
- - - - - java - - - - - - - java - - jdk - - - -
- - - - - 计算长度最大的子串长度(按空格分割) - 多种方式 - - /20190326-%E8%AE%A1%E7%AE%97%E9%95%BF%E5%BA%A6%E6%9C%80%E5%A4%A7%E7%9A%84%E5%AD%90%E4%B8%B2%E9%95%BF%E5%BA%A6(%E6%8C%89%E7%A9%BA%E6%A0%BC%E5%88%86%E5%89%B2)/ - - 一、背景

今天中午一个朋友问我一个问题没来得及回,现在折腾一下;
问题:有一个包含以N个空格分割的字符串,求最大子串的长度,要求时间空间复杂度最优;

二、方案:

  1. 朋友给人方案:直接split分割遍历数组,找出最大值;
  2. 面试官的方案:使用StringTokenizer,找出最大值;
  3. 我想到的方案:indexOf计算index差值,找出最大值;

三、结果:

封装的代码比较复杂,目前来看不在么好分析时间复杂度和空间复杂度;
于是就直观得对比了一下时间;

1
2
3
4
//        output
// use(nano): 15486049, by 方案3
// use(nano): 66060102, by 方案2
// use(nano): 80844275, by 方案1

有时间再深入了解下StringTokenizer的源码

四、代码对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
String oriStr = "aa aa aaaa aaa a a a aa aa a";
int times = 100000;
// 自定义实现
final long start1 = System.nanoTime();
for (int i = 0; i < times; i++) {
String splitStr = " ";
findMaxLength(oriStr, splitStr);
}
final long end1 = System.nanoTime();

// 利用 StringTokenizer
final long start2 = System.nanoTime();
for (int i = 0; i < times; i++) {
findMaxLength4StringTokenizer(oriStr);
}
final long end2 = System.nanoTime();

// 利用 Split
final long start3 = System.nanoTime();
for (int i = 0; i < times; i++) {
String splitStr = " ";
findMaxLength4Split(oriStr, splitStr);
}
final long end3 = System.nanoTime();

System.out.println("use(nano): " + (end1 - start1) + ", by findMaxLength");
System.out.println("use(nano): " + (end2 - start2) + ", by findMaxLength4StringTokenizer");
System.out.println("use(nano): " + (end3 - start3) + ", by findMaxLength4Split");
// output
// use(nano): 15486049, by findMaxLength
// use(nano): 66060102, by findMaxLength4StringTokenizer
// use(nano): 80844275, by findMaxLength4Split

五、完整代码

github也有:https://github.com/hisenyuan/IDEAPractice/blob/master/src/main/java/com/hisen/interview/TestStringTokenizer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
* @Author hisenyuan
* @Description 计算长度最大的子串长度(按空格分割), 自定义实现比较优
* @Date 2019/3/26 20:53
*/
public class TestStringTokenizer {
public static void main(String[] args) {
String oriStr = "aa aa aaaa aaa a a a aa aa a";
int times = 100000;
// 自定义实现
final long start1 = System.nanoTime();
for (int i = 0; i < times; i++) {
String splitStr = " ";
findMaxLength(oriStr, splitStr);
}
final long end1 = System.nanoTime();

// 利用 StringTokenizer
final long start2 = System.nanoTime();
for (int i = 0; i < times; i++) {
findMaxLength4StringTokenizer(oriStr);
}
final long end2 = System.nanoTime();

// 利用 Split
final long start3 = System.nanoTime();
for (int i = 0; i < times; i++) {
String splitStr = " ";
findMaxLength4Split(oriStr, splitStr);
}
final long end3 = System.nanoTime();

System.out.println("use(nano): " + (end1 - start1) + ", by findMaxLength");
System.out.println("use(nano): " + (end2 - start2) + ", by findMaxLength4StringTokenizer");
System.out.println("use(nano): " + (end3 - start3) + ", by findMaxLength4Split");
// output
// use(nano): 15486049, by findMaxLength
// use(nano): 66060102, by findMaxLength4StringTokenizer
// use(nano): 80844275, by findMaxLength4Split
}

private static void findMaxLength4StringTokenizer(String oriStr) {
int maxLength = 0;
final StringTokenizer stringTokenizer = new StringTokenizer(oriStr);
while (stringTokenizer.hasMoreElements()) {
final int length = stringTokenizer.nextToken().length();
maxLength = length > maxLength ? length : maxLength;
}
// System.out.println("max: " + maxLength);
}

private static void findMaxLength(String oriStr, String splitStr) {
int index = 0;
final int maxIndex = oriStr.lastIndexOf(splitStr);
int maxLength = oriStr.length() - maxIndex;
while (index < maxIndex) {
final int currentIndex = oriStr.indexOf(splitStr, index);
int currLength = currentIndex - index;
maxLength = currLength > maxLength ? currLength : maxLength;
index = currentIndex + splitStr.length();
}
// System.out.println("max: " + maxLength);
}

private static void findMaxLength4Split(String oriStr, String splitStr) {
int maxLength = 0;
final String[] strings = oriStr.split(splitStr);
for (String str : strings) {
final int length = str.length();
maxLength = length > maxLength ? length : maxLength;
}
// System.out.println("max: " + maxLength);
}
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Dubbo官方中文文档|用户文档|开发者指南|源码导读|运维管理 - - /20190325-Dubbo%E5%AE%98%E6%96%B9%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3%7C%E7%94%A8%E6%88%B7%E6%96%87%E6%A1%A3%7C%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97%7C%E6%BA%90%E7%A0%81%E5%AF%BC%E8%AF%BB%7C%E8%BF%90%E7%BB%B4%E7%AE%A1%E7%90%86/ - - 一、为什么要学Dubbo

近几年一直在使用dubbo进行支付系统的开发;
作为国内比较受欢迎的一个SOA框架,Dubbo使用简单设计优雅;
里面用到的思想和技术,基本上涵盖大部分互联网公司用到的技术;
记得直属领导说过一句话,看完Dubbo他觉得设计者简直就是天才;
而且看完dubbo的源码,对他的影响很大,处处模仿dubbo的思想;

二、文档地址

中文文档:用户文档、开发者指南、源码导读、运维管理
http://dubbo.apache.org/zh-cn/docs/user/quick-start.html
用户手册(比较完整):
https://dubbo.gitbooks.io/dubbo-user-book/demos/routing-rule.html
官方博客:
http://dubbo.apache.org/zh-cn/blog/index.html

三、Dubbo创始人博客

最后更新于2014年
https://javatar.iteye.com/

]]>
- - - - - java - - - - - - - java - - 源码 - - Dubbo - - - -
- - - - - WeightAlgorithm - 权重算法 - Random、HashCode对比 - - /20190322-WeightAlgorithm%20-%20%E6%9D%83%E9%87%8D%E7%AE%97%E6%B3%95%20-%20Random%E3%80%81HashCode%E5%AF%B9%E6%AF%94/ - - 一、权重算法

权重算法一般在路由里面用的比较多,分布式环境下对等的服务有多个,加权随机选出一个服务来调用;

可能还有其他方面的用途,下面的代码简单的实现了这个权重,本质上就用到了数组,随机下标;

二、代码概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
String[] weight = {"A", "A", "A", "A", "A", "B", "B", "B", "C", "C"};
final int times = 500000;
final long hashStart = System.currentTimeMillis();
List<String> hashRes = getList4Hash(weight, times);
printRes("Hash", hashStart, hashRes);

final long randomStart = System.currentTimeMillis();
List<String> randomRes = getList4Random(weight, times);
printRes("Random", randomStart, randomRes);
// Hash use millis: 931
// A:49.92%
// B:29.99%
// C:20.09%
// Random use millis: 50
// A:50.08%
// B:29.93%
// C:19.99%
}

三、完整代码

github:show all code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.hisen.algorithms;

import com.google.common.collect.Lists;

import java.text.NumberFormat;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @Author hisenyuan
* @Description $end$
* @Date 2019/3/21 21:04
*/
public class WeightAlgorithm {
public static void main(String[] args) {
String[] weight = {"A", "A", "A", "A", "A", "B", "B", "B", "C", "C"};
final int times = 500000;
final long hashStart = System.currentTimeMillis();
List<String> hashRes = getList4Hash(weight, times);
printRes("Hash", hashStart, hashRes);

final long randomStart = System.currentTimeMillis();
List<String> randomRes = getList4Random(weight, times);
printRes("Random", randomStart, randomRes);
// Hash use millis: 931
// A:49.92%
// B:29.99%
// C:20.09%
// Random use millis: 50
// A:50.08%
// B:29.93%
// C:19.99%
}

private static void printRes(String method, long start, List<String> resList) {
final Map<String, Long> collect = resList.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
final double a = collect.get("A");
final double b = collect.get("B");
final double c = collect.get("C");
final double sum = a + b + c;

NumberFormat nt = NumberFormat.getPercentInstance();
nt.setMinimumFractionDigits(2);

System.out.println(method + "\t use millis: " + (System.currentTimeMillis() - start));
System.out.println("A" + ":" + nt.format(a / sum));
System.out.println("B" + ":" + nt.format(b / sum));
System.out.println("C" + ":" + nt.format(c / sum));
}

private static List<String> getList4Hash(String[] weight, int times) {
List<String> result = Lists.newArrayList();
for (int i = 0; i < times; i++) {
final String a = UUID.randomUUID().toString() + System.currentTimeMillis();
final int hash = a.hashCode();
final int index = hash > 0 ? hash : -hash;
final String res = weight[index % weight.length];
result.add(res);
}
return result;
}

private static List<String> getList4Random(String[] weight, int times) {
List<String> result = Lists.newArrayList();
Random random = new Random();
for (int i = 0; i < times; i++) {
final int index = random.nextInt(weight.length);
final String res = weight[index];
result.add(res);
}
return result;
}
}
]]>
- - - - - java - - - - - - - java - - 算法 - - - -
- - - - - git版本差异报告 - git difftool分支对比 - gitLab compare - - /20190309-git%E7%89%88%E6%9C%AC%E5%B7%AE%E5%BC%82%E6%8A%A5%E5%91%8A%20-%20git%20difftool%E5%88%86%E6%94%AF%E5%AF%B9%E6%AF%94%20-%20gitLab%20compare/ - - 一、初衷

有时候上线会出现合错代码,比如功能,或者maven的pom文件;

人都不是十全十美的人,只要是人就会犯错
关键是要想办法去避免,只有工具才不犯错

所以尽量想办法利用工具来防止我们犯错,git来说有很多办法;
简单的命令,或者gitlab的compare报告;
每次上线之前,开发自己看一遍当前版本与线上版本的区别,确认每一个点都是正确的改动;

二、gitlab compare功能(推荐)

2.1 入口:工程首页 -> Repository -> Compare
2.2 选择:上一个版本,当前版本,点击Compare
2.2 结果:一次能看到两个版本的所有commit、改动点

三、git命令

1
2
3
4
5
# 显示版本之间改动的文件名
git difftool master 1.2.4 --stat

# 在命令行挨个显示版本某个文件具体差异
git difftool master 1.2.4
]]>
- - - - - java - - - - - - - java - - gitLab - - git - - - -
- - - - - dubbo transport Data length too large-14277263-max payload-8388608 channel-NettyChannel - - /20190307-dubbo%20transport%20Data%20length%20too%20large-14277263-max%20payload-8388608%20channel-NettyChannel/ - - 一、报错信息
1
com.alibaba.dubbo.remoting.transport.ExceedPayloadLimitException: Data length too large: 14277263, max payload: 8388608, channel: NettyChannel [channel=[id: 0x12a13c8f, /172.0.0.1:49402 => /172.0.0.2:23888]]

二、报错原因

dubbo默认使用Netty传输协议
并且默认的大小限制为:默认为8M,即8388608

三、解决办法

  1. 修改接口
    出现这种情况是因为一个接口查某个表的所有数据(几万条)
    一般这种接口肯定是需要分页的

  2. 更改配置信息
    在dubbo.properties 中增加如下

    1
    dubbo.protocol.dubbo.payload=11557050
]]>
- - - - - java - - - - - - - java - - netty - - dubbo - - - -
- - - - - java.lang.NoClassDefFoundError错误解决 - - /20190223-java.lang.NoClassDefFoundError%E9%94%99%E8%AF%AF%E8%A7%A3%E5%86%B3/ - - maven项目昨天遇到的一个错误:java.lang.NoClassDefFoundError

理论原因:JVM在编译的时候能找到调用方法或静态变量所在的类,但在运行的时候找不到此类而引发的错误。

真实原因:(仅针对当前问题)
项目A依赖公共服务C-1.0.1版本
项目B依赖项目A,B中dependencyManagement强制指定C-1.0.0

此时编译可以通过,运行时如果有用的C的新版本功能就会会出现上述问题

解决办法:对项目B中C的版本升级,使用1.0.1

扩展阅读:
https://blog.csdn.net/qq_27576335/article/details/77102385
https://blog.csdn.net/jamesjxin/article/details/46606307

]]>
- - - - - java - - - - - - - java - - - -
- - - - - MySQL统计成功率等 - 利用case when - - /20190218-MySQL%E7%BB%9F%E8%AE%A1%E6%88%90%E5%8A%9F%E7%8E%87%E7%AD%89%20-%20%E5%88%A9%E7%94%A8case%20when/ - - CONCAT:连接字符串,CONCAT(55.00,’%’)->55.00%
truncate:处理小数点位数,truncate(10.1111,2)->10.11

统计sql

1
2
3
4
5
6
7
8
9
10
11
select res.*, CONCAT(truncate((res.succ / res.`all`) * 100, 2), '%') as 'success rate'
from (select tpc.USER_NO,
tpc.TYPE,
count(*) as 'all',
sum(case when tpc.STATUS = 0 then 1 else 0 end) as 'succ',
sum(case when tpc.STATUS = 1 then 1 else 0 end) as 'fail',
sum(case when tpc.STATUS != 1 && tpc.STATUS != 0 then 1 else 0 end) as 'other'
from t_hisen tpc
where tpc.DATE between '20190216' and '20190217'
group by tpc.USER_NO, tpc.TYPE
order by tpc.USER_NO) res

统计结果

USER_NOTYPEallsuccfailothersuccess rate
4561010010.00%
1231029230679.31%
]]>
- - - - - sql - - - - - - - mysql - - sql - - - -
- - - - - MySQL - Data truncation:Incorrect datetime value - - /20190201-MySQL%20-%20Data%20truncation%20Incorrect%20datetime%20value/ - - 一、问题
1
com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect datetime value: '2039-01-07 12:58:20.625' for column

二、原因

由于程序没有控制好,计算下一次更新时间失误,造成数值过大。

1
2
3
update table_a
SET UPDATE_TIME = '2039-01-07 12:58:20.625'
WHERE ID = 6241

以下是MySQL官方的说法,就是时间超过了范围。

1
2
The TIMESTAMP data type is used for values that contain both date and time parts.
TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC.

]]>
- - - - - sql - - - - - - - mysql - - sql - - - -
- - - - - mac docker compose timezone 问题解决 - - /20181221-mac%20docker%20compose%20timezone%20%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ - - 一、背景介绍

想用docker搭建一个lnmp环境

使用这个脚本:

1
https://github.com/buxiaomo/docker-compose/tree/master/lnmp

执行命令之后报错

1
2
3
4
5
6
7
8
9
10
11
12
13
docker-compose -f lnmp.yml up -d
WARNING: Some services (mysql, nginx, php, redis) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
WARNING: Some services (mysql, nginx, php) use the 'configs' key, which will be ignored. Compose does not support 'configs' configuration - use `docker stack deploy` to deploy to a swarm.
lnmp_mysql_1 is up-to-date
Starting lnmp_redis_1 ...
lnmp_nginx_1 is up-to-date
Starting lnmp_redis_1 ... error

ERROR: for lnmp_redis_1 Cannot start service redis: b'Mounts denied: \r\nThe path /usr/share/zoneinfo/Asia/Shanghai\r\nis not shared from OS X and is not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\nSee https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.\r\n.'

memory: 100M
ERROR: for redis Cannot start service redis: b'Mounts denied: \r\nThe path /usr/share/zoneinfo/Asia/Shanghai\r\nis not shared from OS X and is not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\nSee https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.\r\n.'
ERROR: Encountered errors while bringing up the project.

二、报错原因

  1. mac机器的时间路径与linux不一样
  2. lnmp.yml redis用到了这个时间

三、解决办法

  1. 查看当前机器时间文件真实位置(/etc/localtime 这个路径是不让共享给docker的)

    1
    2
    ls -la /etc/localtime
    lrwxr-xr-x 1 root wheel 39 10 19 11:13 /etc/localtime -> /var/db/timezone/zoneinfo/Asia/Shanghai
  2. 设置位置 :Docker -> Preferences… -> File Sharing

  3. 新增共享目录:/var/db/timezone
  4. apply & restart
  5. 修改lnmp.yml配置文件 全局替换:/usr/share/zoneinfo/Asia/Shanghai 为:/var/db/timezone/zoneinfo/Asia/Shanghai

四、成功信息

1
2
3
4
5
6
7
8
docker-compose -f lnmp.yml up -d
WARNING: Some services (mysql, nginx, php, redis) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
WARNING: Some services (mysql, nginx, php) use the 'configs' key, which will be ignored. Compose does not support 'configs' configuration - use `docker stack deploy` to deploy to a swarm.
Removing lnmp_redis_1
Starting lnmp_nginx_1 ... done
Starting lnmp_mysql_1 ... done
Recreating f5b9db566588_lnmp_redis_1 ... done
Starting lnmp_php_1 ... done
]]>
- - - - - docker - - - - - - - docker - - - -
- - - - - 定时任务的一点思考 - Java - - /20181027-%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83%20-%20Java/ - - 一、背景交代
  1. 部署方式:多点
  2. 业务概要:处理一批数据,可以失败重试

二、实现方式

  1. 主表入一条控制数据,两个控制字段:next,process(0:空闲,1:处理中),子表具体处理业务
  2. 定时任务只扫描主表,通过其他业务字段,加上主表两个控制字段(now>netx,process=0),捞出需要处理的数据
  3. 当开始处理数据时候,先更新process=1,如果更新成功,说明拿到锁(假装是一个分布式锁,因为两台机器)
  4. 如果拿到3的锁,则开始处理子表的业务数据,一次查100条(limit 0,100 desc update_time),直到子表全部处理完成
  5. 如果遇到错误,把错误数据的id存起来,更新子表重试次数,和更新时间,如果达到最大失败次数,更改状态(让4查不出这条数据)
  6. 如果遇到当前处理数据的id存在List中,那么说明当前所有的数据都处理过一次了,退出(如果不判断,失败后会无限循环,直到全部成功)

三、伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void process(MainTable mainTable) {

// 1. 锁控制表
boolean lock = service.lock(mainTable);
LOGGER.info("lockFlag:,", lock, ",mainTableId:", mainTable.getId());
if (!lockFlag) {
return;
}
List<Long> repeatIds = new ArrayList<>();
// 2. 根据主表id,下次扫描时间,每次查询100条
List<SubTable> subTables;
do {
//扫描子表中的数据
subTables = service.querySub(mainTable.getId(), 100);
for (SubTable sub : subTables) {
if (repeatIds.contains(batch.getId())) {
subTables = null;
break;
}
// 处理业务
handle(mainTable, subTables, repeatIds);
}
} while (!CollectionUtils.isEmpty(subTables));

// 5. 检查是否全部终态
checkHandleDone(mainTable);
// 7. 释放锁
boolean unLock = service.unLock(mainTable);
LOGGER.info("lockFlag:,", unLock, ",mainTableId:", mainTable.getId());
}
]]>
- - - - - java - - - - - - - java - - 定时任务 - - - -
- - - - - go程序设计语言练习 - 同统计重复 - gop1.io/ch1/dup2/dup2.go - - /20181027-go%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E8%AF%AD%E8%A8%80%E7%BB%83%E4%B9%A0%20-%20%E5%90%8C%E7%BB%9F%E8%AE%A1%E9%87%8D%E5%A4%8D/ - - 一直在写java,看语法逻辑什么的没有问题

但是刚刚看书上写出来的这个程序,居然不知道怎么在goland里面运行…

于是曲线救国,了解到打印输入的参数。

这断代码就是为了找出输入数据中的重复行

  1. 直接启动,不带参数,启动之后输入参数

    1
    2
    3
    4
    5
    参数0: /private/var/folders/lk/p8gbq87n6rvfndk7wlk23y8r0000gn/T/___go_build_dup2_go
    1 # 一行输入一个
    1 # 一行输入一个
    ^D #这是command + D
    21 # 输出的结果:个数 输入值
  2. 在命令行启动,带

    1
    2
    3
    4
    5
    6
    $ go run dup2.go 'hisen.txt'
    参数0: /var/folders/lk/p8gbq87n6rvfndk7wlk23y8r0000gn/T/go-build525680776/b001/exe/dup2
    参数1: hisen.txt #代码同级目录的文件名
    2 hisen
    2 hisenyuan
    2 123

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main

import (
"bufio"
"fmt"
"os"
"strconv"
)

/**
本程序只能在终端运行:go run dup2.go 'hisen.txt'
hisen.txt 为本文件同级目录的文件
*/
func main() {
// 遍历输出的参数
for idx, args := range os.Args {
fmt.Println("参数"+strconv.Itoa(idx)+":", args)
}
// map[string,int]
counts := make(map[string]int)
// 拿到文件名,从第二个参数开始
files := os.Args[1:]
if len(files) == 0 {
countLines(os.Stdin, counts)
} else {
// 遍历文件
for _, arg := range files {
// 读取文件内容
f, err := os.Open(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
continue
}
countLines(f, counts)
f.Close()
}
}

// 遍历map
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
}

// 把数据装入map
func countLines(file *os.File, counts map[string]int) {
// 从文件中加载数据
input := bufio.NewScanner(file)
// 遍历文件内容
for input.Scan() {
counts[input.Text()]++
}
}

]]>
- - - - - go - - - - - - - go - - - -
- - - - - MySQL批量删除重复数据,保留id最小的一条 - - /20181016-MySQL%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E6%95%B0%E6%8D%AE,%E4%BF%9D%E7%95%99id%E6%9C%80%E5%B0%8F%E7%9A%84%E4%B8%80%E6%9D%A1/ - - stu数据如下:

idname
1hisen
2hisen
3hisen
4hisenyuan
5hisenyuan

删除重复的name,保留id最小的。

思路就是先找出重复数据,然后再找出需要保留的数据(重复中id最小的)

然后删除id不在需要保留的id中的所有数据

1
2
3
4
delete
from stu
where id not in (select t.minId
from (select min(id) minId from stu group by name having count(name) > 1) t)
]]>
- - - - - sql - - - - - - - mysql批量删除重复数据 - - - -
- - - - - Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder - - /20181015-Could%20not%20initialize%20class%20io.jsonwebtoken.impl.DefaultJwtBuilder/ - - 使用jsonwebtoken出现如下错误

1
2
3
Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder
java.lang.NoClassDefFoundError: Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder
at io.jsonwebtoken.Jwts.builder(Jwts.java:116) ~[jjwt-0.7.0.jar:0.7.0]

原因,因为jackson-databindb版本冲突,直接去掉了依赖

jwt必须依赖Jackson所以报错了

出错时候的配置

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
<exclusions>
<exclusion>
<artifactId>jackson-databind</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
</exclusions>
</dependency>

解决办法

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>

]]>
- - - - - java - - - - - - - jsonwebtoken - - - -
- - - - - 《Go语言程序设计》 - ch1/lissajous - GIF动画 - - /20180908-%E3%80%8AGo%E8%AF%AD%E8%A8%80%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B%20-%20ch1:lissajous%20-%20GIF%E5%8A%A8%E7%94%BB/ - - 一、程序说明
  1. 可以以web的方式查看,也可以生成一个图片
  2. 直接go run输出的是一丢乱码(图片)
  3. 需要go build 然后运行程序(具体看代码里面注释)

具体的内容可以看下面的程序代码(虽然是抄书)

二、程序代码

2.1 基本程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package main

import (
"image"
"image/color"
"image/gif"
"io"
"log"
"math"
"math/rand"
"net/http"
"os"
"time"
)

var palette = []color.Color{color.White, color.Black}

const (
whiteIndex = 0 // 画板中的第一种颜色
blackIndex = 1 // 画板中的下一种颜色
)

func main() {
rand.Seed(time.Now().UTC().UnixNano())
if len(os.Args) > 1 && os.Args[1] == "web" {
handler := func(w http.ResponseWriter, r *http.Request) {
lissajous(w)
}
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
return
}
lissajous(os.Stdout)
}
func lissajous(out io.Writer) {
const (
cycles = 5 // 完整的x振荡器变化的个数
res = 0.001 // 角度分辨率
size = 100 // 图像画布包含
nframes = 64 // 动画中的帧数
delay = 8 // 以10ms为单位的帧间延迟
)

freq := rand.Float64() * 3.0 // y振荡器的相对频率
anim := gif.GIF{LoopCount: nframes}
phase := 0.0 // phase difference
for i := 1; i < nframes; i++ {
rect := image.Rect(0, 0, 2*size+1, 2*size+1)
img := image.NewPaletted(rect, palette)
for t := 0.0; t < cycles*2*math.Pi; t += res {
x := math.Sin(t)
y := math.Sin(t*freq + phase)
img.SetColorIndex(size+int(x*size+0.5),size+int(y*size+0.5),blackIndex)
}
phase +=0.1
anim.Delay = append(anim.Delay,delay)
anim.Image = append(anim.Image,img)
}
gif.EncodeAll(out,&anim) // 注意:忽略编码错误(直接运行输出的是一堆乱码)
// go build lissajous.go
// ./lissajous >out.gif # 生成一个图片
// ./lissajous web # 开启一个web服务,可以浏览器访问 localhost:8000
}

2.2 改进型程序,也是课后练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import (
"image"
"image/color"
"image/gif"
"io"
"log"
"math"
"math/rand"
"net/http"
"os"
"time"
)
// 颜色代码数组
var palette = []color.Color{color.Black, color.RGBA{199, 237, 204, 0xff}, color.RGBA{102, 53, 204, 0xff}}


func main() {
rand.Seed(time.Now().UTC().UnixNano())
// 判断传入参数 如果有web,就启动一个web服务器
if len(os.Args) > 1 && os.Args[1] == "web" {
handler := func(w http.ResponseWriter, r *http.Request) {
lissajous(w)
}
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
return
}
lissajous(os.Stdout)
}
func lissajous(out io.Writer) {
const (
cycles = 5 // 完整的x振荡器变化的个数
res = 0.001 // 角度分辨率
size = 100 // 图像画布包含
nframes = 64 // 动画中的帧数
delay = 8 // 以10ms为单位的帧间延迟
)

freq := rand.Float64() * 3.0 // y振荡器的相对频率
anim := gif.GIF{LoopCount: nframes}
phase := 0.0 // phase difference
for index,i :=0, 1; i < nframes; i++ {
rect := image.Rect(0, 0, 2*size+1, 2*size+1)
img := image.NewPaletted(rect, palette)
for t := 0.0; t < cycles*2*math.Pi; t += res {
x := math.Sin(t)
y := math.Sin(t*freq + phase)
img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), uint8(index))
}
// 控制变色频率,生成颜色数组下标
index = i % 3
phase += 0.1
anim.Delay = append(anim.Delay, delay)
anim.Image = append(anim.Image, img)
}
gif.EncodeAll(out, &anim) // 注意:忽略编码错误(直接运行输出的是一堆乱码)
// go build lissajous.go
// ./lissajous >out.gif # 生成一个图片
// ./lissajous web # 开启一个web服务,可以浏览器访问 localhost:8000
}
]]>
- - - - - go - - - - - - - go - - - -
- - - - - 《穿布鞋的马云》 - 部分文字摘录 - - /20180908-%E3%80%8A%E7%A9%BF%E5%B8%83%E9%9E%8B%E7%9A%84%E9%A9%AC%E4%BA%91%E3%80%8B%20-%20%E9%83%A8%E5%88%86%E6%96%87%E5%AD%97%E6%91%98%E5%BD%95/ - - 《穿布鞋的马云》 2018.10.01 ~ 2018.10.02

国庆假期没有什么安排,看了几部电影之后感觉蛮愧疚,又浪费了大把的时间

于是乎在书架上找了本决定看起来压力不那么大的书来看

这本书感觉整体上写的一般,可能定位就是通俗易懂吧

但是对于了解一些细节,还是很有帮助

由于目前待在创业公司,看到很多文字的时候还是很有感触的

如果你想成功,积极乐观地看待任何问题

以下为摘抄:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
马云在校教书,兼职创业:海博翻译社

92派:1992年后创业的企业家

想清楚干什么,然后就要清楚该干什么;知道该干什么之后,要明白自己不该干什么
——>能干多久?想干多久?这件事情该干多久就多久。

记住自己第一天的梦想,至关重要。

海博翻译社的网页是第一个外国人能在互联网上搜到的第一个中国的网页。

1995年是全世界互联网商用的第一年。

只有启蒙才会有更大的市场。

资本永远不能控制一家公司,资本只能为创业服务,而不能控制公司。

一个创业公司的创始人能够取得权威媒体的认可,也是一件惊人的事情。

创业者最重要的是创造条件,如果等到时机成熟的话,一定轮不到我们。

有了理想之后,我觉得,最重要的是给自己一个承诺,承诺自己要把这个事情做出来。

如果你不行动,不给自己的梦想一个实践的机会,你永远没有机会。

创立阿里巴巴之前,三家公司马云得到的创业智慧:
第一,选对创业项目很重要
第二,即便是几个人的小公司,管理制度的建设也很重要
第三,资本控制了你的公司时,你是没有希望的。
第四,创业团队没有相同的理念和共同的目标会导致分裂。
第五,对国有股东有了近距离的认识。

1998年,阿里巴巴做bbs,长城是最早的bbs,xxx到此一游。

阿里巴巴,在美国看到的名字,是“open sesame”芝麻开门的意思。

合作都是团队做出来的,如果别人把你当英雄,你千万不要把自己当英雄,如果自己把自己当英雄必然要走下坡路。

有几种人是难以在团队中培养的,一种是懂资本的人才,其他是财务、法律人才。因为这样的人才不仅要懂专业,而且需要经验。一般的公司多半是在上市之前引进这样的人才。

在我擅长的世界里,我非常自信,非常自如。我没有想过要大包大揽,我知道自己的角色是什么。

对于公众舆论而言,公司只能用一个声音说话,太多声音,只会让这个公司的形象变的模糊。

世界上最不缺的就是钱,缺的是能用资本创造价值的企业家和企业家精神。

1999.10高盛投资阿里巴巴使得后者可以对媒体讲一个好的故事:全世界最好的投资银行看好阿里巴巴的长期发展。它让外界对这家初创公司刮目相看。

永远不要让资本说话,让资本赚钱。

1999.10,拿到高盛500万(40%)投资的马云,随后孙正义想4000万占有49%的股份,后来3000万美元得到30%的股份,再后来,马云马云觉得钱太多反悔,最后孙正义投资2000万获得20%股份。

在所有的创业公司发展中,扩张规模是最容易犯错误的时候,而这时犯的错还都是不小的错误。

人多财务成本高,这并不是主要的,重要的是那些闲人会让全心全意投入加班加点的人感到不平衡,久而久之,公司的文化风气就不行了。

2009.10 西湖论剑,马云请金庸拉拢当时互联网行业的佼佼者参与。前来的有:金庸,新浪王志东,搜狐张朝阳,网易丁磊,my8848王俊涛,加拿大驻华大使,英国驻沪总理事及50多家国际跨国公司在华代表

在创业初期要寻找这些梦之队:没有成功,渴望成功,平凡,团结,有共同理想的人。

2003年每天收入100万元,2004年每天盈利100万元,2005年每天纳税100万元。

拥有好的业绩和产品,同事技巧性地为自己选定一个对标物,这会让你的公司和产品赢得大量关注。

“裸奔”和淘宝的倒立与武侠一样,都是极易被识别的与众不同的公司文化符号,也极易产生传播效应。

好的商人不在于他的梦想多么伟大,但是他的梦想必须是独特的,任何一个成功的企业家,从第一天起都有一个独特的梦想。

2005.8.11雅虎中国并入阿里巴巴。资产技术+10亿美元,获得阿里40%股份。

一个公司如果并购另外一个公司的话,文化融合是一件很难的事情。

聪明是智慧者的天敌,傻瓜用嘴说话,聪明的人用脑子说话,智慧的人用心讲话。所以永远要记住,不要把自己当成最聪明的人,最聪明的人相信总有别人比自己更聪明。

作为一个领导人,应该控制自己的情绪,很多时候发脾气是无能的表现,合理的情绪控制对于团队的和谐,稳定军心有很大作用。

改变别人,先改变自己;要完善世界,先完善自己;要帮助好别人,先帮助自己;如果你不能帮助好自己,那一切都是瞎扯。

人与人之间最本质的区别不是技能、专业、能力、情商,而是人生的使命感和价值观,而企业同理。

先付出,再得到。

没有使命感,人生会找不到奋斗的意义;
没价值诶观,奋斗的方式会扭曲。

80后,90后,我觉得是我的产品,我们没有理由、权利和责任区批判我们的产品,我们唯一有的权利和责任是完善我们的产品。

真正的幸福感是你知道自己在做什么,知道别人在做什么,你会逐渐从痛苦中找到那些快乐。

如果你想成功,积极乐观地看待任何问题

]]>
- - - - - read - - - - - - - 穿布鞋的马云 - - - -
- - - - - mac安装Go环境 & IDE(GoLand) & 基本运行方法(标准输入) - - /20180908-mac%E5%AE%89%E8%A3%85Go%E7%8E%AF%E5%A2%83%20&%20IDE(GoLand)%20&%20%E5%9F%BA%E6%9C%AC%E8%BF%90%E8%A1%8C%E6%96%B9%E6%B3%95/ - - 一、安装GO

1.1 使用Homebrew安装go环境(如果很慢,可以换个源)

1
brew install go

1.2 查看安装信息

1
go env

主要关注如下输出

1
GOROOT="/usr/local/Cellar/go/1.10.3/libexec" # 安装目录

1.3 配置环境变量

1
vi ~/.bash_profile # 没有的话会新建一个文件

输入如下内容,第一行是安装的目录,第三行是工作目录(可以改成自己喜欢的路径)

1
2
3
4
5
GOROOT=/usr/local/Cellar/go/1.10.3/libexec
export GOROOT
export GOPATH=/Users/hisenyuan/golang
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN:$GOROOT/bin

1.4 让配置文件生效并且查看环境变量

1
2
source ~/.bash_profile
go env # 这时你会发现环境变量已经有改变

二、安装GoLand

我是习惯了用jetbrains的idea

发现它家也有go语言的IDE GoLand

于是就去官网下载,安装,找个注册码,修改一下host防止注册码失效

这里就不再累赘了

三、运行需要输入的程序

买了一本《Go语言程序设计》

有些程序需要从标准输入获取信息,然后进行处理,例如dup2

运行之后不知道该肿么办,百度一番之后发现了方法

命令行:Ctl + D

GoLand:command + D

贴一段程序和运行的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import (
"bufio"
"fmt"
"os"
)

// dup2 打印输入中多次出现的文本和行数
// 它从stdin或指定的文件到文件列表读取

func main() {
counts := make(map[string]int)
files := os.Args[1:]
if len(files) == 0 {
countLines(os.Stdin, counts)
} else {
for _, arg := range files {
f, err := os.Open(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
continue
}
countLines(f, counts)
f.Close()
}
}
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
}
func countLines(f *os.File, counts map[string]int) {
input := bufio.NewScanner(f)
for input.Scan(){
counts[input.Text()]++
}
}
// 执行的过程中,得按command d
//123
//123
//123
//111
//222
//222
//112
//111^D
//3123
//2222

]]>
- - - - - go - - - - - - - go - - - -
- - - - - 线上事故记录 - 死锁 - - /20180809-%E7%BA%BF%E4%B8%8A%E4%BA%8B%E6%95%85%E8%AE%B0%E5%BD%95%20-%20%E6%AD%BB%E9%94%81/ - - 一、背景描述

在支付业务当中,每一笔交易都得进行记账。

两种情况:

  1. 第一步先冻结,交易成功,解冻并扣款(A账户->B账户->C账户);
  2. 第一步先冻结,交易失败,解冻并归还(A账户->B账户->A账户);

上面的两种情况各自都是在一个事物当中。

二、问题描述

在同一个商户进行并发操作的时候,交易有成功有失败;

  1. 成功的时候钱是:A账户->B账户;
  2. 失败的时候钱是:B账户->A账户;

因为在各自的事物当中更新两条记录的信息,并且使用了for update(innodb引擎)
ß
在某一瞬间:成功的先锁A账户,失败的先锁了B记录

接下来就两个事物各自持有对方想要的资源,并且不释放已经占有的资源,就造成了死锁

三、解决方法

在程序里面,更新两个账户的钱的时候,始终先更新ID更小的那条记录,那样不管多少个事务同时进来

都会按照固定的顺序去持有资源,比如先A再B,这样就不会出现各自持有对方想要的资源

1
2
3
ID ACCOUNT
10 A
11 B

每个事务都是先锁定A再锁定B,拿不到锁就一直等待

]]>
- - - - - java - - - - -
- - - - - WebSocket - Java & html & JavaScript - 单发 & 群发 - - /20180728-WebSocket%20-%20Java%20&%20html%20&%20JavaScript%20-%20%E5%8D%95%E5%8F%91%20&%20%E7%BE%A4%E5%8F%91/ - - 一、背景说明

最近在做app后台相关接口

自建通知中心目前不能很好的支持给APP推送消息

长连接可以保持推送速度,目前app中内嵌了H5,所以考虑使用websocket

之前没有接触过websocket,百度了一堆之后,页面上可以正常使用

但是没有发现可用使用Java后台进行消息的发送,于是乎就琢磨了一上午,解决了这个问题

现在把这个小工程分享给大家,少走点弯路==

ps:很多不能在后台发送消息,是因为缺少java的客户端

二、准备工作

建立一个maven web 工程

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>com.neovisionaries</groupId>
<artifactId>nv-websocket-client</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>

三、主要代码

websocket服务端主逻辑

为了实现简单的非群发操作,在连接websocket的时候,加上了一些get参数

例如:ws://localhost:8080/websocket?sendTo=hisen&method=methodSingle&user=hisenyuan

然后在后端判断,根据参数做出不同的动作

demo完整工程:https://github.com/hisenyuan/IDEAPractice/tree/master/websocket-demo

配置完Tomcat,即可使用,在java后台运行测试类(com.hisen.ws.client.ClientApp4Java)可发送消息到页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.hisen.ws.server;

import com.hisen.ws.util.Constants;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/websocket")
public class WebSocketServer {
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;

// 实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
// 当前用户
private String user;

/**
* 客户端可以是web页面,也可以是Java后台
* <p>
* 通过连接或者message可以控制发送给谁
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
// 获取url传过来的参数
Map<String, List<String>> parameterMap = session.getRequestParameterMap();
// 发送方式
String method = null;
// 发送给哪些人
List<String> receivers = new ArrayList<>();
// 发送者
String sernder = null;
if (parameterMap.containsKey(Constants.METHOD)) {
method = parameterMap.get(Constants.METHOD).get(0);
}
if (parameterMap.containsKey(Constants.SEND_TO)) {
receivers = parameterMap.get(Constants.SEND_TO);
}
if (parameterMap.containsKey(Constants.USER)) {
sernder = parameterMap.get(Constants.USER).get(0);
}

System.out.println("sender:" + sernder + ",receivers:" + receivers.toString() + ",method:" + method);
if (method == null || method.equals(Constants.METHOD_ALL)) {
//发送所有
send2All(message);
} else {
//单发
send2Users(receivers, message);
}

}

/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
this.user = session.getRequestParameterMap().get(Constants.USER).get(0);
// 放入map
webSocketMap.put(user, this);
//在线数加1
addOnlineCount();
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount() + ",session:" + session.getId() + ",user:" + this.user);
}

/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
// 移除
webSocketMap.remove(this.user);
//在线数减1
subOnlineCount();
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount() + ",user:" + this.user);
}


private void send2Users(List<String> receivers, String message) {
receivers.forEach(e -> {
System.out.println("single receiver:" + e);
Optional.ofNullable(webSocketMap.get(e))
.filter(webSocketServer -> webSocketServer.session.isOpen())
.ifPresent(webSocketServer -> sendOnce(message, e, webSocketServer));
});
}

private void send2All(String message) {
webSocketMap.forEach((key, value) -> {
sendOnce(message, key, value);
});
}

private void sendOnce(String message, String e, WebSocketServer webSocketServer) {
try {
webSocketServer.sendMessage(message);
} catch (IOException exp) {
System.out.println("发送出错,receiver:" + e);
}
}

/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误,user:" + this.user);
error.printStackTrace();
}

/**
* 自定义的方法
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}

public static synchronized int getOnlineCount() {
return onlineCount;
}

public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}

public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
]]>
- - - - - java - - - - -
- - - - - FindLucklyGirl - 找出值班的幸运儿 - - /20180720-FindLucklyGirl%20-%20%E6%89%BE%E5%87%BA%E5%80%BC%E7%8F%AD%E7%9A%84%E5%B9%B8%E8%BF%90%E5%84%BF/ - - 一、背景简介

今天头给我们开会,说到团队对外沟通的问题。

谈到对外需要积极给人解决问题,而不是各种推脱,即使自己不知道,也可以给个眼神找到对的人。

继而谈到需要安排人轮流负责跟外部接洽

由于这个活呢,大伙儿认为不是什么好差事,那就抓阄决定吧

于是乎就感觉可以写一个简单的排班系统小bug,不过我这里只是提供一个简单的思路

二、程序代码

主要的逻辑在这,当然并没有考虑数据持久化的问题

性能等其他的问题,纯粹是一个思路,用hashCode取模主要是打的比较散,很均匀

加上日期什么的,就一个排班表出来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
*
* @param person 目前所有的人
* @param lucklys 这一轮已经值班了的人
*/

private static void findLucklyOneByHashCode(List<String> person, ArrayList<String> lucklys) {
List<String> result = new ArrayList<>(person);
result.removeAll(lucklys);
if (result.size() == 0) {
lucklys.clear();
result = new ArrayList<>(person);
}
String code = System.nanoTime() + UUID.randomUUID().toString();
int index = Math.abs(code.hashCode()) % result.size();
String lucklyOne = result.get(index);
System.out.println("lucklyOne:" + lucklyOne);
lucklys.add(lucklyOne);
}
/**
* hashCode随机取出来的数据
* lucklyOne:G
* lucklyOne:B
* lucklyOne:C
* lucklyOne:D
* lucklyOne:H
* lucklyOne:F
* lucklyOne:E
* lucklyOne:A
*/

]]>
- - - - - java - - - - - - - java - - - -
- - - - - iTerm2自动连接远程服务器 - - /20180720-iTerm2%E8%87%AA%E5%8A%A8%E8%BF%9E%E6%8E%A5%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8/ - - mac自带的终端总感觉不大好用,于是乎就被安利了iTerm2

然后就搜了下自动连接远程服务,于是就发现了一个不错的脚步

整个设置过程还是比较顺畅,

操作步骤:

  1. 新建一个sh文件

    1
    vi auto_ssh.sh
  2. 输入如下内容,[lindex $argv 0] 这个为第一个参数的占位符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #!/usr/bin/expect

    set timeout 30
    spawn ssh -p[lindex $argv 0] [lindex $argv 1]@[lindex $argv 2]
    expect {
    "(yes/no)?"
    {send "yes\n";exp_continue}
    "password:"
    {send "[lindex $argv 3]\n"}
    }
    interact
  3. 复制脚本到bin下并且赋予执行权限

    1
    2
    3
    sudo cp auto_ssh.sh /usr/local/bin/
    cd /usr/local/bin/
    sudo chmod +x auto_ssh.sh
  4. 设置
    在iTerm2中Command+o呼出profile

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 界面右下角,点击:
    edit profiles
    # 界面左下角,点击:
    +
    # 界面上方,点击:
    General
    # 界面靠上,写这个登录的名字:
    name
    # 界面中间,点击选择:
    Login shell
    # 找到这个提示符:Send text at start,输入如下字符
    auto_ssh.sh 8022 root 10.10.20.20 hisenyuan
    # 脚本名称 端口 用户名 ip地址 密码
    1. 运行
      设置完了之后,关闭窗口,重新command+o呼出配置文件,双击刚刚配置的即可自动登录
]]>
- - - - - linux - - - - -
- - - - - java多姿势读写文件 - - /20180718-java%E5%A4%9A%E5%A7%BF%E5%8A%BF%E8%AF%BB%E5%86%99%E6%96%87%E4%BB%B6/ - - 重温经典,特意看了下为什么读文件非得不让等于 -1

fileChannelIn.read(byteBuffer) != -1

EOF = End Of File,其默认值就是-1

这是一个约定好的结束符,不是认为改变的

代码

建议使用大文件进行测试,看效果比较明显

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@Test
public void testCopy() {
String oldFileName = "/Users/hisenyuan/hisen/blog/source/_posts/Test-Java-Code.md";
String newFileName = "/Users/hisenyuan/hisen/test/Test-Java-Code.md";
nioCopy(oldFileName, newFileName);
ioCopy(oldFileName, newFileName.replace(".md", ".md.bak"));
ioCopyByLine(oldFileName,newFileName.replace(".md",".bak." + System.currentTimeMillis()));
}

/**
* 利用NIO进行读写文件
*
* @param oldFileName 原文件的路径
* @param newFileName 新文件的路径
*/
public static void nioCopy(String oldFileName, String newFileName) {
try {
FileChannel fileChannelIn = new FileInputStream(new File(oldFileName)).getChannel();
FileChannel fileChannelOut = new FileOutputStream(new File(newFileName)).getChannel();
//获取文件大小
long size = fileChannelIn.size();
System.out.printf("文件大小为:%s byte \n", size);
//缓冲
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

long start = System.currentTimeMillis();
while (fileChannelIn.read(byteBuffer) != -1) {
//准备写
byteBuffer.flip();
fileChannelOut.write(byteBuffer);
//准备读
byteBuffer.clear();
}
long end = System.currentTimeMillis();
System.out.printf("NIO方式复制完成,耗时 %s 秒\n", (end - start) / 1000);
//关闭
fileChannelIn.close();
fileChannelOut.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* IO方式复制文件
*
* @param oldFileName
* @param newFileName
*/
public static void ioCopy(String oldFileName, String newFileName) {
try {
FileInputStream fileInputStream = new FileInputStream(new File(oldFileName));
FileOutputStream fileOutputStream = new FileOutputStream(new File(newFileName));

long length = new File(oldFileName).length();

System.out.printf("文件大小为:%s byte \n", length);
byte[] buffer = new byte[1024];

long start = System.currentTimeMillis();
int len = 0;
//EOF = End Of File,其默认值就是-1
while ((len = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
long end = System.currentTimeMillis();
System.out.printf("IO方式复制完成,耗时 %s 秒\n", (end - start) / 1000);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static void ioCopyByLine(String oldFileName, String newFileName) {
try {
BufferedReader reader = new BufferedReader(new FileReader(oldFileName));
BufferedWriter writer = new BufferedWriter(new FileWriter(newFileName));
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
writer.flush();
}
} catch (IOException e) {
System.out.println("error:" + e);
}
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Hexo新玩法 - Atom + platformio-ide-terminal - - /20180716-Hexo%E6%96%B0%E7%8E%A9%E6%B3%95%20-%20Atom%20+%20platformio-ide-terminal/ - - 最近在慢慢适应mbp

然后就在研究怎么方便的写博客

首先就是找了几款markdown编辑器,发现GitHub出品的Atom还不错

插件很丰富,然而下载了第一个terminal插件但是无效,一度折腾了很久

后来搜索了一下,发现这个插件很不错:platformio-ide-terminal

利用快捷键:control + 点(1左边那个)

就可以在当前界面呼出终端,而且是当前目录

也就是在_post目录下,写完了之后执行命令就上传了

还是很方便的。后续还需要慢慢熟悉更多的插件与工具

]]>
- - - - - hexo - - - - - - - hexo - - - -
- - - - - 多线程:优雅的使用ExecutorService进行压力测试 - - /20180714-%E5%A4%9A%E7%BA%BF%E7%A8%8B%EF%BC%9A%E4%BC%98%E9%9B%85%E7%9A%84%E4%BD%BF%E7%94%A8ExecutorService%E8%BF%9B%E8%A1%8C%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95/ - - 很多时候写功能或者接口需要进行压力测试,
今天发现jwt在生成token的时候,如果输入都是一样的
仅有一个签发时间不一样,生成的token是有可能是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void testCreate() {
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("hisenyuan").build();
ExecutorService pool = new ThreadPoolExecutor(
20,
50,
10000L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10240),
namedThreadFactory,
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 50; i++) {
// 需要提交的内容
pool.execute(this::createTokenTest);
}
pool.shutdown();
try {
while (!pool.awaitTermination(500, TimeUnit.MILLISECONDS)) {
LOGGER.debug("Waiting for terminate");
}
} catch (InterruptedException e) {
LOGGER.error(e);
}
}
]]>
- - - - - java - - - - - - - java - - - -
- - - - - SpringMVC redirect & return JSON & set HTTP code - - /20180713-SpringMVC%20redirect%20&%20return%20JSON%20&%20set%20HTTP%20code/ - - springmvc正常情况下redirect并且设置指定响应码,异常情况下返回json数据

背景介绍

需求就是正常情况下能redirect到指定的页面
异常的情况下,能够返回JSON格式的错误信息
正常情况和异常情况都需要设置HTTP Code

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RequestMapping(value = "/test", method = RequestMethod.POST)
public String webCharge(HttpServletRequest request, HttpServletResponse response) {
if (1==1) {
response.setStatus(200);
return "redirect:https://github.com/hisenyuan";
} else {
try {
response.setStatus(405);
response.getWriter().write(JSON.toJSONString("hisenyuan"));
return null;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
]]>
- - - - - java - - - - - - - java - - - -
- - - - - Get参数串KV与Map互转 - 利用java8 Stream - - /20180713-Get%E5%8F%82%E6%95%B0%E4%B8%B2KV%E4%B8%8EMap%E4%BA%92%E8%BD%AC%20-%20%E5%88%A9%E7%94%A8java8%20Stream/ - - 一、背景

在各种系统需要加签的时
一般都会把参与签名的数据以get请求参数拼接起来
并且要求有序,这个方法会比较方便

二、实现

2.1 拼接为有序的get请求类字符串

1
2
3
4
5
6
7
8
9
10
public String getSortedStr(Map<String, String> unSortedStr) {
String sortedStr= unSortedStr
.entrySet()
.stream()
.filter(entry -> !StringUtil.isEmpty(entry.getValue()))
.sorted(Map.Entry.comparingByKey())
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("&"));
return sortedStr;
}

2.2 把get类参数字符串转为map

1
2
3
4
5
6
7
8
9
private Map<String,String> getMapData(String getStr){
String[] strs = getStr.split("&");
HashMap<String, String> dataMap = new HashMap<>(16);
for (int i = 0; i < strs.length; i++) {
String[] str = strs[i].split("=");
dataMap.put(str[0], str[1]);
}
return dataMap;
}

对于get类字符串没有发现比较好的方法转换为map

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Java调用有道翻译接口 - 免费翻译API - - /20180712-Java%E8%B0%83%E7%94%A8%E6%9C%89%E9%81%93%E7%BF%BB%E8%AF%91%E6%8E%A5%E5%8F%A3-%E5%85%8D%E8%B4%B9%E7%BF%BB%E8%AF%91API/ - - 这个接口为免费的

一、接口地址

等号后面的为需要翻译的英文

1
http://fanyi.youdao.com/openapi.do?keyfrom=xinlei&key=759115437&type=data&doctype=json&version=1.1&q=hisen

二、代码样例

把全世界200+国家和地区的名字翻译为英文,并且入库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Autowired
SmsCountryService countryService;

@Test
public void updateZhName(){
// 有道翻译接口
String url = "http://fanyi.youdao.com/openapi.do?keyfrom=xinlei&key=759115437&type=data&doctype=json&version=1.1&q=";
// 查询出所有的英文国家名字
List<SmsCountry> countries = countryService.queryAllName();
// httpclient
CloseableHttpClient client = HttpClientBuilder.create().build();
// 翻译每个国家的名字,并且更新数据库
for (SmsCountry country:countries) {
HttpGet request = new HttpGet(url + URLEncoder.encode(country.getName()));
try {
CloseableHttpResponse response = client.execute(request);
String str = EntityUtils.toString(response.getEntity(), "utf-8");
JSONObject jsonObject = JSON.parseObject(str);
// 取出json字符串中数组的值
String s = (String) jsonObject.getJSONArray("translation").get(0);
System.out.println(">>>>> " + s);
if (!StringUtil.isEmpty(s)){
SmsCountry smsCountry = new SmsCountry();
smsCountry.setId(country.getId());
smsCountry.setNameZh(s);
countryService.updateNameById(smsCountry);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

]]>
- - - - - - java - - - -
- - - - - List<Object> 去重 | List对象去重 by java8 stream & lambda - - /20180712-List-Object-%E5%8E%BB%E9%87%8D-List%E5%AF%B9%E8%B1%A1%E5%8E%BB%E9%87%8D-by-java8-stream-lambda/ - - 利用java8的流和lambda表达式能很方便的对list对象进行去重
而且不会造成代码入侵

插播:Java8 对List进行求和、分组、提取对象单个属性:https://www.jianshu.com/p/c71eaeaaf30c

下面的例子仅供参考
github:https://github.com/hisenyuan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.hisen.collection.list.duplicate;

import com.alibaba.rocketmq.shade.com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* @author hisenyuan
* @time 2018/4/19 14:06
* @description list对象根据属性去重 lambda + stream
*/
public class ListDuplicateTest {


public static void main(String[] args) {
List<Person> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Person person = new Person();
person.setAge(10);
person.setName("hsien");
person.setWeight(i);
list.add(person);
}
Person bean = new Person();
bean.setName("hisenyuan");
bean.setAge(33);
bean.setWeight(65);
list.add(bean);

List<Person> collect = list.stream().filter(distinctByKey(Person::getName))
.collect(Collectors.toList());
System.out.println(JSON.toJSONString(collect));
}

/**
* 函数式接口 T -> bollean
* @param keyExtractor
* @param <T>
* @return
*/
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>(16);
return t -> map.putIfAbsent(keyExtractor.apply(t),Boolean.TRUE) == null;

// 这个也可以,不过感觉效率要低一些,线程不是那么安全
// Set<Object> seen = ConcurrentHashMap.newKeySet();
// return t -> seen.add(keyExtractor.apply(t));
}

/**
* 内部类
*/
static class Person {
private int age;
private String name;
private int weight;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getWeight() {
return weight;
}

public Person() {
}
public void setWeight(int weight) {
this.weight = weight;
}
}
}

]]>
- - - -
- - - - - Linux|mac查找目录下文件内容中包含某字符串的命令 - - /20180712-Linux-mac%E6%9F%A5%E6%89%BE%E7%9B%AE%E5%BD%95%E4%B8%8B%E6%96%87%E4%BB%B6%E4%B8%AD%E5%8C%85%E5%90%AB%E6%9F%90%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%91%BD%E4%BB%A4/ - -
1
grep -r hisen ./

上面这条命令。直接查找当前目录下所有内容中包含 hisen 的文件

]]>
- - - -
- - - - - 安全的重叠构造器 - 最佳实践:Build Pattern - - /20180128-%E5%AE%89%E5%85%A8%E7%9A%84%E9%87%8D%E5%8F%A0%E6%9E%84%E9%80%A0%E5%99%A8%20-%20%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%EF%BC%9ABuild%20Pattern/ - -
1
effective java # 2

Builder模式,不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,

得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,

来设置每个相关可选的参数,最后调用无参的build来生成不可变的对象。

完整代码+测试:github:完整代码+测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class NutritionFacts {

private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;

private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}

public static class Builder {

// Required parameters
private final int servingSize;
private final int servings;

private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;

public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}

public Builder calories(int val) {
calories = val;
return this;
}

public Builder fat(int val) {
this.fat = val;
return this;
}

public Builder carbohydrate(int val) {
this.carbohydrate = val;
return this;
}

public Builder sodium(int val) {
this.sodium = val;
return this;
}

public NutritionFacts build() {
return new NutritionFacts(this);
}
}



@Override
public String toString() {
return "NutritionFacts{" +
"servingSize=" + servingSize +
", servings=" + servings +
", calories=" + calories +
", fat=" + fat +
", sodium=" + sodium +
", carbohydrate=" + carbohydrate +
'}';
}
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Java实现Singleton最佳方法 - Enum - - /20180127-%E5%AE%9E%E7%8E%B0Singleton%E6%9C%80%E4%BD%B3%E6%96%B9%E6%B3%95%20-%20Enum/ - - effective java:

1
单元素的枚举类型已经成为实现Singleton的最佳方法

理由:

  1. 因为枚举单例有序列化和线程安全的保证
  2. 避免反射和并发困扰

单例模式模式:完整代码+测试

主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class EnumSingleton {

private EnumSingleton() {
}

public static EnumSingleton getInstance() {
return Singleton.INSTANCE.getInstance();
}

private enum Singleton {
INSTANCE;
private EnumSingleton singleton;

Singleton() {
singleton = new EnumSingleton();
}

public EnumSingleton getInstance() {
return singleton;
}
}
}
]]>
- - - - - java - - - - - - - java - - - -
- - - - - 笔记 - 读后感:《第一本Docker书》 - - /20180127-%E7%AC%94%E8%AE%B0%20-%20%E8%AF%BB%E5%90%8E%E6%84%9F%EF%BC%9A%E3%80%8A%E7%AC%AC%E4%B8%80%E6%9C%ACDocker%E4%B9%A6%E3%80%8B/ - - 忘记开始是在什么地方看到docker这个东西

后面觉得挺好玩,也试了很多次,找过很多的教程。

搭建了一个zookeeper的集群(docker-compose)

然后当我需要搭建redis集群的时候,发现里面很多的概念还不是很懂

比如:volume network

然后加了docker的群,遇到了在以前idea群里面熟的一个人

花了一天的时间看完他传的一本书,昨天买的几本书晚上也到了。
(docker从入门到实践、java并发编程的艺术、effective java中文版 2)

这本书总体来说还行,就是过时了,还在用link,毕竟三年前的东西

有些例子也报错,主要是ruby相关的不行,提醒需要2.2以上的版本

下面是随便做的一点笔记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# 所有容器,不加-a只显示正在运行的;-1 列出最后一次运行的
docker ps -a
# 启动一个已经停止的容器(id,--name 都可以)
docker start hisen_container
# 进入容器(容器重启之后进入交换行)
docker attach hisen_container
# 创建一个守护式容器
docker -d
# 查看日志 可选 -f 类似tail -f Ctrl+c 退出
docker logs hisen_container
# 查看容器内的进程
docker top hisen_container
# 在容器内部运行进程
docker exec -d hisen_container touch /etc/new_conf_file
# 停止守护式容器
docker stop hisen_container
# 自动重启容器
docker run --restart=always ....
=on-failure:5 当容器退出代码为非0时,docker会自动重启,最多重试5次
# 深入容器,会以json串显示更多容器信息
docker inspect hisen_container
# 删除容器,要先stop才行
docker rm hisen_container
# 删除所有容器 -a:所有容器 -q:只返回容器的ID
docker rm 'docker ps -a -q'
# 启动新的名字为other_hisen_container的容器,并且进入bash shell
docker run -i -t --name other_hisen_container ubuntu /bin/bash
# docker文件系统层
可写容器
镜像(比如tomcat)
基础镜像(比如ubuntu)
引导文件系统(容器组、命名空间、设备映射)
内核
# docker简单构成
顶层的读写层+底部不可变的可读层还有一些配置,就成了一个容器。
# 列出所有镜像
docker images
# 仓库名:tag 如果没有指定,默认latest
# 推荐使用Dockerfile构建镜像
FROM ubuntu:14.04
MAINTAINER hisenyuan "hisenyuan@gmail.com"
RUN apt-get update \
&& apt-getinstall -y nginx \
&& echo 'Hi,I am in your container' \
/usr/share/nginx/html/index.html
EXPOSE 80
# 构建镜像,在上面的dockerfile目录
docker build -t="hisenyuan/static_web" .
# 查看构建过程,以及镜像的每一层
docker history 791397495f7a
# 运行构建的镜像 -p端口映射 nginx -g "daemon off;" 前台运行nginx
docker run -d -p 80:80 --name static_web hisenyuan/static_web nginx -g "daemon off;"
# Dockerfile指令
RUN 镜像构建的时候才会执行
CMD 启动一个容器的时候要运行的命令,如有多个,只会生效最后一个,前面的多个会被忽略
ENTRYPOINT 例子:ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"],这个命令不容易覆盖,CMD容易被RUN覆盖
WORKDIR 设定工作目录,ENTRYPOINT CMD 会在这个目录执行
ENV 设置构建时的环境变量,设置之后可以在后续的RUN命令使用
USER 设置运行的用户
VOLUME 向基于镜像创建的容器添加卷。
1.卷可以在容器间共享和重用
2.一个容器不是必须和其他容器共享卷
3.对卷的修改是立即生效的
4.对卷的修改不会对更新镜像产生影响
5.卷会一直存在,直到没有任何容器再使用他
VOLUME ["/opt/project"] 创建一个名为/opt/project的挂载点
ADD 添加上下文目录的文件到构架容器中,如果是压缩包,会自动解压。会使构建缓存无效
COPY 类似ADD、只会在上下文复制文件、也不会解压
ONBUILD 为镜像添加触发器,当一个镜像被用作其他镜像的基础镜像时,该镜像中的触发器将会被执行。
# 删除镜像,会删除这个镜像的每一层
docker rmi hisenyuan/static_web
# 运行一个registry
docker run -p 5000:5000 registry
# 推送镜像到registry
docker tag 791397495f7a yourip:5000/hisenyuan/static_web #打标签
docker push yourip:5000/hisenyuan/static_web #推送到本地注册容器
docker run -t -i docker yourip:5000/hisenyuan/static_web /bin/bash #从registry运行一个容器

==========第五章 在测试中使用docker==========
5.1 使用docker测试静态网站
5.1.1 sample网站的初始Dockerfile
hisen@docker1:~/docker$ mkdir sample
hisen@docker1:~/docker$ cd sample/
hisen@docker1:~/docker/sample$ touch Dockerfile
hisen@docker1:~/docker/sample$ mkdir nginx && cd nginx
hisen@docker1:~/docker/sample/nginx$ vi global.conf
server {
listen 0.0.0.0:80;
server_name _;

root /var/www/html/website;
index index.html index.htm;

access_log /var/log/nginx/default_access.log;
error_log /var/log/nginx/default_error.log;
}

hisen@docker1:~/docker/sample/nginx$ vi nginx.conf
user www-data;
worker_processes 4;
pid /run/nginx.pid;
daemon off;

events { }

http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
}
hisen@docker1:~/docker/sample/nginx$ vi Dockerfile
FROM ubuntu:14.04
MAINTAINER hisenyuan "hisenyuan@gmail.com"
ENV REFRESHED_AT 2018-01-27
RUN apt-get update \
&& apt-get -y -q install nginx \
&& mkdir -p /var/www/html
ADD nginx/global.conf /etc/nginx/conf.d/
ADD nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
hisen@docker1:~/docker/sample/nginx/website$ vi index.html
<head>
<title>Test website</title>
</head>
<body>
<h1>This is a test website</h1>
</body>
hisen@docker1:~/docker/sample/nginx$ docker build -t hisenyuan/nginx .
hisen@docker1:~/docker/sample/nginx$ docker run -d -p 8080:80 --name website \
> -v $PWD/website:/var/www/html/website \
> hisenyuan/nginx nginx # 因为已经在配置文件指定了daemon off,所以能直接启动
# -v 指定了卷的源目录,挂载本地$PWD/website到容器/var/www/html/website目录,conf配置文件指定了工作目录

# 修改之后,刷新浏览器,立马就生效
hisen@docker1:~/docker/sample/nginx$ vi $PWD/website/index.html
<head>
<title>Test website - hisenyuan</title>
</head>
<body>
<h1>This is a test website</h1>
<h2>2018年1月27日 13:20:13</h2>
</body>

5.2 使用docker构建并测试web应用程序
5.2.1 构建sinatra应用程序
# 创建Dockerfile
hisen@docker1:~/docker/sinatra$ vi Dockerfile
FROM ubuntu:14.04
MAINTINER hisenyuan "hisenyuan@gmail.com"
ENV REFRESHED 2018-01-27
RUN apt-get upate \
&& apt-get -y install ruby ruby-dev build-essential redis-tools \
&& gem install --no-rdoc --no-ri sinatra json redis \
&& mkdir -p /opt/webapp
EXPOSE 4567

# 代码网站:https://dockerbook.com/code/5/sinatra/webapp/
wget --cut-dir=3 -nH -r --no-parent https://dockerbook.com/code/5/sinatra/webapp/

5.3 docker用于持续集成

==========第六章 使用docker构建服务==========
6.1 构建第一个应用
==========第七章 Consul、服务发现与注册==========

]]>
- - - - - docker - - - - - - - docker - - - -
- - - - - Java - 利用apache POI读取Excel(xls,xlsx,2003,2007) - - /20180113-Java%20-%20%E5%88%A9%E7%94%A8apache%20POI%E8%AF%BB%E5%8F%96Excel%EF%BC%88xls,xlsx,2003,2007)/ - - 一个简单的工具类,更多java小练习:https://github.com/hisenyuan/IDEAPractice

一、添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- POI start -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<!-- POI end -->

二、测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.hisen.jars.poi;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
* @author hisenyuan
* @time 2018/1/12 17:43
* @description 测试读取
*/
public class TestPoiExcelUtil {

public static void main(String[] args) {
File file = new File("C:\\work\\document\\银行信息.xlsx");
try {
// 每一个excelData为一行数据(存放在数组)
List<String[]> excelData = POIExcelUtil.readExcel(file);
for (String[] data:excelData) {
System.out.println(Arrays.toString(data));
}
} catch (IOException e) {
e.printStackTrace();
}
}

}

三、工具类代码

完整类:POIExcelUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.hisen.jars.poi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
* @author hisenyuan
* @time 2018/1/12 16:35
* @description 利用POI读取excel表格
*/
public class POIExcelUtil {

private static Logger logger = Logger.getLogger(POIExcelUtil.class);
private final static String XLS = "xls";
private final static String XLSX = "xlsx";

public static List<String[]> readExcel(File file) throws IOException {
// 检查文件
checkFile(file);
Workbook workBook = getWorkBook(file);
// 返回对象,每行作为一个数组,放在集合返回
ArrayList<String[]> rowList = new ArrayList<>();
if (null != workBook) {
for (int sheetNum = 0; sheetNum < workBook.getNumberOfSheets(); sheetNum++) {
// 获得当前sheet工作表
Sheet sheet = workBook.getSheetAt(sheetNum);
if (sheet == null) {
continue;
}
// 获得当前sheet的开始行
int firstRowNum = sheet.getFirstRowNum();
// 获得当前sheet的结束行
int lastRowNum = sheet.getLastRowNum();
// 循环所有行(第一行为标题)
for (int rowNum = firstRowNum; rowNum < lastRowNum; rowNum++) {
// 获得当前行
Row row = sheet.getRow(rowNum);
if (row == null) {
continue;
}
// 获得当前行开始的列
short firstCellNum = row.getFirstCellNum();
// 获得当前行的列数
int lastCellNum = row.getPhysicalNumberOfCells();
String[] cells = new String[row.getPhysicalNumberOfCells()];
// 循环当前行
for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++) {
Cell cell = row.getCell(cellNum);
cells[cellNum] = getCellValue(cell);
}
rowList.add(cells);
}
}
}
return rowList;
}
}

]]>
- - - - - java - - - - - - - poi - - - -
- - - - - Redis counter demo - redis并发计数器 - - /20180111-Redis%20counter%20demo%20-%20%20redis%E5%B9%B6%E5%8F%91%E8%AE%A1%E6%95%B0%E5%99%A8/ - - 一、说明

利用redis操作的原子性,实现java 多线程并发的情况下实现计数器。

我本机测试多个线程操作之后,结果会出现一定的延迟,但是最终数字是ok的

应该是redis内部做了一个类似于队列的功能。

需要注意的是,得使用redis连接的线程池,不然会出现异常

这里有一个:JedisUtil 下面用到了

二、 代码实现

2.1 redis操作类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.hisen.thread.count_click_by_redis;

import com.hisen.utils.JedisUtil;
import redis.clients.jedis.JedisPool;
/**
* @author hisenyuan
* @description 操作redis的线程类
*/
public class ClickRedis {

/**
* 必须使用线程池,而且线程池要大于并发数,否则会出现redis超时
*/
private static JedisPool jedis = JedisUtil.getPool();

public static void click() {
jedis.getResource().incrBy("hisen", 1);
}

public static int getCount() {
return Integer.parseInt(jedis.getResource().get("hisen"));
}

public static void declare() {
jedis.getResource().del("hisen");
jedis.close();
}
}

2.2 线程类,模拟点击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.hisen.thread.count_click_by_redis;

/**
* @author hisenyuan
* @description 执行点击的线程类
*/
public class CountClickByRedisThread extends Thread{

private int id;
public CountClickByRedisThread(int id) {
this.id = id;
}

@Override
public void run() {
super.run();
ClickRedis.click();
int count = ClickRedis.getCount();
System.out.println("task:" + id + "\t 执行完毕\t线程编号:" + this.getId() + "\t当前值:" + count);
}
}

2.3 主线程 - 启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.hisen.thread.count_click_by_redis;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

public static void main(String[] args) throws InterruptedException {
/**
* 5 - corePoolSize:核心池的大小
* 10 - maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程
* 200 - keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。
* unit - unit:参数keepAliveTime的时间单位,有7种取值
* workQueue:一个阻塞队列,用来存储等待执行的任务
*/
ThreadPoolExecutor executor = new ThreadPoolExecutor(
50,
100,
200,
TimeUnit.MICROSECONDS,
new ArrayBlockingQueue<Runnable>(50));
// 开启50个线程
for (int i = 0; i < 50; i++) {
executor.execute(new CountClickByRedisThread(i));
}
System.out.println("已经开启所有的子线程");
// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
executor.shutdown();
// 判断所有线程是否已经执行完毕
while (true) {
if (executor.isTerminated()) {
System.out.println("所有的子线程都结束了!");
// 清除redis数据
ClickRedis.declare();
break;
}
Thread.sleep(100);
}
}

}

]]>
- - - - - sql - - - - - - - redis - - - -
- - - - - rabbitMQ的安装和Demo - - /20180111-rabbitMQ%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8CDemo/ - - 零、rabbitMQ介绍

rabbitMQ详细介绍

  1. 如果某个queue有多个订阅,消息分均分到消费者,而不是所有人都收到全部
  2. 接收消息有ack(acknowledgment)机制,发送消息是没有这个机制的
  3. 生产者将消息发送到Exchange(交换器),由Exchange将消息路由到一个或多个Queue中(或者丢弃)。

    一、在ununtu上安装

    1.1 安装

    1
    2
    3
    4
    5
    6
    7
    echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list

    wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -

    sudo apt-get update

    sudo apt-get install rabbitmq-server

1.2 配置

1
2
3
4
5
6
7
8
9
10
11
# 打开管理页面功能
sudo rabbitmq-plugins enable rabbitmq_management
# 查看安装的插件
sudo rabbitmqctl list_users
# 查看用户
sudo rabbitmqctl list_users
# 新增管理员用户
sudo rabbitmqctl add_user admin admin
# 授予管理员权限
sudo rabbitmqctl set_user_tags admin administrator
# 管理页面地址,用刚设置的账户登录管理页面

http://127.0.0.1:15672

二、Java小Demo

2.1 遇到的问题

  1. ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN.
    帐号密码错误,建议使用2.3配置的账户,guest账户不靠谱

  2. connection error
    ip或者port错误,确认信息是否正确,虚拟机的话看看端口映射是否正常

2.2 配置用户

1
2
3
4
5
6
# 添加普通用户
sudo rabbitmqctl add_user hisen hisen
# 添加权限
rabbitmqctl set_permissions -p "/" hisen ".*" ".*" ".*"
# 列出用户权限
rabbitmqctl list_user_permissions hisen

2.3 添加maven依赖

1
2
3
4
5
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.0.0</version>
</dependency>

2.4 发送端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.hisen.jars.rabbitmq;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
// 定义队列名字
public static final String QUEUE_NAME = "hello";

public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("hisen");
factory.setPassword("hisen");
Connection connection = null;
Channel channel = null;
try {
// 创建连接
connection = factory.newConnection();
// 创建信道
channel = connection.createChannel();
// 声明一个队列:名称、持久性的(重启仍存在此队列)、非私有的、非自动删除的
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
for (int i = 0; i < 10; i++) {
// 定义消息内容
String message = "Hello World - " + i;
// 通过信道发布内容
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println("send : " + message);
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
if (null!=channel) {
channel.close();
}
if (null!= connection ) {
connection.close();
}
}
}
}

2.5 接收端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.hisen.jars.rabbitmq;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.joda.time.DateTime;

public class Receive{
// 定义队列名字
public static final String QUEUE_NAME = "hello";

public static void main(String[] args) {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setUsername("hisen");
factory.setPassword("hisen");
try {
// 创建连接
Connection connection = factory.newConnection();
// 创建信道
Channel channel = connection.createChannel();
// 声明一个队列:名称、持久性的(重启仍存在此队列)、非私有的、非自动删除的
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println("watting for message");

/* 定义消费者 */
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println("Received time:" + new DateTime().toString("yyyy-MM-dd HH:mm:ss:SSS EE")+ " the message is -> " + message);
}
};
// 将消费者绑定到队列,并设置自动确认消息(即无需显示确认,如何设置请慎重考虑)
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
]]>
- - - - - java - - - - - - - rabbitMQ - - - -
- - - - - DockerFile - 创建java开发环境镜像 - - /20180111-DockerFile%20-%20%E5%88%9B%E5%BB%BAjava%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E9%95%9C%E5%83%8F/ - - 使用ubuntu官方发布的docker镜像进行二次修改

这是一个菜鸟的脚本,执行命令应该是使用 & 连接,一个RUN命令搞定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
FROM    ubuntu
MAINTAINER Fisher "hisenyuan@gmail.com"
RUN /bin/echo 'root:hisen' |chpasswd
RUN useradd hisen
RUN /bin/echo 'hisen:hisen' |chpasswd
RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
# 显示系统位数
RUNuname -p
# 清空源
RUN echo "" > /etc/apt/sources.list
# 更换为阿里云源
RUNecho "deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse" >> /etc/apt/sources.list
RUNecho "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse" >> /etc/apt/sources.list
# 更新源
RUNapt-get update
# 安装软件
RUN apt-get -y install vim
RUN apt-get -y install curl
RUN apt-get -y install wget
RUN apt-get -y install net-tools
RUN apt-get -y install iputils-ping
RUN apt-get -y install git
# 创建软件文件夹
RUN mkdir -p /usr/hisen/soft/java
RUN mkdir -p /usr/hisen/soft/tomcat
RUN mkdir -p /usr/hisen/soft/maven
RUN mkdir -p /usr/hisen/soft/download
# 添加本地软件包到指定文件夹(会自动解压,软件压缩包必须放在docker同级目录)
ADD jdk-8u151-linux-x64.tar.gz /usr/hisen/soft/java/
ADD apache-tomcat-8.5.24.tar.gz /usr/hisen/soft/tomcat/
ADD apache-maven-3.5.2-bin.tar.gz /usr/hisen/soft/maven/

# 配置环境变量
# java
ENV JAVA_HOME=/usr/hisen/soft/java/jdk1.8.0_151
ENV JRE_HOME=$JAVA_HOME/jre
ENV CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib
ENV PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
# tomcat
ENV CATALINA_HOME=/usr/hisen/soft/tomcat/apache-tomcat-8.5.24
ENV CLASSPATH=.:$JAVA_HOME/lib:$CATALINA_HOME/lib
ENV PATH=$PATH:$CATALINA_HOME/bin
# maven
ENV MAVEN_HOME=/usr/hisen/soft/maven/apache-maven-3.5.2
ENV MAVEN_OPTS="-Xms256m -Xmx512m"
ENV PATH=${MAVEN_HOME}/bin:$PATH

# 监听端口
EXPOSE 22
EXPOSE 80
EXPOSE 8080
CMD /usr/sbin/sshd -D

]]>
- - - - - linux - - - - - - - docker - - - -
- - - - - MySql的执行计划 - - /20171123-MySql%E7%9A%84%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92/ - - ###命令介绍
MySQL的EXPLAIN命令用于SQL语句的查询执行计划(QEP)。

这条命令的输出结果能够让我们了解MySQL 优化器是如何执行

执行下面的SQL

1
explain select * FROM book where name like '活%'

得到下面的数据

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfileredExtra
编号查询方式表名分区连接方式索引(可能)索引索引长度作用列行数百分比额外
1SIMPLEbooknullALLnullnullnullnull11411.11Using where

###建表语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#创建个人信息表
CREATE TABLE hisen_test_explain_people
(
id bigint auto_increment primary key,
zipcode char(32) not null default '',
address varchar(128) not null default '',
lastname char(64) not null default '',
firstname char(64) not null default '',
birthdate char(10) not null default ''
);
#创建索引
alter table hisen_test_explain_people add key(zipcode,firstname,lastname);

#插入个人信息数据
insert into hisen_test_explain_people
(zipcode,address,lastname,firstname,birthdate)
values
('230031','anhui','zhan','jindong','1989-09-15'),
('100000','beijing','zhang','san','1987-03-11'),
('200000','shanghai','wang','wu','1988-08-25');
#创建汽车信息表
CREATE TABLE hisen_test_explain_people_car(
people_id bigint,
plate_number varchar(16) not null default '',
engine_number varchar(16) not null default '',
lasttime timestamp
);
#插入汽车信息
insert into hisen_test_explain_people_car
(people_id,plate_number,engine_number,lasttime)
values
(1,'A121311','12121313','2017-11-23 :21:12:21'),
(2,'B121311','1S121313','2016-11-23 :21:12:21'),
(3,'C121311','1211SAS1','2015-11-23 :21:12:21');

###执行计划样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
explain select zipcode,firstname,lastname from hisen_test_explain_people;
+----+-------------+---------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | hisen_test_explain_people | NULL | index | NULL | zipcode | 160 | NULL | 3 | 100.00 | Using index |
+----+-------------+---------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+

explain select zipcode from (select * from hisen_test_explain_people a) b;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | a | NULL | index | NULL | zipcode | 160 | NULL | 3 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+


id是用来顺序标识整个查询中SELELCT 语句的,通过上面这个简单的嵌套查询可以看到id越大的语句越先执行。
该值可能为NULL,如果这一行用来说明的是其他行的联合结果,比如UNION语句:

explain select * from hisen_test_explain_people where zipcode = 100000 union select * from hisen_test_explain_people where zipcode = 200000;
+----+--------------+---------------------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+---------------------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | hisen_test_explain_people | NULL | ALL | zipcode | NULL | NULL | NULL | 3 | 33.33 | Using where |
| 2 | UNION | hisen_test_explain_people | NULL | ALL | zipcode | NULL | NULL | NULL | 3 | 33.33 | Using where |
|NULL| UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+---------------------------+------------+------+---------------+------+---------+------+------+----------+-----------------+

###参考

  1. 参数解释
  2. 实战演练
]]>
- - - - - sql - - - - - - - mysql - - sql - - explain - - - -
- - - - - Spring - IOC和AOP的原理 - - /20171120-Spring%20-%20IOC%E5%92%8CAOP%E7%9A%84%E5%8E%9F%E7%90%86/ - - 简单理解IOC和AOP

IOC:Inversion of Control,依赖倒置

生活场景助记

如有有天你想喝一瓶矿泉水,你可以去小区便利店,告诉老板你要买矿泉水,然后老板卖给你。

但是你可能需要想这下雨天怎么去小卖部?是否要带伞?去了之后是否有我想要的水等一系列问题。

解决这个问题:

  1. 使用外卖!到平台注册,告诉平台你需要什么水。
  2. 平台给你送到家,你只管付钱拿到水之后直接喝,不用考虑上述问题。

是不是和Spring的做法很类似呢?Spring就是小卖部,你就是A对象,水就是B对象
第一:在Spring中声明一个类:A
第二:告诉Spring,A需要B

假设A是UserAction类,而B是UserService类

1
2
3
4
5
6
7
<bean id="userService" class="org.leadfar.service.UserService"/>
<bean id="documentService" class="org.leadfar.service.DocumentService"/>
<bean id="orgService" class="org.leadfar.service.OrgService"/>

<bean id="userAction" class="org.leadfar.web.UserAction">
<property name="userService" ref="userService"/>
</bean>

在Spring这个商店(工厂)中,有很多对象/服务:userService,documentService,orgService

也有很多会员:userAction等等

声明userAction需要userService即可,

Spring将通过你给它提供的通道主动把userService送上门来,因此UserAction的代码示例类似如下所示:

1
2
3
4
5
6
7
8
9
10
package org.leadfar.web;
public class UserAction{
private UserService userService;
public String login(){
userService.valifyUser(xxx);
}
public void setUserService(UserService userService){
this.userService = userService;
}
}

在这段代码里面,你无需自己创建UserService对象(Spring作为背后无形的手,把UserService对象通过你定义的setUserService()方法把它主动送给了你,这就叫依赖注入!)

Spring依赖注入的实现技术是:动态代理

AOP:Aspect Oriented Programming,面向切面编程

你只要做你关注的事情,其他的事情一概不管,让AOP帮你去做

你可以灵活组合各种杂七杂八的事情交给AOP去做,而不会干扰你关注的事情。

从Spring的角度看,AOP最大的用途就在于提供了事务管理的能力。

事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦)

所以,Spring在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务!

ICO 和 AOP的原理

IoC的实现形式有两种:

  1. 依赖查找:容器提供回调接口和上下文环境给组件。EJB和Apache Avalon都是使用这种方式。
  2. 依赖注入:组件不做定位查询,只是提供普通的Java方法让容器去决定依赖关系。
    容器全权负责组件的装配,它会把符合依赖关系的对象通过JavaBean属性或者构造子传递给需要的对象。

通过JavaBean属性注射依赖关系的做法称为设值方法注入(Setter Injection);

将依赖关系作为构造子参数传入的做法称为构造子注入(Constructor Injection)。

AP实现有多种方案,主要包括:

  1. AspectJ (TM):创建于Xerox PARC. 有近十年历史,技术成熟。但其过于复杂;破坏封装;而且需要专门的Java编译器,易用性较差。
  2. 动态代理AOP:使用JDK提供的动态代理API或字节码Bytecode处理技术来实现。基于动态代理API的具体项目有: JBoss 4.0 JBoss 4.0服务器。
  3. 基于字节码的AOP,例如:Aspectwerkz、CGlib、Spring等。

    后期准备补充

    用过spring的朋友都知道spring的强大和高深,都觉得深不可测,

其实当你真正花些时间读一读源码就知道它的一些技术实现其实是建立在一些最基本的技术之上而已;
例如:

  1. AOP(面向方面编程)的实现是建立在CGLib提供的类代理和jdk提供的接口代理
  2. IOC(控制反转)的实现建立在工厂模式、Java反射机制和jdk的操作XML的DOM解析方式.

    参考

  3. https://www.cnblogs.com/damowang/p/4305107.html
  4. http://blog.csdn.net/linxijun120903/article/details/56487073
]]>
- - - - - java - - - - - - - spring - - - -
- - - - - dubbo与zookeeper的关系 - - /20171115-dubbo%E4%B8%8Ezookeeper%E7%9A%84%E5%85%B3%E7%B3%BB/ - - Zookeeper的作用

zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。

当然也可以 通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。

zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务对应关系从列表中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码 的情况通过添加机器来提高运算能力。

通过添加新的机器向zookeeper注册服务,服务的提供者多了能服务的客户就多了。

dubbo

是管理中间层的工具,在业务层到数据仓库间有非常多服务的接入和服务提供者需要调度,dubbo提供一个框架解决这个问题。

注意这里的dubbo只是一个框架,至于你架子上放什么是完全取决于你的,就像一个汽车骨架,你需要配你的轮子引擎。

这个框架中要完成调度必须要有一个分布式的注册中心,储存所有服务的元数据,你可以用zk,也可以用别的,只是大家都用zk。

zookeeper和dubbo的关系

Dubbo的将注册中心进行抽象,是得它可以外接不同的存储媒介给注册中心提供服务,有ZooKeeper,Memcached,Redis等。

引入了ZooKeeper作为存储媒介,也就把ZooKeeper的特性引进来。

  1. 负载均衡:单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;
  2. 资源同步:单单有负载均衡还不 够,节点之间的数据和资源需要同步,ZooKeeper集群就天然具备有这样的功能;
    1. 命名服务:将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布
    2. Mast选举,分布式锁等。

参考

https://www.cnblogs.com/xiaofei1208/p/7077733.html

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 如何线程安全的使用HashMap - - /20171114-%E5%A6%82%E4%BD%95%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E4%BD%BF%E7%94%A8HashMap/ - - 线程不安全的原因
  1. HashMap底层是一个Entry数组,一旦发生Hash冲突的的时候,HashMap采用拉链法解决碰撞冲突.
  2. put方法也不是同步的
  3. 扩容的方法也不是同步的

参考:https://www.cnblogs.com/qiumingcheng/p/5259892.html

如何线程安全的使用

  1. Hashtable
  2. ConcurrentHashMap
  3. SynchronizedMap

####例子

1
2
3
4
5
6
//Hashtable
Map<String, String> hashtable = new Hashtable<>();
//synchronizedMap
Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
//ConcurrentHashMap
Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

####性能对比
使用ExecutorService来并发运行5个线程,每个线程添加/获取500K个元素。

从数据可以看出,ConcurrentHashMap效率最高

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.hisen.collection.map;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
* @author : yhx
* @date : 2017/11/14 23:36
* @descriptor : 测试不同的Map - 使用ExecutorService来并发运行5个线程,每个线程添加/获取500K个元素。
*/
public class CrunchifyConcurrentHashMapVsSynchronizedMap {
private static final int THREAD_POOL_SIZE = 5;
public static Map<String, Integer> crunchifyHashTableObject = null;
public static Map<String, Integer> crunchifySynchronizedMapObject = null;
public static Map<String, Integer> crunchifyConcurrentHashMapObject = null;

public static void main(String[] args) throws InterruptedException {
// Test with Hashtable Object
crunchifyHashTableObject = new Hashtable<>();
crunchifyPerformTest(crunchifyHashTableObject);

// Test with synchronizedMap Object
crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>());
crunchifyPerformTest(crunchifySynchronizedMapObject);

// Test with ConcurrentHashMap Object
crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>();
crunchifyPerformTest(crunchifyConcurrentHashMapObject);

/**
* 测试结果
Test start for:class java.util.Hashtable
2500K entried added/retrieved in 2953 ms
2500K entried added/retrieved in 4649 ms
2500K entried added/retrieved in 2736 ms
2500K entried added/retrieved in 2628 ms
2500K entried added/retrieved in 2621 ms
For class java.util.Hashtable the average time is 3117 ms

Test start for:class java.util.Collections$SynchronizedMap
2500K entried added/retrieved in 3036 ms
2500K entried added/retrieved in 2881 ms
2500K entried added/retrieved in 2692 ms
2500K entried added/retrieved in 3020 ms
2500K entried added/retrieved in 2806 ms
For class java.util.Collections$SynchronizedMap the average time is 2887 ms

Test start for:class java.util.concurrent.ConcurrentHashMap
2500K entried added/retrieved in 4378 ms
2500K entried added/retrieved in 1126 ms
2500K entried added/retrieved in 1008 ms
2500K entried added/retrieved in 935 ms
2500K entried added/retrieved in 1069 ms
For class java.util.concurrent.ConcurrentHashMap the average time is 1703 ms
*/
}

private static void crunchifyPerformTest(Map<String, Integer> crunchifyThreads)
throws InterruptedException {
System.out.println("Test start for:" + crunchifyThreads.getClass());
long avgTime = 0;
for (int i = 0; i < 5; i++) {
long startTime = System.nanoTime();

ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
for (int j = 0; j < THREAD_POOL_SIZE; j++) {
executorService.execute(new Runnable() {
@Override
public void run() {
for (int k = 0; k < 500000; k++) {
Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000);
// Retrieve value. We are not using it anywhere
Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));
// Put value
crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);
}
}
});
}
// Make sure executor stops
executorService.shutdown();
// Blocks until all tasks have completed execution after a shutdown request
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
long entTime = System.nanoTime();
long totalTime = (entTime - startTime) / 1000000L;
avgTime += totalTime;
System.out.println("2500K entried added/retrieved in " + totalTime + " ms");
}
System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + avgTime / 5 + " ms\n");
}
}

]]>
- - - - - java - - - - - - - HashMap - - - -
- - - - - SQL语句的执行顺序 - 关键字的顺序 - - /20171114-SQL%E8%AF%AD%E5%8F%A5%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/ - - sql执行顺序
  1. from
  2. join
  3. on
  4. where
  5. group by(开始使用select中的别名,后面的语句中都可以使用)
  6. avg,sum….
  7. having
  8. select
  9. distinct
  10. order by

参考资料

http://blog.csdn.net/u014044812/article/details/51004754

http://blog.csdn.net/bitcarmanlee/article/details/51004767 (含流程图)

]]>
- - - - - sql - - - - - - - java - - sql - - - -
- - - - - Oracle - MySQL - 数据库事务隔离级别介绍 - - /20171114-Oracle%20-%20MySQL%20-%20%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E4%BB%8B%E7%BB%8D/ - - 名词解释
  1. 脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
  2. 不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
  3. 幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.

数据库的4个事物隔离级别

√:可能会出现
×:为不会出现

name名称级别脏读不可重复读幻读
Read uncommitted读未提交1
Read committed读提交2×
Repeatable read重复读3××
Serializable序列化4×××

oracle

Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE.

Oracle 默认的事务隔离级别为: READ COMMITED

mysql

Mysql 支持 4 中事务隔离级别.

Mysql 默认的事务隔离级别为: REPEATABLE READ

延伸

参考:https://www.cnblogs.com/andy6/p/6045679.html

]]>
- - - - - sql - - - - - - - java - - sql - - - -
- - - - - 利用Java构造二叉树 - 前序、中序、后续、层次遍历 - - /20171113-%E5%88%A9%E7%94%A8Java%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91%20-%20%E5%89%8D%E5%BA%8F%E3%80%81%E4%B8%AD%E5%BA%8F%E3%80%81%E5%90%8E%E7%BB%AD%E3%80%81%E5%B1%82%E6%AC%A1%E9%81%8D%E5%8E%86/ - - 定义

最多有两棵子树的有序树,称为二叉树。二叉树是一种特殊的树。

性质

这里规定二叉树的根结点的层次为1。

  1. 性质1:则二叉树的第i 层最多有2i-1个结点(在此二叉树的层次从1开始,i≥1)
  2. 性质2:深度为k的二叉树最多有2k-1个结点。(k≥1)
  3. 性质3:对任何一棵二叉树T, 如果其叶结点个数为n0, 度为2的非叶结点个数为n2, 则有
    n0 = n2 + 1
  4. 性质4:具有 n(n>0)个结点的完全二叉树的深度为⎣log2n⎦+1;⎦x⎦表示不超过x的最大整数。
  5. 性质5:如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第⎣l og2n⎦ +1层,每层从左到右),则对任一结点i(1≤i≤n),有:
    5.1 (1)如果i=1,则结点i无双亲,是二叉树的根;如果i>1,则其双亲是结点⎣i/2⎦。
    5.2 (2) 如果2i<=n, 则结点i的左孩子结点是2i;否则,结点i为叶子结点,无左孩子结点。
    5.3 (3)如果2i+1<=n,则结点i的右孩子是结点2i+1; 否则,结点i为叶子结点,无右孩子结点。

完整代码

https://github.com/hisenyuan/btree

二叉链表的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package com.hisen.interview.tiger20171110.btree;

/**
* @author : yhx
* @date : 2017/11/10 18:42
* @descriptor : 二叉树的 - 二叉链表实现
*/
public class LinkBTree implements BTree {

private Object data;
private BTree lChild;
private BTree rChild;

public LinkBTree() {
this.clearTree();
}

public LinkBTree(Object data) {
this.data = data;
this.rChild = null;
this.lChild = null;
}

@Override
public void addLfetTree(BTree lChild) {
this.lChild = lChild;
}

@Override
public BTree getLfetTree() {
return lChild;
}

@Override
public void addRightTree(BTree rChild) {
this.rChild = rChild;
}

@Override
public BTree getRightTree() {
return rChild;
}

@Override
public void clearTree() {
this.data = null;
this.rChild = null;
this.lChild = null;
}

@Override
public int getDeep() {
return deep(this);
}


@Override
public Object getRootData() {
return data;
}

@Override
public boolean hasLeftTree() {
if (lChild != null) {
return true;
}
return false;
}

@Override
public boolean hasRightTree() {
if (rChild != null) {
return true;
}
return false;
}

@Override
public boolean isEmptyTree() {
if ((lChild == null && rChild == null && data == null) || this == null) {
return true;
}
return false;
}

@Override
public boolean isLeaf() {
if (lChild == null && rChild == null) {
return true;
}
return false;
}

@Override
public void removeLeftTree() {
lChild = null;
}

@Override
public void removeRightTree() {
rChild = null;
}

@Override
public BTree getRoot() {
return this;
}

@Override
public void setRootData() {
this.data = data;
}

@Override
public int size() {
return size(this);
}

private int size(BTree bTree) {
if (bTree == null) {
return 0;
} else if (bTree.isLeaf()) {
return 1;
} else {
if (bTree.getLfetTree() == null) {
return size(bTree.getRightTree()) + 1;
} else if (bTree.getRightTree() == null) {
return size(bTree.getLfetTree()) + 1;
} else {
return size(bTree.getLfetTree()) + size(bTree.getRightTree()) + 1;
}
}
}

/**
* 计算二叉树的高度
*/
private int deep(BTree bTree) {
if (bTree.isEmptyTree()) {
return 0;
} else if (bTree.isLeaf()) {
return 1;
} else {
if (bTree.getLfetTree() == null) {
return deep(bTree.getRightTree()) + 1;
} else if (bTree.getRightTree() == null) {
return deep(bTree.getLfetTree()) + 1;
} else {
return Math.max(deep(bTree.getLfetTree()), deep(bTree.getRightTree())) + 1;
}
}
}
}

二叉树的各种遍历

遍历方式:前序、中序、后序、层次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.hisen.interview.tiger20171110.btree;

import java.util.LinkedList;

/**
* @author : yhx
* @date : 2017/11/13 12:15
* @descriptor : 二叉树的遍历
*/
public class OrderBTree implements Visit {

/**
* 前序遍历
*
* @param root 根节点
*/
public void preOrder(BTree root) {
visit(root);
if (root.getLfetTree() != null) {
preOrder(root.getLfetTree());
}
if (root.getRightTree() != null) {
preOrder(root.getRightTree());
}
}

/**
* 中序遍历
*
* @param root 根节点
*/
public void inOrder(BTree root) {
if (root.getLfetTree() != null) {
inOrder(root.getLfetTree());
}
visit(root);
if (root.getRightTree() != null) {
inOrder(root.getRightTree());
}
}

/**
* 后序遍历
* @param root 根节点
*/
public void postOrder(BTree root) {

if (root.getLfetTree() != null) {
postOrder(root.getLfetTree());
}
if (root.getRightTree() != null) {
postOrder(root.getRightTree());
}
visit(root);
}

/**
* 层次遍历 - 利用队列
* @param bTree 根节点
*/
public void levelOrder(BTree bTree){
if (bTree == null){
return;
}
LinkedList<BTree> queue = new LinkedList<>();
BTree current = null;
// 将根节点入队列
queue.offer(bTree);
while (!queue.isEmpty()){
// 队头元素出队列
current = queue.poll();
visit(current);
// 如果左节点不为空,入队列
if (current.getLfetTree() != null){
queue.offer(current.getLfetTree());
}
// 如果右节点不为空,入队列
if (current.getRightTree() != null){
queue.offer(current.getRightTree());
}
}
}
/**
* 访问二叉树的节点
* @param bTree 树的节点
*/
@Override
public void visit(BTree bTree) {
System.out.print(bTree.getRootData() + "\t");
}
}

参考

http://blog.csdn.net/luoweifu/article/details/9077521

http://blog.csdn.net/snow_7/article/details/51815787

]]>
- - - - - java - - - - - - - java - - 二叉树 - - 数据结构 - - - -
- - - - - 修改路由表:网线接内网、无线走外网 - 以及带来的问题 - - /20171031-%E4%BF%AE%E6%94%B9%E8%B7%AF%E7%94%B1%E8%A1%A8%EF%BC%9A%E7%BD%91%E7%BA%BF%E6%8E%A5%E5%86%85%E7%BD%91%E3%80%81%E6%97%A0%E7%BA%BF%E8%B5%B0%E5%A4%96%E7%BD%91%20-%20%E4%BB%A5%E5%8F%8A%E5%B8%A6%E6%9D%A5%E7%9A%84%E9%97%AE%E9%A2%98/ - -
1
2
3
4
5
6
7
8
#删除原有的规则
route delete 0.0.0.0

#新增外网 172.16.188.254为网关(修改之前先看好)
route add 0.0.0.0 mask 0.0.0.0 172.16.188.254 metric 30 -p

#新增内网 16.0.0.0为内网网段 17.82.200.254为网关
route add 16.0.0.0 mask 255.0.0.0 17.82.200.254 metric 10 -p

带来的问题就是:
内网的数据库,在启动之后。时不时会自动断开,导致影响正常工作,时不时得重启程序才能测试

]]>
- - - - - 软件 - - - - - - - 软件 - - - -
- - - - - idea列编辑:IntelliJ IDEA:toggle block selection mode - - /20171024-IntelliJ%20IDEA%EF%BC%9Atoggle%20block%20selection%20mode/ - - 在eclipse中有列编辑模式:toggle block selection mode

在idea中也可以,而且还比较高级,哈哈

idea -> 右上角 -> Edit -> Column Selection Mode -> 移动光标到你想要弄的行

完事在重复一次,就可以退出列编辑模式

]]>
- - - - - idea - - - - - - - idea - - - -
- - - - - 常见算法:Java求最小公倍数和最大公约数三种算法 - - /20171021-%E5%B8%B8%E8%A7%81%E7%AE%97%E6%B3%95%EF%BC%9AJava%E6%B1%82%E6%9C%80%E5%B0%8F%E5%85%AC%E5%80%8D%E6%95%B0%E5%92%8C%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0%E4%B8%89%E7%A7%8D%E7%AE%97%E6%B3%95/ - - 最小公倍数:
数论中的一种概念,两个整数公有的倍数成为他们的公倍数

其中一个最小的公倍数是他们的最小公倍数

同样地,若干个整数公有的倍数中最小的正整数称为它们的最小公倍数


求最小公倍数算法:

最小公倍数=两整数的乘积÷最大公约数


求最大公约数算法:

(1) 辗转相除法

有两整数a和b:

  1. a%b得余数c
  2. 若c=0,则b即为两数的最大公约数
  3. 若c≠0,则a=b,b=c,再回去执行1

例如:求27和15的最大公约数过程为

  1. 27÷15余12
  2. 5÷12余3
  3. 12÷3余0

因此,3即为最大公约数


代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 最大公约数
*/
public int getGCD(int m, int n) {
if (n == 0) {
return m;
}
return getGCD(n, m % n);
}

/**
* 最小公倍数
* @param m
* @param n
* @return
*/
public int getLCM(int m, int n) {
int mn = m * n;
return mn / getGCD(m, n);
}

/**
* 辗转相除求最大公约数
* 有两整数a和b:
* ① a%b得余数c
* ② 若c=0,则b即为两数的最大公约数
* ③ 若c≠0,则a=b,b=c,再回去执行①
*/
public int divisionGCD(int m, int n) {
int a;
while (n != 0) {
a = m % n;
m = n;
n = a;
}
return m;
}
/**
* 相减法求最大公约数
* 有两整数a和b:
* ① 若a>b,则a=a-b
* ② 若a<b,则b=b-a
* ③ 若a=b,则a(或b)即为两数的最大公约数
* ④ 若a≠b,则再回去执行①
*/
public int subtractionGCD(int m,int n){
while(m != n){
if (m>n){
m = m-n;
}else {
n = n - m;
}
}
return m;
}

]]>
- - - - - 算法 - - - - - - - java - - 算法 - - - -
- - - - - Docker 入门实践 - - /20170929-Docker%20%E5%85%A5%E9%97%A8%E5%AE%9E%E8%B7%B5/ - - 早些天不忙的时候看的入门,从有道云笔记搬过来的

简介

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

Docker的应用场景

  1. Web 应用的自动化打包和发布。
  2. 自动化测试和持续集成、发布。
  3. 在服务型环境中部署和调整数据库或其他的后台应用。
  4. 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。

    Docker的优点

  5. 简化程序:Docker 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,便可以实现虚拟化。Docker改变了虚拟化的方式,使开发者可以直接将自己的成果放入Docker中进行管理。方便快捷已经是 Docker的最大优势,过去需要用数天乃至数周的 任务,在Docker容器的处理下,只需要数秒就能完成。
  6. 避免选择恐惧症:如果你有选择恐惧症,还是资深患者。Docker 帮你 打包你的纠结!比如 Docker 镜像;Docker 镜像中包含了运行环境和配置,所以 Docker 可以简化部署多种应用实例工作。比如 Web 应用、后台应用、数据库应用、大数据应用比如 Hadoop 集群、消息队列等等都可以打包成一个镜像部署。
  7. 节省开支:一方面,云计算时代到来,使开发者不必为了追求效果而配置高额的硬件,Docker 改变了高性能必然高价格的思维定势。Docker 与云的结合,让云空间得到更充分的利用。不仅解决了硬件管理的问题,也改变了虚拟化的方式。

    Docker 架构

中文英文解释
镜像Docker ImagesDocker 镜像是用于创建 Docker 容器的模板。
容器Docker Container容器是独立运行的一个或一组应用。
客户端Docker ClientDocker 客户端通过命令行或者其他工具使用 Docker API (查看API) 与 Docker 的守护进程通信。
主机Docker Host一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
仓库Docker RegistryDocker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(查看镜像) 提供了庞大的镜像集合供使用。
工具Docker MachineDocker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。

Ubuntu Docker 安装

使用脚本安装

下载最新的安装包

1
hisen@hisen-pc:~$ wget -qO- https://get.docker.com/ | sh

hello-world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#启动
sudo service docker start

#运行hello-world
docker run hello-world
##报错
docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.31/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
##解决:su 以root方式运行

#docker run hello-world
##提示:没有image,然后自动下载
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b04784fba78d: Pull complete
Digest: sha256:f3b3b28a45160805bb16542c9531888519430e9e6d6ffc09d72261b0d26ff74f
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

#已经启动成功

Docker 使用

1
2
3
4
5
6
7
8
#启动某个指定的镜像,运行hello-world
root@hisen-pc:/home/hisen# docker run ubuntu:15.10 /bin/echo "Hello world"
Unable to find image 'ubuntu:15.10' locally
15.10: Pulling from library/ubuntu
7dcf5a444392: Downloading 6.29MB/51.07MB
759aa75f3cee: Download complete
3fa871dc8a2b: Download complete
224c42ae46e7: Download complete

各个参数解析

docker run ubuntu:15.10 /bin/echo “Hello world”

参数解释
dockerDocker 的二进制执行文件
run与前面的 docker 组合来运行一个容器
ubuntu:15.10指定要运行的镜像,Docker首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。
/bin/echo “Hello world”在启动的容器里执行的命令

以上命令完整的意思可以解释为:Docker 以 ubuntu15.10 镜像创建一个新容器,然后在容器里执行 bin/echo “Hello world”,然后输出结果。

运行交互式的容器

1
2
3
4
5
6
root@hisen-pc:/home/hisen# docker run -i -t ubuntu:15.10 /bin/bash
##这时,我们已经进入了ubuntu:15.10的系统
##这时就是进入了命令行,可以执行相关命令
root@6265d70f44d2:/#
##
运行exit命令或者使用CTRL+D来退出容器

-t:在新容器内指定一个伪终端或终端。
-i:允许你对容器内的标准输入 (STDIN) 进行交互。

启动容器(后台模式)

1
2
root@hisen-pc:/home/hisen# docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
8c2a843273608eb155279ce4e4f9f4c5442c2af3524ba50ca2d6c8ccbd207081

后台启动完成,会返回一串容器的ID

1
2
3
4
##查看是否有容器启动
root@hisen-pc:/home/hisen# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8c2a84327360 ubuntu:15.10 "/bin/sh -c 'while..." About a minute ago Up About a minute

CONTAINER ID:容器ID(这里是:==8c2a84327360==)

NAMES:自动分配的容器名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## 查看上面那个ID的容器的log
root@hisen-pc:/home/hisen# docker logs 8c2a84327360
## 输出内容
hello world
hello world

## 停止容器(指定ID:8c2a84327360)
root@hisen-pc:/home/hisen# docker stop 8c2a84327360
## 输出ID,说明已经停止成功
8c2a84327360

## 检查是否真的关闭
root@hisen-pc:/home/hisen# docker ps
## 输出没有,说明真的关闭了
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Docker 客户端

docker 客户端非常简单 ,我们可以直接输入 docker 命令来查看到 Docker 客户端的所有命令选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
## 调出帮助
root@hisen-pc:/home/hisen# docker
## 输出帮助信息
Usage:docker COMMAND

A self-sufficient runtime for containers

Options:
--config string Location of client config files (default "/root/.docker")
-D, --debug Enable debug mode
--help Print usage
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level

## 了解更详细的帮助信息
root@hisen-pc:/home/hisen#docker stats --help

## 输出详细帮助信息
Usage:docker stats [OPTIONS] [CONTAINER...]

Display a live stream of container(s) resource usage statistics

Options:
-a, --all Show all containers (default shows just running)
--format string Pretty-print images using a Go template
--help Print usage
--no-stream Disable streaming stats and only pull the first result

运行一个WEB容器

运行一个 Python Flask 应用 来搭建一个web应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
## -d:让容器在后台运行。
## -P:将容器内部使用的网络端口映射到我们使用的主机上。
root@hisen-pc:/home/hisen# docker run -d -P training/webapp python app.py
## 提示本地没有,远程下载
Unable to find image 'training/webapp:latest' locally
latest: Pulling from training/webapp
e190868d63f8: Pull complete
909cd34c6fd7: Pull complete
0b9bfabab7c1: Pull complete
a3ed95caeb02: Pull complete
10bbbc0fc0ff: Pull complete
fca59b508e9f: Pull complete
e7ae2541b15b: Pull complete
9dd97ef58ce9: Pull complete
a4c1b0cb7af7: Pull complete
Digest: sha256:06e9c1983bd6d5db5fba376ccd63bfa529e8d02f23d5079b8f74a616308fb11d
Status: Downloaded newer image for training/webapp:latest

## 下载完成直接后台运行了,打印id
b03903d6abf3d65a4e6469a57d11ae539aaad1f166078591e7315ca2fb2611ff

## 查看运行的docker
root@hisen-pc:/home/hisen# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b03903d6abf3 training/webapp "python app.py" 9 minutes ago Up 9 minutes 0.0.0.0:32768->5000/tcp nifty_mirzakhani

0.0.0.0:32768->5000/tcp
docker使用的端口5000

映射到本地端口32768

本地可以访问:127.0.0.1:32768

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 访问:http://localhost:32768/
## 输出
Hello world!

## 可以指定端口 8060为自映射端口,5000为容器端口
root@hisen-pc:/home/hisen# docker run -d -p 8060:5000 training/webapp python app.py
0d68d3d4baf61248129ede721cc6004649ebddaaace8c23f16b677fbe2b2ce7d
root@hisen-pc:/home/hisen# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0d68d3d4baf6 training/webapp "python app.py" 5 seconds ago Up 4 seconds 0.0.0.0:8060->5000/tcp ecstatic_jang

## 查看端口映射情况(可以通过id,或者名字)
## 这里用ID测试
root@hisen-pc:/home/hisen# docker port 0d68d3d4baf6
## 输出信息,后面为映射到本地的端口
5000/tcp -> 0.0.0.0:8060

查看WEB应用程序日志

1
2
3
4
5
6
7
## 带上 -f 参数,动态输出日志
root@hisen-pc:/home/hisen# docker logs -f 0d68d3d4baf6
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

## 刷新页面之后的日志
172.17.0.1 - - [01/Sep/2017 14:09:01] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [01/Sep/2017 14:09:01] "GET /favicon.ico HTTP/1.1" 404 -

查看WEB应用程序容器的进程

1
2
3
4
5
## top后面是跟着id或者name
root@hisen-pc:/home/hisen# docker top 0d68d3d4baf6
## 输出的信息
UID PID PPID C STIME TTY TIME CMD
root 19372 19352 0 21:39 ? 00:00:00 python app.py

检查WEB应用程序

1
2
3
## 会输出一大串JSON形式的字符串
## 记录着 Docker 容器的配置和状态信息
root@hisen-pc:/home/hisen# docker inspect 0d68d3d4baf6

WEB程序其他操作

1
2
3
docker stop  0d68d3d4baf6
## 移除之前必须停止,否则会报错
docker rm 0d68d3d4baf6

Docker 镜像使用

列出所有本地的镜像

1
2
3
4
5
6
root@hisen-pc:/home/hisen# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 1815c82652c0 2 months ago 1.84kB
woailuoli993/jblse 0.2.0 82099e8d7049 6 months ago 7.58MB
ubuntu 15.10 9b9cb95443b5 13 months ago 137MB
training/webapp latest 6fae60ef3446 2 years ago 349MB

其他命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
## 获取一个镜像
## ubuntu:镜像库中的TAG
## 13.10:某个TAG的版本
docker pull ubuntu:13.10


## 搜索某个镜像
docker search httpd

## 拖取镜像
docker pull httpd

## 运行镜像
docker run httpd

## 创建镜像
### 进入某个镜像的bash命令行(在此镜像基础上创建自己的镜像)
root@hisen-pc:/home/hisen# docker run -t -i ubuntu:15.10
### 更新系统(镜像的)
root@4a3f5f4373d8:/# apt-get update

##提交容器副本
### 退出镜像的bash命令界面
root@4a3f5f4373d8:/# exit
exit
### 提交副本
root@hisen-pc:/home/hisen# docker commit -m="has update" -a="hisen" 4a3f5f4373d8 hisen/ubuntu:v2
### 返回副本容器的id
sha256:a94d0bdd2e31aa420c14e9886ff911354c07a7772715bd8d380bd0832f396c82
### 查看,发现刚刚提交的副本显示出来了
root@hisen-pc:/home/hisen# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hisen/ubuntu v2 a94d0bdd2e31 About a minute ago 159MB
hello-world latest 1815c82652c0 2 months ago 1.84kB
woailuoli993/jblse 0.2.0 82099e8d7049 6 months ago 7.58MB
ubuntu 15.10 9b9cb95443b5 13 months ago 137MB
training/webapp latest 6fae60ef3446 2 years ago 349MB

## 参数说明
-m:提交的描述信息
-a:指定镜像作者
4a3f5f4373d8:容器ID(就是你修改之前的id)
hisen/ubuntu:v2:指定要创建的目标镜像名

## 进入新镜像的bash命令
docker run -t -i hisen/ubuntu:v2 /bin/bash

构建一个全新的镜像

新建一个配置文件:Dockerfile,并添加内容

每个指令都会在镜像上创建一个新的层

每一个指令的前缀都必须是大写的。

第一条FROM,指定使用哪个镜像源

RUN 指令告诉docker 在镜像内执行命令,安装了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@hisen-pc:/home/hisen# vi Dockerfile 

FROM ubuntu:16.04
MAINTAINER Fisher "hisenyuan@gmail.com"

RUN /bin/echo 'root:123456' |chpasswd
RUN useradd hisen
RUN /bin/echo 'hisen:123456' |chpasswd
RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
EXPOSE 22
EXPOSE 80
CMD /usr/sbin/sshd -D

## 保存配置文件之后,构建镜像,注意最后那个点.
root@hisen-pc:/home/hisen# docker build -t hisen/ubuntu:16.04 .
sending build context to Docker daemon 776MB

]]>
- - - - - docker - - - - - - - docker - - - -
- - - - - Oracle 取微秒 - 记录点滴 - - /20170923-Oracle-%E5%8F%96%E5%BE%AE%E7%A7%92-%E8%AE%B0%E5%BD%95%E7%82%B9%E6%BB%B4/ - - 昨天在群里看到有人问怎么通过sql在oracle中取微秒

以前没有遇到过,就搜索了一下,找了一会给找到了

1
select to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ss ff') from dual;

输出:年-月-日 时:分:秒 微秒

1
2017-9-24 10:38:27 129368

很少问题是搜索引擎找不到的,学会如何描述问题才是关键

]]>
- - - - - java - - - - - - - sql - - oracle - - - -
- - - - - 布隆过滤器 丨 简介 - Java demo - - /20170907-%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%20%E4%B8%A8%20%E7%AE%80%E4%BB%8B%20-%20Java%20demo/ - - 概述

布隆过滤器的作用是加快判定一个元素是否在集合中出现的方法。

因为其主要是过滤掉了大部分元素间的精确匹配,故称为过滤器。


其应用场景为需要频繁在一个海量的集合中查找某个元素是否存在。

并且通常,这个值不在集合中。

比如Google chrome用此方法检查一个url是否在恶意url库中。

简单的例子

假设有一些字符串,假设有一个字符串a,要在集合B中查找其是否在集合B中。最笨的方法是遍历集合B中的每个元素bi,精确匹配a是否等于bi。若集合B中有N个元素,则最坏情况下需要执行N次精确匹配。

一个改进的方法是将a和B中每个字符串按照特定规则映射为数字,称为hash值。规则可以任意设置。比如取各字符串的首字母和尾字母的编码之乘积,取奇数个字符的编码执行异或,等。将比较字符串问题变成一个比较数字的问题。比较字符串需要从头到尾比较,而数字的比较会快很多。

需要注意的是,当两个字符串相同时,采用相同的映射规则得到的数字一定相同。但当两个字符串不同时,得到的字符串不一定不同。所以,当我们发现两个字符串的hash值相同时,两个字符串不一定相同,所以需要进一步去精确匹配两个字符串是否相同。但采用hash值方法已经能够过滤掉一部分以前需要精确匹配的计算量。仅当hash值相同(假设hash值通过字符串首尾字母计算得来,则当两个字符串首尾字母相同时hash值相同)时才去比较字符串本身。若选择hash值合理,则性能将大幅提高。

布隆过滤器通过将一个字符串使用多个不同的hash值计算方法,映射为多个不同的hash值,当所有这些hash值完全相同时,才认为两个字符串相同。从而进一步降低了放生hash值相同的可能性,从而进一步提高了过滤的性能。

Java代码实现

算法使用了md5值来生成n个不同的hash值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.hisen.interview;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

/**
* 布隆过滤器 - 测试某个元素不存在集合中
* Created by hisenyuan on 2017/9/7 at 8:50.
*/
public class BloomFilter {
public static final int NUM_SLOTS = 1024 * 1024 * 8;
public static final int NUM_HASH = 8;
private BigInteger bitmap = new BigInteger("0");

public static void main(String[] args) {
BloomFilter bf = new BloomFilter();
ArrayList<String> contents = new ArrayList<>();
contents.add("sldkjelsjf");
contents.add("ggl;ker;gekr");
contents.add("wieoneomfwe");
contents.add("sldkjelsvrnlkjf");
contents.add("ksldkflefwefwefe");
for (int i = 0; i < contents.size(); i++) {
bf.adElement(contents.get(i));
}
System.out.println(bf.check("sldkjelsvrnlkjf"));
System.out.println(bf.check("sldkjelsvrnkjf"));
}
private void adElement(String message) {
for (int i = 0; i < NUM_HASH; i++) {
int hashCode = getHash(message, i);
if (!bitmap.testBit(hashCode)) {
bitmap = bitmap.or(new BigInteger("1").shiftLeft(hashCode));
}
}
}
private boolean check(String message) {
for (int i = 0; i < NUM_HASH; i++) {
int hashCode = getHash(message,i);
if (this.bitmap.testBit(hashCode)){
return false;
}
}
return true;
}

private int getHash(String message, int i) {
try {
MessageDigest md5 = MessageDigest.getInstance("md5");
message = message + String.valueOf(i);
byte[] bytes = message.getBytes();
md5.update(bytes);
BigInteger bi = new BigInteger(md5.digest());
return Math.abs(bi.intValue()) % NUM_SLOTS;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return -1;
}
}

]]>
- - - - - java - - - - - - - 算法 - - - -
- - - - - Debian丨Ubuntu丨shadowsocks-libev一键安装脚本 - - /20170904-Debian%E4%B8%A8Ubuntu%E4%B8%A8shadowsocks-libev%E4%B8%80%E9%94%AE%E5%AE%89%E8%A3%85%E8%84%9A%E6%9C%AC/ - - 执行命令:(装完就开机启动)

1
2
3
wget --no-check-certificate -O shadowsocks-libev-debian.sh https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks-libev-debian.sh
chmod +x shadowsocks-libev-debian.sh
./shadowsocks-libev-debian.sh 2>&1 | tee shadowsocks-libev-debian.log

默认设置:

1
2
3
服务器端口:自己设定(如不设定,默认为 8989)
密码:自己设定(如不设定,默认为 teddysun.com)
加密方式:自己设定(如不设定,默认为 aes-256-gcm)

管理命令:

1
2
3
4
启动:/etc/init.d/shadowsocks start
停止:/etc/init.d/shadowsocks stop
重启:/etc/init.d/shadowsocks restart
查看状态:/etc/init.d/shadowsocks status

修改配置文件(端口、密码)

1
2
3
4
5
6
7
8
9
10
vi /etc/shadowsocks-libe/config.json
{
"server":"0.0.0.0",
"server_port":9090,
"local_address":"127.0.0.1",
"local_port":1080,
"password":"password",
"timeout":600,
"method":"aes-256-gcm"
}

卸载命令

1
./shadowsocks-libev-debian.sh uninstall

]]>
- - - - - 工具 - - - - - - - network - - - -
- - - - - IntelliJ IDEA 调试窗口、帮助窗口、搜索框、底部消息等中文乱码问题解决 - - /20170825-IntelliJ%20IDEA%20%E8%B0%83%E8%AF%95%E7%AA%97%E5%8F%A3%E3%80%81%E5%B8%AE%E5%8A%A9%E7%AA%97%E5%8F%A3%E3%80%81%E6%90%9C%E7%B4%A2%E6%A1%86%E3%80%81%E5%BA%95%E9%83%A8%E6%B6%88%E6%81%AF%E7%AD%89%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ - - IntelliJ IDEA 调试窗口、帮助窗口、搜索框、底部消息等中文乱码

在使用的过程中发现,搜索框历史、提交svn后的消息提示乱码

最后发现是由于更改了idea界面的字体,字体对中文支持不佳导致

解决办法:更改为支持中文的字体(比如:微软雅黑 Microsoft YaHei)

设置路径:Settings -> Appearance & Behavior -> UI Options -> override default fonts by

选择何时的字体即可,也可以把勾勾去掉,使用默认的。

]]>
- - - - - idea - - - - - - - 乱码 - - - -
- - - - - 多线程基础 & 进阶 丨 系统架构 - CSDN博主:说好不能打脸 - - /20170818-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80%20&%20%E8%BF%9B%E9%98%B6%20-%20CSDN%E5%8D%9A%E4%B8%BB%EF%BC%9A%E8%AF%B4%E5%A5%BD%E4%B8%8D%E8%83%BD%E6%89%93%E8%84%B8/ - - 发现一个不错的技术博客,分享一下

有时间多学习
多线程基础 & 进阶
系统架构

]]>
- - - - - java - - - - - - - java - - 多线程 - - - -
- - - - - JWT 丨 JSON Web Tokens 丨 java-jwt - - /20170817-JWT%20%E4%B8%A8%20JSON%20Web%20Tokens%20%E4%B8%A8%20java-jwt/ - - JWT简介

JWT(json web token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。

JWT生成的token

欲加密的字符:hisen
加密后的字符:(分三段)

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjoiXCJoaXNlblwiIiwiZXhwIjoxNTAyOTY0Mjk2fQ.gzg4JEm8Z-GoU9eNaNll9I1wQQ0cEAbZC9OBUjAAQqI

JWT的构成

完整的头部,json格式:

  1. 声明类型,这里是jwt
  2. 声明加密的算法 通常直接使用 HMAC SHA256
    1
    2
    3
    4
    {
    'typ': 'JWT',
    'alg': 'HS256'
    }

然后对头部进行base64加密,构成第一段:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

playload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

  1. 标准中注册的声明
  2. 公共的声明
  3. 私有的声明
    标准中注册的声明 (建议但不强制使用)
  4. iss: jwt签发者
  5. sub: jwt所面向的用户
  6. aud: 接收jwt的一方
  7. exp: jwt的过期时间,这个过期时间必须要大于签发时间
  8. nbf: 定义在什么时间之前,该jwt都是不可用的.
  9. iat: jwt的签发时间
  10. jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
    公共的声明
    公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
    私有的声明
    私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
    定义一个payload
    1
    2
    3
    {
    "payload": "hisen"
    }

然后对头部进行base64加密,构成第二段:

1
eyJwYXlsb2FkIjoiXCJoaXNlblwiIiwiZXhwIjoxNTAyOTY0Mjk2fQ

signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成

  1. header (base64后的)
  2. payload (base64后的)
  3. secret
    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三段:
    1
    gzg4JEm8Z-GoU9eNaNll9I1wQQ0cEAbZC9OBUjAAQqI

密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,所以需要保护好。

java实现方式

maven依赖

1
2
3
4
5
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency>

生成token代码(加密)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 加密,传入一个对象和有效期
public static <T> String sign(T object, long maxAge)
throws UnsupportedEncodingException {
Map<String, Object> map = new HashMap<String, Object>();
String jsonString = JSON.toJSONString(object);
map.put("alg", "HS256");
map.put("typ", "JWT");
long exp = System.currentTimeMillis() + maxAge;
String token = JWT.create()
.withHeader(map)//header
.withClaim(PAYLOAD, jsonString)//存放的内容 json
.withClaim(EXP, new DateTime(exp).toDate())//超时时间
.sign(Algorithm.HMAC256(SECRET));//密钥
return token;
}

解析token(解密)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 解密,传入一个加密后的token字符串和解密后的类型
public static <T> T unsign(String token, Class<T> classT) throws UnsupportedEncodingException {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT jwt = verifier.verify(token);
Map<String, Claim> claims = jwt.getClaims();
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
long tokenTime = claims.get(EXP).asDate().getTime();
long now = new Date().getTime();
// 判断令牌是否已经超时
if (tokenTime > now) {
String json = claims.get(PAYLOAD).asString();
// 把json转回对象,返回
return JSON.parseObject(json, classT);
}
}
return null;
}

完整代码 & 测试代码

基于JWT token认证 | JSON Web Tokens | java-jwt 3.2.0 | demo

JWT总结

  1. 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  2. payload部分,JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  3. 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。它不需要在服务端保存会话信息, 所以它易于应用的扩展
  4. 点击登陆,如果帐号密码校验通过,给用户生成一个token返回,然后用户每次登陆都带上这个token即可(放在header?)

JWT官方网站:https://jwt.io/

]]>
- - - - - java - - - - - - - java - - jwt - - - -
- - - - - MySQL 常用操作 - 命令行 - - /20170815-MySQL%20%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C%20-%20%E5%91%BD%E4%BB%A4%E8%A1%8C/ - - 命令行常用操作,备忘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#开启远程登录
grant all privileges on *.* to 'user'@'%' identified by 'passwd' with grant option;

#创建数据库
create database DB;

#创建用户
insert into mysql.user(Host,User,Password) values("localhost","user",password("passwd"));

#删除用户
DELETE FROM user WHERE user="username" and HOST="localhost";

#修改指定用户密码
update mysql.user set password=password('new passwd') where user="username" and host="localhost";

#用户授权
grant all privileges on DB.* to 'user'@'localhost' identified by 'passwd';
grant select,update on DB.* to 'user'@'localhost' identified by 'passwd';

#刷新权限
flush privileges;

#数据库导出
mysqldump -uUSRENAME -pPASSWD DATABASE > DATABASE.sql

#数据库导出(只导出表结构 -d)
mysqldump -uUSRENAME -pPASSWD -d DATABASE > DATABASE.sql

#数据库导入

#1.切换数据库
use DATABASE;
#2.设置编码
set names utf8;
#3.执行导入操作
source /home/abc/abc.sql;

#直接导入
mysql -uUSERNAME -p DATABASE < DATABASE.sql

]]>
- - - - - sql - - - - - - - mysql - - - -
- - - - - apt-get 常用命令 - - /20170815-apt-get%20%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/ - - apt-get应该debian内核系列的系统都能用:

比如:debian、ubuntu、deepin等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
apt-cache search package    #搜索包(相当于yum list | grep pkg)
apt-cache show package #显示包的相关信息,如说明、大小、版本等
apt-cache showpg package #显示包的相关信息,如Reverse Depends(反向依赖)、依赖等
apt-get install package #安装包
apt-get reinstall package #重新安装包
apt-get -f install package #强制安装
apt-get remove package #删除包(只是删掉数据和可执行文件,不删除配置文件)
apt-get remove --purge package #删除包,包括删除配置文件等
apt-get autoremove --purge package #删除包及其依赖的软件包+配置文件等
apt-get update #更新源
apt-get upgrade #更新已安装的包
apt-get dist-upgrade #升级系统
apt-get dselect-upgrade #使用 dselect 升级
apt-cache depends package #了解使用依赖
apt-cache rdepends package #查看该包被哪些包依赖
apt-get build-dep package #安装相关的编译环境
apt-get source package #下载该包的源代码
apt-get clean && apt-get autoclean #清理下载文件的存档 && 只清理过时的包
apt-get check #检查是否有损坏的依赖
dpkg -S filename #查找filename属于哪个软件包
apt-file search filename #查找filename属于哪个软件包
apt-file list packagename #列出软件包的内容
apt-file update #更新apt-file的数据库

dpkg -l #列出当前系统中所有的包.可以和参数less一起使用在分屏查看(类似于rpm -qa)
dpkg -l |grep -i "pkg" #查看系统中与"pkg"相关联的包(类似于rpm -qa | grep pkg)
dpkg -s pkg #查询一个已安装的包的详细信息(类似于rpm -qi)
dpkg -L pkg #查询一个已安装的软件包释放了哪些文件(类似于rpm -ql)
dpkg -S file #查询系统中某个文件属于哪个软件包(类似于rpm -qf)
dpkg -c pkg.deb #查询一个未安装的deb包将会释放哪些文件(类似于rpm -qpl)
dpkg -I pkg.deb #查看一个未安装的deb包的详细信息(类似于rpm -qpi)
dpkg -i pkg.deb #手动安装软件包(不能解决软依赖性问题,可以用apt-get -f install解决)
dpkg -r pkg #卸载软件包(不是完全的卸载,它的配置文件还存在)
dpkg -P pkg #全部卸载(不能解决依赖性的问题)
dpkg-reconfigure pkg #重新配置
dpkg -x pkg.deb dir #将一个deb包解开至dir目录
dpkg --pending --remove #移除多余的软件

# 强制安装一个包(忽略依赖及其它问题)
dpkg --force-all -i pkg.deb #可以参考dpkg --force-help

# 强制卸载一个包
dpkg --force-all -P pkg.deb

aptitude update #更新可用的包列表
aptitude upgrade #升级可用的包
aptitude dist-upgrade #将系统升级到新的发行版
aptitude install pkgname #安装包
aptitude remove pkgname #删除包
aptitude purge pkgname #删除包及其配置文件
aptitude search string #搜索包(相当于yum list | grep pkg,重要)
aptitude show pkgname #显示包的详细信息 (相当于yum info pkg,重要)
aptitude clean #删除下载的包文件
aptitude autoclean #仅删除过期的包文件
apt-get install xrdp #安装图形化

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - 最近在看的Java系列文章 - 计算机程序的思维逻辑 - - /20170814-%E6%9C%80%E8%BF%91%E5%9C%A8%E7%9C%8B%E7%9A%84Java%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0%20-%20%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%80%9D%E7%BB%B4%E9%80%BB%E8%BE%91/ - - 计算机程序的思维逻辑

这个系列的文章还不错

目录—微信公众号

目录—掘金(1-6缺失)

相关笔记或者代码

相关笔记或者代码 - github地址

说明

版权归原作者所有

感觉文章不错,一边看一边实践。

有时候从头开始学习也会有另一番风味。

兴趣是最好的老师,唯有坚持才是实现梦想的唯一途径。

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 面试题收集 - Java - 2017 - - /20170808-%E9%9D%A2%E8%AF%95%E9%A2%98%E6%94%B6%E9%9B%86%20-%20Java%20-%202017/ - - 我只是收集一下题目,检查一下自己。

京东平台产品研发部 - Java

背景:一年工作经验,做电子政务

  1. 一个数组 i[] = [-1000 ~ 1000] 中的任意一些数字,求乘积最大的三个数。
  2. static都能用在哪里?
  3. 1M 等于多少个1
  4. 面向对象的特性
  5. 写出两种单例模式
  6. HashMap有哪些方法
  7. HashMap的底层实现
  8. List和Set的区别
  9. 重载和重写的区别,多态如何体现
  10. 判断字符串是否含有此Url:www.jd.com
    11.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Long l1 = new Long(1024L);
    Long l2 = new Long(1024L);
    System.out.println(l1 == l2);//false
    Integer i1 = 100;
    Integer i2 = 100;
    System.out.println(i1 == i2);//true
    i1 = 100;
    i2 = 100;
    System.out.println(i1 == i2);//true
  11. Spring自定义注解、拦截器相关

  12. 为什么使用注解、有什么好处
  13. Spring中运用了哪些设计模式
  14. Sping和SpringBoot各有什么优缺点
  15. Spring的事物隔离机制
  16. 说说AOP、DI、IOC
  17. redis等用过没
  18. 缓存和memorycache
  19. http://www.jd.com/...username=... 为了防止sql注入,怎么写正则。
  20. mybatis如何防止sql注入
  21. mybatis使用变量时怎么表示
  22. 使用js遍历json数据
  23. 使用Map遍历json数据
  24. elasticsearch相关
  25. 你曾经在社区接触过的技术有哪些
  26. 项目相关:做了哪些部分、职责、用到哪些技术、设计了哪些
  27. 介绍一下RPC调用

谷歌淘来的一个总结

面试题总结 —— JAVA高级工程师

部分答案

  1. 一个数组 i[] = [-1000 ~ 1000] 中的任意一些数字,求乘积最大的三个数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    *
    * 功能:求解一维无序数组中三个数字乘积最大值(正负零均存在)
    */
    @Test
    public void getMaxThreeNum() {
    // 首先对数组进行升序排序
    // 1. 数组左端是0,那么不存在负数,最大值为:i[-1]*i[-2]*i[-3]
    // 2. 数组左端是正数,同上
    // 3. 数组左端是负数,那么可能会负负得正,由于是升序,最大值为:i[0]*i[1]*i[-1]
    // 4. 去上面两个的最大值即可
    int[] i = {0,-10, -9, -8, 7, 6, 5, 4, 3, 2, 1};
    Arrays.sort(i);
    int length = i.length;
    int a = i[0] * i[1] * i[length - 1];
    int b = i[length - 1] * i[length - 2] * i[length - 3];
    if (a > b) {//-10 * -9 * 7 = 630
    System.out.println(i[0] + " * " + i[1] + " * " + i[length - 1] + " = " + a);
    } else {
    System.out.println(i[length - 1] + " * " + i[length - 2] + " * " + i[length - 3] + " = " + b);
    }
    }
  2. 1M 等于多少个1

    1
    2
    3
    4
    5
    @Test
    public void get1McanHoldInt(){
    // int 占用4个字节 4Byte = 4 * 8bit
    System.out.println(1*1024*1024/4);
    }
  3. 写出两种单例模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //懒汉模式
    public class LazySingleton {
    private static LazySingleton instance = null;
    private LazySingleton(){
    super();
    }
    public static LazySingleton getInstance(){
    if(instance == null){
    // 静态方法,使用当前类本身充当进程锁
    synchronized(LazySingleton.class){
    instance = new LazySingleton();
    }
    }
    return instance;
    }
    }

    // 饿汉式单例模式
    public class EagerSingleton {
    private static EagerSingleton instance = new EagerSingleton();
    private EagerSingleton(){
    super();
    }
    // 不需要同步(类加载时已经初始化,不存在多线程的问题)
    // 始终只有一个对象
    public EagerSingleton getInstance(){
    return instance;
    }
    }
  4. 判断字符串是否含有此Url:www.jd.com

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Test
    public void isHaveUrl(){
    String url = "这是京东官网:www.jd.com";
    Pattern pattern = Pattern.compile(".*www.jd.com.*");
    Matcher matcher = pattern.matcher(url);
    if (matcher.find()){
    System.out.println(matcher.group());
    }else {
    System.out.println("未匹配");
    }
    }
  5. http://www.jd.com/...username=... 为了防止sql注入,怎么写正则。

    1
    2
    将包含有 单引号('),分号(;) 和 注释符号(--)的语句给替换掉来防止SQL注入
    str.replaceAll("([';])+|(--)+","");
  6. mybatis如何防止sql注入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    防护一:sql语句不在使用${}表达式,改为#{}
    <select id="getBlogById" resultType="Blog" parameterType=”int”>
    SELECT id,title,author,content
    FROM blog
    WHERE id=#{id}
    </select>

    编译后的SQL:SELECT id,title,author,content FROM blog WHERE id = ? (能避免sql注入)

    <select id="getBlogById" resultType="Blog" parameterType=”int”>
    SELECT id,title,author,content
    FROM blog
    WHERE id=${id} <!--假如id= 3 -->
    </select>

    编译后的SQL:SELECT id,title,author,content FROM blog WHERE id = 3 (无法避免sql注入)

    SQL注入只能对编译过程起作用
    #{}:相当于JDBC中的PreparedStatement
    ${}:是输出变量的值

    #{}是经过预编译的,是安全的;
    ${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。


    防护二:采用阿里的druid数据连接池,添加如下配置
    <!-- 配置监控统计拦截的filters,和防sql注入 -->
    <property name="filters" value="stat,wall" />

    防护三:使用正则表达式过滤输入参数
  7. mybatis使用变量时怎么表示

    1
    2
    3
    4
    5
    6
    7
    #与$的区别
    1.#是把传入的数据当作字符串,如#field#传入的是id,则sql语句生成是这样,order by "id",这当然会报错..
    2.$传入的数据直接生成在sql里,如$field$传入的是id,则sql语句生成是这样,order by id, 这就对了.
    3.#方式能够很大程度防止sql注入.
    4.$方式无法防止sql注入.
    5.$方式一般用于传入数据库对象.例如传入表名.
    6.一般能用#的就别用$.
  8. 使用js遍历json数据

    1
    2
    3
    4
    var data=[{name:"a",age:12},{name:"b",age:11},{name:"c",age:13},{name:"d",age:14}];  
    for(var o in data){
    alert("text:"+data[o].name+" value:"+data[o].age );
    }
]]>
- - - - - java - - - - - - - java - - 面试 - - - -
- - - - - 有道云笔记支持的markdown语法 - hexo试一试 - - /20170807-%E6%9C%89%E9%81%93%E4%BA%91%E7%AC%94%E8%AE%B0%E6%94%AF%E6%8C%81%E7%9A%84markdown%E8%AF%AD%E6%B3%95%20-%20hexo%E8%AF%95%E4%B8%80%E8%AF%95/ - - markdown语法

分割线


  1. 列表1
  2. 列表2
  3. 列表3
1
2
3
4
//插入代码
public void main(String[] args){
System.out.println("Hello World!");
}

一级标题

二级标题

三级标题

四级标题

五级标题
六级标题

删除线

引用,只能写在一行

  • [ ] 未勾选
  • [x] 勾选的

梦殇国际

image

markdown源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
markdown语法
--

分割线

---

1. 列表1
2. 列表2
3. 列表3

//插入代码
public void main(String[] args){
System.out.println("Hello World!");
}

# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题

~~删除线~~

> 引用,只能写在一行

- [ ] 未勾选
- [x] 勾选的

[梦殇国际](http://www.714.hk)

![image](http://www.714.hk/logo.png)
]]>
- - - -
- - - - - Spark第一个java程序 - 字词统计 - Spark java idea - - /20170802-Spark%E7%AC%AC%E4%B8%80%E4%B8%AAjava%E7%A8%8B%E5%BA%8F%20-%20%E5%AD%97%E8%AF%8D%E7%BB%9F%E8%AE%A1%20-%20Spark%20java%20idea/ - - 在本地利用idea,java开发spark程序

原来并不用安装spark什么的这些东西

这样就不会那么繁琐,门槛也低了点

具体过程如下

一、创建工程

  1. idea -> new Project -> maven -> create from archetype -> maven-archetype-quickstart
  2. pom.xml添加依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.11</artifactId>
    <version>2.0.1</version>
    </dependency>
  3. 编写java代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.hisen.spark;

    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaRDD;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.api.java.function.Function;

    /**
    * 第一个spark程序
    * Created by hisenyuan on 2017/8/2 at 16:36.
    */
    public class SimpleApp {

    public static void main(String[] args) {
    // Should be some file on your system
    String logFile = "D:\\logs\\boss_debug.log";
    //设置本地运行,设置名称(在spark web ui上显示)
    SparkConf sparkConf = new SparkConf().setMaster("local").setAppName("Simple Application");
    JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
    JavaRDD<String> logData = javaSparkContext.textFile(logFile).cache();

    //统计包含a的次数
    long countA = logData.filter(new Function<String, Boolean>() {
    public Boolean call(String s) throws Exception {
    return s.contains("a");
    }
    }).count();

    //统计包含b的次数
    long countB = logData.filter(new Function<String, Boolean>() {
    public Boolean call(String s) throws Exception {
    return s.contains("b");
    }
    }).count();
    System.out.printf("Lines with a: %d, lines with b: %d\n", countA, countB);
    //Lines with a: 11146, lines with b: 10760
    }
    }
  4. 运行main方法:结果:Lines with a: 11146, lines with b: 10760

  5. 日志如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    C:\hisenwork\soft\jdk8\bin\java -Dspark.master=local "-javaagent:C:\hisenwork\IntelliJ IDEA 2017.1.1\lib\idea_rt.jar=59366:C:\hisenwork\IntelliJ IDEA 2017.1.1\bin" -Dfile.encoding=UTF-8 -classpath C:\hisenwork\soft\jdk8\jre\lib\charsets.jar;C:\hisenwork\soft\jdk8\jre\lib\deploy.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\access-bridge-64.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\cldrdata.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\dnsns.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\jaccess.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\jfxrt.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\localedata.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\nashorn.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunec.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunjce_provider.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunmscapi.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunpkcs11.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\zipfs.jar;C:\hisenwork\soft\jdk8\jre\lib\javaws.jar;C:\hisenwork\soft\jdk8\jre\lib\jce.jar;C:\hisenwork\soft\jdk8\jre\lib\jfr.jar;C:\hisenwork\soft\jdk8\jre\lib\jfxswt.jar;C:\hisenwork\soft\jdk8\jre\lib\jsse.jar;C:\hisenwork\soft\jdk8\jre\lib\management-agent.jar;C:\hisenwork\soft\jdk8\jre\lib\plugin.jar;C:\hisenwork\soft\jdk8\jre\lib\resources.jar;C:\hisenwork\soft\jdk8\jre\lib\rt.jar;C:\hisenwork\code\rmitec\SparkTest\target\classes;C:\hisenwork\soft\maven\org\apache\spark\spark-core_2.11\2.0.1\spark-core_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\apache\avro\avro-mapred\1.7.7\avro-mapred-1.7.7-hadoop2.jar;C:\hisenwork\soft\maven\org\apache\avro\avro-ipc\1.7.7\avro-ipc-1.7.7.jar;C:\hisenwork\soft\maven\org\apache\avro\avro\1.7.7\avro-1.7.7.jar;C:\hisenwork\soft\maven\org\apache\avro\avro-ipc\1.7.7\avro-ipc-1.7.7-tests.jar;C:\hisenwork\soft\maven\org\codehaus\jackson\jackson-core-asl\1.9.13\jackson-core-asl-1.9.13.jar;C:\hisenwork\soft\maven\org\codehaus\jackson\jackson-mapper-asl\1.9.13\jackson-mapper-asl-1.9.13.jar;C:\hisenwork\soft\maven\com\twitter\chill_2.11\0.8.0\chill_2.11-0.8.0.jar;C:\hisenwork\soft\maven\com\esotericsoftware\kryo-shaded\3.0.3\kryo-shaded-3.0.3.jar;C:\hisenwork\soft\maven\com\esotericsoftware\minlog\1.3.0\minlog-1.3.0.jar;C:\hisenwork\soft\maven\org\objenesis\objenesis\2.1\objenesis-2.1.jar;C:\hisenwork\soft\maven\com\twitter\chill-java\0.8.0\chill-java-0.8.0.jar;C:\hisenwork\soft\maven\org\apache\xbean\xbean-asm5-shaded\4.4\xbean-asm5-shaded-4.4.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-client\2.2.0\hadoop-client-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-common\2.2.0\hadoop-common-2.2.0.jar;C:\hisenwork\soft\maven\commons-cli\commons-cli\1.2\commons-cli-1.2.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-math\2.1\commons-math-2.1.jar;C:\hisenwork\soft\maven\xmlenc\xmlenc\0.52\xmlenc-0.52.jar;C:\hisenwork\soft\maven\commons-io\commons-io\2.1\commons-io-2.1.jar;C:\hisenwork\soft\maven\commons-lang\commons-lang\2.5\commons-lang-2.5.jar;C:\hisenwork\soft\maven\commons-configuration\commons-configuration\1.6\commons-configuration-1.6.jar;C:\hisenwork\soft\maven\commons-collections\commons-collections\3.2.1\commons-collections-3.2.1.jar;C:\hisenwork\soft\maven\commons-digester\commons-digester\1.8\commons-digester-1.8.jar;C:\hisenwork\soft\maven\commons-beanutils\commons-beanutils\1.7.0\commons-beanutils-1.7.0.jar;C:\hisenwork\soft\maven\commons-beanutils\commons-beanutils-core\1.8.0\commons-beanutils-core-1.8.0.jar;C:\hisenwork\soft\maven\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-auth\2.2.0\hadoop-auth-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-compress\1.4.1\commons-compress-1.4.1.jar;C:\hisenwork\soft\maven\org\tukaani\xz\1.0\xz-1.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-hdfs\2.2.0\hadoop-hdfs-2.2.0.jar;C:\hisenwork\soft\maven\org\mortbay\jetty\jetty-util\6.1.26\jetty-util-6.1.26.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-app\2.2.0\hadoop-mapreduce-client-app-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-common\2.2.0\hadoop-mapreduce-client-common-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-client\2.2.0\hadoop-yarn-client-2.2.0.jar;C:\hisenwork\soft\maven\com\google\inject\guice\3.0\guice-3.0.jar;C:\hisenwork\soft\maven\javax\inject\javax.inject\1\javax.inject-1.jar;C:\hisenwork\soft\maven\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-server-common\2.2.0\hadoop-yarn-server-common-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-shuffle\2.2.0\hadoop-mapreduce-client-shuffle-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-api\2.2.0\hadoop-yarn-api-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-core\2.2.0\hadoop-mapreduce-client-core-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-common\2.2.0\hadoop-yarn-common-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-jobclient\2.2.0\hadoop-mapreduce-client-jobclient-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-annotations\2.2.0\hadoop-annotations-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-launcher_2.11\2.0.1\spark-launcher_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-network-common_2.11\2.0.1\spark-network-common_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\fusesource\leveldbjni\leveldbjni-all\1.8\leveldbjni-all-1.8.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\core\jackson-annotations\2.6.5\jackson-annotations-2.6.5.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-network-shuffle_2.11\2.0.1\spark-network-shuffle_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-unsafe_2.11\2.0.1\spark-unsafe_2.11-2.0.1.jar;C:\hisenwork\soft\maven\net\java\dev\jets3t\jets3t\0.7.1\jets3t-0.7.1.jar;C:\hisenwork\soft\maven\commons-codec\commons-codec\1.3\commons-codec-1.3.jar;C:\hisenwork\soft\maven\commons-httpclient\commons-httpclient\3.1\commons-httpclient-3.1.jar;C:\hisenwork\soft\maven\org\apache\curator\curator-recipes\2.4.0\curator-recipes-2.4.0.jar;C:\hisenwork\soft\maven\org\apache\curator\curator-framework\2.4.0\curator-framework-2.4.0.jar;C:\hisenwork\soft\maven\org\apache\curator\curator-client\2.4.0\curator-client-2.4.0.jar;C:\hisenwork\soft\maven\org\apache\zookeeper\zookeeper\3.4.5\zookeeper-3.4.5.jar;C:\hisenwork\soft\maven\com\google\guava\guava\14.0.1\guava-14.0.1.jar;C:\hisenwork\soft\maven\javax\servlet\javax.servlet-api\3.1.0\javax.servlet-api-3.1.0.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-lang3\3.3.2\commons-lang3-3.3.2.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-math3\3.4.1\commons-math3-3.4.1.jar;C:\hisenwork\soft\maven\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;C:\hisenwork\soft\maven\org\slf4j\slf4j-api\1.7.16\slf4j-api-1.7.16.jar;C:\hisenwork\soft\maven\org\slf4j\jul-to-slf4j\1.7.16\jul-to-slf4j-1.7.16.jar;C:\hisenwork\soft\maven\org\slf4j\jcl-over-slf4j\1.7.16\jcl-over-slf4j-1.7.16.jar;C:\hisenwork\soft\maven\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\hisenwork\soft\maven\org\slf4j\slf4j-log4j12\1.7.16\slf4j-log4j12-1.7.16.jar;C:\hisenwork\soft\maven\com\ning\compress-lzf\1.0.3\compress-lzf-1.0.3.jar;C:\hisenwork\soft\maven\org\xerial\snappy\snappy-java\1.1.2.6\snappy-java-1.1.2.6.jar;C:\hisenwork\soft\maven\net\jpountz\lz4\lz4\1.3.0\lz4-1.3.0.jar;C:\hisenwork\soft\maven\org\roaringbitmap\RoaringBitmap\0.5.11\RoaringBitmap-0.5.11.jar;C:\hisenwork\soft\maven\commons-net\commons-net\2.2\commons-net-2.2.jar;C:\hisenwork\soft\maven\org\scala-lang\scala-library\2.11.8\scala-library-2.11.8.jar;C:\hisenwork\soft\maven\org\json4s\json4s-jackson_2.11\3.2.11\json4s-jackson_2.11-3.2.11.jar;C:\hisenwork\soft\maven\org\json4s\json4s-core_2.11\3.2.11\json4s-core_2.11-3.2.11.jar;C:\hisenwork\soft\maven\org\json4s\json4s-ast_2.11\3.2.11\json4s-ast_2.11-3.2.11.jar;C:\hisenwork\soft\maven\com\thoughtworks\paranamer\paranamer\2.6\paranamer-2.6.jar;C:\hisenwork\soft\maven\org\scala-lang\scalap\2.11.0\scalap-2.11.0.jar;C:\hisenwork\soft\maven\org\scala-lang\scala-compiler\2.11.0\scala-compiler-2.11.0.jar;C:\hisenwork\soft\maven\org\scala-lang\modules\scala-parser-combinators_2.11\1.0.1\scala-parser-combinators_2.11-1.0.1.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\core\jersey-client\2.22.2\jersey-client-2.22.2.jar;C:\hisenwork\soft\maven\javax\ws\rs\javax.ws.rs-api\2.0.1\javax.ws.rs-api-2.0.1.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\hk2-api\2.4.0-b34\hk2-api-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\hk2-utils\2.4.0-b34\hk2-utils-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\external\aopalliance-repackaged\2.4.0-b34\aopalliance-repackaged-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\external\javax.inject\2.4.0-b34\javax.inject-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\hk2-locator\2.4.0-b34\hk2-locator-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\javassist\javassist\3.18.1-GA\javassist-3.18.1-GA.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\core\jersey-common\2.22.2\jersey-common-2.22.2.jar;C:\hisenwork\soft\maven\javax\annotation\javax.annotation-api\1.2\javax.annotation-api-1.2.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\bundles\repackaged\jersey-guava\2.22.2\jersey-guava-2.22.2.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\osgi-resource-locator\1.0.1\osgi-resource-locator-1.0.1.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\core\jersey-server\2.22.2\jersey-server-2.22.2.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\media\jersey-media-jaxb\2.22.2\jersey-media-jaxb-2.22.2.jar;C:\hisenwork\soft\maven\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\containers\jersey-container-servlet\2.22.2\jersey-container-servlet-2.22.2.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\containers\jersey-container-servlet-core\2.22.2\jersey-container-servlet-core-2.22.2.jar;C:\hisenwork\soft\maven\org\apache\mesos\mesos\0.21.1\mesos-0.21.1-shaded-protobuf.jar;C:\hisenwork\soft\maven\io\netty\netty-all\4.0.29.Final\netty-all-4.0.29.Final.jar;C:\hisenwork\soft\maven\io\netty\netty\3.8.0.Final\netty-3.8.0.Final.jar;C:\hisenwork\soft\maven\com\clearspring\analytics\stream\2.7.0\stream-2.7.0.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-core\3.1.2\metrics-core-3.1.2.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-jvm\3.1.2\metrics-jvm-3.1.2.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-json\3.1.2\metrics-json-3.1.2.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-graphite\3.1.2\metrics-graphite-3.1.2.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\core\jackson-databind\2.6.5\jackson-databind-2.6.5.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\core\jackson-core\2.6.5\jackson-core-2.6.5.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\module\jackson-module-scala_2.11\2.6.5\jackson-module-scala_2.11-2.6.5.jar;C:\hisenwork\soft\maven\org\scala-lang\scala-reflect\2.11.7\scala-reflect-2.11.7.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\module\jackson-module-paranamer\2.6.5\jackson-module-paranamer-2.6.5.jar;C:\hisenwork\soft\maven\org\apache\ivy\ivy\2.4.0\ivy-2.4.0.jar;C:\hisenwork\soft\maven\oro\oro\2.0.8\oro-2.0.8.jar;C:\hisenwork\soft\maven\net\razorvine\pyrolite\4.9\pyrolite-4.9.jar;C:\hisenwork\soft\maven\net\sf\py4j\py4j\0.10.3\py4j-0.10.3.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-tags_2.11\2.0.1\spark-tags_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\scalatest\scalatest_2.11\2.2.6\scalatest_2.11-2.2.6.jar;C:\hisenwork\soft\maven\org\scala-lang\modules\scala-xml_2.11\1.0.2\scala-xml_2.11-1.0.2.jar;C:\hisenwork\soft\maven\org\spark-project\spark\unused\1.0.0\unused-1.0.0.jar com.hisen.spark.SimpleApp
    Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
    17/08/02 17:00:09 INFO SparkContext: Running Spark version 2.0.1
    17/08/02 17:00:10 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
    17/08/02 17:00:10 INFO SecurityManager: Changing view acls to: Administrator
    17/08/02 17:00:10 INFO SecurityManager: Changing modify acls to: Administrator
    17/08/02 17:00:10 INFO SecurityManager: Changing view acls groups to:
    17/08/02 17:00:10 INFO SecurityManager: Changing modify acls groups to:
    17/08/02 17:00:10 INFO SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users with view permissions: Set(Administrator); groups with view permissions: Set(); users with modify permissions: Set(Administrator); groups with modify permissions: Set()
    17/08/02 17:00:11 INFO Utils: Successfully started service 'sparkDriver' on port 59388.
    17/08/02 17:00:11 INFO SparkEnv: Registering MapOutputTracker
    17/08/02 17:00:11 INFO SparkEnv: Registering BlockManagerMaster
    17/08/02 17:00:11 INFO DiskBlockManager: Created local directory at C:\Users\Administrator\AppData\Local\Temp\blockmgr-630d8294-e5cb-428d-8189-4c3313e46fa3
    17/08/02 17:00:12 INFO MemoryStore: MemoryStore started with capacity 894.3 MB
    17/08/02 17:00:12 INFO SparkEnv: Registering OutputCommitCoordinator
    17/08/02 17:00:12 INFO Utils: Successfully started service 'SparkUI' on port 4040.
    17/08/02 17:00:12 INFO SparkUI: Bound SparkUI to 0.0.0.0, and started at http://169.254.90.236:4040
    17/08/02 17:00:12 INFO Executor: Starting executor ID driver on host localhost
    17/08/02 17:00:12 INFO Utils: Successfully started service 'org.apache.spark.network.netty.NettyBlockTransferService' on port 59397.
    17/08/02 17:00:12 INFO NettyBlockTransferService: Server created on 169.254.90.236:59397
    17/08/02 17:00:12 INFO BlockManagerMaster: Registering BlockManager BlockManagerId(driver, 169.254.90.236, 59397)
    17/08/02 17:00:12 INFO BlockManagerMasterEndpoint: Registering block manager 169.254.90.236:59397 with 894.3 MB RAM, BlockManagerId(driver, 169.254.90.236, 59397)
    17/08/02 17:00:12 INFO BlockManagerMaster: Registered BlockManager BlockManagerId(driver, 169.254.90.236, 59397)
    17/08/02 17:00:14 INFO MemoryStore: Block broadcast_0 stored as values in memory (estimated size 127.1 KB, free 894.2 MB)
    17/08/02 17:00:14 INFO MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 14.3 KB, free 894.2 MB)
    17/08/02 17:00:14 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on 169.254.90.236:59397 (size: 14.3 KB, free: 894.3 MB)
    17/08/02 17:00:14 INFO SparkContext: Created broadcast 0 from textFile at SimpleApp.java:19
    17/08/02 17:00:14 ERROR Shell: Failed to locate the winutils binary in the hadoop binary path
    java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
    at org.apache.hadoop.util.Shell.getQualifiedBinPath(Shell.java:278)
    at org.apache.hadoop.util.Shell.getWinUtilsPath(Shell.java:300)
    at org.apache.hadoop.util.Shell.<clinit>(Shell.java:293)
    at org.apache.hadoop.util.StringUtils.<clinit>(StringUtils.java:76)
    at org.apache.hadoop.mapred.FileInputFormat.setInputPaths(FileInputFormat.java:362)
    at org.apache.spark.SparkContext$$anonfun$hadoopFile$1$$anonfun$29.apply(SparkContext.scala:992)
    at org.apache.spark.SparkContext$$anonfun$hadoopFile$1$$anonfun$29.apply(SparkContext.scala:992)
    at org.apache.spark.rdd.HadoopRDD$$anonfun$getJobConf$6.apply(HadoopRDD.scala:176)
    at org.apache.spark.rdd.HadoopRDD$$anonfun$getJobConf$6.apply(HadoopRDD.scala:176)
    at scala.Option.map(Option.scala:146)
    at org.apache.spark.rdd.HadoopRDD.getJobConf(HadoopRDD.scala:176)
    at org.apache.spark.rdd.HadoopRDD.getPartitions(HadoopRDD.scala:195)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:248)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:246)
    at scala.Option.getOrElse(Option.scala:121)
    at org.apache.spark.rdd.RDD.partitions(RDD.scala:246)
    at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:248)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:246)
    at scala.Option.getOrElse(Option.scala:121)
    at org.apache.spark.rdd.RDD.partitions(RDD.scala:246)
    at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:248)
    at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:246)
    at scala.Option.getOrElse(Option.scala:121)
    at org.apache.spark.rdd.RDD.partitions(RDD.scala:246)
    at org.apache.spark.SparkContext.runJob(SparkContext.scala:1930)
    at org.apache.spark.rdd.RDD.count(RDD.scala:1134)
    at org.apache.spark.api.java.JavaRDDLike$class.count(JavaRDDLike.scala:454)
    at org.apache.spark.api.java.AbstractJavaRDDLike.count(JavaRDDLike.scala:45)
    at com.hisen.spark.SimpleApp.main(SimpleApp.java:26)
    17/08/02 17:00:14 INFO FileInputFormat: Total input paths to process : 1
    17/08/02 17:00:14 INFO SparkContext: Starting job: count at SimpleApp.java:26
    17/08/02 17:00:15 INFO DAGScheduler: Got job 0 (count at SimpleApp.java:26) with 1 output partitions
    17/08/02 17:00:15 INFO DAGScheduler: Final stage: ResultStage 0 (count at SimpleApp.java:26)
    17/08/02 17:00:15 INFO DAGScheduler: Parents of final stage: List()
    17/08/02 17:00:15 INFO DAGScheduler: Missing parents: List()
    17/08/02 17:00:15 INFO DAGScheduler: Submitting ResultStage 0 (MapPartitionsRDD[2] at filter at SimpleApp.java:22), which has no missing parents
    17/08/02 17:00:15 INFO MemoryStore: Block broadcast_1 stored as values in memory (estimated size 3.2 KB, free 894.2 MB)
    17/08/02 17:00:15 INFO MemoryStore: Block broadcast_1_piece0 stored as bytes in memory (estimated size 1966.0 B, free 894.2 MB)
    17/08/02 17:00:15 INFO BlockManagerInfo: Added broadcast_1_piece0 in memory on 169.254.90.236:59397 (size: 1966.0 B, free: 894.3 MB)
    17/08/02 17:00:15 INFO SparkContext: Created broadcast 1 from broadcast at DAGScheduler.scala:1012
    17/08/02 17:00:15 INFO DAGScheduler: Submitting 1 missing tasks from ResultStage 0 (MapPartitionsRDD[2] at filter at SimpleApp.java:22)
    17/08/02 17:00:15 INFO TaskSchedulerImpl: Adding task set 0.0 with 1 tasks
    17/08/02 17:00:15 INFO TaskSetManager: Starting task 0.0 in stage 0.0 (TID 0, localhost, partition 0, PROCESS_LOCAL, 5324 bytes)
    17/08/02 17:00:15 INFO Executor: Running task 0.0 in stage 0.0 (TID 0)
    17/08/02 17:00:15 INFO HadoopRDD: Input split: file:/D:/logs/boss_debug.log:0+4059273
    17/08/02 17:00:15 INFO deprecation: mapred.tip.id is deprecated. Instead, use mapreduce.task.id
    17/08/02 17:00:15 INFO deprecation: mapred.task.id is deprecated. Instead, use mapreduce.task.attempt.id
    17/08/02 17:00:15 INFO deprecation: mapred.task.is.map is deprecated. Instead, use mapreduce.task.ismap
    17/08/02 17:00:15 INFO deprecation: mapred.task.partition is deprecated. Instead, use mapreduce.task.partition
    17/08/02 17:00:15 INFO deprecation: mapred.job.id is deprecated. Instead, use mapreduce.job.id
    17/08/02 17:00:15 INFO MemoryStore: Block rdd_1_0 stored as values in memory (estimated size 5.7 MB, free 888.4 MB)
    17/08/02 17:00:15 INFO BlockManagerInfo: Added rdd_1_0 in memory on 169.254.90.236:59397 (size: 5.7 MB, free: 888.5 MB)
    17/08/02 17:00:15 INFO Executor: Finished task 0.0 in stage 0.0 (TID 0). 1842 bytes result sent to driver
    17/08/02 17:00:16 INFO TaskSetManager: Finished task 0.0 in stage 0.0 (TID 0) in 691 ms on localhost (1/1)
    17/08/02 17:00:16 INFO TaskSchedulerImpl: Removed TaskSet 0.0, whose tasks have all completed, from pool
    17/08/02 17:00:16 INFO DAGScheduler: ResultStage 0 (count at SimpleApp.java:26) finished in 0.726 s
    17/08/02 17:00:16 INFO DAGScheduler: Job 0 finished: count at SimpleApp.java:26, took 1.078428 s
    17/08/02 17:00:16 INFO SparkContext: Starting job: count at SimpleApp.java:33
    17/08/02 17:00:16 INFO DAGScheduler: Got job 1 (count at SimpleApp.java:33) with 1 output partitions
    17/08/02 17:00:16 INFO DAGScheduler: Final stage: ResultStage 1 (count at SimpleApp.java:33)
    17/08/02 17:00:16 INFO DAGScheduler: Parents of final stage: List()
    17/08/02 17:00:16 INFO DAGScheduler: Missing parents: List()
    17/08/02 17:00:16 INFO DAGScheduler: Submitting ResultStage 1 (MapPartitionsRDD[3] at filter at SimpleApp.java:29), which has no missing parents
    17/08/02 17:00:16 INFO MemoryStore: Block broadcast_2 stored as values in memory (estimated size 3.2 KB, free 888.4 MB)
    17/08/02 17:00:16 INFO MemoryStore: Block broadcast_2_piece0 stored as bytes in memory (estimated size 1967.0 B, free 888.4 MB)
    17/08/02 17:00:16 INFO BlockManagerInfo: Added broadcast_2_piece0 in memory on 169.254.90.236:59397 (size: 1967.0 B, free: 888.5 MB)
    17/08/02 17:00:16 INFO SparkContext: Created broadcast 2 from broadcast at DAGScheduler.scala:1012
    17/08/02 17:00:16 INFO DAGScheduler: Submitting 1 missing tasks from ResultStage 1 (MapPartitionsRDD[3] at filter at SimpleApp.java:29)
    17/08/02 17:00:16 INFO TaskSchedulerImpl: Adding task set 1.0 with 1 tasks
    17/08/02 17:00:16 INFO TaskSetManager: Starting task 0.0 in stage 1.0 (TID 1, localhost, partition 0, PROCESS_LOCAL, 5324 bytes)
    17/08/02 17:00:16 INFO Executor: Running task 0.0 in stage 1.0 (TID 1)
    17/08/02 17:00:16 INFO BlockManager: Found block rdd_1_0 locally
    17/08/02 17:00:16 INFO Executor: Finished task 0.0 in stage 1.0 (TID 1). 954 bytes result sent to driver
    17/08/02 17:00:16 INFO DAGScheduler: ResultStage 1 (count at SimpleApp.java:33) finished in 0.100 s
    17/08/02 17:00:16 INFO DAGScheduler: Job 1 finished: count at SimpleApp.java:33, took 0.139033 s
    17/08/02 17:00:16 INFO TaskSetManager: Finished task 0.0 in stage 1.0 (TID 1) in 99 ms on localhost (1/1)
    17/08/02 17:00:16 INFO TaskSchedulerImpl: Removed TaskSet 1.0, whose tasks have all completed, from pool

    Lines with a: 11146, lines with b: 10760

    17/08/02 17:00:16 INFO SparkContext: Invoking stop() from shutdown hook
    17/08/02 17:00:16 INFO SparkUI: Stopped Spark web UI at http://169.254.90.236:4040
    17/08/02 17:00:16 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped!
    17/08/02 17:00:16 INFO MemoryStore: MemoryStore cleared
    17/08/02 17:00:16 INFO BlockManager: BlockManager stopped
    17/08/02 17:00:16 INFO BlockManagerMaster: BlockManagerMaster stopped
    17/08/02 17:00:16 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped!
    17/08/02 17:00:16 INFO SparkContext: Successfully stopped SparkContext
    17/08/02 17:00:16 INFO ShutdownHookManager: Shutdown hook called
    17/08/02 17:00:16 INFO ShutdownHookManager: Deleting directory C:\Users\Administrator\AppData\Local\Temp\spark-c40f2015-170f-4633-89dd-44dcd5bacfec

    Process finished with exit code 0
]]>
- - - - - spark - - - - - - - java - - spark - - - -
- - - - - Mybatis-Generator自动生成dao、model、mapper - - /20170802-Mybatis-Generator%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90dao%E3%80%81model%E3%80%81mapper/ - - 一、配置插件

在resources文件夹下新建:generatorConfig.xml

内容如下:注意修改包名等信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!--
详细说明请看:http://blog.csdn.net/tiantangpw/article/details/51690534
-->
<generatorConfiguration>

<context id="mysqlgenerator" targetRuntime="MyBatis3">
<!--数据库配置-->
<jdbcConnection connectionURL="jdbc:mysql://127.0.0.1:3306/booksystem"
driverClass="com.mysql.jdbc.Driver"
password="hisen"
userId="root"/>

<!--生成Model(实体类,与数据库字段对应的bean)类存放位置-->
<javaModelGenerator targetPackage="com.hisen.entity" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!--生成映射(xxxmapper.xml)文件存放位置-->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!--生成Dao类存放位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.hisen.dao"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>

<!--要生成的表-->
<table tableName="appointment"/>
<table tableName="user"/>
</context>

</generatorConfiguration>

二、添加maven插件

在pom.xml中添加如下内容:plugins节点内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version>
</dependency>
</dependencies>
<configuration>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>

三、使用

在idea调出mavenProject界面,选择plugins,找到mybatis-generator,双击即可

]]>
- - - - - java - - - - - - - sql - - mybatis - - - -
- - - - - mybatis - Invalid bound statement (not found) - - /20170802-mybatis%20-%20Invalid%20bound%20statement%20(not%20found)/ - - 运行报错

1
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hisen.dao.UserMapper.insert

使用mybatis生成插件,产生的mapper,由于路径不对移动了一下mapper.java文件

所以造成mapper.xml里面的namespace错误,无法映射

所以把namespace改为正确的即可

例如:

mapper.UserMapper

改为

com.hisen.dao.UserMapper

]]>
- - - - - java - - - - - - - mybatis - - - -
- - - - - 为什么要使用队列 - Java - - /20170801-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8%E9%98%9F%E5%88%97%20-%20Java/ - - 一、java中的队列:Queue接口

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。

队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素(注意不要弄混队列的头部和尾部)就是说,队列以一种先进先出的方式管理数据,如果你试图向一个 已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线程把中间结果取出并在将来修改它们。队列会自动平衡负载。如果第一个线程集运行得比第二个慢,则第二个 线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来。下表显示了jdk1.5中的阻塞队列的操作:

排序方法平均情况最好情况
add增加一个元素如果队列已满,则抛出一个IllegalSlabEepeplian异常
remove移除并返回队列头部的元素如果队列为空,则抛出一个NoSuchElementException异常
element返回队列头部的元素如果队列为空,则抛出一个NoSuchElementException异常
offer添加一个元素并返回true如果队列已满,则返回false
poll移除并返问队列头部的元素如果队列为空,则返回null
peek返回队列头部的元素如果队列为空,则返回null
put返回队列头部的元素如果队列满,则阻塞
take返回队列头部的元素如果队列为空,则阻塞

二、消息队列

介绍:

消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。

实现高性能,高可用,可伸缩和最终一致性架构。

是大型分布式系统不可缺少的中间件。

目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。

场景:异步处理、应用解耦、流量削锋、日志处理


以上就是关于为什么要使用队列的大致说明


参考:

  1. java中队列的使用
  2. 消息队列的使用场景
]]>
- - - - - java - - - - - - - java - - 队列 - - - -
- - - - - No MyBatis mapper was found in 'com.xxx.dao' package - - /20170801-No%20MyBatis%20mapper%20was%20found%20in%20'%5Bcom.xxx.dao%5D'%20package%20/ - - 出错

maven项目,启动tomcat的时候报错:

1
No MyBatis mapper was found in '[com.hisen.dao]' package

这是由于把mybatis的mapper配置文件放在了java代码的目录下

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
├── java
│ └── com
│ └── hisen
│ ├── dao
│ │ └── BookDao.java
│ │ └── BookMapper.xml
│ ├── entity
│ │ └── Book.java
│ ├── service
│ │ ├── BookService.java
│ │ └── impl
│ │ └── BookServiceImpl.java
│ └── web
│ └── BookController.java
├── resources
│ ├── jdbc.properties
│ ├── logback.xml
│ ├── mybatis-config.xml
│ └── spring
│ ├── spring-dao.xml
│ ├── spring-service.xml
│ └── spring-web.xml
└── webapp
├── index.jsp
└── WEB-INF
├── jsp
│ ├── detail.jsp
│ └── list.jsp
└── web.xml

出错原因

这是因为mapper文件的路径问题:maven在打包的时候默认是认为src/main/java目录下只有java源代码,不会管xml文件。

Java 项目分为两个部分,一个是源码,一个是资源,在使用maven等构建工具时,

默认会将源码编译后再加上资源目录的文件放到target目录下作为最后运行的文件(可以是war,jar,或者目录)。

有时为了方便,我们会在src/main/java源码目录下放了资源文件,例如mybatis的mapper文件,方便我们编程时展开查看。

这时,我们需要设置编译时也将这些配置文件放到target目录下,否则最后的target目录式没有这些文件的。

解决办法

在项目的pom文件里面配置,让maven也从src/main/java目录下读取xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<build>
<!--注意,如果将静态资源放在src/main/java中,那么编译时将被maven忽略,
在target目录下将没有这些资源,所以需要将该目录添加为资源目录.
-->
<resources>
<resource>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>

扩展

如果是把mapper文件什么的放在resources目录下

但是为了分类建了不同的文件夹,也需要修改配置文件,不然会读取不到

1
2
3
4
5
6
7
8
9
10
11
12
<build>
<!--包含下面所有的配置文件(包括子目录)-->
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>

]]>
- - - - - java - - - - - - - java - - xml - - - -
- - - - - SpringMVC把jsp改为Thymeleaf模版引擎 - - /20170801-SpringMVC%E6%8A%8Ajsp%E6%94%B9%E4%B8%BAThymeleaf%E6%A8%A1%E7%89%88%E5%BC%95%E6%93%8E/ - - Thymeleaf简介

前面的例子我们使用的视图技术主要是JSP。JSP的优点是它是Java EE容器的一部分,几乎所有java EE服务器都支持JSP。缺点就是它在视图表现方面的功能很少,假如我们想迭代一个数组之类的,只能使用<% %>来包括Java语句进行。虽然有标准标签库(JSTL)的补足,但是使用仍然不太方便。另外JSP只能在Java EE容器中使用,如果我们希望渲染电子邮件之类的,JSP就无能为力了。

Java生态圈广泛,自然有很多视图框架,除了JSP之外,还有Freemarker、Velocity、Thymeleaf等很多框架。Thymeleaf的优点是它是基于HTML的,即使视图没有渲染成功,也是一个标准的HTML页面。因此它的可读性很不错,也可以作为设计原型来使用。而且它是完全独立于java ee容器的,意味着我们可以在任何需要渲染HTML的地方使用Thymeleaf。

Thymeleaf也提供了spring的支持,我们可以非常方便的在Spring配置文件中声明Thymeleaf Beans,然后用它们渲染视图。

改造 - 由jsp到Thymeleaf

  1. 引入依赖

    1
    2
    3
    4
    5
    6
    <!--thymeleaf模版 spring4.x-->
    <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring4</artifactId>
    <version>3.0.5.RELEASE</version>
    </dependency>
  2. 配置ViewResolver(在spring的xml文件里)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- 配置ViewResolver 使用:thymeleaf 模版引擎-->
    <bean id="templateResolver"
    class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
    <property name="prefix" value="/WEB-INF/templates/"/>
    <property name="suffix" value=".html"/>
    <property name="templateMode" value="HTML5"/>
    <!-- 缓存-->
    <property name="cacheable" value="false"/>
    </bean>
    <bean id="templateEngine"
    class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver"/>
    </bean>
    <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine"/>
    <property name="order" value="1"/>
    <property name="characterEncoding" value="UTF-8"/>
    </bean>
  3. 接下来就可以直接使用了,跟之前的jsp没有什么不同

    1
    2
    3
    4
    5
    6
    7
    @RequestMapping(value = "/listpageplug/{start}", method = RequestMethod.GET)
    private String listPagePlug(@PathVariable("start") String start, Model model) {
    PageHelper.startPage(Integer.valueOf(start), 20);
    List<Book> readingList = bookService.getListPlug();
    model.addAttribute("books", readingList);
    return "readingList";
    }

到这里就改造完了,接下来就是Thymeleaf的各种用法了

这里举一个循环遍历的例子,后台返回了books对象集合

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--判断是否为空-->
<tbody th:unless="${#lists.isEmpty(books)}">
<!--循环-->
<tr th:each="book : ${books}">
<td th:text="${book.bookId}"></td>
<td th:text="${book.name}"></td>
<td th:text="${book.number}"></td>
<td>
<a href="<%=appPath%>/book/detail/${book.bookId}">详情</a> |
<a href="<%=appPath%>/book/del/${book.bookId}">删除</a>
</td>
</tr>
</tbody>

参考

  1. thymeleaf官方指南
  2. 新一代Java模板引擎Thymeleaf
  3. Thymeleaf基本知识
  4. thymeleaf总结文章
  5. Thymeleaf 模板的使用
  6. thymeleaf 学习笔记
]]>
- - - - - java - - - - - - - java - - spring - - - -
- - - - - Google java工具类 - Guava - 常见用法 - - /20170801-Google%20java%E5%B7%A5%E5%85%B7%E7%B1%BB%20-%20Guava%20-%20%E5%B8%B8%E8%A7%81%E7%94%A8%E6%B3%95/ - - 概述

工具类 就是封装平常用的方法,不需要你重复造轮子,节省开发人员时间,提高工作效率。谷歌作为大公司,当然会从日常的工作中提取中很多高效率的方法出来。所以就诞生了Guava。

高效设计良好的API,被Google的开发者设计,实现和使用

遵循高效的java语法实践

使代码更刻度,简洁,简单

节约时间,资源,提高生产力 Guava工程

包含了若干被Google的 Java项目广泛依赖 的核心库,例如:

  1. 集合 [collections]
  2. 缓存 [caching]
  3. 原生类型支持 [primitives support]
  4. 并发库 [concurrency libraries]
  5. 通用注解 [common annotations]
  6. 字符串处理 [string processing]
  7. I/O 等等。

部分用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
public class CommonUsage {

/**
* 2.集合转换为特定格式的字符串
*/
@Test
public void collection2Srt() {
//List转
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
String result = Joiner.on("-").join(list);
System.out.println(result);//a-b-c-d-e-f

//Map 转
HashMap<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
//on 分隔符,withKeyValueSeparator key value之间的分隔符
String s = Joiner.on(",").withKeyValueSeparator("=").join(map);
System.out.println(s);//a=1,b=2,c=3
}

/**
* 3.将String 转为特定的集合
*/
@Test
public void str2Collection() {
String s = "1,2,3,4,5,6,7,8,9";
List<String> list = Splitter.on(",").splitToList(s);
System.out.println(list);

s = "1-2-3-4- 5- 6 ";
//去除空串与空格
List<String> list1 = Splitter.on("-").omitEmptyStrings().trimResults().splitToList(s);
System.out.println(list1);

//将String转换为map
String str = "a=1,b=2";
Map<String, String> map = Splitter.on(",").withKeyValueSeparator("=").split(str);
}

/**
* 4.多个字符切割,或者特定的正则分隔
*/
@Test
public void strSplit() {
String input = "aa.dd,,ff,,.";
List<String> list = Splitter.onPattern("[.|,]").omitEmptyStrings().trimResults()
.splitToList(input);
System.out.println(list);
// 判断匹配结果
boolean result = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z'))
.matches('K');//true
// 保留数字文本
String s1 = CharMatcher.digit().retainFrom("abc 123 efg");//123
// 删除数字文本
String s2 = CharMatcher.digit().removeFrom("abc 123 efg");//abc efg
}
/**
* 9.计算程序开始结束用了多少时间
*/
@Test
public void countTime() {
Stopwatch stopwatch = Stopwatch.createStarted();
for (int i = 0; i < 100000; i++) {
}
//TimeUnit可以指定时间精度
long nanos = stopwatch.elapsed(TimeUnit.MILLISECONDS);
System.out.println(nanos);
}
/**
* 10.Files 文件操作
* 方便,
*/
@Test
public void filesOperation() {
String fileName = "./src/main/java/com/hisen/jars/guava/test.txt";
for (int i = 0; i < 1000; i++) {
//写
filesWrite(fileName, String.valueOf(i) + "\n");
}
//读
List<String> list = filesRead(fileName);
System.out.println("读取到的内容:" + list.toString());
// Files.copy(sourceFile, targetFile); //复制文件
// Files.equal(File,File);//比较文件内容是否完全一致
// touch方法创建或者更新文件的时间戳。
// createTempDir()方法创建临时目录
// Files.createParentDirs(File) 创建父级目录
// getChecksum(File)获得文件的checksum
// hash(File)获得文件的hashmap系列方法获得文件的内存映射
// getFileExtension(String)获得文件的扩展名
// getNameWithoutExtension(String file)获得不带扩展名的文件名
}

/**
* Files写文件
*
* @param fileName 文件名(包括路径)
* @param contents 写入的单行内容
*/
public void filesWrite(final String fileName, final String contents) {
checkNotNull(fileName, "文件名称不能为空");
checkNotNull(contents, "写入内容不能为空");
final File file = new File(fileName);
try {
//Files.write(contents.getBytes(), file);//覆盖
Files.append(contents, file, Charsets.UTF_8);//追加
} catch (IOException e) {
System.out.println("ERROR trying to write to file '" + fileName + "' - "
+ e.toString());
}
}

/**
* Files 文件单行读取
*
* @param filePath 文件名(包括路径)
*/
public List<String> filesRead(String filePath) {
checkNotNull(filePath, "文件名称不能为空");
File file = new File(filePath);
List<String> list = null;
try {
list = Files.readLines(file, Charsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
}

部分摘自:点击查看

]]>
- - - - - java - - - - - - - java - - google - - jar - - - -
- - - - - MySQL update语句 - - /20170801-MySQL%20update%E8%AF%AD%E5%8F%A5/ - - 刚在一个群里,有人有这么一个需求。

表A:id,name

表B:id,其他,name(新增字段)

A,B表通过id关联,要把A的name给对应的B的name

以前也没有写过这种update语句

1
2
3
4
5
6
update 表名 set 字段名=字段值 where 条件
如 update A set name='xiaoming' where id='';
如果是多表查询
update 表1 a inner join 表2 b on ab表的关联 set a.字段=b.字段
如 update A a inner join B b on a.id=b.id set a.name=b.name
就是在table1表和table2表id相同时 把table2的name值赋给table1的name

]]>
- - - - - sql - - - - - - - mysql - - - -
- - - - - Git Bash tree(windows)让windows支持tree - - /20170713-Git%20Bash%20tree%EF%BC%88windows%EF%BC%89/ - - 在安装了git客户端之后,发现Git Bash挺好用

之前在linux shell用过tree命令感觉不错

发现Git Bash也可以实现,于是就记录一下

下载地址:点击前往

下载文件: Binaries Zip

解压文件:bin目录下找到tree.exe

把这个放到git安装目录下后的路径:C:\Program Files\Git\usr\bin\tree.exe

测试:虽然有点丑。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
hisen@HiSEN MINGW64 /c/code/SpringBootCLI/myapp
$ tree
.
|-- mvnw
|-- mvnw.cmd
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- com
| | `-- example
| | `-- myapp
| | |-- DemoApplication.java
| | `-- ServletInitializer.java
| `-- resources
| |-- application.properties
| |-- static
| `-- templates
`-- test
`-- java
`-- com
`-- example
`-- myapp
`-- DemoApplicationTests.java

14 directories, 7 files

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Spring Boot CLI 安装(windows) - - /20170713-Spring%20Boot%20CLI%20%E5%AE%89%E8%A3%85%EF%BC%88windows%EF%BC%89/ - - SpringBootCLI是一个命令行工具,可用于快速搭建基于spring的原型。

它支持运行Groovy脚本,这也就意味着你可以使用类似Java的语法,但不用写很多的模板代码。

Spring Boot不一定非要配合CLI使用,但它绝对是Spring应用取得进展的最快方式.

下载分发包

  1. 地址:getting-started-installing-the-cli
  2. 下载:spring-boot-cli-x.x.x.RELEASE-bin.zip (例如:spring-boot-cli-1.5.4.RELEASE-bin.zip)
  3. 解压
  4. 设置环境变量:C:\hisenwork\soft\spring-1.5.4.RELEASE\bin (在path中添加)
  5. cmd测试:spring –version
    1
    2
    spring --version
    Spring CLI v1.5.4.RELEASE

运行一个简单程序

文件名称:HelloController.groovy

文件内容:

1
2
3
4
5
6
7
@RestController
class HelloController{
@RequestMapping("/")
def hello(){
return "Hello World"
}
}

运行程序:cmd进入文件所在文件夹,执行:spring run HelloController.groovy

提醒:Resolving dependencies第一次初始化时间会久一点,耐心等待

1
2
3
4
5
6
7
8
9
10
11
spring run HelloController.groovy

Resolving dependencies..........................

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.4.RELEASE)

之后在浏览器中输入:http://localhost:8080/

就能看到:Hello World

]]>
- - - - - java - - - - - - - java - - - -
- - - - - mongodb - failed with errno13 Permission denied - - /20170712-mongodb%20-%20failed%20with%20errno%2013%20Permission%20denied/ - - 在命令行输入:mongo报错

1
2
3
4
5
hisen@ubuntu:~$ mongo
MongoDB shell version: 3.2.13
connecting to: test
2017-07-11T23:11:23.827+0800 I STORAGE [main] In File::open(), ::open for '/home/hisen/.mongorc.js' failed with errno:13 Permission denied
The ".mongorc.js" file located in your home folder could not be executed

看倒数第二行,应该是权限的问题,于是

1
2
3
4
5
6
7
8
9
10
11
12
hisen@ubuntu:~$ sudo chown -R hisen /home/hisen/.mongorc.js
hisen@ubuntu:~$ mongo
MongoDB shell version: 3.2.13
connecting to: test
Server has startup warnings:
2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten]
2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten]
2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten]

完美解决,用户权限的问题。

]]>
- - - - - sql - - - - - - - mongodb - - - -
- - - - - MongoDB - 简单操作 - CRUD - JAVA - - /20170711-MongoDB%20-%20%E7%AE%80%E5%8D%95%E6%93%8D%E4%BD%9C%20-%20CRUD%20-%20JAVA/ - - 之前我记得写过简单的测试类,但是忘了,现在重新写一个

顺便用博客记录下,mongodb系列应该会有几篇记录

本篇具体代码:SampleMongoTestNo1.java

1、环境介绍

mongodb安装教程:点击查看

1
2
3
4
5
6
7
8
DataBase:MongoDB V 3.2
Driver :3.4.1
maven:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.1</version>
</dependency>

2、初始化连接

1
2
3
4
5
6
7
8
9
10
11
12
13
private Mongo mg = null;
private DB db;
private DBCollection dbCollection;

@Before
public void initDB(){
//建立连接
mg = new MongoClient("127.0.0.1",27017);
//获取要操作的数据库实例,没有会创建
db = mg.getDB("hisen");
//获取要操作的集合实例,没有会创建
dbCollection = db.getCollection("emp");
}

3、关闭连接

1
2
3
4
5
6
7
8
9
@After
public void destoryDB(){
if (mg == null) {
mg.close();
mg=null;
db=null;
dbCollection=null;
}
}

4、CRUD操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* 插入数据
*/
@Test
public void testCreate(){
DBObject obj = null;
for (int i = 1; i <= 10; i++) {
obj = new BasicDBObject("_id",i).append("name","hisen"+i).append("age",i*5);
dbCollection.save(obj);
}
}

/**
* 查询所有
*/
@Test
public void testReadAll(){
DBCursor dbCursor = dbCollection.find();
while (dbCursor.hasNext()){
System.out.println(dbCursor.next());
}
}

/**
* 修改记录
*/
@Test
public void testUpdate(){
BasicDBObject condition = new BasicDBObject("_id",10);
BasicDBObject res = new BasicDBObject("name", "hisen10_new");
//若没有此语句,直接调用下面的语句,返回结果{ "_id" : 10 , "name" : "hisen10_new"}
BasicDBObject res2 = new BasicDBObject("$set", res);
dbCollection.update(condition,res2);
System.out.println(dbCollection.findOne(new BasicDBObject("_id",10)));
}

/**
* 删除记录
*/
@Test
public void testDelete(){
dbCollection.remove(new BasicDBObject("_id",10));
testReadAll();
}

/**
* 根据主键查询
*/
@Test
public void testReadOneWithId(){
DBObject object = dbCollection.findOne(new BasicDBObject("_id",1));
System.out.println(object);
}

/**
* 模糊查询 - 使用正则
*/
@Test
public void testReadPuzzy(){
Pattern pattern = Pattern.compile("^hisen1");
BasicDBObject basicDBObject = new BasicDBObject("name",pattern);
DBCursor dbCursor = dbCollection.find(basicDBObject);
while (dbCursor.hasNext()){
System.out.println(dbCursor.next());
}
}

/**
* 清空集合
*/
@Test
public void testDrop(){
dbCollection.drop();
testReadAll();
}

]]>
- - - - - sql - - - - - - - mongodb - - - -
- - - - - 浏览器出现:Reimage Repair - zh.reimageplus.com - - /20170705-%E6%B5%8F%E8%A7%88%E5%99%A8%E5%87%BA%E7%8E%B0%EF%BC%9AReimage%20Repair%20-%20zh.reimageplus.com/ - - 近期使用chrome出现打开一些网站老是弹出莫名其妙的网址

最终指向的都是Reimage Repair,网址zh.reimageplus.com
Reimage Repair
网上一查,貌似因为插件被污染的原因

我关闭chrome所有的插件,一个一个排查,最后找到了一个插件出问题

本来还想去举报,发现这个插件被下架了

问题排查

1
2
3
右上角菜单按钮 ---更多工具 --- 扩展程序 --- 关闭所有插件
然后一个个打开,测试某些网页是否还会继续跳出那恶心的东西
然后就找到问题了。

]]>
- - - - - 软件 - - - - - - - 病毒 - - - -
- - - - - ubuntu root su 密码 - - /20170704-Ubuntu%20root%20%E5%AF%86%E7%A0%81/ - - 在安装完ubuntu的时候

只有自己设置的非root的帐号和密码

但是又要用root密码怎么办呢?

1
2
3
4
hisen@ubuntu-1:~$ sudo passwd
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

上面输入的密码就是你的root密码

检测一下

1
2
3
hisen@ubuntu-1:~$ su
Password:
root@ubuntu-1:/home/hisen#

通过,至此结束

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Ubuntu安装RocketMQ - Quick Start - - /20170702-Ubuntu%E5%AE%89%E8%A3%85RocketMQ%20-%20Quick%20Start/ - - 安装需求:
  1. 64位的系统 Linux/Unix/Mac
  2. 64bit JDK 1.8+;
  3. Maven 3.2.x;
  4. Git (一般自带)

如果还未安装jdk、maven建议查看教程:点击查看

Clone & Build

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
git clone -b develop https://github.com/apache/incubator-rocketmq.git
cd incubator-rocketmq
mvn -Prelease-all -DskipTests clean install -U
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Apache RocketMQ 4.2.0-incubating-SNAPSHOT .......... SUCCESS [01:07 min]
[INFO] rocketmq-remoting 4.2.0-incubating-SNAPSHOT ........ SUCCESS [ 15.749 s]
[INFO] rocketmq-common 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 10.243 s]
[INFO] rocketmq-client 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 11.638 s]
[INFO] rocketmq-store 4.2.0-incubating-SNAPSHOT ........... SUCCESS [ 13.108 s]
[INFO] rocketmq-srvutil 4.2.0-incubating-SNAPSHOT ......... SUCCESS [ 2.051 s]
[INFO] rocketmq-filter 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 3.917 s]
[INFO] rocketmq-broker 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 8.726 s]
[INFO] rocketmq-tools 4.2.0-incubating-SNAPSHOT ........... SUCCESS [ 6.002 s]
[INFO] rocketmq-namesrv 4.2.0-incubating-SNAPSHOT ......... SUCCESS [ 2.726 s]
[INFO] rocketmq-logappender 4.2.0-incubating-SNAPSHOT ..... SUCCESS [ 3.514 s]
[INFO] rocketmq-openmessaging 4.2.0-incubating-SNAPSHOT ... SUCCESS [ 2.668 s]
[INFO] rocketmq-example 4.2.0-incubating-SNAPSHOT ......... SUCCESS [ 2.390 s]
[INFO] rocketmq-filtersrv 4.2.0-incubating-SNAPSHOT ....... SUCCESS [ 2.145 s]
[INFO] rocketmq-test 4.2.0-incubating-SNAPSHOT ............ SUCCESS [ 5.428 s]
[INFO] rocketmq-distribution 4.2.0-incubating-SNAPSHOT .... SUCCESS [ 27.002 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:07 min
[INFO] Finished at: 2017-07-02T01:35:40+08:00
[INFO] Final Memory: 60M/247M
[INFO] ------------------------------------------------------------------------
cd distribution/target/apache-rocketmq

Start Name Server

1
2
3
nohup sh bin/mqnamesrv &
tail -f ~/logs/rocketmqlogs/namesrv.log
The Name Server boot success...

Start Broker

1
2
3
nohup sh bin/mqbroker -n localhost:9876 &
tail -f ~/logs/rocketmqlogs/broker.log
The broker[%s, 172.30.30.233:10911] boot success...

Send & Receive Messages

1
2
3
4
5
6
7
Before sending/receiving messages, we need to tell clients the location of name servers. RocketMQ provides multiple ways to achieve this. For simplicity, we use environment variable NAMESRV_ADDR
export NAMESRV_ADDR=localhost:9876
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
SendResult [sendStatus=SEND_OK, msgId= ...

sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
ConsumeMessageThread_%d Receive New Messages: [MessageExt...

Shutdown Servers

1
2
3
4
5
6
7
sh bin/mqshutdown broker
The mqbroker(36695) is running...
Send shutdown request to mqbroker(36695) OK

sh bin/mqshutdown namesrv
The mqnamesrv(36664) is running...
Send shutdown request to mqnamesrv(36664) OK

]]>
- - - - - linux - - - - - - - linux - - rocketmq - - - -
- - - - - powerdesigner Oracle mysql comment 缺少右括号 - - /20170622-powerdesigner%20Oracle%20mysql%20comment%20%E7%BC%BA%E5%B0%91%E5%8F%B3%E6%8B%AC%E5%8F%B7/ - - powerdesigner Oracle mysql comment 缺少右括号

今天人家给我个powerdesigner设计好的表

要我去间数据库,直接复制出来去oracle执行,结果报错。

在comment附近,如果把comment去掉则可以正确执行

最后找到罪魁祸首:powerdesigner没有设置对数据库

解决办法,在powerdesigner页面

1
database-->change curren DBMS

上面是设置你需要的数据库

下面是为更改前的数据库

]]>
- - - - - sql - - - - - - - mysql - - oracle - - - -
- - - - - sql行转列,列转行 - - /20170613-sql%E8%A1%8C%E8%BD%AC%E5%88%97%EF%BC%8C%E5%88%97%E8%BD%AC%E8%A1%8C/ - - 有时候需要行转列或者列转行

在Oracle 11g中,Oracle 又增加了2个查询:pivot(行转列) 和unpivot(列转行)

下面是在mysql中的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mysql> select * from test;
+------+------+--------+
| id | age | weigth |
+------+------+--------+
| 1 | 1 | 3 |
| 1 | 2 | 2 |
| 1 | 3 | 1 |
| 2 | 5 | 6 |
+------+------+--------+
4 rows in set (0.00 sec)

#根据ID分组
mysql> select group_concat(age) from test group by id;
+-------------------+
| group_concat(age) |
+-------------------+
| 1,2,3 |
| 5 |
+-------------------+
2 rows in set (0.01 sec)

#不分组
mysql> select group_concat(age) from test;
+-------------------+
| group_concat(age) |
+-------------------+
| 1,2,3,5 |
+-------------------+
1 row in set (0.00 sec)

]]>
- - - - - sql - - - - - - - mysql - - oracle - - - -
- - - - - oracle全局查找某字段所在的表 - - /20170612-Oracle%E5%85%A8%E5%B1%80%E6%9F%A5%E6%89%BE%E6%9F%90%E5%AD%97%E6%AE%B5%E6%89%80%E5%9C%A8%E7%9A%84%E8%A1%A8/ - - 下面这个存储过程是查找数据库中某个字段值为:hisen 的表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
declare
l_cnt varchar2(20);
v_sql varchar2(4000);
v_tablename varchar(200);
cursor cursor_jsdx is select 'select count(*) from ' || table_name || ' where NAME=''hisen''',table_name from user_tab_columns where column_name='NAME';
--注:这里的字段名要大写
begin
open cursor_jsdx;
Loop
fetch cursor_jsdx into v_sql,v_tablename;
exit when cursor_jsdx%notfound;
execute immediate v_sql into l_cnt;
if l_cnt >0 then
---如果该表有那内容的就打印那个表的名字。
dbms_output.put_line(v_tablename);
end if;
end loop;
Close cursor_jsdx;
end;

]]>
- - - - - sql - - - - - - - oracle - - - -
- - - - - java.sql.SQLException Connections could not be acquired from the underlying database - - /20170602-java.sql.SQLException%20Connections%20could%20not%20be%20acquired%20from%20the%20underlying%20database/ - - java.sql.SQLException: Connections could not be acquired from the underlying database

这个错误出现的原因有的说是因为jdbc配置文件写错了

正确的写法是:

1
2
3
4
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/test
c3p0.user=root
c3p0.password=root

而不是

1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=root

但是我遇到的不是如此

后来发现是因为maven出问题了,jar包没有加载进项目

这是在starkoverflow上看到的回答

1
In my case it was problem that c3p0-0.9.2.1.jar file was not copied by maven into src/main/webapp/web-inf/libs

最后maven重新install之后就解决了

]]>
- - - - - java - - - - - - - java - - - -
- - - - - 尝试使用NIO读取文件 - - /20170601-%E5%B0%9D%E8%AF%95%E4%BD%BF%E7%94%A8NIO%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6/ - - 具体代码如下,这里获取出来的文件大小是准确的

FileInputStream方式获取出来的大小会有差异

NIO与IO的对比详见:NIOCopy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 利用NIO进行读写文件
*
* @param oldFileName 原文件的路径
* @param newFileName 新文件的路径
*/
public static void nioCopy(String oldFileName, String newFileName) {
try {
FileChannel fileChannelIn = new FileInputStream(new File(oldFileName)).getChannel();
FileChannel fileChannelOut = new FileOutputStream(new File(newFileName)).getChannel();
//获取文件大小
long size = fileChannelIn.size();
System.out.printf("文件大小为:%s byte \n",size);
//缓冲
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

long start = System.currentTimeMillis();
while (fileChannelIn.read(byteBuffer) != -1) {
//准备写
byteBuffer.flip();
fileChannelOut.write(byteBuffer);
//准备读
byteBuffer.clear();
}
long end = System.currentTimeMillis();
System.out.printf("NIO方式复制完成,耗时 %s 秒\n",(end-start)/1000);
//关闭
fileChannelIn.close();
fileChannelOut.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - try redis - redis官方教程练习 - - /20170519-try%20redis%20-%20redis%E5%AE%98%E6%96%B9%E6%95%99%E7%A8%8B%E7%BB%83%E4%B9%A0/ - - 这里主要是介绍了几种redis支持的数据结构,以及操作方法

官网地址:http://try.redis.io/

我的redis是安装在linux虚拟机,通过Xshell操作,显示可能跟cmd不大一样

但是操作都是一样的

具体操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#连接redis客户端
hisen@ubuntu:~$ redis-cli
#存放数据
127.0.0.1:6379> set 'connections' '10'
OK
127.0.0.1:6379> get connections
"10"
#自增1方法:incr
127.0.0.1:6379> incr connections
(integer) 11
127.0.0.1:6379> incr connections
(integer) 12
#删除
127.0.0.1:6379> del conncetions
(integer) 0
#不存在才存放数据 setnx:SET-if-not-exists
127.0.0.1:6379> SETNX hisen hello
(integer) 1
127.0.0.1:6379> get hisen
"hello"
127.0.0.1:6379> SETNX hisen hello
(integer) 0
#添加数据
127.0.0.1:6379> set resource:lock "redis demo"
OK
#设置超时时间,单位秒
127.0.0.1:6379> expire resource:lock 120
(integer) 1
#查看剩余时间
127.0.0.1:6379> ttl resource:lock
(integer) 85
127.0.0.1:6379> ttl resource:lock
(integer) 81
127.0.0.1:6379> ttl resource:lock
(integer) 57

#List集合
#放在list最后(right 右边)
127.0.0.1:6379> rpush friends "Alice"
(integer) 1
127.0.0.1:6379> rpush friends "Bob"
(integer) 2
#放在最前(left 左边)
127.0.0.1:6379> lpush friends "Sam"
(integer) 3
#获取所有数据
127.0.0.1:6379> lrange friends 0 -1
1) "Sam"
2) "Alice"
3) "Bob"
#获取下标0-1的数据
127.0.0.1:6379> lrange friends 0 1
1) "Sam"
2) "Alice"
#获取下标1-2的数据
127.0.0.1:6379> lrange friends 1 2
1) "Alice"
2) "Bob"
#获取长度
127.0.0.1:6379> llen friends
(integer) 3
#删除第一个数据(左边)
127.0.0.1:6379> lpop friends
"Sam"
#删除最后一个数据(右边)
127.0.0.1:6379> rpop friends
"Bob"
127.0.0.1:6379> llen friends
(integer) 1
#输出所有
127.0.0.1:6379> lrange friends 0 -1
1) "Alice"

#Set集合
#添加
127.0.0.1:6379> sadd superpowers "flight"
(integer) 1
127.0.0.1:6379> sadd superpowers "x-ray vision"
(integer) 1
127.0.0.1:6379> sadd superpowers "reflexes"
(integer) 1
#删除
127.0.0.1:6379> srem superpowers "reflexes"
(integer) 1
#判断数据是否存在set中
127.0.0.1:6379> sismember superpowers "flight"
(integer) 1
#输出所有
127.0.0.1:6379> smembers superpowers
1) "flight"
2) "x-ray vision"

127.0.0.1:6379> sadd birdpowers "pecking"
(integer) 1
127.0.0.1:6379> sadd birdpowers "flight"
(integer) 1
#合并两个SET,会过滤重复
127.0.0.1:6379> sunion superpowers birdpowers
1) "pecking"
2) "flight"
3) "x-ray vision"

#有序集合,按照数字排序
127.0.0.1:6379> zadd hackers 1940 "Alan Kay"
(integer) 1
127.0.0.1:6379> zadd hackers 1906 "Grace Hopper"
(integer) 1
127.0.0.1:6379> zadd hackers 1953 "Richard Stallman"
(integer) 1
127.0.0.1:6379> zadd hackers 1965 "Yukihiro Mastsumoto"
(integer) 1
127.0.0.1:6379> zadd hackers 1916 "Claude Shannon"
(integer) 1
127.0.0.1:6379> zadd hackers 1969 "Linus Torvalds"
(integer) 1
127.0.0.1:6379> ZADD hackers 1957 "Sophie Wilson"
(integer) 1
127.0.0.1:6379> ZADD hackers 1912 "Alan Turing"
(integer) 1
#输出
127.0.0.1:6379> zrange hackers 2 4
1) "Claude Shannon"
2) "Alan Kay"
3) "Richard Stallman"
127.0.0.1:6379> zrange hackers 0 -1
1) "Grace Hopper"
2) "Alan Turing"
3) "Claude Shannon"
4) "Alan Kay"
5) "Richard Stallman"
6) "Sophie Wilson"
7) "Yukihiro Mastsumoto"
8) "Linus Torvalds"
127.0.0.1:6379>

#哈希集合
127.0.0.1:6379> hset user:1000 name "hisen"
(integer) 1
127.0.0.1:6379> hset user:1000 email "hisen@hisen.com"
(integer) 1
127.0.0.1:6379> hset user:1000 pwassword "pswd"
(integer) 1
127.0.0.1:6379> hgetall user:1000
1) "name"
2) "hisen"
3) "email"
4) "hisen@hisen.com"
5) "pwassword"
6) "pswd"

#自增
27.0.0.1:6379> hset user:1000 visits 10
(integer) 1
127.0.0.1:6379> hset user:1000 visits 1
(integer) 0
127.0.0.1:6379> hincrby user:1000 visits 1
(integer) 2
127.0.0.1:6379> hincrby user:1000 visits 1
(integer) 3
127.0.0.1:6379> hincrby user:1000 visits 10
(integer) 13
127.0.0.1:6379> hdel user:1000 visits
(integer) 1
127.0.0.1:6379> hincrby user:1000 visits 1
(integer) 1

]]>
- - - - - sql - - - - - - - java - - sql - - - -
- - - - - MySQL索引相关知识和应用 - - /20170518-MySQL%E7%B4%A2%E5%BC%95%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E5%92%8C%E5%BA%94%E7%94%A8/ - - hisen库中有一个post表,数据20w,非重复数据20条;

结构如下

1
2
3
4
5
6
7
8
9
mysql> describe post;
+---------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | YES | MUL | NULL | |
| content | varchar(2550) | YES | | NULL | |
+---------+---------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

添加普通索引

1
2
3
mysql> alter table post add index index_post_title (title);
Query OK, 0 rows affected (1.22 sec)
Records: 0 Duplicates: 0 Warnings: 0

删除索引

1
2
3
mysql> drop index index_post_title on post;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0

添加索引之后查询速度明显加快

1
2
3
4
mysql> select count(title) from post group by title;
20 rows in set (0.25 sec)
#加了索引之后
20 rows in set (0.08 sec)

使用索引的情况

  1. 表的主关键字:自动建立唯一索引
  2. 表的字段唯一约束:ORACLE利用索引来保证数据的完整性
  3. 直接条件查询的字段
  4. 在SQL中用于条件约束的字段
  5. 查询中与其它表关联的字段
  6. 查询中排序的字段
  7. 查询中统计或分组统计的字段

不使用索引的情况

  1. 表记录太少
  2. 经常插入、删除、修改的表
  3. 数据重复且分布平均的表字段:假如10万数据只有A、B状态,且A、B各50%,这样建立索引就不会提速
  4. 经常和主字段一块查询但主字段索引值比较多的表字段

MySql在建立索引优化时需要注意的问题

  1. 创建索引:对于查询占主要的应用来说,索引显得尤为重要。很多时候性能问题很简单的就是因为我们忘了添加索引而造成的,或者说没有添加更为有效的索引导致。如果不加索引的话,那么查找任何哪怕只是一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降。但是也不是什么情况都非得建索引不可,比如性别可能就只有两个值,建索引不仅没什么优势,还会影响到更新速度,这被称为过度索引。
  2. 复合索引:比如有一条语句是这样的:select * from users where area=’beijing’ and age=22;如果我们是在area和age上分别创建单个索引的话,由于mysql查询每次只能使用一个索引,所以虽然这样已经相对不做索引时全表扫描提高了很多效率,但是如果在area、age两列上创建复合索引的话将带来更高的效率。如果我们创建了(area, age,salary)的复合索引,那么其实相当于创建了(area,age,salary)、(area,age)、(area)三个索引,这被称为最佳左前缀特性。因此我们在创建复合索引时应该将最常用作限制条件的列放在最左边,依次递减。
  3. 索引不会包含有NULL值的列:只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
  4. 使用短索引:对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的 列,如果在前10 个或20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
  5. 排序的索引问题:mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
  6. like语句操作:一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
  7. 不要在列上进行运算:select * from users where YEAR(adddate);
  8. 不使用NOT IN和操作:NOT IN和操作都不会使用索引将进行全表扫描。NOT IN可以NOT EXISTS代替,id3则可使用id>3 or id
]]>
- - - - - mysql - - - - - - - mysql - - 索引 - - - -
- - - - - MySQL下A库的a表导入到B库的b表 - - /20170516-MySQL%E4%B8%8BA%E5%BA%93%E7%9A%84a%E8%A1%A8%E5%AF%BC%E5%85%A5%E5%88%B0B%E5%BA%93%E7%9A%84b%E8%A1%A8/ - - 数据库名称表名employeesemployeeshisenemployee

现在想把第一行的数据导入到第二行,具体如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> describe employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set

mysql> select count(*) from employees;
+----------+
| count(*) |
+----------+
| 300024 |
+----------+
1 row in set

employees数据库中的employees表有30万数据

我想把这个数据导出到另外一个数据库hisen中的employee表中

具体操作如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> create table hisen.employee as(
-> select * from employees.employees
-> );
Query OK, 300024 rows affected
Records: 300024 Duplicates: 0 Warnings: 0

mysql> use hisen;
Database changed
mysql> select count(*) from employee;
+----------+
| count(*) |
+----------+
| 300024 |
+----------+
1 row in set

这种操作是针对所有字段都导出的情形,创建表跟插入数据合二为一

如果导出部分字段或者有其他限制条件写sql即可

这应该是最简单的方法~

]]>
- - - - - mysql - - - - - - - mysql - - - -
- - - - - Data URI scheme 利用base64字符串通过image标签显示图片 - - /20170512-Data%20URI%20scheme%20%E5%88%A9%E7%94%A8base64%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%80%9A%E8%BF%87image%E6%A0%87%E7%AD%BE%E6%98%BE%E7%A4%BA%E5%9B%BE%E7%89%87/ - - 目前,Data URI scheme支持的类型有:

  1. data:,文本数据
  2. data:text/plain,文本数据
  3. data:text/html,HTML代码
  4. data:text/html;base64,base64编码的HTML代码
  5. data:text/css,CSS代码
  6. data:text/css;base64,base64编码的CSS代码
  7. data:text/javascript,Javascript代码
  8. data:text/javascript;base64,base64编码的Javascript代码
  9. data:image/gif;base64,base64编码的gif图片数据
  10. data:image/png;base64,base64编码的png图片数据
  11. data:image/jpeg;base64,base64编码的jpeg图片数据
  12. data:image/x-icon;base64,base64编码的icon图片数据

Data URL是在本地直接绘制图片,不是从服务器加载,所以节省了HTTP连接,起到加速网页的作用。

也无法获取到图片在服务器上的真实地址

注意:本方法适合于小图片,大图片就不要考虑了,另外IE8以下浏览器不支持这种方法。

用这种方法会加重客户端的CPU和内存负担,总之有利有弊。

前台代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ page import="com.hisen.image.ShowImageByBase64" %><%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2017/5/11
Time: 18:55
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% String imageStr = ShowImageByBase64.showimage();%>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="data:image/png;base64,<%=imageStr%>" alt="base64图片"/>
</body>
</html>

后台代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.hisen.image;

import com.hisen.utils.Base64Util;
import com.hisen.utils.File2ByteArraysUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import sun.misc.BASE64Encoder;

/**
* <img src="data:image/png;base64,<%=imageStr%>" alt="base64image"/>
* Created by hisenyuan on 2017/5/11 at 18:44.
*/
public class ShowImageByBase64 {

public static String showimage() {
//写相对路径会报错,暂时不知道如何解决
String imagePath = "C:\\1\\830.jpg";
byte[] bytes = File2ByteArraysUtil.file2Bytes(imagePath);
String s = Base64Util.encodeBase64(bytes);
return s;
}

/**
* sun.misc.BASE64Encoder
*/
public static String encodeBase64(byte[] str) {
if (str == null) {
return null;
} else {
BASE64Encoder encoder = new BASE64Encoder();
try {
return encoder.encode(str);
} catch (Exception var3) {
return null;
}
}
}

/***
* file2byte[]
* @param path
* @return
*/
public static byte[] file2Bytes(String path) {
byte[] buffer = null;
File file = new File(path);
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
byte[] b = new byte[1000];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
}

]]>
- - - - - java - - - - - - - java - - jsp - - - -
- - - - - 笔记本在BIOS Setup里面设置双显卡模式 - - /20170509-%E7%AC%94%E8%AE%B0%E6%9C%AC%E5%9C%A8BIOS%20Setup%E9%87%8C%E9%9D%A2%E8%AE%BE%E7%BD%AE%E5%8F%8C%E6%98%BE%E5%8D%A1%E6%A8%A1%E5%BC%8F/ - - 我的本子是双显卡的,英特尔的核芯显卡和英伟达的独显

但是之前完全没有把独显用上,在英伟达的设置里选择使用显卡感觉也没有用

后来找到在BIOS里面设置,貌似管用,联想的机子可以看看

其他的机子应该也差不多

连接:http://iknow.lenovo.com/detail/dc_102471.html

1
2
3
6. IdeaPad Z380/Z480/Z580/U310/U410,Lenovo G480A/V370A/V470A/V570A
依次选择“Configuration”、“Graphic Device”,其中有两个选项:Optimus表示可切换显卡模式;
UMA Only表示集显模式。选择好后,按F10并根据提示保存退出即可。

我的默认居然是:UMA Only

]]>
- - - - - 软件 - - - - - - - 软件 - - - -
- - - - - Linux安装tree命令 - apt-get install tree - - /20170503-Linux%E5%AE%89%E8%A3%85tree%E5%91%BD%E4%BB%A4%20-%20apt-get%20install%20tree/ - - 目前debian系列的系统都无法使用apt-get install tree来安装tree命令

以下是安装步骤

1
2
3
4
5
6
7
8
9
#下载(官网为:http://mama.indstate.edu/users/ice/tree/)
wget http://mama.indstate.edu/users/ice/tree/src/tree-1.7.0.tgz
#解压
tar -zxvf tree-1.7.0.tgz
#进入目录
cd tree-1.7.0
#安装
sudo make install
#完毕

最简单的使用方法,在目录下输入:tree

使用效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
hisen@ubuntu:~/dl$ tree
.
├── hisen.log
├── master
└── test_db-master
├── Changelog
├── employees_partitioned_5.1.sql
├── employees_partitioned.sql
├── employees.sql
├── images
│   ├── employees.gif
│   ├── employees.jpg
│   └── employees.png
├── load_departments.dump
├── load_dept_emp.dump
├── load_dept_manager.dump
├── load_employees.dump
├── load_salaries1.dump
├── load_salaries2.dump
├── load_salaries3.dump
├── load_titles.dump
├── objects.sql
├── README.md
├── sakila
│   ├── README.md
│   ├── sakila-mv-data.sql
│   └── sakila-mv-schema.sql
├── show_elapsed.sql
├── sql_test.sh
├── test_employees_md5.sql
└── test_employees_sha.sql

3 directories, 26 files

使用参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
tree命令行参数:

-a 显示所有文件和目录。
-A 使用ASNI绘图字符显示树状图而非以ASCII字符组合。
-C 在文件和目录清单加上色彩,便于区分各种类型。
-d 显示目录名称而非内容。
-D 列出文件或目录的更改时间。
-f 在每个文件或目录之前,显示完整的相对路径名称。
-F 在执行文件,目录,Socket,符号连接,管道名称名称,各自加上"*","/","=","@","|"号。
-g 列出文件或目录的所属群组名称,没有对应的名称时,则显示群组识别码。
-i 不以阶梯状列出文件或目录名称。
-I 不显示符合范本样式的文件或目录名称。
-l 如遇到性质为符号连接的目录,直接列出该连接所指向的原始目录。
-n 不在文件和目录清单加上色彩。
-N 直接列出文件和目录名称,包括控制字符。
-p 列出权限标示。
-P 只显示符合范本样式的文件或目录名称。
-q 用"?"号取代控制字符,列出文件和目录名称。
-s 列出文件或目录大小。
-t 用文件和目录的更改时间排序。
-u 列出文件或目录的拥有者名称,没有对应的名称时,则显示用户识别码。
-x 将范围局限在现行的文件系统中,若指定目录下的某些子目录,其存放于另一个文件系统上,则将该子目录予以排除在寻找范围外。

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - github api - github api中文说明 - - /20170428-github%20api%20-%20github%20api%E4%B8%AD%E6%96%87%E8%AF%B4%E6%98%8E/ - - github api的网址

1
https://api.github.com/

这里介绍两个api

1
2
#获取个人信息
https://api.github.com/users/{user};

key含义value
login登录名称hisen-yuan
id数字编号16789019
avatar_url头像地址https://avatars1.githubusercontent.com/u/16789019?v=3
name用户昵称hisenyuan
blog博客地址http://hisen.me
location地理位置China
bio个人说明Java R & D
public_repos仓库个数11
created_at创建时间2016-01-20 01:57:15Z
updated_at最后更新2017-04-20 14:03:27Z
1
2
#获取项目信息
https://api.github.com/users/{user}/repos
key含义value
id项目编号88646378
name项目名称dubbo
html_url项目地址https://github.com/hisen-yuan/dubbo
created_at创建时间2017-04-18T16:21:57Z
updated_at更新时间2017-04-18T16:23:16Z
pushed_at提交时间2017-04-19T02:33:33Z
size项目大小6514
language编程语言Java

github提供的所有api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
current_user_url: "https://api.github.com/user",
current_user_authorizations_html_url: "https://github.com/settings/connections/applications{/client_id}",
authorizations_url: "https://api.github.com/authorizations",
code_search_url: "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
commit_search_url: "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
emails_url: "https://api.github.com/user/emails",
emojis_url: "https://api.github.com/emojis",
events_url: "https://api.github.com/events",
feeds_url: "https://api.github.com/feeds",
followers_url: "https://api.github.com/user/followers",
following_url: "https://api.github.com/user/following{/target}",
gists_url: "https://api.github.com/gists{/gist_id}",
hub_url: "https://api.github.com/hub",
issue_search_url: "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
issues_url: "https://api.github.com/issues",
keys_url: "https://api.github.com/user/keys",
notifications_url: "https://api.github.com/notifications",
organization_repositories_url: "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
organization_url: "https://api.github.com/orgs/{org}",
public_gists_url: "https://api.github.com/gists/public",
rate_limit_url: "https://api.github.com/rate_limit",
repository_url: "https://api.github.com/repos/{owner}/{repo}",
repository_search_url: "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
current_user_repositories_url: "https://api.github.com/user/repos{?type,page,per_page,sort}",
starred_url: "https://api.github.com/user/starred{/owner}{/repo}",
starred_gists_url: "https://api.github.com/gists/starred",
team_url: "https://api.github.com/teams",
user_url: "https://api.github.com/users/{user}",
user_organizations_url: "https://api.github.com/user/orgs",
user_repositories_url: "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
user_search_url: "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"

]]>
- - - -
- - - - - Java获取股票信息 - java获取股票信息接口 - - /20170427-Java%E8%8E%B7%E5%8F%96%E8%82%A1%E7%A5%A8%E4%BF%A1%E6%81%AF%20-%20java%E8%8E%B7%E5%8F%96%E8%82%A1%E7%A5%A8%E4%BF%A1%E6%81%AF%E6%8E%A5%E5%8F%A3/ - - 用下面的接口获取股票的数据

sh:代表上海市场

sz:代表深圳市场

后面是加上股票代码,这是因为上海和深圳的股票代码有重复的

1
http://hq.sinajs.cn/list=sh600877

返回的信息

1
var hq_str_sh600877="中国嘉陵,6.340,6.400,6.360,6.470,6.210,6.340,6.350,15012913,95227966.000,56500,6.340,12100,6.330,16100,6.320,17500,6.310,47400,6.300,13600,6.350,11300,6.360,32400,6.370,39100,6.380,41200,6.390,2017-04-27,15:00:00,00";

有效信息为引号里面的数据

下面的数字代表分割数组后所在的下标

下面是数据字段对应的含义

源代码:GetStockInformation.java

表格如下:

位置含义测试数据
0股票名字中国嘉陵
1今日开盘价6.340
2昨日收盘价6.400
3当前价格6.360
4今日最高价6.470
5今日最低价6.210
6买一报价6.340
7卖一报价6.350
8成交数量(百股)15012913
9成交金额(元)95227966.000
10买一数量(股)56500
11买一报价6.340
12买二数量(股)12100
13买二报价6.330
14买三数量(股)16100
15买三报价6.320
16买四数量(股)17500
17买四报价6.310
18买五数量(股)47400
19买五报价6.300
20卖一数量(股)13600
21卖一报价6.350
22卖二数量(股)11300
23卖二报价6.360
24卖三数量(股)32400
25卖三报价6.370
26卖四数量(股)39100
27卖四报价6.380
28卖五数量(股)41200
29卖五报价6.390
30当前日期2017-04-27
31当前时间15:00:00
32未知00
]]>
- - - - - java - - - - - - - java - - - -
- - - - - MySQL decode - MySQL中类似oracle的decode实现方法 - - /20170427-MySQL%20decode%20-%20MySQL%E4%B8%AD%E7%B1%BB%E4%BC%BCoracle%E7%9A%84decode%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95/ - - 在oracle中直接有decode函数 decode(cola,null,0)

表示如果cola为空,赋值为0

在mysql中的具体实现如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
mysql> describe book;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| book_id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| number | int(11) | NO | | NULL | |
+---------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

mysql> select * from book;
+---------+-------------------------+--------+
| book_id | name | number |
+---------+-------------------------+--------+
| 123 | 123 | 122 |
| 1000 | Java程序设计 | 5 |
| 1001 | 数据结构 | 9 |
| 1002 | 设计模式 | 10 |
| 1003 | 编译原理 | 10 |
| 1004 | MySQL从删库到跑路 | 100 |
| 1005 | 活着 | 10 |
| 1232 | 11111 | 124 |
| 2001 | 测试 | 2001 |
| 10064 | 老鼠爱大米 | 10088 |
| 10066 | 老鼠爱大米 | 1008 |
| 10088 | 测试宝典 | 1008 |
| 10096 | maven实战 | 10096 |
| 11111 | 111111 | 11111 |
| 12311 | 都懂得 | 1222 |
| 123222 | 222 | 1111 |
+---------+-------------------------+--------+
16 rows in set (0.00 sec)

mysql> select if(count(b.book_id)=16,"十六","不是十六") '结果' from book b;
+--------+
| 结果 |
+--------+
| 十六 |
+--------+
1 row in set (0.00 sec)

mysql> select case count(b.book_id) when 16 then '十六' else '其他' end as '结果' from book b;
+--------+
| 结果 |
+--------+
| 十六 |
+--------+
1 row in set (0.00 sec)

]]>
- - - - - mysql - - - - - - - mysql - - sql - - - -
- - - - - Java原生类库java.util.zip - 文件夹压缩与解压 - - /20170426-Java%E5%8E%9F%E7%94%9F%E7%B1%BB%E5%BA%93java.util.zip%20-%20%E6%96%87%E4%BB%B6%E5%A4%B9%E5%8E%8B%E7%BC%A9%E4%B8%8E%E8%A7%A3%E5%8E%8B/ - - 到处搜了一下也没有看到专门做好的jar包

真实的目录结构如下:

1
2
3
4
5
6
7
C:\1\hisenyuan\build.png
C:\1\hisenyuan\DSCN6812.JPG
C:\1\hisenyuan\test\test\hello.zip
C:\1\hisenyuan\test\hisenyuan.zip
C:\1\hisenyuan\test\test.txt
C:\1\hisenyuan\test\test\hello\hello.txt
C:\1\hisenyuan\tomcat.png

压缩包的目录结构如下:

1
2
3
4
5
6
7
build.png
DSCN6812.JPG
test\hello.zip
test\hisenyuan.zip
test\test.txt
test\hello\hello.txt
tomcat.png

全部代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package com.hisen.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

/**
* 文件夹压缩解压工具
* Created by hisenyuan on 2017/4/20 at 17:27.
*/
public class ZipOrUnZipFileUtil {

private InputStream is;
private ZipOutputStream zos;
private int lastIndexOf;

@Test
public void testZipOrUnZipFile() {
//分隔符,windows linux下有所不同
String separator = File.separator;
//想要压缩的文件所在目录 C:\1\hisenyuan
String folderPath = "c:" + separator + "1" + separator + "hisenyuan";
//压缩文件路径:C:\1\hisenyuan\hisenyuan.zip
String zipFilePath = "c:" + separator + "1" + separator + "hisenyuan" + ".zip";
//解压文件所在的目录 E:\file\hisenyuan
String newPath = "e:" + separator + "file" + separator + "hisenyuan";

unZipFile(zipFilePath, newPath);
zipFile(folderPath);
}


/**
* 压缩文件
*
* @param filePath 压缩文件夹的路径
*/
private void zipFile(String filePath) {
File file = new File(filePath);
File zipFile = new File(filePath + ".zip");
lastIndexOf = file.getAbsolutePath().length() + 1;
try {
zos = new ZipOutputStream(new FileOutputStream(zipFile));
zos.setComment("log");
long start = System.currentTimeMillis();
listAllFile(filePath);
long stop = System.currentTimeMillis();
System.out.println("zip done,time:" + (stop - start) / 1000 + "s");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(zos, is);
}
}

/**
* 循环遍历当前文件夹下的所有文件,使用递归
*/
private void listAllFile(String filePath) {
File file = new File(filePath);
if (file.exists()) {
File[] files = file.listFiles();
if (files == null) {
System.out.println("folder is null");
} else {
for (File file2 : files) {
if (file2.isDirectory()) {
listAllFile(file2.getAbsolutePath());
} else {
String file3 = file2.getAbsolutePath();
try {
is = new FileInputStream(file3);
//在zip压缩包当中出现的文件名
String name = file3.substring(lastIndexOf, file3.length());
System.out.println("name:" + name);
zos.putNextEntry(new ZipEntry(name));
int temp;
int bufferSize = 1024 * 5;
byte[] buffer = new byte[bufferSize];
while ((temp = is.read(buffer, 0, bufferSize)) != -1) {
zos.write(buffer, 0, temp);
zos.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}

/**
* 解压文件
*
* @param filePath 压缩文件所在目录
* @param newPath 想解压到那个目录
*/
private void unZipFile(String filePath, String newPath) {
//压缩文件所在的父目录
String oldPath = new File(filePath).getParentFile().toString();
File outFile;
ZipInputStream zipInputStream = null;
OutputStream outputStream = null;
InputStream inputStream = null;
ZipEntry zipEntry;
try {
ZipFile zipFile = new ZipFile(filePath);

zipInputStream = new ZipInputStream(new FileInputStream(filePath));
while (null != (zipEntry = zipInputStream.getNextEntry())) {
System.out.println("解压缩" + zipEntry.getName() + "文件。");
//newPath为空就代表解压在当前目录
if ("".equals(newPath) || newPath.isEmpty()) {
outFile = new File(oldPath + zipEntry.getName());
} else {
//防止传入的目录不存在
File file = new File(newPath);
if (!file.exists()) {
file.mkdir();
}
outFile = new File(newPath + File.separator + zipEntry.getName());
}
//判断当前文件路径是否存在,不存在就创建
buildFile(outFile);
inputStream = zipFile.getInputStream(zipEntry);
outputStream = new FileOutputStream(outFile);
int temp;
int bufferSize = 1024 * 5;
byte[] buffer = new byte[bufferSize];
while ((temp = inputStream.read(buffer, 0, bufferSize)) != -1) {
outputStream.write(buffer, 0, temp);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(inputStream, outputStream, zipInputStream);
}
}

/**
* 判断文件是否存在,不存在创建
*/
private static void buildFile(File file) {
if (!file.exists()) {
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

]]>
- - - - - java - - - - - - - java - - 解压 - - - -
- - - - - zookeeper & dubbo搭建 - 在IDEA上运行阿里巴巴Dubbo-demo - - /20170419-zookeeper%20&%20dubbo%E6%90%AD%E5%BB%BA%20-%20%E5%9C%A8IDEA%E4%B8%8A%E8%BF%90%E8%A1%8C%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Dubbo-demo/ - -

IDEA上搭建dubbo服务的简单过程

只是简单的让例子在IntelliJ IDEA跑起来

目前是最新的版本:2.5.4-SNAPSHOT

本文档更新时间:2017年04月19日01:08:02

一 、安装zookeeper

参考链接:ubuntu apt-get安装zookeeper

二、Idea clone本项目

导出项目之后,配置一下tomcat,添加dubbo-admin:war到tomcat中

项目github地址:https://github.com/hisen-yuan/dubbo

三、启动tomcat,即可访问dubbo管理后台

默认账号:root

默认密码:root

四、启动服务提供者&消费者demo

  1. 修改dubbo-demo-consumer配置文件中的注册中心地址
    1
    /dubbo/dubbo-demo/dubbo-demo-consumer/src/test/resources/dubbo.properties
1
2
3
#dubbo.registry.address=multicast://224.5.6.7:1234
#使用本地的zookeeper做注册中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
  1. 修改ubbo-demo-provider配置文件中的注册中心地址
    1
    /dubbo/dubbo-demo/dubbo-demo-provider/src/test/resources/dubbo.properties
1
2
3
#dubbo.registry.address=multicast://224.5.6.7:1234
#使用本地的zookeeper做注册中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
  1. 分别启动dubbo-demo下ubbo-demo-provider、dubbo-demo-consumer下的测试方法

即可在后台看到有服务在运行

]]>
- - - - - java - - - - - - - java - - zookeeper - - dubbo - - - -
- - - - - MySQL一个group by查询出最大值和非group by所在字段的值 - - /20170418-MySQL%E4%B8%80%E4%B8%AAgroup%20by%E6%9F%A5%E8%AF%A2%E5%87%BA%E6%9C%80%E5%A4%A7%E5%80%BC%E5%92%8C%E9%9D%9Egroup%20by%E6%89%80%E5%9C%A8%E5%AD%97%E6%AE%B5%E7%9A%84%E5%80%BC/ - - 表结构,数据内容如下。

需求是:查找出每个年级,年纪最大的人的名字。

个人的思维只停留在

1
2
3
4
5
6
7
8
9
10
11
mysql> select name, max(age),grade from  stu group by grade;
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'hisen.stu.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

mysql> SELECT A.* FROM stu A INNER JOIN (SELECT MAX(AGE) AS MAX_AGE,GRADE FROM stu GROUP BY GRADE ) B ON A.GRADE = B.GRADE AND A.AGE= B.MAX_AGE;
+----+------+------+-------+
| id | name | age | grade |
+----+------+------+-------+
| 3 | c | 13 | 1 |
| 6 | f | 13 | 2 |
+----+------+------+-------+
2 rows in set (0.00 sec)

折腾了一会自己不知道怎么解决,后来倒是解决了。

具体过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
mysql> describe stu;
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | char(1) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| grade | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> select * from stu;
+----+------+------+-------+
| id | name | age | grade |
+----+------+------+-------+
| 1 | a | 11 | 1 |
| 2 | b | 12 | 1 |
| 3 | c | 13 | 1 |
| 4 | d | 11 | 2 |
| 5 | e | 12 | 2 |
| 6 | f | 13 | 2 |
+----+------+------+-------+
6 rows in set (0.00 sec)

mysql> select max(name), max(age),grade from stu group by grade;
+-----------+----------+-------+
| max(name) | max(age) | grade |
+-----------+----------+-------+
| c | 13 | 1 |
| f | 13 | 2 |
+-----------+----------+-------+
2 rows in set (0.01 sec)

]]>
- - - - - mysql - - - - - - - mysql - - - -
- - - - - Docker一键安装脚本Ubuntu-Debian-CentOS-Fedora-racleLinux - - /20170417-Docker%E4%B8%80%E4%BB%B6%E5%AE%89%E8%A3%85%E8%84%9A%E6%9C%ACUbuntu-Debian-CentOS-Fedora-racleLinux/ - - 系统要求
Ubuntu 14.04、16.04

Debian 7.7、8.0

CentOS 7.X

Fedora 20、21、22

OracleLinux 6、7

安装方法:

1
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

详情:http://mirrors.aliyun.com/help/docker-engine

]]>
- - - - - linux - - - - - - - docker - - - -
- - - - - ubuntu安装docker-ce并配置国内源和加速器 - - /20170417-ubuntu%E5%AE%89%E8%A3%85docker-ce%E5%B9%B6%E9%85%8D%E7%BD%AE%E5%9B%BD%E5%86%85%E6%BA%90%E5%92%8C%E5%8A%A0%E9%80%9F%E5%99%A8/ - - 一、配置ubuntu国内镜像,这里推荐阿里云,右上角搜索:换阿里云源

二、安装docker

1
2
3
4
sudo apt-get update
sudo apt-get install \
linux-image-extra-$(uname -r) \
linux-image-extra-virtual

安装docker包

1
2
3
4
5
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common

添加docker官方GPG秘钥,留意最后那个符号也要复制

1
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

安装稳定版仓库

1
2
3
4
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"

再次更新源

1
sudo apt-get update

安装docker-ce

1
sudo apt-get install docker-ce

三、给docker添加国内加速器

在阿里云申请一个账号,打开连接https://cr.console.aliyun.com/#/accelerator

拷贝您的专属加速器地址(每个人专属的,登陆需要密码),然后

1
vi /etc/systemd/system/multi-user.target.wants/docker.service

可以看到如下内容

1
2
3
4
5
6
7
8
9
10
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
#下面这行是默认的,我注释了,添加了下面一行
#ExecStart=/usr/bin/dockerd -H fd://
ExecStart=/usr/bin/dockerd -H fd:// --registry-mirror=https://9s3ekxxx.mirror.aliyuncs.com
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=1048576

找到 ExecStart= 这一行,在这行最后添加加速器地址 –registry-mirror=<加速器地址>

如:ExecStart=/usr/bin/dockerd -H fd:// –registry-mirror=https://xxxxxx.mirror.aliyuncs.com

四、重新加载配置并且重新启动

1
2
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

至此docker安装及国内加速器都好了,开始你的docker之旅吧。

1
sudo docker run hello-world

看到如下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
hisen@ubuntu:/$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://cloud.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/

到此就圆满结束

最后给个彩蛋,阿里云一键安装脚本,执行下面命令即可安装最新版docker

1
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

详情:http://mirrors.aliyun.com/help/docker-engine

]]>
- - - - - linux - - - - - - - docker - - ubuntu - - - -
- - - - - html - 原生javascript动态显示当前时间和日期 - - /20170412-html%20-%20%E5%8E%9F%E7%94%9Fjavascript%E5%8A%A8%E6%80%81%E6%98%BE%E7%A4%BA%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E5%92%8C%E6%97%A5%E6%9C%9F/ - - 显示当前时间:

2017年04月12日 18:26:37 星期三

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>显示当前时间 - 原生javascript</title>
<script type="text/javascript" language="javascript">
function show_cur_times(){
//获取当前日期
var date_time = new Date();
//定义星期
var week;
//switch判断
switch (date_time.getDay()){
case 1: week="星期一"; break;
case 2: week="星期二"; break;
case 3: week="星期三"; break;
case 4: week="星期四"; break;
case 5: week="星期五"; break;
case 6: week="星期六"; break;
default:week="星期天"; break;
}
//年
var year = date_time.getFullYear();
//判断小于10,前面补0
if(year<10){
year="0"+year;
}
//月
var month = date_time.getMonth()+1;
//判断小于10,前面补0
if(month<10){
month="0"+month;
}
//日
var day = date_time.getDate();
//判断小于10,前面补0
if(day<10){
day="0"+day;
}
//时
var hours =date_time.getHours();
//判断小于10,前面补0
if(hours<10){
hours="0"+hours;
}
//分
var minutes =date_time.getMinutes();
//判断小于10,前面补0
if(minutes<10){
minutes="0"+minutes;
}
//秒
var seconds=date_time.getSeconds();
//判断小于10,前面补0
if(seconds<10){
seconds="0"+seconds;
}
//拼接年月日时分秒
var date_str = year+"年"+month+"月"+day+"日 "+hours+":"+minutes+":"+seconds+" "+week;
//显示在id为showtimes的容器里
document.getElementById("showtimes").innerHTML= date_str;
}
//设置1秒调用一次show_cur_times函数
setInterval("show_cur_times()",100);
</script>
</head>
<body>
显示当前时间:<p id="showtimes"></p>
</body>
</html>

]]>
- - - -
- - - - - Maven镜像库设置 - maven mirrors - maven 镜像 阿里 - - /20170412-Maven%E9%95%9C%E5%83%8F%E5%BA%93%E8%AE%BE%E7%BD%AE%20-%20maven%20mirrors%20-%20maven%20%E9%95%9C%E5%83%8F%20%E9%98%BF%E9%87%8C/ - - 配置文件:

1
yourPath\maven-3.3.9\conf\settings.xml

找到里面的,添加镜像即可

1
2
<mirrors>
</mirrors>

这里写的是被镜像的ID

如果写成:* (星号)

所有的请求都会到这个镜像上,包括各种本地库

注意:千万不要配成*

否则内网的仓库或者你配的镜像里面没有一下jar包的时候不会去别的地方搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--阿里云:速度挺快-->
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

<!--谷歌:北京速度不错-->
<mirror>
<id>google-maven-central</id>
<name>Google Maven Central</name>
<url>https://maven-central.storage.googleapis.com</url>
<mirrorOf>central</mirrorOf>
</mirror>

]]>
- - - - - maven - - - - - - - maven - - - -
- - - - - Oracle 分页的三种方式 - - /20170411-Oracle%E5%88%86%E9%A1%B5%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F/ - - 第一种:

1
2
3
4
5
select *
from (select t.*, rownum rn
from (select * from EW_AUTH_FLOW) t
where rownum <= 5)
where rn > 2;

第二种:

1
2
3
select *
from (select t.*, rownum rn from ew_auth_flow t where rownum <= 5)
where rn > 2;

第三种:

1
2
3
select *
from (select t.*, rownum rn from ew_auth_flow t)
where rn between 2 and 5;

]]>
- - - - - database - - - - - - - sql - - - -
- - - - - Jedis - Software caused connection abort:recv failed - - /20170411-Jedis%20-%20Software%20caused%20connection%20abort%20recv%20failed/ - - 在使用jedis的时候出现这个问题:

redis.clients.jedis.exceptions.JedisConnectionException:java.net.SocketException:Software caused connection abort: recv failed

我是windows上java运行,然后redis是在虚拟机的,通过映射访问

解决办法:

编辑redis配置文件:

1
sudo vi /etc/redis/redis.conf

找到

1
bind 127.0.0.1

改成

1
bind 0.0.0.0

改完之后重启redis

1
service redis restart

即可。这跟mysql一样,允许任何ip连接!

]]>
- - - - - java - - - - - - - jedis - - redis - - - -
- - - - - Mysql Employees Database练习题&答案 - - /20170410-Mysql%20Employees%20%20Database%E7%BB%83%E4%B9%A0%E9%A2%98&%E7%AD%94%E6%A1%88/ - - Employees数据库是mysql官方提供的一个测试用数据库

里面含有几十万条数据。

找了好久也没有找到比较匹配的题目

就找了个匹配度比较高的题来练习,如果你还没有导入Employees Sample Database

请参考:点击导入Employees

本次操作在Xshell中完成,也就是mysql命令行。

简单的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#登陆数据库
hisen@ubuntu:/$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 5.7.17-0ubuntu0.16.04.2 (Ubuntu)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| employees |
| log4j |
| mysql |
| performance_schema |
| ssm |
| sys |
+--------------------+
7 rows in set (0.00 sec)

mysql> use employees
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+----------------------+
| Tables_in_employees |
+----------------------+
| current_dept_emp |
| departments |
| dept_emp |
| dept_emp_latest_date |
| dept_manager |
| employees |
| salaries |
| titles |
+----------------------+
8 rows in set (0.00 sec)

mysql> select * from employees limit 10;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
| 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 |
| 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 |
| 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
| 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 |
| 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 |
| 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 |
| 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 |
| 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 |
| 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 |
+--------+------------+------------+-----------+--------+------------+
10 rows in set (0.00 sec)
#统计各部门曾经拥有的员工数量
mysql> select dept_no,count(*) emp_sum
-> from dept_emp
-> group by dept_no
-> order by emp_sum desc;
+---------+---------+
| dept_no | emp_sum |
+---------+---------+
| d005 | 85707 |
| d004 | 73485 |
| d007 | 52245 |
| d009 | 23580 |
| d008 | 21126 |
| d001 | 20211 |
| d006 | 20117 |
| d003 | 17786 |
| d002 | 17346 |
+---------+---------+
9 rows in set (0.63 sec)
#创建视图
mysql> CREATE VIEW test AS
-> SELECT dept_no, COUNT(*) AS emp_sum
-> FROM dept_emp
-> GROUP BY dept_no
-> ORDER BY emp_sum DESC
-> ;
Query OK, 0 rows affected (0.16 sec)
mysql> select * from test;
+---------+---------+
| dept_no | emp_sum |
+---------+---------+
| d005 | 85707 |
| d004 | 73485 |
| d007 | 52245 |
| d009 | 23580 |
| d008 | 21126 |
| d001 | 20211 |
| d006 | 20117 |
| d003 | 17786 |
| d002 | 17346 |
+---------+---------+
9 rows in set (0.11 sec)
#联合查询,加上部门名称
mysql> select test.dept_no,emp_sum,dept_name
-> from test,departments
-> where test.dept_no = departments.dept_no;
+---------+---------+--------------------+
| dept_no | emp_sum | dept_name |
+---------+---------+--------------------+
| d009 | 23580 | Customer Service |
| d005 | 85707 | Development |
| d002 | 17346 | Finance |
| d003 | 17786 | Human Resources |
| d001 | 20211 | Marketing |
| d004 | 73485 | Production |
| d006 | 20117 | Quality Management |
| d008 | 21126 | Research |
| d007 | 52245 | Sales |
+---------+---------+--------------------+
9 rows in set (0.16 sec)

练习题目和答案:

建议看看输出的结果自己写下sql,不要单纯的复制粘贴。

因为数据量比较大, 很多时候我都加了5条数据的限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#1.查找整个职员表的所有内容。
mysql> select * from employees limit 5;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
| 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 |
| 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 |
| 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
| 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 |
+--------+------------+------------+-----------+--------+------------+
5 rows in set (0.00 sec)

#2.查看雇员名字(last_name)。
mysql> select last_name from employees limit 5;
+-----------+
| last_name |
+-----------+
| Facello |
| Simmel |
| Bamford |
| Koblick |
| Maliniak |
+-----------+
5 rows in set (0.00 sec)

#3.查看雇员编号、名字和部门
mysql> select e.last_name,e.emp_no,d.dept_name from employees e,departments d,dept_emp de where e.emp_no = de.emp_no and de.dept_no = d.dept_no limit 5;
+-------------+--------+------------------+
| last_name | emp_no | dept_name |
+-------------+--------+------------------+
| Sluis | 10011 | Customer Service |
| Lortz | 10038 | Customer Service |
| Tramer | 10049 | Customer Service |
| Billingsley | 10060 | Customer Service |
| Syrzycki | 10088 | Customer Service |
+-------------+--------+------------------+
5 rows in set (0.00 sec)

#4.显示所有雇员的工号、姓名、工资
mysql> select e.emp_no,concat(e.last_name,' ',e.first_name) name,s.salary from employees e,salaries s where e.emp_no=s.emp_no limit 5;
+--------+----------------+--------+
| emp_no | name | salary |
+--------+----------------+--------+
| 10001 | Facello Georgi | 60117 |
| 10001 | Facello Georgi | 62102 |
| 10001 | Facello Georgi | 66074 |
| 10001 | Facello Georgi | 66596 |
| 10001 | Facello Georgi | 66961 |
+--------+----------------+--------+
5 rows in set (0.00 sec)

#5.查找在d005号部门工作的雇员
mysql> select e.emp_no,concat(e.last_name,' ',e.first_name) name,d.dept_no,d.dept_name from employees e,dept_emp de,departments d where e.emp_no = de.emp_no and de.dept_no = d.dept_no and d.dept_no = 'd005' limit 5;
+--------+--------------------+---------+-------------+
| emp_no | name | dept_no | dept_name |
+--------+--------------------+---------+-------------+
| 10001 | Facello Georgi | d005 | Development |
| 10006 | Preusig Anneke | d005 | Development |
| 10008 | Kalloufi Saniya | d005 | Development |
| 10012 | Bridgland Patricio | d005 | Development |
| 10014 | Genin Berni | d005 | Development |
+--------+--------------------+---------+-------------+
5 rows in set (0.05 sec)

#6.要求查找职位为Engineer和Senior Engineer的雇员姓名(last_name)
mysql> select e.last_name from employees e,titles t where t.emp_no=e.emp_no and t.title in('Engineer','Senior Engineer') limit 5;
+-----------+
| last_name |
+-----------+
| Facello |
| Bamford |
| Koblick |
| Koblick |
| Preusig |
+-----------+
5 rows in set (0.00 sec)

#7.查找职位不是Engineer和Senior Engineer的部门编号,雇员部门及姓名。将姓名显示为(first_name+last_name命名为”Name”)
mysql> select d.dept_no,d.dept_name,concat(e.first_name,' ',e.last_name) name from employees e,titles t,dept_emp de,departmentss d where e.emp_no=de.emp_no and de.dept_no = d.dept_no and t.emp_no = e.emp_no and t.title not in('Engineer','Senior Engineer'') limit 5;
+---------+------------------+--------------+
| dept_no | dept_name | name |
+---------+------------------+--------------+
| d009 | Customer Service | Mary Sluis |
| d009 | Customer Service | Huan Lortz |
| d009 | Customer Service | Huan Lortz |
| d009 | Customer Service | Basil Tramer |
| d009 | Customer Service | Basil Tramer |
+---------+------------------+--------------+
5 rows in set (0.00 sec)

#8.查找哪些雇员的工资在60000到90000之间
mysql> select concat(e.first_name,' ',e.last_name) name,s.salary from employees e,salaries s where s.salary between 60000 and 90000 and e.emp_no = s.emp_no limit 5;
+----------------+--------+
| name | salary |
+----------------+--------+
| Georgi Facello | 60117 |
| Georgi Facello | 62102 |
| Georgi Facello | 66074 |
| Georgi Facello | 66596 |
| Georgi Facello | 66961 |
+----------------+--------+
5 rows in set (0.00 sec)

#9.查找哪些雇员的工资不在60000到90000之间
mysql> select concat(e.first_name,' ',e.last_name) name,s.salary from employees e,salaries s where s.salary not between 60000 and 90000 and e.emp_no = s.emp_no limit 10;
+-------------------+--------+
| name | salary |
+-------------------+--------+
| Parto Bamford | 40006 |
| Parto Bamford | 43616 |
| Parto Bamford | 43466 |
| Parto Bamford | 43636 |
| Parto Bamford | 43478 |
| Parto Bamford | 43699 |
| Parto Bamford | 43311 |
| Chirstian Koblick | 40054 |
| Chirstian Koblick | 42283 |
| Chirstian Koblick | 42542 |
+-------------------+--------+
10 rows in set (0.00 sec)

#10.查找first_name以P开头,后面仅有四个字母的雇员信息
mysql> select e.emp_no,concat(e.first_name,' ',e.last_name) name from employees e where e.first_name like 'P____' and e.first_name not like 'p__' limit 5;
+--------+---------------------+
| emp_no | name |
+--------+---------------------+
| 10003 | Parto Bamford |
| 10101 | Perla Heyers |
| 10138 | Perry Shimshoni |
| 10353 | Phule Hammerschmidt |
| 10387 | Parto Wrigley |
+--------+---------------------+
5 rows in set (0.00 sec)

#11.查找last_name以K开头的雇员信息
mysql> select e.emp_no,concat(e.first_name,' ',e.last_name) name from employees e where e.first_name like 'K%' limit 5;
+--------+----------------------+
| emp_no | name |
+--------+----------------------+
| 10005 | Kyoichi Maliniak |
| 10016 | Kazuhito Cappelletti |
| 10018 | Kazuhide Peha |
| 10031 | Karsten Joslin |
| 10066 | Kwee Schusler |
+--------+----------------------+
5 rows in set (0.00 sec)

#12.查找名字以字母K开头,以i结尾,并且第三个字母为o的雇员名字(First_name)、职位和所在部门号
mysql> select concat(e.first_name,' ',e.last_name) name,t.title,de.dept_no from employees e,dept_emp de,titles t where e.first_name like 'K_o%i' and e.emp_no = t.emp_no and e.emp_no = de.emp_no limit 5;
+------------------+--------------+---------+
| name | title | dept_no |
+------------------+--------------+---------+
| Kyoichi Maliniak | Senior Staff | d003 |
| Kyoichi Maliniak | Staff | d003 |
| Kyoichi Wossner | Staff | d007 |
| Kyoichi Flexer | Senior Staff | d007 |
| Kyoichi Flexer | Staff | d007 |
+------------------+--------------+---------+
5 rows in set (0.00 sec)

#13.查找哪些雇员的职位名不以Se开头
mysql> select concat(e.first_name,' ',last_name) name,t.title from employees e,titles t where e.emp_no = t.emp_no and t.title not like 'Se%' limit 5;
+-------------------+--------------------+
| name | title |
+-------------------+--------------------+
| Bezalel Simmel | Staff |
| Chirstian Koblick | Engineer |
| Kyoichi Maliniak | Staff |
| Tzvetan Zielinski | Staff |
| Saniya Kalloufi | Assistant Engineer |
+-------------------+--------------------+
5 rows in set (0.00 sec)

#14.查找d005号部门里不是Staff的雇员信息
mysql> select concat(e.first_name,' ',last_name) name,t.title from employees e,dept_emp de,titles t where e.emp_no = de.emp_no and e.emp_no = t.emp_no and t.title != 'Staff' limit 5;
+-------------------+-----------------+
| name | title |
+-------------------+-----------------+
| Georgi Facello | Senior Engineer |
| Parto Bamford | Senior Engineer |
| Chirstian Koblick | Engineer |
| Chirstian Koblick | Senior Engineer |
| Kyoichi Maliniak | Senior Staff |
+-------------------+-----------------+
5 rows in set (0.00 sec)

#15.查找d005号部门工资大于100000的员工的信息
mysql> select concat(e.first_name,' ',last_name) name,t.title,s.salary from employees e,dept_emp de,titles t,salaries s where e.emp_no = de.emp_no and e.emp_no = t.emp_no and e.emp_no = s.emp_no and de.dept_no = 'd005' and s.salary > 100000 limit 5;
+---------------+--------------------+--------+
| name | title | salary |
+---------------+--------------------+--------+
| Kwee Schusler | Assistant Engineer | 102425 |
| Kwee Schusler | Assistant Engineer | 102674 |
| Kwee Schusler | Assistant Engineer | 103672 |
| Kwee Schusler | Engineer | 102425 |
| Kwee Schusler | Engineer | 102674 |
+---------------+--------------------+--------+
5 rows in set (0.00 sec)

#16.按字母顺序显示雇员的名字(last_name)
mysql> select concat(e.first_name,' ',last_name) name from employees e order by e.last_name limit 5;
+------------------+
| name |
+------------------+
| Aluzio Aamodt |
| Sachem Aamodt |
| Sreenivas Aamodt |
| Mokhtar Aamodt |
| Bartek Aamodt |
+------------------+
5 rows in set (0.24 sec)

#17.按部门编号降序显示雇员信息
mysql> select concat(e.first_name,' ',last_name) name,de.dept_no from employees e,dept_emp de where e.emp_no = de.emp_no order by de.dept_no desc limit 5;
+-------------------+---------+
| name | dept_no |
+-------------------+---------+
| Pohua Sichman | d009 |
| Uri Juneja | d009 |
| Mohammed Pleszkun | d009 |
| Chiranjit Kuzuoka | d009 |
| Ronghao Morrow | d009 |
+-------------------+---------+
5 rows in set (0.00 sec)

#18.计算每个部门的平均工资和工资总和
mysql> select de.dept_no,sum(s.salary),avg(s.salary) from employees e,dept_emp de,salaries s where e.emp_no = de.emp_no and e.emp_no = s.emp_no group by de.dept_no;
+---------+---------------+---------------+
| dept_no | sum(s.salary) | avg(s.salary) |
+---------+---------------+---------------+
| d001 | 13725425266 | 71913.2000 |
| d002 | 11650834677 | 70489.3649 |
| d003 | 9363811425 | 55574.8794 |
| d004 | 41554438942 | 59605.4825 |
| d005 | 48179456393 | 59478.9012 |
| d006 | 10865203635 | 57251.2719 |
| d007 | 40030089342 | 80667.6058 |
| d008 | 11969730427 | 59665.1817 |
| d009 | 13143639841 | 58770.3665 |
+---------+---------------+---------------+
9 rows in set (7.14 sec)

#19.查询每个部门的每个职位的雇员数
mysql> select de.dept_no,t.title,sum(e.emp_no) from employees e,dept_emp de,titles t where e.emp_no = de.emp_no and e.emp_no = t.emp_no group by de.dept_no,t.title limit 5;
+---------+--------------+---------------+
| dept_no | title | sum(e.emp_no) |
+---------+--------------+---------------+
| d001 | Manager | 220061 |
| d001 | Senior Staff | 3561178455 |
| d001 | Staff | 4142508539 |
| d002 | Manager | 220199 |
| d002 | Senior Staff | 3090249864 |
+---------+--------------+---------------+
5 rows in set (3.13 sec)

#20.请算出employees表中所有雇员的平均工资
mysql> select avg(s.salary) from employees e,salaries s where e.emp_no = s.emp_no;
+---------------+
| avg(s.salary) |
+---------------+
| 63810.7448 |
+---------------+
1 row in set (4.48 sec)

#21.请查询出employees表中的最低工资的员工信息
mysql> select concat(first_name,' ',last_name) name from employees e,salaries s where e.emp_no = s.emp_no and s.salary = (selecct min(s.salary) salary from salaries s);
+--------------+
| name |
+--------------+
| Olivera Baek |
+--------------+
1 row in set (1.99 sec)

#22.请计算出每个部门的平均工资、最高工资和最低工资
mysql> select de.dept_no,avg(s.salary),max(s.salary),min(s.salary) from employees e,dept_emp de,salaries s where e.emp_no = de.emp_no and e.emp_no = s.emp_no group by de.dept_no;
+---------+---------------+---------------+---------------+
| dept_no | avg(s.salary) | max(s.salary) | min(s.salary) |
+---------+---------------+---------------+---------------+
| d001 | 71913.2000 | 145128 | 39127 |
| d002 | 70489.3649 | 142395 | 38812 |
| d003 | 55574.8794 | 141953 | 38735 |
| d004 | 59605.4825 | 138273 | 38623 |
| d005 | 59478.9012 | 144434 | 38849 |
| d006 | 57251.2719 | 132103 | 38786 |
| d007 | 80667.6058 | 158220 | 39169 |
| d008 | 59665.1817 | 130211 | 38851 |
| d009 | 58770.3665 | 144866 | 38836 |
+---------+---------------+---------------+---------------+
9 rows in set (7.06 sec)

#23.查询薪水发放时间在1986-06-26 ~ 1987-06-25薪水高于46145号雇员并且工种与他相同的雇员情况。
select e.* from employees e,titles t,salaries s where e.emp_no = t.emp_no and e.emp_no = t.emp_no and t.title = (select title from titles where emp_no = 46135) and s.salary > (select s.salary from salaries s where s.from_date > str_to_date('1986-06-26','%Y-%m-%d') and s.to_date < str_to_date('1987-06-2 25','%Y-%m-%d') and s.emp_no = '46135') and s.from_date > str_to_date('1986-06-26','%Y-%m-%d') and s.to_date < str_to_date('1987-06-2 25','%Y-%m-%d') limit 10;
+--------+------------+------------+------------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+------------+--------+------------+
| 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
| 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 |
| 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 |
| 10012 | 1960-10-04 | Patricio | Bridgland | M | 1992-12-18 |
| 10014 | 1956-02-12 | Berni | Genin | M | 1987-03-11 |
| 10018 | 1954-06-19 | Kazuhide | Peha | F | 1987-04-03 |
| 10020 | 1952-12-24 | Mayuko | Warwick | M | 1991-01-26 |
| 10022 | 1952-07-08 | Shahaf | Famili | M | 1995-08-22 |
| 10023 | 1953-09-29 | Bojan | Montemayor | F | 1989-12-17 |
| 10026 | 1953-04-03 | Yongqiao | Berztiss | M | 1995-03-20 |
+--------+------------+------------+------------+--------+------------+
10 rows in set, 2 warnings (0.05 sec)


#24.查询工资在10000到50000之间的雇员所在部门的所有人员的信息。
mysql> select e.*,d.dept_no,d.dept_name from employees e,dept_emp de,departments d where e.emp_no = de.emp_no and de.dept_no =d.dept_no and e.emp_no in (select distinct s.emp_no from salaries s where s.salary between 10000 and 50000) limit 10;
+--------+------------+-------------+-----------+--------+------------+---------+------------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date | dept_no | dept_name |
+--------+------------+-------------+-----------+--------+------------+---------+------------------+
| 10011 | 1953-11-07 | Mary | Sluis | F | 1990-01-22 | d009 | Customer Service |
| 10038 | 1960-07-20 | Huan | Lortz | M | 1989-09-20 | d009 | Customer Service |
| 10049 | 1961-04-24 | Basil | Tramer | F | 1992-05-04 | d009 | Customer Service |
| 10098 | 1961-09-23 | Sreekrishna | Servieres | F | 1985-05-13 | d009 | Customer Service |
| 10112 | 1963-08-13 | Yuichiro | Swick | F | 1985-10-08 | d009 | Customer Service |
| 10115 | 1964-12-25 | Chikara | Rissland | M | 1986-01-23 | d009 | Customer Service |
| 10126 | 1954-08-07 | Kayoko | Valtorta | M | 1985-09-08 | d009 | Customer Service |
| 10128 | 1958-02-15 | Babette | Lamba | F | 1988-06-06 | d009 | Customer Service |
| 10137 | 1959-07-30 | Maren | Hutton | M | 1985-02-18 | d009 | Customer Service |
| 10154 | 1957-01-17 | Abdulah | Thibadeau | F | 1990-12-12 | d009 | Customer Service |
+--------+------------+-------------+-----------+--------+------------+---------+------------------+
10 rows in set (1.28 sec)

#25.查询出在薪水发放时间在1986-06-26 ~ 1987-06-25的员工信息
###(工号,姓名,性别,薪水,职位)
mysql> select e.emp_no,concat(e.first_name," ",e.last_name) name,e.gender,s.salary,t.title from employees e,salaries s,titles t where e.emp_no = s.emp_no and e.emp_no = t.emp_no and s.from_date > str_to_date('1986-06-26','%Y-%m-%d') and s.to_date < str_to_date('1987-06-2 25','%Y-%m-%d') limit 10;
+--------+-------------------+--------+--------+-----------------+
| emp_no | name | gender | salary | title |
+--------+-------------------+--------+--------+-----------------+
| 13543 | Tua Garigliano | F | 78646 | Staff |
| 13589 | Arfst Munck | F | 44306 | Staff |
| 14085 | Jiafu Constantine | F | 43575 | Staff |
| 14613 | Kien Herath | M | 54124 | Staff |
| 19083 | Nobuyoshi Asmuth | M | 40000 | Engineer |
| 19135 | Leaf Soicher | M | 40000 | Senior Engineer |
| 19761 | Vasiliy Niizuma | M | 65098 | Staff |
| 19769 | Bezalel Holburn | M | 53118 | Engineer |
| 22892 | Giao Monkewich | M | 79554 | Senior Staff |
| 24101 | Yakichi Manderick | F | 45092 | Engineer |
+--------+-------------------+--------+--------+-----------------+
10 rows in set, 1 warning (0.07 sec)

]]>
- - - - - sql - - - - - - - mysql - - - -
- - - - - Linux查看日志文件常用命令 - - /20170407-linux%E6%9F%A5%E7%9C%8B%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/ - - linux查看日志文件内容命令tail、cat、tac、head、echo

tail -f test.log

你会看到屏幕不断有内容被打印出来. 这时候中断第一个进程Ctrl-C,

cat -n hisen.log | grep ‘907’

在文件当中查找指定的内容,这里是查询:907


linux 如何显示一个文件的某几行(中间几行)

从第3000行开始,显示1000行。即显示3000~3999行

1
cat filename | tail -n +3000 | head -n 1000

显示1000行到3000行

1
cat filename| head -n 3000 | tail -n +1000

*注意两种方法的顺序
分解:

  1. tail -n 1000:显示最后1000行
  2. tail -n +1000:从1000行开始显示,显示1000行以后的
  3. head -n 1000:显示前面1000行

用sed命令
sed -n ‘5,10p’ filename 这样你就可以只查看文件的第5行到第10行。

例:cat mylog.log | tail -n 1000 #输出mylog.log 文件最后一千行


cat主要有三大功能:

1.一次显示整个文件。$ cat filename

2.从键盘创建一个文件。$ cat > filename

只能创建新文件,不能编辑已有文件.

3.将几个文件合并为一个文件: $cat file1 file2 > file

参数:

-n 或 –number 由 1 开始对所有输出的行数编号

-b 或 –number-nonblank 和 -n 相似,只不过对于空白行不编号

-s 或 –squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行

-v 或 –show-nonprinting

例:

把 textfile1 的档案内容加上行号后输入 textfile2 这个档案里

1
cat -n textfile1 > textfile2

把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3 里。

1
cat -b textfile1 textfile2 >> textfile3

把test.txt文件扔进垃圾箱,赋空值test.txt

1
cat /dev/null > /etc/test.txt

注意:>意思是创建,>>是追加。千万不要弄混了。


tac (反向列示)

tac 是将 cat 反写过来,所以他的功能就跟 cat 相反, cat 是由第一行到最后一行连续显示在萤幕上,

而 tac 则是由最后一行到第一行反向在萤幕上显示出来!


在Linux中echo命令用来在标准输出上显示一段字符,比如:
echo “the echo command test!”

这个就会输出“the echo command test!”这一行文字!

echo “the echo command test!”>a.sh

这个就会在a.sh文件中输出“the echo command test!”这一行文字!

该命令的一般格式为: echo [ -n ] 字符串其中选项n表示输出文字后不换行;字符串能加引号,也能不加引号。

用echo命令输出加引号的字符串时,将字符串原样输出;

用echo命令输出不加引号的字符串时,将字符串中的各个单词作为字符串输出,各字符串之间用一个空格分割。

一些实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#在整个文件搜索含有907的内容
hisen@ubuntu:~/dl$ cat -n hisen.log | grep '907'
25[2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Art
26[2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Dep

#从第3行开始,显示10行 即:3~12 并且显示行号
hisen@ubuntu:~/dl$ cat -n hisen.log | tail -n +3 | head -n 10
307-Apr-2017 15:48:50.072 信息 [main] org.apache.coyote.Abstrac
407-Apr-2017 15:48:50.137 信息 [main] Using a shared selector f
507-Apr-2017 15:48:50.172 测试 [main] Initializing ProtocolHand
607-Apr-2017 15:48:50.186 信息 [main] Using a shared selector f
707-Apr-2017 15:48:50.187 你猜 [main] load Initialization proce
807-Apr-2017 15:48:50.261 信息 [main] StandardService.startInte
907-Apr-2017 15:48:50.261 信息 [main] Servlet Engine: Apache To
1007-Apr-2017 15:48:50.293 信息 [main] Starting ProtocolHandler
1107-Apr-2017 15:48:50.318 哪里 [main] ProtocolHandler [ajp-nio-
1207-Apr-2017 15:48:50.328 信息 [main] start Server startup in 1
#显示10行前三行
hisen@ubuntu:~/dl$ cat -n hisen.log | head -n +10 | tail -n 3
807-Apr-2017 15:48:50.261 信息 [main] StandardService.startInte
907-Apr-2017 15:48:50.261 信息 [main] Servlet Engine: Apache To
1007-Apr-2017 15:48:50.293 信息 [main] Starting ProtocolHandler

#显示3-10行
hisen@ubuntu:~/dl$ cat -n hisen.log | head -n 10 | tail -n +3
307-Apr-2017 15:48:50.072 信息 [main] org.apache.coyote.Abstrac
407-Apr-2017 15:48:50.137 信息 [main] Using a shared selector f
507-Apr-2017 15:48:50.172 测试 [main] Initializing ProtocolHand
607-Apr-2017 15:48:50.186 信息 [main] Using a shared selector f
707-Apr-2017 15:48:50.187 你猜 [main] load Initialization proce
807-Apr-2017 15:48:50.261 信息 [main] StandardService.startInte
907-Apr-2017 15:48:50.261 信息 [main] Servlet Engine: Apache To
1007-Apr-2017 15:48:50.293 信息 [main] Starting ProtocolHandler

#显示倒数第二行
hisen@ubuntu:~/dl$ cat -n hisen.log | tail -n 2
25[2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Art
26[2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Dep

#从24行开始,显示到最后
hisen@ubuntu:~/dl$ cat -n hisen.log | tail -n +24
2407-Apr-2017 15:49:04.585 信息 during time.
25[2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Art
26[2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Dep
]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Hexo next主题添加本地搜索 - 不使用第三方服务 - - /20170407-Hexo%20next%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0%E6%9C%AC%E5%9C%B0%E6%90%9C%E7%B4%A2%20-%20%E4%B8%8D%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E6%9C%8D%E5%8A%A1/ - - 之前安装过第三方的搜索服务,贼蛋疼。都不免费了。

也有自己安装插件,然后写js的,麻烦

后来找到两个插件,安装之后就搞定了

感谢开发的作者!!!

安装插件

记得要在站点根目录执行下面的安装操作

1.安装 hexo-generator-search

1
npm install hexo-generator-searchdb --save

2.安装 hexo-generator-searchdb

1
npm install hexo-generator-searchdb --save

启用搜索

编辑站点文件_config.yml,添加以下内容开启搜索

1
2
3
4
5
search:
path: search.xml
field: post
format: html
limit: 10000

编辑主题文件_config.yml,启用本地搜索功能:

# Local searchlocal_search:  enable: true

效果预览

小缺点

第一次点击搜索的时候反应会比较慢

因为是要加载一个xml文件

]]>
- - - - - hexo - - - - - - - hexo - - - -
- - - - - DBeaver-连接全部数据库工具-DBeaver快捷键 - - /20170402-DBeaver-%E8%BF%9E%E6%8E%A5%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%B7%A5%E5%85%B7-DBeaver%E5%BF%AB%E6%8D%B7%E9%94%AE/ - - Windows、Linux、Mac OS X多个平台都可以用

据我观察这个数据库可视化工具很不错,基于java

以各种驱动来连接数据库,也就是说java支持的数据库都可以用他连接

挺好用的,免费!!!

安装之后新建连接,选择你要链接的数据库,配置一下就好了。

下载地址

官网下载地址

快捷键

1
2
3
4
5
6
#Shift + Home选中当前光标到行首
#Shift + End选中当前光标到行尾
#Shift + ↑/↓/←/→ 移动光标并且选中
#Alt + X 执行选中的sql
#Ctrl + Enter 执行当前光标所在行的sql
#Ctrl + Alt + F 格式化SQL(file -> properties -> +SQL Editor -> SQL formatting)
]]>
- - - - - sql - - - - - - - sql - - - -
- - - - - MySQL 数据库安装官方自带employees测试库 - - /20170401-MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%89%E8%A3%85%E5%AE%98%E6%96%B9%E8%87%AA%E5%B8%A6employees%E6%B5%8B%E8%AF%95%E8%A1%A8/ - - MySQL 官方是有一个自带的数据库,名为:Employees Sample Database

官网介绍:Employees Sample Database

表名中文
department部门表
dept_emp部门员工任职期表(按部门&时期)
dept_manager部门经理任职期表(按时期)
employees员工详情表
salaries员工薪资表(按时期)
title员工职称表(按时期)


导入的操作过程,在ubuntu上进行操作

一、导入数据库操作过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#ubuntu apt-get 安装的mysql默认的配置文件
#添加一行:default-storage-engine=InnoDB
hisen@ubuntu:/$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
#获取root权限
hisen@ubuntu:/var/lib$ su
Password:
#进数据库目录
root@ubuntu:# cd /var/lib/mysql
#删除两个文件
root@ubuntu:/var/lib/mysql# rm ib_logfile0
root@ubuntu:/var/lib/mysql# rm ib_logfile1
#重启数据库
root@ubuntu:/# service mysql restart
#下载Employees database
hisen@ubuntu:~/dl$ wget https://codeload.github.com/datacharmer/test_db/zip/master
#解压
hisen@ubuntu:~/dl$ unzip master
#查看
hisen@ubuntu:~/dl$ ll
total 35840
drwxrwxr-x 3 hisen hisen 4096 Apr 1 16:50 ./
drwxr-xr-x 10 hisen hisen 4096 Apr 1 16:40 ../
-rw-rw-r-- 1 hisen hisen 36687570 Apr 1 16:30 master
drwxrwxr-x 4 hisen hisen 4096 Oct 14 2015 test_db-master/
#进目录
hisen@ubuntu:~/dl$ cd test_db-master/
#导入数据库
hisen@ubuntu:~/dl/test_db-master$ mysql -u root -p < employees.sql
Enter password:
INFO
CREATING DATABASE STRUCTURE
INFO
storage engine: InnoDB
INFO
LOADING departments
INFO
LOADING employees
INFO
LOADING dept_emp
INFO
LOADING dept_manager
INFO
LOADING titles
INFO
LOADING salaries
data_load_time_diff
00:01:11
#导入成功
#验证是否导入成功
hisen@ubuntu:~/dl/test_db-master$ mysql -u root -p < test_employees_md5.sql
Enter password:
INFO
TESTING INSTALLATION
table_nameexpected_recordsexpected_crc
employees3000244ec56ab5ba37218d187cf6ab09ce1aa1
departments9d1af5e170d2d1591d776d5638d71fc5f
dept_manager248720e2f0853ac9096b689c14664f847e
dept_emp331603ccf6fe516f990bdaa49713fc478701b7
titles443308bfa016c472df68e70a03facafa1bc0a8
salaries2844047fd220654e95aea1b169624ffe3fca934
table_namefound_records found_crc
employees3000244ec56ab5ba37218d187cf6ab09ce1aa1
departments9d1af5e170d2d1591d776d5638d71fc5f
dept_manager248720e2f0853ac9096b689c14664f847e
dept_emp331603ccf6fe516f990bdaa49713fc478701b7
titles443308bfa016c472df68e70a03facafa1bc0a8
salaries2844047fd220654e95aea1b169624ffe3fca934
table_namerecords_matchcrc_match
employeesOKok
departmentsOKok
dept_managerOKok
dept_empOKok
titlesOKok
salariesOKok
computation_time
00:00:22
summaryresult
CRCOK
countOK

二、数据库练习

练习题点击查看练习题

]]>
- - - - - mysql - - - - - - - mysql - - - -
- - - - - su Authentication failure - - /20170401-su%20%20Authentication%20failure/ - - 想要获取root权限,提示如下

1
2
3
hisen@ubuntu:/var/lib$ su
Password:
su: Authentication failure

解决办法

1
2
3
4
5
6
7
hisen@ubuntu:$ sudo passwd root
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
hisen@ubuntu:$ su
Password:
root@ubuntu:# cd mysql

重新设置一下密码即可,我这边装的时候设置的用户是:hisen

刚刚重新设置的密码就是你装系统的时候设置的用户密码。

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - IDEA设置intellij-java-google-style - - /20170401-IDEA%E8%AE%BE%E7%BD%AEintellij-java-google-style/ - - 一直想弄个格式化代码,后来发现很多人用谷歌的,于是也来整一份

保存一份google code的xml,链接有最新的
intellij-java-google-style.xml
设置方法如下:Setting -> Editor -> Code Stytle -> Java

最后一步就选择你存放之前保存的xml

然后就大功告成,来个对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.hisen.json;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
* Created by hisenyuan on 2017/3/23 at 18:02.
*/
public class test {
public static void main(String[] args) {String s = "{'A':'a'}";
JSONObject obj= JSON.parseObject(s);
System.out.println(obj.get("A"));
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.hisen.json;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
* Created by hisenyuan on 2017/3/23 at 18:02.
*/
public class test {

public static void main(String[] args) {
String s = "{'A':'a'}";
JSONObject obj = JSON.parseObject(s);
System.out.println(obj.get("A"));
}
}

]]>
- - - - - idea - - - - - - - intellij-java-google-style - - - -
- - - - - Java判断全角半角字符以及相互转换 - - /20170401-Java%E5%88%A4%E6%96%AD%E5%85%A8%E8%A7%92%E5%8D%8A%E8%A7%92%E5%AD%97%E7%AC%A6%E4%BB%A5%E5%8F%8A%E7%9B%B8%E4%BA%92%E8%BD%AC%E6%8D%A2/ - - 在计算机屏幕上,一个汉字要占两个英文字符的位置,人们把一个英文字符所占的位置称为”半角”,

相对地把一个汉字所占的位置称为”全角”。在汉字输入时,系统提供”半角”和”全角”两种不同的输入状态,

但是对于英文字母、符号和数字这些通用字符就不同于汉字,在半角状态它们被作为英文字符处理;

而在全角状态,它们又可作为中文字符处理。

半角和全角切换方法:单击输入法工具条上的按钮或按键盘上的Shift+Space键来切换。

1、全角:指一个字符占用两个标准字符位置。

汉字字符和规定了全角的英文字符及国标GB2312-80中的图形符号和特殊字符都是全角字符。一般的系统命令是不用全角字符的,只是在作文字处理时才会使用全角字符。


2、半角:指一字符占用一个标准的字符位置。

通常的英文字母、数字键、符号键都是半角的,半角的显示内码都是一个字节。在系统内部,以上三种字符是作为基本代码处理的,所以用户输入命令和参数时一般都使用半角。


3、全角与半角各在什么情况下使用?

全角占两个字节,半角占一个字节。

半角全角主要是针对标点符号来说的,全角标点占两个字节,半角占一个字节,而不管是半角还是全角,汉字都还是要占两个字节。

在编程序的源代码中只能使用半角标点(不包括字符串内部的数据)

在不支持汉字等语言的计算机上只能使用半角标点(其实这种情况根本就不存在半角全角的概念)

对于大多数字体来说,全角看起来比半角大,当然这不是本质区别了。


4、全角和半角的区别
全角就是字母和数字等与汉字占等宽位置的字。半角就是ASCII方式的字符,

在没有汉字输入法起做用的时候输入的字母数字和字符都是半角的。

在汉字输入法出现的时候,输入的字母数字默认为半角,但是标点则是默认为全角,

可以通过鼠标点击输入法工具条上的相应按钮来改变。


5、关于“全角”和“半角”:

全角:是指中GB2312-80(《信息交换用汉字编码字符集·基本集》)中的各种符号。

半角:是指英文件ASCII码中的各种符号。

全角状态下字母、数字符号等都会占两个字节的位置,也就是一个汉字那么宽,半角状态下,

字母数字符号一般会占一个字节,也就是半个汉字的位置,全角半角对汉字没有影响。

有两种方式可以判断:

1:通过正则表达式来进行判断 [^\x00-\xff]

2: 通过字符编码的范围进行判断.

通过打印所有的字符发现:

  1. 半角字符是从33开始到126结束
  2. 与半角字符对应的全角字符是从65281开始到65374结束
  3. 其中半角的空格是32.对应的全角空格是12288
  4. 半角和全角的关系很明显,除空格外的字符偏移量是65248(65281-33 = 65248)

具体的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.hisen.String;

/**
* 半角字符和全角字符的转换 以及 判断
* Created by hisenyuan on 2017/4/1 at 10:37.
*/
public class FullHalf {
/**
* ASCII表中可见字符从!开始,偏移位值为33(Decimal)
*/
static final char DBC_CHAR_START = 33; // 半角!

/**
* ASCII表中可见字符到~结束,偏移位值为126(Decimal)
*/
static final char DBC_CHAR_END = 126; // 半角~

/**
* 全角对应于ASCII表的可见字符从!开始,偏移值为65281
*/
static final char SBC_CHAR_START = 65281; // 全角!

/**
* 全角对应于ASCII表的可见字符到~结束,偏移值为65374
*/
static final char SBC_CHAR_END = 65374; // 全角~

/**
* ASCII表中除空格外的可见字符与对应的全角字符的相对偏移
*/
static final int CONVERT_STEP = 65248; // 全角半角转换间隔

/**
* 全角空格的值,它没有遵从与ASCII的相对偏移,必须单独处理
*/
static final char SBC_SPACE = 12288; // 全角空格 12288

/**
* 半角空格的值,在ASCII中为32(Decimal)
*/
static final char DBC_SPACE = ' '; // 半角空格
public static void main(String[] args) {
String s = "123456";
//半角转换成全角字符
String s1 = bj2qj(s);
//全角转换成半角
String s2 = qj2bj(s1);
System.out.println("全角:"+s1 +" -> 半角:"+s2);
System.out.println("--------------------------");
String fh = s1+s2;
//判断全角还是半角
fullOrHalf(fh);
//打印ASCII表中所有字符
printAllChar();
}
/**
* <PRE>
* 半角字符->全角字符转换
* 只处理空格,!到˜之间的字符,忽略其他
* </PRE>
*/
private static String bj2qj(String src) {
if (src == null) {
return src;
}
StringBuilder buf = new StringBuilder(src.length());
char[] ca = src.toCharArray();
for (int i = 0; i < ca.length; i++) {
if (ca[i] == DBC_SPACE) { // 如果是半角空格,直接用全角空格替代
buf.append(SBC_SPACE);
} else if ((ca[i] >= DBC_CHAR_START) && (ca[i] <= DBC_CHAR_END)) { // 字符是!到~之间的可见字符
buf.append((char) (ca[i] + CONVERT_STEP));
} else { // 不对空格以及ascii表中其他可见字符之外的字符做任何处理
buf.append(ca[i]);
}
}
return buf.toString();
}
/**
* <PRE>
* 全角字符->半角字符转换
* 只处理全角的空格,全角!到全角~之间的字符,忽略其他
* </PRE>
*/
public static String qj2bj(String src) {
if (src == null) {
return src;
}
StringBuilder buf = new StringBuilder(src.length());
char[] ca = src.toCharArray();
for (int i = 0; i < src.length(); i++) {
if (ca[i] >= SBC_CHAR_START && ca[i] <= SBC_CHAR_END) { // 如果位于全角!到全角~区间内
buf.append((char) (ca[i] - CONVERT_STEP));
} else if (ca[i] == SBC_SPACE) { // 如果是全角空格
buf.append(DBC_SPACE);
} else { // 不处理全角空格,全角!到全角~区间外的字符
buf.append(ca[i]);
}
}
return buf.toString();
}

/**
* 使用正则表达式判断字符是否为全角
* @param str
*/
public static void fullOrHalf(String str){
char[] chars = str.toCharArray();
for (int i = 0; i < chars.length; i++) {
String temp = String.valueOf(chars[i]);
// 正则判断是全角字符
if (temp.matches("[^\\x00-\\xff]")) {
System.out.println("全角 -> " + temp);
}
// 判断是半角字符
else {
System.out.println("半角 -> " + temp);
}
}
}

/**
* 打印所有字符
*/
public static void printAllChar(){
for (int i = Character.MIN_VALUE; i <= Character.MAX_VALUE; ++i) {
System.out.println("ASCII:"+i + " -> " + "字符:"+(char)i);
}
}
}

]]>
- - - - - java - - - - - - - java - - - -
- - - - - IDEA部署tomcat项目前台传输的json到后台乱码 - IDEA乱码解决 - - /20170330-IDEA%E9%83%A8%E7%BD%B2tomcat%E9%A1%B9%E7%9B%AE%E5%89%8D%E5%8F%B0%E4%BC%A0%E8%BE%93%E7%9A%84json%E5%88%B0%E5%90%8E%E5%8F%B0%E4%B9%B1%E7%A0%81/ - - idea涉及编码的地方都改了
主要是编译时候的编码,tomcat的编码,以及idea配置里面的编码

一、idea配置文件

1
\HOME\IntelliJ IDEA 2016.3.4\bin\idea64.exe.vmoptions

增加一行:-Dfile.encoding=UTF-8

二、编译参数

1
2
File -> Settings -> Build, Execution, Deployment
-> Compiler -> Java Compiler -> Addition command line parameters

在空格里面添加:-encoding utf-8

三、工程编码

1
File -> Settings -> Editor -> File Encodings

此页面三个地方都选择UTF-8

四、tomcat参数

1
Run/debug Configuration tomcat

VM options:-Dfile.encoding=UTF-8

]]>
- - - - - idea - - - - - - - java - - idea - - - -
- - - - - Unable to ping server at localhost 1099 - 出现的原因 - - /20170330-Unable%20to%20ping%20server%20at%20localhost%201099%20-%20%E5%87%BA%E7%8E%B0%E7%9A%84%E5%8E%9F%E5%9B%A0/ - - 之前老是出现

1
2
Application Server was not connected before run configuration stop, 
reason: Unable to ping server at localhost:1099

我遇到这个问题一般是这些原因:

  1. 这个端口被占用,一般进程管理把所有java进程杀了可以解决
  2. 由于在IDEA中错误的给tomcat添加了参数,比如下面这个。去掉即可

这是下VM option中加了:-URIEncoding=UTF-8

1
2
3
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Unrecognized option: -URIEncoding=UTF-8

]]>
- - - - - idea - - - - - - - java - - idea - - - -
- - - - - Oracle性能优化常用的SQL - - /20170329-Oracle%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%B8%B8%E7%94%A8%E7%9A%84SQL/ - - 使用如下sql能查出相应的信息,oracle博大精深要掌握得花时间

太长了,来个阅读全文吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
--显示数据库当前连接数
select count(*) from v$process;

--显示数据库最大连接数
select value from v$parameter where name ='processes';

--修改最大Oracle最大连接数:
alter system set processes = 300 scope = spfile;

--显示当前session连接数
select count(*) from v$session;

--查看当前有哪些用户正在使用数据
SELECT osuser, a.username,cpu_time/executions/1000000||'s', sql_fulltext,machine from v$session a, v$sqlarea b where a.sql_address =b.address order by cpu_time/executions desc;

--查看连接oracle的所有机器的连接数
select machine,count(*) from v$session group by machine;

--查看连接oracle的所有机器的连接数和状态
select machine,status,count(*) from v$session group by machine,status order by status;

--查看消耗磁盘读取最多的SQL Top 5:
select disk_reads,sql_text,SQL_FULLTEXT
from (select sql_text,disk_reads,SQL_FULLTEXT,
dense_rank() over
(order by disk_reads desc) disk_reads_rank
from v$sql)
where disk_reads_rank <=5;

--开始
--通过linux中消耗资源高的进程号获取oracle消耗资源的sql语句:
--1.linux中使用top命名查看oracle进程中消耗资源最高的进程号;
--2.oracle中使用命令:
select c.spid,a.p1,a.p1raw,a.p2,a.event,b.sql_text,b.SQL_FULLTEXT,b.SQL_ID
from v$session a,v$sql b,v$process c
where a.wait_class<>'Idle' and a.sql_id=b.sql_id and a.PADDR=c.addr
order by event;
--3.查询结果显示出各个sql语句对应的进程号,从中找出top命令中对应消耗资源高的进程号即可找到相应的sql语句。
--结束

--判断回滚段竞争的SQL语句:(当Ratio大于2时存在回滚段竞争,需要增加更多的回滚段)
select rn.name, rs.GETS, rs.WAITS, (rs.WAITS / rs.GETS) * 100 ratio
from v$rollstat rs, v$rollname rn
where rs.USN = rn.usn;

---判断恢复日志竞争的SQL语句:(immediate_contention或wait_contention的值大于1时存在竞争)
select name,
(t.IMMEDIATE_MISSES / decode((t.IMMEDIATE_GETS + t.IMMEDIATE_MISSES),0,-1,(t.IMMEDIATE_GETS + t.IMMEDIATE_MISSES))) * 100 immediate_contention,
(t.MISSES / decode((t.GETS + t.MISSES), 0, -1, (t.GETS + t.MISSES))) * 100 wait_contention
from v$latch t
where name in ('redo copy', 'redo allocation');

--判断表空间碎片:(如果最大空闲空间占总空间很大比例则可能不存在碎片,如果比例较小,且有许多空闲空间,则可能碎片很多)
select t.tablespace_name,
sum(t.bytes),
max(t.bytes),
count(*),
max(t.bytes) / sum(t.bytes) radio
from dba_free_space t
group by t.tablespace_name
order by t.tablespace_name;

--确定命中排序域的次数:
select t.NAME, t.VALUE from v$sysstat t where t.NAME like 'sort%'

--查看当前SGA值:SGA(System Global Area)系统全局区。这是一个非常庞大的内存区间
select * from v$sga;

--确定高速缓冲区命中率:(如果命中率低于70%,则应该加大init.ora参数中的DB_BLOCK_BUFFER的值)
select 1 - sum(decode(name, 'physical reads', value, 0)) /
(sum(decode(name, 'db block gets', value, 0)) +
sum(decode(name, 'consistent gets', value, 0))) hit_ratio
from v$sysstat t
where name in ('physical reads', 'db block gets', 'consistent gets');

--确定共享池中的命中率:(如果ratio1大于1时,需要加大共享池,如果ratio2大于10%时,需要加大共享池SHARED_POOL_SIZE)
select * from
(
select sum(pins) pins,
sum(reloads) reloads,
(sum(reloads) / sum(pins)) * 100 ratio1
from v$librarycache
),
(
select sum(gets) gets,
sum(getmisses) getmisses,
(sum(getmisses) / sum(gets)) * 100 ratio2
from v$rowcache
)

--查询INIT.ORA参数:
select * from v$parameter;

--求当前会话的SID,SERIAL#
SELECT Sid, Serial# FROM V$session
WHERE Audsid = Sys_Context('USERENV', 'SESSIONID');

--查询session的OS进程ID(有输入)
SELECT p.Spid "OS Thread", b.NAME "Name-User", s.Program, s.Sid, s.Serial#,s.Osuser, s.Machine
FROM V$process p, V$session s, V$bgprocess b
WHERE p.Addr = s.Paddr
AND p.Addr = b.Paddr And (s.sid=&1 or p.spid=&1)
UNION ALL
SELECT p.Spid "OS Thread", s.Username "Name-User", s.Program, s.Sid,s.Serial#, s.Osuser, s.Machine
FROM V$process p, V$session s
WHERE p.Addr = s.Paddr
And (s.sid=&1 or p.spid=&1)
AND s.Username IS NOT NULL;

--查看锁(lock)情况
SELECT /*+ RULE */ Ls.Osuser Os_User_Name, Ls.Username User_Name,Decode(Ls.TYPE,
'RW', 'Row wait enqueue lock', 'TM', 'DML enqueue lock','TX', 'Transaction enqueue lock', 'UL', 'User supplied lock') Lock_Type,o.Object_Name OBJECT,Decode(Ls.Lmode,1, NULL, 2, 'Row Share', 3, 'Row Exclusive',
4, 'Share', 5, 'Share Row Exclusive', 6, 'Exclusive',NULL) Lock_Mode,o.Owner, Ls.Sid, Ls.Serial# Serial_Num, Ls.Id1, Ls.Id2 FROM Sys.Dba_Objects o,
(SELECT s.Osuser, s.Username, l.TYPE, l.Lmode, s.Sid, s.Serial#, l.Id1,l.Id2 FROM V$session s, V$lock l
WHERE s.Sid = l.Sid) Ls
WHERE o.Object_Id = Ls.Id1
AND o.Owner <> 'SYS'
ORDER BY o.Owner, o.Object_Name;

--查看等待(wait)情况
SELECT Ws.CLASS, Ws.COUNT COUNT, SUM(Ss.VALUE) Sum_Value
FROM V$waitstat Ws, V$sysstat Ss
WHERE Ss.NAME IN ('db block gets', 'consistent gets')
GROUP BY Ws.CLASS, Ws.COUNT;

--求process/session的状态
SELECT p.Pid, p.Spid, s.Program, s.Sid, s.Serial#
FROM V$process p, V$session s
WHERE s.Paddr = p.Addr;

--查看表空间的名称及大小
select t.tablespace_name, round(sum(bytes/(1024*1024)),0) ts_size
from dba_tablespaces t, dba_data_files d
where t.tablespace_name = d.tablespace_name
group by t.tablespace_name;

--查看表空间物理文件的名称及大小
select tablespace_name, file_id, file_name,
round(bytes/(1024*1024),0) total_space
from dba_data_files
order by tablespace_name;

--查看回滚段名称及大小
select segment_name, tablespace_name, r.status,
(initial_extent/1024) InitialExtent,(next_extent/1024) NextExtent,

--查看控制文件
select name from v$controlfile;

--查看日志文件
select member from v$logfile;

--查看表空间的使用情况
select sum(bytes)/(1024*1024) as free_space,tablespace_name
from dba_free_space
group by tablespace_name;
SELECT A.TABLESPACE_NAME,A.BYTES TOTAL,B.BYTES USED, C.BYTES FREE,
(B.BYTES*100)/A.BYTES "% USED",(C.BYTES*100)/A.BYTES "% FREE"
FROM SYS.SM$TS_AVAIL A,SYS.SM$TS_USED B,SYS.SM$TS_FREE C
WHERE A.TABLESPACE_NAME=B.TABLESPACE_NAME AND A.TABLESPACE_NAME=C.TABLESPACE_NAME;

--捕捉运行很久的SQL
select username,sid,opname,
round(sofar*100 / totalwork,0) || '%' as progress,
time_remaining,sql_text
from v$session_longops , v$sql
where time_remaining <> 0
and sql_address = address
and sql_hash_value = hash_value

--耗资源的进程(top session)
select s.schemaname schema_name, decode(sign(48 - command), 1,
to_char(command), 'Action Code #' || to_char(command) ) action, status
session_status, s.osuser os_user_name, s.sid, p.spid , s.serial# serial_num,
nvl(s.username, '[Oracle process]') user_name, s.terminal terminal,
s.program program, st.value criteria_value from v$sesstat st, v$session s , v$process p
where st.sid = s.sid and st.statistic# = to_number('38') and ('ALL' = 'ALL'
or s.status = 'ALL') and p.addr = s.paddr order by st.value desc, p.spid asc, s.username asc, s.osuser asc

--查看有哪些实例在运行:
select * from v$active_instances;

]]>
- - - - - db - - - - - - - oracle - - - -
- - - - - IDEA智能补全快捷键更改为Ctrl + 逗号 - - /20170328-IDEA%E6%99%BA%E8%83%BD%E8%A1%A5%E5%85%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%9B%B4%E6%94%B9%E4%B8%BACtrl%20+%20%E9%80%97%E5%8F%B7/ - - 一直就听说idea的智能补全很厉害,但是Ctrl + 空格 被万恶的输入法给占用了
网上搜也不是很清晰,这里就写个博客记录一下

快捷键设置:file->setting->Keymap->Main menu->Code->Completion->Basic
找到之后右键Add keyboard Shortcut,然后按下:Ctrl + 逗号

]]>
- - - - - idea - - - - - - - idea - - - -
- - - - - Oracle中exists在update中无法限制住条件,在select中可以 - - /20170323-Oracle%E4%B8%ADexists%E5%9C%A8update%E4%B8%AD%E6%97%A0%E6%B3%95%E9%99%90%E5%88%B6%E4%BD%8F%E6%9D%A1%E4%BB%B6%EF%BC%8C%E5%9C%A8select%E4%B8%AD%E5%8F%AF%E4%BB%A5/ - - 这个问题是自己写的一个bug,标示不知道原因是什么

现在暂时使用 in 代替解决了

下面的查询是能限制住acct_type

1
2
3
4
5
SELECT ew.customer_id,cf.acct_type
FROM ew_quota_info ew,
cf_customer cf
WHERE cf.acct_type in(2,3)
AND ew.customer_id = cf.id

但是在update的时候,会把acct_type=1的也更新了

1
2
3
4
5
6
7
8
9
update ew_quota_info ew
set ew.all_amt = 100000
where exists(
SELECT ew.customer_id
FROM ew_quota_info ew,
cf_customer cf
WHERE cf.acct_type in(2,3)
AND ew.customer_id = cf.id
)

]]>
- - - - - sql - - - - - - - mysql - - oracle - - - -
- - - - - Intellij IDEA中使用MyBatis-generator 自动生成MyBatis代码 - - /20170322-Intellij%20IDEA%E4%B8%AD%E4%BD%BF%E7%94%A8MyBatis-generator%20%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90MyBatis%E4%BB%A3%E7%A0%81/ - - MyBatis Generator是一个非常方便的代码生成工具,

可以根据数据库中表结构自动生成CRUD代码,可以满足大部分需求。

MyBatis Generator (MBG) 是一个Mybatis的代码生成器 ,

可以根据数据库中表结构自动生成简单的CRUD(插入,查询,更新,删除)操作。

但联合查询和存储过程,需手动手写SQL和对象。

PS:配置过程中请注意自己的工程目录结构

一、pom.xml添加插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version>
</dependency>
</dependencies>
<configuration>
<overwrite>true</overwrite>
</configuration>
</plugin>

二、配置generatorConfig.xml

resources下建generatorConfig.xml,作为mybatis-generator-maven-plugin插件的执行目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

<context id="mysqlgenerator" targetRuntime="MyBatis3">
<!--数据库连接信息-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/ssm"
userId="root"
password="hisen" />
<!--代码相关路径和包-->
<javaModelGenerator targetPackage="com.hisen.mybatis.model" targetProject="src/main/java" />

<sqlMapGenerator targetPackage="com.hisen.mybatis.mapper" targetProject="src/main/resources" />

<javaClientGenerator type="XMLMAPPER" targetPackage="com.hisen.mybatis.mapper" targetProject="src/main/java" />
<!--表名-->
<table tableName="appointment"/>
<table tableName="book"/>

</context>

</generatorConfiguration>

三、Intellij配置

MyBatis Generator生成代码的运行方式:命令行、使用Ant、使用Maven、Java编码。

本文采用Maven插件mybatis-generator-maven-plugin来运行MyBatis Generator,用的是命令行的方式。

配置插件

选择目录,输入命令:mybatis-generator:generate -e

找到插件。双击执行

即可看到生成的文件

]]>
- - - - - java - - - - - - - idea - - mybatis - - - -
- - - - - Ubuntu安装nginx - - /20170321-Ubuntu%E5%AE%89%E8%A3%85nginx/ - - Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。

Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,

第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,

因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。

说明:这只是一个初步的安装,后续进一步实践

安装Nginx依赖库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#安装gcc g++的依赖库
sudo apt-get install build-essential
sudo apt-get install libtool
#安装 pcre依赖库
sudo apt-get update
sudo apt-get install libpcre3 libpcre3-dev
#安装 zlib依赖库(http://www.zlib.net)
sudo apt-get install zlib1g-dev
#安装 ssl依赖库
sudo apt-get install openssl
#下载最新版本:
wget http://nginx.org/download/nginx-1.9.9.tar.gz
#解压
tar -zxvf nginx-1.9.9.tar.gz
#进入解压目录:
cd nginx-1.9.9
#配置:
sudo ./configure --prefix=/usr/local/nginx
#编辑nginx:
sudo make
#注意:这里可能会报错,提示“pcre.h No such file or directory”,具体详见:http://stackoverflow.com/questions/22555561/error-building-fatal-error-pcre-h-no-such-file-or-directory
#需要安装 libpcre3-dev,命令为:sudo apt-get install libpcre3-dev
#安装nginx:
sudo make install
#启动nginx:
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
#注意:-c 指定配置文件的路径,不加的话,nginx会自动加载默认路径的配置文件,可以通过 -h查看帮助命令。
#查看nginx进程:
ps -ef|grep nginx

Nginx常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#启动
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
#进入目录
cd /usr/local/nginx/
#关闭
sudo ./sbin/nginx -s quit
#Nginx重新加载配置
sudo ./sbin/nginx -s reload
#指定配置文件
sudo ./sbin/nginx -c /usr/local/nginx/conf/nginx.conf
#查看版本
sudo ./sbin/nginx -v
#显示帮助
sudo ./sbin/nginx -h

安装之后就直接监听80端口,浏览器打开127.0.0.1即可访问出现如下页面:

]]>
- - - - - 软件 - - - - - - - nginx - - - -
- - - - - log4j日志信息插入mysql数据库 - - /20170321-log4j%E6%97%A5%E5%BF%97%E4%BF%A1%E6%81%AF%E6%8F%92%E5%85%A5mysql%E6%95%B0%E6%8D%AE%E5%BA%93/ - - 以前不知道这玩意还能直接插入MySQL数据库

我是使用IDEA,maven

具体的目录结果见github:github

log4j配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#可以设置级别:debug>info>error
#debug:显示debug、info、error
#info:显示info、error
#error:只error
log4j.rootLogger=debug,info,database
#注意的地方database 对应 log4j.appender.database.URL的database 若认log4j.rootLogger=debug,info,db 那么 log4j.appender.database.URL的database 要改成db
#log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.logfile.DatePattern=.yyyy-MM-dd
#log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
#输出到控制台
#log4j.appender.appender1=org.apache.log4j.ConsoleAppender
#样式为TTCCLayout
#log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout
#设置级别:
#log4j.rootLogger=debug,appender1

#输出到文件(这里默认为追加方式)
#log4j.appender.appender1=org.apache.log4j.FileAppender
#设置文件输出路径
#【1】文本文件
#log4j.appender.appender1.File=c:/Log4JDemo02.log
#【2】HTML文件
log4j.appender.appender1.File=c:/Log4JDemo02.html
#设置文件输出样式
#log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout
log4j.appender.appender1.layout=org.apache.log4j.HTMLLayout
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%c] - - <%m>%n
log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.DatePattern=.yyyy-MM-dd
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] wang- <%m>%n
#下面是配置将日志信息插入数据库,
#配置输出目标为数据库(假如要将日志在控制台输出,配置为log4j.appender. stdout =org.apache.log4j.ConsoleAppender;将日志写入文件,配置为log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
#这样的配置在许多地方都要有,需要可查有关资料),当然你也可以自己扩展org.apache.log4j.jdbc.JDBCAppender这个类,只需要在这里配置就可以了例如我们配置我自己扩展的MyJDBCAppender,配置为#log4j.appender.db=com.neam.commons.MyJDBCAppender
log4j.appender.database.Threshold=info
#定义什么级别的错误将写入到数据库中
log4j.appender.database.BufferSize=1
#设置缓存大小,就是当有1条日志信息是才往数据库插一次
log4j.appender.database=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.database.driver=com.mysql.jdbc.Driver
#设置要将日志插入到数据库的驱动
log4j.appender.database.URL=jdbc:mysql://127.0.0.1:3306/log4j?useUnicode=true&characterEncoding=UTF-8
log4j.appender.database.user=root
log4j.appender.database.password=hisen

log4j.appender.database.sql=insert into log (Class,Mothod,createTime,LogLevel,MSG) values ('%C','%M','%d{yyyy-MM-dd HH:mm:ss}','%p','%m')
log4j.appender.database.layout=org.apache.log4j.PatternLayout

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.hisen.log4j.log4j2MySQL;

import org.apache.log4j.Logger;

/**
* 测试一下log4j把日志插入到mysql数据库
* 插入语句和数据库配置在log4j的配置文件中
* Created by hisenyuan on 2017/3/21 at 14:23.
*/
public class Log4jDemo {
private static Logger logger = Logger.getLogger(Log4jDemo.class);

public static void main(String[] args) {
logger.debug("这是debug信息");
// 记录info级别的信息
logger.info("这是info信息");
logger.info("这里做了一个XX操作,入库,做操作日志");
// 记录error级别的信息
logger.error("这是error信息");
hisen();
}

public static void hisen() {
logger.debug("这是方法中debug信息");
// 记录info级别的信息
logger.info("这是方法中info信息");
// 记录error级别的信息
logger.error("这是方法中error信息");
}
}
]]>
- - - - - java - - - - - - - java - - - -
- - - - - 企业邮箱签名模版 - - /20170317-%E4%BC%81%E4%B8%9A%E9%82%AE%E7%AE%B1%E7%AD%BE%E5%90%8D%E6%A8%A1%E7%89%88/ - - 下面是源码,有些邮箱可以直接用html源码设置。

我用得是网易邮箱大师,把代码存为本地网页打开全选复制

粘贴到邮箱大师的签名里面即可!亲测有效,还挺好看的

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<html>
<head></head>
<body>
<div>
<style>.g {clear:both; height:0;}.mailsign {font-size:12px; color:#808080; margin:0 35px; line-height:22px;}.logo {width:305px; height:35px; margin:20px 0 10px -15px;}.name {font-weight:700; font-size:14px; float:left; color:#808080; height:30px; line-height:30px;}.position {float:left; margin-left:15px; color:#808080; height:30px; line-height:30px;}.company1 {clear:both; font-weight:700; font-size:14px; color:#808080; margin-top:10px;}.company2 {clear:both; font-weight:700; font-size:14px; color:#808080; margin-bottom:10px;}.add{}.add .zip { margin-left:5px; color:#a0a0a0; font-size:10px;}.tel {}.fax {}.phone {}.website {}.website a {color:#808080; text-decoration:none !important;}.eng {}.state {color:#a0a0a0; margin-top:20px; padding:15px; border:1px solid #CCC; border-radius:10px}.state h4 { margin:0;}.state p {margin:0; font-size:7.5pt;}</style>

<div class="mailsign">
<hr />
<div class="g">
<div class="name">
<t id="tname">
HiSEN
</t>
</div>
<div class="position">
<t id="tposition">
中级java开发工程师
</t>
</div>
</div>
<div class="g"></div>
<div class="company1">
HiSEN网络在线技术有限公司
</div>
<div class="company2">
<t id="tcompany">
技术部
</t>
</div>
<div class="add">
<span>地址/Add :</span>
<t id="tadd">
北京朝阳区xxx
</t>
<div class="mob">
<span>手机/Mob:</span>
<t id="tmob">
15555555555
</t>
</div>
<div class="website">
<span>网址/URL :</span>
<a href="http://hisen.me/" target="_blank">hisen.me</a>
</div>
<div class="state">
<h4>保密声明:</h4>
<p>此文件中可能含有机密类信息,仅限于上方提到的人员使用。若非以上人员或负责将该信息传送给上述人员的职员或代理人,严禁对此文件作任何形式的汇报、散布、传播及复制。若非此文件的指定收件人,请立即以邮件形式联系发件人并销毁所有原始文件的拷贝。</p>
<h4>CONFIDENTIALITY NOTICE:</h4>
<p>The information contained in this transmission may contain privileged and confidential information and is intended only for the use of the person(s) named above. If you are not the intended recipient, or an employee or agent responsible for delivering this message to the intended recipient, any review, dissemination, distribution or duplication of this communication is strictly prohibited. If you are not the intended recipient, please contact the sender immediately by reply e-mail and destroy all copies of the original message.</p>
</div>
</div>
</div>
</body>
</html>

]]>
- - - - - 其他 - - - - - - - 模版 - - - -
- - - - - JAVA中几种常用的RPC框架 - - /20170317-JAVA%E4%B8%AD%E5%87%A0%E7%A7%8D%E5%B8%B8%E7%94%A8%E7%9A%84RPC%E6%A1%86%E6%9E%B6/ - - RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,

作用是有助于系统的垂直拆分,使系统更易拓展。

Java中的RPC框架比较多,各有特色,广泛使用的有RMI、Hessian、Dubbo等。

RPC还有一个特点就是能够跨语言,本文只以JAVA语言里的RPC为例。

其他的框架结构也类似,区别在于对象的序列化方法,传输对象的通讯协议,

以及注册中心的管理与failover设计(利用zookeeper)。

客户端和服务端可以运行在不同的JVM中,Client只需要引入接口,

接口的实现以及运行时需要的数据都在Server端,RPC的主要依赖技术是序列化、反序列化和传输协议,

JAVA里对应的就是对象的序列化、反序列化以及序列化后数据的传输。

RMI的序列化和反序列化是JAVA自带的,Hessian里的序列化和反序列化是私有的,传输协议则是HTTP,

Dubbo的序列化可以多种选择,一般使用Hessian的序列化协议,传输则是TCP协议,使用了高性能的NIO框架Netty。

对于序列化,我还了解一些,像Google的ProBuffer、JBoss Marshalling和Apache Thrift等

1.RMI

rmi
JAVA自带的远程方法调用工具,不过有一定的局限性,

毕竟是JAVA语言最开始时的设计,后来很多框架的原理都基于RMI,RMI的使用如下:

对外接口

1
2
3
public interface IService extends Remote {  
public String queryName(String no) throws RemoteException;
}

服务实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

// 服务实现
public class ServiceImpl extends UnicastRemoteObject implements IService {

/**
*/
private static final long serialVersionUID = 682805210518738166L;

/**
* @throws RemoteException
*/
protected ServiceImpl() throws RemoteException {
super();
}

/* (non-Javadoc)
* @see com.suning.ebuy.wd.web.IService#queryName(java.lang.String)
*/
@Override
public String queryName(String no) throws RemoteException {
// 方法的具体实现
System.out.println("hello" + no);
return String.valueOf(System.currentTimeMillis());
}

}

RMI客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

// RMI客户端
public class Client {

public static void main(String[] args) {
// 注册管理器
Registry registry = null;
try {
// 获取服务注册管理器
registry = LocateRegistry.getRegistry("127.0.0.1",8088);
// 列出所有注册的服务
String[] list = registry.list();
for(String s : list){
System.out.println(s);
}
} catch (RemoteException e) {

}
try {
// 根据命名获取服务
IService server = (IService) registry.lookup("vince");
// 调用远程方法
String result = server.queryName("ha ha ha ha");
// 输出调用结果
System.out.println("result from remote : " + result);
} catch (AccessException e) {

} catch (RemoteException e) {

} catch (NotBoundException e) {

}
}
}

RMI服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

// RMI服务端
public class Server {

public static void main(String[] args) {
// 注册管理器
Registry registry = null;
try {
// 创建一个服务注册管理器
registry = LocateRegistry.createRegistry(8088);

} catch (RemoteException e) {

}
try {
// 创建一个服务
ServiceImpl server = new ServiceImpl();
// 将服务绑定命名
registry.rebind("vince", server);

System.out.println("bind server");
} catch (RemoteException e) {

}
}
}

服务注册管理器写在了Server里,当然也可以抽出来单独作为一个服务,

在其他一些框架中,往往用Zookeeper充当注册管理角色。

2.Hessian(基于HTTP的远程方法调用)

Hessian
基于HTTP协议传输,在性能方面还不够完美,负载均衡和失效转移依赖于应用的负载均衡器,

Hessian的使用则与RMI类似,区别在于淡化了Registry的角色,通过显示的地址调用,

利用HessianProxyFactory根据配置的地址create一个代理对象,另外还要引入Hessian的Jar包。

3、Dubbo(淘宝开源的基于TCP的RPC框架)

Dubbo

基于Netty的高性能RPC框架,是阿里巴巴开源的,总体原理如下:

在了解Dubbo之前,要先对Zookeeper有深入的理解,当理解了zookeeper后,Dubbo也就了无秘密了。

Dubbo的详细说明在淘宝开源里说的非常详细,在工作中很多生产项目都用了Dubbo,过程中也发现了很多需要注意的地方.

]]>
- - - - - java - - - - - - - java - - - -
- - - - - mybatis:No constructor found in xxx matching - - /20170317-mybatis%EF%BC%9ANo%20constructor%20found%20in%20xxx%20matching%20%5Bjava.lang.Integer,%20java.lang.String,%20java.lang.Integer%5D/ - - 如下错误提示:

mybatis:No constructor found in xxx matching [java.lang.Integer, java.lang.String, java.lang.Integer]

原因:xxx 这个bean缺少一个默认的构造方法!


解决:加上默认的构造方法即可


我是在单元测试的时候遇到这个问题

]]>
- - - - - java - - - - - - - java - - - -
- - - - - linux cal date命令详解 - - /20170314-linux%20cal%20date%E5%91%BD%E4%BB%A4%E8%AF%A6%E8%A7%A3/ - -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
##显示当月的日历
hisen@ubuntu:~$ cal
March 2017
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

##指定显示1997年11月当月的日历
hisen@ubuntu:~$ cal 11 1997
November 1997
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
##显示2018年全年12个月的日历
hisen@ubuntu:~$ cal -m 12 -y 2018
2018
January February March
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 1 2 3 1 2 3
7 8 9 10 11 12 13 4 5 6 7 8 9 10 4 5 6 7 8 9 10
14 15 16 17 18 19 20 11 12 13 14 15 16 17 11 12 13 14 15 16 17
21 22 23 24 25 26 27 18 19 20 21 22 23 24 18 19 20 21 22 23 24
28 29 30 31 25 26 27 28 25 26 27 28 29 30 31


April May June
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7 1 2 3 4 5 1 2
8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9
15 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16
22 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23
29 30 27 28 29 30 31 24 25 26 27 28 29 30


July August September
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7 1 2 3 4 1
8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8
15 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15
22 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22
29 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29
30

October November December
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 1 2 3 1
7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8
14 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15
21 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22
28 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29
30 31
##输出当前日期
hisen@ubuntu:~$ date
Tue Mar 14 11:48:14 CST 2017
##格式化输出年月日
hisen@ubuntu:~$ date "+%Y-%m-%d"
2017-03-14
##格式化输出当前时间
hisen@ubuntu:~$ date "+%H:%M:%S"
11:49:10
##格式化输出年月日时间
hisen@ubuntu:~$ date "+%Y-%m-%d %H:%M:%S"
2017-03-14 11:49:18
]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - 1103 host 'xxx' is not allowed to connect to this mysql - - /20170311-1103%20host%20%E2%80%98xxx%E2%80%99%20is%20not%20allowed%20to%20connect%20to%20this%20mysql/ - - 出现原因:
这是由于mysql服务端root用户所对应的客户端权限设置问题。

默认所对应的客户端地址只有localhost(也就是服务端的机器),

我们目的是任何地址都可以用root访问mysql服务端。

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
$ mysql -u root -p
#进入mysql交互界面
mysql> use mysql;
#使用mysql这个库
mysql> grant all privileges on *.* to 'root'@'%' identified by 'hisen';
#让root可以在任何ip登陆,密码为:hisen
mysql> flush privileges;
#刷新
mysql> exit;
#退出
$ service mysql restart
#重启mysql

]]>
- - - - - java - - - - - - - mysql - - - -
- - - - - Ubuntu虚拟机安装MySQL并且开启root远程访问 - - /20170311-Ubuntu%E5%AE%89%E8%A3%85MySQL%E5%B9%B6%E4%B8%94%E5%BC%80%E5%90%AFroot%E8%BF%9C%E7%A8%8B%E8%AE%BF%E9%97%AE/ - - 安装mysql很简单,关键是开启这个远程很坑!!!

一、安装

1.安装

1
sudo apt-get install mysql-server

等待完成即可,过程中需要设置密码

2.查看是否成功

1
sudo netstat -tap | grep mysql

3.登陆mysql

1
mysql -u root -p

这条命令回车之后需要输入mysql密码

二、开启远程访问

1
2
3
4
5
6
7
8
9
10
$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
#找到bind-address=127.0.0.1直接注释
$ mysql -u root -p -h
#登陆mysql
mysql> use mysql;
#使用mysql这个库
mysql> GRANT ALL PRIVILEGES ON *.* TO root@"%" IDENTIFIED BY "hisen";
#把root用户改成可以在任何ip上登陆,并且密码为:hisen
mysql> flush privileges;
#刷新

重启:service mysql restart

接下来就可以在navicat里面连接了

三、注意事项

因为在网上找的很多教程,都是说改这个配置文件:这个是错误的

1
/etc/mysql/my.cf

如果是通过apt-get方式安装的,默认的是第二步那个配置文件

]]>
- - - - - sql - - - - - - - mysql - - - -
- - - - - mybatis常用jdbctype - - /20170310-mybatis%E5%B8%B8%E7%94%A8jdbctype/ - - Mybatis中javaType和jdbcType对应关系

JDBC TypeJava Type
CHARString
VARCHARString
LONGVARCHARString
NUMERICjava.math.BigDecimal
DECIMALjava.math.BigDecimal
BITboolean
BOOLEANboolean
TINYINTbyte
SMALLINTshort
INTEGERint
BIGINTlong
REALfloat
FLOATdouble
DOUBLEdouble
BINARYbyte[]
VARBINARYbyte[]
LONGVARBINARYbyte[]
DATEjava.sql.Date
TIMEjava.sql.Time
TIMESTAMPjava.sql.Timestamp
CLOBClob
BLOBBlob
ARRAYArray
DISTINCTmapping of underlying type
STRUCTStruct
REFRef
DATALINKjava.net.URL
]]>
- - - - - java - - - - - - - java - - mybatis - - - -
- - - - - SQL优化之小细节 - 点滴记录 - - /20170309-SQL%E4%BC%98%E5%8C%96%E4%B9%8B%E5%B0%8F%E7%BB%86%E8%8A%82%20-%20%E7%82%B9%E6%BB%B4%E8%AE%B0%E5%BD%95/ - - 不知道现在是不是还很多人首先就把关联的id放在where的第一位

这里有一个简单的对比,情况相同的时候,两个sql的时间相差八倍

优:0.077s

1
2
3
4
5
6
7
8
9
SELECT ew.all_amt ,
ew.customer_id,
cf.id,
cf.cert_id,
cf.acct_type
FROM ew_quota_info ew,
cf_customer cf
WHERE cf.acct_type in(2,3)
AND ew.customer_id = cf.id

劣:0.630s

1
2
3
4
5
6
7
8
9
SELECT ew.all_amt ,
ew.customer_id,
cf.id,
cf.cert_id,
cf.acct_type
FROM ew_quota_info ew,
cf_customer cf
WHERE ew.customer_id = cf.id
AND cf.acct_type in(2,3)

上述原因:where子句从后往前执行,应该把大的过滤条件放在后面
记录时间:2017年3月9日 10:44:59


]]>
- - - - - sql - - - - - - - java - - sql - - - -
- - - - - ubuntu重装系统 - 在ubuntu系统下重新安装 - - /20170305-Ubuntu%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%20-%20%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E9%87%8D%E6%96%B0%E5%AE%89%E8%A3%85/ - - 有时候在linux环境下需要重新安装一下系统

这里我就说一下今天我安装的方法。

下载好ubuntu的镜像,随便放在一个非系统盘的根目录下

改名为:ubuntu.iso

1
2
3
4
5
6
7
8
9
10
11
sudo chmod 777 /boot/grub/grub.cfg
sudo vi /boot/grub/grub.cfg
#在 export linux_gfx_mode 下面添加如下内容
menuentry "install ubuntu powered by hisen" {
search --set -f /ubuntu.iso
loopback loop /ubuntu.iso
set root=(loop)
linux /casper/vmlinuz.efi boot=casper iso-scan/filename=/ubuntu.iso
initrd /casper/initrd.lz
boot
}

保存退出,重启就会进入系统。

桌面上点击那个安装的图标即可完成重装

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Ubuntu 16 安装IDEA 并且设置快捷启动 - - /20170304-Ubuntu%2016%20%E5%AE%89%E8%A3%85IDEA%20%E5%B9%B6%E4%B8%94%E8%AE%BE%E7%BD%AE%E5%BF%AB%E6%8D%B7%E5%90%AF%E5%8A%A8/ - - 安装简单,下载官网的文件(with java的比较方便)

解压之后在bin目录下执行

1
sudo sh idea.sh

就会进入安装程序,接下来会跳出图形界面,跟windows差不多的步骤

没有激活码可以看之前的文章

关键的一个是我发现网上说的建立桌面快捷方式不行

就这样弄个方便的

1
2
3
4
5
6
cd ~
ln -s /idea home/bin/idea.sh idea
#接下来执行 idea & 就可以打开,如果提示权限不够
#就执行 sudo idea &
sudo idea &
#后面的 & 代表后台运行的意思,不影响控制台

]]>
- - - - - java - - - - - - - java - - idea - - - -
- - - - - idea jrebel 7.0 破解 - 获取JRebel激活码 - - /20170303-idea%20jrebel%207.0%20%E7%A0%B4%E8%A7%A3%20-%20%E8%8E%B7%E5%8F%96JRebel%E6%BF%80%E6%B4%BB%E7%A0%81/ - - 其实这玩意完全不要破解,直接官网注册就会给一个注册码

注册地址:https://zeroturnaround.com

注册完了之后在IDEA里面去设置,会提醒激活。

tomcat部署了项目之后,点击JR启动是可以热部署的!!!

改了java代码都不要重新启动项目,哈哈!!!

]]>
- - - - - java - - - - - - - java - - idea - - - -
- - - - - IDEA failed to create jvm:error code -1 - - /20170303-idea%20failed%20to%20create%20jvm%20error%20code%20-1/ - - 今天先更改了 idea64.exe.vmoptions 这个配置文件

一直么有重启,后来就安装了个插件重启一下,结果就泪崩了

一直出现这个错误
IDEA failed to create jvm:error code -1,jvm:error code -1
总以为是环境变量配置的问题,或者是文件损坏了什么

重启,重装jdk,重新配置什么都试过,不管用。

后来替换了配置文件就好了!!!

解决方案

配置文件路径:

1
2
3
\IDEA HOME\bin\idea64.exe.vmoptions
或者
\IDEA HOME\bin\idea.exe.vmoptions

默认配置文件内容如下:

32bit

1
2
3
4
5
6
7
8
9
10
11
-server
-Xms128m
-Xmx512m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow

64bit

1
2
3
4
5
6
7
8
9
10
-Xms128m
-Xmx750m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow

by the way:

IDEA 写博客真是舒服啊~

完全不用切换来切换去的!

]]>
- - - - - java - - - - - - - java - - - -
- - - - - count函数效率问题 - 了解count函数 - - /20170303-count%E5%87%BD%E6%95%B0%E6%95%88%E7%8E%87%E9%97%AE%E9%A2%98%20-%20%E4%BA%86%E8%A7%A3count%E5%87%BD%E6%95%B0/ - - count函数的作用

想要真正的理解count函数,我们就必须明白count函数的作用。

作用一:统计某一列非空(not null)值得数量,即统计某列有值得结果数,使用count(col)。

作用二:统计结果集的行数,此时不用管某列是否为null值。即使用count(*).

明白了这点,我们就应该知道MySQL的count(*)并不是想象中的那样,统计每一列的值,而是直接忽视掉所有列,直接统计行数,那么它的效率肯定是很高的。

但是有一点,当col指定了该字段为NOT NULL时实际上,MySQL会自动将count(col)转为count(*),但是这样也同样耗费了些时间,如果col没有指定为NOT NULL的话,那么效率就更低了,MySQL就必须要判断每一行的值是否为空。

所以综上所述,如果是要统计行数最好优先使用select count(*)

当统计某一列等于多少的值得时候可以使用下面两种方法:

1
2
SELECT SUM(IF(id = 23,1,0)) FROM table 
SELECT COUNT(id = 23 OR NULL) FROM table

]]>
- - - - - java - - - - - - - mysql - - - -
- - - - - 利用IDEA写Hexo博客的一些技巧 - - /20170303-%E5%88%A9%E7%94%A8IDEA%E5%86%99Hexo%E5%8D%9A%E5%AE%A2%E7%9A%84%E4%B8%80%E4%BA%9B%E6%8A%80%E5%B7%A7/ - - 今天偶然看到有人说用idea写博客

刚开始我觉得这样会很麻烦,后来想想以前写博客也是醉了

先新建一个 _post 的快捷方式

进去,然后到博客根目录

打开Git Bash,然后执行

1
hexo n "你要写的文章题目"

然后在 _post 快捷方式打开刚刚新建的markdown文件,用markdownpad打开编辑。。。

编辑完了回到Git Bash。。。。想想就很麻烦


于是乎用IDEA打开博客根目录

1
sources -> _post -> new -> Edit File Templates

name:markdown extension:md
内容:

1
2
3
4
5
6
7
---
title: ${NAME}
keywords: []
date: ${DATE} ${TIME}
tags: []
categories:
---

接下来apply -> *.md -> 下面选择markdown

以后新建markdown文件就会默认带上这个模版

效果

1
2
3
4
5
title: 利用IDEA写Hexo博客的一些技巧
keywords: []
date: 2017/3/3 10:51
tags: []
categories: hexo

接下来到了发布的时间,于是我们可以设置一下Terminal(在setting里面),设置为bash(git目录下)

设置完了之后Alt + F12 调出 Terminal 即可进行git操作

到此,大功告成,我要去更新博客了

]]>
- - - - - hexo - - - - - - - hexo - - idea - - - -
- - - - - IDEA 貌似注解失效 - 报错:cannot resolve method xxx - - /20170301-IDEA-%E8%B2%8C%E4%BC%BC%E6%B3%A8%E8%A7%A3%E5%A4%B1%E6%95%88-%E6%8A%A5%E9%94%99%EF%BC%9Acannot-resolve-method-xxx/ - - 在java平台上lombok提供了简单的注解的形式

来帮助我们消除一些必须有但看起来很臃肿的代码

比如属性的get/set,及对象的toString等方法,特别是相对于 POJO;


出现问题

  1. 各种log方法,set方法中红色波浪线
  2. 提示:cannot resolve method xxx
  3. 虽然出现错误但是编译是可以通过的

问题原因

原来的代码用了lombok简单注解
比如maven的pom.xml文件有如下配置

1
2
3
4
5
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>

解决办法

安装lombok plugin


装完插件之后就舒服了,也不报错

]]>
- - - - - java - - - - - - - java - - idea - - - -
- - - - - IDEA Denpendencies红色波浪线报错,所有的包无法导入 - 一种解决办法 - - /20170228-IDEA-Denpendencies%E7%BA%A2%E8%89%B2%E6%B3%A2%E6%B5%AA%E7%BA%BF%E6%8A%A5%E9%94%99-%E6%89%80%E6%9C%89%E7%9A%84%E5%8C%85%E6%97%A0%E6%B3%95%E5%AF%BC%E5%85%A5-%E4%B8%80%E7%A7%8D%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/ - -
1
failed to read artifact descriptor for xxx:jar

一下午那代码里面是各种报错

凡是引入的大部分都报错

原因就是maven管理的jar没有添加上依赖

最后在stackoverflow找到了良药

上面有图片,错误会详细一点,如果你的也相同,可以试一试

1
maven project -> Execute Maven Goal -> mvn -U clean install

执行以上命令之后等待完成,应该就好了

参考自stackoverflow:点击查看

]]>
- - - - - java - - - - - - - java - - idea - - Maven - - - -
- - - - - 如何学习NIO - NIO简单的例子 - - /20170227-%E5%A6%82%E4%BD%95%E5%AD%A6%E4%B9%A0NIO-NIO%E7%AE%80%E5%8D%95%E7%9A%84%E4%BE%8B%E5%AD%90/ - - 这里参照一些例子写了个简单的CS模型

例子代码:NIO应用之简单的CS模型

可以用来简单的理解一下java nio


深入的理解可以看看下面的链接。

Java NIO 系列教程:点击查看

如何学习Java的NIO?:点击查看


]]>
- - - - - java - - - - - - - java - - nio - - - -
- - - - - java.lang.IllegalArgumentException: addChild: Child name '/hisen' is not unique - - /20170227-java-lang-IllegalArgumentException-addChild-Child-name-hisen-is-not-unique/ - - 报错如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
27-Feb-2017 12:53:31.268 严重 [RMI TCP Connection(13)-127.0.0.1] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method manageApp
java.lang.IllegalArgumentException: addChild: Child name '/hisen' is not unique
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:738)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:728)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1702)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:482)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:431)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)

重要的是:

1
2
org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method manageApp
java.lang.IllegalArgumentException: addChild: Child name '/hisen' is not unique

IDEA解决办法

Project Structure -> Artifacts

查看里面是否有配置相同的Artifacts

删除即可

]]>
- - - -
- - - - - 查看linux进程 - 多重方式 - - /20170227-%E6%9F%A5%E7%9C%8Blinux%E8%BF%9B%E7%A8%8B-%E5%A4%9A%E9%87%8D%E6%96%B9%E5%BC%8F/ - - 可以使用ps命令。
它能显示当前运行中进程的相关信息,包括进程的PID。

Linux和UNIX都支持ps命令,显示所有运行中进程的相关信息。

ps命令能提供一份当前进程的快照。如果想状态可以自动刷新,可以使用top命令。

ps命令

输入下面的ps命令,显示所有运行中的进程:

1
ps aux | less

这个命令按 q 退出
后面加了“| less”就会分页显示,如果去掉会一次性显示出所有结果

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
hisen@hisen-server:~$ ps aux | less
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.2 0.5 37956 6028 ? Ss 09:03 0:02 /sbin/init
root 2 0.0 0.0 0 0 ? S 09:03 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S 09:03 0:00 [ksoftirqd/0]
root 4 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/0:0]
root 5 0.0 0.0 0 0 ? S< 09:03 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/u2:0]
root 7 0.0 0.0 0 0 ? S 09:03 0:00 [rcu_sched]
root 8 0.0 0.0 0 0 ? S 09:03 0:00 [rcu_bh]
root 9 0.0 0.0 0 0 ? S 09:03 0:00 [migration/0]
root 10 0.0 0.0 0 0 ? S 09:03 0:00 [watchdog/0]
root 11 0.0 0.0 0 0 ? S 09:03 0:00 [kdevtmpfs]
root 12 0.0 0.0 0 0 ? S< 09:03 0:00 [netns]
root 13 0.0 0.0 0 0 ? S< 09:03 0:00 [perf]
root 14 0.0 0.0 0 0 ? S 09:03 0:00 [khungtaskd]
root 15 0.0 0.0 0 0 ? S< 09:03 0:00 [writeback]
root 16 0.0 0.0 0 0 ? SN 09:03 0:00 [ksmd]
root 17 0.0 0.0 0 0 ? SN 09:03 0:00 [khugepaged]
root 18 0.0 0.0 0 0 ? S< 09:03 0:00 [crypto]
root 19 0.0 0.0 0 0 ? S< 09:03 0:00 [kintegrityd]
root 20 0.0 0.0 0 0 ? S< 09:03 0:00 [bioset]
root 21 0.0 0.0 0 0 ? S< 09:03 0:00 [kblockd]
root 22 0.0 0.0 0 0 ? S< 09:03 0:00 [ata_sff]
root 23 0.0 0.0 0 0 ? S< 09:03 0:00 [md]
root 24 0.0 0.0 0 0 ? S< 09:03 0:00 [devfreq_wq]
root 25 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/u2:1]
root 26 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/0:1]
root 28 0.0 0.0 0 0 ? S 09:03 0:00 [kswapd0]
root 29 0.0 0.0 0 0 ? S< 09:03 0:00 [vmstat]
root 30 0.0 0.0 0 0 ? S 09:03 0:00 [fsnotify_mark]
root 31 0.0 0.0 0 0 ? S 09:03 0:00 [ecryptfs-kthrea]
:


查看系统中的每个进程

1
2
ps -A
ps -e

-A:显示所有进程

a:显示终端中包括其它用户的所有进程

x:显示无控制终端的进程

显示进程的树状图

1
pstree

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
hisen@hisen-server:~$ pstree
systemd─┬─accounts-daemon─┬─{gdbus}
│ └─{gmain}
├─acpid
├─agetty
├─atd
├─cron
├─dbus-daemon
├─dhclient
├─2*[iscsid]
├─java───14*[{java}]
├─lvmetad
├─lxcfs───2*[{lxcfs}]
├─mdadm
├─polkitd─┬─{gdbus}
│ └─{gmain}
├─redis-server───2*[{redis-server}]
├─rsyslogd─┬─{in:imklog}
│ ├─{in:imuxsock}
│ └─{rs:main Q:Reg}
├─snapd───5*[{snapd}]
├─sshd───sshd───sshd───bash───pstree
├─systemd───(sd-pam)
├─systemd-journal
├─systemd-logind
├─systemd-timesyn───{sd-resolve}
└─systemd-udevd

]]>
- - - - - linux - - - - - - - linux - - - -
- - - - - Java开发的一点思考 - - /20170226-Java%E5%BC%80%E5%8F%91%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/ - - 目前的形式来说java后台市场还是挺大,虽然也有很多python和php的系统。

要想从事企业级的项目开发,你必须掌握如下要点:

  1. 掌握项目开发的基本步骤
  2. 具备极强的面向对象的分析与设计技巧
  3. 掌握用例驱动、以架构为核心的主流开发方法

没有人愿意自己一辈子就满足于掌握了一些代码实现的技巧,

别人告诉你要实现什么,你就用代码堆砌来实现别人的要求!

你必须学会从整个项目的角度去思考!

你必须学会假如你是项目经理,你该如何思考!

你必须学会假如你是架构师,你该如何思考!

你必须掌握针对某个特定问题领域的分析方法!

关于基础知识:

JavaSE

  • 基本语法、数据类型、操作符等:int、long、Integer、Long、if、else、for、while
  • 面向对象:class(类)、Object(对象)、instance(实例)、state(状态)、behavior(行为)、field、method、new、可见性(访问控制)、attribute、property、package、import、static variable、class variable、instance variable、heap、method area、stack、GC(垃圾回收)、override、overload、对象转型(casting)、多态、this、super
  • 异常处理:理解编译期错误和运行期错误的区别、Exception、RuntimeException、checked exception、unchecked exception、try、catch、finally、throw new XXXException、throws XXXException、异常处理的基本原则
  • 数组与集合:数组的定义和使用方法、Collection、List、Set、Map、ArrayList、HashSet、HashMap、Iterator、equals、hashCode、Comparable、Comparator
  • 常用类:String、intern、常量池、StringBuffer、java.util.Date、SimpleDateFormat、Regex(正则表达式)
  • 反射机制:Class、Method、Field、invoke、newInstance、BeanUtils(apache-commons)、PropertyUtils(apache-commons)
  • 输入输出流:InputStream、OutputStream、Reader、Writer、Adapter设计模式与原始流类、Decorator设计模式与包装流类、对象序列化和反序列化
  • 多线程:Thread、Runnable、sleep、wait、notify、synchronized、lock

Servlet和JSP

  • HttpServlet、doGet、doPost、HttpServletRequest、HttpServletResponse、request.getParameter()、request.setAttribute()、request.getAttribute()、request.getSession()、ServletContext、Filter、web.xml、tomcat、forward与redirect、http协议的无状态性、cookie、JSP Scope Object、<c:out …/>、<c:forEach …>

  • HTML与JavaScript:你需要能够理解常见的网页标签、理解在网页中引入JavaScript的方法、以及JavaScript的基本语法与使用方法

以上,就是你进一步学习Java所必备的基本知识。

特别是一些个专业术语和名词,看到这些名词,如果你像看到亲爹一样亲切,

那么说明你对Java的基础知识就很熟悉了,记住,仅仅是熟悉了

接下来是SSH

对于初学者来说,这三大框架被赋予了太多神秘的色彩,似乎它们是重中之重的知识!但是对于拥有多年Java开发经验的专业技术人员来说,对于那些Java牛人来说,却对这三大框架不太感冒!难道它们不重要吗?

现在很多企业都在用这三大框架,所以很多企业也把掌握这三大框架作为招聘的必备条件。不可否认的是,也有很多大型企业没有用这三大框架,这些企业经过多年发展,自身已经有一定的技术积累,也形成了自己独特的技术框架体系。这三大框架既可以说很重要,也可以说不重要。

说重要的原因在于:这三大框架对JavaEE开发中所存在的普遍的问题,提供了优美的解决方案,它们蕴含了这个行业中最NB的开发人员的努力和想法,所以,学习这三大框架,你就可以窥探到这些处于技术巅峰的牛人们究竟对一个问题是怎么想的,通过一种什么样的设计思路去解决问题的。所以,对于你来说,你没有太多项目开发的经验,经验是什么?经验就是你知道可能会遇到哪些问题,针对哪个问题可以有哪些解决方法,在某个情景下,哪种解决方法是较好的,哪种方法不太好等等!如果你没做过什么项目,你根本就不会去意识到你可能会遇到哪些问题,而这些问题往往又是非常关键的!解决得不好,会影响到你的程序的稳定性、可扩展性等等!三大框架就给初学者提供了了解你以后可能会遇到哪些问题,以及针对这些问题的解决方案!

当你了解了这三大框架为什么是重要的,那么你也就能理解,为什么这三大框架也可以说是不重要的。如果你曾经开发过很多项目,你碰到了各种各样的问题,凭着你的技术功底,逐个击破了这些问题,在这些人眼里,三大框架(是不是还有N个框架?呵呵)都是浮云!

你属于哪一种人呢?如果你没有太多项目开发经验,那么三大框架对于你来说就是非常重要的!而且,由此你也知道了该怎么去学这三大框架。对于三大框架的学习而言,着力点在于给你展示问题,并触发你自己主动的思考,我们鼓励你提出自己的想法,也许你的想法很白痴,但那毕竟是你自己的想法,如果你不知道牛人的想法,那你怎么知道自己的想法是很白痴的呢?在这种思想的碰撞过程中,你就会逐渐提高自己!所以,三大框架学完之后,你不应该只是看到一大堆配置文件,你不应该只是看到了一些Action,一些Service,一些映射文件,你不应该只知道session.save/update/delete,你不应该只是知道struts2中有一堆interceptor,你不应该只是看到一堆jar包……

如果你只是知道拷贝一堆jar包,定义一系列配置文件之后,SSH三大框架就能够运行起来了,也可以给你干活了,那么,很悲哀的是,你仍然没有掌握三大框架的精粹!请你回答以下问题:


Struts2

  • 为什么每次请求都要创建一个Action对象?
  • ModelDriven拦截器的配置中refreshModelBeforeResult解决了什么问题?
  • 为什么在web.xml中配置的StrutsPrepareAndExecuteFilter要过滤所有的请求?
  • 请你给我谈谈ValueStack?
  • Struts2是如何实现MVC设计模式的?

Spring

  • 你为什么要用Spring?
  • 请你聊一聊IOC/DI?
  • 什么是声明式的事务管理?为什么要使用声明式的事务管理?Spring如何实现声明式的事务管理?
  • 把spring和hibernate集成,定义事务管理特性的时候,为何要将除了添加、删除、更新操作之外的方法,即主要执行查询任务的方法定义为read-only?

Hibernate

  • 请你谈谈你对ORM映射的理解?
  • 很多人说Hibernate不适合大项目,性能有问题,你是如何理解的?
  • Hibernate为什么一定要定义一个数据库标识?
  • 为什么Hibernate建议你的实体类实现hashCode和equals方法?
  • 谈谈你对Hibernate实体类中的数据库标识与数据库主键之间关系的认识?
  • 谈谈你对Hibernate关联映射与数据库外键之间关系的认识?
  • 调用session.save()方法,hibernate一定会发出insert语句吗?谈谈你的理解
  • 调用session.update()方法,hibernate一定会发出update语句吗?谈谈你的理解
  • 请你聊一下以下名词、概念或用法:lazy、lazy=”extra”、inverse、fetch、fetch=”join”、fetch=”subselect”、batch-size
  • 配置了lazy=”true”一定会实现懒加载吗?
  • 请你谈谈Hibernate中的“N+1”问题
  • 请你谈谈Hibernate有哪些最佳实践?

以上并非SSH中全部重点的问题,但它们能考察你能否灵活运用SSH框架!如果你能深刻理解这些问题,再配以合适的实战项目训练,你也会逐渐成为牛人!

最后是项目开发能力 ##:

不管你是学Java还是别的技术,你的根本目的在于给客户创造价值!否则,你下大力气学习的东西,随着技术的进步和更新,很快就会过时!所以,技术的核心在于用技术创造有价值的成果!也就是说,客户需要什么,你就要用技术把客户需要的东西给他造出来!一个公司之所以要用各种福利条件极力挽留你,是因为你能够给公司带来极高的利益!那么,你有什么可以给公司利用的呢?公司最看重你的哪方面的能力呢?

做项目需要的能力很多,其中最核心最基础的就是建模能力(现在最主流的就是面向对象建模!)。什么是建模能力呢?


我给大家一个面试题
一个保险公司的保险卡管理模块:销售人员领取保险卡信息(保险卡数量、卡号、领取日期),然后直接销售给客户,销售完毕后,将保险卡信息录入保险公司系统内部(销售人员信息、购买人信息、购买的保险卡数量、卡号等),客户登录保险公司网站激活保险卡,需要填写(保险卡卡号、激活密码、被保险人信息、受益人信息)

要求就是:如果这个模块交给你来做,你要怎么做?你要解决哪些问题?你可否画个图,给我描述一下你的想法是什么吗?

这只是一个面试题而已,因为只有简单几句话,所以我把它放到这里,让大家感受一下所谓建模要解决什么问题。而业务领域的问题实在是太多了!也许一个几十上百页的需求文档才能把某个业务领域的问题描述清楚,而你的职责就是要把它们实现出来!

某个公司要开发一个考勤管理系统,要求与现有的人力资源系统对接,你是主要的技术负责人,那么,你要做哪些工作呢?

某ERP项目要实现一个排班管理模块,交给你去完成,你如何去完成呢?


不要抱怨项目经理给你的信息太少(只有几句话),不要抱怨客户没有描述清楚他们的需求……你的价值就在于理顺所有的问题,用各种手段获得你想要的信息,按照一定的思路汇总,并在特定的时间里逐个解决它!

你应该意识到学Java不是一个坦克大战、一个网络飞车、一个CMS、一个DRP、一个OA那么简单,你不要沉迷于那些技术细节(虽然也是有必要的,但不要钻牛角尖),不要满足于实现了CRUD式的项目需求(虽然这是基础中的基础),在你的前方,永远有一个目标在那里,需要你去努力追赶!

今后你将面对更加繁杂的需求,你学习项目的唯一目的,就是:学习如何将需求转化为实现,如何对需求进行分析,如何建立概念模型,如何理顺各种概念之间的关系,如何进行设计,如何选择合适的技术来实现你的设计方案,如何对你的实现进行测试,如何解决你所遇到的形形色色的问题(性能、需求变更等)。当你真正到公司里面从事了几年开发之后,你就会同意我的说法!


利用Java找工作,需要的就是项目经验,项目经验就是理解项目开发的基本过程,理解项目的分析方法,理解项目的设计思路,理解项目的实现技巧,理解项目的测试方法,理解项目中各种问题的解决方案。


码农只是复制粘贴,并不注重原理,说不出所以然,所以做了几年还只能是码农。

加油,共勉!

]]>
- - - - - java - - - - - - - java - - - -
- - - - - Ubuntu 16 LTS 安装zookeeper并开机启动 - - /20170224-Ubuntu-16-LTS-%E5%AE%89%E8%A3%85zookeeper%E5%B9%B6%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8/ - - 一、安装
1
sudo apt-get install zookeeper

默认信息

1
2
3
4
#安装路径
/usr/share/zookeeper
#配置文件
/etc/zookeeper/conf/zoo.cfg

二、启动服务端

1
hisen@hisen-server:/usr/share/zookeeper/bin$ sudo sh zkServer.sh

如果报错

1
2
3
4
5
6
7
zkServer.sh: 81: /home/xxx/zookeeper-3.4.6/bin/zkEnv.sh: Syntax error: "(" unexpected (expecting "fi")
网上找了一圈原因,大概意思就是脚本里面用到的shell版本与系统当前使用的shell版本不兼容,导致异常。
查看当前ubuntu系统的shell,默认是使用dash,但是脚本里面是使用的bash,问题就在这里了。
解决步骤:
修改当前系统的shell版本:
dpkg-reconfigure dash
Tab 移动到NO 回车即可(选择否)

三、验证是否成功

1
2
3
hisen@hisen-server:/usr/share/zookeeper/bin$ sudozkCli.sh -server localhost:2181
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]

出现上面的信息说明成功了

四、设置开机启动

1.创建配置文件

1
sudo vi /etc/init.d/zookeeper

添加以下信息,注意自己的相关路径是否相同,不同修改之

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/sh
#Configurations injected by install_server below....
EXEC=/usr/share/zookeeper/bin/zkServer.sh
ZOO_LOG_DIR="/var/zookeeper"
JAVA_HOME=/usr/hisen/soft/jdk8
PATH=${JAVA_HOME}/bin:$PATH
###############
# SysV Init Information
# chkconfig: - 58 74
# description: zookeeper is the zookeeper daemon.
### BEGIN INIT INFO
# Provides: zookeeper
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Should-Start: $syslog $named
# Should-Stop: $syslog $named
# Short-Description: start and stop zookeeper
# Description: zookeeper daemon
### END INIT INFO
case $1 in
start) /usr/share/zookeeper/bin/zkServer.sh start;;
stop) /usr/share/zookeeper/bin/zkServer.sh stop;;
status) /usr/share/zookeeper/bin/zkServer.sh status;;
restart) /usr/share/zookeeper/bin/zkServer.sh restart;;
*) echo "require start|stop|status|restart" ;;
esac

2.授权

1
sudo chmod +x zookeeper

3.安装开机启动管理软件(一般自带)

1
sudo apt-get install rcconf

4.进入管理界面

1
sudo rcconf

↑ ↓ 移动光标,空格键选中zookeeper

Tab 使光标移动到OK 回车即可

]]>
- - - - - linux - - - - - - - zookeeper - - - -
- - - - - Ubuntu 16 LTS 安装redis - apt-get install redis-server - - /20170223-Ubuntu-16-LTS-%E5%AE%89%E8%A3%85redis-ape-get-install-redis-server/ - - 配置文件的路径: /etc/redis/redis.conf

redis服务路径: /etc/init.d/redis-server

默认是开机启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#安装
hisen@hisen-server:/$ sudo apt-get install redis-server
#打开服务
hisen@hisen-server:/$ service redis start
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to start 'redis-server.service'.
Authenticating as: hisen,,, (hisen)
Password:
==== AUTHENTICATION COMPLETE ===
#打开客户端
hisen@hisen-server:/$ redis-cli
#操作数据库
127.0.0.1:6379> set hisen hisen.me
OK
#获取数据
127.0.0.1:6379> get hisen
"hisen.me"
127.0.0.1:6379>
#停止数据库
hisen@hisen-server:/$ service redis stop
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to stop 'redis-server.service'.
Authenticating as: hisen,,, (hisen)
Password:
==== AUTHENTICATION COMPLETE ===
hisen@hisen-server:/$
]]>
- - - - - linux - - - - - - - redis - - - -
- - - - - BeanCreationException: Error creating bean with name 'xxxService' - - /20170223-BeanCreationException-Error-creating-bean-with-name-xxxService/ - - 出现的问题:

1
2
3
[platform] ERROR 2017-02-22 17:46:05,756 [RMI TCP Connection(4)-127.0.0.1] org.springframework.web.context.ContextLoader.() | Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pmTranLimitLiteServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.msds.zkutil.ZkLockFactory] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.annotation.Resource(mappedName=, shareable=true, description=, name=, type=class java.lang.Object, authenticationType=CONTAINER, lookup=)}
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:306) ~[spring-context-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1116) ~[spring-beans-3.2.4.RELEASE.jar:3.2.4.RELEASE]

最重要的是这句

1
2
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name'pmTranLimitLiteServiceImpl'

出现的原因:
缺少相关的jar包或者依赖

建议不要自己配置idea的module和artificts

直接在pom.xml文件添加

1
2
<artifactId>hisen-project</artifactId><!--加在这句话后面-->
<packaging>war</packaging><!--加上这句话就会自动给你打war包-->


其他原因

开始不知道什么问题,后来搜索这个服务。

发现这跟dubbo有关,于是百度搜索进了官网

没想到常见问题里面就有说这个事情

1
2
3
13. 出现org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xxxService': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Method must not be null怎么办?

通常是classpath下存在spring多个版本的jar包,排除掉不需要的spring包即可。

更多dubbo问题:点击查看

]]>
- - - -
- - - - - IDEA - Java.Lang.OutOfMemoryError: PermGen Space - - /20170222-Java-Lang-OutOfMemoryError-PermGen-Space/ - - Java.Lang.OutOfMemoryError: PermGen Space

Tomcat只分配了非常小的PermGen内存,这里重新设置一下

直接在配置tomcat的时候,在VM options填入:

1
-XX:PermSize=97m -XX:MaxPermSize=256m

]]>
- - - - - java - - - - - - - idea - - tomcat - - - -
- - - - - Maven目录下*.lastupdated命令行批量删除 - - /20170220-Maven%E7%9B%AE%E5%BD%95%E4%B8%8B-lastupdated%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4/ - - windows系统

1
2
cd %userprofile%\.m2\repository
for /r %i in (*.lastUpdated) do del %i

或者新建一个bat文件,批处理。就不用每次都在cmd敲命令了

1
2
3
4
5
6
7
8
9
10
11
12
13
@echo off
echo 确认删除maven仓库下*.lastUpdated文件?
pause
::create by hisenyuan(hisenyuan@gmail.com)

::这里写你的仓库路径
set REPOSITORY_PATH=C:\hisenwork\soft\maven
echo 正在搜索...
for /f "delims=" %%i in ('dir /b /s "%REPOSITORY_PATH%\*lastUpdated*"') do (
del /s /q %%i
)
echo 完毕
pause

linux系统

1
find /app/maven/localRepository -name "*.lastUpdated" -exec grep -q "Could not transfer" {} \; -print -exec rm {} \;

]]>
- - - - - java - - - - - - - java - - maven - - - -
- - - - - IDEA maven项目通过mongo-java-driver折腾一下MongoDB - - /20170220-IDEA-maven%E9%A1%B9%E7%9B%AE%E9%80%9A%E8%BF%87mongo-java-driver%E6%8A%98%E8%85%BE%E4%B8%80%E4%B8%8BMongoDB/ - - 这几天折腾ubuntu然后安装了下mongodb

通过Oracle VM VirtualBox端口转发,连接了虚拟机的MongoDB

1.用idea创建一个maven项目

2.在pom.xml中添加mongodb java驱动

1
2
3
4
5
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.2.2</version>
</dependency>

3.参考官方:MongoDB Driver Quick Tour

本用例github地址:mongodbTest

贴下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package com.hisen.jdbc;

import com.mongodb.Block;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;

import java.util.ArrayList;
import java.util.List;

import static com.mongodb.client.model.Accumulators.sum;
import static com.mongodb.client.model.Aggregates.group;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Projections.excludeId;
import static com.mongodb.client.model.Sorts.descending;
import static com.mongodb.client.model.Updates.inc;
import static com.mongodb.client.model.Updates.set;
import static java.util.Collections.singletonList;

/**
* Created by hisenyuan on 2017/2/20 at 13:42.
*/
public class QuickTour {
public static void main(String[] args) {
MongoClient mongoClient = new MongoClient("localhost", 27017);
MongoDatabase database = mongoClient.getDatabase("mydb");
//gets the collection test
MongoCollection<Document> collection = database.getCollection("test");
//插入数据
//insert(collection);
//插入大量数据
insertMany(collection);
//统计条数
count(collection);
//打印查询到的第一条数据
print(collection);
//打印所有数据
findAll(collection);
//按条件查询一条数据
findOneWithFilter(collection);
//按条件查询一个Set集合
getSet(collection);
//排序,输出第一条
sortDocuments(collection);
//获取指定的field
projectingFields(collection);
aggregations(collection);
//更新
update(collection);
//删除数据
delete(collection);
}

/**
* 插入单个文档
* @param collection
*/
public static void insert(MongoCollection<Document> collection) {
//create document
Document doc = new Document("name", "MongoDB")
.append("type", "database")
.append("count", 1)
.append("info", new Document("x", 203).append("y", 102));
//insert the document into the collection
collection.insertOne(doc);
}

/**
* 一次性插入多条数据,用ArrayList拼装
* @param collection
*/
public static void insertMany(MongoCollection<Document> collection) {
//Create the documents in a loop
List<Document> documents = new ArrayList<Document>();
for (int i = 0; i < 100; i++) {
documents.add(new Document("i", i));
}
collection.insertMany(documents);
}

/**
* 统计集合中的总条数
* @param collection
*/
public static void count(MongoCollection<Document> collection) {
//Count Documents in A Collection
System.out.println("总条数:" + collection.count());
}

/**
* 打印查询到的第一条数据
* @param collection
*/
public static void print(MongoCollection<Document> collection) {
//prints the first document found in the collection
Document myDoc = collection.find().first();
System.out.println(myDoc.toJson());
}

/**
* 查询集合中所有的数据
* 不推荐使用foreach循环
* @param collection
*/
public static void findAll(MongoCollection<Document> collection) {
System.out.println("----------------输出所有数据----------------");
MongoCursor<Document> cursor = collection.find().iterator();
try {
while (cursor.hasNext()) {
System.out.println(cursor.next().toJson());
}
} finally {
cursor.close();
}
}

/**
* 条件查询单条数据
* @param collection
*/
public static void findOneWithFilter(MongoCollection<Document> collection) {
//Get A Single Document with a Query Filter
Document myDoc = collection.find(eq("i", 71)).first();
System.out.println(myDoc.toJson());
}

/**
* 获得一个Set集合的数据
* @param collection
*/
public static void getSet(MongoCollection<Document> collection) {
// now use a range query to get a larger subset
Block<Document> printBlock = new Block<Document>() {
public void apply(final Document document) {
System.out.println(document.toJson());
}
};
//i的值大于98的数据
System.out.println("----------------i大于98的数据----------------");
collection.find(gt("i", 98)).forEach(printBlock);
//50 - 51 gt:大于 lte:小于等于
System.out.println("----------------50<i<=51的数据----------------");
collection.find(and(gt("i", 50), lte("i", 51))).forEach(printBlock);
}

/**
* 排序
* @param collection
*/
public static void sortDocuments(MongoCollection<Document> collection) {
//根据i的值进行排序
Document myDoc = collection.find(exists("i")).sort(descending("i")).first();
System.out.println("----------------排序,输出第一条----------------");
System.out.println(myDoc.toJson());
}

/**
* 获取想要的field(字段)
* @param collection
*/
public static void projectingFields(MongoCollection<Document> collection) {
System.out.println("----------------获取指定field,输出第一条----------------");
//排除ID字段
Document myDoc = collection.find().projection(excludeId()).first();
System.out.println(myDoc.toJson());
}

public static void aggregations(MongoCollection<Document> collection) {
/*
collection.aggregate(asList(
match(gt("i", 0)),
project(Document.parse("{ITimes10: {$multiply: ['$i', 10]}}")))
).forEach(printBlock);
*/
//求和
Document myDoc = collection.aggregate(singletonList(group(null, sum("total", "$i")))).first();
System.out.println(myDoc.toJson());
}

/**
* 更新document的方法
* @param collection
*/
public static void update(MongoCollection<Document> collection) {
//更新一个,如果不引入下面这个包,set方法会报错
//import static com.mongodb.client.model.Updates.*;
collection.updateOne(eq("i", 10), set("i", 110));
//更新多个,小于100的都加100
UpdateResult updateResult = collection.updateMany(lt("i", 190), inc("i", 100));
System.out.println("----------------更新条数----------------");
System.out.println(updateResult.getModifiedCount());
}

/**
* 删除数据的方法
* @param collection
*/
public static void delete(MongoCollection<Document> collection) {
collection.deleteOne(eq("i", 210));
//gte 大于等于100的都删除
DeleteResult deleteResult = collection.deleteMany(gte("i", 100));
System.out.println("----------------删除条数----------------");
System.out.println(deleteResult.getDeletedCount());
}
/*
public static void bulk(MongoCollection<Document> collection) {
// 2. Ordered bulk operation - order is guarenteed
collection.bulkWrite(
Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
new InsertOneModel<>(new Document("_id", 5)),
new InsertOneModel<>(new Document("_id", 6)),
new UpdateOneModel<>(new Document("_id", 1),
new Document("$set", new Document("x", 2))),
new DeleteOneModel<>(new Document("_id", 2)),
new ReplaceOneModel<>(new Document("_id", 3),
new Document("_id", 3).append("x", 4))));
// 2. Unordered bulk operation - no guarantee of order of operation
collection.bulkWrite(
Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
new InsertOneModel<>(new Document("_id", 5)),
new InsertOneModel<>(new Document("_id", 6)),
new UpdateOneModel<>(new Document("_id", 1),
new Document("$set", new Document("x", 2))),
new DeleteOneModel<>(new Document("_id", 2)),
new ReplaceOneModel<>(new Document("_id", 3),
new Document("_id", 3).append("x", 4))),
new BulkWriteOptions().ordered(false));
}
*/
}

]]>
- - - - - java - - - - - - - java - - mongo - - - -
- - - - - Windows10开机bat启动VirtualBox虚拟机并且后台运行 - - /20170220-Windows10%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8VirtualBox%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%B9%B6%E4%B8%94%E5%90%8E%E5%8F%B0%E8%BF%90%E8%A1%8C/ - - 1.制作启动脚本

新建一个start.bat文件,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@echo off

echo 本命令可让us在后台运行
echo 启动之后可以关闭本窗口

::进入虚拟机目录
cd C:\"Program Files"\Oracle\VirtualBox

::执行相关命令 同时启动两台虚拟机
VBoxManage startvm "us" --type headless
::VBoxManage startvm "ubuntu" --type headless

::执行完之后按回车退出窗口
pause

2.设置开机启动

把start.bat文件复制到[启动]文件夹里面

[启动]文件夹路径

1
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp

文件管理器地址栏显示大概是这样

1
Windows > [开始]菜单 > 程序 > 启动

放进去之后就可以开机启动了!

启动之后Xshell连接即可

]]>
- - - - - 软件 - - - - - - - linux - - VirtualBox - - bat - - - -
- - - - - Ubuntu16.04使用阿里云镜像安装Mongodb & 配置 - - /20170219-Ubuntu16-04%E4%BD%BF%E7%94%A8%E9%98%BF%E9%87%8C%E4%BA%91%E9%95%9C%E5%83%8F%E5%AE%89%E8%A3%85Mongodb-%E9%85%8D%E7%BD%AE/ - - 一、使用阿里云镜像安装Mongodb

1 > 添加 MongoDB 公共GPG钥匙

1
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

2 > 创建列表文件

这里把官网repo.mongodb.org

换成了mirrors.aliyun.com

1
echo "deb http://mirrors.aliyun.com/mongodb/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

3 > 重新加载本地包数据库

1
sudo apt-get update

4 > 安装MongoDB

1
sudo apt-get install -y mongodb-org

5 > 启动MongoDB

1
sudo service mongod start

6 > 打开MongoDB客户端

1
sudo mongo

7 > 关闭MongoDB

1
sudo service mongod stop

展示一下

1
2
3
4
5
6
7
hisen@hisen-server:~$ sudo service mongod start
hisen@hisen-server:~$ mongo
MongoDB shell version: 3.2.12
connecting to: test
> 1 + 1
2
>

安装成功

MongoDB默认的数据文件和日志文件分别存储在下面的位置

数据文件:/var/lib/mongodb

日志文件:/var/log/mongodb

你可以修改/etc/mongod.conf 文件来改变相应的存储位置。

如果你想改变运行MongoDB的用户

你必须把 /var/lib/mongodb

和 /var/log/mongodb 2个目录的访问权限付给该用户

二、配置MongoDB

1允许远程访问

绑定ip

1
$ sudo vim /etc/mongod.conf

打开配置文件,添加需要增加的

不建议采用注释掉 bindIP 的方案,非常容易受到攻击

1
2
3
4
# network interfaces  
net:
port: 27017
bindIp: 0.0.0.0

接受所有ip

重启

1
$ sudo service mongod restart

2.配置防火墙 (不配置也行)
Ubuntu16.04 桌面版默认没有安装好 ipTable,用如下命令安装

1
2
3
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install iptables-persistent

安装过程中,弹窗选择YES

安装完成后:

1
sudo iptables -A INPUT -p tcp --dport 27017 -j ACCEPT

]]>
- - - -
- - - - - Oracle VM VirtualBox命令行打开虚拟机并且后台运行 - - /20170219-Oracle-VM-VirtualBox%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%89%93%E5%BC%80%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%B9%B6%E4%B8%94%E5%90%8E%E5%8F%B0%E8%BF%90%E8%A1%8C/ - - 这几天在折腾Ubuntu虚拟机,想着也就让他做一台服务器罢了

没想到安装之后发现一直没法让VirtualBox隐藏到托盘

按正常程序走,打开一个虚拟机会出现两个GUI界面:

  1. VirtualBox界面
  2. 虚拟机界面

第一个可以在打开虚拟机之后关闭,第二个不能关闭也不能隐藏到托盘

痛苦!!!

这里给出解决方案:通过cmd命令行启动并且后台运行

一、进入VirtualBox安装目录,我这里是

1
C:\tool\Oracle\VirtualBox>

二、列出所有安装的虚拟机

1
2
3
4
C:\tool\Oracle\VirtualBox>VBoxManage list vms
"centos" {162f777f-e3c4-4717-8b5b-4a4e43a8b552}
"debian" {48ebecba-77ec-483d-ab73-bf9ee777e2f6}
"ubuntu" {d8050b3a-b04b-4fe9-8a90-16086dac0ae9}

前面是NAME,后面是UUID,之后的name用这连个代替都可以

三、命令行启动虚拟机

1
2
3
C:\tool\Oracle\VirtualBox>VBoxManage startvm "ubuntu" --type headless
Waiting for VM "ubuntu" to power on...
VM "ubuntu" has been successfully started.

headless是在后台运行,并且默认开启vrdp服务,可以通过远程桌面工具来访问

下面是打开ubuntu之后用Xshell链接的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Connecting to 127.0.0.1:2222...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.

Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-62-generic x86_64)

* Documentation: https://help.ubuntu.com/

362 个可升级软件包。
0 个安全更新。

Last login: Sat Feb 18 16:25:58 2017 from 10.0.2.2
hisen@hisen-VirtualBox:~$ ls
download 公共的 模板 视频 图片 文档 下载 音乐 桌面

四、所有相关的命令

下面所有的NAME都可以用二中的name和UUID代替

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 列出所有安装的虚拟机
VBoxManage list vms

# 后台打开,无界面
VBoxManage startvm "NAME" --type headless

# gui方式启动,跟桌面点击没有区别
VBoxManage startvm NAME --type gui

# 列出运行中的虚拟机
VBoxManage list runningvms

# 关闭虚拟机,等价于点击系统关闭按钮,正常关机
VBoxManage controlvm NAME acpipowerbutton

# 关闭虚拟机,等价于直接关闭电源,非正常关机
VBoxManage controlvm NAME poweroff

# 暂停虚拟机的运行
VBoxManage controlvm NAME pause

# 恢复暂停的虚拟机
VBoxManage controlvm NAME resume

# 保存当前虚拟机的运行状态
VBoxManage controlvm NAME savestate

]]>
- - - - - linux - - - - - - - linux - - ubuntu - - - -
- - - - - Ubuntu 16 LTS 关闭图形界面 - - /20170218-Ubuntu-16-LTS-%E5%85%B3%E9%97%AD%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2/ - - 有两个方法。

第一个方法

到了图形化界面,打开terminal(终端)执行

1
sudo init 3

就会跳转到命令行界面,并且只有命令行

就是一个全屏的terminal。

第二个方法

在安装ubuntu的时候,有选择是否安装图形化界面的选项,选择不安装,那么系统将不带有图形化界面而默认进入命令行界面。

第三个方法

http://hisen.me/20170219-Oracle-VM-VirtualBox%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%89%93%E5%BC%80%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%B9%B6%E4%B8%94%E5%90%8E%E5%8F%B0%E8%BF%90%E8%A1%8C/

第四个方法

直接安装Ubuntu Server,目前我就是这样

不过装了之后还是推荐第三个方法!!!

]]>
- - - - - linux - - - - - - - linux - - Ubuntu - - - -
- - - - - Windows ssh连接 VirtualBox Ubuntu 16虚拟机 - - /20170218-Windows%20ssh%E8%BF%9E%E6%8E%A5VirtualBox-Ubuntu-16%E8%99%9A%E6%8B%9F%E6%9C%BA/ - - 安装完虚拟机之后想在windows下用xshell链接Ubuntu虚拟机

这种配置下,虚拟机能上网,又能跟win连接,感觉很完美

VirtualBox的端口转发很不错,可以转发tomcat什么的

准备工作

1.给Ubuntu安装openssh-server

1
sudo apt-get install openssh-server

2.查看虚拟机ip 我的是:10.0.2.15(看上面那段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hisen@hisen-VirtualBox:/$ ifconfig -a
enp0s3 Link encap:以太网 硬件地址 08:00:27:45:f3:35
inet 地址:10.0.2.15 广播:10.0.2.255 掩码:255.255.255.0
inet6 地址: fe80::ed57:82d2:60cb:7f96/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:169175 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:34436 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:210738565 (210.7 MB) 发送字节:3633455 (3.6 MB)

lo Link encap:本地环回
inet 地址:127.0.0.1 掩码:255.0.0.0
inet6 地址: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 跃点数:1
接收数据包:1718 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:1718 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1
接收字节:234769 (234.7 KB) 发送字节:234769 (234.7 KB)

VirtualBox设置端口转发

  1. 在VirtualBox启动页面,右键Ubuntu —>设置
  2. 网络 —> 连接方式 —> 网络地址转换(NAT)
  3. 高级 —> 端口转发 —> 点击添加按钮
名称协议主机IP主机端口子系统IP子系统端口
sshTCP127.0.0.1222210.0.2.1522

子系统ip写你的虚拟机ip即可

xshell链接Ubuntu虚拟机

在xshell链接Ubuntu虚拟机的时候

ip写上麦的主机ip:127.0.0.1

端口写上面的主机端口:2222

然后上面配置的端口转发就可以转发到虚拟机上,顺利连接!!!

]]>
- - - - - linux - - - - - - - linux - - shell - - ssh - - - -
- - - - - Ubuntu 16 换阿里云源 - - /20170218-Ubuntu-16-%E6%8D%A2%E9%98%BF%E9%87%8C%E4%BA%91%E6%BA%90/ - -
  • sudo vi /etc/apt/sources.list
  • 删除里面所有的内容,添加下面的内容
  • sudo apt-get update
  • 三步完成之后即可!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
    ]]>
    - - - - - linux - - - - - - - ubuntu - - 阿里云 - - - -
    - - - - - Linux-Ubuntu安装:JDK & Tomcat & Maven - 简单教程 - - /20170218-Ubuntu%E5%AE%89%E8%A3%85JDK8-Tomcat-%E7%AE%80%E5%8D%95%E6%95%99%E7%A8%8B/ - - 在网速搜索很多教程,感觉写的都太难了我去

    准备工作:

    1. 下载JDK,并解压(选择适合自己的版本:地址)
    2. 下载Tomcat,并解压(选择适合自己的版本:地址)
    3. 下载Maven,并解压(选择适合自己的版本:地址)

    目录约定:

    1. java路径:/usr/hisen/soft/java/jdk8
    2. tomcat路径:/usr/hisen/soft/tomcat/tomcat8
    3. maven路径:/usr/hisen/soft/maven/maven-3.5.0

    说明以上路径都是解压之后的,请解压之后自行重命名文件夹等工作

    下面开始配置环境变量:

    1
    sudo vi /etc/profile

    底部添加:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #java的环境变量配置
    export JAVA_HOME=/usr/hisen/soft/java/jdk8
    export JRE_HOME=$JAVA_HOME/jre
    export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib
    export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin

    #tomcat的环境变量配置
    export CATALINA_HOME=/usr/hisen/soft/tomcat/tomcat8
    export CLASSPATH=.:$JAVA_HOME/lib:$CATALINA_HOME/lib
    export PATH=$PATH:$CATALINA_HOME/bin

    #maven环境变量
    export MAVEN_HOME=/usr/hisen/soft/maven/maven-3.5.0
    export MAVEN_OPTS="-Xms256m -Xmx512m"
    export PATH=${MAVEN_HOME}/bin:$PATH

    让刚刚的配置生效:

    1
    source /etc/profile

    查看maven

    1
    mvn -v

    查看java版本

    1
    java -version

    如果还是默认的OpenJDK

    1
    2
    3
    4
    5
    sudo update-alternatives --install /usr/bin/java java /usr/hisen/soft/java/jdk8/bin/java 300  
    sudo update-alternatives --install /usr/bin/javac javac /usr/hisen/soft/java/jdk8/bin/javac 300

    #选择默认JDK即可
    sudo update-alternatives --config java

    进入tomcat的bin目录

    1
    sudo vi catalina.sh

    顶部添加

    1
    2
    3
    #让tomcat知道java在哪里
    JAVA_HOME=/usr/hisen/soft/java/jdk8
    JRE_HOME=$JAVA_HOME/jre

    之后进入在tomcat bin目录执行

    1
    2
    3
    4
    5
    6
    7
    hisen@hisen:/usr/hisen/soft/tomcat/tomcat8/bin$ sudo sh startup.sh
    Using CATALINA_BASE: /usr/hisen/soft/tomcat/tomcat8
    Using CATALINA_HOME: /usr/hisen/soft/tomcat/tomcat8
    Using CATALINA_TMPDIR: /usr/hisen/soft/tomcat/tomcat8/temp
    Using JRE_HOME: /usr/hisen/soft/java/jdk8/jre
    Using CLASSPATH: /usr/hisen/soft/tomcat/tomcat8/bin/bootstrap.jar:/usr/hisen/soft/tomcat/tomcat8/bin/tomcat-juli.jar
    Tomcat started.

    这样就启动了tomcat!!!!

    注意

    如果没有在 catalina.sh 添加java路径,会报错

    1
    2
    3
    hisen@hisen-VirtualBox:/usr/hisen/soft/tomcat/tomcat8/bin$ sudo sh startup.sh
    Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
    At least one of these environment variable is needed to run this program

    ]]>
    - - - - - linux - - - - - - - linux - - - -
    - - - - - centos更换yum源为阿里云 - 三步搞定 - - /20170217-centos%E6%9B%B4%E6%8D%A2yum%E6%BA%90%E4%B8%BA%E9%98%BF%E9%87%8C%E4%BA%91-%E4%B8%89%E9%83%A8%E6%90%9E%E5%AE%9A/ - - 阿里云是最近新出的一个镜像源。得益于阿里云的高速发展,这么大的需求,肯定会推出自己的镜像源。
    阿里云Linux安装镜像源地址:http://mirrors.aliyun.com/

    CentOS系统更换软件安装源

    第一步:备份你的原镜像文件,以免出错后可以恢复。

    1、备份
    mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

    2、下载新的CentOS-Base.repo 到/etc/yum.repos.d/

    (如果无wget命令,底部有具体说明)

    CentOS 5

    1
    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-5.repo

    CentOS 6

    1
    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo

    CentOS 7

    1
    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

    3、之后运行yum makecache生成缓存


    ps:如果你跟我一样苦逼:

    1
    -bash: wget: command not found

    然后:

    1
    yum -y install wget #失败

    那么你可以直接选择上面对应系统的文件下载链接

    下载好文件之后改名为CentOS-Base.repo

    直接放到/etc/yum.repos.d/目录下即可

    ]]>
    - - - - - linux - - - - - - - linux - - centos - - - -
    - - - - - HashMap和Hashtable的区别 - - /20170217-HashMap%E5%92%8CHashtable%E7%9A%84%E5%8C%BA%E5%88%AB/ - - 在Java 2以前,一般使用Hashtable来映射键值和元素。为了使用Java集合框架,Java对Hashtable进行了重新设计,但是,为了向后兼容保留了所有的方法。Hashtable实现了Map接口,除了Hashtable具有同步功能之外,它与HashMap的用法是一样的。·在使用时一般是用ArrayList代替Vector,LinkedList代替Stack,HashMap代替HashTable,即使在多线程中需要同步,也是用同步包装类。

    另外在使用上还有一些小的差异,比如:

    1. HashTable的key和value都不允许为null值,而HashMap的key和value则都是允许null值的。这个其实没有好坏之分,只是Sun为了统一Collection的操作特性而改进的。
    2. HashTable有一个contains(Object value)方法,功能上与containsValue(Object value)一样,但是在实现上花销更大,现在已不推荐使用。而HashMap只有containsValue(Object value)方法。
    3. HashTable使用Enumeration,HashMap使用Iterator。Iterator其实与Enmeration功能上很相似,只是多了删除的功能。用Iterator不过是在名字上变得更为贴切一些。模式的另外一个很重要的功用,就是能够形成一种交流的语言(或者说文化)。有时候,你说Enumeration大家都不明白,说Iterator就都明白了。

    在实现上两者已有一些差异,这里简单说明一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public Hashtable(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal Load: "+loadFactor);
    if (initialCapacity==0)
    initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry[initialCapacity];
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    initHashSeedAsNeeded(initialCapacity);
    }
    public Hashtable(int initialCapacity) {
    this(initialCapacity, 0.75f);
    }
    public Hashtable() {
    this(11, 0.75f);
    }


    public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal initial capacity: " +
    initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
    initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal load factor: " +
    loadFactor);
    this.loadFactor = loadFactor;
    threshold = initialCapacity;
    init();
    }
    public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    HashTable中构造hash数组时initialCapacity默认大小是11,增加的方式是 old*2+1。HashMap中构造hash数组时initialCapacity默认大小是16,而且一定是2的指数。对于哈希值的使用也有所不同,HashTable直接使用对象的hashCode,代码是这样的:

    1
    2
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;

    而HashMap重新计算hash值,而且用与代替求模:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int hash = hash(k);
    int i = indexFor(hash, table.length);

    static int hash(Object x) {
      int h = x.hashCode();

      h += ~(h << 9);
      h ^= (h >>> 14);
      h += (h << 4);
      h ^= (h >>> 10);
      return h;
    }
    static int indexFor(int h, int length) {
      return h & (length-1);
    }

    仅供参考,内容来源于互联网

    ]]>
    - - - - - java - - - - - - - HashMap - - Hashtable - - - -
    - - - - - java编译错误:程序包javax.servlet不存在javax.servlet.* - - /20170217-java%E7%BC%96%E8%AF%91%E9%94%99%E8%AF%AF%EF%BC%9A%E7%A8%8B%E5%BA%8F%E5%8C%85javax-servlet%E4%B8%8D%E5%AD%98%E5%9C%A8javax-servlet/ - - 之前用myeclipse开发的项目

    今天导入到IDEA中去,发现编译出问题

    ava编译错误:程序包javax.servlet不存在javax.servlet.*


    原因大概是myeclipse中可以选择Java EE项目

    而idea没有,缺少 servlet-api.jar 这个jar包


    解决办法:

    1. 复制tomcat文件夹下lib—>servlet-api.jar 这个jar包
    2. 添加到IDEA项目中去:粘贴到External Library目录下

    即可

    ]]>
    - - - - - java - - - - - - - java - - - -
    - - - - - hexo百度抓取失败解决办法 - - /20170216-hexo%E7%99%BE%E5%BA%A6%E6%8A%93%E5%8F%96%E5%A4%B1%E8%B4%A5%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/ - - 上一篇帖子说明了一下百度抓取不到的原因是因为github把百度爬虫给屏蔽了

    这里给出的解决办法是用hexo自动提交插件

    需要获取一个自动提交的token

    1. 注册百度站长工具:http://zhanzhang.baidu.com
    2. 添加你的hexo域名
    3. 找到网页抓取 - 链接提交 - 下拉选择你的hexo站点
    4. 数据提交方式 - 自动提交 - 主动推送(实时)
    5. 推送接口
    6. 接口调用地址: http://data.zz.baidu.com/urls?site=hisen.me&token=XmxXyESxyz1hANxE
    7. 复制上面token=后面的内容,那就是你的token

    安装自动提交插件:

    1. npm install hexo-baidu-url-submit –save
    2. 编辑站点配置文件_config.yml,添加一下内容
      1
      2
      3
      4
      5
      baidu_url_submit:
      count: 1 ## 提交最新的一个链接
      host: www.hui-wang.info ## 在百度站长平台中注册的域名
      token: your_token ## 请注意这是您的秘钥, 所以请不要把博客源代码发布在公众仓库里!
      path: baidu_urls.txt ## 文本文档的地址, 新链接会保存在此文本文档里

    加入新的deployer: 原来type前面是没有 - 的

    但是不这样处理执行hexo g 会报错

    1
    2
    3
    4
    5
    deploy:
    - type: git
    repository: yoururl
    branch: master
    - type: baidu_url_submitter

    生成效果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ hexo g
    INFO Start processing
    INFO Generating Baidu urls for last 1 posts
    INFO Posts urls generated in baidu_urls.txt
    http://hisen.me/20170216-hexo百度抓取失败解决办法/
    INFO Files loaded in 1.31 s

    $ hexo d
    INFO Deploy done: git
    INFO Deploying: baidu_url_submitter
    INFO Submitting urls
    http://hisen.me/20170216-hexo百度抓取失败解决办法/
    {"remain":1,"success":1}
    INFO Deploy done: baidu_url_submitter

    感谢插件作者:王辉的博客

    ]]>
    - - - - - 软件 - - - - - - - hexo - - - -
    - - - - - Hxeo百度站长工具抓取失败的原因 - - /20170216-Hxeot%E7%99%BE%E5%BA%A6%E7%AB%99%E9%95%BF%E5%B7%A5%E5%85%B7%E6%8A%93%E5%8F%96%E5%A4%B1%E8%B4%A5%E7%9A%84%E5%8E%9F%E5%9B%A0/ - - 近期在折腾下hexo博客

    发现百度搜索网址都搜索不到自己的站点

    我只把hexo传到github上,然而去百度站长工具提交网址几条后也没有反应

    我就测试了一下百度站长工具 - 抓取诊断

    结果是抓取失败:Github把百度的爬虫给干掉了!所以。。。


    具体内容如下:

    1
    2
    3
    4
    5
    6
    7
    提交网址:http://hisen.me/20170214-maven%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/
    抓取网址:http://hisen.me/20170214-maven%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/
    抓取UA:Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
    抓取时间:2017-02-16 09:48:55
    网站IP:151.***.***.133 已反馈,预计几分钟内完成更新
    下载时长:0.331秒
    抓取异常信息:拒绝访问 查看帮助

    返回HTTP头:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    HTTP/1.1 403 Forbidden
    Cache-Control: no-cache
    Content-Type: text/html
    Transfer-Encoding: chunked
    Accept-Ranges: bytes
    Date: Thu, 16 Feb 2017 01:48:55 GMT
    Via: 1.1 varnish
    Connection: close
    X-Served-By: cache-itm7421-ITM
    X-Cache: MISS
    X-Cache-Hits: 0
    X-Timer: S1487209735.687949,VS0,VE187
    Vary: Accept-Encoding
    X-Fastly-Request-ID: 3e528056d3333113dfb5da5c92a2421fa71f3705

    ]]>
    - - - - - 软件 - - - - - - - hexo - - - -
    - - - - - IDEA修改全局设置,如maven等 - - /20170215-IDEA%E4%BF%AE%E6%94%B9%E5%85%A8%E5%B1%80%E8%AE%BE%E7%BD%AE%EF%BC%8C%E5%A6%82maven%E7%AD%89/ - - 这几天在折腾maven项目

    我发现居然每次新open一个项目就得配置下maven

    因为默认的maven配置文件不行,我自定义的文件用的是阿里云的镜像

    那样快一点,于是很郁闷,决定要搞定他!!!

    结果这样设置就可以了,全局设置。

    1
    File--->Other Setting--->Default Setting

    接下来的设置就是一样的了,各种设置都可以,只要你想全局生效

    ]]>
    - - - - - 软件 - - - - - - - idea - - - -
    - - - - - windows任务栏由底部改到左边,并自动隐藏 - - /20170215-Windows%E4%BB%BB%E5%8A%A1%E6%A0%8F%E7%94%B1%E5%BA%95%E9%83%A8%E6%94%B9%E5%88%B0%E5%B7%A6%E8%BE%B9%EF%BC%8C%E5%B9%B6%E8%87%AA%E5%8A%A8%E9%9A%90%E8%97%8F/ - - windows默认是在底部的

    在底部任务栏空白处:

    右键—设置—任务栏—在桌面模式下自动隐藏任务栏(开)—任务栏在屏幕上的位置(上 | 下 | 左 | 右)

    这样就设置完毕了,毕竟笔记本太小
    用idea的时候居然有些界面很难点击OK什么的!!!


    知乎有专门讨论任务栏放置位置分析,有兴趣的可以看看:

    Windows 任务栏放在窗口左边和下面哪一种更合理?各有什么利弊?

    ]]>
    - - - - - 软件 - - - - - - - idea - - - -
    - - - - - idea启动tomcat服务失败 SEVERE [RMI TCP Connection(3)-127.0.0.1] - - /20170215-idea%E5%90%AF%E5%8A%A8tomcat%E6%9C%8D%E5%8A%A1%E5%A4%B1%E8%B4%A5-SEVERE-RMI-TCP-Connection-3-127-0-0-1/ - - 工程是从eclipse生成的,用idea开发。

    重复了一遍以往正常的不能再正常了的导入配置,结果遇到了如下问题:

    SEVERE [RMI TCP Connection(3)-127.0.0.1]

    1
    15-Feb-2017 11:05:25.993 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.listenerStart Skipped installing application listeners due to previous error(s)

    删除导入项目中的web.xml文件

    因为idea要用的东西自己会自动生成

    然后就搞定了这个纠结我一天的问题

    我是百度搜索这个错误:RMI TCP Connection(3)-127.0.0.1

    得到的答案,感谢10100:查看原文

    ]]>
    - - - - - java - - - - - - - idea - - tomcat - - - -
    - - - - - maven环境搭建 - - /20170214-maven%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/ - - 一、环境的配置
    1. 下载maven:http://maven.apache.org/download.cgi
    2. 解压到相关目录
    3. 配置环境变量:计算机—系统属性—高级系统设置—环境变量
    4. 系统变量新建:M2_HOME 变量值:C:\tool\JAVA\apache-maven-3.3.9
    5. path中添加:%M2_HOME%\bin; (注意看看前面有没有分号隔开,没有添上)
    6. 启动cmd,输入mvm -v可以查看版本
      1
      2
      3
      4
      5
      6
      Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)
      Maven home: C:\tool\JAVA\apache-maven-3.3.9
      Java version: 1.7.0_51, vendor: Oracle Corporation
      Java home: C:\hisenwork\Java\jdk1.7.0_51\jre
      Default locale: zh_CN, platform encoding: GBK
      OS name: "windows 8", version: "6.2", arch: "amd64", family: "windows"

    二、自定义设置(优化)

    1.自定义下载目录,修改配置文件

    1
    C:\hisenwork\soft\maven-3.3.9\conf\settings.xml

    C:\hisenwork\soft\maven-3.3.9为你解压的maven路径


    ps:如果不想自己设置,我有现成的settings.xml,直接复制粘贴,覆盖原来的即可
    现成的settings.xml内容我放在底部

    搜索:localRepository

    在注释外添加以下代码,以后下载maven相关文件就会在这

    1
    2
    3
    <!--自定义存放目录-->
    <localRepository>C:\hisenwork\soft\maven
    </localRepository>

    2.自定义镜像(推荐阿里云,速度飞快)

    搜索:mirrors

    在里面添加:

    1
    2
    3
    4
    5
    6
    7
    <!--阿里云-->
    <mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>*</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>

    3.运行命令初始化maven

    1
    mvn  help:system

    然后你会看到命令行飞快的移动,在你刚刚设置的目录下会出现很多东西

    类似于这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    $ mvn  help:system
    [INFO] Scanning for projects...
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom (4 KB at 1.8 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 15.9 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom (26 KB at 15.5 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom (15 KB at 19.3 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar (25 KB at 30.6 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.pom (7 KB at 5.5 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom (9 KB at 8.2 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/22/maven-parent-22.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/22/maven-parent-22.pom (30 KB at 7.9 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/apache/11/apache-11.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/apache/11/apache-11.pom (15 KB at 12.8 KB/sec)
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 49.223 s
    [INFO] Finished at: 2017-02-14T13:09:33+08:00
    [INFO] Final Memory: 11M/152M
    [INFO] ------------------------------------------------------------------------

    三、maven相关命令

    创建 Maven 项目

    1
    mvn archetype:create

    编译源代码(编译到target文件夹中)

    1
    mvn compile

    编译测试代码

    1
    mvn test-compile

    运行应用程序中的单元测试

    1
    mvn test

    生成项目相关信息的网站

    1
    mvn site

    清除目标目录中的生成结果(把默认target文件夹中的数据清理)

    1
    mvn clean

    项目打包

    1
    mvn package

    将打包好的包安装到本地仓库中,以使其塔项目能够调用

    1
    mvn install

    生成 Eclipse 项目文件

    1
    mvn eclipse:eclipse

    忽略测试文档编译

    1
    mvn -Dmaven.test.skip=true

    部署到私有服务器

    1
    cargo:deploy

    四、settings.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    <?xml version="1.0" encoding="UTF-8"?>
    <!--
    author:HiSEN
    website:http://hisen.me
    -->
    <!--
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements. See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership. The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied. See the License for the
    specific language governing permissions and limitations
    under the License.
    -->

    <!--
    | This is the configuration file for Maven. It can be specified at two levels:
    |
    | 1. User Level. This settings.xml file provides configuration for a single user,
    | and is normally provided in ${user.home}/.m2/settings.xml.
    |
    | NOTE: This location can be overridden with the CLI option:
    |
    | -s /path/to/user/settings.xml
    |
    | 2. Global Level. This settings.xml file provides configuration for all Maven
    | users on a machine (assuming they're all using the same Maven
    | installation). It's normally provided in
    | ${maven.home}/conf/settings.xml.
    |
    | NOTE: This location can be overridden with the CLI option:
    |
    | -gs /path/to/global/settings.xml
    |
    | The sections in this sample file are intended to give you a running start at
    | getting the most out of your Maven installation. Where appropriate, the default
    | values (values used when the setting is not specified) are provided.
    |
    |-->
    <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <!-- localRepository
    | The path to the local repository maven will use to store artifacts.
    |
    | Default: ${user.home}/.m2/repository
    <localRepository>/path/to/local/repo</localRepository>
    -->
    <!--自定义存放目录-->
    <localRepository>C:\hisenwork\soft\maven
    </localRepository>

    <!-- interactiveMode
    | This will determine whether maven prompts you when it needs input. If set to false,
    | maven will use a sensible default value, perhaps based on some other setting, for
    | the parameter in question.
    |
    | Default: true
    <interactiveMode>true</interactiveMode>
    -->

    <!-- offline
    | Determines whether maven should attempt to connect to the network when executing a build.
    | This will have an effect on artifact downloads, artifact deployment, and others.
    |
    | Default: false
    <offline>false</offline>
    -->

    <!-- pluginGroups
    | This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e.
    | when invoking a command line like "mvn prefix:goal". Maven will automatically add the group identifiers
    | "org.apache.maven.plugins" and "org.codehaus.mojo" if these are not already contained in the list.
    |-->
    <pluginGroups>
    <!-- pluginGroup
    | Specifies a further group identifier to use for plugin lookup.
    <pluginGroup>com.your.plugins</pluginGroup>
    -->
    </pluginGroups>

    <!-- proxies
    | This is a list of proxies which can be used on this machine to connect to the network.
    | Unless otherwise specified (by system property or command-line switch), the first proxy
    | specification in this list marked as active will be used.
    |-->
    <proxies>
    <!-- proxy
    | Specification for one proxy, to be used in connecting to the network.
    |
    <proxy>
    <id>optional</id>
    <active>true</active>
    <protocol>http</protocol>
    <username>proxyuser</username>
    <password>proxypass</password>
    <host>proxy.host.net</host>
    <port>80</port>
    <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
    -->
    </proxies>

    <!-- servers
    | This is a list of authentication profiles, keyed by the server-id used within the system.
    | Authentication profiles can be used whenever maven must make a connection to a remote server.
    |-->
    <servers>
    <!-- server
    | Specifies the authentication information to use when connecting to a particular server, identified by
    | a unique name within the system (referred to by the 'id' attribute below).
    |
    | NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are
    | used together.
    |
    <server>
    <id>deploymentRepo</id>
    <username>repouser</username>
    <password>repopwd</password>
    </server>
    -->

    <!-- Another sample, using keys to authenticate.
    <server>
    <id>siteServer</id>
    <privateKey>/path/to/private/key</privateKey>
    <passphrase>optional; leave empty if not used.</passphrase>
    </server>
    -->
    </servers>

    <!-- mirrors
    | This is a list of mirrors to be used in downloading artifacts from remote repositories.
    |
    | It works like this: a POM may declare a repository to use in resolving certain artifacts.
    | However, this repository may have problems with heavy traffic at times, so people have mirrored
    | it to several places.
    |
    | That repository definition will have a unique id, so we can create a mirror reference for that
    | repository, to be used as an alternate download site. The mirror site will be the preferred
    | server for that repository.
    |-->
    <mirrors>
    <!-- mirror
    | Specifies a repository mirror site to use instead of a given repository. The repository that
    | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
    | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
    |
    <mirror>
    <id>mirrorId</id>
    <mirrorOf>repositoryId</mirrorOf>
    <name>Human Readable Name for this Mirror.</name>
    <url>http://my.repository.com/repo/path</url>
    </mirror>
    -->
    <!--阿里云-->
    <mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>*</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
    </mirrors>

    <!-- profiles
    | This is a list of profiles which can be activated in a variety of ways, and which can modify
    | the build process. Profiles provided in the settings.xml are intended to provide local machine-
    | specific paths and repository locations which allow the build to work in the local environment.
    |
    | For example, if you have an integration testing plugin - like cactus - that needs to know where
    | your Tomcat instance is installed, you can provide a variable here such that the variable is
    | dereferenced during the build process to configure the cactus plugin.
    |
    | As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles
    | section of this document (settings.xml) - will be discussed later. Another way essentially
    | relies on the detection of a system property, either matching a particular value for the property,
    | or merely testing its existence. Profiles can also be activated by JDK version prefix, where a
    | value of '1.4' might activate a profile when the build is executed on a JDK version of '1.4.2_07'.
    | Finally, the list of active profiles can be specified directly from the command line.
    |
    | NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact
    | repositories, plugin repositories, and free-form properties to be used as configuration
    | variables for plugins in the POM.
    |
    |-->
    <profiles>
    <!-- profile
    | Specifies a set of introductions to the build process, to be activated using one or more of the
    | mechanisms described above. For inheritance purposes, and to activate profiles via <activatedProfiles/>
    | or the command line, profiles have to have an ID that is unique.
    |
    | An encouraged best practice for profile identification is to use a consistent naming convention
    | for profiles, such as 'env-dev', 'env-test', 'env-production', 'user-jdcasey', 'user-brett', etc.
    | This will make it more intuitive to understand what the set of introduced profiles is attempting
    | to accomplish, particularly when you only have a list of profile id's for debug.
    |
    | This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo.
    <profile>
    <id>jdk-1.4</id>

    <activation>
    <jdk>1.4</jdk>
    </activation>

    <repositories>
    <repository>
    <id>jdk14</id>
    <name>Repository for JDK 1.4 builds</name>
    <url>http://www.myhost.com/maven/jdk14</url>
    <layout>default</layout>
    <snapshotPolicy>always</snapshotPolicy>
    </repository>
    </repositories>
    </profile>
    -->

    <!--
    | Here is another profile, activated by the system property 'target-env' with a value of 'dev',
    | which provides a specific path to the Tomcat instance. To use this, your plugin configuration
    | might hypothetically look like:
    |
    | ...
    | <plugin>
    | <groupId>org.myco.myplugins</groupId>
    | <artifactId>myplugin</artifactId>
    |
    | <configuration>
    | <tomcatLocation>${tomcatPath}</tomcatLocation>
    | </configuration>
    | </plugin>
    | ...
    |
    | NOTE: If you just wanted to inject this configuration whenever someone set 'target-env' to
    | anything, you could just leave off the <value/> inside the activation-property.
    |
    <profile>
    <id>env-dev</id>

    <activation>
    <property>
    <name>target-env</name>
    <value>dev</value>
    </property>
    </activation>

    <properties>
    <tomcatPath>/path/to/tomcat/instance</tomcatPath>
    </properties>
    </profile>
    -->
    </profiles>

    <!-- activeProfiles
    | List of profiles that are active for all builds.
    |
    <activeProfiles>
    <activeProfile>alwaysActiveProfile</activeProfile>
    <activeProfile>anotherAlwaysActiveProfile</activeProfile>
    </activeProfiles>
    -->
    </settings>
    ]]>
    - - - - - java - - - - - - - java - - maven - - - -
    - - - - - IDEA自定义注释模板(javadoc) - Jindent插件 - 安装&设置 - - /20170214-IDEA%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E9%87%8A%E6%A8%A1%E6%9D%BF-javadoc-Jindent%E6%8F%92%E4%BB%B6-%E5%AE%89%E8%A3%85-%E8%AE%BE%E7%BD%AE/ - - 在线安装

    1. File—>Settings—>Plugin—>Browse repositories…
    2. 搜索框输入:jindent
    3. Jindent - Source Code Formatter 选中
    4. 右边 install
    5. 等待安装完成
    6. apply之后会让你重启idea
    ]]>
    - - - - - 软件 - - - - - - - javadoc - - - -
    - - - - - PLSQL注册码 - 9999/12/31 - - /20170214-PLSQL%E6%B3%A8%E5%86%8C%E7%A0%81-9999-12-31/ - - PLSQL注册码
    过期时间为:9999/12/31

    1
    2
    3
    4
    Registration successful

    01.601769 - unlimited user licence
    service contract:9999/12/31


    Product Code:

    1
    4t46t6vydkvsxekkvf3fjnpzy5wbuhphqz

    serial Number:

    1
    601769

    password:

    1
    xs374ca

    ]]>
    - - - - - 软件 - - - - - - - java - - PLSQL - - - -
    - - - - - Intellij Idea 快捷键汇总&介绍 - - /20170213-Intellij-Idea-%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%B1%87%E6%80%BB-%E4%BB%8B%E7%BB%8D/ - - 最常用快捷键(1~18)

    1. Ctrl+E,可以显示最近编辑的文件列表
    2. Shift+Click可以关闭文件
    3. Ctrl+[或]可以跳到大括号的开头结尾
    4. Ctrl+Shift+Backspace可以跳转到上次编辑的地方
    5. Ctrl+F12,可以显示当前文件的结构
    6. Ctrl+F7可以查询当前元素在当前文件中的引用,然后按F3可以选择
    7. Ctrl+N,可以快速打开类
    8. Ctrl+Shift+N,可以快速打开文件
    9. Alt+Q可以看到当前方法的声明
    10. Ctrl+W可以选择单词继而语句继而行继而函数
    11. Alt+F1可以将正在编辑的元素在各个面板中定位
    12. Ctrl+P,可以显示参数信息
    13. Ctrl+Shift+Insert可以选择剪贴板内容并插入
    14. Alt+Insert可以生成构造器/Getter/Setter等
    15. Ctrl+Alt+V 可以引入变量。例如把括号内的SQL赋成一个变量
    16. Ctrl+Alt+T可以把代码包在一块内,例如try/catch
    17. Alt+Up and Alt+Down可在方法间快速移动
    18. 在一些地方按Alt+Enter可以得到一些Intention Action,例如将”==”改为”equals()”
    19. Ctrl+Shift+Alt+N可以快速打开符号
    20. Ctrl+Shift+Space在很多时候都能够给出Smart提示
    21. Alt+F3可以快速寻找
    22. Ctrl+/和Ctrl+Shift+/可以注释代码
    23. Ctrl+Alt+B可以跳转到抽象方法的实现
    24. Ctrl+O可以选择父类的方法进行重写
    25. Ctrl+Q可以看JavaDoc
    26. Ctrl+Alt+Space是类名自动完成
    27. 快速打开类/文件/符号时,可以使用通配符,也可以使用缩写
    28. Live Templates! Ctrl+J
    29. Ctrl+Shift+F7可以高亮当前元素在当前文件中的使用
    30. Ctrl+Alt+Up /Ctrl+Alt+Down可以快速跳转搜索结果
    31. Ctrl+Shift+J可以整合两行
    32. Alt+F8是计算变量值

    Alt+回车 导入包,自动修正

    Ctrl+N 查找类

    Ctrl+Shift+N 查找文件

    Ctrl+Alt+L 格式化代码

    Ctrl+Alt+O 优化导入的类和包

    Alt+Insert 生成代码(如get,set方法,构造函数等)

    Ctrl+E或者Alt+Shift+C 最近更改的代码

    Ctrl+R 替换文本

    Ctrl+F 查找文本

    Ctrl+Shift+Space 自动补全代码

    Ctrl+空格 代码提示

    Ctrl+Alt+Space 类名或接口名提示

    Ctrl+P 方法参数提示

    Ctrl+Shift+Alt+N 查找类中的方法或变量

    Alt+Shift+C 对比最近修改的代码

    Shift+F6 重构-重命名

    Ctrl+Shift+先上键

    Ctrl+X 删除行

    Ctrl+D 复制行

    Ctrl+/ 或 Ctrl+Shift+/ 注释(// 或者// )

    Ctrl+J 自动代码

    Ctrl+E 最近打开的文件

    Ctrl+H 显示类结构图

    Ctrl+Q 显示注释文档

    Alt+F1 查找代码所在位置

    Alt+1 快速打开或隐藏工程面板

    Ctrl+Alt+ left/right 返回至上次浏览的位置

    Alt+ left/right 切换代码视图

    Alt+ Up/Down 在方法间快速移动定位

    Ctrl+Shift+Up/Down 代码向上/下移动。

    F2 或Shift+F2 高亮错误或警告快速定位

    代码标签输入完成后,按Tab,生成代码。

    选中文本,按Ctrl+Shift+F7 ,高亮显示所有该文本,按Esc高亮消失。

    Ctrl+W 选中代码,连续按会有其他效果

    选中文本,按Alt+F3 ,逐个往下查找相同文本,并高亮显示。

    Ctrl+Up/Down 光标跳转到第一行或最后一行下

    Ctrl+B 快速打开光标处的类或方法

    ]]>
    - - - - - 软件 - - - - - - - java - - idea - - - -
    - - - - - 网关、快捷支付,代收代付,账户托管,二维码扫码支付 - - /20170213-%E7%BD%91%E5%85%B3%E3%80%81%E5%BF%AB%E6%8D%B7%E6%94%AF%E4%BB%98%EF%BC%8C%E4%BB%A3%E6%94%B6%E4%BB%A3%E4%BB%98%EF%BC%8C%E8%B4%A6%E6%88%B7%E6%89%98%E7%AE%A1%EF%BC%8C%E4%BA%8C%E7%BB%B4%E7%A0%81%E6%89%AB%E7%A0%81%E6%94%AF%E4%BB%98/ - - 一、网关支付

    这是在线支付的最普遍形式。
    大致支付过程:第三方支付公司作为代理(网关),接入一堆银行。用户在网关页面(可以在商户端,也可以第三方支付平台端)选择银行,页面跳转到第三方支付平台,然后重定向到对应的银行,用户在银行电子银行官网,采用网银(个人网银或企业网银)完成支付。

    网关支付分为:B2C、B2B两类。
    涉及的概念:网银支付、银行卡支付。

    我们一般说的网关支付是指在PC上的在线支付,由于国内银行基本上都要求安装对应的安全控件,且需要银行的网银客户端,这也是大家经常抱怨网银不支持MAC/Linux等操作系统、不支持除IE外的浏览器等兼容性问题。
    在手机端也有类似网关支付的形态,但由于操作过程较为麻烦,体验不好,一般都采用快捷支付等支付形式。

    二、快捷支付

    快捷支付一般是指首次需要验证卡要素,生成协议号或者TOKEN,后面支付直接凭协议号扣款。走的交易形式是消费。快捷支付相比于我们原先说的无磁无密支付[MOTO]在限额上有劣势,体验上有优势。
    一个相当于长期关系,MOTO相当于一次性关系,每次来都要输入卡要素。

    三、代收代付

    代收代付业务是我社利用自身的结算便利,接受客户的委托代为办理指定款项的收付事宜的业务。

    由中介公司或第三方代为收取和支付费用。

    顾名思义,代收代付是指先付出去,然后再收回来,金额必须相等。

    比如代办运输业务,如果是收取一定比率的手续费,就改变了性质。

    代收代付业务分录:

    代付时,借:其他应收款 贷:银行存款

    收回时,借:银行存款 贷:其他应收款

    四、账户托管

    待完善

    五、二维码扫码支付

    微信扫码业务流程说明:

    1. 商户后台系统根据用户选购的商品生成订单。
    2. 用户确认支付后调用微信支付【统一下单API】生成预支付交易;
    3. 微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
    4. 商户后台系统根据返回的code_url生成二维码。
    5. 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
    6. 微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
    7. 用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
    8. 微信支付系统根据用户授权完成支付交易。
    9. 微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
    10. 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
    11. 未收到支付通知的情况,商户后台系统调用【查询订单API】。
    12. 商户确认订单已支付后给用户发货。

    日后需要用到这方面的东西,先整理一番

    系统的学习或者理解之后再来说说深层次的东西。

    ]]>
    - - - - - biz - - - - - - - 支付 - - - -
    - - - - - 阿里巴巴Java开发手册,摘要&下载链接 - - /20170213-%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%8C%E6%91%98%E8%A6%81-%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5/ - - 阿里巴巴java开发手册

    看了几个小时,感觉还是不错

    都按规范来写,可以避免很多错误

    下载链接

    点击下载

    ]]>
    - - - - - java - - - - - - - java - - - -
    - - - - - MarkdownPad2注册码 20170213 - - /20170213-MarkdownPad2%E6%B3%A8%E5%86%8C%E7%A0%81-20170213/ - - MarkdownPad2注册码

    亲测有效:2017年2月13日 18:07:25


    邮箱:

    1
    Soar360@live.com

    授权密钥:

    1
    GBPduHjWfJU1mZqcPM3BikjYKF6xKhlKIys3i1MU2eJHqWGImDHzWdD6xhMNLGVpbP2M5SN6bnxn2kSE8qHqNY5QaaRxmO3YSMHxlv2EYpjdwLcPwfeTG7kUdnhKE0vVy4RidP6Y2wZ0q74f47fzsZo45JE2hfQBFi2O9Jldjp1mW8HUpTtLA2a5/sQytXJUQl/QKO0jUQY4pa5CCx20sV1ClOTZtAGngSOJtIOFXK599sBr5aIEFyH0K7H4BoNMiiDMnxt1rD8Vb/ikJdhGMMQr0R4B+L3nWU97eaVPTRKfWGDE8/eAgKzpGwrQQoDh+nzX1xoVQ8NAuH+s4UcSeQ==

    ]]>
    - - - - - soft - - - - - - - soft - - - -
    - - - - - Hexo提速优化 - 压缩html、css、js by hexo-neat插件 - - /20170211-Hexo%E6%8F%90%E9%80%9F%E4%BC%98%E5%8C%96-%E5%8E%8B%E7%BC%A9html%E3%80%81css%E3%80%81js-by-hexo-neat%E6%8F%92%E4%BB%B6/ - - hexo博客生成的HTML代码留有大量的空白

    通过搜索发现有不错的方法可以解决这个问题

    那就是安装一个插件!名字叫:hexo-neat

    安装

    命令行进入博客根目录,执行以下命令

    1
    npm install hexo-neat --save

    如果使用的是淘宝的cnmp执行以下命令

    1
    cnpm install hexo-neat --save

    配置

    打开站点配置文件_config.yml,添加以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #hexo-neat 优化提速插件
    neat_enable: true

    neat_html:
    enable: true
    exclude:

    neat_css:
    enable: true
    exclude:
    - '*.min.css'

    neat_js:
    enable: true
    mangle: true
    output:
    compress:
    exclude:
    - '*.min.js'

    安装好之后,就直接正常使用(跟以前没有装的时候一样使用),

    重新生成博客就可以发现html源码已经压缩了

    只是在生成博客的过程中可能要浪费一丁点时间

    访问速度有所提升!

    参考:点击查看

    ]]>
    - - - - - 软件 - - - - - - - hexo - - - -
    - - - - - Java静态代码块 - - /20170211-Java%E9%9D%99%E6%80%81%E4%BB%A3%E7%A0%81%E5%9D%97/ - - 有时候重新回味一下以前的知识也很美妙

    总能发现以前自己没有怎么在意的细节


    静态代码块是在类中独立于类成员的static语句块,可以有多个。

    如果要初始化静态变量,可以声明一个静态块。

    格式如下:

    1
    2
    3
    static {
    //块执行代码
    }

    静态块存在单独的内存中,仅在该类被加载时执行,示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package com.hisen.javaGaiShu.page91test20;

    public class JingTaiDaiMaKuai {
    private static String a;
    private String b;
    static {
    JingTaiDaiMaKuai.a = "我学习了很多语言";
    System.out.println(a);
    JingTaiDaiMaKuai t = new JingTaiDaiMaKuai();
    t.fina();
    t.b="Java语言";
    System.out.println(t.b);
    }

    static {
    JingTaiDaiMaKuai.a = "I Love Java";
    System.out.println(a);
    }

    public static void main(String[] args) {

    }

    static {
    JingTaiDaiMaKuai.a = "我还将继续学习下去";
    System.out.println(a);
    }

    private void fina() {
    System.out.println("但是我最喜欢的是:");
    }
    }


    输出如下:

    1
    2
    3
    4
    5
    我学习了很多语言
    但是我最喜欢的是:
    Java语言
    I Love Java
    我还将继续学习下去

    静态代码块在运行main方法时可以直接调用而不用创建实例。

    静态代码块直接是按顺序执行的。

    ]]>
    - - - - - java - - - - - - - java - - 练习 - - - -
    - - - - - Java三种创建对象的方式 - - /20170210-Java%E4%B8%89%E7%A7%8D%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%96%B9%E5%BC%8F/ - - 有一个实现了Cloneable接口的Person类
    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <使用new关键字创建对象>
    hisen已成年
    体重为:50
    年龄为:23
    <使用newInstance()方法创建对象>
    体重为:0
    年龄为:0
    <使用clone()方法创建对象>
    hisen已成年
    体重为:50
    年龄为:23

    具体实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    package com.hisen.javaGaiShu.page73test5;

    /**
    * 三种方式创建对象
    *
    * @author hisenyuan 2017年2月10日 下午10:27:14
    */
    public class Person implements Cloneable {
    private String name;
    private int weight;
    private int age;

    public Person() {
    }

    public Person(String name, int weight, int age) {
    super();
    this.name = name;
    this.weight = weight;
    this.age = age;
    }

    public void young() {
    if (age >= 18 && age <= 100)
    System.out.println(name + "已成年");
    if (age > 0 && age < 18)
    System.out.println(name + "未成年");
    }

    @Override
    public String toString() {
    return "体重为:" + weight + "\n年龄为:" + age;
    }

    public static void main(String[] args) throws Exception {
    System.out.println("<使用new关键字创建对象>");
    Person p1 = new Person("hisen", 50, 23);
    p1.young();
    System.out.println(p1);

    System.out.println("<使用newInstance()方法创建对象>");
    Class c = Class.forName("com.hisen.javaGaiShu.page73test5.Person");
    Person p2 = (Person) c.newInstance();
    p2.young();
    System.out.println(p2);

    System.out.println("<使用clone()方法创建对象>");
    Person p3 = (Person) p1.clone();
    p3.young();
    System.out.println(p3);
    }

    }

    ]]>
    - - - - - java - - - - - - - java - - 对象 - - - -
    - - - - - Java基本数据类型 - 以及相关内容 - - /20170210-Java%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/ - - 类型名称关键字占用内存取值范围包装类字节型byte1-128~127Byte短整形short2-32768~32767Short整形int4-2147483648~2147483647Integer长整形long8-9223372036854775808L ~ 9223372036854775807LLong单精度浮点float4-3.4E38~3.4E38(6~7个有效位)Float双精度浮点double8-1.7E308~1.7E308(15个有效值)Double字符型char2ISO单一字符集,其表示范围是0~65535Charater布尔型boolean1true 或 falseBoolean

    所有基本数据类型的大小(所占用的字节数)都是明确规定好的,

    在各种平台上都保持不变,这一特性有助于提高Java程序的可移植性。


    引用数据类型包括字符串、数组、类和接口。

    引用数据类型是用户自定义、用来限制其他数据类型。

    引用数据类型的变量在内存中存储的是数据的引用,并不是数据本身,

    引用类型是使用间接方法去获取数据


    java中int为什么占用4个字节?

    回答1:

    现在流行的编译器,都是规定的int是四个字节~

    像tc这样老版的编译器,int才是两个字节,

    然后也是一样,由于一个字节占八位,最高为符号位,又人为规定,1000000000000000……这个补码编码为-2^31所以,范围就是-2^31~2^31-1

    回答2:

    JAVA是采用Unicode编码。每一个字节占8位。

    你电脑系统应该是32位系统(工具),这样每个int就是 4个字节

    其中一个字节由8个二进制位组成

    回答3:

    int常见为4个字节,跟操作系统有关系。

    turbo c(以及Turbo c的一些衍生编译器,他们用的一套编译程序)是dos时代的编译器,

    是上世纪80年代的产物,严重过时,属于老掉牙的产品,

    他们编译出来的程序是16位操作系统dos下的程序,所以长度为16位,即两个字节。

    windows为了兼容dos,所以turbo c生成的文件也可以在windows中运行。

    其他一般就都是4个字节了。

    操作系统16位的时候,int 2字节,操作系统32位的时候,int 4字节,由于32位系统之前占主流地位,实际现在就算是64位系统,出于兼容性考虑,int也是4字节的


    ]]>
    - - - - - java - - - - - - - java - - - -
    - - - - - windows10 专业版激活密钥 - - /20170210-Windows10-%E4%B8%93%E4%B8%9A%E7%89%88%E6%BF%80%E6%B4%BB%E5%AF%86%E9%92%A5/ - - 2017年2月10日 16:03:48
    亲测可用,这比激活工具方便多了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    VK7JG-NPHTM-C97JM-9MPGT-3V66T

    NPPR9-FWDCX-D2C8J-H872K-2YT43

    W269N-WFGWX-YVC9B-4J6C9-T83GX

    NYW94-47Q7H-7X9TT-W7TXD-JTYPM

    NJ4MX-VQQ7Q-FP3DB-VDGHX-7XM87

    MH37W-N47XK-V7XM9-C7227-GCQG9
    ]]>
    - - - - - soft - - - - - - - soft - - - -
    - - - - - hexo新建文章时候默认带上categories,tags - - /20170209-hexo%E6%96%B0%E5%BB%BA%E6%96%87%E7%AB%A0%E6%97%B6%E5%80%99%E9%BB%98%E8%AE%A4%E5%B8%A6%E4%B8%8Acategories%EF%BC%8Ctags/ - - 在博客的 scaffolds 文件夹里有个post.md 添加上需要的配置就行

    这里是创建post的模板。

    我的默认设置成这样:

    1
    2
    3
    4
    5
    6
    ---
    title: {{ title }}
    date: {{ date }}
    tags: []
    categories:
    ---

    tags: [关键词1,关键词2]

    ]]>
    - - - - - hexo - - - - - - - hexo - - - -
    - - - - - Hexo 绑定个人域名简单方法 - - /20170209-Hexo-%E7%BB%91%E5%AE%9A%E4%B8%AA%E4%BA%BA%E5%9F%9F%E5%90%8D%E7%AE%80%E5%8D%95%E6%96%B9%E6%B3%95/ - - 1.直接注册个域名(随便在哪里)
    2.添加域名解析

    记录类型主机记录记录值
    CNAME@hisen-yuan.github.io

    主机记录:@ 代表顶级域名,例如hisen.me 如果想要www.hisen.me把@改成www

    记录值:你的博客原始地址

    3.在\blog\source下添加CNAME文件,没有后缀名,内容为你的域名,注意不要带http://

    我的域名解析为 hisen.me 文件内容为 hisen.me

    等待解析生效即可!

    ]]>
    - - - - - - hexo - - - -
    - - - - - Hexo Landscape主题JS优化,不使用谷歌CDN - - /20170208-Hexo-Landscape%E4%B8%BB%E9%A2%98JS%E4%BC%98%E5%8C%96%EF%BC%8C%E4%B8%8D%E4%BD%BF%E7%94%A8%E8%B0%B7%E6%AD%8CCDN/ - - jQuery库的优化

    修改这个文件:

    1
    themes/landscape/layout/_partial/after-footer.ejs

    将17行左右的

    1
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>

    改为

    1
    <script src="http://apps.bdimg.com/libs/jquery/2.0.3/jquery.min.js"></script>

    重新生成博客之后就把谷歌的cdn替换成百度的cdn了

    ]]>
    - - - - - - hexo - - - -
    - - - - - 手机平板电脑原样显示html效果 - html知识 - - /20170208-%E6%89%8B%E6%9C%BA%E5%B9%B3%E6%9D%BF%E7%94%B5%E8%84%91%E5%8E%9F%E6%A0%B7%E6%98%BE%E7%A4%BAhtml%E6%95%88%E6%9E%9C-html%E7%9F%A5%E8%AF%86/ - - 有时候可能你会发现
    在电脑上显示300x300大小的东西看起来很正常
    但是用手机去访问的话,就出现等比例缩小了
    但是300x300的大小完全不用缩小
    直接等大不是更好?

    在head加上这两行代码,可以做到原样输出

    1
    2
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    ]]>
    - - - - - - html - - - -
    - - - - - 如何检测Linux VPS系统架构是Xen、KVM还是OpenVZ - - /20170208-%E5%A6%82%E4%BD%95%E6%A3%80%E6%B5%8BLinux-VPS%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E6%98%AFXen%E3%80%81KVM%E8%BF%98%E6%98%AFOpenVZ/ - - 结果
    1
    2
    3
    [root]# virt-what
    xen
    xen-hvm

    Centos

    1
    2
    3
    4
    5
    6
    wget http://people.redhat.com/~rjones/virt-what/files/virt-what-1.12.tar.gz
    tar zxvf virt-what-1.12.tar.gz
    cd virt-what-1.12/
    ./configure
    make && make install
    virt-what

    Ubuntu/debian

    1
    2
    apt-get install virt-what
    virt-what
    ]]>
    - - - - - - linux - - - -
    - - - - - String 和 StringBuilder、StringBuffer的区别 - - /20170208-String-%E5%92%8C-StringBuilder%E3%80%81StringBuffer%E7%9A%84%E5%8C%BA%E5%88%AB/ - - String和StringBuilder、StringBuffer的区别?

    答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,

    它们可以储存和操作字符串。其中String是只读字符串,

    也就意味着String引用的字符串内容是不能被改变的。

    而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。

    StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,

    区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,

    因此它的效率也比StringBuffer要高。

    简而言之:

    String:不能被修改

    StringBuffer:可以随意修改,有synchronized修饰,是线程安全的,效率略低

    StringBuilder:可以随意修改,无synchronized修饰,不是线程安全的,效率高

    面试题1:说出程序的输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    classStringEqualTest {

    publicstaticvoidmain(String[] args) {
    String s1 = "Programming";
    String s2 = new String("Programming");
    String s3 = "Program" + "ming";
    System.out.println(s1 == s2);//false
    System.out.println(s1 == s3);//true
    System.out.println(s1 == s1.intern());//true
    }
    }

    存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。

    String的intern()方法就是扩充常量池的一个方法;

    当一个String实例str调用intern()方法时,

    Java查找常量池中是否有相同Unicode的字符串常量,

    如果有,则返回其的引用,

    如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用

    面试题2

    什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append方法连接字符串性能更好?

    答:

    如果使用少量的字符串操作,使用 (+运算符)连接字符串;

    如果频繁的对大量字符串进行操作,则使用

    1:全局变量或者需要多线程支持则使用StringBuffer;

    2:局部变量或者单线程不涉及线程安全则使有StringBuilder。

    ]]>
    - - - - - - java - - StringBuilder - - StringBuffer的区别 - - - -
    - - - - - MySQL使用like查找汉字乱码 - - /20170208-MySQL%E4%BD%BF%E7%94%A8like%E6%9F%A5%E6%89%BE%E6%B1%89%E5%AD%97%E4%B9%B1%E7%A0%81/ - - 第一种解决办法:BINARY

    在关键字之前加上:BINARY,会使关键字强制转换为二进制字符串

    1
    select id form t where chinese like **BINARY** %汉字%

    第二种解决办法:改关键字类型

    把关键字的类型改成:BINARY

    这两种办法都可以解决乱码问题

    ]]>
    - - - - - - mysql - - like - - - -
    - - - - - new ImageIcon()无法加载同目录图片 - - /20170208-new-ImageIcon-%E6%97%A0%E6%B3%95%E5%8A%A0%E8%BD%BD%E5%90%8C%E7%9B%AE%E5%BD%95%E5%9B%BE%E7%89%87/ - - 错误:

    1
    new ImageIcon("1.jpg")

    正确:

    1
    new ImageIcon("src/com/hisen/thread/progressbar/1.jpg")

    图片路径:

    1
    test\src\com\hisen\thread\progressbar\1.jpg

    所谓的相对路径,是相对于这个工程而言的,而不是当前文件夹而言。

    ]]>
    - - - - - - java - - jframe - - - -
    - - - - - eclipse无法链接github - - /20170208-eclipse%E6%97%A0%E6%B3%95%E9%93%BE%E6%8E%A5github/ - - 浏览器什么的都能打开github.com
    就是eclipse无法提交到github,每次都是连接超时
    然后就直接修改host了,目前有效
    2017年1月14日 18:01:34

    host位置:

    1
    C:\Windows\System32\drivers\etc

    host文件最后一行加上下面内容即可

    1
    192.30.253.112       github.com

    ]]>
    - - - - - - eclipse - - github - - - -
    - - - - - 解决:eclipse下*.properties显示Unicode乱码 - - /20170208-%E8%A7%A3%E5%86%B3%EF%BC%9Aeclipse%E4%B8%8B-properties%E6%98%BE%E7%A4%BAUnicode%E4%B9%B1%E7%A0%81/ - - eclipse的*.properties文件,默认的编码方式是iso-8859-1

    Window -> preferences -> general -> Contents Types -> Text(展开)
    -> Java Aroperties File(点击) -> *.properties(locked)(点击)
    -> 把iso-8859-1改为 UTF-8 -> Update -> OK

    然后就可以正常显示中文了

    ]]>
    - - - - - - java - - 乱码 - - properties - - - -
    - - - - - log4j:WARN No appenders could be found for logger - - /20170208-log4j-WARN-No-appenders-could-be-found-for-logger/ - - 解决办法为:在项目的src下面新建file名为log4j.properties文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    ###设置
    log4j.rootLogger = debug,stdout,D,E

    ###输出信息到控制抬
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

    ###输出DEBUG 级别以上的日志到=E://logs/error.log
    log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.D.File = E://logs/log.log
    log4j.appender.D.Append = true
    log4j.appender.D.Threshold = DEBUG
    log4j.appender.D.layout = org.apache.log4j.PatternLayout
    log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

    ###输出ERROR 级别以上的日志到=E://logs/error.log
    log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.E.File =E://logs/error.log
    log4j.appender.E.Append = true
    log4j.appender.E.Threshold = ERROR
    log4j.appender.E.layout = org.apache.log4j.PatternLayout
    log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

    log4j详细使用方法:点击查看

    ]]>
    - - - - - - java - - log4j - - - -
    - - - - - 记录一次面试过程中交流的一些题 - - /20170126-%E8%AE%B0%E5%BD%95%E4%B8%80%E6%AC%A1%E9%9D%A2%E8%AF%95%E8%BF%87%E7%A8%8B%E4%B8%AD%E4%BA%A4%E6%B5%81%E7%9A%84%E4%B8%80%E4%BA%9B%E9%A2%98/ - - 以下是面试一家支付类公司的过程当中遇到的面试题

    主要是交流比较多,不是先笔试

    直接是把这些问题带入到具体的情景当中去

    可能这样更能考验出一个人真正的技术水平

    1,很多文件,读出数字,加1写回,谈谈你的想法

    2,能继承string类?

    1
    不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变。

    3,能有个包名一样的String类?如果有一样的会调用哪个?

    4,一个主线程等待其他线程完成,如果其中有线程出错怎么办?

    1
    2
    把线程可能会出现的问题处理掉
    出错了能保证让他重新执行

    5,Oracle默认端口?

    1521

    6,b继承a,b的对象能强转成a嘛?

    不能把一个对象强制转换成另外一个对象

    7,数据库去重,删除所有重复记录,只留下一条

    1
    2
    3
    4
    5
    6
    DELETE
    FROM EMP E
    WHERE E.ROWID >
    (SELECT MIN(X.ROWID)
    FROM EMP X
    WHERE X.EMP_NO = E.EMP_NO);

    8,try c里面,没打印出错误来,是为什么?

    1
    2
    1.可能是程序执行是正确的
    2.可能存在调用,但是调用的方法里面出现了错误,没有抛出或者是运行时错误

    9,JAVA数据类型

    简单类型二进制位数封装器类
    boolean1Boolean
    byte8Byte
    char16Character
    short16Short
    Int32Integer
    long64Long
    float32Float
    double64Double
    voidVoid

    10,银行金额字段

    金融数字是BigDecimal类型

    11,用什么解析XML,有什么优缺点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    DOM4J(Document Object Model for Java)
    虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支。
    它合并了许多超出基本XML文档表示的功能,
    包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。
    它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能。
    从2000下半年开始,它就一直处于开发之中。

    为支持所有这些功能,DOM4J使用接口和抽象基本类方法。
    DOM4J大量使用了API中的Collections类,但是在许多情况下,
    它还提供一些替代方法以允许更好的性能或更直接的编码方法。
    直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性。

    在添加灵活性、XPath集成和对大文档处理的目标时,
    DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作。
    它还致力于成为比JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标。
    在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为。

    DOM4J是一个非常非常优秀的Java XML API,
    具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。
    如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,
    特别值得一提的是连Sun的JAXM也在用DOM4J.

    【优点】
    ①大量使用了Java集合类,方便Java开发人员,同时提供一些提高性能的替代方法。
    ②支持XPath。
    ③有很好的性能。

    【缺点】
    ①大量使用了接口,API较为复杂。

    12,阿贾克斯熟悉吗?能发起请求下载文档吗?返回类型有哪些,遇到错误怎么提示用户

    1
    2
    不能发起文档下载,返回的类型只有字符型,
    出错了alert出错误

    13,jquery选择器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    jQuery 元素选择器
    jQuery 使用 CSS 选择器来选取 HTML 元素。
    $("p") 选取 <p> 元素。
    $("p.intro") 选取所有 class="intro" 的 <p> 元素。
    $("p#demo") 选取所有 id="demo" 的 <p> 元素。

    jQuery 属性选择器
    jQuery 使用 XPath 表达式来选择带有给定属性的元素。
    $("[href]") 选取所有带有 href 属性的元素。
    $("[href='#']") 选取所有带有 href 值等于 "#" 的元素。
    $("[href!='#']") 选取所有带有 href 值不等于 "#" 的元素。
    $("[href$='.jpg']") 选取所有 href 值以 ".jpg" 结尾的元素。

    14,空指针异常,怎么定位错误

    1
    2
    定位到出现错误的行数
    看看附近的各种调用是否存可能出现空指针异常,再慢慢排除

    ]]>
    - - - - - - java - - 面试 - - - -
    - - - - - Linux常用的几个命令 - - /20170122-Linux%E5%B8%B8%E7%94%A8%E7%9A%84%E5%87%A0%E4%B8%AA%E5%91%BD%E4%BB%A4/ - - 1、查看日志最后几行

    1
    tail -100 /access.log

    2、进入目录相关

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #进入一个目录
    root@hisenyuan:/# cd /home/wwwlog/
    #进入当前目录下的www.google.com目录
    root@hisenyuan:/home/wwwlog# cd ./www.google.com
    #进入父目录
    root@hisenyuan:/home/wwwlog/www.google.com# cd ../
    #进入linux系统根目录
    root@hisenyuan:/home/wwwlog# cd /
    #根目录
    root@hisenyuan:/#

    3、看倒数多少行

    1
    2
    3
    4
    #看倒数10行
    tail -10 /filepath/filename
    #看行数外加过滤含有指定字符的行
    tail -10 access.log | grep -v "yourstring"

    4、过滤特定行,保存结果到新文件

    1
    cat /root/old.text | grep -v "yourstring"> /root/new.text

    ]]>
    - - - - - - java - - linux - - - -
    - - - - - 基本排序算法的时间/空间复杂度表 - - /20170120-%E5%9F%BA%E6%9C%AC%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%97%B6%E9%97%B4-%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E8%A1%A8/ - - 排序方法平均情况最好情况最坏情况辅助空间稳定性冒泡排序O(n²)O(nlogn)O(n²)O(1)稳定简单选择O(n²)O(n²)O(n²)O(1)稳定直接插入O(n²)O(n)O(n²)O(1)稳定希尔排序O(nlogn)~O(n²)O(n^1.3)O(n²)O(1)不稳定堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定归并排序O(nlogn)O(nlogn)O(nlogn)O(n)不稳定快速排序O(nlogn)O(nlogn)O(n²)O(nlogn)~O(n)不稳定

    以上

    ]]>
    - - - - - java - - - - - - - java - - 算法 - - 排序 - - - -
    - - - - - Oracle SQL语句优化 - 写出高效SQL - - /20170120-Oracle-SQL%E8%AF%AD%E5%8F%A5%E4%BC%98%E5%8C%96-%E5%86%99%E5%87%BA%E9%AB%98%E6%95%88SQL/ - - [1]选择最有效率的表名顺序

    只在基于规则的优化器中有效,ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.

    [2]WHERE子句中的连接顺序

    ORACLE采用自下而上(从后往前)的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾

    [3]SELECT子句中避免使用’*’

    ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间.需要什么字段就查询什么字段,永远不要查询出不需要的字段来

    [4]减少访问数据库的次数

    ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等;尽量使用缓存技术;

    [5]设置单次访问合适的检索数据量

    在SQLPlus , SQLForms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200

    [6]使用DECODE函数来减少处理时间

    使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表,因为decode函数有短路效应,类似java中短路与,有合适的就会返回而不继续扫描后面的内容

    [7]整合简单,无关联的数据库访问

    如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系),原因见[4]

    [8]删除重复记录,最高效的方法

    因为这里使用的是rowid

    1
    2
    3
    4
    5
    6
    DELETE
    FROM EMP E
    WHERE E.ROWID >
    (SELECT MIN(X.ROWID)
    FROM EMP X
    WHERE X.EMP_NO = E.EMP_NO);

    [9]用TRUNCATE替代DELETE

    当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短. (注: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML)

    [10]尽量多使用COMMIT

    只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少:
    COMMIT所释放的资源:

    1. 回滚段上用于恢复数据的信息
    2. 被程序语句获得的锁
    3. redo log buffer 中的空间
    4. ORACLE为管理上述3种资源中的内部花费

    [11]用Where子句替换HAVING子句

    避免使用HAVING子句, HAVING在检索出所有记录后对结果集进行过滤。这个处理需要排序,总计等操作。 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里

    [12]减少对表的查询

    在含有子查询的SQL语句中,要特别注意减少对表的查询。例子:

    1
    2
    3
    4
    5
    6
    7
    8
    SELECT TAB_NAME
    FROM TABLES
    WHERE (TAB_NAME,
    DB_VER) =
    (SELECT TAB_NAME,
    DB_VER
    FROM TAB_COLUMNS
    WHERE VERSION = 604)

    [13]通过内部函数提高SQL效率

    复杂的SQL往往牺牲了执行效率。能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的。一般把复杂的sql分解再拼起来。

    [14]使用表的别名(Alias)

    当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上。这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。

    [15]用EXISTS替代IN、用NOT EXISTS替代NOT IN

    在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率。在子查询中,NOT IN子句将执行一个内部的排序和合并。无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历).。为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS.

    高效:

    1
    2
    3
    4
    5
    SELECT *
    FROM EMP (基础表)
    WHERE EMPNO > 0
    AND EXISTS
    (SELECT 'X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = 'MELB')

    低效:

    1
    2
    3
    4
    5
    6
    7
    SELECT *
    FROM EMP (基础表)
    WHERE EMPNO > 0
    AND DEPTNO IN
    (SELECT DEPTNO
    FROM DEPT
    WHERE LOC = 'MELB')

    [16]识别’低效执行’的SQL语句

    虽然目前各种关于SQL优化的图形化工具层出不穷

    但是写出自己的SQL工具来解决问题始终是一个最好的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    SELECT EXECUTIONS,
    DISK_READS,
    BUFFER_GETS,
    ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,
    ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,
    SQL_TEXT
    FROM V$SQLAREA
    WHERE EXECUTIONS>0
    AND BUFFER_GETS > 0
    AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8
    ORDER BY 4 DESC;

    [17]用索引提高效率

    索引是表的一个概念部分,用来提高检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构。通常,通过索引查询数据比全表扫描要快。 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引。同样在联结多个表时使用索引也可以提高效率。另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证。那些LONG或LONG RAW数据类型,你可以索引几乎所有的列。 通常,在大型表中使用索引特别有效。当然,你也会发现,在扫描小表时,使用索引同样能提高效率。虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时, 索引本身也会被修改。这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O 。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。定期的重构索引是有必要的。

    1
    ALTER  INDEX <INDEXNAME> REBUILD <TABLESPACENAME>

    [18]用EXISTS替换DISTINCT

    当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT。一般可以考虑用EXIST替换,EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果。

    低效:

    1
    2
    3
    4
    5
    SELECT DISTINCT DEPT_NO,
    DEPT_NAME
    FROM DEPT D,
    EMP E
    WHERE D.DEPT_NO = E.DEPT_NO

    高效:

    1
    2
    3
    4
    5
    6
    7
    SELECT DEPT_NO,
    DEPT_NAME
    FROM DEPT D
    WHERE EXISTS
    (SELECT 'X'
    FROM EMP E
    WHERE E.DEPT_NO = D.DEPT_NO);

    [19]sql语句用大写

    因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行

    [20]在java代码中尽量少用连接符“+”连接字符串

    一般来说StringBuilder(非线程安全)是一个不错的选择

    [21]通常避免在索引列上使用NOT

    我们要避免在索引列上使用NOT,NOT会产生在和在索引列上使用函数相同的影响。当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描。

    [22]避免在索引列上使用计算

    WHERE子句中,如果索引列是函数的一部分。优化器将不使用索引而使用全表扫描。

    举例:

    低效:

    1
    SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;

    高效:

    1
    SELECT … FROM DEPT WHERE SAL > 25000/12;

    [23]用>=替代>

    高效:

    1
    SELECT * FROM  EMP  WHERE  DEPTNO >=4

    低效:

    1
    SELECT * FROM EMP WHERE DEPTNO >3

    两者的区别在于,前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录。

    [24]用UNION替换OR (适用于索引列)

    通常情况下,用UNION替换WHERE子句中的OR将会起到较好的效果。对索引列使用OR将造成全表扫描。注意,以上规则只针对多个索引列有效。如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低。在下面的例子中, LOC_ID 和REGION上都建有索引。

    高效:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    SELECT LOC_ID,
    LOC_DESC,
    REGION
    FROM LOCATION
    WHERE LOC_ID = 10
    UNION
    SELECT LOC_ID,
    LOC_DESC,
    REGION
    FROM LOCATION
    WHERE REGION = ''MELBOURNE''

    低效:

    1
    2
    3
    4
    5
    6
    SELECT LOC_ID,
    LOC_DESC,
    REGION
    FROM LOCATION
    WHERE LOC_ID = 10
    OR REGION = ''MELBOURNE''

    如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面

    [25]用IN来替换OR

    这是一条简单易记的规则,但是实际的执行效果还须检验,在ORACLE8i下,两者的执行路径似乎是相同的。

    低效:

    1
    SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30

    高效:

    1
    SELECT… FROM LOCATION WHERE LOC_IN  IN (10,20,30);

    [26]避免在索引列上使用IS NULL和IS NOT NULL

    避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引。对于单列索引,如果列包含空值,索引中将不存在此记录。对于复合索引,如果每个列都为空,索引中同样不存在此记录。如果至少有一个列不为空,则记录存在于索引中。

    举例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入)。然而如果所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空。 因此你可以插入1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引。

    低效: (索引失效)

    1
    SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL;

    高效: (索引有效)

    1
    SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0;

    [27]总是使用索引的第一个列

    如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引。这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引

    [28]用UNION-ALL替换UNION

    当SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序。如果用UNION ALL替代UNION, 这样排序就不是必要了。效率就会因此得到提高。需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录。因此各位还是要从业务需求分析使用UNION ALL的可行性。 UNION 将对结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存。对于这块内存的优化也是相当重要的。下面的SQL可以用来查询排序的消耗量。

    低效:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    UNION
    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'

    高效:

    1
    2
    3
    4
    5
    6
    7
    8
    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    UNION ALL
    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95'

    [29]用WHERE替代ORDER BY

    ORDER BY 子句只在两种严格的条件下使用索引

    ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序

    ORDER BY中所有的列必须定义为非空

    WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列

    例如:

    表DEPT包含以下列:

    DEPT_CODE PK NOT NULL

    DEPT_DESC NOT NULL

    DEPT_TYPE NULL

    低效: (索引不被使用)

    1
    SELECT DEPT_CODE FROM  DEPT  ORDER BY  DEPT_TYPE

    高效: (使用索引)

    1
    SELECT DEPT_CODE  FROM  DEPT  WHERE  DEPT_TYPE > 0

    [30]避免改变索引列的类型

    当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换

    假设 EMPNO是一个数值类型的索引列.

    1
    SELECT …  FROM EMP  WHERE  EMPNO = '123'

    实际上,经过ORACLE类型转换, 语句转化为:

    1
    SELECT …  FROM EMP  WHERE  EMPNO = TO_NUMBER('123')

    幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变

    现在,假设EMP_TYPE是一个字符类型的索引列

    1
    SELECT …  FROM EMP  WHERE EMP_TYPE = 123

    这个语句被ORACLE转换为:

    1
    SELECT …  FROM EMP  WHERETO_NUMBER(EMP_TYPE)=123

    因为内部发生的类型转换, 这个索引将不会被用到!

    为了避免ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来。 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型

    [31]需要当心的WHERE子句

    某些SELECT 语句中的WHERE子句不使用索引
    这里有一些例子

    在下面的例子里,

    1. ‘!=’ 将不使用索引。记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中
    2. ‘||’是字符连接函数. 就象其他函数那样, 停用了索引
    3. ‘+’是数学函数. 就象其他数学函数那样, 停用了索引
    4. 相同的索引列不能互相比较,这将会启用全表扫描

    [32]检索数据量超过表中30%,索引失效

    1. 如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高
    2. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别。 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍!

    [33]避免使用耗费资源的操作

    带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎
    执行耗费资源的排序(SORT)功能。

    DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序。
    通常, 带有UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写。

    如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以考虑的, 毕竟它们的可读性很强。

    [34]优化GROUP BY

    提高GROUP BY 语句的效率,可以通过将不需要的记录在GROUP BY 之前过滤掉。

    下面两个查询返回相同结果但第二个明显就快了许多。

    低效:

    1
    2
    3
    4
    5
    6
    SELECT JOB,
    AVG(SAL)
    FROM EMP
    GROUP JOB
    HAVING JOB = 'PRESIDENT'
    OR JOB = 'MANAGER'

    高效:

    1
    2
    3
    4
    5
    6
    SELECT JOB,
    AVG(SAL)
    FROM EMP
    WHERE JOB = 'PRESIDENT'
    OR JOB = 'MANAGER'
    GROUP JOB

    本文参考其他文章整理而来

    出处:http://www.cnblogs.com/rootq/archive/2008/11/17/1334727.html

    不过互联网上这篇文章很多,都没有版权注明。我也不知道原创是谁!

    ]]>
    - - - - - - sql - - oracle - - oarcle优化 - - - -
    - - - - - 该Java语句创建了多少个对象? - - /20170120-%E8%AF%A5Java%E8%AF%AD%E5%8F%A5%E5%88%9B%E5%BB%BA%E4%BA%86%E5%A4%9A%E5%B0%91%E4%B8%AA%E5%AF%B9%E8%B1%A1%EF%BC%9F/ - -
    1
    String str = new String("java");

    答案:最少一个,最多两个

    1. java中有常量池的概念,常量池和类文件相关,其数据存放的区域是在方法区中(方法区是jvm中内存模型的概念)
    2. 因为当你使用关键字new的时候是一定会生成一个String类的实例,当你使用直接量的方式定义了一个字符串时,假如这个字符串在常量池中,则不会去实例化String,反之则会生成一个String类的实例,并置入常量池
    ]]>
    - - - - - - java - - - -
    - - - - - MySQL一些简单的语句 - - /20170120-MySQL%E4%B8%80%E4%BA%9B%E7%AE%80%E5%8D%95%E7%9A%84%E8%AF%AD%E5%8F%A5/ - - emlog_ad字段
    id
    status
    position
    title
    weight
    content

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    --找出重复
    SELECT a.*
    FROM emlog_ad a
    WHERE a.id IN
    (SELECT b.id id
    FROM emlog_ad b
    GROUP BY b.title
    HAVING count(b.id)>1);

    --删除重复留下id最小的
    SELECT a.*
    FROM emlog_ad a
    WHERE a.id IN
    (SELECT b.id
    FROM emlog_ad b
    GROUP BY b.title
    HAVING count(b.id)>1)
    AND a.id NOT IN
    (SELECT min(c.id)
    FROM emlog_ad c
    GROUP BY c.title
    HAVING count(c.id)>1);

    --一句sql把所有AA改为BB,CC改为DD
    UPDATE emlog_ad a
    SET a.`status`=(
    CASE
    WHEN a.`status` = '1' THEN '11'
    WHEN a.`status`='2' THEN '22'
    END
    );
    ]]>
    - - - - - - mysql - - - -
    - - - - - hexo安装过程 - - /20170120-hexo%E5%AE%89%E8%A3%85%E8%BF%87%E7%A8%8B/ - - 准备工作
    1. Node.js:点击下载
    2. git:点击下载
    3. MarkdownPad:点击下载

    安装好上面三个工具
    可能会遇到的问题:

    1、Git Bash执行node -v提示无效 或者 npm install 报 command not found

    解决办法:在环境变量 - 用户变量中 - 新建用户变量 - 添加nodejs安装路径

    如:C:\tool\nodejs

    2、ERROR Deployer not found : github

    解决办法:

    1. 配置文件有问题,冒号后面都有一个空格的
    2. 执行:npm install hexo-deployer-git –save (这命令是为了解决hexo新版本的部署问题)

    3使用淘宝镜像加快安装速度
    安装cnpm,使用命令:

    1
    npm install cnpm -g --registry=https://registry.npm.taobao.org

    安装过程

    1. 打开Git Bash
    2. 进入nodejs安装目录
    3. 开始安装hexo,输入下面代码
    4. npm install -g hexo#等待安装完成,这个过程可能会快也可能很慢,耐心等待
    5. mkdir blog && cd blog #上面这个代码是创建一个博客存放的目录
    6. hexo init#初始化
    7. cnpm install #安装依赖包
    8. 完成之后,本地博客就搭建完成
    9. hexo g #生成静态页面
    10. hexo s #启动服务器,打开http://localhost:4000 就是本地博客

    本地博客安装完成,下面介绍发布到github上

    1. 登陆github,没有就注册
    2. 点击右上角加号+
    3. Create a new repository
    4. 名字写:yourgithubname.github.io
    5. 创建完成
    6. 点击Setting
    7. 选择一个主题,然后就好了
    8. 编辑blog文件夹里面的_config.yml配置文件
    9. 最后面添加
      1
      2
      3
      4
      deploy:
      type: git
      repository: http://github.com/yourname/yourname.github.io.git
      branch: master

    最后执行

    1. hexo g#重新生成静态博客
    2. hexo d#将本地静态博客部署到github

    现在你在浏览器打开:http://yourname.github.io就可以访问你的博客了
    到此为止就搭建完了一个博客

    开始写第一篇文章:
    执行:hexo new “你的文章标题”
    然后你在blog/source/_posts文件夹下面有文件,用markdownpad打开编辑
    执行:

    1. hexo g#重新生成
    2. hexo s#本地查看效果
    3. hexo d#上传到github
    4. 或者不预览,直接一步上传到github:hexo d -g
    ]]>
    - - - - - - hexo - - github - - - -
    - - - - - Test Java Code - - /20170120-Test-Java-Code/ - - 这里我只是贴一段代码测试一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    package com.hisen.interview;

    /**
    * 变量不能被重写
    *
    * @author hisenyuan 2017年1月18日 下午10:33:33
    */
    public class AboutExtends {
    public static class A {
    public int a = 0;

    public void fun() {
    System.out.println("A");
    }

    static {
    System.out.println("Astatic");
    }
    {
    System.out.println("I'm A class");
    }
    }

    public static class B extends A {
    public int a = 1;

    public void fun() {
    System.out.println("B");
    }

    static {
    System.out.println("Bstatic");
    }
    {
    System.out.println("I'm B class");
    }
    }

    public static void main(String[] args) {
    // 里面的static块方法,new了就会执行
    // new new B()两个都执行,new new A()执行A的
    //static代码块在{}代码块后面执行
    A classA = new B();
    System.out.println(classA.a);
    classA.fun();
    // 输出信息
    // Astatic
    // Bstatic
    // I'm A class
    // I'm B class
    // 1
    // B

    // 多态记忆口诀
    // 变量多态看左边
    // 方法多态看右边
    // 静态多态看左边
    }
    }
    ]]>
    - - - - - - java - - - -
    - - - - - 解决:html rendering error - MarkdownPad 2 - - /20170119-%E8%A7%A3%E5%86%B3%EF%BC%9Ahtml-rendering-error-MarkdownPad-2/ - - MarkdownPad 2在windows 10上会遇到这个错误

    官方发布了这个问题的解决办法

    详见:点击前往 页面中搜索:This view has crashed

    解决办法

    windows 10系统 需要下载 一个 awesomium_v1.6.6_sdk_win

    这是一个 HTML UI ENGINE

    下载地址:http://markdownpad.com/download/awesomium_v1.6.6_sdk_win.exe

    ]]>
    - - - - - - MarkdownPad - - html rendering error - - - -
    - - - - + + + 1103 host 'xxx' is not allowed to connect to this mysql + /20170311-1103%20host%20%E2%80%98xxx%E2%80%99%20is%20not%20allowed%20to%20connect%20to%20this%20mysql/ + 出现原因:
    这是由于mysql服务端root用户所对应的客户端权限设置问题。

    默认所对应的客户端地址只有localhost(也就是服务端的机器),

    我们目的是任何地址都可以用root访问mysql服务端。

    解决办法:

    $ mysql -u root -p
    #进入mysql交互界面
    mysql> use mysql;
    #使用mysql这个库
    mysql> grant all privileges on *.* to 'root'@'%' identified by 'hisen';
    #让root可以在任何ip登陆,密码为:hisen
    mysql> flush privileges;
    #刷新
    mysql> exit;
    #退出
    $ service mysql restart
    #重启mysql

    ]]>
    + + java + + + mysql + +
    + + BeanCreationException: Error creating bean with name 'xxxService' + /20170223-BeanCreationException-Error-creating-bean-with-name-xxxService/ + 出现的问题:

    [platform] ERROR 2017-02-22 17:46:05,756 [RMI TCP Connection(4)-127.0.0.1] org.springframework.web.context.ContextLoader.() | Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pmTranLimitLiteServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.msds.zkutil.ZkLockFactory] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.annotation.Resource(mappedName=, shareable=true, description=, name=, type=class java.lang.Object, authenticationType=CONTAINER, lookup=)}
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:306) ~[spring-context-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1116) ~[spring-beans-3.2.4.RELEASE.jar:3.2.4.RELEASE]

    最重要的是这句

    org.springframework.beans.factory.BeanCreationException:
    Error creating bean with name'pmTranLimitLiteServiceImpl'

    出现的原因:
    缺少相关的jar包或者依赖

    建议不要自己配置idea的module和artificts

    直接在pom.xml文件添加

    <artifactId>hisen-project</artifactId><!--加在这句话后面-->
    <packaging>war</packaging><!--加上这句话就会自动给你打war包-->


    其他原因

    开始不知道什么问题,后来搜索这个服务。

    发现这跟dubbo有关,于是百度搜索进了官网

    没想到常见问题里面就有说这个事情

    13. 出现org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xxxService': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Method must not be null怎么办?

    通常是classpath下存在spring多个版本的jar包,排除掉不需要的spring包即可。

    更多dubbo问题:点击查看

    ]]>
    +
    + + 关于学习英语 + /20210605-About-learning-English/ + 一、背景

    作为一个软件开发工程师,日常很多机会和英文打交道。
    特别是上一份工作,做全球支付项目,需要用到英文与国际友人沟通。
    奈何自己的英语水平捉襟见肘,于是经常会有意地去收集相关的文章。

    今天心血来潮,再次看了一下之前收藏的相关文章,感觉收获不少。
    于是就想写一篇文章归集一下相关的内容,方便日后翻阅,顺便分享给有需要的人。

    二、资源

    复旦大学中文系教授严峰

    程序员圈”名”人王垠 :

    • 解谜英语语法
      语法、动词比较重要
      推荐:练习造句。分析句子。
      语法树的概念蛮好理解,我让我明白了什么叫宾补~

    • 英语学习的一些经验
      语法和句子结构是关键,其次才是词汇量。
      如果你的词汇量足够阅读技术文档,那就可以开始看了。
      遇到不懂的单词,查询之,使用英英字典,记录在本子上,加强记忆。

    GitHub 上的成功人士:

    三、后话

    还是得坚持看文档,背单词。

    ]]>
    + + 成长 + + + English + +
    + + Agent CGlib VS JDK | 动态代理比较 + /20190330-Agent%20CGlib%20VS%20JDK%20%7C%20%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E6%AF%94%E8%BE%83/ + 一、代理简介

    1.1 代理是什么

    举个例子:快到吃饭的点了,你有两种选择:1)自己做,2)叫外卖
    1.1.1 自己做你嫌麻烦,那就叫外卖,只管收外卖其它不关注(解耦);
    1.1.2 自己做的不好吃,那厨师上门,厨师给你增强菜的味道(增强);

    官方定义:对其他对象提供一种代理以控制对这个对象的访问。

    1.2 使用场景是什么

    1.2.1 设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。

    1.2.2 我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。

    1.2.3 Spring的AOP机制就是采用动态代理的机制来实现切面编程。

    二、CGlib VS JDK 两种方式实现代理

    2.1 差异

    2.1.1 JDK:只能代理接口
    2.1.2 CGlib:直接代理类

    2.2 简单demo

    完整代码:https://github.com/hisenyuan/IDEAPractice/tree/master/src/main/java/com/hisen/jdk/agent

    public static void main(String[] args) {
    System.out.println("===CGlibAgentTest===");
    cGlibAgentTest();

    System.out.println("\n===JDKDynamicAgentTest===");
    jdkDynamicAgentTest();
    }

    private static void cGlibAgentTest() {
    CGlibAgent cGlibAgent = new CGlibAgent();
    Apple apple = (Apple) cGlibAgent.getInstance(Apple.class);
    apple.show();

    System.out.println();

    Orange orange = (Orange) cGlibAgent.getInstance(Orange.class);
    orange.show();
    }

    private static void jdkDynamicAgentTest() {
    // must be return interface
    Fruit apple = (Fruit) DynamicAgent.agent(Fruit.class, new Apple());
    apple.show();

    System.out.println();

    Fruit orange = (Fruit) DynamicAgent.agent(Fruit.class, new Orange());
    orange.show();
    }
    //===CGlibAgentTest===
    //-> before invoking
    //Apple show method is invoked
    //-> after invoking
    //
    //-> before invoking
    // Orange show method is invoked
    //-> after invoking
    //
    //===JDKDynamicAgentTest===
    //-> before invoking
    // Apple show method is invoked
    //-> after invoking
    //
    //-> before invoking
    // Orange show method is invoked
    //-> after invoking

    三、参考连接

    http://www.cnblogs.com/puyangsky/p/6218925.html
    https://blog.csdn.net/u011784767/article/details/78281384

    ]]>
    + + java + + + java + jdk + +
    + + Autowire、Resource的区别 | Java注解 + /20190722-Autowire%E3%80%81Resource%E7%9A%84%E5%8C%BA%E5%88%AB%20%7C%20Java%E6%B3%A8%E8%A7%A3/ + 零、本文背景

    项目中看到有一个缓存接口存在多个实现类,
    但是在代码中使用@Resource注解注入,
    之前有了解过@Autowire @Resource的区别,
    于是就尝试着搜索@@Resource,于是就有本文的总结了。

    一、两个注解

    1.1 @Autowire

    1.1.1 Spring开发;
    1.1.2 按照type来注入;

    1.2 @Resource

    1.2.1 JDK开发;
    1.2.2 按照名称注入,若无,则按type来注入(未指定name的情况下);

    二、后记

    1. 做事情要有计划,得主动;
    2. 需要想清楚自己想要什么样的生活,然后朝着目标奋斗;
    3. 使用现成的代码尽量搞清楚来龙去脉,可以学习知识,更能避免被坑;
    4. 每天的学习时间需要保证,坚持很重要;
    ]]>
    + + java + + + java + +
    + + Data URI scheme 利用base64字符串通过image标签显示图片 + /20170512-Data%20URI%20scheme%20%E5%88%A9%E7%94%A8base64%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%80%9A%E8%BF%87image%E6%A0%87%E7%AD%BE%E6%98%BE%E7%A4%BA%E5%9B%BE%E7%89%87/ + 目前,Data URI scheme支持的类型有:

    1. data:,文本数据
    2. data:text/plain,文本数据
    3. data:text/html,HTML代码
    4. data:text/html;base64,base64编码的HTML代码
    5. data:text/css,CSS代码
    6. data:text/css;base64,base64编码的CSS代码
    7. data:text/javascript,Javascript代码
    8. data:text/javascript;base64,base64编码的Javascript代码
    9. data:image/gif;base64,base64编码的gif图片数据
    10. data:image/png;base64,base64编码的png图片数据
    11. data:image/jpeg;base64,base64编码的jpeg图片数据
    12. data:image/x-icon;base64,base64编码的icon图片数据

    Data URL是在本地直接绘制图片,不是从服务器加载,所以节省了HTTP连接,起到加速网页的作用。

    也无法获取到图片在服务器上的真实地址

    注意:本方法适合于小图片,大图片就不要考虑了,另外IE8以下浏览器不支持这种方法。

    用这种方法会加重客户端的CPU和内存负担,总之有利有弊。

    前台代码:

    <%@ page import="com.hisen.image.ShowImageByBase64" %><%--
    Created by IntelliJ IDEA.
    User: Administrator
    Date: 2017/5/11
    Time: 18:55
    To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <% String imageStr = ShowImageByBase64.showimage();%>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    <img src="data:image/png;base64,<%=imageStr%>" alt="base64图片"/>
    </body>
    </html>

    后台代码:

    package com.hisen.image;

    import com.hisen.utils.Base64Util;
    import com.hisen.utils.File2ByteArraysUtil;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import sun.misc.BASE64Encoder;

    /**
    * <img src="data:image/png;base64,<%=imageStr%>" alt="base64image"/>
    * Created by hisenyuan on 2017/5/11 at 18:44.
    */
    public class ShowImageByBase64 {

    public static String showimage() {
    //写相对路径会报错,暂时不知道如何解决
    String imagePath = "C:\\1\\830.jpg";
    byte[] bytes = File2ByteArraysUtil.file2Bytes(imagePath);
    String s = Base64Util.encodeBase64(bytes);
    return s;
    }

    /**
    * sun.misc.BASE64Encoder
    */
    public static String encodeBase64(byte[] str) {
    if (str == null) {
    return null;
    } else {
    BASE64Encoder encoder = new BASE64Encoder();
    try {
    return encoder.encode(str);
    } catch (Exception var3) {
    return null;
    }
    }
    }

    /***
    * file2byte[]
    * @param path
    * @return
    */
    public static byte[] file2Bytes(String path) {
    byte[] buffer = null;
    File file = new File(path);
    try {
    FileInputStream fis = new FileInputStream(file);
    ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
    byte[] b = new byte[1000];
    int n;
    while ((n = fis.read(b)) != -1) {
    bos.write(b, 0, n);
    }
    fis.close();
    bos.close();
    buffer = bos.toByteArray();
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return buffer;
    }
    }

    ]]>
    + + java + + + java + jsp + +
    + + Curator - 封装分布式锁等 | ZooKeeper目前最好用的客户端 + /20190425-Curator%20-%20ZooKeeper%E7%9B%AE%E5%89%8D%E6%9C%80%E5%A5%BD%E7%94%A8%E7%9A%84%E5%AE%A2%E6%88%B7%E7%AB%AF/ + 一、什么是Curator

    Apache Curator is a Java/JVM client library for Apache ZooKeeper, a distributed coordination service.
    It includes a highlevel API framework and utilities to make using Apache ZooKeeper much easier and more reliable.
    It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL.

    特别说明:Guava is to Java What Curator is to ZooKeeper

    二、常见用法

    后期补上自己的一些demo,其实官方文档已经介绍很全了
    http://curator.apache.org/getting-started.html

    三、其它说明

    了解这个客户端是在《从Paxos到Zookeeper:分布式一致性原理与实战》这本书里面看到的(书单)
    Curator号称是世界上最好用的zk客户端,相比zkClinet来说拥有更好的封装
    让我想起Redisson和Jedis的模样

    昨天一个做移动端的前同事截图问我那些代码什么意思,用的就是Curator封装的分布式锁

    相比于Redis分布式存在超时问题,zookeeper分布式锁利用临时节点可以避免

    目前dubbo master上使用的是Curator 4.0.1

    ]]>
    + + java + + + java + +
    + + DockerFile - 创建java开发环境镜像 + /20180111-DockerFile%20-%20%E5%88%9B%E5%BB%BAjava%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E9%95%9C%E5%83%8F/ + 使用ubuntu官方发布的docker镜像进行二次修改

    这是一个菜鸟的脚本,执行命令应该是使用 & 连接,一个RUN命令搞定

    FROM    ubuntu
    MAINTAINER Fisher "hisenyuan@gmail.com"
    RUN /bin/echo 'root:hisen' |chpasswd
    RUN useradd hisen
    RUN /bin/echo 'hisen:hisen' |chpasswd
    RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
    # 显示系统位数
    RUN uname -p
    # 清空源
    RUN echo "" > /etc/apt/sources.list
    # 更换为阿里云源
    RUN echo "deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse" >> /etc/apt/sources.list
    RUN echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse" >> /etc/apt/sources.list
    # 更新源
    RUN apt-get update
    # 安装软件
    RUN apt-get -y install vim
    RUN apt-get -y install curl
    RUN apt-get -y install wget
    RUN apt-get -y install net-tools
    RUN apt-get -y install iputils-ping
    RUN apt-get -y install git
    # 创建软件文件夹
    RUN mkdir -p /usr/hisen/soft/java
    RUN mkdir -p /usr/hisen/soft/tomcat
    RUN mkdir -p /usr/hisen/soft/maven
    RUN mkdir -p /usr/hisen/soft/download
    # 添加本地软件包到指定文件夹(会自动解压,软件压缩包必须放在docker同级目录)
    ADD jdk-8u151-linux-x64.tar.gz /usr/hisen/soft/java/
    ADD apache-tomcat-8.5.24.tar.gz /usr/hisen/soft/tomcat/
    ADD apache-maven-3.5.2-bin.tar.gz /usr/hisen/soft/maven/

    # 配置环境变量
    # java
    ENV JAVA_HOME=/usr/hisen/soft/java/jdk1.8.0_151
    ENV JRE_HOME=$JAVA_HOME/jre
    ENV CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib
    ENV PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
    # tomcat
    ENV CATALINA_HOME=/usr/hisen/soft/tomcat/apache-tomcat-8.5.24
    ENV CLASSPATH=.:$JAVA_HOME/lib:$CATALINA_HOME/lib
    ENV PATH=$PATH:$CATALINA_HOME/bin
    # maven
    ENV MAVEN_HOME=/usr/hisen/soft/maven/apache-maven-3.5.2
    ENV MAVEN_OPTS="-Xms256m -Xmx512m"
    ENV PATH=${MAVEN_HOME}/bin:$PATH

    # 监听端口
    EXPOSE 22
    EXPOSE 80
    EXPOSE 8080
    CMD /usr/sbin/sshd -D

    ]]>
    + + linux + + + docker + +
    + + Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder + /20181015-Could%20not%20initialize%20class%20io.jsonwebtoken.impl.DefaultJwtBuilder/ + 使用jsonwebtoken出现如下错误

    Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder
    java.lang.NoClassDefFoundError: Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder
    at io.jsonwebtoken.Jwts.builder(Jwts.java:116) ~[jjwt-0.7.0.jar:0.7.0]

    原因,因为jackson-databindb版本冲突,直接去掉了依赖

    jwt必须依赖Jackson所以报错了

    出错时候的配置

    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
    <exclusions>
    <exclusion>
    <artifactId>jackson-databind</artifactId>
    <groupId>com.fasterxml.jackson.core</groupId>
    </exclusion>
    </exclusions>
    </dependency>

    解决办法

    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
    </dependency>

    ]]>
    + + java + + + jsonwebtoken + +
    + + BeanUtils 对象拷贝 - 仅拷贝指定属性 - 接口性能优化 + /20200926-BeanUtils-copyLimitedProperties-for-better-performance-of-response/ + 零、背景

    很多时候我们调用接口并不是需要接口返回的所有信息;
    就像查询数据库很少使用 select * from table; 一样;

    为了使现有的存储结构以及代码逻辑改动最少
    想办法在最外层的接口对返回的对象进行精简

    目的使为了提高接口性能:减少 RPC 传输时间、节省网络带宽、节省序列化开销

    一、方案

    1. 直接创建一个对象,根据入参传入的所需字段,写一堆 if else 进行 get set;
    2. 使用序列化工具,转为类似 Map 结构,取值拼装;
    3. 使用 BeanWrapper 操作对象;

    前面两种比较呆板,属于定制化开发;

    二、实践

    Spring BeanWrapper 方案处理非集合对象比较完美,输出对象小不少;
    基本数据类型有默认值,无法去除,不过这种对大小影响不大;
    完整代码:github.com

    public static void main(String[] args) {
    Order order = getOrder();
    Order smallerOrder = new Order();
    List<String> keepFields = Lists.newArrayList();
    keepFields.add("orderId");
    keepFields.add("address.province");
    // i want get all names, not only index 1, how to do ?
    keepFields.add("productInfos[1].name");
    copyLimitedProperties(order, smallerOrder, keepFields);
    Gson gson = new Gson();
    System.out.println("src:" + gson.toJson(order));
    System.out.println("des" + gson.toJson(smallerOrder));
    }

    输出 src:

    {
    "address": {
    "province": "B",
    "city": "A",
    "county": "C"
    },
    "productInfos": [
    {
    "productId": 0,
    "name": "name0",
    "price": 1,
    "num": 0,
    "imgUrl": "url1"
    },
    {
    "productId": 0,
    "name": "name1",
    "price": 2,
    "num": 0,
    "imgUrl": "url1"
    },
    {
    "productId": 0,
    "name": "name2",
    "price": 3,
    "num": 0,
    "imgUrl": "url1"
    }
    ]
    }

    输出dst

    {
    "address": {
    "province": "B"
    },
    "productInfos": [
    {
    "productId": 0,
    "num": 0
    },
    {
    "productId": 0,
    "name": "name1",
    "num": 0
    }
    ]
    }

    三、缺点

    目前 Spring BeanWrapper 处理集合类型比较费劲,需要告知一个个具体的路径
    比如:productInfos[1].name
    如果能做到 productInfos[any].name 这种,然后取出所有 index 的属性,就比较完美
    (目前解决方法:通过自定义处理,遇到 any 标识,自动补全所有 index 的 key 即可,只是不那么优雅)

    缺陷原因:内部逻辑直接用 key 转换为 index ,没有 any 逻辑。

    else if (value instanceof List) {
    index = Integer.parseInt(key);
    List<Object> list = (List)value;
    this.growCollectionIfNecessary(list, index, indexedPropertyName, ph, i + 1);
    value = list.get(index);
    }

    代码位置:org.springframework.beans.AbstractNestablePropertyAccessor#getPropertyValue

    四、参考

    Spring 官方文档:Bean Manipulation and the BeanWrapper

    ]]>
    + + java + + + java + +
    + + Docker一键安装脚本Ubuntu-Debian-CentOS-Fedora-racleLinux + /20170417-Docker%E4%B8%80%E4%BB%B6%E5%AE%89%E8%A3%85%E8%84%9A%E6%9C%ACUbuntu-Debian-CentOS-Fedora-racleLinux/ + 系统要求
    Ubuntu 14.04、16.04

    Debian 7.7、8.0

    CentOS 7.X

    Fedora 20、21、22

    OracleLinux 6、7

    安装方法:

    curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

    详情:http://mirrors.aliyun.com/help/docker-engine

    ]]>
    + + linux + + + docker + +
    + + Docker 入门实践 + /20170929-Docker%20%E5%85%A5%E9%97%A8%E5%AE%9E%E8%B7%B5/ + 早些天不忙的时候看的入门,从有道云笔记搬过来的

    简介

    Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。
    Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
    容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

    Docker的应用场景

    1. Web 应用的自动化打包和发布。
    2. 自动化测试和持续集成、发布。
    3. 在服务型环境中部署和调整数据库或其他的后台应用。
    4. 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。

      Docker的优点

    5. 简化程序:Docker 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,便可以实现虚拟化。Docker改变了虚拟化的方式,使开发者可以直接将自己的成果放入Docker中进行管理。方便快捷已经是 Docker的最大优势,过去需要用数天乃至数周的 任务,在Docker容器的处理下,只需要数秒就能完成。
    6. 避免选择恐惧症:如果你有选择恐惧症,还是资深患者。Docker 帮你 打包你的纠结!比如 Docker 镜像;Docker 镜像中包含了运行环境和配置,所以 Docker 可以简化部署多种应用实例工作。比如 Web 应用、后台应用、数据库应用、大数据应用比如 Hadoop 集群、消息队列等等都可以打包成一个镜像部署。
    7. 节省开支:一方面,云计算时代到来,使开发者不必为了追求效果而配置高额的硬件,Docker 改变了高性能必然高价格的思维定势。Docker 与云的结合,让云空间得到更充分的利用。不仅解决了硬件管理的问题,也改变了虚拟化的方式。

      Docker 架构

    中文英文解释
    镜像Docker ImagesDocker 镜像是用于创建 Docker 容器的模板。
    容器Docker Container容器是独立运行的一个或一组应用。
    客户端Docker ClientDocker 客户端通过命令行或者其他工具使用 Docker API (查看API) 与 Docker 的守护进程通信。
    主机Docker Host一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
    仓库Docker RegistryDocker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(查看镜像) 提供了庞大的镜像集合供使用。
    工具Docker MachineDocker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。

    Ubuntu Docker 安装

    使用脚本安装

    下载最新的安装包

    hisen@hisen-pc:~$ wget -qO- https://get.docker.com/ | sh

    hello-world

    #启动
    sudo service docker start

    #运行hello-world
    docker run hello-world
    ##报错
    docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.31/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
    ##解决:su 以root方式运行

    #docker run hello-world
    ##提示:没有image,然后自动下载
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    b04784fba78d: Pull complete
    Digest: sha256:f3b3b28a45160805bb16542c9531888519430e9e6d6ffc09d72261b0d26ff74f
    Status: Downloaded newer image for hello-world:latest

    Hello from Docker!
    This message shows that your installation appears to be working correctly.

    #已经启动成功

    Docker 使用

    #启动某个指定的镜像,运行hello-world
    root@hisen-pc:/home/hisen# docker run ubuntu:15.10 /bin/echo "Hello world"
    Unable to find image 'ubuntu:15.10' locally
    15.10: Pulling from library/ubuntu
    7dcf5a444392: Downloading 6.29MB/51.07MB
    759aa75f3cee: Download complete
    3fa871dc8a2b: Download complete
    224c42ae46e7: Download complete

    各个参数解析

    docker run ubuntu:15.10 /bin/echo “Hello world”

    参数解释
    dockerDocker 的二进制执行文件
    run与前面的 docker 组合来运行一个容器
    ubuntu:15.10指定要运行的镜像,Docker首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。
    /bin/echo “Hello world”在启动的容器里执行的命令

    以上命令完整的意思可以解释为:Docker 以 ubuntu15.10 镜像创建一个新容器,然后在容器里执行 bin/echo “Hello world”,然后输出结果。

    运行交互式的容器

    root@hisen-pc:/home/hisen# docker run -i -t ubuntu:15.10 /bin/bash
    ##这时,我们已经进入了ubuntu:15.10的系统
    ##这时就是进入了命令行,可以执行相关命令
    root@6265d70f44d2:/#
    ##
    运行exit命令或者使用CTRL+D来退出容器

    -t:在新容器内指定一个伪终端或终端。
    -i:允许你对容器内的标准输入 (STDIN) 进行交互。

    启动容器(后台模式)

    root@hisen-pc:/home/hisen# docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
    8c2a843273608eb155279ce4e4f9f4c5442c2af3524ba50ca2d6c8ccbd207081

    后台启动完成,会返回一串容器的ID

    ##查看是否有容器启动
    root@hisen-pc:/home/hisen# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    8c2a84327360 ubuntu:15.10 "/bin/sh -c 'while..." About a minute ago Up About a minute

    CONTAINER ID:容器ID(这里是:==8c2a84327360==)

    NAMES:自动分配的容器名称

    ## 查看上面那个ID的容器的log
    root@hisen-pc:/home/hisen# docker logs 8c2a84327360
    ## 输出内容
    hello world
    hello world

    ## 停止容器(指定ID:8c2a84327360)
    root@hisen-pc:/home/hisen# docker stop 8c2a84327360
    ## 输出ID,说明已经停止成功
    8c2a84327360

    ## 检查是否真的关闭
    root@hisen-pc:/home/hisen# docker ps
    ## 输出没有,说明真的关闭了
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

    Docker 客户端

    docker 客户端非常简单 ,我们可以直接输入 docker 命令来查看到 Docker 客户端的所有命令选项。

    ## 调出帮助
    root@hisen-pc:/home/hisen# docker
    ## 输出帮助信息
    Usage: docker COMMAND

    A self-sufficient runtime for containers

    Options:
    --config string Location of client config files (default "/root/.docker")
    -D, --debug Enable debug mode
    --help Print usage
    -H, --host list Daemon socket(s) to connect to
    -l, --log-level string Set the logging level

    ## 了解更详细的帮助信息
    root@hisen-pc:/home/hisen#docker stats --help

    ## 输出详细帮助信息
    Usage: docker stats [OPTIONS] [CONTAINER...]

    Display a live stream of container(s) resource usage statistics

    Options:
    -a, --all Show all containers (default shows just running)
    --format string Pretty-print images using a Go template
    --help Print usage
    --no-stream Disable streaming stats and only pull the first result

    运行一个WEB容器

    运行一个 Python Flask 应用 来搭建一个web应用

    ## -d:让容器在后台运行。
    ## -P:将容器内部使用的网络端口映射到我们使用的主机上。
    root@hisen-pc:/home/hisen# docker run -d -P training/webapp python app.py
    ## 提示本地没有,远程下载
    Unable to find image 'training/webapp:latest' locally
    latest: Pulling from training/webapp
    e190868d63f8: Pull complete
    909cd34c6fd7: Pull complete
    0b9bfabab7c1: Pull complete
    a3ed95caeb02: Pull complete
    10bbbc0fc0ff: Pull complete
    fca59b508e9f: Pull complete
    e7ae2541b15b: Pull complete
    9dd97ef58ce9: Pull complete
    a4c1b0cb7af7: Pull complete
    Digest: sha256:06e9c1983bd6d5db5fba376ccd63bfa529e8d02f23d5079b8f74a616308fb11d
    Status: Downloaded newer image for training/webapp:latest

    ## 下载完成直接后台运行了,打印id
    b03903d6abf3d65a4e6469a57d11ae539aaad1f166078591e7315ca2fb2611ff

    ## 查看运行的docker
    root@hisen-pc:/home/hisen# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    b03903d6abf3 training/webapp "python app.py" 9 minutes ago Up 9 minutes 0.0.0.0:32768->5000/tcp nifty_mirzakhani

    0.0.0.0:32768->5000/tcp
    docker使用的端口5000

    映射到本地端口32768

    本地可以访问:127.0.0.1:32768

    ## 访问:http://localhost:32768/
    ## 输出
    Hello world!

    ## 可以指定端口 8060为自映射端口,5000为容器端口
    root@hisen-pc:/home/hisen# docker run -d -p 8060:5000 training/webapp python app.py
    0d68d3d4baf61248129ede721cc6004649ebddaaace8c23f16b677fbe2b2ce7d
    root@hisen-pc:/home/hisen# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    0d68d3d4baf6 training/webapp "python app.py" 5 seconds ago Up 4 seconds 0.0.0.0:8060->5000/tcp ecstatic_jang

    ## 查看端口映射情况(可以通过id,或者名字)
    ## 这里用ID测试
    root@hisen-pc:/home/hisen# docker port 0d68d3d4baf6
    ## 输出信息,后面为映射到本地的端口
    5000/tcp -> 0.0.0.0:8060

    查看WEB应用程序日志

    ## 带上 -f 参数,动态输出日志
    root@hisen-pc:/home/hisen# docker logs -f 0d68d3d4baf6
    * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

    ## 刷新页面之后的日志
    172.17.0.1 - - [01/Sep/2017 14:09:01] "GET / HTTP/1.1" 200 -
    172.17.0.1 - - [01/Sep/2017 14:09:01] "GET /favicon.ico HTTP/1.1" 404 -

    查看WEB应用程序容器的进程

    ## top后面是跟着id或者name
    root@hisen-pc:/home/hisen# docker top 0d68d3d4baf6
    ## 输出的信息
    UID PID PPID C STIME TTY TIME CMD
    root 19372 19352 0 21:39 ? 00:00:00 python app.py

    检查WEB应用程序

    ## 会输出一大串JSON形式的字符串
    ## 记录着 Docker 容器的配置和状态信息
    root@hisen-pc:/home/hisen# docker inspect 0d68d3d4baf6

    WEB程序其他操作

    docker stop  0d68d3d4baf6
    ## 移除之前必须停止,否则会报错
    docker rm 0d68d3d4baf6

    Docker 镜像使用

    列出所有本地的镜像

    root@hisen-pc:/home/hisen# docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    hello-world latest 1815c82652c0 2 months ago 1.84kB
    woailuoli993/jblse 0.2.0 82099e8d7049 6 months ago 7.58MB
    ubuntu 15.10 9b9cb95443b5 13 months ago 137MB
    training/webapp latest 6fae60ef3446 2 years ago 349MB

    其他命令

    ## 获取一个镜像
    ## ubuntu:镜像库中的TAG
    ## 13.10:某个TAG的版本
    docker pull ubuntu:13.10


    ## 搜索某个镜像
    docker search httpd

    ## 拖取镜像
    docker pull httpd

    ## 运行镜像
    docker run httpd

    ## 创建镜像
    ### 进入某个镜像的bash命令行(在此镜像基础上创建自己的镜像)
    root@hisen-pc:/home/hisen# docker run -t -i ubuntu:15.10
    ### 更新系统(镜像的)
    root@4a3f5f4373d8:/# apt-get update

    ##提交容器副本
    ### 退出镜像的bash命令界面
    root@4a3f5f4373d8:/# exit
    exit
    ### 提交副本
    root@hisen-pc:/home/hisen# docker commit -m="has update" -a="hisen" 4a3f5f4373d8 hisen/ubuntu:v2
    ### 返回副本容器的id
    sha256:a94d0bdd2e31aa420c14e9886ff911354c07a7772715bd8d380bd0832f396c82
    ### 查看,发现刚刚提交的副本显示出来了
    root@hisen-pc:/home/hisen# docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    hisen/ubuntu v2 a94d0bdd2e31 About a minute ago 159MB
    hello-world latest 1815c82652c0 2 months ago 1.84kB
    woailuoli993/jblse 0.2.0 82099e8d7049 6 months ago 7.58MB
    ubuntu 15.10 9b9cb95443b5 13 months ago 137MB
    training/webapp latest 6fae60ef3446 2 years ago 349MB

    ## 参数说明
    -m:提交的描述信息
    -a:指定镜像作者
    4a3f5f4373d8:容器ID(就是你修改之前的id)
    hisen/ubuntu:v2:指定要创建的目标镜像名

    ## 进入新镜像的bash命令
    docker run -t -i hisen/ubuntu:v2 /bin/bash

    构建一个全新的镜像

    新建一个配置文件:Dockerfile,并添加内容

    每个指令都会在镜像上创建一个新的层

    每一个指令的前缀都必须是大写的。

    第一条FROM,指定使用哪个镜像源

    RUN 指令告诉docker 在镜像内执行命令,安装了什么

    root@hisen-pc:/home/hisen# vi Dockerfile 

    FROM ubuntu:16.04
    MAINTAINER Fisher "hisenyuan@gmail.com"

    RUN /bin/echo 'root:123456' |chpasswd
    RUN useradd hisen
    RUN /bin/echo 'hisen:123456' |chpasswd
    RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
    EXPOSE 22
    EXPOSE 80
    CMD /usr/sbin/sshd -D

    ## 保存配置文件之后,构建镜像,注意最后那个点.
    root@hisen-pc:/home/hisen# docker build -t hisen/ubuntu:16.04 .
    sending build context to Docker daemon 776MB

    ]]>
    + + docker + + + docker + +
    + + DBeaver-连接全部数据库工具-DBeaver快捷键 + /20170402-DBeaver-%E8%BF%9E%E6%8E%A5%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%B7%A5%E5%85%B7-DBeaver%E5%BF%AB%E6%8D%B7%E9%94%AE/ + Windows、Linux、Mac OS X多个平台都可以用

    据我观察这个数据库可视化工具很不错,基于java

    以各种驱动来连接数据库,也就是说java支持的数据库都可以用他连接

    挺好用的,免费!!!

    安装之后新建连接,选择你要链接的数据库,配置一下就好了。

    下载地址

    官网下载地址

    快捷键

    #Shift + Home选中当前光标到行首
    #Shift + End选中当前光标到行尾
    #Shift + ↑/↓/←/→ 移动光标并且选中
    #Alt + X 执行选中的sql
    #Ctrl + Enter 执行当前光标所在行的sql
    #Ctrl + Alt + F 格式化SQL(file -> properties -> +SQL Editor -> SQL formatting)
    ]]>
    + + sql + + + sql + +
    + + Dubbo官方中文文档|用户文档|开发者指南|源码导读|运维管理 + /20190325-Dubbo%E5%AE%98%E6%96%B9%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3%7C%E7%94%A8%E6%88%B7%E6%96%87%E6%A1%A3%7C%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97%7C%E6%BA%90%E7%A0%81%E5%AF%BC%E8%AF%BB%7C%E8%BF%90%E7%BB%B4%E7%AE%A1%E7%90%86/ + 一、为什么要学Dubbo

    近几年一直在使用dubbo进行支付系统的开发;
    作为国内比较受欢迎的一个SOA框架,Dubbo使用简单设计优雅;
    里面用到的思想和技术,基本上涵盖大部分互联网公司用到的技术;
    记得直属领导说过一句话,看完Dubbo他觉得设计者简直就是天才;
    而且看完dubbo的源码,对他的影响很大,处处模仿dubbo的思想;

    二、文档地址

    中文文档:用户文档、开发者指南、源码导读、运维管理
    http://dubbo.apache.org/zh-cn/docs/user/quick-start.html
    用户手册(比较完整):
    https://dubbo.gitbooks.io/dubbo-user-book/demos/routing-rule.html
    官方博客:
    http://dubbo.apache.org/zh-cn/blog/index.html

    三、Dubbo创始人博客

    最后更新于2014年
    https://javatar.iteye.com/

    ]]>
    + + java + + + java + 源码 + Dubbo + +
    + + Get参数串KV与Map互转 - 利用java8 Stream + /20180713-Get%E5%8F%82%E6%95%B0%E4%B8%B2KV%E4%B8%8EMap%E4%BA%92%E8%BD%AC%20-%20%E5%88%A9%E7%94%A8java8%20Stream/ + 一、背景

    在各种系统需要加签的时
    一般都会把参与签名的数据以get请求参数拼接起来
    并且要求有序,这个方法会比较方便

    二、实现

    2.1 拼接为有序的get请求类字符串

    public String getSortedStr(Map<String, String> unSortedStr) {
    String sortedStr= unSortedStr
    .entrySet()
    .stream()
    .filter(entry -> !StringUtil.isEmpty(entry.getValue()))
    .sorted(Map.Entry.comparingByKey())
    .map(entry -> entry.getKey() + "=" + entry.getValue())
    .collect(Collectors.joining("&"));
    return sortedStr;
    }

    2.2 把get类参数字符串转为map

    private Map<String,String> getMapData(String getStr){
    String[] strs = getStr.split("&");
    HashMap<String, String> dataMap = new HashMap<>(16);
    for (int i = 0; i < strs.length; i++) {
    String[] str = strs[i].split("=");
    dataMap.put(str[0], str[1]);
    }
    return dataMap;
    }

    对于get类字符串没有发现比较好的方法转换为map

    ]]>
    + + java + + + java + +
    + + Java 文件读取,部分中文乱码 - 分析与解决 + /20210731-File-reading-part-of-Chinese-garbled-code/ + 一、背景

    最近做项目有一个地址库文件需要放在后端
    由于文件在 jar 包中的问题,一些读取文件的姿势失效(方便的 Guava Files)
    最后通过 getResourceAsStream 解决

    接下来遇到了一件奇怪的事情,部分汉字乱码了,
    调整编码,重新编辑汉字都试过了,无法解决。

    最后求助于百度搜索,得到了一些有效的信息。
    汉字是两个字节的,如果每次读固定个字节,可能会把汉字截断,造成乱码。
    再次印证了基础知识的重要性!

    二、相关代码

    2.1 罪魁祸首

    利用缓冲区读取文件,会出现边界情况下把汉字分割成两次来读。

    private static String getAddressJson(String path) throws IOException {
    log.info("cacheFile start. PATH:{}", path);
    InputStream resourceAsStream = Address.class.getClassLoader().getResourceAsStream(path);
    StringBuilder sb = new StringBuilder();
    byte[] buf = new byte[10240];
    int length;

    while ((length = Objects.requireNonNull(resourceAsStream).read(buf)) != -1) {
    // 此处 new String 放进去了一半中文字符,导致乱码
    sb.append(new String(buf, 0, length, StandardCharsets.UTF_8));
    }
    resourceAsStream.close();

    return sb.toString();
    }

    2.2 完美运行

    据说现在很多人没法纯手写通过流读取文件了…(说的就是我!)

    private static String getAddressJson(String path) throws IOException {
    log.info("cacheFile start. PATH:{}", path);
    InputStream resourceAsStream = Address.class.getClassLoader().getResourceAsStream(path);
    StringBuilder sb = new StringBuilder();
    InputStreamReader isr = new InputStreamReader(Objects.requireNonNull(resourceAsStream));
    BufferedReader br = new BufferedReader(isr);
    String newLine;
    while ((newLine = br.readLine()) != null) {
    sb.append(newLine);
    }
    resourceAsStream.close();
    return sb.toString();
    }

    三、参考文章

    CSDN

    ]]>
    + + java + + + java + +
    + + 王慧文(美团):很少人知道自己在愚昧之巅 + /20200723-Few-people-know-that-they-are-ignorance/ + 零、写在前面

    早几天在领导 JG 的朋友圈看到这篇文章
    简单几个字的标题,让我产生了兴趣
    一来这个标题是挺吸引人,典型的自媒体文章
    二来JG 之前推过一些书很不错
    三来一般领导朋友圈都不会推垃圾信息

    全文看来其实感觉还是挺切合我的认知:

    1. 人很难认清自己,很难找到方向,成长路上有他人指点会很棒
    2. 除了遇到的人合读过的书,十年之后你还是你
    3. 多与他人交流,多帮助他人,利人利己
    4. 运动、学习、反馈、重塑大脑

    离开学校
    大部分时间都是围绕着工作
    有些人可能觉得当前的工作不满意
    然后想着学点什么高大上的东西为以后的工作做准备
    极端情况下这种选择是对的,大部分情况会让自己进退两难
    正确的选择应该是:过好当下,瞭望远方,持续学习,总结反思,慢慢进步
    架构师同事 SGLS 说过:做好当下的事情才是最重要的,不管现在是做什么项目,设计和实现是我们自己可以控制的。

    以下为原文

    审视是一个特别好的词。当一个人开始审视世界、审视经历、审视自己的时候,更容易做出有担当的选择。
    作为美团“二号首长”,王慧文选择在2020年底正式“退休”,这不可分说地成为了焦点——
    王慧文,是怎么成为“王慧文”的?

    一、“不自我设限”,造就无边界的王慧文

    什么是促使人成长的本质原因?
    王慧文一直在渴求突破,也一直在探究这个问题。

    1. 人要接受变化,有动荡才有进化

    王慧文修行的开端,就是校内网。
    2006年3月,王慧文、王兴和另外四名同伴绞尽脑汁、黑白颠倒,终于让校内网收获了一百多万用户。
    王兴开始四处找投资,但在多数投资人眼里看来,他们仍是群“生瓜蛋子”、“杂牌军”,校内网不过是几个大学生做的小打小闹的玩意儿。
    但在这时,千橡集团董事长陈一舟出现了,他愿意拿出一千多万人民币,收购校内网。《九败一胜:美团创始人王兴创业十年》中这样描述

    陈一舟说,如果你们不卖,我们可以拿这钱砸到市场上推广。当然,校内网的这群狂妄的小子被这句话激怒了,拒绝跟陈一舟谈。
    回绝了陈一舟后,王兴的融资依旧不顺利,陈一舟却与王兴他们保持接触,认准了校内网这条大鱼,准备放长线,持久诱惑。
    2006年9月,陈一舟再次抬高了收购校内网的价码,校内网召开了五人的股东会。
    王慧文觉得校内网的机会难得,做成的希望很大,而且是自己一手做起来的产品,感情已经很深,所以坚决反对卖掉。王兴态度中立,但倾向于不卖。赖斌强则考虑的方面更多,他支持卖掉,以免大家背着一身巨债创业。
    除了他们三个大股东,还有另两名小股东,杨俊和付栋平。他们加入较晚,股份也较少,觉得卖不卖都行,比起被收购,他们更担心王慧文和赖斌强由于意见不合而导致团队破裂。
    这次争论后不久,陈一舟就就又开出了更高的收购价格。几人意识到,如果继续争论不休,团队的裂痕势必更深。万般无奈之下,校内网不得不同意陈一舟的条件。
    那一晚,大家都不说话,一起去吃夜宵的时候,王兴、王慧文、赖斌强全喝醉了,所有人大哭了一场。

    不论校内网是成功的,还是失败的,在王慧文和王兴的心里,它永远都是遗憾的。
    回首往事,王慧文已经不再执着,反而觉得这段遗憾对自己的人生是件好事。他说:
    如果校内网做成了,我会变成让别人非常讨厌的人,刚愎、自负,因为挫折,所以我才懂得反省自己,能更理智客观冷静地看待自己。
    还有人问王慧文,如果当初不卖的话,校内网能不能做得成?王慧文答:
    这取决于我们遇到什么样的投资商,如果投资商能够在很多地方帮助我们,是可能做成的;如果投完钱就不怎么管,让我们自己随便搞,那就不行,因为我们还不够成熟。
    管理者的自我反思,会反馈在他的战略、领导力、管理、业务实操等能力上。
    人必须反思,是因为事物是时刻变化的,不断反思,才能不断跟上变化。
    王慧文在一次采访中说:美团的持续高速增长,不是我们在推动着业务发展,而是业务快速发展,我们努力地跟上,不是我们做了什么事情,只是我们跟上了,没掉队。
    在一个快速发展的行业里,在一个快速变化的时代里, 创业者、管理者如果没有自我反思,是很难生存下来的。
    在第一次创业修行中,王慧文就悟出一个道理:人,把事做成不易,不断成长、突破自己边界更难。

    2. 不设限的人生,成长机会无限

    “君子不器”是王慧文的人生原则之一。
    2019年,在极客公园创新大会上,极客公园创始人、总裁张鹏问王慧文:你觉得美团的基因是什么?
    王慧文当时就说出了“君子不器”四个字,他面对张鹏侃侃而谈:孔子说,君子不器,君子不是一个器皿,人不要搞自我定义,自我定义就是自我设限,我不倾向于用美团的基因来自我设限。
    他还用“基因突变”举例,基因变化变错的概率比变对的概率高几百万倍,所以大家都很恐惧基因变化,抵触变化。
    但王慧文认为变化不可怕,自我设限才最可怕,他还幽默地说:一旦变对了呢?一旦长出翅膀了呢?
    这种不自我设限,就是“美团无边界,专注而不专一”的根源。
    君子不器,有两个含义:
    一是人想要成长就不能有固化思维,事物是变化的,人也要具备动态发展的眼光,反思过去,展望未来;
    二是人不能像器皿那样自我设限,器皿只有某一方面的用途和才能,而人应该博学多识。
    孔子的这两点要求,王慧文都做到了,他的生活和工作就是如此。

    《财经天下》周刊记者朱晓培这样形容“百变的王慧文”

    在王慧文的朋友圈里,总能看见他忽然想到一个问题,比如:是不是20世纪在唯心主义和唯物主义领域都没有出现过大师?如果地球上没有美洲,哥伦布的船直接西行到了亚洲,今天的世界格局又会是什么样子?为什么射雕英雄传的五绝里只有反派欧阳锋没有广招门徒?

    正如王慧文在内部信中所说,他个人的兴趣“散乱不稳定”。
    当年创业时,王慧文和王兴都不会敲代码,但他们都觉得:没事,我们可以学。同样,后来在美团时,有一段时间王慧文要研究零售,他几乎每天都看一本关于零售的书,并且跟很多人谈论自己的心得。

    王慧文敬佩爱因斯坦,爱因斯坦曾说:科学和艺术一样,都让我们的世界更加绚丽多彩。
    爱因斯坦格外推崇西方古典音乐。这位“相对论”的创始人经常和“量子论”的创始人普朗克一起演奏贝多芬的作品,爱因斯坦拉小提琴,普朗克弹钢琴。相对论和量子论共同构成了21世纪物理学科的两大支柱,而它们的创始人不仅都对科学抱有偏执的热情,也都对艺术也怀有高涨的热爱,这真的很奇妙。

    王慧文从中体会到:爱因斯坦经常从艺术和审美的视角去探讨科学命题,人如果永远只站在一个视角来看待人生,人生就会变得特别狭隘,不够丰富,而站在不同视角来看待问题,不设限地长大,才非常重要。
    不自我定义、不自我设限、不抗拒变化、不沉溺过去的王慧文,与“无边界”的美团极为匹配。

    2012年底,美团实现盈亏平衡后,王慧文认为团购领域大局已定,又开始把目光聚焦在了外卖领域。
    2015年10月,早已在“千团大战”中站稳脚跟的美团,开始跟大众点评整合,从T型战略逐步转向餐饮、酒旅、综合三大业务架构。几次调整后,到店餐饮事业群、外卖配送事业群和餐饮生态平台被统一归进餐饮平台,总负责人仍然是王慧文。
    2017年12月,美团再次调整架构,王慧文担任大零售事业群总裁,统筹生鲜零售、外卖、配送、餐饮B2B等业务。同时,王慧文还负责出行事业部,正式和滴滴展开角逐。
    至此,美团点评已经构建了新到店事业群、大零售事业群、酒店旅游事业群和出行事业部四大业务体系,王慧文是其中两大事业群的统帅。

    如果管理者认为自己就是一个器皿,那么你就会认为自己的状态永远不会改变,也没有改变的必要。如果王慧文这样想,就不会为美团的基业立下赫赫战功。
    只有当你明白“君子不器”的道理时,才会意识到成长的必要性,也才会清醒地看到自己过去的弱点。
    不自我设限,多元思考人生,始终相信自己有更多机会,这样的管理者才能持续地自我突破,才能在激烈的竞争中拼杀出属于自己的战场。

    二、“练强兵,结硬寨,打呆仗”,

    造就能征善战的王慧文
    王慧文信奉曾国藩的九字箴言:练强兵,结硬寨,打呆仗。

    1. 有担当的管理者,麾下自有万千雄兵

    “练强兵”很容易理解,当时清朝腐败,太平天国武装起义,清朝的正规军竟无力抵抗,只好调动地方军事力量,多亏有曾国藩统率的湘军,才能击败太平军。此后,湘军成为了清朝军事上强有力的骨干。
    没有强兵,则无法作战。但强兵如何练成?
    曾国藩治理湘军,主要凭思想纪律,而不是战术技巧。
    王慧文“练强兵”,看重三点:人聪明,接地气,学习能力强(不是考试能力强)。
    其次,王慧文主张:管理者的一个重要责任,就是——担当。
    王慧文有一句话曾经火遍全网:
    有担当的管理者,要把下属从“愚昧之巅”推向“绝望之谷”,至于下属能否爬上“开悟之坡”,看个人造化。
    王慧文解释,这句话的核心词是“担当”。
    他向来不喜欢说“责任”,因为他认为:责任可以逃避,只有有担当的管理者才不会逃避责任。
    那么,什么是“愚昧之巅”、“绝望之谷”、“开悟之坡”?
    这些词语源于 达克效应 ,王慧文说:我思考我个人的成长和周围同事的成长,每个人都希望自己最后成为智者(大师),但是,我们要承认一个事实,大部分人没有走上去。

    愚昧之巅——人的智慧极低,但是自信度极高;
    绝望之谷——认识到自己的不足,智慧有所增长,自信度大幅降低;
    开悟之坡——智慧继续增长,自信度开始反弹增长;
    持续平稳高原——智慧达到顶级,自信心也持续平稳,走上大师之路。

    王慧文说:大部分人没能从愚昧之巅走到绝望之谷,大部分人都在这个部分遇到了困难。
    王慧文借用“达克效应”理论,不是为了拆穿别人的智慧不足,而是为了强调管理者的担当,他认为:

    大部分人都知道别人处在愚昧之巅,但是很少有人知道自己处在愚昧之巅,这就产生了非常大的信息不对称。
    为什么人不知道自己在愚昧之巅?就是因为每一个人在自己成长的过程中,没有得到有效反馈,没有人告诉他“你现在在愚昧之巅”。因此,有效反馈变得非常重要和稀缺。
    但是再进一步思考,为什么很多人没有得到这个反馈?因为别人没有这个责任反馈给你,而且戳破真相是有风险的。

    王慧文觉得他们在离开美团的环境后,很可能更没希望得到他人的有效反馈了,所以他有好几次,在同事或下属离职时,告诉了对方正处于愚昧之巅的真相,但是转眼就被拉黑了。
    尽管王慧文承认他们是一个好人、正直的人,也是一个想成长的、对美团有过贡献的人,将来也会成为对社会有贡献的人,王慧文希望对方成长得更好,才戳破真相,但是要戳破真相、给他人有效反馈,就必须承担得罪人的风险。

    也正因此,管理者才更需要担当。
    王慧文说:你给别人反馈的时候,也可能你自己是一个傻×,可能我自己是在愚昧之巅,其实人家是对的,这个可能性也是很大的,通过这个行为反而证明了你可能在愚昧之巅的事实。
    管理者能不能从善意出发,帮下属敲响警钟?又能不能在被误解时,甘愿承受来自下属的伤害?或者是在发现自己比下属更愚昧的时候,还能不能调整心态,通过努力学习来提升自己的智慧和自信?
    回答这些问题的最好方式,就是努力向一个有担当的管理者去逼近。
    担当,是管理者们最稀缺的能力,但却是“练强兵”必不可少的素质。

    2. 优秀管理者,都在暗下笨功夫

    “结硬寨,打呆仗”,是曾国藩的兵法内核。字面意思是,把军营扎得坚如磐石,打仗时不讲究技巧而是追求笨方法。
    曾国藩带兵打仗有个规矩,他到任何地方安营扎寨之后,不论当时是刮风还是下雨,首先命令士兵们挖掘战壕,壕深约两米,而且还要筑墙,墙高约八尺高,墙外再挖一道沟。这就确保整个营盘固若金汤。
    湘军总是要挖沟、筑墙,行军速度非常慢。由于这种打法显得特别笨,所以才叫“结硬寨,打呆仗”。
    王慧文的工作速度并不慢,但他也爱下笨功夫,一个细节一个细节得打磨,确保每个业务流程都能稳扎稳打。
    美团前员工这样评价王慧文:老王可能是我见过在互联网行业,大佬级别中所谓“比你聪明还更拼搏”,并且会关注很多业务细节的人。
    工作起来,王慧文就像变成了拼命三郎,让很多比他年轻的人都心生畏惧。
    据美团前员工回忆:每周六下午,是大团队的业务管理会,每个分支业务的负责人要详细地讲解业务进度,用数据说话,以结果为导向,绝对不让周报变成“流水账”;
    在这个过程中,王慧文会非常认真地听每个人的讲解,绝不走过场,绝不讲废话;
    某个员工在周报中提及“某个数据变化属于正常波动”,这是产品负责人没有特别认真思考或经验欠缺、全局感不强而惯用的一个理由,而此时王慧文则会一针见血地指出是因为什么问题、什么原因导致的数据变化,让人心服口服。
    王兴在内部信里这样评价王慧文:

    美团精神,老王身体力行、堪称典范。回顾老王过去九年多的工作,既有冲锋在前的勇猛,又有安营扎寨的稳健;既有舍我其谁的担当,又有功成不必在我的潇洒;既有“天下兴亡,匹夫有责”的责任感,又有“我们什么都没有,但是我们有兄弟和勇气”的真性情;既长期有耐心地保持战略定力,又坚持时不我待、只争朝夕地忘情投入。
    美团人经常加班到凌晨两三点,但转天早上九点又准时坐到办公室里开会,这让很多人对美团这家公司佩服不已。

    2013年6月,美团收购猛买网,猛买网创始人张智勇在加入美团之后,曾发微博说:牛x都是苦x堆出来的,你看美团网牛x,没看到的都是苦x。原来公司没有比它做得好,就是我不够苦x,是因为我管理不好。在这里,每月每周都能学到东西,蛮好的。辛苦没关系,能学到东西就行,最怕加班没结果。

    对王慧文来说,周六全天开会,周日半天高管会,每周留给自己的时间或许只有一个下午而已。但王慧文向外界和美团内部员工展现的,永远是一副精力充沛、充满激情的样子。
    这就像将军带兵打仗,“跟我上”和“给我上”,一字之差,但全军的战斗力却会有天壤之别。
    前者能把团队里每个人的心思都拧成一股绳,无往不胜;后者却永远苦于团队像一盘散沙。

    三、“不宜贪天之功,知止不殆”,

    造就未来不可限量的王慧文
    王慧文在今年年初的内部信里,把自己在美团拼搏的17年激情燃烧岁月,轻描淡写地说成了“我运气实在太好”。
    低调、收敛,不是王慧文今天才有的风格,而是他始终具备的性格底色。
    王慧文经常开玩笑地说,我是农民的孩子,只会拼命工作。玩笑的背后,是他专注做事的态度,以及不居功自傲的格局和胸襟。
    管理者要常常怀有“清零思维”,过去的错误要变成未来的前车之鉴,不能用来惩罚自己;过去的成就要变成未来的经验和警示,不能用来炫耀自己、骄傲自满。
    如果管理者觉得自己“满了”,也就意味着,你认为自己的成长之路到头了。
    清零思维也是一种包容心。《九败一胜:美团创始人王兴创业十年》写道:王慧文曾去过无锡,见到88米高的灵山大佛,从远处看大佛,跟山一样高,走近大佛,人还没有大佛的脚趾头高。
    王慧文心想,如果这尊大佛有脚臭的话,那么来观光的人就只能闻到大佛的脚臭味,而看不见整个大佛的巍巍如山。只有包容的人,心胸宽广,眼界开阔,才看得到大佛的宝相庄严。
    该书的作者李志刚说:也只有包容的CEO,才能听到下面的真实心声,兼听则明,才能有各种奇人异士来帮你做事。
    王慧文这17年来,已经为美团培养了大批人才,也为人才梯度建设付出了贡献,他离职后,自然也不需要人才来帮自己做事了,但是这种包容心会始终伴随他,让他的未来无法限量。
    王慧文也包容对手,他和王兴都不认为美团是在跟同行们“争”,美团是在“竞”,他说:我们不认为千团大战是我们打赢了别人,而是我们跟上了行业节奏,他们没有跟上,这个认知是跟打仗不一样的;同向为竞,相向为争。

    包容,就是不眷恋。

    王慧文不眷恋名利荣誉,他说“我运气实在太好,不宜继续贪天之功,知止不殆”;
    王慧文不眷恋自己的能力,他说“感谢我的家人,承受很多困难,无怨无尽地给我支持和包容”,还感谢了时代、王兴和所有同事;
    王慧文不眷恋他的江湖,他说“美团十年,豪兴不浅。他日江湖相逢,再当杯酒言欢”。

    金庸先生曾写:天上白云,散了又聚,聚了又散,人生离合,亦复如斯。

    今天,我们预测王慧文的人生“下半场”会有怎样的风景,已经是徒劳,因为他早已选择了一条“不确定”的人生旅程。
    王慧文说过,固定靶比移动靶更难打中。人生就像一个移动靶,我们谁也不知道自己的未来将穿梭至何处,也几乎不可能帮别人预测。
    我们只能希望,当自己修炼出清零思维、包容心、不眷恋的人生态度时,能够跟上时代变化莫测的脚步,不要掉队。
    不论人生的下半场将会如何,至少王慧文已经可以潇洒地说出一句——“这十年激烈精彩,不负年华”。

    四、参考

    美团王慧文:很少人知道自己在愚昧之巅

    ]]>
    + + 随说 + + + 王慧文 + +
    + + 初识 Spring AOP 与 BeanPostProcess + /20220326-Get-to-know-spring-AOP-and-BeanPostProcess/ + 零、背景

    最近在做重构的项目
    进入阶段性收尾阶段
    总结记录下相关的内容
    方便大家遇到类似问题可以想起有某个地方可以参考

    一、初识 AOP

    目前的 AOP 应用,由于公司生态体系不够完善
    利用 AOP + ThreadLocal(transmittable-thread-local,ttl)
    做一部分链路追踪的事情( 耗时打印,traceId 处理 )
    这部分倒是很简单,只是之前用的很少

    PS:链路追踪蛮重要,针对排查问题,性能监控等大有帮助,多关注开源协议/实现( 如 CNCF )

    二、初识 BeanPostProcess

    2.1 应用场景

    目前做的项目当中,有一个 IDC 负载均衡的工具
    有设置当前机器所属机房的方法,但是没有提供从配置文件读取的能力
    因为 IDC 路由不生效,会造成跨机房访问,导致访问延迟偏高( 回头再写性能优化相关内容 )

    当时发现这个问题的时候已经上线,以稳定性为主,不想升级 jar 版本
    于是乎当时想通过 AOP 或者字节码的方式去做一个增强

    当然,由于知识浅薄,AOP 写对了,但是不生效,工具初始化的先于 AOP 的作用时间。

    接下来就通过搜索引起各种找,偶然发现 BeanPostProcess (之前也通过一些 spring 回调做应用预热)

    于是就开始一番操作,果然给解决问题了。

    2.2 代码概览

    public class Idc implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof ClientFactory) {
    ClientFactory clientFactory = (ClientFactory) bean;
    String idc = System.getenv("IDC");
    if (StringUtils.isNotBlank(idc)) {
    clientFactory.setIdc(idc);
    }
    clientFactory.setIdc("nj");
    }
    return bean;
    }
    }

    2.2 BeanPostProcess 文档

    BeanPostProcess API

    三、总结

    之前一直在做业务开发
    也很少做从零到一的项目
    即使看过一些 spring 的书籍和文档
    但是还是没有理解太深,没有太注意
    果然是书到用时方恨少哇,以后还是万事深挖一步

    spring 生命周期,启动流程,各种回调,有必要看看,跑 demo 试试。

    四、参考文档

    ]]>
    + + java + + + java + spring + +
    + + Git Bash tree(windows)让windows支持tree + /20170713-Git%20Bash%20tree%EF%BC%88windows%EF%BC%89/ + 在安装了git客户端之后,发现Git Bash挺好用

    之前在linux shell用过tree命令感觉不错

    发现Git Bash也可以实现,于是就记录一下

    下载地址:点击前往

    下载文件: Binaries Zip

    解压文件:bin目录下找到tree.exe

    把这个放到git安装目录下后的路径:C:\Program Files\Git\usr\bin\tree.exe

    测试:虽然有点丑。。。

    hisen@HiSEN MINGW64 /c/code/SpringBootCLI/myapp
    $ tree
    .
    |-- mvnw
    |-- mvnw.cmd
    |-- pom.xml
    `-- src
    |-- main
    | |-- java
    | | `-- com
    | | `-- example
    | | `-- myapp
    | | |-- DemoApplication.java
    | | `-- ServletInitializer.java
    | `-- resources
    | |-- application.properties
    | |-- static
    | `-- templates
    `-- test
    `-- java
    `-- com
    `-- example
    `-- myapp
    `-- DemoApplicationTests.java

    14 directories, 7 files

    ]]>
    + + linux + + + linux + +
    + + Debian丨Ubuntu丨shadowsocks-libev一键安装脚本 + /20170904-Debian%E4%B8%A8Ubuntu%E4%B8%A8shadowsocks-libev%E4%B8%80%E9%94%AE%E5%AE%89%E8%A3%85%E8%84%9A%E6%9C%AC/ + 执行命令:(装完就开机启动)

    wget --no-check-certificate -O shadowsocks-libev-debian.sh https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks-libev-debian.sh
    chmod +x shadowsocks-libev-debian.sh
    ./shadowsocks-libev-debian.sh 2>&1 | tee shadowsocks-libev-debian.log

    默认设置:

    服务器端口:自己设定(如不设定,默认为 8989)
    密码:自己设定(如不设定,默认为 teddysun.com)
    加密方式:自己设定(如不设定,默认为 aes-256-gcm)

    管理命令:

    启动:/etc/init.d/shadowsocks start
    停止:/etc/init.d/shadowsocks stop
    重启:/etc/init.d/shadowsocks restart
    查看状态:/etc/init.d/shadowsocks status

    修改配置文件(端口、密码)

    vi /etc/shadowsocks-libe/config.json
    {
    "server":"0.0.0.0",
    "server_port":9090,
    "local_address":"127.0.0.1",
    "local_port":1080,
    "password":"password",
    "timeout":600,
    "method":"aes-256-gcm"
    }

    卸载命令

    ./shadowsocks-libev-debian.sh uninstall

    ]]>
    + + 工具 + + + network + +
    + + Google java工具类 - Guava - 常见用法 + /20170801-Google%20java%E5%B7%A5%E5%85%B7%E7%B1%BB%20-%20Guava%20-%20%E5%B8%B8%E8%A7%81%E7%94%A8%E6%B3%95/ + 概述

    工具类 就是封装平常用的方法,不需要你重复造轮子,节省开发人员时间,提高工作效率。谷歌作为大公司,当然会从日常的工作中提取中很多高效率的方法出来。所以就诞生了Guava。

    高效设计良好的API,被Google的开发者设计,实现和使用

    遵循高效的java语法实践

    使代码更刻度,简洁,简单

    节约时间,资源,提高生产力 Guava工程

    包含了若干被Google的 Java项目广泛依赖 的核心库,例如:

    1. 集合 [collections]
    2. 缓存 [caching]
    3. 原生类型支持 [primitives support]
    4. 并发库 [concurrency libraries]
    5. 通用注解 [common annotations]
    6. 字符串处理 [string processing]
    7. I/O 等等。

    部分用法如下:

    public class CommonUsage {

    /**
    * 2.集合转换为特定格式的字符串
    */
    @Test
    public void collection2Srt() {
    //List转
    List<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");
    list.add("c");
    list.add("d");
    list.add("e");
    list.add("f");
    String result = Joiner.on("-").join(list);
    System.out.println(result);//a-b-c-d-e-f

    //Map 转
    HashMap<String, Integer> map = Maps.newHashMap();
    map.put("a", 1);
    map.put("b", 2);
    map.put("c", 3);
    //on 分隔符,withKeyValueSeparator key value之间的分隔符
    String s = Joiner.on(",").withKeyValueSeparator("=").join(map);
    System.out.println(s);//a=1,b=2,c=3
    }

    /**
    * 3.将String 转为特定的集合
    */
    @Test
    public void str2Collection() {
    String s = "1,2,3,4,5,6,7,8,9";
    List<String> list = Splitter.on(",").splitToList(s);
    System.out.println(list);

    s = "1-2-3-4- 5- 6 ";
    //去除空串与空格
    List<String> list1 = Splitter.on("-").omitEmptyStrings().trimResults().splitToList(s);
    System.out.println(list1);

    //将String转换为map
    String str = "a=1,b=2";
    Map<String, String> map = Splitter.on(",").withKeyValueSeparator("=").split(str);
    }

    /**
    * 4.多个字符切割,或者特定的正则分隔
    */
    @Test
    public void strSplit() {
    String input = "aa.dd,,ff,,.";
    List<String> list = Splitter.onPattern("[.|,]").omitEmptyStrings().trimResults()
    .splitToList(input);
    System.out.println(list);
    // 判断匹配结果
    boolean result = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z'))
    .matches('K');//true
    // 保留数字文本
    String s1 = CharMatcher.digit().retainFrom("abc 123 efg");//123
    // 删除数字文本
    String s2 = CharMatcher.digit().removeFrom("abc 123 efg");//abc efg
    }
    /**
    * 9.计算程序开始结束用了多少时间
    */
    @Test
    public void countTime() {
    Stopwatch stopwatch = Stopwatch.createStarted();
    for (int i = 0; i < 100000; i++) {
    }
    //TimeUnit可以指定时间精度
    long nanos = stopwatch.elapsed(TimeUnit.MILLISECONDS);
    System.out.println(nanos);
    }
    /**
    * 10.Files 文件操作
    * 方便,
    */
    @Test
    public void filesOperation() {
    String fileName = "./src/main/java/com/hisen/jars/guava/test.txt";
    for (int i = 0; i < 1000; i++) {
    //写
    filesWrite(fileName, String.valueOf(i) + "\n");
    }
    //读
    List<String> list = filesRead(fileName);
    System.out.println("读取到的内容:" + list.toString());
    // Files.copy(sourceFile, targetFile); //复制文件
    // Files.equal(File,File);//比较文件内容是否完全一致
    // touch方法创建或者更新文件的时间戳。
    // createTempDir()方法创建临时目录
    // Files.createParentDirs(File) 创建父级目录
    // getChecksum(File)获得文件的checksum
    // hash(File)获得文件的hashmap系列方法获得文件的内存映射
    // getFileExtension(String)获得文件的扩展名
    // getNameWithoutExtension(String file)获得不带扩展名的文件名
    }

    /**
    * Files写文件
    *
    * @param fileName 文件名(包括路径)
    * @param contents 写入的单行内容
    */
    public void filesWrite(final String fileName, final String contents) {
    checkNotNull(fileName, "文件名称不能为空");
    checkNotNull(contents, "写入内容不能为空");
    final File file = new File(fileName);
    try {
    //Files.write(contents.getBytes(), file);//覆盖
    Files.append(contents, file, Charsets.UTF_8);//追加
    } catch (IOException e) {
    System.out.println("ERROR trying to write to file '" + fileName + "' - "
    + e.toString());
    }
    }

    /**
    * Files 文件单行读取
    *
    * @param filePath 文件名(包括路径)
    */
    public List<String> filesRead(String filePath) {
    checkNotNull(filePath, "文件名称不能为空");
    File file = new File(filePath);
    List<String> list = null;
    try {
    list = Files.readLines(file, Charsets.UTF_8);
    } catch (IOException e) {
    e.printStackTrace();
    }
    return list;
    }
    }

    部分摘自:点击查看

    ]]>
    + + java + + + java + google + jar + +
    + + HashMap和Hashtable的区别 + /20170217-HashMap%E5%92%8CHashtable%E7%9A%84%E5%8C%BA%E5%88%AB/ + 在Java 2以前,一般使用Hashtable来映射键值和元素。为了使用Java集合框架,Java对Hashtable进行了重新设计,但是,为了向后兼容保留了所有的方法。Hashtable实现了Map接口,除了Hashtable具有同步功能之外,它与HashMap的用法是一样的。·在使用时一般是用ArrayList代替Vector,LinkedList代替Stack,HashMap代替HashTable,即使在多线程中需要同步,也是用同步包装类。

    另外在使用上还有一些小的差异,比如:

    1. HashTable的key和value都不允许为null值,而HashMap的key和value则都是允许null值的。这个其实没有好坏之分,只是Sun为了统一Collection的操作特性而改进的。
    2. HashTable有一个contains(Object value)方法,功能上与containsValue(Object value)一样,但是在实现上花销更大,现在已不推荐使用。而HashMap只有containsValue(Object value)方法。
    3. HashTable使用Enumeration,HashMap使用Iterator。Iterator其实与Enmeration功能上很相似,只是多了删除的功能。用Iterator不过是在名字上变得更为贴切一些。模式的另外一个很重要的功用,就是能够形成一种交流的语言(或者说文化)。有时候,你说Enumeration大家都不明白,说Iterator就都明白了。

    在实现上两者已有一些差异,这里简单说明一下:

    public Hashtable(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal Load: "+loadFactor);
    if (initialCapacity==0)
    initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry[initialCapacity];
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    initHashSeedAsNeeded(initialCapacity);
    }
    public Hashtable(int initialCapacity) {
    this(initialCapacity, 0.75f);
    }
    public Hashtable() {
    this(11, 0.75f);
    }


    public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal initial capacity: " +
    initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
    initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal load factor: " +
    loadFactor);
    this.loadFactor = loadFactor;
    threshold = initialCapacity;
    init();
    }
    public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    HashTable中构造hash数组时initialCapacity默认大小是11,增加的方式是 old*2+1。HashMap中构造hash数组时initialCapacity默认大小是16,而且一定是2的指数。对于哈希值的使用也有所不同,HashTable直接使用对象的hashCode,代码是这样的:

    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;

    而HashMap重新计算hash值,而且用与代替求模:

    int hash = hash(k);
    int i = indexFor(hash, table.length);

    static int hash(Object x) {
      int h = x.hashCode();

      h += ~(h << 9);
      h ^= (h >>> 14);
      h += (h << 4);
      h ^= (h >>> 10);
      return h;
    }
    static int indexFor(int h, int length) {
      return h & (length-1);
    }

    仅供参考,内容来源于互联网

    ]]>
    + + java + + + HashMap + Hashtable + +
    + + Guice Demo | solve NoSuchMethodError + /20190424-Guice%20Demo%20%7C%20solve%20NoSuchMethodError/ + 一、Guice简介

    Google公司的Bob lee开发的轻量级IoC容器,其特点是:

    1. 速度快,号称是spring的100倍速度
    2. 无配置文件,实用JDK5.0的annotation描述组件依赖,简单,而且有编译器检查和重构支持
    3. 简单,代码量很少

    二、简单样例

    1. 详细代码:https://github.com/hisenyuan/IDEAPractice/tree/master/src/main/java/com/hisen/jars/guice
    2. 依赖

      <dependency>
      <groupId>com.google.inject</groupId>
      <artifactId>guice</artifactId>
      <version>4.2.2</version>
      </dependency>
    3. 测试类

      public class HelloApp extends BaseServer {
      @Inject
      private HelloServiceImpl hello;

      @Test
      public void testSayHello() {
      // 方式一
      Injector injector = Guice.createInjector();
      HelloService helloService = injector.getInstance(HelloService.class);
      helloService.sayHello("hisen");

      // 方式二 其实是在BaseServer中做了方式1的事情 【类似Spring的方式】
      hello.sayHello("1");
      }
      }

    三、解决问题

    java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V

    github有人遇到同样的问题:https://github.com/SeleniumHQ/selenium/issues/3880

    把本地的guava版本由19.0改为21.0成功解决问题

    ]]>
    + + java + + + java + +
    + + Hexo next主题添加本地搜索 - 不使用第三方服务 + /20170407-Hexo%20next%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0%E6%9C%AC%E5%9C%B0%E6%90%9C%E7%B4%A2%20-%20%E4%B8%8D%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E6%9C%8D%E5%8A%A1/ + 之前安装过第三方的搜索服务,贼蛋疼。都不免费了。

    也有自己安装插件,然后写js的,麻烦

    后来找到两个插件,安装之后就搞定了

    感谢开发的作者!!!

    安装插件

    记得要在站点根目录执行下面的安装操作

    1.安装 hexo-generator-search

    npm install hexo-generator-searchdb --save

    2.安装 hexo-generator-searchdb

    npm install hexo-generator-searchdb --save

    启用搜索

    编辑站点文件_config.yml,添加以下内容开启搜索

    search:
    path: search.xml
    field: post
    format: html
    limit: 10000

    编辑主题文件_config.yml,启用本地搜索功能:

    # Local search
    +local_search:
    +  enable: true
    +

    效果预览

    小缺点

    第一次点击搜索的时候反应会比较慢

    因为是要加载一个xml文件

    ]]>
    + + hexo + + + hexo + +
    + + Hexo Landscape主题JS优化,不使用谷歌CDN + /20170208-Hexo-Landscape%E4%B8%BB%E9%A2%98JS%E4%BC%98%E5%8C%96%EF%BC%8C%E4%B8%8D%E4%BD%BF%E7%94%A8%E8%B0%B7%E6%AD%8CCDN/ + jQuery库的优化

    修改这个文件:

    themes/landscape/layout/_partial/after-footer.ejs

    将17行左右的

    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>

    改为

    <script src="http://apps.bdimg.com/libs/jquery/2.0.3/jquery.min.js"></script>

    重新生成博客之后就把谷歌的cdn替换成百度的cdn了

    ]]>
    + + hexo + +
    + + FindLucklyGirl - 找出值班的幸运儿 + /20180720-FindLucklyGirl%20-%20%E6%89%BE%E5%87%BA%E5%80%BC%E7%8F%AD%E7%9A%84%E5%B9%B8%E8%BF%90%E5%84%BF/ + 一、背景简介

    今天头给我们开会,说到团队对外沟通的问题。

    谈到对外需要积极给人解决问题,而不是各种推脱,即使自己不知道,也可以给个眼神找到对的人。

    继而谈到需要安排人轮流负责跟外部接洽

    由于这个活呢,大伙儿认为不是什么好差事,那就抓阄决定吧

    于是乎就感觉可以写一个简单的排班系统小bug,不过我这里只是提供一个简单的思路

    二、程序代码

    主要的逻辑在这,当然并没有考虑数据持久化的问题

    性能等其他的问题,纯粹是一个思路,用hashCode取模主要是打的比较散,很均匀

    加上日期什么的,就一个排班表出来了。

    /**
    *
    * @param person 目前所有的人
    * @param lucklys 这一轮已经值班了的人
    */

    private static void findLucklyOneByHashCode(List<String> person, ArrayList<String> lucklys) {
    List<String> result = new ArrayList<>(person);
    result.removeAll(lucklys);
    if (result.size() == 0) {
    lucklys.clear();
    result = new ArrayList<>(person);
    }
    String code = System.nanoTime() + UUID.randomUUID().toString();
    int index = Math.abs(code.hashCode()) % result.size();
    String lucklyOne = result.get(index);
    System.out.println("lucklyOne:" + lucklyOne);
    lucklys.add(lucklyOne);
    }
    /**
    * hashCode随机取出来的数据
    * lucklyOne:G
    * lucklyOne:B
    * lucklyOne:C
    * lucklyOne:D
    * lucklyOne:H
    * lucklyOne:F
    * lucklyOne:E
    * lucklyOne:A
    */

    ]]>
    + + java + + + java + +
    + + Hexo 绑定个人域名简单方法 + /20170209-Hexo-%E7%BB%91%E5%AE%9A%E4%B8%AA%E4%BA%BA%E5%9F%9F%E5%90%8D%E7%AE%80%E5%8D%95%E6%96%B9%E6%B3%95/ + 1.直接注册个域名(随便在哪里)
    2.添加域名解析

    记录类型主机记录记录值
    CNAME@hisen-yuan.github.io

    主机记录:@ 代表顶级域名,例如hisen.me 如果想要www.hisen.me把@改成www

    记录值:你的博客原始地址

    3.在\blog\source下添加CNAME文件,没有后缀名,内容为你的域名,注意不要带http://

    我的域名解析为 hisen.me 文件内容为 hisen.me

    等待解析生效即可!

    ]]>
    + + hexo + +
    + + Hxeo百度站长工具抓取失败的原因 + /20170216-Hxeot%E7%99%BE%E5%BA%A6%E7%AB%99%E9%95%BF%E5%B7%A5%E5%85%B7%E6%8A%93%E5%8F%96%E5%A4%B1%E8%B4%A5%E7%9A%84%E5%8E%9F%E5%9B%A0/ + 近期在折腾下hexo博客

    发现百度搜索网址都搜索不到自己的站点

    我只把hexo传到github上,然而去百度站长工具提交网址几条后也没有反应

    我就测试了一下百度站长工具 - 抓取诊断

    结果是抓取失败:Github把百度的爬虫给干掉了!所以。。。


    具体内容如下:

    提交网址:	http://hisen.me/20170214-maven%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/
    抓取网址: http://hisen.me/20170214-maven%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/
    抓取UA: Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
    抓取时间: 2017-02-16 09:48:55
    网站IP: 151.***.***.133 已反馈,预计几分钟内完成更新
    下载时长: 0.331秒
    抓取异常信息: 拒绝访问 查看帮助

    返回HTTP头:

    HTTP/1.1 403 Forbidden
    Cache-Control: no-cache
    Content-Type: text/html
    Transfer-Encoding: chunked
    Accept-Ranges: bytes
    Date: Thu, 16 Feb 2017 01:48:55 GMT
    Via: 1.1 varnish
    Connection: close
    X-Served-By: cache-itm7421-ITM
    X-Cache: MISS
    X-Cache-Hits: 0
    X-Timer: S1487209735.687949,VS0,VE187
    Vary: Accept-Encoding
    X-Fastly-Request-ID: 3e528056d3333113dfb5da5c92a2421fa71f3705

    ]]>
    + + 软件 + + + hexo + +
    + + Hexo提速优化 - 压缩html、css、js by hexo-neat插件 + /20170211-Hexo%E6%8F%90%E9%80%9F%E4%BC%98%E5%8C%96-%E5%8E%8B%E7%BC%A9html%E3%80%81css%E3%80%81js-by-hexo-neat%E6%8F%92%E4%BB%B6/ + hexo博客生成的HTML代码留有大量的空白

    通过搜索发现有不错的方法可以解决这个问题

    那就是安装一个插件!名字叫:hexo-neat

    安装

    命令行进入博客根目录,执行以下命令

    npm install hexo-neat --save

    如果使用的是淘宝的cnmp执行以下命令

    cnpm install hexo-neat --save

    配置

    打开站点配置文件_config.yml,添加以下内容:

    #hexo-neat 优化提速插件
    neat_enable: true

    neat_html:
    enable: true
    exclude:

    neat_css:
    enable: true
    exclude:
    - '*.min.css'

    neat_js:
    enable: true
    mangle: true
    output:
    compress:
    exclude:
    - '*.min.js'

    安装好之后,就直接正常使用(跟以前没有装的时候一样使用),

    重新生成博客就可以发现html源码已经压缩了

    只是在生成博客的过程中可能要浪费一丁点时间

    访问速度有所提升!

    参考:点击查看

    ]]>
    + + 软件 + + + hexo + +
    + + IDEA maven项目通过mongo-java-driver折腾一下MongoDB + /20170220-IDEA-maven%E9%A1%B9%E7%9B%AE%E9%80%9A%E8%BF%87mongo-java-driver%E6%8A%98%E8%85%BE%E4%B8%80%E4%B8%8BMongoDB/ + 这几天折腾ubuntu然后安装了下mongodb

    通过Oracle VM VirtualBox端口转发,连接了虚拟机的MongoDB

    1.用idea创建一个maven项目

    2.在pom.xml中添加mongodb java驱动

    <dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>3.2.2</version>
    </dependency>

    3.参考官方:MongoDB Driver Quick Tour

    本用例github地址:mongodbTest

    贴下代码:

    package com.hisen.jdbc;

    import com.mongodb.Block;
    import com.mongodb.MongoClient;
    import com.mongodb.client.MongoCollection;
    import com.mongodb.client.MongoCursor;
    import com.mongodb.client.MongoDatabase;
    import com.mongodb.client.result.DeleteResult;
    import com.mongodb.client.result.UpdateResult;
    import org.bson.Document;

    import java.util.ArrayList;
    import java.util.List;

    import static com.mongodb.client.model.Accumulators.sum;
    import static com.mongodb.client.model.Aggregates.group;
    import static com.mongodb.client.model.Filters.*;
    import static com.mongodb.client.model.Projections.excludeId;
    import static com.mongodb.client.model.Sorts.descending;
    import static com.mongodb.client.model.Updates.inc;
    import static com.mongodb.client.model.Updates.set;
    import static java.util.Collections.singletonList;

    /**
    * Created by hisenyuan on 2017/2/20 at 13:42.
    */
    public class QuickTour {
    public static void main(String[] args) {
    MongoClient mongoClient = new MongoClient("localhost", 27017);
    MongoDatabase database = mongoClient.getDatabase("mydb");
    //gets the collection test
    MongoCollection<Document> collection = database.getCollection("test");
    //插入数据
    //insert(collection);
    //插入大量数据
    insertMany(collection);
    //统计条数
    count(collection);
    //打印查询到的第一条数据
    print(collection);
    //打印所有数据
    findAll(collection);
    //按条件查询一条数据
    findOneWithFilter(collection);
    //按条件查询一个Set集合
    getSet(collection);
    //排序,输出第一条
    sortDocuments(collection);
    //获取指定的field
    projectingFields(collection);
    aggregations(collection);
    //更新
    update(collection);
    //删除数据
    delete(collection);
    }

    /**
    * 插入单个文档
    * @param collection
    */
    public static void insert(MongoCollection<Document> collection) {
    //create document
    Document doc = new Document("name", "MongoDB")
    .append("type", "database")
    .append("count", 1)
    .append("info", new Document("x", 203).append("y", 102));
    //insert the document into the collection
    collection.insertOne(doc);
    }

    /**
    * 一次性插入多条数据,用ArrayList拼装
    * @param collection
    */
    public static void insertMany(MongoCollection<Document> collection) {
    //Create the documents in a loop
    List<Document> documents = new ArrayList<Document>();
    for (int i = 0; i < 100; i++) {
    documents.add(new Document("i", i));
    }
    collection.insertMany(documents);
    }

    /**
    * 统计集合中的总条数
    * @param collection
    */
    public static void count(MongoCollection<Document> collection) {
    //Count Documents in A Collection
    System.out.println("总条数:" + collection.count());
    }

    /**
    * 打印查询到的第一条数据
    * @param collection
    */
    public static void print(MongoCollection<Document> collection) {
    //prints the first document found in the collection
    Document myDoc = collection.find().first();
    System.out.println(myDoc.toJson());
    }

    /**
    * 查询集合中所有的数据
    * 不推荐使用foreach循环
    * @param collection
    */
    public static void findAll(MongoCollection<Document> collection) {
    System.out.println("----------------输出所有数据----------------");
    MongoCursor<Document> cursor = collection.find().iterator();
    try {
    while (cursor.hasNext()) {
    System.out.println(cursor.next().toJson());
    }
    } finally {
    cursor.close();
    }
    }

    /**
    * 条件查询单条数据
    * @param collection
    */
    public static void findOneWithFilter(MongoCollection<Document> collection) {
    //Get A Single Document with a Query Filter
    Document myDoc = collection.find(eq("i", 71)).first();
    System.out.println(myDoc.toJson());
    }

    /**
    * 获得一个Set集合的数据
    * @param collection
    */
    public static void getSet(MongoCollection<Document> collection) {
    // now use a range query to get a larger subset
    Block<Document> printBlock = new Block<Document>() {
    public void apply(final Document document) {
    System.out.println(document.toJson());
    }
    };
    //i的值大于98的数据
    System.out.println("----------------i大于98的数据----------------");
    collection.find(gt("i", 98)).forEach(printBlock);
    //50 - 51 gt:大于 lte:小于等于
    System.out.println("----------------50<i<=51的数据----------------");
    collection.find(and(gt("i", 50), lte("i", 51))).forEach(printBlock);
    }

    /**
    * 排序
    * @param collection
    */
    public static void sortDocuments(MongoCollection<Document> collection) {
    //根据i的值进行排序
    Document myDoc = collection.find(exists("i")).sort(descending("i")).first();
    System.out.println("----------------排序,输出第一条----------------");
    System.out.println(myDoc.toJson());
    }

    /**
    * 获取想要的field(字段)
    * @param collection
    */
    public static void projectingFields(MongoCollection<Document> collection) {
    System.out.println("----------------获取指定field,输出第一条----------------");
    //排除ID字段
    Document myDoc = collection.find().projection(excludeId()).first();
    System.out.println(myDoc.toJson());
    }

    public static void aggregations(MongoCollection<Document> collection) {
    /*
    collection.aggregate(asList(
    match(gt("i", 0)),
    project(Document.parse("{ITimes10: {$multiply: ['$i', 10]}}")))
    ).forEach(printBlock);
    */
    //求和
    Document myDoc = collection.aggregate(singletonList(group(null, sum("total", "$i")))).first();
    System.out.println(myDoc.toJson());
    }

    /**
    * 更新document的方法
    * @param collection
    */
    public static void update(MongoCollection<Document> collection) {
    //更新一个,如果不引入下面这个包,set方法会报错
    //import static com.mongodb.client.model.Updates.*;
    collection.updateOne(eq("i", 10), set("i", 110));
    //更新多个,小于100的都加100
    UpdateResult updateResult = collection.updateMany(lt("i", 190), inc("i", 100));
    System.out.println("----------------更新条数----------------");
    System.out.println(updateResult.getModifiedCount());
    }

    /**
    * 删除数据的方法
    * @param collection
    */
    public static void delete(MongoCollection<Document> collection) {
    collection.deleteOne(eq("i", 210));
    //gte 大于等于100的都删除
    DeleteResult deleteResult = collection.deleteMany(gte("i", 100));
    System.out.println("----------------删除条数----------------");
    System.out.println(deleteResult.getDeletedCount());
    }
    /*
    public static void bulk(MongoCollection<Document> collection) {
    // 2. Ordered bulk operation - order is guarenteed
    collection.bulkWrite(
    Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
    new InsertOneModel<>(new Document("_id", 5)),
    new InsertOneModel<>(new Document("_id", 6)),
    new UpdateOneModel<>(new Document("_id", 1),
    new Document("$set", new Document("x", 2))),
    new DeleteOneModel<>(new Document("_id", 2)),
    new ReplaceOneModel<>(new Document("_id", 3),
    new Document("_id", 3).append("x", 4))));
    // 2. Unordered bulk operation - no guarantee of order of operation
    collection.bulkWrite(
    Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
    new InsertOneModel<>(new Document("_id", 5)),
    new InsertOneModel<>(new Document("_id", 6)),
    new UpdateOneModel<>(new Document("_id", 1),
    new Document("$set", new Document("x", 2))),
    new DeleteOneModel<>(new Document("_id", 2)),
    new ReplaceOneModel<>(new Document("_id", 3),
    new Document("_id", 3).append("x", 4))),
    new BulkWriteOptions().ordered(false));
    }
    */
    }

    ]]>
    + + java + + + java + mongo + +
    + + 全球国家和地区列表,中英文国名、国际代码、电话区号(SQL & 文本) + /20191120-Global-country-and-region-information-list/ + 零、说明

    包含全球200+国家和地区的信息
    本列表属于之前有外国人整理过
    利用有道翻译API翻译了国家名
    涉及到全球的业务应该会用的上

    一、SQL

    sql如下:

    SET FOREIGN_KEY_CHECKS=0;
    -- ----------------------------
    -- Table structure for `sms_country`
    -- ----------------------------
    DROP TABLE IF EXISTS `sms_country`;
    CREATE TABLE `sms_country` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `iso` char(2) NOT NULL,
    `iso3` char(3) DEFAULT NULL,
    `name` varchar(80) NOT NULL,
    `name_zh` varchar(80) DEFAULT NULL,
    `nicename` varchar(80) NOT NULL,
    `numcode` smallint(6) DEFAULT NULL,
    `phonecode` int(5) NOT NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=240 DEFAULT CHARSET=utf8;

    -- ----------------------------
    -- Records of sms_country
    -- ----------------------------
    INSERT INTO `sms_country` VALUES ('1', 'AF', 'AFG', 'AFGHANISTAN', '阿富汗', 'Afghanistan', '4', '93');
    INSERT INTO `sms_country` VALUES ('2', 'AL', 'ALB', 'ALBANIA', '阿尔巴尼亚', 'Albania', '8', '355');
    INSERT INTO `sms_country` VALUES ('3', 'DZ', 'DZA', 'ALGERIA', '阿尔及利亚', 'Algeria', '12', '213');
    INSERT INTO `sms_country` VALUES ('4', 'AS', 'ASM', 'AMERICAN SAMOA', '美属萨摩亚', 'American Samoa', '16', '1684');
    INSERT INTO `sms_country` VALUES ('5', 'AD', 'AND', 'ANDORRA', '安道尔', 'Andorra', '20', '376');
    INSERT INTO `sms_country` VALUES ('6', 'AO', 'AGO', 'ANGOLA', '安哥拉', 'Angola', '24', '244');
    INSERT INTO `sms_country` VALUES ('7', 'AI', 'AIA', 'ANGUILLA', '安圭拉岛', 'Anguilla', '660', '1264');
    INSERT INTO `sms_country` VALUES ('8', 'AQ', null, 'ANTARCTICA', '南极洲', 'Antarctica', null, '0');
    INSERT INTO `sms_country` VALUES ('9', 'AG', 'ATG', 'ANTIGUA AND BARBUDA', '安提瓜岛和巴布达', 'Antigua and Barbuda', '28', '1268');
    INSERT INTO `sms_country` VALUES ('10', 'AR', 'ARG', 'ARGENTINA', '阿根廷', 'Argentina', '32', '54');
    INSERT INTO `sms_country` VALUES ('11', 'AM', 'ARM', 'ARMENIA', '亚美尼亚', 'Armenia', '51', '374');
    INSERT INTO `sms_country` VALUES ('12', 'AW', 'ABW', 'ARUBA', '阿鲁巴岛', 'Aruba', '533', '297');
    INSERT INTO `sms_country` VALUES ('13', 'AU', 'AUS', 'AUSTRALIA', '澳大利亚', 'Australia', '36', '61');
    INSERT INTO `sms_country` VALUES ('14', 'AT', 'AUT', 'AUSTRIA', '奥地利', 'Austria', '40', '43');
    INSERT INTO `sms_country` VALUES ('15', 'AZ', 'AZE', 'AZERBAIJAN', '阿塞拜疆', 'Azerbaijan', '31', '994');
    INSERT INTO `sms_country` VALUES ('16', 'BS', 'BHS', 'BAHAMAS', '巴哈马群岛', 'Bahamas', '44', '1242');
    INSERT INTO `sms_country` VALUES ('17', 'BH', 'BHR', 'BAHRAIN', '巴林', 'Bahrain', '48', '973');
    INSERT INTO `sms_country` VALUES ('18', 'BD', 'BGD', 'BANGLADESH', '孟加拉国', 'Bangladesh', '50', '880');
    INSERT INTO `sms_country` VALUES ('19', 'BB', 'BRB', 'BARBADOS', '巴巴多斯', 'Barbados', '52', '1246');
    INSERT INTO `sms_country` VALUES ('20', 'BY', 'BLR', 'BELARUS', '白俄罗斯', 'Belarus', '112', '375');
    INSERT INTO `sms_country` VALUES ('21', 'BE', 'BEL', 'BELGIUM', '比利时', 'Belgium', '56', '32');
    INSERT INTO `sms_country` VALUES ('22', 'BZ', 'BLZ', 'BELIZE', '伯利兹', 'Belize', '84', '501');
    INSERT INTO `sms_country` VALUES ('23', 'BJ', 'BEN', 'BENIN', '贝宁', 'Benin', '204', '229');
    INSERT INTO `sms_country` VALUES ('24', 'BM', 'BMU', 'BERMUDA', '百慕大', 'Bermuda', '60', '1441');
    INSERT INTO `sms_country` VALUES ('25', 'BT', 'BTN', 'BHUTAN', '不丹', 'Bhutan', '64', '975');
    INSERT INTO `sms_country` VALUES ('26', 'BO', 'BOL', 'BOLIVIA', '玻利维亚', 'Bolivia', '68', '591');
    INSERT INTO `sms_country` VALUES ('27', 'BA', 'BIH', 'BOSNIA AND HERZEGOVINA', '波斯尼亚和黑塞哥维那', 'Bosnia and Herzegovina', '70', '387');
    INSERT INTO `sms_country` VALUES ('28', 'BW', 'BWA', 'BOTSWANA', '博茨瓦纳', 'Botswana', '72', '267');
    INSERT INTO `sms_country` VALUES ('29', 'BV', null, 'BOUVET ISLAND', '布维岛', 'Bouvet Island', null, '0');
    INSERT INTO `sms_country` VALUES ('30', 'BR', 'BRA', 'BRAZIL', '巴西', 'Brazil', '76', '55');
    INSERT INTO `sms_country` VALUES ('31', 'IO', null, 'BRITISH INDIAN OCEAN TERRITORY', '英属印度洋领地', 'British Indian Ocean Territory', null, '246');
    INSERT INTO `sms_country` VALUES ('32', 'BN', 'BRN', 'BRUNEI DARUSSALAM', '文莱达鲁萨兰国', 'Brunei Darussalam', '96', '673');
    INSERT INTO `sms_country` VALUES ('33', 'BG', 'BGR', 'BULGARIA', '保加利亚', 'Bulgaria', '100', '359');
    INSERT INTO `sms_country` VALUES ('34', 'BF', 'BFA', 'BURKINA FASO', '布吉纳法索', 'Burkina Faso', '854', '226');
    INSERT INTO `sms_country` VALUES ('35', 'BI', 'BDI', 'BURUNDI', '布隆迪', 'Burundi', '108', '257');
    INSERT INTO `sms_country` VALUES ('36', 'KH', 'KHM', 'CAMBODIA', '柬埔寨', 'Cambodia', '116', '855');
    INSERT INTO `sms_country` VALUES ('37', 'CM', 'CMR', 'CAMEROON', '喀麦隆', 'Cameroon', '120', '237');
    INSERT INTO `sms_country` VALUES ('38', 'CA', 'CAN', 'CANADA', '加拿大', 'Canada', '124', '1');
    INSERT INTO `sms_country` VALUES ('39', 'CV', 'CPV', 'CAPE VERDE', '佛得角', 'Cape Verde', '132', '238');
    INSERT INTO `sms_country` VALUES ('40', 'KY', 'CYM', 'CAYMAN ISLANDS', '开曼群岛', 'Cayman Islands', '136', '1345');
    INSERT INTO `sms_country` VALUES ('41', 'CF', 'CAF', 'CENTRAL AFRICAN REPUBLIC', '中非共和国', 'Central African Republic', '140', '236');
    INSERT INTO `sms_country` VALUES ('42', 'TD', 'TCD', 'CHAD', '乍得', 'Chad', '148', '235');
    INSERT INTO `sms_country` VALUES ('43', 'CL', 'CHL', 'CHILE', '智利', 'Chile', '152', '56');
    INSERT INTO `sms_country` VALUES ('44', 'CN', 'CHN', 'CHINA', '中国', 'China', '156', '86');
    INSERT INTO `sms_country` VALUES ('45', 'CX', null, 'CHRISTMAS ISLAND', '圣诞岛', 'Christmas Island', null, '61');
    INSERT INTO `sms_country` VALUES ('46', 'CC', null, 'COCOS (KEELING) ISLANDS', 'COCOS(KEELING)岛', 'Cocos (Keeling) Islands', null, '672');
    INSERT INTO `sms_country` VALUES ('47', 'CO', 'COL', 'COLOMBIA', '哥伦比亚', 'Colombia', '170', '57');
    INSERT INTO `sms_country` VALUES ('48', 'KM', 'COM', 'COMOROS', '科摩罗', 'Comoros', '174', '269');
    INSERT INTO `sms_country` VALUES ('49', 'CG', 'COG', 'CONGO', '刚果', 'Congo', '178', '242');
    INSERT INTO `sms_country` VALUES ('50', 'CD', 'COD', 'CONGO, THE DEMOCRATIC REPUBLIC OF THE', '刚果民主共和国的', 'Congo, the Democratic Republic of the', '180', '242');
    INSERT INTO `sms_country` VALUES ('51', 'CK', 'COK', 'COOK ISLANDS', '库克群岛', 'Cook Islands', '184', '682');
    INSERT INTO `sms_country` VALUES ('52', 'CR', 'CRI', 'COSTA RICA', '哥斯达黎加', 'Costa Rica', '188', '506');
    INSERT INTO `sms_country` VALUES ('53', 'CI', 'CIV', 'COTE D\'IVOIRE', '科特迪瓦', 'Cote D\'Ivoire', '384', '225');
    INSERT INTO `sms_country` VALUES ('54', 'HR', 'HRV', 'CROATIA', '克罗地亚', 'Croatia', '191', '385');
    INSERT INTO `sms_country` VALUES ('55', 'CU', 'CUB', 'CUBA', '古巴', 'Cuba', '192', '53');
    INSERT INTO `sms_country` VALUES ('56', 'CY', 'CYP', 'CYPRUS', '塞浦路斯', 'Cyprus', '196', '357');
    INSERT INTO `sms_country` VALUES ('57', 'CZ', 'CZE', 'CZECH REPUBLIC', '捷克共和国', 'Czech Republic', '203', '420');
    INSERT INTO `sms_country` VALUES ('58', 'DK', 'DNK', 'DENMARK', '丹麦', 'Denmark', '208', '45');
    INSERT INTO `sms_country` VALUES ('59', 'DJ', 'DJI', 'DJIBOUTI', '吉布提', 'Djibouti', '262', '253');
    INSERT INTO `sms_country` VALUES ('60', 'DM', 'DMA', 'DOMINICA', '多米尼加', 'Dominica', '212', '1767');
    INSERT INTO `sms_country` VALUES ('61', 'DO', 'DOM', 'DOMINICAN REPUBLIC', '多米尼加共和国', 'Dominican Republic', '214', '1809');
    INSERT INTO `sms_country` VALUES ('62', 'EC', 'ECU', 'ECUADOR', '厄瓜多尔', 'Ecuador', '218', '593');
    INSERT INTO `sms_country` VALUES ('63', 'EG', 'EGY', 'EGYPT', '埃及', 'Egypt', '818', '20');
    INSERT INTO `sms_country` VALUES ('64', 'SV', 'SLV', 'EL SALVADOR', '萨尔瓦多', 'El Salvador', '222', '503');
    INSERT INTO `sms_country` VALUES ('65', 'GQ', 'GNQ', 'EQUATORIAL GUINEA', '赤道几内亚', 'Equatorial Guinea', '226', '240');
    INSERT INTO `sms_country` VALUES ('66', 'ER', 'ERI', 'ERITREA', '厄立特里亚', 'Eritrea', '232', '291');
    INSERT INTO `sms_country` VALUES ('67', 'EE', 'EST', 'ESTONIA', '爱沙尼亚', 'Estonia', '233', '372');
    INSERT INTO `sms_country` VALUES ('68', 'ET', 'ETH', 'ETHIOPIA', '埃塞俄比亚', 'Ethiopia', '231', '251');
    INSERT INTO `sms_country` VALUES ('69', 'FK', 'FLK', 'FALKLAND ISLANDS (MALVINAS)', '福克兰群岛(马尔维纳斯)', 'Falkland Islands (Malvinas)', '238', '500');
    INSERT INTO `sms_country` VALUES ('70', 'FO', 'FRO', 'FAROE ISLANDS', '法罗群岛', 'Faroe Islands', '234', '298');
    INSERT INTO `sms_country` VALUES ('71', 'FJ', 'FJI', 'FIJI', '斐济', 'Fiji', '242', '679');
    INSERT INTO `sms_country` VALUES ('72', 'FI', 'FIN', 'FINLAND', '芬兰', 'Finland', '246', '358');
    INSERT INTO `sms_country` VALUES ('73', 'FR', 'FRA', 'FRANCE', '法国', 'France', '250', '33');
    INSERT INTO `sms_country` VALUES ('74', 'GF', 'GUF', 'FRENCH GUIANA', '法属圭亚那', 'French Guiana', '254', '594');
    INSERT INTO `sms_country` VALUES ('75', 'PF', 'PYF', 'FRENCH POLYNESIA', '法属波利尼西亚', 'French Polynesia', '258', '689');
    INSERT INTO `sms_country` VALUES ('76', 'TF', null, 'FRENCH SOUTHERN TERRITORIES', '法国南部地区', 'French Southern Territories', null, '0');
    INSERT INTO `sms_country` VALUES ('77', 'GA', 'GAB', 'GABON', '加蓬', 'Gabon', '266', '241');
    INSERT INTO `sms_country` VALUES ('78', 'GM', 'GMB', 'GAMBIA', '冈比亚', 'Gambia', '270', '220');
    INSERT INTO `sms_country` VALUES ('79', 'GE', 'GEO', 'GEORGIA', '乔治亚州', 'Georgia', '268', '995');
    INSERT INTO `sms_country` VALUES ('80', 'DE', 'DEU', 'GERMANY', '德国', 'Germany', '276', '49');
    INSERT INTO `sms_country` VALUES ('81', 'GH', 'GHA', 'GHANA', '加纳', 'Ghana', '288', '233');
    INSERT INTO `sms_country` VALUES ('82', 'GI', 'GIB', 'GIBRALTAR', '直布罗陀', 'Gibraltar', '292', '350');
    INSERT INTO `sms_country` VALUES ('83', 'GR', 'GRC', 'GREECE', '希腊', 'Greece', '300', '30');
    INSERT INTO `sms_country` VALUES ('84', 'GL', 'GRL', 'GREENLAND', '格陵兰岛', 'Greenland', '304', '299');
    INSERT INTO `sms_country` VALUES ('85', 'GD', 'GRD', 'GRENADA', '格林纳达', 'Grenada', '308', '1473');
    INSERT INTO `sms_country` VALUES ('86', 'GP', 'GLP', 'GUADELOUPE', '瓜德罗普岛', 'Guadeloupe', '312', '590');
    INSERT INTO `sms_country` VALUES ('87', 'GU', 'GUM', 'GUAM', '关岛', 'Guam', '316', '1671');
    INSERT INTO `sms_country` VALUES ('88', 'GT', 'GTM', 'GUATEMALA', '危地马拉', 'Guatemala', '320', '502');
    INSERT INTO `sms_country` VALUES ('89', 'GN', 'GIN', 'GUINEA', '几内亚', 'Guinea', '324', '224');
    INSERT INTO `sms_country` VALUES ('90', 'GW', 'GNB', 'GUINEA-BISSAU', '几内亚比绍', 'Guinea-Bissau', '624', '245');
    INSERT INTO `sms_country` VALUES ('91', 'GY', 'GUY', 'GUYANA', '圭亚那', 'Guyana', '328', '592');
    INSERT INTO `sms_country` VALUES ('92', 'HT', 'HTI', 'HAITI', '海地', 'Haiti', '332', '509');
    INSERT INTO `sms_country` VALUES ('93', 'HM', null, 'HEARD ISLAND AND MCDONALD ISLANDS', '听到岛和麦当劳的岛屿', 'Heard Island and Mcdonald Islands', null, '0');
    INSERT INTO `sms_country` VALUES ('94', 'VA', 'VAT', 'HOLY SEE (VATICAN CITY STATE)', '教廷(梵蒂冈)', 'Holy See (Vatican City State)', '336', '39');
    INSERT INTO `sms_country` VALUES ('95', 'HN', 'HND', 'HONDURAS', '洪都拉斯', 'Honduras', '340', '504');
    INSERT INTO `sms_country` VALUES ('96', 'HK', 'HKG', 'HONG KONG', '香港', 'Hong Kong', '344', '852');
    INSERT INTO `sms_country` VALUES ('97', 'HU', 'HUN', 'HUNGARY', '匈牙利', 'Hungary', '348', '36');
    INSERT INTO `sms_country` VALUES ('98', 'IS', 'ISL', 'ICELAND', '冰岛', 'Iceland', '352', '354');
    INSERT INTO `sms_country` VALUES ('99', 'IN', 'IND', 'INDIA', '印度', 'India', '356', '91');
    INSERT INTO `sms_country` VALUES ('100', 'ID', 'IDN', 'INDONESIA', '印尼', 'Indonesia', '360', '62');
    INSERT INTO `sms_country` VALUES ('101', 'IR', 'IRN', 'IRAN, ISLAMIC REPUBLIC OF', '伊朗伊斯兰共和国', 'Iran, Islamic Republic of', '364', '98');
    INSERT INTO `sms_country` VALUES ('102', 'IQ', 'IRQ', 'IRAQ', '伊拉克', 'Iraq', '368', '964');
    INSERT INTO `sms_country` VALUES ('103', 'IE', 'IRL', 'IRELAND', '爱尔兰', 'Ireland', '372', '353');
    INSERT INTO `sms_country` VALUES ('104', 'IL', 'ISR', 'ISRAEL', '以色列', 'Israel', '376', '972');
    INSERT INTO `sms_country` VALUES ('105', 'IT', 'ITA', 'ITALY', '意大利', 'Italy', '380', '39');
    INSERT INTO `sms_country` VALUES ('106', 'JM', 'JAM', 'JAMAICA', '牙买加', 'Jamaica', '388', '1876');
    INSERT INTO `sms_country` VALUES ('107', 'JP', 'JPN', 'JAPAN', '日本', 'Japan', '392', '81');
    INSERT INTO `sms_country` VALUES ('108', 'JO', 'JOR', 'JORDAN', '约旦', 'Jordan', '400', '962');
    INSERT INTO `sms_country` VALUES ('109', 'KZ', 'KAZ', 'KAZAKHSTAN', '哈萨克斯坦', 'Kazakhstan', '398', '7');
    INSERT INTO `sms_country` VALUES ('110', 'KE', 'KEN', 'KENYA', '肯尼亚', 'Kenya', '404', '254');
    INSERT INTO `sms_country` VALUES ('111', 'KI', 'KIR', 'KIRIBATI', '基里巴斯', 'Kiribati', '296', '686');
    INSERT INTO `sms_country` VALUES ('112', 'KP', 'PRK', 'KOREA, DEMOCRATIC PEOPLE\'S REPUBLIC OF', '朝鲜民主主义人民共和国', 'Korea, Democratic People\'s Republic of', '408', '850');
    INSERT INTO `sms_country` VALUES ('113', 'KR', 'KOR', 'KOREA, REPUBLIC OF', '朝鲜共和国', 'Korea, Republic of', '410', '82');
    INSERT INTO `sms_country` VALUES ('114', 'KW', 'KWT', 'KUWAIT', '科威特', 'Kuwait', '414', '965');
    INSERT INTO `sms_country` VALUES ('115', 'KG', 'KGZ', 'KYRGYZSTAN', '吉尔吉斯斯坦', 'Kyrgyzstan', '417', '996');
    INSERT INTO `sms_country` VALUES ('116', 'LA', 'LAO', 'LAO PEOPLE\'S DEMOCRATIC REPUBLIC', '老挝人民民主共和国', 'Lao People\'s Democratic Republic', '418', '856');
    INSERT INTO `sms_country` VALUES ('117', 'LV', 'LVA', 'LATVIA', '拉脱维亚', 'Latvia', '428', '371');
    INSERT INTO `sms_country` VALUES ('118', 'LB', 'LBN', 'LEBANON', '黎巴嫩', 'Lebanon', '422', '961');
    INSERT INTO `sms_country` VALUES ('119', 'LS', 'LSO', 'LESOTHO', '莱索托', 'Lesotho', '426', '266');
    INSERT INTO `sms_country` VALUES ('120', 'LR', 'LBR', 'LIBERIA', '利比里亚', 'Liberia', '430', '231');
    INSERT INTO `sms_country` VALUES ('121', 'LY', 'LBY', 'LIBYAN ARAB JAMAHIRIYA', '阿拉伯利比亚民众国', 'Libyan Arab Jamahiriya', '434', '218');
    INSERT INTO `sms_country` VALUES ('122', 'LI', 'LIE', 'LIECHTENSTEIN', '列支敦斯登', 'Liechtenstein', '438', '423');
    INSERT INTO `sms_country` VALUES ('123', 'LT', 'LTU', 'LITHUANIA', '立陶宛', 'Lithuania', '440', '370');
    INSERT INTO `sms_country` VALUES ('124', 'LU', 'LUX', 'LUXEMBOURG', '卢森堡', 'Luxembourg', '442', '352');
    INSERT INTO `sms_country` VALUES ('125', 'MO', 'MAC', 'MACAO', '澳门', 'Macao', '446', '853');
    INSERT INTO `sms_country` VALUES ('126', 'MK', 'MKD', 'MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF', '前南斯拉夫马其顿共和国', 'Macedonia, the Former Yugoslav Republic of', '807', '389');
    INSERT INTO `sms_country` VALUES ('127', 'MG', 'MDG', 'MADAGASCAR', '马达加斯加', 'Madagascar', '450', '261');
    INSERT INTO `sms_country` VALUES ('128', 'MW', 'MWI', 'MALAWI', '马拉维', 'Malawi', '454', '265');
    INSERT INTO `sms_country` VALUES ('129', 'MY', 'MYS', 'MALAYSIA', '马来西亚', 'Malaysia', '458', '60');
    INSERT INTO `sms_country` VALUES ('130', 'MV', 'MDV', 'MALDIVES', '马尔代夫', 'Maldives', '462', '960');
    INSERT INTO `sms_country` VALUES ('131', 'ML', 'MLI', 'MALI', '马里', 'Mali', '466', '223');
    INSERT INTO `sms_country` VALUES ('132', 'MT', 'MLT', 'MALTA', '马耳他', 'Malta', '470', '356');
    INSERT INTO `sms_country` VALUES ('133', 'MH', 'MHL', 'MARSHALL ISLANDS', '马绍尔群岛', 'Marshall Islands', '584', '692');
    INSERT INTO `sms_country` VALUES ('134', 'MQ', 'MTQ', 'MARTINIQUE', '马提尼克岛', 'Martinique', '474', '596');
    INSERT INTO `sms_country` VALUES ('135', 'MR', 'MRT', 'MAURITANIA', '毛利塔尼亚', 'Mauritania', '478', '222');
    INSERT INTO `sms_country` VALUES ('136', 'MU', 'MUS', 'MAURITIUS', '毛里求斯', 'Mauritius', '480', '230');
    INSERT INTO `sms_country` VALUES ('137', 'YT', null, 'MAYOTTE', '马约特岛', 'Mayotte', null, '269');
    INSERT INTO `sms_country` VALUES ('138', 'MX', 'MEX', 'MEXICO', '墨西哥', 'Mexico', '484', '52');
    INSERT INTO `sms_country` VALUES ('139', 'FM', 'FSM', 'MICRONESIA, FEDERATED STATES OF', '密克罗尼西亚联邦', 'Micronesia, Federated States of', '583', '691');
    INSERT INTO `sms_country` VALUES ('140', 'MD', 'MDA', 'MOLDOVA, REPUBLIC OF', '摩尔多瓦共和国', 'Moldova, Republic of', '498', '373');
    INSERT INTO `sms_country` VALUES ('141', 'MC', 'MCO', 'MONACO', '摩纳哥', 'Monaco', '492', '377');
    INSERT INTO `sms_country` VALUES ('142', 'MN', 'MNG', 'MONGOLIA', '蒙古', 'Mongolia', '496', '976');
    INSERT INTO `sms_country` VALUES ('143', 'MS', 'MSR', 'MONTSERRAT', '蒙特塞拉特', 'Montserrat', '500', '1664');
    INSERT INTO `sms_country` VALUES ('144', 'MA', 'MAR', 'MOROCCO', '摩洛哥', 'Morocco', '504', '212');
    INSERT INTO `sms_country` VALUES ('145', 'MZ', 'MOZ', 'MOZAMBIQUE', 'MOZAMBIQUE', 'Mozambique', '508', '258');
    INSERT INTO `sms_country` VALUES ('146', 'MM', 'MMR', 'MYANMAR', '缅甸', 'Myanmar', '104', '95');
    INSERT INTO `sms_country` VALUES ('147', 'NA', 'NAM', 'NAMIBIA', '纳米比亚', 'Namibia', '516', '264');
    INSERT INTO `sms_country` VALUES ('148', 'NR', 'NRU', 'NAURU', '瑙鲁', 'Nauru', '520', '674');
    INSERT INTO `sms_country` VALUES ('149', 'NP', 'NPL', 'NEPAL', '尼泊尔', 'Nepal', '524', '977');
    INSERT INTO `sms_country` VALUES ('150', 'NL', 'NLD', 'NETHERLANDS', '荷兰', 'Netherlands', '528', '31');
    INSERT INTO `sms_country` VALUES ('151', 'AN', 'ANT', 'NETHERLANDS ANTILLES', '荷属安的列斯群岛', 'Netherlands Antilles', '530', '599');
    INSERT INTO `sms_country` VALUES ('152', 'NC', 'NCL', 'NEW CALEDONIA', '新喀里多尼亚', 'New Caledonia', '540', '687');
    INSERT INTO `sms_country` VALUES ('153', 'NZ', 'NZL', 'NEW ZEALAND', '新西兰', 'New Zealand', '554', '64');
    INSERT INTO `sms_country` VALUES ('154', 'NI', 'NIC', 'NICARAGUA', '尼加拉瓜', 'Nicaragua', '558', '505');
    INSERT INTO `sms_country` VALUES ('155', 'NE', 'NER', 'NIGER', '尼日尔', 'Niger', '562', '227');
    INSERT INTO `sms_country` VALUES ('156', 'NG', 'NGA', 'NIGERIA', '尼日利亚', 'Nigeria', '566', '234');
    INSERT INTO `sms_country` VALUES ('157', 'NU', 'NIU', 'NIUE', '纽埃岛', 'Niue', '570', '683');
    INSERT INTO `sms_country` VALUES ('158', 'NF', 'NFK', 'NORFOLK ISLAND', '诺福克岛', 'Norfolk Island', '574', '672');
    INSERT INTO `sms_country` VALUES ('159', 'MP', 'MNP', 'NORTHERN MARIANA ISLANDS', '北马里亚纳群岛', 'Northern Mariana Islands', '580', '1670');
    INSERT INTO `sms_country` VALUES ('160', 'NO', 'NOR', 'NORWAY', '挪威', 'Norway', '578', '47');
    INSERT INTO `sms_country` VALUES ('161', 'OM', 'OMN', 'OMAN', '阿曼', 'Oman', '512', '968');
    INSERT INTO `sms_country` VALUES ('162', 'PK', 'PAK', 'PAKISTAN', '巴基斯坦', 'Pakistan', '586', '92');
    INSERT INTO `sms_country` VALUES ('163', 'PW', 'PLW', 'PALAU', '帕劳', 'Palau', '585', '680');
    INSERT INTO `sms_country` VALUES ('164', 'PS', null, 'PALESTINIAN TERRITORY, OCCUPIED', '巴勒斯坦的领土,占领', 'Palestinian Territory, Occupied', null, '970');
    INSERT INTO `sms_country` VALUES ('165', 'PA', 'PAN', 'PANAMA', '巴拿马', 'Panama', '591', '507');
    INSERT INTO `sms_country` VALUES ('166', 'PG', 'PNG', 'PAPUA NEW GUINEA', '巴布新几内亚', 'Papua New Guinea', '598', '675');
    INSERT INTO `sms_country` VALUES ('167', 'PY', 'PRY', 'PARAGUAY', '巴拉圭', 'Paraguay', '600', '595');
    INSERT INTO `sms_country` VALUES ('168', 'PE', 'PER', 'PERU', '秘鲁', 'Peru', '604', '51');
    INSERT INTO `sms_country` VALUES ('169', 'PH', 'PHL', 'PHILIPPINES', '菲律宾', 'Philippines', '608', '63');
    INSERT INTO `sms_country` VALUES ('170', 'PN', 'PCN', 'PITCAIRN', '皮特克恩', 'Pitcairn', '612', '0');
    INSERT INTO `sms_country` VALUES ('171', 'PL', 'POL', 'POLAND', '波兰', 'Poland', '616', '48');
    INSERT INTO `sms_country` VALUES ('172', 'PT', 'PRT', 'PORTUGAL', '葡萄牙', 'Portugal', '620', '351');
    INSERT INTO `sms_country` VALUES ('173', 'PR', 'PRI', 'PUERTO RICO', '波多黎各', 'Puerto Rico', '630', '1787');
    INSERT INTO `sms_country` VALUES ('174', 'QA', 'QAT', 'QATAR', '卡塔尔', 'Qatar', '634', '974');
    INSERT INTO `sms_country` VALUES ('175', 'RE', 'REU', 'REUNION', '团聚', 'Reunion', '638', '262');
    INSERT INTO `sms_country` VALUES ('176', 'RO', 'ROM', 'ROMANIA', '罗马尼亚', 'Romania', '642', '40');
    INSERT INTO `sms_country` VALUES ('177', 'RU', 'RUS', 'RUSSIAN FEDERATION', '俄罗斯联邦', 'Russian Federation', '643', '70');
    INSERT INTO `sms_country` VALUES ('178', 'RW', 'RWA', 'RWANDA', '卢旺达', 'Rwanda', '646', '250');
    INSERT INTO `sms_country` VALUES ('179', 'SH', 'SHN', 'SAINT HELENA', '圣赫勒拿', 'Saint Helena', '654', '290');
    INSERT INTO `sms_country` VALUES ('180', 'KN', 'KNA', 'SAINT KITTS AND NEVIS', '圣基茨和尼维斯', 'Saint Kitts and Nevis', '659', '1869');
    INSERT INTO `sms_country` VALUES ('181', 'LC', 'LCA', 'SAINT LUCIA', '圣卢西亚岛', 'Saint Lucia', '662', '1758');
    INSERT INTO `sms_country` VALUES ('182', 'PM', 'SPM', 'SAINT PIERRE AND MIQUELON', '圣皮埃尔和MIQUELON', 'Saint Pierre and Miquelon', '666', '508');
    INSERT INTO `sms_country` VALUES ('183', 'VC', 'VCT', 'SAINT VINCENT AND THE GRENADINES', '圣文森特和格林纳丁斯', 'Saint Vincent and the Grenadines', '670', '1784');
    INSERT INTO `sms_country` VALUES ('184', 'WS', 'WSM', 'SAMOA', '萨摩亚', 'Samoa', '882', '684');
    INSERT INTO `sms_country` VALUES ('185', 'SM', 'SMR', 'SAN MARINO', '圣马力诺', 'San Marino', '674', '378');
    INSERT INTO `sms_country` VALUES ('186', 'ST', 'STP', 'SAO TOME AND PRINCIPE', '圣多美和王子', 'Sao Tome and Principe', '678', '239');
    INSERT INTO `sms_country` VALUES ('187', 'SA', 'SAU', 'SAUDI ARABIA', '沙特阿拉伯', 'Saudi Arabia', '682', '966');
    INSERT INTO `sms_country` VALUES ('188', 'SN', 'SEN', 'SENEGAL', '塞内加尔', 'Senegal', '686', '221');
    INSERT INTO `sms_country` VALUES ('189', 'CS', null, 'SERBIA AND MONTENEGRO', '塞尔维亚和黑山', 'Serbia and Montenegro', null, '381');
    INSERT INTO `sms_country` VALUES ('190', 'SC', 'SYC', 'SEYCHELLES', '塞舌尔', 'Seychelles', '690', '248');
    INSERT INTO `sms_country` VALUES ('191', 'SL', 'SLE', 'SIERRA LEONE', '塞拉利昂', 'Sierra Leone', '694', '232');
    INSERT INTO `sms_country` VALUES ('192', 'SG', 'SGP', 'SINGAPORE', '新加坡', 'Singapore', '702', '65');
    INSERT INTO `sms_country` VALUES ('193', 'SK', 'SVK', 'SLOVAKIA', '斯洛伐克', 'Slovakia', '703', '421');
    INSERT INTO `sms_country` VALUES ('194', 'SI', 'SVN', 'SLOVENIA', '斯洛文尼亚', 'Slovenia', '705', '386');
    INSERT INTO `sms_country` VALUES ('195', 'SB', 'SLB', 'SOLOMON ISLANDS', '所罗门群岛', 'Solomon Islands', '90', '677');
    INSERT INTO `sms_country` VALUES ('196', 'SO', 'SOM', 'SOMALIA', '索马里', 'Somalia', '706', '252');
    INSERT INTO `sms_country` VALUES ('197', 'ZA', 'ZAF', 'SOUTH AFRICA', '南非', 'South Africa', '710', '27');
    INSERT INTO `sms_country` VALUES ('198', 'GS', null, 'SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS', '南乔治亚岛和南桑威奇群岛', 'South Georgia and the South Sandwich Islands', null, '0');
    INSERT INTO `sms_country` VALUES ('199', 'ES', 'ESP', 'SPAIN', '西班牙', 'Spain', '724', '34');
    INSERT INTO `sms_country` VALUES ('200', 'LK', 'LKA', 'SRI LANKA', '斯里兰卡', 'Sri Lanka', '144', '94');
    INSERT INTO `sms_country` VALUES ('201', 'SD', 'SDN', 'SUDAN', '苏丹', 'Sudan', '736', '249');
    INSERT INTO `sms_country` VALUES ('202', 'SR', 'SUR', 'SURINAME', '苏里南', 'Suriname', '740', '597');
    INSERT INTO `sms_country` VALUES ('203', 'SJ', 'SJM', 'SVALBARD AND JAN MAYEN', '斯瓦尔巴群岛和扬马延岛', 'Svalbard and Jan Mayen', '744', '47');
    INSERT INTO `sms_country` VALUES ('204', 'SZ', 'SWZ', 'SWAZILAND', '斯威士兰', 'Swaziland', '748', '268');
    INSERT INTO `sms_country` VALUES ('205', 'SE', 'SWE', 'SWEDEN', '瑞典', 'Sweden', '752', '46');
    INSERT INTO `sms_country` VALUES ('206', 'CH', 'CHE', 'SWITZERLAND', '瑞士', 'Switzerland', '756', '41');
    INSERT INTO `sms_country` VALUES ('207', 'SY', 'SYR', 'SYRIAN ARAB REPUBLIC', '阿拉伯叙利亚共和国', 'Syrian Arab Republic', '760', '963');
    INSERT INTO `sms_country` VALUES ('208', 'TW', 'TWN', 'TAIWAN, PROVINCE OF CHINA', '台湾,中国的省份', 'Taiwan, Province of China', '158', '886');
    INSERT INTO `sms_country` VALUES ('209', 'TJ', 'TJK', 'TAJIKISTAN', '塔吉克斯坦', 'Tajikistan', '762', '992');
    INSERT INTO `sms_country` VALUES ('210', 'TZ', 'TZA', 'TANZANIA, UNITED REPUBLIC OF', '坦桑尼亚联合共和国', 'Tanzania, United Republic of', '834', '255');
    INSERT INTO `sms_country` VALUES ('211', 'TH', 'THA', 'THAILAND', '泰国', 'Thailand', '764', '66');
    INSERT INTO `sms_country` VALUES ('212', 'TL', null, 'TIMOR-LESTE', '东帝汶', 'Timor-Leste', null, '670');
    INSERT INTO `sms_country` VALUES ('213', 'TG', 'TGO', 'TOGO', '多哥', 'Togo', '768', '228');
    INSERT INTO `sms_country` VALUES ('214', 'TK', 'TKL', 'TOKELAU', '托克劳', 'Tokelau', '772', '690');
    INSERT INTO `sms_country` VALUES ('215', 'TO', 'TON', 'TONGA', '汤加', 'Tonga', '776', '676');
    INSERT INTO `sms_country` VALUES ('216', 'TT', 'TTO', 'TRINIDAD AND TOBAGO', '特立尼达和多巴哥', 'Trinidad and Tobago', '780', '1868');
    INSERT INTO `sms_country` VALUES ('217', 'TN', 'TUN', 'TUNISIA', '突尼斯', 'Tunisia', '788', '216');
    INSERT INTO `sms_country` VALUES ('218', 'TR', 'TUR', 'TURKEY', '土耳其', 'Turkey', '792', '90');
    INSERT INTO `sms_country` VALUES ('219', 'TM', 'TKM', 'TURKMENISTAN', '土库曼斯坦', 'Turkmenistan', '795', '7370');
    INSERT INTO `sms_country` VALUES ('220', 'TC', 'TCA', 'TURKS AND CAICOS ISLANDS', '特克斯和凯科斯群岛', 'Turks and Caicos Islands', '796', '1649');
    INSERT INTO `sms_country` VALUES ('221', 'TV', 'TUV', 'TUVALU', '图瓦卢', 'Tuvalu', '798', '688');
    INSERT INTO `sms_country` VALUES ('222', 'UG', 'UGA', 'UGANDA', '乌干达', 'Uganda', '800', '256');
    INSERT INTO `sms_country` VALUES ('223', 'UA', 'UKR', 'UKRAINE', '乌克兰', 'Ukraine', '804', '380');
    INSERT INTO `sms_country` VALUES ('224', 'AE', 'ARE', 'UNITED ARAB EMIRATES', '阿拉伯联合酋长国', 'United Arab Emirates', '784', '971');
    INSERT INTO `sms_country` VALUES ('225', 'GB', 'GBR', 'UNITED KINGDOM', '联合王国', 'United Kingdom', '826', '44');
    INSERT INTO `sms_country` VALUES ('226', 'US', 'USA', 'UNITED STATES', '美国', 'United States', '840', '1');
    INSERT INTO `sms_country` VALUES ('227', 'UM', null, 'UNITED STATES MINOR OUTLYING ISLANDS', '美国小离岛', 'United States Minor Outlying Islands', null, '1');
    INSERT INTO `sms_country` VALUES ('228', 'UY', 'URY', 'URUGUAY', '乌拉圭', 'Uruguay', '858', '598');
    INSERT INTO `sms_country` VALUES ('229', 'UZ', 'UZB', 'UZBEKISTAN', '乌兹别克斯坦', 'Uzbekistan', '860', '998');
    INSERT INTO `sms_country` VALUES ('230', 'VU', 'VUT', 'VANUATU', '瓦努阿图', 'Vanuatu', '548', '678');
    INSERT INTO `sms_country` VALUES ('231', 'VE', 'VEN', 'VENEZUELA', '委内瑞拉', 'Venezuela', '862', '58');
    INSERT INTO `sms_country` VALUES ('232', 'VN', 'VNM', 'VIET NAM', '越南', 'Viet Nam', '704', '84');
    INSERT INTO `sms_country` VALUES ('233', 'VG', 'VGB', 'VIRGIN ISLANDS, BRITISH', '维尔京群岛,英国', 'Virgin Islands, British', '92', '1284');
    INSERT INTO `sms_country` VALUES ('234', 'VI', 'VIR', 'VIRGIN ISLANDS, U.S.', '维尔京群岛,美国', 'Virgin Islands, U.s.', '850', '1340');
    INSERT INTO `sms_country` VALUES ('235', 'WF', 'WLF', 'WALLIS AND FUTUNA', '瓦利斯群岛和富图纳群岛', 'Wallis and Futuna', '876', '681');
    INSERT INTO `sms_country` VALUES ('236', 'EH', 'ESH', 'WESTERN SAHARA', '西撒哈拉', 'Western Sahara', '732', '212');
    INSERT INTO `sms_country` VALUES ('237', 'YE', 'YEM', 'YEMEN', '也门', 'Yemen', '887', '967');
    INSERT INTO `sms_country` VALUES ('238', 'ZM', 'ZMB', 'ZAMBIA', '赞比亚', 'Zambia', '894', '260');
    INSERT INTO `sms_country` VALUES ('239', 'ZW', 'ZWE', 'ZIMBABWE', '津巴布韦', 'Zimbabwe', '716', '263');

    二、文本

    1,AF,AFG,AFGHANISTAN,阿富汗,Afghanistan,4,93
    2,AL,ALB,ALBANIA,阿尔巴尼亚,Albania,8,355
    3,DZ,DZA,ALGERIA,阿尔及利亚,Algeria,12,213
    4,AS,ASM,AMERICAN SAMOA,美属萨摩亚,American Samoa,16,1684
    5,AD,AND,ANDORRA,安道尔,Andorra,20,376
    6,AO,AGO,ANGOLA,安哥拉,Angola,24,244
    7,AI,AIA,ANGUILLA,安圭拉岛,Anguilla,660,1264
    8,AQ,,ANTARCTICA,南极洲,Antarctica,,0
    9,AG,ATG,ANTIGUA AND BARBUDA,安提瓜岛和巴布达,Antigua and Barbuda,28,1268
    10,AR,ARG,ARGENTINA,阿根廷,Argentina,32,54
    11,AM,ARM,ARMENIA,亚美尼亚,Armenia,51,374
    12,AW,ABW,ARUBA,阿鲁巴岛,Aruba,533,297
    13,AU,AUS,AUSTRALIA,澳大利亚,Australia,36,61
    14,AT,AUT,AUSTRIA,奥地利,Austria,40,43
    15,AZ,AZE,AZERBAIJAN,阿塞拜疆,Azerbaijan,31,994
    16,BS,BHS,BAHAMAS,巴哈马群岛,Bahamas,44,1242
    17,BH,BHR,BAHRAIN,巴林,Bahrain,48,973
    18,BD,BGD,BANGLADESH,孟加拉国,Bangladesh,50,880
    19,BB,BRB,BARBADOS,巴巴多斯,Barbados,52,1246
    20,BY,BLR,BELARUS,白俄罗斯,Belarus,112,375
    21,BE,BEL,BELGIUM,比利时,Belgium,56,32
    22,BZ,BLZ,BELIZE,伯利兹,Belize,84,501
    23,BJ,BEN,BENIN,贝宁,Benin,204,229
    24,BM,BMU,BERMUDA,百慕大,Bermuda,60,1441
    25,BT,BTN,BHUTAN,不丹,Bhutan,64,975
    26,BO,BOL,BOLIVIA,玻利维亚,Bolivia,68,591
    27,BA,BIH,BOSNIA AND HERZEGOVINA,波斯尼亚和黑塞哥维那,Bosnia and Herzegovina,70,387
    28,BW,BWA,BOTSWANA,博茨瓦纳,Botswana,72,267
    29,BV,,BOUVET ISLAND,布维岛,Bouvet Island,,0
    30,BR,BRA,BRAZIL,巴西,Brazil,76,55
    31,IO,,BRITISH INDIAN OCEAN TERRITORY,英属印度洋领地,British Indian Ocean Territory,,246
    32,BN,BRN,BRUNEI DARUSSALAM,文莱达鲁萨兰国,Brunei Darussalam,96,673
    33,BG,BGR,BULGARIA,保加利亚,Bulgaria,100,359
    34,BF,BFA,BURKINA FASO,布吉纳法索,Burkina Faso,854,226
    35,BI,BDI,BURUNDI,布隆迪,Burundi,108,257
    36,KH,KHM,CAMBODIA,柬埔寨,Cambodia,116,855
    37,CM,CMR,CAMEROON,喀麦隆,Cameroon,120,237
    38,CA,CAN,CANADA,加拿大,Canada,124,1
    39,CV,CPV,CAPE VERDE,佛得角,Cape Verde,132,238
    40,KY,CYM,CAYMAN ISLANDS,开曼群岛,Cayman Islands,136,1345
    41,CF,CAF,CENTRAL AFRICAN REPUBLIC,中非共和国,Central African Republic,140,236
    42,TD,TCD,CHAD,乍得,Chad,148,235
    43,CL,CHL,CHILE,智利,Chile,152,56
    44,CN,CHN,CHINA,中国,China,156,86
    45,CX,,CHRISTMAS ISLAND,圣诞岛,Christmas Island,,61
    46,CC,,COCOS (KEELING) ISLANDS,COCOS(KEELING)岛,Cocos (Keeling) Islands,,672
    47,CO,COL,COLOMBIA,哥伦比亚,Colombia,170,57
    48,KM,COM,COMOROS,科摩罗,Comoros,174,269
    49,CG,COG,CONGO,刚果,Congo,178,242
    50,CD,COD,CONGO, THE DEMOCRATIC REPUBLIC OF THE,刚果民主共和国的,Congo, the Democratic Republic of the,180,242
    51,CK,COK,COOK ISLANDS,库克群岛,Cook Islands,184,682
    52,CR,CRI,COSTA RICA,哥斯达黎加,Costa Rica,188,506
    53,CI,CIV,COTE D'IVOIRE,科特迪瓦,Cote D'Ivoire,384,225
    54,HR,HRV,CROATIA,克罗地亚,Croatia,191,385
    55,CU,CUB,CUBA,古巴,Cuba,192,53
    56,CY,CYP,CYPRUS,塞浦路斯,Cyprus,196,357
    57,CZ,CZE,CZECH REPUBLIC,捷克共和国,Czech Republic,203,420
    58,DK,DNK,DENMARK,丹麦,Denmark,208,45
    59,DJ,DJI,DJIBOUTI,吉布提,Djibouti,262,253
    60,DM,DMA,DOMINICA,多米尼加,Dominica,212,1767
    61,DO,DOM,DOMINICAN REPUBLIC,多米尼加共和国,Dominican Republic,214,1809
    62,EC,ECU,ECUADOR,厄瓜多尔,Ecuador,218,593
    63,EG,EGY,EGYPT,埃及,Egypt,818,20
    64,SV,SLV,EL SALVADOR,萨尔瓦多,El Salvador,222,503
    65,GQ,GNQ,EQUATORIAL GUINEA,赤道几内亚,Equatorial Guinea,226,240
    66,ER,ERI,ERITREA,厄立特里亚,Eritrea,232,291
    67,EE,EST,ESTONIA,爱沙尼亚,Estonia,233,372
    68,ET,ETH,ETHIOPIA,埃塞俄比亚,Ethiopia,231,251
    69,FK,FLK,FALKLAND ISLANDS (MALVINAS),福克兰群岛(马尔维纳斯),Falkland Islands (Malvinas),238,500
    70,FO,FRO,FAROE ISLANDS,法罗群岛,Faroe Islands,234,298
    71,FJ,FJI,FIJI,斐济,Fiji,242,679
    72,FI,FIN,FINLAND,芬兰,Finland,246,358
    73,FR,FRA,FRANCE,法国,France,250,33
    74,GF,GUF,FRENCH GUIANA,法属圭亚那,French Guiana,254,594
    75,PF,PYF,FRENCH POLYNESIA,法属波利尼西亚,French Polynesia,258,689
    76,TF,,FRENCH SOUTHERN TERRITORIES,法国南部地区,French Southern Territories,,0
    77,GA,GAB,GABON,加蓬,Gabon,266,241
    78,GM,GMB,GAMBIA,冈比亚,Gambia,270,220
    79,GE,GEO,GEORGIA,乔治亚州,Georgia,268,995
    80,DE,DEU,GERMANY,德国,Germany,276,49
    81,GH,GHA,GHANA,加纳,Ghana,288,233
    82,GI,GIB,GIBRALTAR,直布罗陀,Gibraltar,292,350
    83,GR,GRC,GREECE,希腊,Greece,300,30
    84,GL,GRL,GREENLAND,格陵兰岛,Greenland,304,299
    85,GD,GRD,GRENADA,格林纳达,Grenada,308,1473
    86,GP,GLP,GUADELOUPE,瓜德罗普岛,Guadeloupe,312,590
    87,GU,GUM,GUAM,关岛,Guam,316,1671
    88,GT,GTM,GUATEMALA,危地马拉,Guatemala,320,502
    89,GN,GIN,GUINEA,几内亚,Guinea,324,224
    90,GW,GNB,GUINEA-BISSAU,几内亚比绍,Guinea-Bissau,624,245
    91,GY,GUY,GUYANA,圭亚那,Guyana,328,592
    92,HT,HTI,HAITI,海地,Haiti,332,509
    93,HM,,HEARD ISLAND AND MCDONALD ISLANDS,听到岛和麦当劳的岛屿,Heard Island and Mcdonald Islands,,0
    94,VA,VAT,HOLY SEE (VATICAN CITY STATE),教廷(梵蒂冈),Holy See (Vatican City State),336,39
    95,HN,HND,HONDURAS,洪都拉斯,Honduras,340,504
    96,HK,HKG,HONG KONG,香港,Hong Kong,344,852
    97,HU,HUN,HUNGARY,匈牙利,Hungary,348,36
    98,IS,ISL,ICELAND,冰岛,Iceland,352,354
    99,IN,IND,INDIA,印度,India,356,91
    100,ID,IDN,INDONESIA,印尼,Indonesia,360,62
    101,IR,IRN,IRAN, ISLAMIC REPUBLIC OF,伊朗伊斯兰共和国,Iran, Islamic Republic of,364,98
    102,IQ,IRQ,IRAQ,伊拉克,Iraq,368,964
    103,IE,IRL,IRELAND,爱尔兰,Ireland,372,353
    104,IL,ISR,ISRAEL,以色列,Israel,376,972
    105,IT,ITA,ITALY,意大利,Italy,380,39
    106,JM,JAM,JAMAICA,牙买加,Jamaica,388,1876
    107,JP,JPN,JAPAN,日本,Japan,392,81
    108,JO,JOR,JORDAN,约旦,Jordan,400,962
    109,KZ,KAZ,KAZAKHSTAN,哈萨克斯坦,Kazakhstan,398,7
    110,KE,KEN,KENYA,肯尼亚,Kenya,404,254
    111,KI,KIR,KIRIBATI,基里巴斯,Kiribati,296,686
    112,KP,PRK,KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF,朝鲜民主主义人民共和国,Korea, Democratic People's Republic of,408,850
    113,KR,KOR,KOREA, REPUBLIC OF,朝鲜共和国,Korea, Republic of,410,82
    114,KW,KWT,KUWAIT,科威特,Kuwait,414,965
    115,KG,KGZ,KYRGYZSTAN,吉尔吉斯斯坦,Kyrgyzstan,417,996
    116,LA,LAO,LAO PEOPLE'S DEMOCRATIC REPUBLIC,老挝人民民主共和国,Lao People's Democratic Republic,418,856
    117,LV,LVA,LATVIA,拉脱维亚,Latvia,428,371
    118,LB,LBN,LEBANON,黎巴嫩,Lebanon,422,961
    119,LS,LSO,LESOTHO,莱索托,Lesotho,426,266
    120,LR,LBR,LIBERIA,利比里亚,Liberia,430,231
    121,LY,LBY,LIBYAN ARAB JAMAHIRIYA,阿拉伯利比亚民众国,Libyan Arab Jamahiriya,434,218
    122,LI,LIE,LIECHTENSTEIN,列支敦斯登,Liechtenstein,438,423
    123,LT,LTU,LITHUANIA,立陶宛,Lithuania,440,370
    124,LU,LUX,LUXEMBOURG,卢森堡,Luxembourg,442,352
    125,MO,MAC,MACAO,澳门,Macao,446,853
    126,MK,MKD,MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF,前南斯拉夫马其顿共和国,Macedonia, the Former Yugoslav Republic of,807,389
    127,MG,MDG,MADAGASCAR,马达加斯加,Madagascar,450,261
    128,MW,MWI,MALAWI,马拉维,Malawi,454,265
    129,MY,MYS,MALAYSIA,马来西亚,Malaysia,458,60
    130,MV,MDV,MALDIVES,马尔代夫,Maldives,462,960
    131,ML,MLI,MALI,马里,Mali,466,223
    132,MT,MLT,MALTA,马耳他,Malta,470,356
    133,MH,MHL,MARSHALL ISLANDS,马绍尔群岛,Marshall Islands,584,692
    134,MQ,MTQ,MARTINIQUE,马提尼克岛,Martinique,474,596
    135,MR,MRT,MAURITANIA,毛利塔尼亚,Mauritania,478,222
    136,MU,MUS,MAURITIUS,毛里求斯,Mauritius,480,230
    137,YT,,MAYOTTE,马约特岛,Mayotte,,269
    138,MX,MEX,MEXICO,墨西哥,Mexico,484,52
    139,FM,FSM,MICRONESIA, FEDERATED STATES OF,密克罗尼西亚联邦,Micronesia, Federated States of,583,691
    140,MD,MDA,MOLDOVA, REPUBLIC OF,摩尔多瓦共和国,Moldova, Republic of,498,373
    141,MC,MCO,MONACO,摩纳哥,Monaco,492,377
    142,MN,MNG,MONGOLIA,蒙古,Mongolia,496,976
    143,MS,MSR,MONTSERRAT,蒙特塞拉特,Montserrat,500,1664
    144,MA,MAR,MOROCCO,摩洛哥,Morocco,504,212
    145,MZ,MOZ,MOZAMBIQUE,MOZAMBIQUE,Mozambique,508,258
    146,MM,MMR,MYANMAR,缅甸,Myanmar,104,95
    147,NA,NAM,NAMIBIA,纳米比亚,Namibia,516,264
    148,NR,NRU,NAURU,瑙鲁,Nauru,520,674
    149,NP,NPL,NEPAL,尼泊尔,Nepal,524,977
    150,NL,NLD,NETHERLANDS,荷兰,Netherlands,528,31
    151,AN,ANT,NETHERLANDS ANTILLES,荷属安的列斯群岛,Netherlands Antilles,530,599
    152,NC,NCL,NEW CALEDONIA,新喀里多尼亚,New Caledonia,540,687
    153,NZ,NZL,NEW ZEALAND,新西兰,New Zealand,554,64
    154,NI,NIC,NICARAGUA,尼加拉瓜,Nicaragua,558,505
    155,NE,NER,NIGER,尼日尔,Niger,562,227
    156,NG,NGA,NIGERIA,尼日利亚,Nigeria,566,234
    157,NU,NIU,NIUE,纽埃岛,Niue,570,683
    158,NF,NFK,NORFOLK ISLAND,诺福克岛,Norfolk Island,574,672
    159,MP,MNP,NORTHERN MARIANA ISLANDS,北马里亚纳群岛,Northern Mariana Islands,580,1670
    160,NO,NOR,NORWAY,挪威,Norway,578,47
    161,OM,OMN,OMAN,阿曼,Oman,512,968
    162,PK,PAK,PAKISTAN,巴基斯坦,Pakistan,586,92
    163,PW,PLW,PALAU,帕劳,Palau,585,680
    164,PS,,PALESTINIAN TERRITORY, OCCUPIED,巴勒斯坦的领土,占领,Palestinian Territory, Occupied,,970
    165,PA,PAN,PANAMA,巴拿马,Panama,591,507
    166,PG,PNG,PAPUA NEW GUINEA,巴布新几内亚,Papua New Guinea,598,675
    167,PY,PRY,PARAGUAY,巴拉圭,Paraguay,600,595
    168,PE,PER,PERU,秘鲁,Peru,604,51
    169,PH,PHL,PHILIPPINES,菲律宾,Philippines,608,63
    170,PN,PCN,PITCAIRN,皮特克恩,Pitcairn,612,0
    171,PL,POL,POLAND,波兰,Poland,616,48
    172,PT,PRT,PORTUGAL,葡萄牙,Portugal,620,351
    173,PR,PRI,PUERTO RICO,波多黎各,Puerto Rico,630,1787
    174,QA,QAT,QATAR,卡塔尔,Qatar,634,974
    175,RE,REU,REUNION,团聚,Reunion,638,262
    176,RO,ROM,ROMANIA,罗马尼亚,Romania,642,40
    177,RU,RUS,RUSSIAN FEDERATION,俄罗斯联邦,Russian Federation,643,70
    178,RW,RWA,RWANDA,卢旺达,Rwanda,646,250
    179,SH,SHN,SAINT HELENA,圣赫勒拿,Saint Helena,654,290
    180,KN,KNA,SAINT KITTS AND NEVIS,圣基茨和尼维斯,Saint Kitts and Nevis,659,1869
    181,LC,LCA,SAINT LUCIA,圣卢西亚岛,Saint Lucia,662,1758
    182,PM,SPM,SAINT PIERRE AND MIQUELON,圣皮埃尔和MIQUELON,Saint Pierre and Miquelon,666,508
    183,VC,VCT,SAINT VINCENT AND THE GRENADINES,圣文森特和格林纳丁斯,Saint Vincent and the Grenadines,670,1784
    184,WS,WSM,SAMOA,萨摩亚,Samoa,882,684
    185,SM,SMR,SAN MARINO,圣马力诺,San Marino,674,378
    186,ST,STP,SAO TOME AND PRINCIPE,圣多美和王子,Sao Tome and Principe,678,239
    187,SA,SAU,SAUDI ARABIA,沙特阿拉伯,Saudi Arabia,682,966
    188,SN,SEN,SENEGAL,塞内加尔,Senegal,686,221
    189,CS,,SERBIA AND MONTENEGRO,塞尔维亚和黑山,Serbia and Montenegro,,381
    190,SC,SYC,SEYCHELLES,塞舌尔,Seychelles,690,248
    191,SL,SLE,SIERRA LEONE,塞拉利昂,Sierra Leone,694,232
    192,SG,SGP,SINGAPORE,新加坡,Singapore,702,65
    193,SK,SVK,SLOVAKIA,斯洛伐克,Slovakia,703,421
    194,SI,SVN,SLOVENIA,斯洛文尼亚,Slovenia,705,386
    195,SB,SLB,SOLOMON ISLANDS,所罗门群岛,Solomon Islands,90,677
    196,SO,SOM,SOMALIA,索马里,Somalia,706,252
    197,ZA,ZAF,SOUTH AFRICA,南非,South Africa,710,27
    198,GS,,SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS,南乔治亚岛和南桑威奇群岛,South Georgia and the South Sandwich Islands,,0
    199,ES,ESP,SPAIN,西班牙,Spain,724,34
    200,LK,LKA,SRI LANKA,斯里兰卡,Sri Lanka,144,94
    201,SD,SDN,SUDAN,苏丹,Sudan,736,249
    202,SR,SUR,SURINAME,苏里南,Suriname,740,597
    203,SJ,SJM,SVALBARD AND JAN MAYEN,斯瓦尔巴群岛和扬马延岛,Svalbard and Jan Mayen,744,47
    204,SZ,SWZ,SWAZILAND,斯威士兰,Swaziland,748,268
    205,SE,SWE,SWEDEN,瑞典,Sweden,752,46
    206,CH,CHE,SWITZERLAND,瑞士,Switzerland,756,41
    207,SY,SYR,SYRIAN ARAB REPUBLIC,阿拉伯叙利亚共和国,Syrian Arab Republic,760,963
    208,TW,TWN,TAIWAN, PROVINCE OF CHINA,台湾,中国的省份,Taiwan, Province of China,158,886
    209,TJ,TJK,TAJIKISTAN,塔吉克斯坦,Tajikistan,762,992
    210,TZ,TZA,TANZANIA, UNITED REPUBLIC OF,坦桑尼亚联合共和国,Tanzania, United Republic of,834,255
    211,TH,THA,THAILAND,泰国,Thailand,764,66
    212,TL,,TIMOR-LESTE,东帝汶,Timor-Leste,,670
    213,TG,TGO,TOGO,多哥,Togo,768,228
    214,TK,TKL,TOKELAU,托克劳,Tokelau,772,690
    215,TO,TON,TONGA,汤加,Tonga,776,676
    216,TT,TTO,TRINIDAD AND TOBAGO,特立尼达和多巴哥,Trinidad and Tobago,780,1868
    217,TN,TUN,TUNISIA,突尼斯,Tunisia,788,216
    218,TR,TUR,TURKEY,土耳其,Turkey,792,90
    219,TM,TKM,TURKMENISTAN,土库曼斯坦,Turkmenistan,795,7370
    220,TC,TCA,TURKS AND CAICOS ISLANDS,特克斯和凯科斯群岛,Turks and Caicos Islands,796,1649
    221,TV,TUV,TUVALU,图瓦卢,Tuvalu,798,688
    222,UG,UGA,UGANDA,乌干达,Uganda,800,256
    223,UA,UKR,UKRAINE,乌克兰,Ukraine,804,380
    224,AE,ARE,UNITED ARAB EMIRATES,阿拉伯联合酋长国,United Arab Emirates,784,971
    225,GB,GBR,UNITED KINGDOM,联合王国,United Kingdom,826,44
    226,US,USA,UNITED STATES,美国,United States,840,1
    227,UM,,UNITED STATES MINOR OUTLYING ISLANDS,美国小离岛,United States Minor Outlying Islands,,1
    228,UY,URY,URUGUAY,乌拉圭,Uruguay,858,598
    229,UZ,UZB,UZBEKISTAN,乌兹别克斯坦,Uzbekistan,860,998
    230,VU,VUT,VANUATU,瓦努阿图,Vanuatu,548,678
    231,VE,VEN,VENEZUELA,委内瑞拉,Venezuela,862,58
    232,VN,VNM,VIET NAM,越南,Viet Nam,704,84
    233,VG,VGB,VIRGIN ISLANDS, BRITISH,维尔京群岛,英国,Virgin Islands, British,92,1284
    234,VI,VIR,VIRGIN ISLANDS, U.S.,维尔京群岛,美国,Virgin Islands, U.s.,850,1340
    235,WF,WLF,WALLIS AND FUTUNA,瓦利斯群岛和富图纳群岛,Wallis and Futuna,876,681
    236,EH,ESH,WESTERN SAHARA,西撒哈拉,Western Sahara,732,212
    237,YE,YEM,YEMEN,也门,Yemen,887,967
    238,ZM,ZMB,ZAMBIA,赞比亚,Zambia,894,260
    239,ZW,ZWE,ZIMBABWE,津巴布韦,Zimbabwe,716,263

    三、参考信息

    1. https://www.iban.com/currency-codes
    2. https://country-code.cl/
    ]]>
    + + 基础数据 + + + 基础数据 + +
    + + IDEA 貌似注解失效 - 报错:cannot resolve method xxx + /20170301-IDEA-%E8%B2%8C%E4%BC%BC%E6%B3%A8%E8%A7%A3%E5%A4%B1%E6%95%88-%E6%8A%A5%E9%94%99%EF%BC%9Acannot-resolve-method-xxx/ + 在java平台上lombok提供了简单的注解的形式

    来帮助我们消除一些必须有但看起来很臃肿的代码

    比如属性的get/set,及对象的toString等方法,特别是相对于 POJO;


    出现问题

    1. 各种log方法,set方法中红色波浪线
    2. 提示:cannot resolve method xxx
    3. 虽然出现错误但是编译是可以通过的

    问题原因

    原来的代码用了lombok简单注解
    比如maven的pom.xml文件有如下配置

    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.8</version>
    </dependency>

    解决办法

    安装lombok plugin


    装完插件之后就舒服了,也不报错

    ]]>
    + + java + + + java + idea + +
    + + 字节码增强 - 初识链路追踪 - ByteBuddy + /20230226-Bytecode_enhancement_part_1/ + 1. 简单介绍

    1.1 背景

    • 不能通过 -javaagent 方式启动
    • 需要增强非业务代码( Spring AOP 不够用)
    • 业务方尽量少改动代码

    1.2 效果

    [Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@87f383f on sun.instrument.InstrumentationImpl@4eb7f003
    [Byte Buddy] INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@87f383f on sun.instrument.InstrumentationImpl@4eb7f003
    [Byte Buddy] TRANSFORM com.hisen.agent.util.Hisen [sun.misc.Launcher$AppClassLoader@18b4aac2, null, Thread[main,5,main], loaded=false]
    name before:hisen
    hello: hisen1677417020466
    public static void com.hisen.agent.util.Hisen.hello(java.lang.String): took 0 millisecond
    HisenAdvice exit. time use:0

    2. 代码逻辑

    2.1 启动类

    /**
    * @author hisenyuan
    * @date 2023/2/6 21:31
    */
    public class Start {
    public static void main(String[] args) {
    startTraceAgent();
    Hisen.hello("hisen");
    }

    private static void startTraceAgent() {
    Instrumentation install = ByteBuddyAgent.install();
    HisenInterceptor.init(install);
    new HisenInstrumentation().init(install);
    }
    }

    2.2 拦截方式

    会改变程序执行的堆栈

    /**
    * @author hisenyuan
    * @date 2023/2/8 21:17
    */
    public class HisenInterceptor {
    public static void init(Instrumentation instrumentation) {
    new AgentBuilder.Default()
    .type(ElementMatchers.nameEndsWith("Hisen"))
    .transform((builder, type, classLoader, module, protectionDomain) -> builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(TimeInterceptor.class)))
    .installOn(instrumentation);
    }

    public static class TimeInterceptor {
    @RuntimeType
    public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable) throws Exception {
    long start = System.currentTimeMillis();
    try {
    return callable.call();
    } catch (Exception e) {
    // 进行异常信息上报
    System.out.println("方法执行发生异常" + e.getMessage());
    throw e;
    } finally {
    System.out.println(method + ": took " + (System.currentTimeMillis() - start) + " millisecond");
    }
    }
    }
    }

    2.3 代码注入

    不会改变代码堆栈流
    这种方式也可以增强系统类

    /**
    * @author hisenyuan
    * @date 2023/2/8 20:16
    */
    public class HisenInstrumentation {
    public void init(Instrumentation instrumentation) {
    new AgentBuilder.Default()
    .disableClassFormatChanges()
    .with(RETRANSFORMATION)
    // Make sure we see helpful logs
    .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
    .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
    .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
    .ignore(none())
    // Ignore Byte Buddy and JDK classes we are not interested in
    .ignore(
    nameStartsWith("net.bytebuddy.")
    .or(nameStartsWith("jdk.internal.reflect."))
    .or(nameStartsWith("java.lang.invoke."))
    .or(nameStartsWith("com.sun.proxy."))
    )
    .disableClassFormatChanges()
    .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
    .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
    .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
    .type(nameEndsWith("Hisen"))
    .transform(
    (builder, type, classLoader, transformer, module) ->
    builder.visit(Advice.to(HisenAdvice.class).on(isMethod().and(named("hello")))))
    .installOn(instrumentation);
    }

    public static class HisenAdvice {
    @Advice.OnMethodEnter
    static long enter(@Advice.Argument(value = 0, typing = DYNAMIC, readOnly = false) String name) {
    System.out.println("name before:" + name);
    // change name
    name += System.currentTimeMillis();
    return System.currentTimeMillis();
    }

    @Advice.OnMethodExit(onThrowable = RuntimeException.class)
    static void exit(
    @Advice.Enter
    long startTime) {
    System.out.println("HisenAdvice exit. time use:" + (System.currentTimeMillis() - startTime));
    }
    }
    }

    ]]>
    + + java + + + java + +
    + + Hexo新玩法 - Atom + platformio-ide-terminal + /20180716-Hexo%E6%96%B0%E7%8E%A9%E6%B3%95%20-%20Atom%20+%20platformio-ide-terminal/ + 最近在慢慢适应mbp

    然后就在研究怎么方便的写博客

    首先就是找了几款markdown编辑器,发现GitHub出品的Atom还不错

    插件很丰富,然而下载了第一个terminal插件但是无效,一度折腾了很久

    后来搜索了一下,发现这个插件很不错:platformio-ide-terminal

    利用快捷键:control + 点(1左边那个)

    就可以在当前界面呼出终端,而且是当前目录

    也就是在_post目录下,写完了之后执行命令就上传了

    还是很方便的。后续还需要慢慢熟悉更多的插件与工具

    ]]>
    + + hexo + + + hexo + +
    + + IDEA Denpendencies红色波浪线报错,所有的包无法导入 - 一种解决办法 + /20170228-IDEA-Denpendencies%E7%BA%A2%E8%89%B2%E6%B3%A2%E6%B5%AA%E7%BA%BF%E6%8A%A5%E9%94%99-%E6%89%80%E6%9C%89%E7%9A%84%E5%8C%85%E6%97%A0%E6%B3%95%E5%AF%BC%E5%85%A5-%E4%B8%80%E7%A7%8D%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/ +
    failed to read artifact descriptor for xxx:jar

    一下午那代码里面是各种报错

    凡是引入的大部分都报错

    原因就是maven管理的jar没有添加上依赖

    最后在stackoverflow找到了良药

    上面有图片,错误会详细一点,如果你的也相同,可以试一试

    maven project -> Execute Maven Goal -> mvn -U clean install

    执行以上命令之后等待完成,应该就好了

    参考自stackoverflow:点击查看

    ]]>
    + + java + + + java + idea + Maven + +
    + + IDEA修改全局设置,如maven等 + /20170215-IDEA%E4%BF%AE%E6%94%B9%E5%85%A8%E5%B1%80%E8%AE%BE%E7%BD%AE%EF%BC%8C%E5%A6%82maven%E7%AD%89/ + 这几天在折腾maven项目

    我发现居然每次新open一个项目就得配置下maven

    因为默认的maven配置文件不行,我自定义的文件用的是阿里云的镜像

    那样快一点,于是很郁闷,决定要搞定他!!!

    结果这样设置就可以了,全局设置。

    File--->Other Setting--->Default Setting

    接下来的设置就是一样的了,各种设置都可以,只要你想全局生效

    ]]>
    + + 软件 + + + idea + +
    + + IDEA智能补全快捷键更改为Ctrl + 逗号 + /20170328-IDEA%E6%99%BA%E8%83%BD%E8%A1%A5%E5%85%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%9B%B4%E6%94%B9%E4%B8%BACtrl%20+%20%E9%80%97%E5%8F%B7/ + 一直就听说idea的智能补全很厉害,但是Ctrl + 空格 被万恶的输入法给占用了
    网上搜也不是很清晰,这里就写个博客记录一下

    快捷键设置:file->setting->Keymap->Main menu->Code->Completion->Basic
    找到之后右键Add keyboard Shortcut,然后按下:Ctrl + 逗号

    ]]>
    + + idea + + + idea + +
    + + IDEA自定义注释模板(javadoc) - Jindent插件 - 安装&设置 + /20170214-IDEA%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E9%87%8A%E6%A8%A1%E6%9D%BF-javadoc-Jindent%E6%8F%92%E4%BB%B6-%E5%AE%89%E8%A3%85-%E8%AE%BE%E7%BD%AE/ + 在线安装

    1. File—>Settings—>Plugin—>Browse repositories…
    2. 搜索框输入:jindent
    3. Jindent - Source Code Formatter 选中
    4. 右边 install
    5. 等待安装完成
    6. apply之后会让你重启idea
    ]]>
    + + 软件 + + + javadoc + +
    + + IDEA部署tomcat项目前台传输的json到后台乱码 - IDEA乱码解决 + /20170330-IDEA%E9%83%A8%E7%BD%B2tomcat%E9%A1%B9%E7%9B%AE%E5%89%8D%E5%8F%B0%E4%BC%A0%E8%BE%93%E7%9A%84json%E5%88%B0%E5%90%8E%E5%8F%B0%E4%B9%B1%E7%A0%81/ + idea涉及编码的地方都改了
    主要是编译时候的编码,tomcat的编码,以及idea配置里面的编码

    一、idea配置文件

    \HOME\IntelliJ IDEA 2016.3.4\bin\idea64.exe.vmoptions

    增加一行:-Dfile.encoding=UTF-8

    二、编译参数

    File -> Settings -> Build, Execution, Deployment
    -> Compiler -> Java Compiler -> Addition command line parameters

    在空格里面添加:-encoding utf-8

    三、工程编码

    File -> Settings -> Editor -> File Encodings

    此页面三个地方都选择UTF-8

    四、tomcat参数

    Run/debug Configuration tomcat

    VM options:-Dfile.encoding=UTF-8

    ]]>
    + + idea + + + java + idea + +
    + + IDEA设置intellij-java-google-style + /20170401-IDEA%E8%AE%BE%E7%BD%AEintellij-java-google-style/ + 一直想弄个格式化代码,后来发现很多人用谷歌的,于是也来整一份

    保存一份google code的xml,链接有最新的
    intellij-java-google-style.xml
    设置方法如下:Setting -> Editor -> Code Stytle -> Java

    最后一步就选择你存放之前保存的xml

    然后就大功告成,来个对比

    package com.hisen.json;

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;

    /**
    * Created by hisenyuan on 2017/3/23 at 18:02.
    */
    public class test {
    public static void main(String[] args) {String s = "{'A':'a'}";
    JSONObject obj= JSON.parseObject(s);
    System.out.println(obj.get("A"));
    }
    }


    package com.hisen.json;

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;

    /**
    * Created by hisenyuan on 2017/3/23 at 18:02.
    */
    public class test {

    public static void main(String[] args) {
    String s = "{'A':'a'}";
    JSONObject obj = JSON.parseObject(s);
    System.out.println(obj.get("A"));
    }
    }

    ]]>
    + + idea + + + intellij-java-google-style + +
    + + IntelliJ IDEA 调试窗口、帮助窗口、搜索框、底部消息等中文乱码问题解决 + /20170825-IntelliJ%20IDEA%20%E8%B0%83%E8%AF%95%E7%AA%97%E5%8F%A3%E3%80%81%E5%B8%AE%E5%8A%A9%E7%AA%97%E5%8F%A3%E3%80%81%E6%90%9C%E7%B4%A2%E6%A1%86%E3%80%81%E5%BA%95%E9%83%A8%E6%B6%88%E6%81%AF%E7%AD%89%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ + IntelliJ IDEA 调试窗口、帮助窗口、搜索框、底部消息等中文乱码

    在使用的过程中发现,搜索框历史、提交svn后的消息提示乱码

    最后发现是由于更改了idea界面的字体,字体对中文支持不佳导致

    解决办法:更改为支持中文的字体(比如:微软雅黑 Microsoft YaHei)

    设置路径:Settings -> Appearance & Behavior -> UI Options -> override default fonts by

    选择何时的字体即可,也可以把勾勾去掉,使用默认的。

    ]]>
    + + idea + + + 乱码 + +
    + + idea列编辑:IntelliJ IDEA:toggle block selection mode + /20171024-IntelliJ%20IDEA%EF%BC%9Atoggle%20block%20selection%20mode/ + 在eclipse中有列编辑模式:toggle block selection mode

    在idea中也可以,而且还比较高级,哈哈

    idea -> 右上角 -> Edit -> Column Selection Mode -> 移动光标到你想要弄的行

    完事在重复一次,就可以退出列编辑模式

    ]]>
    + + idea + + + idea + +
    + + Intellij Idea 快捷键汇总&介绍 + /20170213-Intellij-Idea-%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%B1%87%E6%80%BB-%E4%BB%8B%E7%BB%8D/ + 最常用快捷键(1~18)

    1. Ctrl+E,可以显示最近编辑的文件列表
    2. Shift+Click可以关闭文件
    3. Ctrl+[或]可以跳到大括号的开头结尾
    4. Ctrl+Shift+Backspace可以跳转到上次编辑的地方
    5. Ctrl+F12,可以显示当前文件的结构
    6. Ctrl+F7可以查询当前元素在当前文件中的引用,然后按F3可以选择
    7. Ctrl+N,可以快速打开类
    8. Ctrl+Shift+N,可以快速打开文件
    9. Alt+Q可以看到当前方法的声明
    10. Ctrl+W可以选择单词继而语句继而行继而函数
    11. Alt+F1可以将正在编辑的元素在各个面板中定位
    12. Ctrl+P,可以显示参数信息
    13. Ctrl+Shift+Insert可以选择剪贴板内容并插入
    14. Alt+Insert可以生成构造器/Getter/Setter等
    15. Ctrl+Alt+V 可以引入变量。例如把括号内的SQL赋成一个变量
    16. Ctrl+Alt+T可以把代码包在一块内,例如try/catch
    17. Alt+Up and Alt+Down可在方法间快速移动
    18. 在一些地方按Alt+Enter可以得到一些Intention Action,例如将”==”改为”equals()”
    19. Ctrl+Shift+Alt+N可以快速打开符号
    20. Ctrl+Shift+Space在很多时候都能够给出Smart提示
    21. Alt+F3可以快速寻找
    22. Ctrl+/和Ctrl+Shift+/可以注释代码
    23. Ctrl+Alt+B可以跳转到抽象方法的实现
    24. Ctrl+O可以选择父类的方法进行重写
    25. Ctrl+Q可以看JavaDoc
    26. Ctrl+Alt+Space是类名自动完成
    27. 快速打开类/文件/符号时,可以使用通配符,也可以使用缩写
    28. Live Templates! Ctrl+J
    29. Ctrl+Shift+F7可以高亮当前元素在当前文件中的使用
    30. Ctrl+Alt+Up /Ctrl+Alt+Down可以快速跳转搜索结果
    31. Ctrl+Shift+J可以整合两行
    32. Alt+F8是计算变量值

    Alt+回车 导入包,自动修正

    Ctrl+N 查找类

    Ctrl+Shift+N 查找文件

    Ctrl+Alt+L 格式化代码

    Ctrl+Alt+O 优化导入的类和包

    Alt+Insert 生成代码(如get,set方法,构造函数等)

    Ctrl+E或者Alt+Shift+C 最近更改的代码

    Ctrl+R 替换文本

    Ctrl+F 查找文本

    Ctrl+Shift+Space 自动补全代码

    Ctrl+空格 代码提示

    Ctrl+Alt+Space 类名或接口名提示

    Ctrl+P 方法参数提示

    Ctrl+Shift+Alt+N 查找类中的方法或变量

    Alt+Shift+C 对比最近修改的代码

    Shift+F6 重构-重命名

    Ctrl+Shift+先上键

    Ctrl+X 删除行

    Ctrl+D 复制行

    Ctrl+/ 或 Ctrl+Shift+/ 注释(// 或者// )

    Ctrl+J 自动代码

    Ctrl+E 最近打开的文件

    Ctrl+H 显示类结构图

    Ctrl+Q 显示注释文档

    Alt+F1 查找代码所在位置

    Alt+1 快速打开或隐藏工程面板

    Ctrl+Alt+ left/right 返回至上次浏览的位置

    Alt+ left/right 切换代码视图

    Alt+ Up/Down 在方法间快速移动定位

    Ctrl+Shift+Up/Down 代码向上/下移动。

    F2 或Shift+F2 高亮错误或警告快速定位

    代码标签输入完成后,按Tab,生成代码。

    选中文本,按Ctrl+Shift+F7 ,高亮显示所有该文本,按Esc高亮消失。

    Ctrl+W 选中代码,连续按会有其他效果

    选中文本,按Alt+F3 ,逐个往下查找相同文本,并高亮显示。

    Ctrl+Up/Down 光标跳转到第一行或最后一行下

    Ctrl+B 快速打开光标处的类或方法

    ]]>
    + + 软件 + + + java + idea + +
    + + Intellij IDEA中使用MyBatis-generator 自动生成MyBatis代码 + /20170322-Intellij%20IDEA%E4%B8%AD%E4%BD%BF%E7%94%A8MyBatis-generator%20%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90MyBatis%E4%BB%A3%E7%A0%81/ + MyBatis Generator是一个非常方便的代码生成工具,

    可以根据数据库中表结构自动生成CRUD代码,可以满足大部分需求。

    MyBatis Generator (MBG) 是一个Mybatis的代码生成器 ,

    可以根据数据库中表结构自动生成简单的CRUD(插入,查询,更新,删除)操作。

    但联合查询和存储过程,需手动手写SQL和对象。

    PS:配置过程中请注意自己的工程目录结构

    一、pom.xml添加插件

    <plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.2</version>
    <dependencies>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.5</version>
    </dependency>
    </dependencies>
    <configuration>
    <overwrite>true</overwrite>
    </configuration>
    </plugin>

    二、配置generatorConfig.xml

    resources下建generatorConfig.xml,作为mybatis-generator-maven-plugin插件的执行目标。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
    PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
    "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

    <generatorConfiguration>

    <context id="mysqlgenerator" targetRuntime="MyBatis3">
    <!--数据库连接信息-->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
    connectionURL="jdbc:mysql://127.0.0.1:3306/ssm"
    userId="root"
    password="hisen" />
    <!--代码相关路径和包-->
    <javaModelGenerator targetPackage="com.hisen.mybatis.model" targetProject="src/main/java" />

    <sqlMapGenerator targetPackage="com.hisen.mybatis.mapper" targetProject="src/main/resources" />

    <javaClientGenerator type="XMLMAPPER" targetPackage="com.hisen.mybatis.mapper" targetProject="src/main/java" />
    <!--表名-->
    <table tableName="appointment"/>
    <table tableName="book"/>

    </context>

    </generatorConfiguration>

    三、Intellij配置

    MyBatis Generator生成代码的运行方式:命令行、使用Ant、使用Maven、Java编码。

    本文采用Maven插件mybatis-generator-maven-plugin来运行MyBatis Generator,用的是命令行的方式。

    配置插件

    选择目录,输入命令:mybatis-generator:generate -e

    找到插件。双击执行

    即可看到生成的文件

    ]]>
    + + java + + + idea + mybatis + +
    + + JWT 丨 JSON Web Tokens 丨 java-jwt + /20170817-JWT%20%E4%B8%A8%20JSON%20Web%20Tokens%20%E4%B8%A8%20java-jwt/ + JWT简介

    JWT(json web token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。

    JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。

    JWT生成的token

    欲加密的字符:hisen
    加密后的字符:(分三段)

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjoiXCJoaXNlblwiIiwiZXhwIjoxNTAyOTY0Mjk2fQ.gzg4JEm8Z-GoU9eNaNll9I1wQQ0cEAbZC9OBUjAAQqI

    JWT的构成

    完整的头部,json格式:

    1. 声明类型,这里是jwt
    2. 声明加密的算法 通常直接使用 HMAC SHA256
      {
      'typ': 'JWT',
      'alg': 'HS256'
      }

    然后对头部进行base64加密,构成第一段:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

    playload

    载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

    1. 标准中注册的声明
    2. 公共的声明
    3. 私有的声明
      标准中注册的声明 (建议但不强制使用)
    4. iss: jwt签发者
    5. sub: jwt所面向的用户
    6. aud: 接收jwt的一方
    7. exp: jwt的过期时间,这个过期时间必须要大于签发时间
    8. nbf: 定义在什么时间之前,该jwt都是不可用的.
    9. iat: jwt的签发时间
    10. jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
      公共的声明
      公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
      私有的声明
      私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
      定义一个payload
      {
      "payload": "hisen"
      }

    然后对头部进行base64加密,构成第二段:

    eyJwYXlsb2FkIjoiXCJoaXNlblwiIiwiZXhwIjoxNTAyOTY0Mjk2fQ

    signature

    jwt的第三部分是一个签证信息,这个签证信息由三部分组成

    1. header (base64后的)
    2. payload (base64后的)
    3. secret
      这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三段:
      gzg4JEm8Z-GoU9eNaNll9I1wQQ0cEAbZC9OBUjAAQqI

    密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,所以需要保护好。

    java实现方式

    maven依赖

    <dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</version>
    </dependency>

    生成token代码(加密)

    // 加密,传入一个对象和有效期
    public static <T> String sign(T object, long maxAge)
    throws UnsupportedEncodingException {
    Map<String, Object> map = new HashMap<String, Object>();
    String jsonString = JSON.toJSONString(object);
    map.put("alg", "HS256");
    map.put("typ", "JWT");
    long exp = System.currentTimeMillis() + maxAge;
    String token = JWT.create()
    .withHeader(map)//header
    .withClaim(PAYLOAD, jsonString)//存放的内容 json
    .withClaim(EXP, new DateTime(exp).toDate())//超时时间
    .sign(Algorithm.HMAC256(SECRET));//密钥
    return token;
    }

    解析token(解密)

    // 解密,传入一个加密后的token字符串和解密后的类型
    public static <T> T unsign(String token, Class<T> classT) throws UnsupportedEncodingException {
    JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
    DecodedJWT jwt = verifier.verify(token);
    Map<String, Claim> claims = jwt.getClaims();
    if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
    long tokenTime = claims.get(EXP).asDate().getTime();
    long now = new Date().getTime();
    // 判断令牌是否已经超时
    if (tokenTime > now) {
    String json = claims.get(PAYLOAD).asString();
    // 把json转回对象,返回
    return JSON.parseObject(json, classT);
    }
    }
    return null;
    }

    完整代码 & 测试代码

    基于JWT token认证 | JSON Web Tokens | java-jwt 3.2.0 | demo

    JWT总结

    1. 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
    2. payload部分,JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
    3. 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。它不需要在服务端保存会话信息, 所以它易于应用的扩展
    4. 点击登陆,如果帐号密码校验通过,给用户生成一个token返回,然后用户每次登陆都带上这个token即可(放在header?)

    JWT官方网站:https://jwt.io/

    ]]>
    + + java + + + java + jwt + +
    + + Java - 利用apache POI读取Excel(xls,xlsx,2003,2007) + /20180113-Java%20-%20%E5%88%A9%E7%94%A8apache%20POI%E8%AF%BB%E5%8F%96Excel%EF%BC%88xls,xlsx,2003,2007)/ + 一个简单的工具类,更多java小练习:https://github.com/hisenyuan/IDEAPractice

    一、添加依赖

    <!-- POI start -->
    <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.9</version>
    </dependency>
    <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.9</version>
    </dependency>
    <dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.2</version>
    </dependency>
    <!-- POI end -->

    二、测试类

    package com.hisen.jars.poi;

    import java.io.File;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.List;

    /**
    * @author hisenyuan
    * @time 2018/1/12 17:43
    * @description 测试读取
    */
    public class TestPoiExcelUtil {

    public static void main(String[] args) {
    File file = new File("C:\\work\\document\\银行信息.xlsx");
    try {
    // 每一个excelData为一行数据(存放在数组)
    List<String[]> excelData = POIExcelUtil.readExcel(file);
    for (String[] data:excelData) {
    System.out.println(Arrays.toString(data));
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    }

    三、工具类代码

    完整类:POIExcelUtil.java

    package com.hisen.jars.poi;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    import org.apache.log4j.Logger;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;

    /**
    * @author hisenyuan
    * @time 2018/1/12 16:35
    * @description 利用POI读取excel表格
    */
    public class POIExcelUtil {

    private static Logger logger = Logger.getLogger(POIExcelUtil.class);
    private final static String XLS = "xls";
    private final static String XLSX = "xlsx";

    public static List<String[]> readExcel(File file) throws IOException {
    // 检查文件
    checkFile(file);
    Workbook workBook = getWorkBook(file);
    // 返回对象,每行作为一个数组,放在集合返回
    ArrayList<String[]> rowList = new ArrayList<>();
    if (null != workBook) {
    for (int sheetNum = 0; sheetNum < workBook.getNumberOfSheets(); sheetNum++) {
    // 获得当前sheet工作表
    Sheet sheet = workBook.getSheetAt(sheetNum);
    if (sheet == null) {
    continue;
    }
    // 获得当前sheet的开始行
    int firstRowNum = sheet.getFirstRowNum();
    // 获得当前sheet的结束行
    int lastRowNum = sheet.getLastRowNum();
    // 循环所有行(第一行为标题)
    for (int rowNum = firstRowNum; rowNum < lastRowNum; rowNum++) {
    // 获得当前行
    Row row = sheet.getRow(rowNum);
    if (row == null) {
    continue;
    }
    // 获得当前行开始的列
    short firstCellNum = row.getFirstCellNum();
    // 获得当前行的列数
    int lastCellNum = row.getPhysicalNumberOfCells();
    String[] cells = new String[row.getPhysicalNumberOfCells()];
    // 循环当前行
    for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++) {
    Cell cell = row.getCell(cellNum);
    cells[cellNum] = getCellValue(cell);
    }
    rowList.add(cells);
    }
    }
    }
    return rowList;
    }
    }

    ]]>
    + + java + + + poi + +
    + + IDEA - Java.Lang.OutOfMemoryError: PermGen Space + /20170222-Java-Lang-OutOfMemoryError-PermGen-Space/ + Java.Lang.OutOfMemoryError: PermGen Space

    Tomcat只分配了非常小的PermGen内存,这里重新设置一下

    直接在配置tomcat的时候,在VM options填入:

    -XX:PermSize=97m -XX:MaxPermSize=256m

    ]]>
    + + java + + + idea + tomcat + +
    + + JMH使用案例-日期格式化工具性能对比 + /20210511-JMH-use-cases-date-format-performance/ + 一、背景

    最近在看代码,发现一个 Date 格式化为 String 的方法。

    public String dateFormatString() {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(date);
    }

    看到这个方法想到

    • 每次都 new 一个 format 会快么(非线程安全,得每次都 new)?
    • SimpleDateFormat 格式化快么?
    • “yyyy-MM-dd HH:mm:ss.SSS” 改成静态常量会不会快点?

    带着以上三个疑问,就想着做个对比测试。
    恰巧最近在 perfma 社区看 jvm 相关内容时,刷到了『性能调优必备利器之 JMH』

    优点:不用自己写相关统计代码,而且统计方式有多种

    二、结论

    性能从低到高

    • java.text.SimpleDateFormat
    • org.apache.commons.lang.time.DateFormatUtils
    • org.joda.time.DateTime 字符串静态常量影响
    • 是否静态常量几乎没有影响
    • 使用常量反而性能有所下降(???为何)
    Benchmark                                             Mode  Cnt        Score        Error  Units
    DateConvertTest.dateFormatString thrpt 10 867333.631 ± 9510.553 ops/s
    DateConvertTest.dateFormatStringApache thrpt 10 2158217.955 ± 89012.866 ops/s
    DateConvertTest.dateFormatStringApacheStaticPattern thrpt 10 2141167.550 ± 93715.889 ops/s
    DateConvertTest.dateFormatStringJoda thrpt 10 2802803.925 ± 121600.833 ops/s
    DateConvertTest.dateFormatStringJodaStaticPattern thrpt 10 2744918.391 ± 131925.235 ops/s

    说明:
    Error 列是空的,看 Score 和 Units 即可
    ops/s:一秒钟执行多少次操作

    三、代码

    3.1 引入相关 jar

    <dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.23</version>
    </dependency>
    <dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.23</version>
    </dependency>

    3.2 IDEA 安装 JMH 插件

    插件中心搜索:JMH
    安装量最高的那个就是

    3.3 编写代码

    import org.apache.commons.lang.time.DateFormatUtils;
    import org.joda.time.DateTime;
    import org.openjdk.jmh.annotations.Benchmark;
    import org.openjdk.jmh.annotations.Fork;
    import org.openjdk.jmh.annotations.Measurement;
    import org.openjdk.jmh.annotations.Scope;
    import org.openjdk.jmh.annotations.State;
    import org.openjdk.jmh.annotations.Warmup;

    import java.text.SimpleDateFormat;
    import java.util.Date;

    /**
    * @author hisenyuan
    * @date 2021/5/10 18:39
    */
    // 默认的 State,每个测试线程分配一个实例
    @State(Scope.Thread)
    // 如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。
    @Fork(1)
    // 预热的次数 3 次基准测试(不是执行三次方法)
    @Warmup(iterations = 3)
    // 基准执行次数 10 次(参数含义同上)
    @Measurement(iterations = 10)
    public class DateConvertTest {
    private final static Date date = new Date();
    private final static String PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";

    @Benchmark
    public String dateFormatString() {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(date);
    }

    @Benchmark
    public String dateFormatStringJoda() {
    return new DateTime(date).toString("yyyy-MM-dd HH:mm:ss.SSS");
    }

    @Benchmark
    public String dateFormatStringJodaStaticPattern() {
    return new DateTime(date).toString(PATTERN);
    }

    @Benchmark
    public String dateFormatStringApache() {
    return DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss.SSS");
    }

    @Benchmark
    public String dateFormatStringApacheStaticPattern() {
    return DateFormatUtils.format(date, PATTERN);
    }
    }

    3.4 执行

    安装了 JMH 插件之后,直接对着类名右键,选择运行即可。
    JMH 会默认执行当前类下面的所有的基准测试。

    四、执行结果

    ETA 00:10:50 代表整个测试需要近11分钟

    # JMH version: 1.23
    # VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
    # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
    # VM options: -Dfile.encoding=UTF-8
    # Warmup: 3 iterations, 10 s each
    # Measurement: 10 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Throughput, ops/time
    # Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatString

    # Run progress: 0.00% complete, ETA 00:10:50
    # Fork: 1 of 1
    # Warmup Iteration 1: 698627.190 ops/s
    # Warmup Iteration 2: 873835.360 ops/s
    # Warmup Iteration 3: 870972.646 ops/s
    Iteration 1: 875650.632 ops/s
    Iteration 2: 872407.599 ops/s
    Iteration 3: 864390.061 ops/s
    Iteration 4: 872456.756 ops/s
    Iteration 5: 870538.248 ops/s
    Iteration 6: 861600.599 ops/s
    Iteration 7: 868469.312 ops/s
    Iteration 8: 868930.572 ops/s
    Iteration 9: 864686.616 ops/s
    Iteration 10: 854205.911 ops/s


    Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatString":
    867333.631 ±(99.9%) 9510.553 ops/s [Average]
    (min, avg, max) = (854205.911, 867333.631, 875650.632), stdev = 6290.642
    CI (99.9%): [857823.077, 876844.184] (assumes normal distribution)


    # JMH version: 1.23
    # VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
    # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
    # VM options: -Dfile.encoding=UTF-8
    # Warmup: 3 iterations, 10 s each
    # Measurement: 10 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Throughput, ops/time
    # Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApache

    # Run progress: 20.00% complete, ETA 00:08:42
    # Fork: 1 of 1
    # Warmup Iteration 1: 1922497.366 ops/s
    # Warmup Iteration 2: 2121425.254 ops/s
    # Warmup Iteration 3: 2142439.041 ops/s
    Iteration 1: 2174791.808 ops/s
    Iteration 2: 2101868.007 ops/s
    Iteration 3: 2174003.026 ops/s
    Iteration 4: 2202522.277 ops/s
    Iteration 5: 2039234.570 ops/s
    Iteration 6: 2105338.449 ops/s
    Iteration 7: 2205933.974 ops/s
    Iteration 8: 2175865.041 ops/s
    Iteration 9: 2167544.736 ops/s
    Iteration 10: 2235077.658 ops/s


    Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApache":
    2158217.955 ±(99.9%) 89012.866 ops/s [Average]
    (min, avg, max) = (2039234.570, 2158217.955, 2235077.658), stdev = 58876.499
    CI (99.9%): [2069205.089, 2247230.821] (assumes normal distribution)


    # JMH version: 1.23
    # VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
    # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
    # VM options: -Dfile.encoding=UTF-8
    # Warmup: 3 iterations, 10 s each
    # Measurement: 10 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Throughput, ops/time
    # Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApacheStaticPattern

    # Run progress: 40.00% complete, ETA 00:06:31
    # Fork: 1 of 1
    # Warmup Iteration 1: 1964208.975 ops/s
    # Warmup Iteration 2: 2138997.654 ops/s
    # Warmup Iteration 3: 2164194.409 ops/s
    Iteration 1: 2221565.433 ops/s
    Iteration 2: 2231776.725 ops/s
    Iteration 3: 2210494.794 ops/s
    Iteration 4: 2057487.199 ops/s
    Iteration 5: 2111366.133 ops/s
    Iteration 6: 2150355.985 ops/s
    Iteration 7: 2116673.562 ops/s
    Iteration 8: 2140376.847 ops/s
    Iteration 9: 2099801.246 ops/s
    Iteration 10: 2071777.579 ops/s


    Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringApacheStaticPattern":
    2141167.550 ±(99.9%) 93715.889 ops/s [Average]
    (min, avg, max) = (2057487.199, 2141167.550, 2231776.725), stdev = 61987.258
    CI (99.9%): [2047451.661, 2234883.440] (assumes normal distribution)


    # JMH version: 1.23
    # VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
    # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
    # VM options: -Dfile.encoding=UTF-8
    # Warmup: 3 iterations, 10 s each
    # Measurement: 10 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Throughput, ops/time
    # Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJoda

    # Run progress: 60.00% complete, ETA 00:04:21
    # Fork: 1 of 1
    # Warmup Iteration 1: 2767967.530 ops/s
    # Warmup Iteration 2: 2839342.269 ops/s
    # Warmup Iteration 3: 2824726.959 ops/s
    Iteration 1: 2859527.645 ops/s
    Iteration 2: 2799624.402 ops/s
    Iteration 3: 2876558.678 ops/s
    Iteration 4: 2862478.579 ops/s
    Iteration 5: 2882872.177 ops/s
    Iteration 6: 2860788.081 ops/s
    Iteration 7: 2742885.180 ops/s
    Iteration 8: 2779727.212 ops/s
    Iteration 9: 2719266.181 ops/s
    Iteration 10: 2644311.112 ops/s


    Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJoda":
    2802803.925 ±(99.9%) 121600.833 ops/s [Average]
    (min, avg, max) = (2644311.112, 2802803.925, 2882872.177), stdev = 80431.422
    CI (99.9%): [2681203.092, 2924404.757] (assumes normal distribution)


    # JMH version: 1.23
    # VM version: JDK 1.8.0_171, Java HotSpot(TM) 64-Bit Server VM, 25.171-b11
    # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
    # VM options: -Dfile.encoding=UTF-8
    # Warmup: 3 iterations, 10 s each
    # Measurement: 10 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 1 thread, will synchronize iterations
    # Benchmark mode: Throughput, ops/time
    # Benchmark: com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJodaStaticPattern

    # Run progress: 80.00% complete, ETA 00:02:10
    # Fork: 1 of 1
    # Warmup Iteration 1: 2711358.480 ops/s
    # Warmup Iteration 2: 2823432.725 ops/s
    # Warmup Iteration 3: 2745545.759 ops/s
    Iteration 1: 2752446.099 ops/s
    Iteration 2: 2792047.450 ops/s
    Iteration 3: 2828389.094 ops/s
    Iteration 4: 2799921.909 ops/s
    Iteration 5: 2670924.332 ops/s
    Iteration 6: 2533779.871 ops/s
    Iteration 7: 2733321.639 ops/s
    Iteration 8: 2738839.592 ops/s
    Iteration 9: 2802775.869 ops/s
    Iteration 10: 2796738.055 ops/s


    Result "com.jd.finance.jrpaypassword.performance.DateConvertTest.dateFormatStringJodaStaticPattern":
    2744918.391 ±(99.9%) 131925.235 ops/s [Average]
    (min, avg, max) = (2533779.871, 2744918.391, 2828389.094), stdev = 87260.375
    CI (99.9%): [2612993.156, 2876843.626] (assumes normal distribution)


    # Run complete. Total time: 00:10:52

    REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
    why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
    experiments, perform baseline and negative tests that provide experimental control, make sure
    the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
    Do not assume the numbers tell you what you want them to tell.

    Benchmark Mode Cnt Score Error Units
    DateConvertTest.dateFormatString thrpt 10 867333.631 ± 9510.553 ops/s
    DateConvertTest.dateFormatStringApache thrpt 10 2158217.955 ± 89012.866 ops/s
    DateConvertTest.dateFormatStringApacheStaticPattern thrpt 10 2141167.550 ± 93715.889 ops/s
    DateConvertTest.dateFormatStringJoda thrpt 10 2802803.925 ± 121600.833 ops/s
    DateConvertTest.dateFormatStringJodaStaticPattern thrpt 10 2744918.391 ± 131925.235 ops/s

    五、JMH 介绍

    ]]>
    + + java + + + java + +
    + + Java三种创建对象的方式 + /20170210-Java%E4%B8%89%E7%A7%8D%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%96%B9%E5%BC%8F/ + 有一个实现了Cloneable接口的Person类
    输出:

    <使用new关键字创建对象>
    hisen已成年
    体重为:50
    年龄为:23
    <使用newInstance()方法创建对象>
    体重为:0
    年龄为:0
    <使用clone()方法创建对象>
    hisen已成年
    体重为:50
    年龄为:23

    具体实现:

    package com.hisen.javaGaiShu.page73test5;

    /**
    * 三种方式创建对象
    *
    * @author hisenyuan 2017年2月10日 下午10:27:14
    */
    public class Person implements Cloneable {
    private String name;
    private int weight;
    private int age;

    public Person() {
    }

    public Person(String name, int weight, int age) {
    super();
    this.name = name;
    this.weight = weight;
    this.age = age;
    }

    public void young() {
    if (age >= 18 && age <= 100)
    System.out.println(name + "已成年");
    if (age > 0 && age < 18)
    System.out.println(name + "未成年");
    }

    @Override
    public String toString() {
    return "体重为:" + weight + "\n年龄为:" + age;
    }

    public static void main(String[] args) throws Exception {
    System.out.println("<使用new关键字创建对象>");
    Person p1 = new Person("hisen", 50, 23);
    p1.young();
    System.out.println(p1);

    System.out.println("<使用newInstance()方法创建对象>");
    Class c = Class.forName("com.hisen.javaGaiShu.page73test5.Person");
    Person p2 = (Person) c.newInstance();
    p2.young();
    System.out.println(p2);

    System.out.println("<使用clone()方法创建对象>");
    Person p3 = (Person) p1.clone();
    p3.young();
    System.out.println(p3);
    }

    }

    ]]>
    + + java + + + java + 对象 + +
    + + JAVA中几种常用的RPC框架 + /20170317-JAVA%E4%B8%AD%E5%87%A0%E7%A7%8D%E5%B8%B8%E7%94%A8%E7%9A%84RPC%E6%A1%86%E6%9E%B6/ + RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,

    作用是有助于系统的垂直拆分,使系统更易拓展。

    Java中的RPC框架比较多,各有特色,广泛使用的有RMI、Hessian、Dubbo等。

    RPC还有一个特点就是能够跨语言,本文只以JAVA语言里的RPC为例。

    其他的框架结构也类似,区别在于对象的序列化方法,传输对象的通讯协议,

    以及注册中心的管理与failover设计(利用zookeeper)。

    客户端和服务端可以运行在不同的JVM中,Client只需要引入接口,

    接口的实现以及运行时需要的数据都在Server端,RPC的主要依赖技术是序列化、反序列化和传输协议,

    JAVA里对应的就是对象的序列化、反序列化以及序列化后数据的传输。

    RMI的序列化和反序列化是JAVA自带的,Hessian里的序列化和反序列化是私有的,传输协议则是HTTP,

    Dubbo的序列化可以多种选择,一般使用Hessian的序列化协议,传输则是TCP协议,使用了高性能的NIO框架Netty。

    对于序列化,我还了解一些,像Google的ProBuffer、JBoss Marshalling和Apache Thrift等

    1.RMI

    rmi
    JAVA自带的远程方法调用工具,不过有一定的局限性,

    毕竟是JAVA语言最开始时的设计,后来很多框架的原理都基于RMI,RMI的使用如下:

    对外接口

    public interface IService extends Remote {  
    public String queryName(String no) throws RemoteException;
    }

    服务实现

    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;

    // 服务实现
    public class ServiceImpl extends UnicastRemoteObject implements IService {

    /**
    */
    private static final long serialVersionUID = 682805210518738166L;

    /**
    * @throws RemoteException
    */
    protected ServiceImpl() throws RemoteException {
    super();
    }

    /* (non-Javadoc)
    * @see com.suning.ebuy.wd.web.IService#queryName(java.lang.String)
    */
    @Override
    public String queryName(String no) throws RemoteException {
    // 方法的具体实现
    System.out.println("hello" + no);
    return String.valueOf(System.currentTimeMillis());
    }

    }

    RMI客户端


    import java.rmi.AccessException;
    import java.rmi.NotBoundException;
    import java.rmi.RemoteException;
    import java.rmi.registry.LocateRegistry;
    import java.rmi.registry.Registry;

    // RMI客户端
    public class Client {

    public static void main(String[] args) {
    // 注册管理器
    Registry registry = null;
    try {
    // 获取服务注册管理器
    registry = LocateRegistry.getRegistry("127.0.0.1",8088);
    // 列出所有注册的服务
    String[] list = registry.list();
    for(String s : list){
    System.out.println(s);
    }
    } catch (RemoteException e) {

    }
    try {
    // 根据命名获取服务
    IService server = (IService) registry.lookup("vince");
    // 调用远程方法
    String result = server.queryName("ha ha ha ha");
    // 输出调用结果
    System.out.println("result from remote : " + result);
    } catch (AccessException e) {

    } catch (RemoteException e) {

    } catch (NotBoundException e) {

    }
    }
    }

    RMI服务端


    import java.rmi.RemoteException;
    import java.rmi.registry.LocateRegistry;
    import java.rmi.registry.Registry;

    // RMI服务端
    public class Server {

    public static void main(String[] args) {
    // 注册管理器
    Registry registry = null;
    try {
    // 创建一个服务注册管理器
    registry = LocateRegistry.createRegistry(8088);

    } catch (RemoteException e) {

    }
    try {
    // 创建一个服务
    ServiceImpl server = new ServiceImpl();
    // 将服务绑定命名
    registry.rebind("vince", server);

    System.out.println("bind server");
    } catch (RemoteException e) {

    }
    }
    }

    服务注册管理器写在了Server里,当然也可以抽出来单独作为一个服务,

    在其他一些框架中,往往用Zookeeper充当注册管理角色。

    2.Hessian(基于HTTP的远程方法调用)

    Hessian
    基于HTTP协议传输,在性能方面还不够完美,负载均衡和失效转移依赖于应用的负载均衡器,

    Hessian的使用则与RMI类似,区别在于淡化了Registry的角色,通过显示的地址调用,

    利用HessianProxyFactory根据配置的地址create一个代理对象,另外还要引入Hessian的Jar包。

    3、Dubbo(淘宝开源的基于TCP的RPC框架)

    Dubbo

    基于Netty的高性能RPC框架,是阿里巴巴开源的,总体原理如下:

    在了解Dubbo之前,要先对Zookeeper有深入的理解,当理解了zookeeper后,Dubbo也就了无秘密了。

    Dubbo的详细说明在淘宝开源里说的非常详细,在工作中很多生产项目都用了Dubbo,过程中也发现了很多需要注意的地方.

    ]]>
    + + java + + + java + +
    + + Java GC - 理论与实践 - 附优化案例 + /20190429-Java%20GC%20-%20%E7%90%86%E8%AE%BA%E4%B8%8E%E5%AE%9E%E8%B7%B5%20-%20%E9%99%84%E4%BC%98%E5%8C%96%E6%A1%88%E4%BE%8B/ + 一、理论知识

    常见参数:

    1. -Xms 堆初始化 例如:-Xms256m
    2. -Xmx 堆最大值 例如:-Xmx512m
    3. -Xmn 堆新生代 例如:-Xmn100m
    4. -XX:NewRatio 新生代与老年代的比例
    5. -XX:SurvivorRatio 新生代区域比例,默认8,代表Eden:From Survivor:To Survivor = 8:1:1

    垃圾回收器:

    1. Serial/Serial Old 新生代/老年代,古老,单线程,暂停所有用户线程,复制算法/标记整理算法
    2. ParNew 1的多线程版本
    3. Parallel Scavenge 新生代,多线程,不需要暂停用户线程,复制算法
    4. Parallel Old 老年代,多线程,不需要暂停用户线程,标记整理算法
    5. CMS(Current Mark Sweep,详情)老年代,与ParNew配合使用,牺牲吞吐量获得最短停顿,标记整理算法
    6. G1 并行与并发收集器,可预测的停顿时间

    二、实践案例

    1. Full GC 之前进行 Minor GC 避免扫描过多的对象, 配置:CMSScavengeBeforeRemark
    2. Xms和Xmx设置为相同,这样可以减少内存自动扩容和收缩带来的性能损失
    3. JVM调优是最后的稻草,进行JVM调优之前应该先优化架构和代码
    4. 调优是一个复杂的过程,根据具体的场景结合对垃圾回收器的深入理解进行调优,才可能事半功倍。

    各个区大小比例建议

    # 活跃空间大小:Full GC后堆中老年代占用空间的大小
    空间 倍数
    总大小 3-4倍活跃空间大小
    新生代 1-1.5倍活跃空间大小
    老年代 2-3倍活跃空间大小

    三、参考连接

    1.1 https://www.cnblogs.com/honey01/p/9475726.html
    1.2 https://www.cnblogs.com/dolphin0520/p/3783345.html
    2.1 https://tech.meituan.com/2017/12/29/jvm-optimize.html

    ]]>
    + + java + + + java + +
    + + Java原生类库java.util.zip - 文件夹压缩与解压 + /20170426-Java%E5%8E%9F%E7%94%9F%E7%B1%BB%E5%BA%93java.util.zip%20-%20%E6%96%87%E4%BB%B6%E5%A4%B9%E5%8E%8B%E7%BC%A9%E4%B8%8E%E8%A7%A3%E5%8E%8B/ + 到处搜了一下也没有看到专门做好的jar包

    真实的目录结构如下:

    C:\1\hisenyuan\build.png
    C:\1\hisenyuan\DSCN6812.JPG
    C:\1\hisenyuan\test\test\hello.zip
    C:\1\hisenyuan\test\hisenyuan.zip
    C:\1\hisenyuan\test\test.txt
    C:\1\hisenyuan\test\test\hello\hello.txt
    C:\1\hisenyuan\tomcat.png

    压缩包的目录结构如下:

    build.png
    DSCN6812.JPG
    test\hello.zip
    test\hisenyuan.zip
    test\test.txt
    test\hello\hello.txt
    tomcat.png

    全部代码如下:

    package com.hisen.utils;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipFile;
    import java.util.zip.ZipInputStream;
    import java.util.zip.ZipOutputStream;
    import org.apache.commons.io.IOUtils;
    import org.junit.Test;

    /**
    * 文件夹压缩解压工具
    * Created by hisenyuan on 2017/4/20 at 17:27.
    */
    public class ZipOrUnZipFileUtil {

    private InputStream is;
    private ZipOutputStream zos;
    private int lastIndexOf;

    @Test
    public void testZipOrUnZipFile() {
    //分隔符,windows linux下有所不同
    String separator = File.separator;
    //想要压缩的文件所在目录 C:\1\hisenyuan
    String folderPath = "c:" + separator + "1" + separator + "hisenyuan";
    //压缩文件路径:C:\1\hisenyuan\hisenyuan.zip
    String zipFilePath = "c:" + separator + "1" + separator + "hisenyuan" + ".zip";
    //解压文件所在的目录 E:\file\hisenyuan
    String newPath = "e:" + separator + "file" + separator + "hisenyuan";

    unZipFile(zipFilePath, newPath);
    zipFile(folderPath);
    }


    /**
    * 压缩文件
    *
    * @param filePath 压缩文件夹的路径
    */
    private void zipFile(String filePath) {
    File file = new File(filePath);
    File zipFile = new File(filePath + ".zip");
    lastIndexOf = file.getAbsolutePath().length() + 1;
    try {
    zos = new ZipOutputStream(new FileOutputStream(zipFile));
    zos.setComment("log");
    long start = System.currentTimeMillis();
    listAllFile(filePath);
    long stop = System.currentTimeMillis();
    System.out.println("zip done,time:" + (stop - start) / 1000 + "s");
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } finally {
    IOUtils.closeQuietly(zos, is);
    }
    }

    /**
    * 循环遍历当前文件夹下的所有文件,使用递归
    */
    private void listAllFile(String filePath) {
    File file = new File(filePath);
    if (file.exists()) {
    File[] files = file.listFiles();
    if (files == null) {
    System.out.println("folder is null");
    } else {
    for (File file2 : files) {
    if (file2.isDirectory()) {
    listAllFile(file2.getAbsolutePath());
    } else {
    String file3 = file2.getAbsolutePath();
    try {
    is = new FileInputStream(file3);
    //在zip压缩包当中出现的文件名
    String name = file3.substring(lastIndexOf, file3.length());
    System.out.println("name:" + name);
    zos.putNextEntry(new ZipEntry(name));
    int temp;
    int bufferSize = 1024 * 5;
    byte[] buffer = new byte[bufferSize];
    while ((temp = is.read(buffer, 0, bufferSize)) != -1) {
    zos.write(buffer, 0, temp);
    zos.flush();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }
    }

    /**
    * 解压文件
    *
    * @param filePath 压缩文件所在目录
    * @param newPath 想解压到那个目录
    */
    private void unZipFile(String filePath, String newPath) {
    //压缩文件所在的父目录
    String oldPath = new File(filePath).getParentFile().toString();
    File outFile;
    ZipInputStream zipInputStream = null;
    OutputStream outputStream = null;
    InputStream inputStream = null;
    ZipEntry zipEntry;
    try {
    ZipFile zipFile = new ZipFile(filePath);

    zipInputStream = new ZipInputStream(new FileInputStream(filePath));
    while (null != (zipEntry = zipInputStream.getNextEntry())) {
    System.out.println("解压缩" + zipEntry.getName() + "文件。");
    //newPath为空就代表解压在当前目录
    if ("".equals(newPath) || newPath.isEmpty()) {
    outFile = new File(oldPath + zipEntry.getName());
    } else {
    //防止传入的目录不存在
    File file = new File(newPath);
    if (!file.exists()) {
    file.mkdir();
    }
    outFile = new File(newPath + File.separator + zipEntry.getName());
    }
    //判断当前文件路径是否存在,不存在就创建
    buildFile(outFile);
    inputStream = zipFile.getInputStream(zipEntry);
    outputStream = new FileOutputStream(outFile);
    int temp;
    int bufferSize = 1024 * 5;
    byte[] buffer = new byte[bufferSize];
    while ((temp = inputStream.read(buffer, 0, bufferSize)) != -1) {
    outputStream.write(buffer, 0, temp);
    }
    }
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    IOUtils.closeQuietly(inputStream, outputStream, zipInputStream);
    }
    }

    /**
    * 判断文件是否存在,不存在创建
    */
    private static void buildFile(File file) {
    if (!file.exists()) {
    File parent = file.getParentFile();
    if (parent != null && !parent.exists()) {
    parent.mkdirs();
    }
    try {
    file.createNewFile();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }

    ]]>
    + + java + + + java + 解压 + +
    + + Java获取股票信息 - java获取股票信息接口 + /20170427-Java%E8%8E%B7%E5%8F%96%E8%82%A1%E7%A5%A8%E4%BF%A1%E6%81%AF%20-%20java%E8%8E%B7%E5%8F%96%E8%82%A1%E7%A5%A8%E4%BF%A1%E6%81%AF%E6%8E%A5%E5%8F%A3/ + 用下面的接口获取股票的数据

    sh:代表上海市场

    sz:代表深圳市场

    后面是加上股票代码,这是因为上海和深圳的股票代码有重复的

    http://hq.sinajs.cn/list=sh600877

    返回的信息

    var hq_str_sh600877="中国嘉陵,6.340,6.400,6.360,6.470,6.210,6.340,6.350,15012913,95227966.000,56500,6.340,12100,6.330,16100,6.320,17500,6.310,47400,6.300,13600,6.350,11300,6.360,32400,6.370,39100,6.380,41200,6.390,2017-04-27,15:00:00,00";

    有效信息为引号里面的数据

    下面的数字代表分割数组后所在的下标

    下面是数据字段对应的含义

    源代码:GetStockInformation.java

    表格如下:

    位置含义测试数据
    0股票名字中国嘉陵
    1今日开盘价6.340
    2昨日收盘价6.400
    3当前价格6.360
    4今日最高价6.470
    5今日最低价6.210
    6买一报价6.340
    7卖一报价6.350
    8成交数量(百股)15012913
    9成交金额(元)95227966.000
    10买一数量(股)56500
    11买一报价6.340
    12买二数量(股)12100
    13买二报价6.330
    14买三数量(股)16100
    15买三报价6.320
    16买四数量(股)17500
    17买四报价6.310
    18买五数量(股)47400
    19买五报价6.300
    20卖一数量(股)13600
    21卖一报价6.350
    22卖二数量(股)11300
    23卖二报价6.360
    24卖三数量(股)32400
    25卖三报价6.370
    26卖四数量(股)39100
    27卖四报价6.380
    28卖五数量(股)41200
    29卖五报价6.390
    30当前日期2017-04-27
    31当前时间15:00:00
    32未知00
    ]]>
    + + java + + + java + +
    + + Java开发的一点思考 + /20170226-Java%E5%BC%80%E5%8F%91%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83/ + 目前的形式来说java后台市场还是挺大,虽然也有很多python和php的系统。

    要想从事企业级的项目开发,你必须掌握如下要点:

    1. 掌握项目开发的基本步骤
    2. 具备极强的面向对象的分析与设计技巧
    3. 掌握用例驱动、以架构为核心的主流开发方法

    没有人愿意自己一辈子就满足于掌握了一些代码实现的技巧,

    别人告诉你要实现什么,你就用代码堆砌来实现别人的要求!

    你必须学会从整个项目的角度去思考!

    你必须学会假如你是项目经理,你该如何思考!

    你必须学会假如你是架构师,你该如何思考!

    你必须掌握针对某个特定问题领域的分析方法!

    关于基础知识:

    JavaSE

    • 基本语法、数据类型、操作符等:int、long、Integer、Long、if、else、for、while
    • 面向对象:class(类)、Object(对象)、instance(实例)、state(状态)、behavior(行为)、field、method、new、可见性(访问控制)、attribute、property、package、import、static variable、class variable、instance variable、heap、method area、stack、GC(垃圾回收)、override、overload、对象转型(casting)、多态、this、super
    • 异常处理:理解编译期错误和运行期错误的区别、Exception、RuntimeException、checked exception、unchecked exception、try、catch、finally、throw new XXXException、throws XXXException、异常处理的基本原则
    • 数组与集合:数组的定义和使用方法、Collection、List、Set、Map、ArrayList、HashSet、HashMap、Iterator、equals、hashCode、Comparable、Comparator
    • 常用类:String、intern、常量池、StringBuffer、java.util.Date、SimpleDateFormat、Regex(正则表达式)
    • 反射机制:Class、Method、Field、invoke、newInstance、BeanUtils(apache-commons)、PropertyUtils(apache-commons)
    • 输入输出流:InputStream、OutputStream、Reader、Writer、Adapter设计模式与原始流类、Decorator设计模式与包装流类、对象序列化和反序列化
    • 多线程:Thread、Runnable、sleep、wait、notify、synchronized、lock

    Servlet和JSP

    • HttpServlet、doGet、doPost、HttpServletRequest、HttpServletResponse、request.getParameter()、request.setAttribute()、request.getAttribute()、request.getSession()、ServletContext、Filter、web.xml、tomcat、forward与redirect、http协议的无状态性、cookie、JSP Scope Object、<c:out …/>、<c:forEach …>

    • HTML与JavaScript:你需要能够理解常见的网页标签、理解在网页中引入JavaScript的方法、以及JavaScript的基本语法与使用方法

    以上,就是你进一步学习Java所必备的基本知识。

    特别是一些个专业术语和名词,看到这些名词,如果你像看到亲爹一样亲切,

    那么说明你对Java的基础知识就很熟悉了,记住,仅仅是熟悉了

    接下来是SSH

    对于初学者来说,这三大框架被赋予了太多神秘的色彩,似乎它们是重中之重的知识!但是对于拥有多年Java开发经验的专业技术人员来说,对于那些Java牛人来说,却对这三大框架不太感冒!难道它们不重要吗?

    现在很多企业都在用这三大框架,所以很多企业也把掌握这三大框架作为招聘的必备条件。不可否认的是,也有很多大型企业没有用这三大框架,这些企业经过多年发展,自身已经有一定的技术积累,也形成了自己独特的技术框架体系。这三大框架既可以说很重要,也可以说不重要。

    说重要的原因在于:这三大框架对JavaEE开发中所存在的普遍的问题,提供了优美的解决方案,它们蕴含了这个行业中最NB的开发人员的努力和想法,所以,学习这三大框架,你就可以窥探到这些处于技术巅峰的牛人们究竟对一个问题是怎么想的,通过一种什么样的设计思路去解决问题的。所以,对于你来说,你没有太多项目开发的经验,经验是什么?经验就是你知道可能会遇到哪些问题,针对哪个问题可以有哪些解决方法,在某个情景下,哪种解决方法是较好的,哪种方法不太好等等!如果你没做过什么项目,你根本就不会去意识到你可能会遇到哪些问题,而这些问题往往又是非常关键的!解决得不好,会影响到你的程序的稳定性、可扩展性等等!三大框架就给初学者提供了了解你以后可能会遇到哪些问题,以及针对这些问题的解决方案!

    当你了解了这三大框架为什么是重要的,那么你也就能理解,为什么这三大框架也可以说是不重要的。如果你曾经开发过很多项目,你碰到了各种各样的问题,凭着你的技术功底,逐个击破了这些问题,在这些人眼里,三大框架(是不是还有N个框架?呵呵)都是浮云!

    你属于哪一种人呢?如果你没有太多项目开发经验,那么三大框架对于你来说就是非常重要的!而且,由此你也知道了该怎么去学这三大框架。对于三大框架的学习而言,着力点在于给你展示问题,并触发你自己主动的思考,我们鼓励你提出自己的想法,也许你的想法很白痴,但那毕竟是你自己的想法,如果你不知道牛人的想法,那你怎么知道自己的想法是很白痴的呢?在这种思想的碰撞过程中,你就会逐渐提高自己!所以,三大框架学完之后,你不应该只是看到一大堆配置文件,你不应该只是看到了一些Action,一些Service,一些映射文件,你不应该只知道session.save/update/delete,你不应该只是知道struts2中有一堆interceptor,你不应该只是看到一堆jar包……

    如果你只是知道拷贝一堆jar包,定义一系列配置文件之后,SSH三大框架就能够运行起来了,也可以给你干活了,那么,很悲哀的是,你仍然没有掌握三大框架的精粹!请你回答以下问题:


    Struts2

    • 为什么每次请求都要创建一个Action对象?
    • ModelDriven拦截器的配置中refreshModelBeforeResult解决了什么问题?
    • 为什么在web.xml中配置的StrutsPrepareAndExecuteFilter要过滤所有的请求?
    • 请你给我谈谈ValueStack?
    • Struts2是如何实现MVC设计模式的?

    Spring

    • 你为什么要用Spring?
    • 请你聊一聊IOC/DI?
    • 什么是声明式的事务管理?为什么要使用声明式的事务管理?Spring如何实现声明式的事务管理?
    • 把spring和hibernate集成,定义事务管理特性的时候,为何要将除了添加、删除、更新操作之外的方法,即主要执行查询任务的方法定义为read-only?

    Hibernate

    • 请你谈谈你对ORM映射的理解?
    • 很多人说Hibernate不适合大项目,性能有问题,你是如何理解的?
    • Hibernate为什么一定要定义一个数据库标识?
    • 为什么Hibernate建议你的实体类实现hashCode和equals方法?
    • 谈谈你对Hibernate实体类中的数据库标识与数据库主键之间关系的认识?
    • 谈谈你对Hibernate关联映射与数据库外键之间关系的认识?
    • 调用session.save()方法,hibernate一定会发出insert语句吗?谈谈你的理解
    • 调用session.update()方法,hibernate一定会发出update语句吗?谈谈你的理解
    • 请你聊一下以下名词、概念或用法:lazy、lazy=”extra”、inverse、fetch、fetch=”join”、fetch=”subselect”、batch-size
    • 配置了lazy=”true”一定会实现懒加载吗?
    • 请你谈谈Hibernate中的“N+1”问题
    • 请你谈谈Hibernate有哪些最佳实践?

    以上并非SSH中全部重点的问题,但它们能考察你能否灵活运用SSH框架!如果你能深刻理解这些问题,再配以合适的实战项目训练,你也会逐渐成为牛人!

    最后是项目开发能力 ##:

    不管你是学Java还是别的技术,你的根本目的在于给客户创造价值!否则,你下大力气学习的东西,随着技术的进步和更新,很快就会过时!所以,技术的核心在于用技术创造有价值的成果!也就是说,客户需要什么,你就要用技术把客户需要的东西给他造出来!一个公司之所以要用各种福利条件极力挽留你,是因为你能够给公司带来极高的利益!那么,你有什么可以给公司利用的呢?公司最看重你的哪方面的能力呢?

    做项目需要的能力很多,其中最核心最基础的就是建模能力(现在最主流的就是面向对象建模!)。什么是建模能力呢?


    我给大家一个面试题
    一个保险公司的保险卡管理模块:销售人员领取保险卡信息(保险卡数量、卡号、领取日期),然后直接销售给客户,销售完毕后,将保险卡信息录入保险公司系统内部(销售人员信息、购买人信息、购买的保险卡数量、卡号等),客户登录保险公司网站激活保险卡,需要填写(保险卡卡号、激活密码、被保险人信息、受益人信息)

    要求就是:如果这个模块交给你来做,你要怎么做?你要解决哪些问题?你可否画个图,给我描述一下你的想法是什么吗?

    这只是一个面试题而已,因为只有简单几句话,所以我把它放到这里,让大家感受一下所谓建模要解决什么问题。而业务领域的问题实在是太多了!也许一个几十上百页的需求文档才能把某个业务领域的问题描述清楚,而你的职责就是要把它们实现出来!

    某个公司要开发一个考勤管理系统,要求与现有的人力资源系统对接,你是主要的技术负责人,那么,你要做哪些工作呢?

    某ERP项目要实现一个排班管理模块,交给你去完成,你如何去完成呢?


    不要抱怨项目经理给你的信息太少(只有几句话),不要抱怨客户没有描述清楚他们的需求……你的价值就在于理顺所有的问题,用各种手段获得你想要的信息,按照一定的思路汇总,并在特定的时间里逐个解决它!

    你应该意识到学Java不是一个坦克大战、一个网络飞车、一个CMS、一个DRP、一个OA那么简单,你不要沉迷于那些技术细节(虽然也是有必要的,但不要钻牛角尖),不要满足于实现了CRUD式的项目需求(虽然这是基础中的基础),在你的前方,永远有一个目标在那里,需要你去努力追赶!

    今后你将面对更加繁杂的需求,你学习项目的唯一目的,就是:学习如何将需求转化为实现,如何对需求进行分析,如何建立概念模型,如何理顺各种概念之间的关系,如何进行设计,如何选择合适的技术来实现你的设计方案,如何对你的实现进行测试,如何解决你所遇到的形形色色的问题(性能、需求变更等)。当你真正到公司里面从事了几年开发之后,你就会同意我的说法!


    利用Java找工作,需要的就是项目经验,项目经验就是理解项目开发的基本过程,理解项目的分析方法,理解项目的设计思路,理解项目的实现技巧,理解项目的测试方法,理解项目中各种问题的解决方案。


    码农只是复制粘贴,并不注重原理,说不出所以然,所以做了几年还只能是码农。

    加油,共勉!

    ]]>
    + + java + + + java + +
    + + Java对象的一生 | 从new到被回收 + /20190408-Java%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%B8%80%E7%94%9F/ + 一、出生过程

    这里讲述的是第一次出生的过程,即之前class没有被加载。

    1.1 类初始化

    1.1.1 加载

    1.1.1.1 通过类的全限定名获取定义此类的二进制字节流(可以从zip包、网络、运行时动态生成);
    1.1.1.2 将这个字节流所代表的静态存储结构转化为方法去运行时数据结构;
    1.1.1.3 在内存(方法区)中生成一个代表这个类的java.lang.Class对象,作为方法区这个类各种数据访问的入口;

    1.1.2 验证

    1.1.2.1 文件格式验证

    1.1.2.1.1 魔数是否以0xCAFEEBABE开头(咖啡宝贝)
    1.1.2.1.2 主、次版本号是否在当前虚拟机处理的范围之内(不同的jdk版本编译出来的版本不一致,可向前兼容)
    1.1.2.1.3 常量池是否有不被支持的类型(检查常量tag标志)
    1.1.2.1.4 指向常量的各种索引值是否指向不存在或者不符合类型的常量
    1.1.2.1.5 CONSTANT_Utf8_info型的常量中是否有不符合UTF-8编码的数据
    1.1.2.1.6 Class文件中各个部分以及文件本身是否有被删除或者附加的其它信息

    这些操作是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身安全

    1.1.2.2 元数据验证

    1.1.2.2.1 是否有父类(Object除外)
    1.1.2.2.2 是否继续了不允许继承的类(被final修饰的)
    1.1.2.2.3 如果当前不是抽象类,是否实现了其父类或接口中要求实现的所有方法
    1.1.2.2.4 类中的字段、方法是否与父类产生矛盾(如覆盖父类final字段、不合法的重载)
    这些操作是对字节码进行语义分析,确保符合Java语言规范要求

    1.1.2.3 字节码验证

    1.1.2.3.1 确保操作数栈的数据类型与指令代码序列能完美配合(反例:操作数栈为int,使用的时候按long加载)
    1.1.2.3.2 确保跳转指令不会跳转到方法体以外的字节码指令上
    1.1.2.3.3 确保方法体重点类型转换是有效的
    这些操作主要是通过数据流和控制流分析,确定程序的语义是合法的、符合逻辑的。
    JDK1.6之后有一个优化,利用StackMapTable来验证是否合法

    1.1.2.4 符号引用验证

    1.1.2.4.1 符号引用中通过字符串描述的全限定名是否能找到对应的类
    1.1.2.4.2 符号引用中的类、字段、方法是否可以被当前类访问
    在将符号引用转化为直接引用的时候触发符号引用验证

    1.1.3 准备

    1.1.3.1 仅为类变量分配内存,并且赋值为初始值,例如int为0(被static修饰的)
    1.1.3.2 特殊情况,被final修饰的static变量会直接赋值为代码给定的值
    准备阶段是正式为类变量分配内存并设置类变量初始值,这些变量所使用的内存都将在方法区中进行分配。

    1.1.4 解析

    虚拟机将常量池中的符号引用替换为直接引用
    符号引用:一组用来描述所引用目标的符号,所引用的目标不一定在内存中
    直接引用:直接指向目标的指针、相对偏移量、能间接定位到句柄,直接引用的目标必须在内存中存在

    1.1.5 初始化

    <client>()方法:由编译器自动收集类中所有变量的赋值动作和静态语句块(static{}块)中的语句合并产生
    按顺序收集。父类的此方法先执行,虚拟机会保证线程安全
    真正开始执行类中定义的Java代码,

    1.2 类实例化(New)

    1.2.1 分配内存:在类初始化完成之后,能知道所需要的内存大小

    指针碰撞:内存规整的,中间指针划分已用、空闲,那么分配就是指针向空闲一方移动对象大小相等的距离
    空闲列表:内存不规整,用列表维护一个可用内存地址,从列表中找出一个合适大小的空间分配给实例

    1.2.2 初始化为0值

    1.2.3 设置对象头信息

    1.2.4 执行<init>()方法,按照程序员的意思给对象赋值

    <init>()方法:实例构造器

    类的初始化是指类加载过程中的初始化阶段对类变量按照程序猿的意图进行赋值的过程;
    类的实例化是指在类完全加载到内存中后创建对象的过程。

    二、生平事迹

    听候线程的指令,执行相关方法

    三、驾鹤西去

    可达性分析算法:从GC Root节点开始向下搜索,搜索所走过的引用链为引用链,当没有任何引用链时说明对象不可达
    经历过两次不可达标记才会被标记为可回收
    堆内存回收主要发生在堆上的新生代,为Minor GC,使用的是复制算法
    新生代分为:Eden、to Survivor、from Survivor,后两者为Survivor,三者的比例为8:1:1

    在Minor GC之前,to Survivor为空,对象存在:Eden、from Survivor
    在Minor GC执行
    Eden存活着的对象拷贝到to Survivor,同一时候对象年龄+1

    from Survivor区中的幸存对象会考虑对象年龄
    假设年龄没达到阈值,对象依旧拷贝到to survivor中
    假设对象达到阈值那么将被移到老年代

    复制阶段完毕后,Eden和from幸存区中仅仅保存死对象,能够视为清空
    假设在复制过程中to幸存区被填满了,剩余的对象将被放到老年代

    在Minor GC之后
    from survivor和to survivor会调换一下名字,下次Minor GC时,to survivor变为from Survivor

    四、参考连接

    1. 《深入理解Java虚拟机》
    2. 一个Java Class自述短暂的一生
    3. 一切皆对象:论对象的一生一世
    4. Java对象的生命周期与垃圾回收以及四种引用
    5. Java new一个对象的过程中发生了什么

    目前写的不是太完善,有机会写的透彻明白一些。

    ]]>
    + + java + + + java + +
    + + Java静态代码块 + /20170211-Java%E9%9D%99%E6%80%81%E4%BB%A3%E7%A0%81%E5%9D%97/ + 有时候重新回味一下以前的知识也很美妙

    总能发现以前自己没有怎么在意的细节


    静态代码块是在类中独立于类成员的static语句块,可以有多个。

    如果要初始化静态变量,可以声明一个静态块。

    格式如下:

    static {
    //块执行代码
    }

    静态块存在单独的内存中,仅在该类被加载时执行,示例如下:

    package com.hisen.javaGaiShu.page91test20;

    public class JingTaiDaiMaKuai {
    private static String a;
    private String b;
    static {
    JingTaiDaiMaKuai.a = "我学习了很多语言";
    System.out.println(a);
    JingTaiDaiMaKuai t = new JingTaiDaiMaKuai();
    t.fina();
    t.b="Java语言";
    System.out.println(t.b);
    }

    static {
    JingTaiDaiMaKuai.a = "I Love Java";
    System.out.println(a);
    }

    public static void main(String[] args) {

    }

    static {
    JingTaiDaiMaKuai.a = "我还将继续学习下去";
    System.out.println(a);
    }

    private void fina() {
    System.out.println("但是我最喜欢的是:");
    }
    }


    输出如下:

    我学习了很多语言
    但是我最喜欢的是:
    Java语言
    I Love Java
    我还将继续学习下去

    静态代码块在运行main方法时可以直接调用而不用创建实例。

    静态代码块直接是按顺序执行的。

    ]]>
    + + java + + + java + 练习 + +
    + + Java判断全角半角字符以及相互转换 + /20170401-Java%E5%88%A4%E6%96%AD%E5%85%A8%E8%A7%92%E5%8D%8A%E8%A7%92%E5%AD%97%E7%AC%A6%E4%BB%A5%E5%8F%8A%E7%9B%B8%E4%BA%92%E8%BD%AC%E6%8D%A2/ + 在计算机屏幕上,一个汉字要占两个英文字符的位置,人们把一个英文字符所占的位置称为”半角”,

    相对地把一个汉字所占的位置称为”全角”。在汉字输入时,系统提供”半角”和”全角”两种不同的输入状态,

    但是对于英文字母、符号和数字这些通用字符就不同于汉字,在半角状态它们被作为英文字符处理;

    而在全角状态,它们又可作为中文字符处理。

    半角和全角切换方法:单击输入法工具条上的按钮或按键盘上的Shift+Space键来切换。

    1、全角:指一个字符占用两个标准字符位置。

    汉字字符和规定了全角的英文字符及国标GB2312-80中的图形符号和特殊字符都是全角字符。一般的系统命令是不用全角字符的,只是在作文字处理时才会使用全角字符。


    2、半角:指一字符占用一个标准的字符位置。

    通常的英文字母、数字键、符号键都是半角的,半角的显示内码都是一个字节。在系统内部,以上三种字符是作为基本代码处理的,所以用户输入命令和参数时一般都使用半角。


    3、全角与半角各在什么情况下使用?

    全角占两个字节,半角占一个字节。

    半角全角主要是针对标点符号来说的,全角标点占两个字节,半角占一个字节,而不管是半角还是全角,汉字都还是要占两个字节。

    在编程序的源代码中只能使用半角标点(不包括字符串内部的数据)

    在不支持汉字等语言的计算机上只能使用半角标点(其实这种情况根本就不存在半角全角的概念)

    对于大多数字体来说,全角看起来比半角大,当然这不是本质区别了。


    4、全角和半角的区别
    全角就是字母和数字等与汉字占等宽位置的字。半角就是ASCII方式的字符,

    在没有汉字输入法起做用的时候输入的字母数字和字符都是半角的。

    在汉字输入法出现的时候,输入的字母数字默认为半角,但是标点则是默认为全角,

    可以通过鼠标点击输入法工具条上的相应按钮来改变。


    5、关于“全角”和“半角”:

    全角:是指中GB2312-80(《信息交换用汉字编码字符集·基本集》)中的各种符号。

    半角:是指英文件ASCII码中的各种符号。

    全角状态下字母、数字符号等都会占两个字节的位置,也就是一个汉字那么宽,半角状态下,

    字母数字符号一般会占一个字节,也就是半个汉字的位置,全角半角对汉字没有影响。

    有两种方式可以判断:

    1:通过正则表达式来进行判断 [^\x00-\xff]

    2: 通过字符编码的范围进行判断.

    通过打印所有的字符发现:

    1. 半角字符是从33开始到126结束
    2. 与半角字符对应的全角字符是从65281开始到65374结束
    3. 其中半角的空格是32.对应的全角空格是12288
    4. 半角和全角的关系很明显,除空格外的字符偏移量是65248(65281-33 = 65248)

    具体的代码如下:

    package com.hisen.String;

    /**
    * 半角字符和全角字符的转换 以及 判断
    * Created by hisenyuan on 2017/4/1 at 10:37.
    */
    public class FullHalf {
    /**
    * ASCII表中可见字符从!开始,偏移位值为33(Decimal)
    */
    static final char DBC_CHAR_START = 33; // 半角!

    /**
    * ASCII表中可见字符到~结束,偏移位值为126(Decimal)
    */
    static final char DBC_CHAR_END = 126; // 半角~

    /**
    * 全角对应于ASCII表的可见字符从!开始,偏移值为65281
    */
    static final char SBC_CHAR_START = 65281; // 全角!

    /**
    * 全角对应于ASCII表的可见字符到~结束,偏移值为65374
    */
    static final char SBC_CHAR_END = 65374; // 全角~

    /**
    * ASCII表中除空格外的可见字符与对应的全角字符的相对偏移
    */
    static final int CONVERT_STEP = 65248; // 全角半角转换间隔

    /**
    * 全角空格的值,它没有遵从与ASCII的相对偏移,必须单独处理
    */
    static final char SBC_SPACE = 12288; // 全角空格 12288

    /**
    * 半角空格的值,在ASCII中为32(Decimal)
    */
    static final char DBC_SPACE = ' '; // 半角空格
    public static void main(String[] args) {
    String s = "123456";
    //半角转换成全角字符
    String s1 = bj2qj(s);
    //全角转换成半角
    String s2 = qj2bj(s1);
    System.out.println("全角:"+s1 +" -> 半角:"+s2);
    System.out.println("--------------------------");
    String fh = s1+s2;
    //判断全角还是半角
    fullOrHalf(fh);
    //打印ASCII表中所有字符
    printAllChar();
    }
    /**
    * <PRE>
    * 半角字符->全角字符转换
    * 只处理空格,!到˜之间的字符,忽略其他
    * </PRE>
    */
    private static String bj2qj(String src) {
    if (src == null) {
    return src;
    }
    StringBuilder buf = new StringBuilder(src.length());
    char[] ca = src.toCharArray();
    for (int i = 0; i < ca.length; i++) {
    if (ca[i] == DBC_SPACE) { // 如果是半角空格,直接用全角空格替代
    buf.append(SBC_SPACE);
    } else if ((ca[i] >= DBC_CHAR_START) && (ca[i] <= DBC_CHAR_END)) { // 字符是!到~之间的可见字符
    buf.append((char) (ca[i] + CONVERT_STEP));
    } else { // 不对空格以及ascii表中其他可见字符之外的字符做任何处理
    buf.append(ca[i]);
    }
    }
    return buf.toString();
    }
    /**
    * <PRE>
    * 全角字符->半角字符转换
    * 只处理全角的空格,全角!到全角~之间的字符,忽略其他
    * </PRE>
    */
    public static String qj2bj(String src) {
    if (src == null) {
    return src;
    }
    StringBuilder buf = new StringBuilder(src.length());
    char[] ca = src.toCharArray();
    for (int i = 0; i < src.length(); i++) {
    if (ca[i] >= SBC_CHAR_START && ca[i] <= SBC_CHAR_END) { // 如果位于全角!到全角~区间内
    buf.append((char) (ca[i] - CONVERT_STEP));
    } else if (ca[i] == SBC_SPACE) { // 如果是全角空格
    buf.append(DBC_SPACE);
    } else { // 不处理全角空格,全角!到全角~区间外的字符
    buf.append(ca[i]);
    }
    }
    return buf.toString();
    }

    /**
    * 使用正则表达式判断字符是否为全角
    * @param str
    */
    public static void fullOrHalf(String str){
    char[] chars = str.toCharArray();
    for (int i = 0; i < chars.length; i++) {
    String temp = String.valueOf(chars[i]);
    // 正则判断是全角字符
    if (temp.matches("[^\\x00-\\xff]")) {
    System.out.println("全角 -> " + temp);
    }
    // 判断是半角字符
    else {
    System.out.println("半角 -> " + temp);
    }
    }
    }

    /**
    * 打印所有字符
    */
    public static void printAllChar(){
    for (int i = Character.MIN_VALUE; i <= Character.MAX_VALUE; ++i) {
    System.out.println("ASCII:"+i + " -> " + "字符:"+(char)i);
    }
    }
    }

    ]]>
    + + java + + + java + +
    + + Linux 日志分析出现次数前10的数据 | awk sort uniq + /20190410-Linux-log-analyze-top-10/ + 一、命令组合
    $ more sort.log | awk '{print $1}' | sort | uniq -c | sort -k1nr | head -3
    4 5
    3 1
    2 2

    二、命令详解

    more:一次读取少量的数据,避免一次性载入大文件,比cat好些
    awk ‘{print $1}’:以awk默认的分隔符,并且打印第一列
    sort:把上一步的结果按ASCII排序
    uniq -c:如果重复那么计数,uniq命令可以组合很多其它参数
    sort -k1nr:根据第一列的数据进行排序,n是按数值大小排序,r是倒序排序
    head -3:显示前面三行数据

    三、原始数据

    $ cat sort.log
    1
    2
    3
    4
    5
    6
    1
    1
    2
    3
    4
    5
    5
    5
    6
    ]]>
    + + linux + + + linux + +
    + + Jedis - Software caused connection abort:recv failed + /20170411-Jedis%20-%20Software%20caused%20connection%20abort%20recv%20failed/ + 在使用jedis的时候出现这个问题:

    redis.clients.jedis.exceptions.JedisConnectionException:java.net.SocketException:Software caused connection abort: recv failed

    我是windows上java运行,然后redis是在虚拟机的,通过映射访问

    解决办法:

    编辑redis配置文件:

    sudo vi /etc/redis/redis.conf

    找到

    bind 127.0.0.1

    改成

    bind 0.0.0.0

    改完之后重启redis

    service redis restart

    即可。这跟mysql一样,允许任何ip连接!

    ]]>
    + + java + + + jedis + redis + +
    + + Java调用有道翻译接口 - 免费翻译API + /20180712-Java%E8%B0%83%E7%94%A8%E6%9C%89%E9%81%93%E7%BF%BB%E8%AF%91%E6%8E%A5%E5%8F%A3-%E5%85%8D%E8%B4%B9%E7%BF%BB%E8%AF%91API/ + 这个接口为免费的

    一、接口地址

    等号后面的为需要翻译的英文

    http://fanyi.youdao.com/openapi.do?keyfrom=xinlei&key=759115437&type=data&doctype=json&version=1.1&q=hisen

    二、代码样例

    把全世界200+国家和地区的名字翻译为英文,并且入库

    @Autowired
    SmsCountryService countryService;

    @Test
    public void updateZhName(){
    // 有道翻译接口
    String url = "http://fanyi.youdao.com/openapi.do?keyfrom=xinlei&key=759115437&type=data&doctype=json&version=1.1&q=";
    // 查询出所有的英文国家名字
    List<SmsCountry> countries = countryService.queryAllName();
    // httpclient
    CloseableHttpClient client = HttpClientBuilder.create().build();
    // 翻译每个国家的名字,并且更新数据库
    for (SmsCountry country:countries) {
    HttpGet request = new HttpGet(url + URLEncoder.encode(country.getName()));
    try {
    CloseableHttpResponse response = client.execute(request);
    String str = EntityUtils.toString(response.getEntity(), "utf-8");
    JSONObject jsonObject = JSON.parseObject(str);
    // 取出json字符串中数组的值
    String s = (String) jsonObject.getJSONArray("translation").get(0);
    System.out.println(">>>>> " + s);
    if (!StringUtil.isEmpty(s)){
    SmsCountry smsCountry = new SmsCountry();
    smsCountry.setId(country.getId());
    smsCountry.setNameZh(s);
    countryService.updateNameById(smsCountry);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    ]]>
    + + java + +
    + + Java基本数据类型 - 以及相关内容 + /20170210-Java%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/ + 类型名称关键字占用内存取值范围包装类字节型byte1-128~127Byte短整形short2-32768~32767Short整形int4-2147483648~2147483647Integer长整形long8-9223372036854775808L ~ 9223372036854775807LLong单精度浮点float4-3.4E38~3.4E38(6~7个有效位)Float双精度浮点double8-1.7E308~1.7E308(15个有效值)Double字符型char2ISO单一字符集,其表示范围是0~65535Charater布尔型boolean1true 或 falseBoolean

    所有基本数据类型的大小(所占用的字节数)都是明确规定好的,

    在各种平台上都保持不变,这一特性有助于提高Java程序的可移植性。


    引用数据类型包括字符串、数组、类和接口。

    引用数据类型是用户自定义、用来限制其他数据类型。

    引用数据类型的变量在内存中存储的是数据的引用,并不是数据本身,

    引用类型是使用间接方法去获取数据


    java中int为什么占用4个字节?

    回答1:

    现在流行的编译器,都是规定的int是四个字节~

    像tc这样老版的编译器,int才是两个字节,

    然后也是一样,由于一个字节占八位,最高为符号位,又人为规定,1000000000000000……这个补码编码为-2^31所以,范围就是-2^31~2^31-1

    回答2:

    JAVA是采用Unicode编码。每一个字节占8位。

    你电脑系统应该是32位系统(工具),这样每个int就是 4个字节

    其中一个字节由8个二进制位组成

    回答3:

    int常见为4个字节,跟操作系统有关系。

    turbo c(以及Turbo c的一些衍生编译器,他们用的一套编译程序)是dos时代的编译器,

    是上世纪80年代的产物,严重过时,属于老掉牙的产品,

    他们编译出来的程序是16位操作系统dos下的程序,所以长度为16位,即两个字节。

    windows为了兼容dos,所以turbo c生成的文件也可以在windows中运行。

    其他一般就都是4个字节了。

    操作系统16位的时候,int 2字节,操作系统32位的时候,int 4字节,由于32位系统之前占主流地位,实际现在就算是64位系统,出于兼容性考虑,int也是4字节的


    ]]>
    + + java + + + java + +
    + + Linux|mac查找目录下文件内容中包含某字符串的命令 + /20180712-Linux-mac%E6%9F%A5%E6%89%BE%E7%9B%AE%E5%BD%95%E4%B8%8B%E6%96%87%E4%BB%B6%E4%B8%AD%E5%8C%85%E5%90%AB%E6%9F%90%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%91%BD%E4%BB%A4/ +
    grep -r hisen ./

    上面这条命令。直接查找当前目录下所有内容中包含 hisen 的文件

    ]]>
    +
    + + MarkdownPad2注册码 20170213 + /20170213-MarkdownPad2%E6%B3%A8%E5%86%8C%E7%A0%81-20170213/ + MarkdownPad2注册码

    亲测有效:2017年2月13日 18:07:25


    邮箱:

    Soar360@live.com

    授权密钥:

    GBPduHjWfJU1mZqcPM3BikjYKF6xKhlKIys3i1MU2eJHqWGImDHzWdD6xhMNLGVpbP2M5SN6bnxn2kSE8qHqNY5QaaRxmO3YSMHxlv2EYpjdwLcPwfeTG7kUdnhKE0vVy4RidP6Y2wZ0q74f47fzsZo45JE2hfQBFi2O9Jldjp1mW8HUpTtLA2a5/sQytXJUQl/QKO0jUQY4pa5CCx20sV1ClOTZtAGngSOJtIOFXK599sBr5aIEFyH0K7H4BoNMiiDMnxt1rD8Vb/ikJdhGMMQr0R4B+L3nWU97eaVPTRKfWGDE8/eAgKzpGwrQQoDh+nzX1xoVQ8NAuH+s4UcSeQ==

    ]]>
    + + soft + + + soft + +
    + + Maven镜像库设置 - maven mirrors - maven 镜像 阿里 + /20170412-Maven%E9%95%9C%E5%83%8F%E5%BA%93%E8%AE%BE%E7%BD%AE%20-%20maven%20mirrors%20-%20maven%20%E9%95%9C%E5%83%8F%20%E9%98%BF%E9%87%8C/ + 配置文件:

    yourPath\maven-3.3.9\conf\settings.xml

    找到里面的,添加镜像即可

    <mirrors>
    </mirrors>

    这里写的是被镜像的ID

    如果写成:* (星号)

    所有的请求都会到这个镜像上,包括各种本地库

    注意:千万不要配成*

    否则内网的仓库或者你配的镜像里面没有一下jar包的时候不会去别的地方搜索

    <!--阿里云:速度挺快-->
    <mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>central</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>

    <!--谷歌:北京速度不错-->
    <mirror>
    <id>google-maven-central</id>
    <name>Google Maven Central</name>
    <url>https://maven-central.storage.googleapis.com</url>
    <mirrorOf>central</mirrorOf>
    </mirror>

    ]]>
    + + maven + + + maven + +
    + + MongoDB - 简单操作 - CRUD - JAVA + /20170711-MongoDB%20-%20%E7%AE%80%E5%8D%95%E6%93%8D%E4%BD%9C%20-%20CRUD%20-%20JAVA/ + 之前我记得写过简单的测试类,但是忘了,现在重新写一个

    顺便用博客记录下,mongodb系列应该会有几篇记录

    本篇具体代码:SampleMongoTestNo1.java

    1、环境介绍

    mongodb安装教程:点击查看

    DataBase:MongoDB V 3.2
    Driver :3.4.1
    maven:
    <dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.4.1</version>
    </dependency>

    2、初始化连接

    private Mongo mg = null;
    private DB db;
    private DBCollection dbCollection;

    @Before
    public void initDB(){
    //建立连接
    mg = new MongoClient("127.0.0.1",27017);
    //获取要操作的数据库实例,没有会创建
    db = mg.getDB("hisen");
    //获取要操作的集合实例,没有会创建
    dbCollection = db.getCollection("emp");
    }

    3、关闭连接

    @After
    public void destoryDB(){
    if (mg == null) {
    mg.close();
    mg=null;
    db=null;
    dbCollection=null;
    }
    }

    4、CRUD操作

    /**
    * 插入数据
    */
    @Test
    public void testCreate(){
    DBObject obj = null;
    for (int i = 1; i <= 10; i++) {
    obj = new BasicDBObject("_id",i).append("name","hisen"+i).append("age",i*5);
    dbCollection.save(obj);
    }
    }

    /**
    * 查询所有
    */
    @Test
    public void testReadAll(){
    DBCursor dbCursor = dbCollection.find();
    while (dbCursor.hasNext()){
    System.out.println(dbCursor.next());
    }
    }

    /**
    * 修改记录
    */
    @Test
    public void testUpdate(){
    BasicDBObject condition = new BasicDBObject("_id",10);
    BasicDBObject res = new BasicDBObject("name", "hisen10_new");
    //若没有此语句,直接调用下面的语句,返回结果{ "_id" : 10 , "name" : "hisen10_new"}
    BasicDBObject res2 = new BasicDBObject("$set", res);
    dbCollection.update(condition,res2);
    System.out.println(dbCollection.findOne(new BasicDBObject("_id",10)));
    }

    /**
    * 删除记录
    */
    @Test
    public void testDelete(){
    dbCollection.remove(new BasicDBObject("_id",10));
    testReadAll();
    }

    /**
    * 根据主键查询
    */
    @Test
    public void testReadOneWithId(){
    DBObject object = dbCollection.findOne(new BasicDBObject("_id",1));
    System.out.println(object);
    }

    /**
    * 模糊查询 - 使用正则
    */
    @Test
    public void testReadPuzzy(){
    Pattern pattern = Pattern.compile("^hisen1");
    BasicDBObject basicDBObject = new BasicDBObject("name",pattern);
    DBCursor dbCursor = dbCollection.find(basicDBObject);
    while (dbCursor.hasNext()){
    System.out.println(dbCursor.next());
    }
    }

    /**
    * 清空集合
    */
    @Test
    public void testDrop(){
    dbCollection.drop();
    testReadAll();
    }

    ]]>
    + + sql + + + mongodb + +
    + + 回顾2019,展望2020 + /20191229-Looking-back-to-2019-looking-forward-to-2020/ + 零、摘要

    2019
    1.【完成】换一份合适的工作
    2.【完成】信息系统项目管理师
    3.【完成】坚持阅读,数量质量超过2018

    2020
    1.羽毛球/骑行/等锻炼项目
    2.通过一项含金量较高的考试项目
    3.持续阅读/5本华章CS/其它不限
    4.深入了解公司内部组件/知其所以然

    2019预料之中夹着惊喜,正反馈激励我前行。
    2020充实得度过每一天,有所追求有所进步。

    一、成长

    自己想要什么样的生活?

    最近几年一直在思考这两个问题
    如今已经慢慢勾勒出了自己想要的生活
    大概就是舒服的活着,向身边人学习,帮助他人成长,体现自己的社会价值(马斯洛需求层次理论的4~5之间)

    想清楚自己想要什么样的生活之后
    接下来就是给自己设定一系列目标
    例如:长期目标、5年规划、3年规划

    抛砖引玉:

    1. 接触牛人,多和牛人交流取经/询问适当的建议,以之为榜样
    2. 保持好奇,对于自己不懂的技术或者其它任何事,各个角度去了解学习
    3. 管理精力,提高单位时间的效率,乐于锻炼,锻炼的人一般都精力充沛
    4. 持续学习,只有持续的投入系统的学习和总结方能保持竞争力
    5. 保持敬畏,认真负责得处理好份内得每一件事,再考虑锦上添花的事情

    二、阅读

    全部书单
    从17年开始阅读量稍微上来一点到如今已经走过了快3个年头
    在阅读中观摩了不少他人的经历、接触了各种不同的思维方式、看了很多恍然大悟的见解、领略了技术的魅力
    有一句话说的不错:书是一面镜子,折射出一个人的方方面面
    翻越自己的书单,也能看到一个变化的过程,从兴趣开始,逐步转向个人提升以及对精神世界的追求
    目前发现不少的书籍内容有部分的”重叠”,所以有些书还是看的比较快,有些书值得重复阅读,特别是技术书

    2019 Top8 (非权威排行)
    《贫穷的本质》
    《重塑大脑,重塑人生》
    《领域驱动设计》(系列)
    《代码简洁之道:程序员的职业素养》
    《枪炮、病菌与钢铁:人类社会的命运》
    《可伸缩服务架构:框架与中间件》
    《Java并发编程的艺术》
    《深入理解Java虚拟机》

    三、生活

    关键字:绿植、养鱼、骑行、羽毛球、辣椒

    绿植和养鱼
    看书累了/下班回家浇浇水赏赏鱼放空下自己挺好
    偶尔还会收获成长的喜悦、以及把控感

    骑行和羽毛球
    运动方面实际行动比较多的今年是羽毛球
    技术不咋地,野路子,打算2020培训下
    骑行看季节看队友,春秋时节郊区遛遛挺好

    辣椒
    作为一个江西人,应该是无辣不欢的体质,无惧新型社交绝症

    四、习惯

    2019
    起床:06:30
    午休:20分钟
    入睡:01:00

    2020
    起床:05:00
    午休:20分钟
    入睡:23:30

    起床热身,煮早餐,基本上八点半左右出门
    步行过程中耳机听互联网广播(目前是:36氪随身听)
    地铁上耳机切换至音乐模式看书(kindle/纸书)
    2020走养生路线,早睡早起!
    中午看20分钟的书
    早上看约1~3小时
    晚上看书0~1小时

    关注周围的事情,了解动态,吸取营养,输出影响力

    五、随说

    1. 每天花点时间冥想放松效果很好(多次)
    2. 写字好处多,整理思绪、调整心情、集中注意力
    3. 多给予正反馈、多微笑
    4. 减少无效的时间开支(短视频/垃圾新闻/娱乐等性价比低的活动)
    5. 做任何事情始于兴趣/好奇/压力,持续于正反馈(得到自己想要的/有益的)
    6. 把自己所见所学所想进行归纳总结,寻求内在的统一是一个很有趣的过程
    7. 感谢遇到的人和事,做一个高度容错的系统,并且在错误中捞金
    8. 想办法优化自己的思维方式(阅读/取经/经历)
    9. 写博客/发微博是一个自我总结和展示的有效途径(不一定要写多好)

    六、推荐

    1. 科技爱好者周刊
    2. 王垠
    3. 彭博商业周刊(刊物)
    4. 中国国家地里(刊物)
    5. 36氪随身听(广播)
    ]]>
    + + 随说 + + + 随说 + +
    + + MySQL update语句 + /20170801-MySQL%20update%E8%AF%AD%E5%8F%A5/ + 刚在一个群里,有人有这么一个需求。

    表A:id,name

    表B:id,其他,name(新增字段)

    A,B表通过id关联,要把A的name给对应的B的name

    以前也没有写过这种update语句

    update 表名 set 字段名=字段值 where 条件
    如 update A set name='xiaoming' where id='';
    如果是多表查询
    update 表1 a inner join 表2 b on ab表的关联 set a.字段=b.字段
    如 update A a inner join B b on a.id=b.id set a.name=b.name
    就是在table1表和table2表id相同时 把table2的name值赋给table1的name

    ]]>
    + + sql + + + mysql + +
    + + 回顾 2021,展望 2022 + /20220102-Looking-back-to-2021-looking-forward-to-2022/ + 零、摘要

    回顾 2021

    1. 【完成】换了一份不错的工作;
    2. 【完成】坚持阅读,追求质量;
    3. 【待办】运动量不够,锻炼偏少;

    展望 2022

    1. 体重控制在 65Kg 以内,学会自由泳;
    2. 持续阅读,技术与非技术书籍交替看,5 本基础书;
    3. 深入理解公司内部技术栈、深入了解 2-3 条业务线;
    4. 重新学习 Dubbo,学习设计理念,以及分布式系统架构;

    一、成长

    技术方面今年感觉成长不是太明显
    有所改变的是会深入洞察需求
    逐步完善设计之后开始 coding,而不是着急开始。

    对于技术底层也有一定地涉猎,更多地可能是在钻电商业务。

    二、阅读

    本年度阅读图书 38 本
    由于上半年部分时间在准备换工作
    以及 6 月份换了工作之后花在工作的时间比较多
    所以今年的阅读量下降地厉害,但是还是会坚持每天都翻几页

    收获较多的为以下几本:

    • 《置身事内》
    • 《幕后产品》
    • 《商业的本质和互联网》
    • 《曾国藩的正面与侧面》
    • 《分析与思考:黄奇帆复旦经济课》

    全部书单

    三、生活

    唯一的变化可能是多了 2 只鳄鱼龟。
    夜深人静之时,回到家,给乌龟喂食互动下也挺有意思。

    四、习惯

    下半年由于工作原因,晚睡较多,很少早起。
    需要找准自己的节奏,调节步伐,逐步早起。

    五、随说

    1. 做个正常人,不做圣人;
    2. 力所能及地帮助身边人;
    3. 多关注家乡的教育和经济,有机会可以出一把力;

    六、推荐

    这些年能坚持下来的也就只有下面两个了~
    其它的可能不太适合长时间跟踪

    1. 科技爱好者周刊
    2. GitHub 月度流行项目
    ]]>
    + + 随说 + + + 随说 + +
    + + List<Object> 去重 | List对象去重 by java8 stream & lambda + /20180712-List-Object-%E5%8E%BB%E9%87%8D-List%E5%AF%B9%E8%B1%A1%E5%8E%BB%E9%87%8D-by-java8-stream-lambda/ + 利用java8的流和lambda表达式能很方便的对list对象进行去重
    而且不会造成代码入侵

    插播:Java8 对List进行求和、分组、提取对象单个属性:https://www.jianshu.com/p/c71eaeaaf30c

    下面的例子仅供参考
    github:https://github.com/hisenyuan

    package com.hisen.collection.list.duplicate;

    import com.alibaba.rocketmq.shade.com.alibaba.fastjson.JSON;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.stream.Collectors;

    /**
    * @author hisenyuan
    * @time 2018/4/19 14:06
    * @description list对象根据属性去重 lambda + stream
    */
    public class ListDuplicateTest {


    public static void main(String[] args) {
    List<Person> list = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
    Person person = new Person();
    person.setAge(10);
    person.setName("hsien");
    person.setWeight(i);
    list.add(person);
    }
    Person bean = new Person();
    bean.setName("hisenyuan");
    bean.setAge(33);
    bean.setWeight(65);
    list.add(bean);

    List<Person> collect = list.stream().filter(distinctByKey(Person::getName))
    .collect(Collectors.toList());
    System.out.println(JSON.toJSONString(collect));
    }

    /**
    * 函数式接口 T -> bollean
    * @param keyExtractor
    * @param <T>
    * @return
    */
    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>(16);
    return t -> map.putIfAbsent(keyExtractor.apply(t),Boolean.TRUE) == null;

    // 这个也可以,不过感觉效率要低一些,线程不是那么安全
    // Set<Object> seen = ConcurrentHashMap.newKeySet();
    // return t -> seen.add(keyExtractor.apply(t));
    }

    /**
    * 内部类
    */
    static class Person {
    private int age;
    private String name;
    private int weight;

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public int getWeight() {
    return weight;
    }

    public Person() {
    }
    public void setWeight(int weight) {
    this.weight = weight;
    }
    }
    }

    ]]>
    +
    + + MySQL 常用操作 - 命令行 + /20170815-MySQL%20%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C%20-%20%E5%91%BD%E4%BB%A4%E8%A1%8C/ + 命令行常用操作,备忘

    #开启远程登录
    grant all privileges on *.* to 'user'@'%' identified by 'passwd' with grant option;

    #创建数据库
    create database DB;

    #创建用户
    insert into mysql.user(Host,User,Password) values("localhost","user",password("passwd"));

    #删除用户
    DELETE FROM user WHERE user="username" and HOST="localhost";

    #修改指定用户密码
    update mysql.user set password=password('new passwd') where user="username" and host="localhost";

    #用户授权
    grant all privileges on DB.* to 'user'@'localhost' identified by 'passwd';
    grant select,update on DB.* to 'user'@'localhost' identified by 'passwd';

    #刷新权限
    flush privileges;

    #数据库导出
    mysqldump -uUSRENAME -pPASSWD DATABASE > DATABASE.sql

    #数据库导出(只导出表结构 -d)
    mysqldump -uUSRENAME -pPASSWD -d DATABASE > DATABASE.sql

    #数据库导入

    #1.切换数据库
    use DATABASE;
    #2.设置编码
    set names utf8;
    #3.执行导入操作
    source /home/abc/abc.sql;

    #直接导入
    mysql -uUSERNAME -p DATABASE < DATABASE.sql

    ]]>
    + + sql + + + mysql + +
    + + Maven目录下*.lastupdated命令行批量删除 + /20170220-Maven%E7%9B%AE%E5%BD%95%E4%B8%8B-lastupdated%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4/ + windows系统

    cd %userprofile%\.m2\repository
    for /r %i in (*.lastUpdated) do del %i

    或者新建一个bat文件,批处理。就不用每次都在cmd敲命令了

    @echo off
    echo 确认删除maven仓库下*.lastUpdated文件?
    pause
    ::create by hisenyuan(hisenyuan@gmail.com)

    ::这里写你的仓库路径
    set REPOSITORY_PATH=C:\hisenwork\soft\maven
    echo 正在搜索...
    for /f "delims=" %%i in ('dir /b /s "%REPOSITORY_PATH%\*lastUpdated*"') do (
    del /s /q %%i
    )
    echo 完毕
    pause

    linux系统

    find /app/maven/localRepository -name "*.lastUpdated" -exec grep -q "Could not transfer" {} \; -print -exec rm {} \;

    ]]>
    + + java + + + java + maven + +
    + + MySQL decode - MySQL中类似oracle的decode实现方法 + /20170427-MySQL%20decode%20-%20MySQL%E4%B8%AD%E7%B1%BB%E4%BC%BCoracle%E7%9A%84decode%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95/ + 在oracle中直接有decode函数 decode(cola,null,0)

    表示如果cola为空,赋值为0

    在mysql中的具体实现如下,

    mysql> describe book;
    +---------+--------------+------+-----+---------+----------------+
    | Field | Type | Null | Key | Default | Extra |
    +---------+--------------+------+-----+---------+----------------+
    | book_id | bigint(20) | NO | PRI | NULL | auto_increment |
    | name | varchar(100) | NO | | NULL | |
    | number | int(11) | NO | | NULL | |
    +---------+--------------+------+-----+---------+----------------+
    3 rows in set (0.00 sec)

    mysql> select * from book;
    +---------+-------------------------+--------+
    | book_id | name | number |
    +---------+-------------------------+--------+
    | 123 | 123 | 122 |
    | 1000 | Java程序设计 | 5 |
    | 1001 | 数据结构 | 9 |
    | 1002 | 设计模式 | 10 |
    | 1003 | 编译原理 | 10 |
    | 1004 | MySQL从删库到跑路 | 100 |
    | 1005 | 活着 | 10 |
    | 1232 | 11111 | 124 |
    | 2001 | 测试 | 2001 |
    | 10064 | 老鼠爱大米 | 10088 |
    | 10066 | 老鼠爱大米 | 1008 |
    | 10088 | 测试宝典 | 1008 |
    | 10096 | maven实战 | 10096 |
    | 11111 | 111111 | 11111 |
    | 12311 | 都懂得 | 1222 |
    | 123222 | 222 | 1111 |
    +---------+-------------------------+--------+
    16 rows in set (0.00 sec)

    mysql> select if(count(b.book_id)=16,"十六","不是十六") '结果' from book b;
    +--------+
    | 结果 |
    +--------+
    | 十六 |
    +--------+
    1 row in set (0.00 sec)

    mysql> select case count(b.book_id) when 16 then '十六' else '其他' end as '结果' from book b;
    +--------+
    | 结果 |
    +--------+
    | 十六 |
    +--------+
    1 row in set (0.00 sec)

    ]]>
    + + mysql + + + mysql + sql + +
    + + MySQL一个group by查询出最大值和非group by所在字段的值 + /20170418-MySQL%E4%B8%80%E4%B8%AAgroup%20by%E6%9F%A5%E8%AF%A2%E5%87%BA%E6%9C%80%E5%A4%A7%E5%80%BC%E5%92%8C%E9%9D%9Egroup%20by%E6%89%80%E5%9C%A8%E5%AD%97%E6%AE%B5%E7%9A%84%E5%80%BC/ + 表结构,数据内容如下。

    需求是:查找出每个年级,年纪最大的人的名字。

    个人的思维只停留在

    mysql> select name, max(age),grade from  stu group by grade;
    ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'hisen.stu.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

    mysql> SELECT A.* FROM stu A INNER JOIN (SELECT MAX(AGE) AS MAX_AGE,GRADE FROM stu GROUP BY GRADE ) B ON A.GRADE = B.GRADE AND A.AGE= B.MAX_AGE;
    +----+------+------+-------+
    | id | name | age | grade |
    +----+------+------+-------+
    | 3 | c | 13 | 1 |
    | 6 | f | 13 | 2 |
    +----+------+------+-------+
    2 rows in set (0.00 sec)

    折腾了一会自己不知道怎么解决,后来倒是解决了。

    具体过程如下:

    mysql> describe stu;
    +-------+---------+------+-----+---------+----------------+
    | Field | Type | Null | Key | Default | Extra |
    +-------+---------+------+-----+---------+----------------+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | name | char(1) | YES | | NULL | |
    | age | int(11) | YES | | NULL | |
    | grade | int(11) | YES | | NULL | |
    +-------+---------+------+-----+---------+----------------+
    4 rows in set (0.00 sec)

    mysql> select * from stu;
    +----+------+------+-------+
    | id | name | age | grade |
    +----+------+------+-------+
    | 1 | a | 11 | 1 |
    | 2 | b | 12 | 1 |
    | 3 | c | 13 | 1 |
    | 4 | d | 11 | 2 |
    | 5 | e | 12 | 2 |
    | 6 | f | 13 | 2 |
    +----+------+------+-------+
    6 rows in set (0.00 sec)

    mysql> select max(name), max(age),grade from stu group by grade;
    +-----------+----------+-------+
    | max(name) | max(age) | grade |
    +-----------+----------+-------+
    | c | 13 | 1 |
    | f | 13 | 2 |
    +-----------+----------+-------+
    2 rows in set (0.01 sec)

    ]]>
    + + mysql + + + mysql + +
    + + MySQL下A库的a表导入到B库的b表 + /20170516-MySQL%E4%B8%8BA%E5%BA%93%E7%9A%84a%E8%A1%A8%E5%AF%BC%E5%85%A5%E5%88%B0B%E5%BA%93%E7%9A%84b%E8%A1%A8/ + 数据库名称表名employeesemployeeshisenemployee

    现在想把第一行的数据导入到第二行,具体如下

    mysql> describe employees;
    +------------+---------------+------+-----+---------+-------+
    | Field | Type | Null | Key | Default | Extra |
    +------------+---------------+------+-----+---------+-------+
    | emp_no | int(11) | NO | PRI | NULL | |
    | birth_date | date | NO | | NULL | |
    | first_name | varchar(14) | NO | | NULL | |
    | last_name | varchar(16) | NO | | NULL | |
    | gender | enum('M','F') | NO | | NULL | |
    | hire_date | date | NO | | NULL | |
    +------------+---------------+------+-----+---------+-------+
    6 rows in set

    mysql> select count(*) from employees;
    +----------+
    | count(*) |
    +----------+
    | 300024 |
    +----------+
    1 row in set

    employees数据库中的employees表有30万数据

    我想把这个数据导出到另外一个数据库hisen中的employee表中

    具体操作如下

    mysql> create table hisen.employee as(
    -> select * from employees.employees
    -> );
    Query OK, 300024 rows affected
    Records: 300024 Duplicates: 0 Warnings: 0

    mysql> use hisen;
    Database changed
    mysql> select count(*) from employee;
    +----------+
    | count(*) |
    +----------+
    | 300024 |
    +----------+
    1 row in set

    这种操作是针对所有字段都导出的情形,创建表跟插入数据合二为一

    如果导出部分字段或者有其他限制条件写sql即可

    这应该是最简单的方法~

    ]]>
    + + mysql + + + mysql + +
    + + MySQL使用like查找汉字乱码 + /20170208-MySQL%E4%BD%BF%E7%94%A8like%E6%9F%A5%E6%89%BE%E6%B1%89%E5%AD%97%E4%B9%B1%E7%A0%81/ + 第一种解决办法:BINARY

    在关键字之前加上:BINARY,会使关键字强制转换为二进制字符串

    select id form t where chinese like **BINARY** %汉字%

    第二种解决办法:改关键字类型

    把关键字的类型改成:BINARY

    这两种办法都可以解决乱码问题

    ]]>
    + + mysql + like + +
    + + MySQL - Data truncation:Incorrect datetime value + /20190201-MySQL%20-%20Data%20truncation%20Incorrect%20datetime%20value/ + 一、问题
    com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect datetime value: '2039-01-07 12:58:20.625' for column

    二、原因

    由于程序没有控制好,计算下一次更新时间失误,造成数值过大。

    update table_a
    SET UPDATE_TIME = '2039-01-07 12:58:20.625'
    WHERE ID = 6241

    以下是MySQL官方的说法,就是时间超过了范围。

    The TIMESTAMP data type is used for values that contain both date and time parts.
    TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC.

    ]]>
    + + sql + + + mysql + sql + +
    + + MySQL一些简单的语句 + /20170120-MySQL%E4%B8%80%E4%BA%9B%E7%AE%80%E5%8D%95%E7%9A%84%E8%AF%AD%E5%8F%A5/ + emlog_ad字段
    id
    status
    position
    title
    weight
    content

    --找出重复
    SELECT a.*
    FROM emlog_ad a
    WHERE a.id IN
    (SELECT b.id id
    FROM emlog_ad b
    GROUP BY b.title
    HAVING count(b.id)>1);

    --删除重复留下id最小的
    SELECT a.*
    FROM emlog_ad a
    WHERE a.id IN
    (SELECT b.id
    FROM emlog_ad b
    GROUP BY b.title
    HAVING count(b.id)>1)
    AND a.id NOT IN
    (SELECT min(c.id)
    FROM emlog_ad c
    GROUP BY c.title
    HAVING count(c.id)>1);

    --一句sql把所有AA改为BB,CC改为DD
    UPDATE emlog_ad a
    SET a.`status`=(
    CASE
    WHEN a.`status` = '1' THEN '11'
    WHEN a.`status`='2' THEN '22'
    END
    );
    ]]>
    + + mysql + +
    + + MySql的执行计划 + /20171123-MySql%E7%9A%84%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92/ + ###命令介绍
    MySQL的EXPLAIN命令用于SQL语句的查询执行计划(QEP)。

    这条命令的输出结果能够让我们了解MySQL 优化器是如何执行

    执行下面的SQL

    explain select * FROM book where name like '活%'

    得到下面的数据

    idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfileredExtra
    编号查询方式表名分区连接方式索引(可能)索引索引长度作用列行数百分比额外
    1SIMPLEbooknullALLnullnullnullnull11411.11Using where

    ###建表语句

    #创建个人信息表
    CREATE TABLE hisen_test_explain_people
    (
    id bigint auto_increment primary key,
    zipcode char(32) not null default '',
    address varchar(128) not null default '',
    lastname char(64) not null default '',
    firstname char(64) not null default '',
    birthdate char(10) not null default ''
    );
    #创建索引
    alter table hisen_test_explain_people add key(zipcode,firstname,lastname);

    #插入个人信息数据
    insert into hisen_test_explain_people
    (zipcode,address,lastname,firstname,birthdate)
    values
    ('230031','anhui','zhan','jindong','1989-09-15'),
    ('100000','beijing','zhang','san','1987-03-11'),
    ('200000','shanghai','wang','wu','1988-08-25');
    #创建汽车信息表
    CREATE TABLE hisen_test_explain_people_car(
    people_id bigint,
    plate_number varchar(16) not null default '',
    engine_number varchar(16) not null default '',
    lasttime timestamp
    );
    #插入汽车信息
    insert into hisen_test_explain_people_car
    (people_id,plate_number,engine_number,lasttime)
    values
    (1,'A121311','12121313','2017-11-23 :21:12:21'),
    (2,'B121311','1S121313','2016-11-23 :21:12:21'),
    (3,'C121311','1211SAS1','2015-11-23 :21:12:21');

    ###执行计划样例

    explain select zipcode,firstname,lastname from hisen_test_explain_people;
    +----+-------------+---------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+---------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
    | 1 | SIMPLE | hisen_test_explain_people | NULL | index | NULL | zipcode | 160 | NULL | 3 | 100.00 | Using index |
    +----+-------------+---------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+

    explain select zipcode from (select * from hisen_test_explain_people a) b;
    +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
    | 1 | SIMPLE | a | NULL | index | NULL | zipcode | 160 | NULL | 3 | 100.00 | Using index |
    +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+


    id是用来顺序标识整个查询中SELELCT 语句的,通过上面这个简单的嵌套查询可以看到id越大的语句越先执行。
    该值可能为NULL,如果这一行用来说明的是其他行的联合结果,比如UNION语句:

    explain select * from hisen_test_explain_people where zipcode = 100000 union select * from hisen_test_explain_people where zipcode = 200000;
    +----+--------------+---------------------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+--------------+---------------------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
    | 1 | PRIMARY | hisen_test_explain_people | NULL | ALL | zipcode | NULL | NULL | NULL | 3 | 33.33 | Using where |
    | 2 | UNION | hisen_test_explain_people | NULL | ALL | zipcode | NULL | NULL | NULL | 3 | 33.33 | Using where |
    |NULL| UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
    +----+--------------+---------------------------+------------+------+---------------+------+---------+------+------+----------+-----------------+

    ###参考

    1. 参数解释
    2. 实战演练
    ]]>
    + + sql + + + mysql + sql + explain + +
    + + Linux常用的几个命令 + /20170122-Linux%E5%B8%B8%E7%94%A8%E7%9A%84%E5%87%A0%E4%B8%AA%E5%91%BD%E4%BB%A4/ + 1、查看日志最后几行

    tail -100 /access.log

    2、进入目录相关

    #进入一个目录
    root@hisenyuan:/# cd /home/wwwlog/
    #进入当前目录下的www.google.com目录
    root@hisenyuan:/home/wwwlog# cd ./www.google.com
    #进入父目录
    root@hisenyuan:/home/wwwlog/www.google.com# cd ../
    #进入linux系统根目录
    root@hisenyuan:/home/wwwlog# cd /
    #根目录
    root@hisenyuan:/#

    3、看倒数多少行

    #看倒数10行
    tail -10 /filepath/filename
    #看行数外加过滤含有指定字符的行
    tail -10 access.log | grep -v "yourstring"

    4、过滤特定行,保存结果到新文件

    cat /root/old.text | grep -v "yourstring"> /root/new.text

    ]]>
    + + java + linux + +
    + + Mybatis-Generator自动生成dao、model、mapper + /20170802-Mybatis-Generator%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90dao%E3%80%81model%E3%80%81mapper/ + 一、配置插件

    在resources文件夹下新建:generatorConfig.xml

    内容如下:注意修改包名等信息

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
    PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
    "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <!--
    详细说明请看:http://blog.csdn.net/tiantangpw/article/details/51690534
    -->
    <generatorConfiguration>

    <context id="mysqlgenerator" targetRuntime="MyBatis3">
    <!--数据库配置-->
    <jdbcConnection connectionURL="jdbc:mysql://127.0.0.1:3306/booksystem"
    driverClass="com.mysql.jdbc.Driver"
    password="hisen"
    userId="root"/>

    <!--生成Model(实体类,与数据库字段对应的bean)类存放位置-->
    <javaModelGenerator targetPackage="com.hisen.entity" targetProject="src/main/java">
    <property name="enableSubPackages" value="true"/>
    <property name="trimStrings" value="true"/>
    </javaModelGenerator>
    <!--生成映射(xxxmapper.xml)文件存放位置-->
    <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
    <property name="enableSubPackages" value="true"/>
    </sqlMapGenerator>
    <!--生成Dao类存放位置-->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.hisen.dao"
    targetProject="src/main/java">
    <property name="enableSubPackages" value="true"/>
    </javaClientGenerator>

    <!--要生成的表-->
    <table tableName="appointment"/>
    <table tableName="user"/>
    </context>

    </generatorConfiguration>

    二、添加maven插件

    在pom.xml中添加如下内容:plugins节点内

    <build>
    <plugins>
    <plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.2</version>
    <dependencies>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.5</version>
    </dependency>
    </dependencies>
    <configuration>
    <overwrite>true</overwrite>
    </configuration>
    </plugin>
    </plugins>
    </build>

    三、使用

    在idea调出mavenProject界面,选择plugins,找到mybatis-generator,双击即可

    ]]>
    + + java + + + sql + mybatis + +
    + + MySQL统计成功率等 - 利用case when + /20190218-MySQL%E7%BB%9F%E8%AE%A1%E6%88%90%E5%8A%9F%E7%8E%87%E7%AD%89%20-%20%E5%88%A9%E7%94%A8case%20when/ + CONCAT:连接字符串,CONCAT(55.00,’%’)->55.00%
    truncate:处理小数点位数,truncate(10.1111,2)->10.11

    统计sql

    select res.*, CONCAT(truncate((res.succ / res.`all`) * 100, 2), '%') as 'success rate'
    from (select tpc.USER_NO,
    tpc.TYPE,
    count(*) as 'all',
    sum(case when tpc.STATUS = 0 then 1 else 0 end) as 'succ',
    sum(case when tpc.STATUS = 1 then 1 else 0 end) as 'fail',
    sum(case when tpc.STATUS != 1 && tpc.STATUS != 0 then 1 else 0 end) as 'other'
    from t_hisen tpc
    where tpc.DATE between '20190216' and '20190217'
    group by tpc.USER_NO, tpc.TYPE
    order by tpc.USER_NO) res

    统计结果

    USER_NOTYPEallsuccfailothersuccess rate
    4561010010.00%
    1231029230679.31%
    ]]>
    + + sql + + + mysql + sql + +
    + + MySQL索引相关知识和应用 + /20170518-MySQL%E7%B4%A2%E5%BC%95%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E5%92%8C%E5%BA%94%E7%94%A8/ + hisen库中有一个post表,数据20w,非重复数据20条;

    结构如下

    mysql> describe post;
    +---------+---------------+------+-----+---------+----------------+
    | Field | Type | Null | Key | Default | Extra |
    +---------+---------------+------+-----+---------+----------------+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | title | varchar(255) | YES | MUL | NULL | |
    | content | varchar(2550) | YES | | NULL | |
    +---------+---------------+------+-----+---------+----------------+
    3 rows in set (0.00 sec)

    添加普通索引

    mysql> alter table post add index index_post_title (title);
    Query OK, 0 rows affected (1.22 sec)
    Records: 0 Duplicates: 0 Warnings: 0

    删除索引

    mysql> drop index index_post_title on post;
    Query OK, 0 rows affected (0.02 sec)
    Records: 0 Duplicates: 0 Warnings: 0

    添加索引之后查询速度明显加快

    mysql> select count(title) from post group by title;
    20 rows in set (0.25 sec)
    #加了索引之后
    20 rows in set (0.08 sec)

    使用索引的情况

    1. 表的主关键字:自动建立唯一索引
    2. 表的字段唯一约束:ORACLE利用索引来保证数据的完整性
    3. 直接条件查询的字段
    4. 在SQL中用于条件约束的字段
    5. 查询中与其它表关联的字段
    6. 查询中排序的字段
    7. 查询中统计或分组统计的字段

    不使用索引的情况

    1. 表记录太少
    2. 经常插入、删除、修改的表
    3. 数据重复且分布平均的表字段:假如10万数据只有A、B状态,且A、B各50%,这样建立索引就不会提速
    4. 经常和主字段一块查询但主字段索引值比较多的表字段

    MySql在建立索引优化时需要注意的问题

    1. 创建索引:对于查询占主要的应用来说,索引显得尤为重要。很多时候性能问题很简单的就是因为我们忘了添加索引而造成的,或者说没有添加更为有效的索引导致。如果不加索引的话,那么查找任何哪怕只是一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降。但是也不是什么情况都非得建索引不可,比如性别可能就只有两个值,建索引不仅没什么优势,还会影响到更新速度,这被称为过度索引。
    2. 复合索引:比如有一条语句是这样的:select * from users where area=’beijing’ and age=22;如果我们是在area和age上分别创建单个索引的话,由于mysql查询每次只能使用一个索引,所以虽然这样已经相对不做索引时全表扫描提高了很多效率,但是如果在area、age两列上创建复合索引的话将带来更高的效率。如果我们创建了(area, age,salary)的复合索引,那么其实相当于创建了(area,age,salary)、(area,age)、(area)三个索引,这被称为最佳左前缀特性。因此我们在创建复合索引时应该将最常用作限制条件的列放在最左边,依次递减。
    3. 索引不会包含有NULL值的列:只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
    4. 使用短索引:对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的 列,如果在前10 个或20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
    5. 排序的索引问题:mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
    6. like语句操作:一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
    7. 不要在列上进行运算:select * from users where YEAR(adddate);
    8. 不使用NOT IN和操作:NOT IN和操作都不会使用索引将进行全表扫描。NOT IN可以NOT EXISTS代替,id3则可使用id>3 or id
    ]]>
    + + mysql + + + mysql + 索引 + +
    + + MySQL 数据库安装官方自带employees测试库 + /20170401-MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%89%E8%A3%85%E5%AE%98%E6%96%B9%E8%87%AA%E5%B8%A6employees%E6%B5%8B%E8%AF%95%E8%A1%A8/ + MySQL 官方是有一个自带的数据库,名为:Employees Sample Database

    官网介绍:Employees Sample Database

    表名中文
    department部门表
    dept_emp部门员工任职期表(按部门&时期)
    dept_manager部门经理任职期表(按时期)
    employees员工详情表
    salaries员工薪资表(按时期)
    title员工职称表(按时期)


    导入的操作过程,在ubuntu上进行操作

    一、导入数据库操作过程

    #ubuntu apt-get 安装的mysql默认的配置文件
    #添加一行:default-storage-engine=InnoDB
    hisen@ubuntu:/$ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
    #获取root权限
    hisen@ubuntu:/var/lib$ su
    Password:
    #进数据库目录
    root@ubuntu:# cd /var/lib/mysql
    #删除两个文件
    root@ubuntu:/var/lib/mysql# rm ib_logfile0
    root@ubuntu:/var/lib/mysql# rm ib_logfile1
    #重启数据库
    root@ubuntu:/# service mysql restart
    #下载Employees database
    hisen@ubuntu:~/dl$ wget https://codeload.github.com/datacharmer/test_db/zip/master
    #解压
    hisen@ubuntu:~/dl$ unzip master
    #查看
    hisen@ubuntu:~/dl$ ll
    total 35840
    drwxrwxr-x 3 hisen hisen 4096 Apr 1 16:50 ./
    drwxr-xr-x 10 hisen hisen 4096 Apr 1 16:40 ../
    -rw-rw-r-- 1 hisen hisen 36687570 Apr 1 16:30 master
    drwxrwxr-x 4 hisen hisen 4096 Oct 14 2015 test_db-master/
    #进目录
    hisen@ubuntu:~/dl$ cd test_db-master/
    #导入数据库
    hisen@ubuntu:~/dl/test_db-master$ mysql -u root -p < employees.sql
    Enter password:
    INFO
    CREATING DATABASE STRUCTURE
    INFO
    storage engine: InnoDB
    INFO
    LOADING departments
    INFO
    LOADING employees
    INFO
    LOADING dept_emp
    INFO
    LOADING dept_manager
    INFO
    LOADING titles
    INFO
    LOADING salaries
    data_load_time_diff
    00:01:11
    #导入成功
    #验证是否导入成功
    hisen@ubuntu:~/dl/test_db-master$ mysql -u root -p < test_employees_md5.sql
    Enter password:
    INFO
    TESTING INSTALLATION
    table_name expected_records expected_crc
    employees 300024 4ec56ab5ba37218d187cf6ab09ce1aa1
    departments 9 d1af5e170d2d1591d776d5638d71fc5f
    dept_manager 24 8720e2f0853ac9096b689c14664f847e
    dept_emp 331603 ccf6fe516f990bdaa49713fc478701b7
    titles 443308 bfa016c472df68e70a03facafa1bc0a8
    salaries 2844047 fd220654e95aea1b169624ffe3fca934
    table_name found_records found_crc
    employees 300024 4ec56ab5ba37218d187cf6ab09ce1aa1
    departments 9 d1af5e170d2d1591d776d5638d71fc5f
    dept_manager 24 8720e2f0853ac9096b689c14664f847e
    dept_emp 331603 ccf6fe516f990bdaa49713fc478701b7
    titles 443308 bfa016c472df68e70a03facafa1bc0a8
    salaries 2844047 fd220654e95aea1b169624ffe3fca934
    table_name records_match crc_match
    employees OK ok
    departments OK ok
    dept_manager OK ok
    dept_emp OK ok
    titles OK ok
    salaries OK ok
    computation_time
    00:00:22
    summary result
    CRC OK
    count OK

    二、数据库练习

    练习题点击查看练习题

    ]]>
    + + mysql + + + mysql + +
    + + MySQL批量删除重复数据,保留id最小的一条 + /20181016-MySQL%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E6%95%B0%E6%8D%AE,%E4%BF%9D%E7%95%99id%E6%9C%80%E5%B0%8F%E7%9A%84%E4%B8%80%E6%9D%A1/ + stu数据如下:

    idname
    1hisen
    2hisen
    3hisen
    4hisenyuan
    5hisenyuan

    删除重复的name,保留id最小的。

    思路就是先找出重复数据,然后再找出需要保留的数据(重复中id最小的)

    然后删除id不在需要保留的id中的所有数据

    delete
    from stu
    where id not in (select t.minId
    from (select min(id) minId from stu group by name having count(name) > 1) t)
    ]]>
    + + sql + + + mysql批量删除重复数据 + +
    + + No MyBatis mapper was found in 'com.xxx.dao' package + /20170801-No%20MyBatis%20mapper%20was%20found%20in%20'%5Bcom.xxx.dao%5D'%20package%20/ + 出错

    maven项目,启动tomcat的时候报错:

    No MyBatis mapper was found in '[com.hisen.dao]' package

    这是由于把mybatis的mapper配置文件放在了java代码的目录下

    目录结构

    ├── java
    │ └── com
    │ └── hisen
    │ ├── dao
    │ │ └── BookDao.java
    │ │ └── BookMapper.xml
    │ ├── entity
    │ │ └── Book.java
    │ ├── service
    │ │ ├── BookService.java
    │ │ └── impl
    │ │ └── BookServiceImpl.java
    │ └── web
    │ └── BookController.java
    ├── resources
    │ ├── jdbc.properties
    │ ├── logback.xml
    │ ├── mybatis-config.xml
    │ └── spring
    │ ├── spring-dao.xml
    │ ├── spring-service.xml
    │ └── spring-web.xml
    └── webapp
    ├── index.jsp
    └── WEB-INF
    ├── jsp
    │ ├── detail.jsp
    │ └── list.jsp
    └── web.xml

    出错原因

    这是因为mapper文件的路径问题:maven在打包的时候默认是认为src/main/java目录下只有java源代码,不会管xml文件。

    Java 项目分为两个部分,一个是源码,一个是资源,在使用maven等构建工具时,

    默认会将源码编译后再加上资源目录的文件放到target目录下作为最后运行的文件(可以是war,jar,或者目录)。

    有时为了方便,我们会在src/main/java源码目录下放了资源文件,例如mybatis的mapper文件,方便我们编程时展开查看。

    这时,我们需要设置编译时也将这些配置文件放到target目录下,否则最后的target目录式没有这些文件的。

    解决办法

    在项目的pom文件里面配置,让maven也从src/main/java目录下读取xml配置文件

    <build>
    <!--注意,如果将静态资源放在src/main/java中,那么编译时将被maven忽略,
    在target目录下将没有这些资源,所以需要将该目录添加为资源目录.
    -->
    <resources>
    <resource>
    <directory>${basedir}/src/main/java</directory>
    <includes>
    <include>**/*.xml</include>
    </includes>
    </resource>
    </resources>
    </build>

    扩展

    如果是把mapper文件什么的放在resources目录下

    但是为了分类建了不同的文件夹,也需要修改配置文件,不然会读取不到

    <build>
    <!--包含下面所有的配置文件(包括子目录)-->
    <resources>
    <resource>
    <directory>${basedir}/src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    </resource>
    </resources>
    </build>

    ]]>
    + + java + + + java + xml + +
    + + Mysql Employees Database练习题&答案 + /20170410-Mysql%20Employees%20%20Database%E7%BB%83%E4%B9%A0%E9%A2%98&%E7%AD%94%E6%A1%88/ + Employees数据库是mysql官方提供的一个测试用数据库

    里面含有几十万条数据。

    找了好久也没有找到比较匹配的题目

    就找了个匹配度比较高的题来练习,如果你还没有导入Employees Sample Database

    请参考:点击导入Employees

    本次操作在Xshell中完成,也就是mysql命令行。

    简单的操作

    #登陆数据库
    hisen@ubuntu:/$ mysql -u root -p
    Enter password:
    Welcome to the MySQL monitor. Commands end with ; or \g.
    Your MySQL connection id is 8
    Server version: 5.7.17-0ubuntu0.16.04.2 (Ubuntu)

    Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.

    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

    mysql> show databases;
    +--------------------+
    | Database |
    +--------------------+
    | information_schema |
    | employees |
    | log4j |
    | mysql |
    | performance_schema |
    | ssm |
    | sys |
    +--------------------+
    7 rows in set (0.00 sec)

    mysql> use employees
    Reading table information for completion of table and column names
    You can turn off this feature to get a quicker startup with -A

    Database changed
    mysql> show tables;
    +----------------------+
    | Tables_in_employees |
    +----------------------+
    | current_dept_emp |
    | departments |
    | dept_emp |
    | dept_emp_latest_date |
    | dept_manager |
    | employees |
    | salaries |
    | titles |
    +----------------------+
    8 rows in set (0.00 sec)

    mysql> select * from employees limit 10;
    +--------+------------+------------+-----------+--------+------------+
    | emp_no | birth_date | first_name | last_name | gender | hire_date |
    +--------+------------+------------+-----------+--------+------------+
    | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
    | 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 |
    | 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 |
    | 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
    | 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 |
    | 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 |
    | 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 |
    | 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 |
    | 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 |
    | 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 |
    +--------+------------+------------+-----------+--------+------------+
    10 rows in set (0.00 sec)
    #统计各部门曾经拥有的员工数量
    mysql> select dept_no,count(*) emp_sum
    -> from dept_emp
    -> group by dept_no
    -> order by emp_sum desc;
    +---------+---------+
    | dept_no | emp_sum |
    +---------+---------+
    | d005 | 85707 |
    | d004 | 73485 |
    | d007 | 52245 |
    | d009 | 23580 |
    | d008 | 21126 |
    | d001 | 20211 |
    | d006 | 20117 |
    | d003 | 17786 |
    | d002 | 17346 |
    +---------+---------+
    9 rows in set (0.63 sec)
    #创建视图
    mysql> CREATE VIEW test AS
    -> SELECT dept_no, COUNT(*) AS emp_sum
    -> FROM dept_emp
    -> GROUP BY dept_no
    -> ORDER BY emp_sum DESC
    -> ;
    Query OK, 0 rows affected (0.16 sec)
    mysql> select * from test;
    +---------+---------+
    | dept_no | emp_sum |
    +---------+---------+
    | d005 | 85707 |
    | d004 | 73485 |
    | d007 | 52245 |
    | d009 | 23580 |
    | d008 | 21126 |
    | d001 | 20211 |
    | d006 | 20117 |
    | d003 | 17786 |
    | d002 | 17346 |
    +---------+---------+
    9 rows in set (0.11 sec)
    #联合查询,加上部门名称
    mysql> select test.dept_no,emp_sum,dept_name
    -> from test,departments
    -> where test.dept_no = departments.dept_no;
    +---------+---------+--------------------+
    | dept_no | emp_sum | dept_name |
    +---------+---------+--------------------+
    | d009 | 23580 | Customer Service |
    | d005 | 85707 | Development |
    | d002 | 17346 | Finance |
    | d003 | 17786 | Human Resources |
    | d001 | 20211 | Marketing |
    | d004 | 73485 | Production |
    | d006 | 20117 | Quality Management |
    | d008 | 21126 | Research |
    | d007 | 52245 | Sales |
    +---------+---------+--------------------+
    9 rows in set (0.16 sec)

    练习题目和答案:

    建议看看输出的结果自己写下sql,不要单纯的复制粘贴。

    因为数据量比较大, 很多时候我都加了5条数据的限制。

    #1.查找整个职员表的所有内容。
    mysql> select * from employees limit 5;
    +--------+------------+------------+-----------+--------+------------+
    | emp_no | birth_date | first_name | last_name | gender | hire_date |
    +--------+------------+------------+-----------+--------+------------+
    | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
    | 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 |
    | 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 |
    | 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
    | 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 |
    +--------+------------+------------+-----------+--------+------------+
    5 rows in set (0.00 sec)

    #2.查看雇员名字(last_name)。
    mysql> select last_name from employees limit 5;
    +-----------+
    | last_name |
    +-----------+
    | Facello |
    | Simmel |
    | Bamford |
    | Koblick |
    | Maliniak |
    +-----------+
    5 rows in set (0.00 sec)

    #3.查看雇员编号、名字和部门
    mysql> select e.last_name,e.emp_no,d.dept_name from employees e,departments d,dept_emp de where e.emp_no = de.emp_no and de.dept_no = d.dept_no limit 5;
    +-------------+--------+------------------+
    | last_name | emp_no | dept_name |
    +-------------+--------+------------------+
    | Sluis | 10011 | Customer Service |
    | Lortz | 10038 | Customer Service |
    | Tramer | 10049 | Customer Service |
    | Billingsley | 10060 | Customer Service |
    | Syrzycki | 10088 | Customer Service |
    +-------------+--------+------------------+
    5 rows in set (0.00 sec)

    #4.显示所有雇员的工号、姓名、工资
    mysql> select e.emp_no,concat(e.last_name,' ',e.first_name) name,s.salary from employees e,salaries s where e.emp_no=s.emp_no limit 5;
    +--------+----------------+--------+
    | emp_no | name | salary |
    +--------+----------------+--------+
    | 10001 | Facello Georgi | 60117 |
    | 10001 | Facello Georgi | 62102 |
    | 10001 | Facello Georgi | 66074 |
    | 10001 | Facello Georgi | 66596 |
    | 10001 | Facello Georgi | 66961 |
    +--------+----------------+--------+
    5 rows in set (0.00 sec)

    #5.查找在d005号部门工作的雇员
    mysql> select e.emp_no,concat(e.last_name,' ',e.first_name) name,d.dept_no,d.dept_name from employees e,dept_emp de,departments d where e.emp_no = de.emp_no and de.dept_no = d.dept_no and d.dept_no = 'd005' limit 5;
    +--------+--------------------+---------+-------------+
    | emp_no | name | dept_no | dept_name |
    +--------+--------------------+---------+-------------+
    | 10001 | Facello Georgi | d005 | Development |
    | 10006 | Preusig Anneke | d005 | Development |
    | 10008 | Kalloufi Saniya | d005 | Development |
    | 10012 | Bridgland Patricio | d005 | Development |
    | 10014 | Genin Berni | d005 | Development |
    +--------+--------------------+---------+-------------+
    5 rows in set (0.05 sec)

    #6.要求查找职位为Engineer和Senior Engineer的雇员姓名(last_name)
    mysql> select e.last_name from employees e,titles t where t.emp_no=e.emp_no and t.title in('Engineer','Senior Engineer') limit 5;
    +-----------+
    | last_name |
    +-----------+
    | Facello |
    | Bamford |
    | Koblick |
    | Koblick |
    | Preusig |
    +-----------+
    5 rows in set (0.00 sec)

    #7.查找职位不是Engineer和Senior Engineer的部门编号,雇员部门及姓名。将姓名显示为(first_name+last_name命名为”Name”)
    mysql> select d.dept_no,d.dept_name,concat(e.first_name,' ',e.last_name) name from employees e,titles t,dept_emp de,departmentss d where e.emp_no=de.emp_no and de.dept_no = d.dept_no and t.emp_no = e.emp_no and t.title not in('Engineer','Senior Engineer'') limit 5;
    +---------+------------------+--------------+
    | dept_no | dept_name | name |
    +---------+------------------+--------------+
    | d009 | Customer Service | Mary Sluis |
    | d009 | Customer Service | Huan Lortz |
    | d009 | Customer Service | Huan Lortz |
    | d009 | Customer Service | Basil Tramer |
    | d009 | Customer Service | Basil Tramer |
    +---------+------------------+--------------+
    5 rows in set (0.00 sec)

    #8.查找哪些雇员的工资在60000到90000之间
    mysql> select concat(e.first_name,' ',e.last_name) name,s.salary from employees e,salaries s where s.salary between 60000 and 90000 and e.emp_no = s.emp_no limit 5;
    +----------------+--------+
    | name | salary |
    +----------------+--------+
    | Georgi Facello | 60117 |
    | Georgi Facello | 62102 |
    | Georgi Facello | 66074 |
    | Georgi Facello | 66596 |
    | Georgi Facello | 66961 |
    +----------------+--------+
    5 rows in set (0.00 sec)

    #9.查找哪些雇员的工资不在60000到90000之间
    mysql> select concat(e.first_name,' ',e.last_name) name,s.salary from employees e,salaries s where s.salary not between 60000 and 90000 and e.emp_no = s.emp_no limit 10;
    +-------------------+--------+
    | name | salary |
    +-------------------+--------+
    | Parto Bamford | 40006 |
    | Parto Bamford | 43616 |
    | Parto Bamford | 43466 |
    | Parto Bamford | 43636 |
    | Parto Bamford | 43478 |
    | Parto Bamford | 43699 |
    | Parto Bamford | 43311 |
    | Chirstian Koblick | 40054 |
    | Chirstian Koblick | 42283 |
    | Chirstian Koblick | 42542 |
    +-------------------+--------+
    10 rows in set (0.00 sec)

    #10.查找first_name以P开头,后面仅有四个字母的雇员信息
    mysql> select e.emp_no,concat(e.first_name,' ',e.last_name) name from employees e where e.first_name like 'P____' and e.first_name not like 'p__' limit 5;
    +--------+---------------------+
    | emp_no | name |
    +--------+---------------------+
    | 10003 | Parto Bamford |
    | 10101 | Perla Heyers |
    | 10138 | Perry Shimshoni |
    | 10353 | Phule Hammerschmidt |
    | 10387 | Parto Wrigley |
    +--------+---------------------+
    5 rows in set (0.00 sec)

    #11.查找last_name以K开头的雇员信息
    mysql> select e.emp_no,concat(e.first_name,' ',e.last_name) name from employees e where e.first_name like 'K%' limit 5;
    +--------+----------------------+
    | emp_no | name |
    +--------+----------------------+
    | 10005 | Kyoichi Maliniak |
    | 10016 | Kazuhito Cappelletti |
    | 10018 | Kazuhide Peha |
    | 10031 | Karsten Joslin |
    | 10066 | Kwee Schusler |
    +--------+----------------------+
    5 rows in set (0.00 sec)

    #12.查找名字以字母K开头,以i结尾,并且第三个字母为o的雇员名字(First_name)、职位和所在部门号
    mysql> select concat(e.first_name,' ',e.last_name) name,t.title,de.dept_no from employees e,dept_emp de,titles t where e.first_name like 'K_o%i' and e.emp_no = t.emp_no and e.emp_no = de.emp_no limit 5;
    +------------------+--------------+---------+
    | name | title | dept_no |
    +------------------+--------------+---------+
    | Kyoichi Maliniak | Senior Staff | d003 |
    | Kyoichi Maliniak | Staff | d003 |
    | Kyoichi Wossner | Staff | d007 |
    | Kyoichi Flexer | Senior Staff | d007 |
    | Kyoichi Flexer | Staff | d007 |
    +------------------+--------------+---------+
    5 rows in set (0.00 sec)

    #13.查找哪些雇员的职位名不以Se开头
    mysql> select concat(e.first_name,' ',last_name) name,t.title from employees e,titles t where e.emp_no = t.emp_no and t.title not like 'Se%' limit 5;
    +-------------------+--------------------+
    | name | title |
    +-------------------+--------------------+
    | Bezalel Simmel | Staff |
    | Chirstian Koblick | Engineer |
    | Kyoichi Maliniak | Staff |
    | Tzvetan Zielinski | Staff |
    | Saniya Kalloufi | Assistant Engineer |
    +-------------------+--------------------+
    5 rows in set (0.00 sec)

    #14.查找d005号部门里不是Staff的雇员信息
    mysql> select concat(e.first_name,' ',last_name) name,t.title from employees e,dept_emp de,titles t where e.emp_no = de.emp_no and e.emp_no = t.emp_no and t.title != 'Staff' limit 5;
    +-------------------+-----------------+
    | name | title |
    +-------------------+-----------------+
    | Georgi Facello | Senior Engineer |
    | Parto Bamford | Senior Engineer |
    | Chirstian Koblick | Engineer |
    | Chirstian Koblick | Senior Engineer |
    | Kyoichi Maliniak | Senior Staff |
    +-------------------+-----------------+
    5 rows in set (0.00 sec)

    #15.查找d005号部门工资大于100000的员工的信息
    mysql> select concat(e.first_name,' ',last_name) name,t.title,s.salary from employees e,dept_emp de,titles t,salaries s where e.emp_no = de.emp_no and e.emp_no = t.emp_no and e.emp_no = s.emp_no and de.dept_no = 'd005' and s.salary > 100000 limit 5;
    +---------------+--------------------+--------+
    | name | title | salary |
    +---------------+--------------------+--------+
    | Kwee Schusler | Assistant Engineer | 102425 |
    | Kwee Schusler | Assistant Engineer | 102674 |
    | Kwee Schusler | Assistant Engineer | 103672 |
    | Kwee Schusler | Engineer | 102425 |
    | Kwee Schusler | Engineer | 102674 |
    +---------------+--------------------+--------+
    5 rows in set (0.00 sec)

    #16.按字母顺序显示雇员的名字(last_name)
    mysql> select concat(e.first_name,' ',last_name) name from employees e order by e.last_name limit 5;
    +------------------+
    | name |
    +------------------+
    | Aluzio Aamodt |
    | Sachem Aamodt |
    | Sreenivas Aamodt |
    | Mokhtar Aamodt |
    | Bartek Aamodt |
    +------------------+
    5 rows in set (0.24 sec)

    #17.按部门编号降序显示雇员信息
    mysql> select concat(e.first_name,' ',last_name) name,de.dept_no from employees e,dept_emp de where e.emp_no = de.emp_no order by de.dept_no desc limit 5;
    +-------------------+---------+
    | name | dept_no |
    +-------------------+---------+
    | Pohua Sichman | d009 |
    | Uri Juneja | d009 |
    | Mohammed Pleszkun | d009 |
    | Chiranjit Kuzuoka | d009 |
    | Ronghao Morrow | d009 |
    +-------------------+---------+
    5 rows in set (0.00 sec)

    #18.计算每个部门的平均工资和工资总和
    mysql> select de.dept_no,sum(s.salary),avg(s.salary) from employees e,dept_emp de,salaries s where e.emp_no = de.emp_no and e.emp_no = s.emp_no group by de.dept_no;
    +---------+---------------+---------------+
    | dept_no | sum(s.salary) | avg(s.salary) |
    +---------+---------------+---------------+
    | d001 | 13725425266 | 71913.2000 |
    | d002 | 11650834677 | 70489.3649 |
    | d003 | 9363811425 | 55574.8794 |
    | d004 | 41554438942 | 59605.4825 |
    | d005 | 48179456393 | 59478.9012 |
    | d006 | 10865203635 | 57251.2719 |
    | d007 | 40030089342 | 80667.6058 |
    | d008 | 11969730427 | 59665.1817 |
    | d009 | 13143639841 | 58770.3665 |
    +---------+---------------+---------------+
    9 rows in set (7.14 sec)

    #19.查询每个部门的每个职位的雇员数
    mysql> select de.dept_no,t.title,sum(e.emp_no) from employees e,dept_emp de,titles t where e.emp_no = de.emp_no and e.emp_no = t.emp_no group by de.dept_no,t.title limit 5;
    +---------+--------------+---------------+
    | dept_no | title | sum(e.emp_no) |
    +---------+--------------+---------------+
    | d001 | Manager | 220061 |
    | d001 | Senior Staff | 3561178455 |
    | d001 | Staff | 4142508539 |
    | d002 | Manager | 220199 |
    | d002 | Senior Staff | 3090249864 |
    +---------+--------------+---------------+
    5 rows in set (3.13 sec)

    #20.请算出employees表中所有雇员的平均工资
    mysql> select avg(s.salary) from employees e,salaries s where e.emp_no = s.emp_no;
    +---------------+
    | avg(s.salary) |
    +---------------+
    | 63810.7448 |
    +---------------+
    1 row in set (4.48 sec)

    #21.请查询出employees表中的最低工资的员工信息
    mysql> select concat(first_name,' ',last_name) name from employees e,salaries s where e.emp_no = s.emp_no and s.salary = (selecct min(s.salary) salary from salaries s);
    +--------------+
    | name |
    +--------------+
    | Olivera Baek |
    +--------------+
    1 row in set (1.99 sec)

    #22.请计算出每个部门的平均工资、最高工资和最低工资
    mysql> select de.dept_no,avg(s.salary),max(s.salary),min(s.salary) from employees e,dept_emp de,salaries s where e.emp_no = de.emp_no and e.emp_no = s.emp_no group by de.dept_no;
    +---------+---------------+---------------+---------------+
    | dept_no | avg(s.salary) | max(s.salary) | min(s.salary) |
    +---------+---------------+---------------+---------------+
    | d001 | 71913.2000 | 145128 | 39127 |
    | d002 | 70489.3649 | 142395 | 38812 |
    | d003 | 55574.8794 | 141953 | 38735 |
    | d004 | 59605.4825 | 138273 | 38623 |
    | d005 | 59478.9012 | 144434 | 38849 |
    | d006 | 57251.2719 | 132103 | 38786 |
    | d007 | 80667.6058 | 158220 | 39169 |
    | d008 | 59665.1817 | 130211 | 38851 |
    | d009 | 58770.3665 | 144866 | 38836 |
    +---------+---------------+---------------+---------------+
    9 rows in set (7.06 sec)

    #23.查询薪水发放时间在1986-06-26 ~ 1987-06-25薪水高于46145号雇员并且工种与他相同的雇员情况。
    select e.* from employees e,titles t,salaries s where e.emp_no = t.emp_no and e.emp_no = t.emp_no and t.title = (select title from titles where emp_no = 46135) and s.salary > (select s.salary from salaries s where s.from_date > str_to_date('1986-06-26','%Y-%m-%d') and s.to_date < str_to_date('1987-06-2 25','%Y-%m-%d') and s.emp_no = '46135') and s.from_date > str_to_date('1986-06-26','%Y-%m-%d') and s.to_date < str_to_date('1987-06-2 25','%Y-%m-%d') limit 10;
    +--------+------------+------------+------------+--------+------------+
    | emp_no | birth_date | first_name | last_name | gender | hire_date |
    +--------+------------+------------+------------+--------+------------+
    | 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
    | 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 |
    | 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 |
    | 10012 | 1960-10-04 | Patricio | Bridgland | M | 1992-12-18 |
    | 10014 | 1956-02-12 | Berni | Genin | M | 1987-03-11 |
    | 10018 | 1954-06-19 | Kazuhide | Peha | F | 1987-04-03 |
    | 10020 | 1952-12-24 | Mayuko | Warwick | M | 1991-01-26 |
    | 10022 | 1952-07-08 | Shahaf | Famili | M | 1995-08-22 |
    | 10023 | 1953-09-29 | Bojan | Montemayor | F | 1989-12-17 |
    | 10026 | 1953-04-03 | Yongqiao | Berztiss | M | 1995-03-20 |
    +--------+------------+------------+------------+--------+------------+
    10 rows in set, 2 warnings (0.05 sec)


    #24.查询工资在10000到50000之间的雇员所在部门的所有人员的信息。
    mysql> select e.*,d.dept_no,d.dept_name from employees e,dept_emp de,departments d where e.emp_no = de.emp_no and de.dept_no =d.dept_no and e.emp_no in (select distinct s.emp_no from salaries s where s.salary between 10000 and 50000) limit 10;
    +--------+------------+-------------+-----------+--------+------------+---------+------------------+
    | emp_no | birth_date | first_name | last_name | gender | hire_date | dept_no | dept_name |
    +--------+------------+-------------+-----------+--------+------------+---------+------------------+
    | 10011 | 1953-11-07 | Mary | Sluis | F | 1990-01-22 | d009 | Customer Service |
    | 10038 | 1960-07-20 | Huan | Lortz | M | 1989-09-20 | d009 | Customer Service |
    | 10049 | 1961-04-24 | Basil | Tramer | F | 1992-05-04 | d009 | Customer Service |
    | 10098 | 1961-09-23 | Sreekrishna | Servieres | F | 1985-05-13 | d009 | Customer Service |
    | 10112 | 1963-08-13 | Yuichiro | Swick | F | 1985-10-08 | d009 | Customer Service |
    | 10115 | 1964-12-25 | Chikara | Rissland | M | 1986-01-23 | d009 | Customer Service |
    | 10126 | 1954-08-07 | Kayoko | Valtorta | M | 1985-09-08 | d009 | Customer Service |
    | 10128 | 1958-02-15 | Babette | Lamba | F | 1988-06-06 | d009 | Customer Service |
    | 10137 | 1959-07-30 | Maren | Hutton | M | 1985-02-18 | d009 | Customer Service |
    | 10154 | 1957-01-17 | Abdulah | Thibadeau | F | 1990-12-12 | d009 | Customer Service |
    +--------+------------+-------------+-----------+--------+------------+---------+------------------+
    10 rows in set (1.28 sec)

    #25.查询出在薪水发放时间在1986-06-26 ~ 1987-06-25的员工信息
    ###(工号,姓名,性别,薪水,职位)
    mysql> select e.emp_no,concat(e.first_name," ",e.last_name) name,e.gender,s.salary,t.title from employees e,salaries s,titles t where e.emp_no = s.emp_no and e.emp_no = t.emp_no and s.from_date > str_to_date('1986-06-26','%Y-%m-%d') and s.to_date < str_to_date('1987-06-2 25','%Y-%m-%d') limit 10;
    +--------+-------------------+--------+--------+-----------------+
    | emp_no | name | gender | salary | title |
    +--------+-------------------+--------+--------+-----------------+
    | 13543 | Tua Garigliano | F | 78646 | Staff |
    | 13589 | Arfst Munck | F | 44306 | Staff |
    | 14085 | Jiafu Constantine | F | 43575 | Staff |
    | 14613 | Kien Herath | M | 54124 | Staff |
    | 19083 | Nobuyoshi Asmuth | M | 40000 | Engineer |
    | 19135 | Leaf Soicher | M | 40000 | Senior Engineer |
    | 19761 | Vasiliy Niizuma | M | 65098 | Staff |
    | 19769 | Bezalel Holburn | M | 53118 | Engineer |
    | 22892 | Giao Monkewich | M | 79554 | Senior Staff |
    | 24101 | Yakichi Manderick | F | 45092 | Engineer |
    +--------+-------------------+--------+--------+-----------------+
    10 rows in set, 1 warning (0.07 sec)

    ]]>
    + + sql + + + mysql + +
    + + Linux安装tree命令 - apt-get install tree + /20170503-Linux%E5%AE%89%E8%A3%85tree%E5%91%BD%E4%BB%A4%20-%20apt-get%20install%20tree/ + 目前debian系列的系统都无法使用apt-get install tree来安装tree命令

    以下是安装步骤

    #下载(官网为:http://mama.indstate.edu/users/ice/tree/)
    wget http://mama.indstate.edu/users/ice/tree/src/tree-1.7.0.tgz
    #解压
    tar -zxvf tree-1.7.0.tgz
    #进入目录
    cd tree-1.7.0
    #安装
    sudo make install
    #完毕

    最简单的使用方法,在目录下输入:tree

    使用效果

    hisen@ubuntu:~/dl$ tree
    .
    ├── hisen.log
    ├── master
    └── test_db-master
    ├── Changelog
    ├── employees_partitioned_5.1.sql
    ├── employees_partitioned.sql
    ├── employees.sql
    ├── images
    │   ├── employees.gif
    │   ├── employees.jpg
    │   └── employees.png
    ├── load_departments.dump
    ├── load_dept_emp.dump
    ├── load_dept_manager.dump
    ├── load_employees.dump
    ├── load_salaries1.dump
    ├── load_salaries2.dump
    ├── load_salaries3.dump
    ├── load_titles.dump
    ├── objects.sql
    ├── README.md
    ├── sakila
    │   ├── README.md
    │   ├── sakila-mv-data.sql
    │   └── sakila-mv-schema.sql
    ├── show_elapsed.sql
    ├── sql_test.sh
    ├── test_employees_md5.sql
    └── test_employees_sha.sql

    3 directories, 26 files

    使用参数

    tree命令行参数:

    -a 显示所有文件和目录。
    -A 使用ASNI绘图字符显示树状图而非以ASCII字符组合。
    -C 在文件和目录清单加上色彩,便于区分各种类型。
    -d 显示目录名称而非内容。
    -D 列出文件或目录的更改时间。
    -f 在每个文件或目录之前,显示完整的相对路径名称。
    -F 在执行文件,目录,Socket,符号连接,管道名称名称,各自加上"*","/","=","@","|"号。
    -g 列出文件或目录的所属群组名称,没有对应的名称时,则显示群组识别码。
    -i 不以阶梯状列出文件或目录名称。
    -I 不显示符合范本样式的文件或目录名称。
    -l 如遇到性质为符号连接的目录,直接列出该连接所指向的原始目录。
    -n 不在文件和目录清单加上色彩。
    -N 直接列出文件和目录名称,包括控制字符。
    -p 列出权限标示。
    -P 只显示符合范本样式的文件或目录名称。
    -q 用"?"号取代控制字符,列出文件和目录名称。
    -s 列出文件或目录大小。
    -t 用文件和目录的更改时间排序。
    -u 列出文件或目录的拥有者名称,没有对应的名称时,则显示用户识别码。
    -x 将范围局限在现行的文件系统中,若指定目录下的某些子目录,其存放于另一个文件系统上,则将该子目录予以排除在寻找范围外。

    ]]>
    + + linux + + + linux + +
    + + 网件 R8000 利用U盘扩展 OpenWrt overlay 空间 + /20221030-OpenWrt_NETGEAR_R8000_use_USB_flash_disk_to_expand_overlay/ + 一、问题

    OpenWrt 官方关于 netgear r8000 的信息
    https://openwrt.org/toh/netgear/r8000
    “Second data partition (79 MiB) not available in OpenWrt”

    128M 的空间,安装 OpenWrt 之后就剩下 21M 的空间可以安装插件
    那自然是不够用的了,于是乎就找了些扩大空间的办法,最靠谱的就是利用U盘挂载

    二、解决

    2.1 获取U盘信息

    插上 U盘 至 USB3 插口
    登录 OpenWrt 控制台,查看 USB 在 OpenWrt 中的名称
    也就是 /dev/sda

    root@OpenWrt:~# cat /proc/scsi/usb-storage/0
    Host scsi0: usb-storage
    Vendor: SanDisk
    Product: Extreme
    Serial Number: AA010316152154386775
    Protocol: Transparent SCSI
    Transport: Bulk
    Quirks: SANE_SENSE

    root@OpenWrt:~# ls /dev/sda*
    /dev/sda

    2.2 安装所需软件

    安装相关软件
    因为需要格式化 U盘 至 ext4 的格式
    以及挂载U盘空间至 overlay

    root@OpenWrt:~# opkg update && opkg install block-mount e2fsprogs kmod-fs-ext4 kmod-usb-storage kmod-usb2 kmod-usb3

    2.3 格式化U盘

    格式化 U盘 至 ext4

    root@OpenWrt:~# mkfs.ext4 /dev/sda
    mke2fs 1.46.5 (30-Dec-2021)
    /dev/sda contains a ext4 file system
    created on Sat Oct 29 08:03:58 2022
    Proceed anyway? (y,N) y

    2.3 挂载 U盘

    挂载 U盘 至 overlay
    重启之后,就大功告成
    放心地去安装各种插件吧

    root@OpenWrt:~# mount /dev/sda /mnt
    root@OpenWrt:~# mkdir /tmp/root
    root@OpenWrt:~# mount -o bind / /tmp/root
    root@OpenWrt:~# cp /tmp/root/* /mnt -a
    root@OpenWrt:~# umount /tmp/root
    root@OpenWrt:~# umount /mnt
    root@OpenWrt:~# block detect > /etc/config/fstab
    root@OpenWrt:~# uci set fstab.@mount[0].target='/overlay'
    root@OpenWrt:~# uci set fstab.@mount[0].enabled='1'
    root@OpenWrt:~# uci commit fstab
    root@OpenWrt:~# reboot

    三、参考

    OpenWrt挂载U盘或SD卡作为根文件系统

    ]]>
    + + homelab + + + OpenWrt + +
    + + OpenWrt 新版本安装 passwall + /20221030-OpenWrt_22.03_install_passwall/ + 一、背景

    最近看了不少软路由的东西
    于是折腾了一波 OpenWrt
    奈何网件 R8000 配置一般,跑起来体验不好。
    后续估计是会上 x86 主机了,虽然可能会性能过剩。

    二、问题

    在官方原版的 OpenWrt 22.03 版本中
    如果直接在 software 中安装 luci_app_passwall
    那么安装后会提示没法实现透明代理,还需要安装一些额外的软件才行

    三、解决问题

    opkg install ipset ipt2socks iptables iptables-mod-conntrack-extra iptables-mod-iprange iptables-mod-socket iptables-mod-tproxy kmod-ipt-nat

    安装完成之后,再按教程操作即可.
    原因就是新版本的系统中默认不包含上述模块。
    问题解决参考自 GitHub issue.

    四、参考

    ]]>
    + + homelab + + + OpenWrt + +
    + + Oracle VM VirtualBox命令行打开虚拟机并且后台运行 + /20170219-Oracle-VM-VirtualBox%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%89%93%E5%BC%80%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%B9%B6%E4%B8%94%E5%90%8E%E5%8F%B0%E8%BF%90%E8%A1%8C/ + 这几天在折腾Ubuntu虚拟机,想着也就让他做一台服务器罢了

    没想到安装之后发现一直没法让VirtualBox隐藏到托盘

    按正常程序走,打开一个虚拟机会出现两个GUI界面:

    1. VirtualBox界面
    2. 虚拟机界面

    第一个可以在打开虚拟机之后关闭,第二个不能关闭也不能隐藏到托盘

    痛苦!!!

    这里给出解决方案:通过cmd命令行启动并且后台运行

    一、进入VirtualBox安装目录,我这里是

    C:\tool\Oracle\VirtualBox>

    二、列出所有安装的虚拟机

    C:\tool\Oracle\VirtualBox>VBoxManage list vms
    "centos" {162f777f-e3c4-4717-8b5b-4a4e43a8b552}
    "debian" {48ebecba-77ec-483d-ab73-bf9ee777e2f6}
    "ubuntu" {d8050b3a-b04b-4fe9-8a90-16086dac0ae9}

    前面是NAME,后面是UUID,之后的name用这连个代替都可以

    三、命令行启动虚拟机

    C:\tool\Oracle\VirtualBox>VBoxManage startvm "ubuntu" --type headless
    Waiting for VM "ubuntu" to power on...
    VM "ubuntu" has been successfully started.

    headless是在后台运行,并且默认开启vrdp服务,可以通过远程桌面工具来访问

    下面是打开ubuntu之后用Xshell链接的

    Connecting to 127.0.0.1:2222...
    Connection established.
    To escape to local shell, press 'Ctrl+Alt+]'.

    Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-62-generic x86_64)

    * Documentation: https://help.ubuntu.com/

    362 个可升级软件包。
    0 个安全更新。

    Last login: Sat Feb 18 16:25:58 2017 from 10.0.2.2
    hisen@hisen-VirtualBox:~$ ls
    download 公共的 模板 视频 图片 文档 下载 音乐 桌面

    四、所有相关的命令

    下面所有的NAME都可以用二中的name和UUID代替

    # 列出所有安装的虚拟机
    VBoxManage list vms

    # 后台打开,无界面
    VBoxManage startvm "NAME" --type headless

    # gui方式启动,跟桌面点击没有区别
    VBoxManage startvm NAME --type gui

    # 列出运行中的虚拟机
    VBoxManage list runningvms

    # 关闭虚拟机,等价于点击系统关闭按钮,正常关机
    VBoxManage controlvm NAME acpipowerbutton

    # 关闭虚拟机,等价于直接关闭电源,非正常关机
    VBoxManage controlvm NAME poweroff

    # 暂停虚拟机的运行
    VBoxManage controlvm NAME pause

    # 恢复暂停的虚拟机
    VBoxManage controlvm NAME resume

    # 保存当前虚拟机的运行状态
    VBoxManage controlvm NAME savestate

    ]]>
    + + linux + + + linux + ubuntu + +
    + + Oracle 取微秒 - 记录点滴 + /20170923-Oracle-%E5%8F%96%E5%BE%AE%E7%A7%92-%E8%AE%B0%E5%BD%95%E7%82%B9%E6%BB%B4/ + 昨天在群里看到有人问怎么通过sql在oracle中取微秒

    以前没有遇到过,就搜索了一下,找了一会给找到了

    select to_char(systimestamp, 'yyyy-mm-dd hh24:mi:ss ff') from dual;

    输出:年-月-日 时:分:秒 微秒

    2017-9-24 10:38:27 129368

    很少问题是搜索引擎找不到的,学会如何描述问题才是关键

    ]]>
    + + java + + + sql + oracle + +
    + + Oracle SQL语句优化 - 写出高效SQL + /20170120-Oracle-SQL%E8%AF%AD%E5%8F%A5%E4%BC%98%E5%8C%96-%E5%86%99%E5%87%BA%E9%AB%98%E6%95%88SQL/ + [1]选择最有效率的表名顺序

    只在基于规则的优化器中有效,ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.

    [2]WHERE子句中的连接顺序

    ORACLE采用自下而上(从后往前)的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾

    [3]SELECT子句中避免使用’*’

    ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间.需要什么字段就查询什么字段,永远不要查询出不需要的字段来

    [4]减少访问数据库的次数

    ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等;尽量使用缓存技术;

    [5]设置单次访问合适的检索数据量

    在SQLPlus , SQLForms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200

    [6]使用DECODE函数来减少处理时间

    使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表,因为decode函数有短路效应,类似java中短路与,有合适的就会返回而不继续扫描后面的内容

    [7]整合简单,无关联的数据库访问

    如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系),原因见[4]

    [8]删除重复记录,最高效的方法

    因为这里使用的是rowid

    DELETE
    FROM EMP E
    WHERE E.ROWID >
    (SELECT MIN(X.ROWID)
    FROM EMP X
    WHERE X.EMP_NO = E.EMP_NO);

    [9]用TRUNCATE替代DELETE

    当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短. (注: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML)

    [10]尽量多使用COMMIT

    只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少:
    COMMIT所释放的资源:

    1. 回滚段上用于恢复数据的信息
    2. 被程序语句获得的锁
    3. redo log buffer 中的空间
    4. ORACLE为管理上述3种资源中的内部花费

    [11]用Where子句替换HAVING子句

    避免使用HAVING子句, HAVING在检索出所有记录后对结果集进行过滤。这个处理需要排序,总计等操作。 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里

    [12]减少对表的查询

    在含有子查询的SQL语句中,要特别注意减少对表的查询。例子:

    SELECT TAB_NAME
    FROM TABLES
    WHERE (TAB_NAME,
    DB_VER) =
    (SELECT TAB_NAME,
    DB_VER
    FROM TAB_COLUMNS
    WHERE VERSION = 604)

    [13]通过内部函数提高SQL效率

    复杂的SQL往往牺牲了执行效率。能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的。一般把复杂的sql分解再拼起来。

    [14]使用表的别名(Alias)

    当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上。这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。

    [15]用EXISTS替代IN、用NOT EXISTS替代NOT IN

    在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率。在子查询中,NOT IN子句将执行一个内部的排序和合并。无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历).。为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS.

    高效:

    SELECT *
    FROM EMP (基础表)
    WHERE EMPNO > 0
    AND EXISTS
    (SELECT 'X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = 'MELB')

    低效:

    SELECT *
    FROM EMP (基础表)
    WHERE EMPNO > 0
    AND DEPTNO IN
    (SELECT DEPTNO
    FROM DEPT
    WHERE LOC = 'MELB')

    [16]识别’低效执行’的SQL语句

    虽然目前各种关于SQL优化的图形化工具层出不穷

    但是写出自己的SQL工具来解决问题始终是一个最好的方法:

    SELECT EXECUTIONS,
    DISK_READS,
    BUFFER_GETS,
    ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,
    ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,
    SQL_TEXT
    FROM V$SQLAREA
    WHERE EXECUTIONS>0
    AND BUFFER_GETS > 0
    AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8
    ORDER BY 4 DESC;

    [17]用索引提高效率

    索引是表的一个概念部分,用来提高检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构。通常,通过索引查询数据比全表扫描要快。 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引。同样在联结多个表时使用索引也可以提高效率。另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证。那些LONG或LONG RAW数据类型,你可以索引几乎所有的列。 通常,在大型表中使用索引特别有效。当然,你也会发现,在扫描小表时,使用索引同样能提高效率。虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时, 索引本身也会被修改。这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O 。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。定期的重构索引是有必要的。

    ALTER  INDEX <INDEXNAME> REBUILD <TABLESPACENAME>

    [18]用EXISTS替换DISTINCT

    当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT。一般可以考虑用EXIST替换,EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果。

    低效:

    SELECT DISTINCT DEPT_NO,
    DEPT_NAME
    FROM DEPT D,
    EMP E
    WHERE D.DEPT_NO = E.DEPT_NO

    高效:

    SELECT DEPT_NO,
    DEPT_NAME
    FROM DEPT D
    WHERE EXISTS
    (SELECT 'X'
    FROM EMP E
    WHERE E.DEPT_NO = D.DEPT_NO);

    [19]sql语句用大写

    因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行

    [20]在java代码中尽量少用连接符“+”连接字符串

    一般来说StringBuilder(非线程安全)是一个不错的选择

    [21]通常避免在索引列上使用NOT

    我们要避免在索引列上使用NOT,NOT会产生在和在索引列上使用函数相同的影响。当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描。

    [22]避免在索引列上使用计算

    WHERE子句中,如果索引列是函数的一部分。优化器将不使用索引而使用全表扫描。

    举例:

    低效:

    SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;

    高效:

    SELECT … FROM DEPT WHERE SAL > 25000/12;

    [23]用>=替代>

    高效:

    SELECT * FROM  EMP  WHERE  DEPTNO >=4

    低效:

    SELECT * FROM EMP WHERE DEPTNO >3

    两者的区别在于,前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录。

    [24]用UNION替换OR (适用于索引列)

    通常情况下,用UNION替换WHERE子句中的OR将会起到较好的效果。对索引列使用OR将造成全表扫描。注意,以上规则只针对多个索引列有效。如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低。在下面的例子中, LOC_ID 和REGION上都建有索引。

    高效:

    SELECT LOC_ID,
    LOC_DESC,
    REGION
    FROM LOCATION
    WHERE LOC_ID = 10
    UNION
    SELECT LOC_ID,
    LOC_DESC,
    REGION
    FROM LOCATION
    WHERE REGION = ''MELBOURNE''

    低效:

    SELECT LOC_ID,
    LOC_DESC,
    REGION
    FROM LOCATION
    WHERE LOC_ID = 10
    OR REGION = ''MELBOURNE''

    如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面

    [25]用IN来替换OR

    这是一条简单易记的规则,但是实际的执行效果还须检验,在ORACLE8i下,两者的执行路径似乎是相同的。

    低效:

    SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30

    高效:

    SELECT… FROM LOCATION WHERE LOC_IN  IN (10,20,30);

    [26]避免在索引列上使用IS NULL和IS NOT NULL

    避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引。对于单列索引,如果列包含空值,索引中将不存在此记录。对于复合索引,如果每个列都为空,索引中同样不存在此记录。如果至少有一个列不为空,则记录存在于索引中。

    举例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入)。然而如果所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空。 因此你可以插入1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引。

    低效: (索引失效)

    SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL;

    高效: (索引有效)

    SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0;

    [27]总是使用索引的第一个列

    如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引。这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引

    [28]用UNION-ALL替换UNION

    当SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序。如果用UNION ALL替代UNION, 这样排序就不是必要了。效率就会因此得到提高。需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录。因此各位还是要从业务需求分析使用UNION ALL的可行性。 UNION 将对结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存。对于这块内存的优化也是相当重要的。下面的SQL可以用来查询排序的消耗量。

    低效:

    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    UNION
    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'

    高效:

    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    UNION ALL
    SELECT ACCT_NUM,
    BALANCE_AMT
    FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95'

    [29]用WHERE替代ORDER BY

    ORDER BY 子句只在两种严格的条件下使用索引

    ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序

    ORDER BY中所有的列必须定义为非空

    WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列

    例如:

    表DEPT包含以下列:

    DEPT_CODE PK NOT NULL

    DEPT_DESC NOT NULL

    DEPT_TYPE NULL

    低效: (索引不被使用)

    SELECT DEPT_CODE FROM  DEPT  ORDER BY  DEPT_TYPE

    高效: (使用索引)

    SELECT DEPT_CODE  FROM  DEPT  WHERE  DEPT_TYPE > 0

    [30]避免改变索引列的类型

    当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换

    假设 EMPNO是一个数值类型的索引列.

    SELECT …  FROM EMP  WHERE  EMPNO = '123'

    实际上,经过ORACLE类型转换, 语句转化为:

    SELECT …  FROM EMP  WHERE  EMPNO = TO_NUMBER('123')

    幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变

    现在,假设EMP_TYPE是一个字符类型的索引列

    SELECT …  FROM EMP  WHERE EMP_TYPE = 123

    这个语句被ORACLE转换为:

    SELECT …  FROM EMP  WHERETO_NUMBER(EMP_TYPE)=123

    因为内部发生的类型转换, 这个索引将不会被用到!

    为了避免ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来。 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型

    [31]需要当心的WHERE子句

    某些SELECT 语句中的WHERE子句不使用索引
    这里有一些例子

    在下面的例子里,

    1. ‘!=’ 将不使用索引。记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中
    2. ‘||’是字符连接函数. 就象其他函数那样, 停用了索引
    3. ‘+’是数学函数. 就象其他数学函数那样, 停用了索引
    4. 相同的索引列不能互相比较,这将会启用全表扫描

    [32]检索数据量超过表中30%,索引失效

    1. 如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高
    2. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别。 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍!

    [33]避免使用耗费资源的操作

    带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎
    执行耗费资源的排序(SORT)功能。

    DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序。
    通常, 带有UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写。

    如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以考虑的, 毕竟它们的可读性很强。

    [34]优化GROUP BY

    提高GROUP BY 语句的效率,可以通过将不需要的记录在GROUP BY 之前过滤掉。

    下面两个查询返回相同结果但第二个明显就快了许多。

    低效:

    SELECT JOB,
    AVG(SAL)
    FROM EMP
    GROUP JOB
    HAVING JOB = 'PRESIDENT'
    OR JOB = 'MANAGER'

    高效:

    SELECT JOB,
    AVG(SAL)
    FROM EMP
    WHERE JOB = 'PRESIDENT'
    OR JOB = 'MANAGER'
    GROUP JOB

    本文参考其他文章整理而来

    出处:http://www.cnblogs.com/rootq/archive/2008/11/17/1334727.html

    不过互联网上这篇文章很多,都没有版权注明。我也不知道原创是谁!

    ]]>
    + + sql + oracle + oarcle优化 + +
    + + Oracle中exists在update中无法限制住条件,在select中可以 + /20170323-Oracle%E4%B8%ADexists%E5%9C%A8update%E4%B8%AD%E6%97%A0%E6%B3%95%E9%99%90%E5%88%B6%E4%BD%8F%E6%9D%A1%E4%BB%B6%EF%BC%8C%E5%9C%A8select%E4%B8%AD%E5%8F%AF%E4%BB%A5/ + 这个问题是自己写的一个bug,标示不知道原因是什么

    现在暂时使用 in 代替解决了

    下面的查询是能限制住acct_type

    SELECT ew.customer_id,cf.acct_type
    FROM ew_quota_info ew,
    cf_customer cf
    WHERE cf.acct_type in(2,3)
    AND ew.customer_id = cf.id

    但是在update的时候,会把acct_type=1的也更新了

    update ew_quota_info ew
    set ew.all_amt = 100000
    where exists(
    SELECT ew.customer_id
    FROM ew_quota_info ew,
    cf_customer cf
    WHERE cf.acct_type in(2,3)
    AND ew.customer_id = cf.id
    )

    ]]>
    + + sql + + + mysql + oracle + +
    + + SQL优化之小细节 - 点滴记录 + /20170309-SQL%E4%BC%98%E5%8C%96%E4%B9%8B%E5%B0%8F%E7%BB%86%E8%8A%82%20-%20%E7%82%B9%E6%BB%B4%E8%AE%B0%E5%BD%95/ + 不知道现在是不是还很多人首先就把关联的id放在where的第一位

    这里有一个简单的对比,情况相同的时候,两个sql的时间相差八倍

    优:0.077s

    SELECT ew.all_amt ,
    ew.customer_id,
    cf.id,
    cf.cert_id,
    cf.acct_type
    FROM ew_quota_info ew,
    cf_customer cf
    WHERE cf.acct_type in(2,3)
    AND ew.customer_id = cf.id

    劣:0.630s

    SELECT ew.all_amt ,
    ew.customer_id,
    cf.id,
    cf.cert_id,
    cf.acct_type
    FROM ew_quota_info ew,
    cf_customer cf
    WHERE ew.customer_id = cf.id
    AND cf.acct_type in(2,3)

    上述原因:where子句从后往前执行,应该把大的过滤条件放在后面
    记录时间:2017年3月9日 10:44:59


    ]]>
    + + sql + + + java + sql + +
    + + Oracle - MySQL - 数据库事务隔离级别介绍 + /20171114-Oracle%20-%20MySQL%20-%20%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E4%BB%8B%E7%BB%8D/ + 名词解释
    1. 脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
    2. 不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
    3. 幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.

    数据库的4个事物隔离级别

    √:可能会出现
    ×:为不会出现

    name名称级别脏读不可重复读幻读
    Read uncommitted读未提交1
    Read committed读提交2×
    Repeatable read重复读3××
    Serializable序列化4×××

    oracle

    Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE.

    Oracle 默认的事务隔离级别为: READ COMMITED

    mysql

    Mysql 支持 4 中事务隔离级别.

    Mysql 默认的事务隔离级别为: REPEATABLE READ

    延伸

    参考:https://www.cnblogs.com/andy6/p/6045679.html

    ]]>
    + + sql + + + java + sql + +
    + + SQL语句的执行顺序 - 关键字的顺序 + /20171114-SQL%E8%AF%AD%E5%8F%A5%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/ + sql执行顺序
    1. from
    2. join
    3. on
    4. where
    5. group by(开始使用select中的别名,后面的语句中都可以使用)
    6. avg,sum….
    7. having
    8. select
    9. distinct
    10. order by

    参考资料

    http://blog.csdn.net/u014044812/article/details/51004754

    http://blog.csdn.net/bitcarmanlee/article/details/51004767 (含流程图)

    ]]>
    + + sql + + + java + sql + +
    + + oracle全局查找某字段所在的表 + /20170612-Oracle%E5%85%A8%E5%B1%80%E6%9F%A5%E6%89%BE%E6%9F%90%E5%AD%97%E6%AE%B5%E6%89%80%E5%9C%A8%E7%9A%84%E8%A1%A8/ + 下面这个存储过程是查找数据库中某个字段值为:hisen 的表

    declare
    l_cnt varchar2(20);
    v_sql varchar2(4000);
    v_tablename varchar(200);
    cursor cursor_jsdx is select 'select count(*) from ' || table_name || ' where NAME=''hisen''',table_name from user_tab_columns where column_name='NAME';
    --注:这里的字段名要大写
    begin
    open cursor_jsdx;
    Loop
    fetch cursor_jsdx into v_sql,v_tablename;
    exit when cursor_jsdx%notfound;
    execute immediate v_sql into l_cnt;
    if l_cnt >0 then
    ---如果该表有那内容的就打印那个表的名字。
    dbms_output.put_line(v_tablename);
    end if;
    end loop;
    Close cursor_jsdx;
    end;

    ]]>
    + + sql + + + oracle + +
    + + Oracle 分页的三种方式 + /20170411-Oracle%E5%88%86%E9%A1%B5%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F/ + 第一种:

    select *
    from (select t.*, rownum rn
    from (select * from EW_AUTH_FLOW) t
    where rownum <= 5)
    where rn > 2;

    第二种:

    select *
    from (select t.*, rownum rn from ew_auth_flow t where rownum <= 5)
    where rn > 2;

    第三种:

    select *
    from (select t.*, rownum rn from ew_auth_flow t)
    where rn between 2 and 5;

    ]]>
    + + database + + + sql + +
    + + Spring Boot CLI 安装(windows) + /20170713-Spring%20Boot%20CLI%20%E5%AE%89%E8%A3%85%EF%BC%88windows%EF%BC%89/ + SpringBootCLI是一个命令行工具,可用于快速搭建基于spring的原型。

    它支持运行Groovy脚本,这也就意味着你可以使用类似Java的语法,但不用写很多的模板代码。

    Spring Boot不一定非要配合CLI使用,但它绝对是Spring应用取得进展的最快方式.

    下载分发包

    1. 地址:getting-started-installing-the-cli
    2. 下载:spring-boot-cli-x.x.x.RELEASE-bin.zip (例如:spring-boot-cli-1.5.4.RELEASE-bin.zip)
    3. 解压
    4. 设置环境变量:C:\hisenwork\soft\spring-1.5.4.RELEASE\bin (在path中添加)
    5. cmd测试:spring –version
      spring --version
      Spring CLI v1.5.4.RELEASE

    运行一个简单程序

    文件名称:HelloController.groovy

    文件内容:

    @RestController
    class HelloController{
    @RequestMapping("/")
    def hello(){
    return "Hello World"
    }
    }

    运行程序:cmd进入文件所在文件夹,执行:spring run HelloController.groovy

    提醒:Resolving dependencies第一次初始化时间会久一点,耐心等待

    spring run HelloController.groovy

    Resolving dependencies..........................

    . ____ _ __ _ _
    /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
    \\/ ___)| |_)| | | | | || (_| | ) ) ) )
    ' |____| .__|_| |_|_| |_\__, | / / / /
    =========|_|==============|___/=/_/_/_/
    :: Spring Boot :: (v1.5.4.RELEASE)

    之后在浏览器中输入:http://localhost:8080/

    就能看到:Hello World

    ]]>
    + + java + + + java + +
    + + Oracle性能优化常用的SQL + /20170329-Oracle%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%B8%B8%E7%94%A8%E7%9A%84SQL/ + 使用如下sql能查出相应的信息,oracle博大精深要掌握得花时间

    太长了,来个阅读全文吧

    --显示数据库当前连接数
    select count(*) from v$process;

    --显示数据库最大连接数
    select value from v$parameter where name ='processes';

    --修改最大Oracle最大连接数:
    alter system set processes = 300 scope = spfile;

    --显示当前session连接数
    select count(*) from v$session;

    --查看当前有哪些用户正在使用数据
    SELECT osuser, a.username,cpu_time/executions/1000000||'s', sql_fulltext,machine from v$session a, v$sqlarea b where a.sql_address =b.address order by cpu_time/executions desc;

    --查看连接oracle的所有机器的连接数
    select machine,count(*) from v$session group by machine;

    --查看连接oracle的所有机器的连接数和状态
    select machine,status,count(*) from v$session group by machine,status order by status;

    --查看消耗磁盘读取最多的SQL Top 5:
    select disk_reads,sql_text,SQL_FULLTEXT
    from (select sql_text,disk_reads,SQL_FULLTEXT,
    dense_rank() over
    (order by disk_reads desc) disk_reads_rank
    from v$sql)
    where disk_reads_rank <=5;

    --开始
    --通过linux中消耗资源高的进程号获取oracle消耗资源的sql语句:
    --1.linux中使用top命名查看oracle进程中消耗资源最高的进程号;
    --2.oracle中使用命令:
    select c.spid,a.p1,a.p1raw,a.p2,a.event,b.sql_text,b.SQL_FULLTEXT,b.SQL_ID
    from v$session a,v$sql b,v$process c
    where a.wait_class<>'Idle' and a.sql_id=b.sql_id and a.PADDR=c.addr
    order by event;
    --3.查询结果显示出各个sql语句对应的进程号,从中找出top命令中对应消耗资源高的进程号即可找到相应的sql语句。
    --结束

    --判断回滚段竞争的SQL语句:(当Ratio大于2时存在回滚段竞争,需要增加更多的回滚段)
    select rn.name, rs.GETS, rs.WAITS, (rs.WAITS / rs.GETS) * 100 ratio
    from v$rollstat rs, v$rollname rn
    where rs.USN = rn.usn;

    ---判断恢复日志竞争的SQL语句:(immediate_contention或wait_contention的值大于1时存在竞争)
    select name,
    (t.IMMEDIATE_MISSES / decode((t.IMMEDIATE_GETS + t.IMMEDIATE_MISSES),0,-1,(t.IMMEDIATE_GETS + t.IMMEDIATE_MISSES))) * 100 immediate_contention,
    (t.MISSES / decode((t.GETS + t.MISSES), 0, -1, (t.GETS + t.MISSES))) * 100 wait_contention
    from v$latch t
    where name in ('redo copy', 'redo allocation');

    --判断表空间碎片:(如果最大空闲空间占总空间很大比例则可能不存在碎片,如果比例较小,且有许多空闲空间,则可能碎片很多)
    select t.tablespace_name,
    sum(t.bytes),
    max(t.bytes),
    count(*),
    max(t.bytes) / sum(t.bytes) radio
    from dba_free_space t
    group by t.tablespace_name
    order by t.tablespace_name;

    --确定命中排序域的次数:
    select t.NAME, t.VALUE from v$sysstat t where t.NAME like 'sort%'

    --查看当前SGA值:SGA(System Global Area)系统全局区。这是一个非常庞大的内存区间
    select * from v$sga;

    --确定高速缓冲区命中率:(如果命中率低于70%,则应该加大init.ora参数中的DB_BLOCK_BUFFER的值)
    select 1 - sum(decode(name, 'physical reads', value, 0)) /
    (sum(decode(name, 'db block gets', value, 0)) +
    sum(decode(name, 'consistent gets', value, 0))) hit_ratio
    from v$sysstat t
    where name in ('physical reads', 'db block gets', 'consistent gets');

    --确定共享池中的命中率:(如果ratio1大于1时,需要加大共享池,如果ratio2大于10%时,需要加大共享池SHARED_POOL_SIZE)
    select * from
    (
    select sum(pins) pins,
    sum(reloads) reloads,
    (sum(reloads) / sum(pins)) * 100 ratio1
    from v$librarycache
    ),
    (
    select sum(gets) gets,
    sum(getmisses) getmisses,
    (sum(getmisses) / sum(gets)) * 100 ratio2
    from v$rowcache
    )

    --查询INIT.ORA参数:
    select * from v$parameter;

    --求当前会话的SID,SERIAL#
    SELECT Sid, Serial# FROM V$session
    WHERE Audsid = Sys_Context('USERENV', 'SESSIONID');

    --查询session的OS进程ID(有输入)
    SELECT p.Spid "OS Thread", b.NAME "Name-User", s.Program, s.Sid, s.Serial#,s.Osuser, s.Machine
    FROM V$process p, V$session s, V$bgprocess b
    WHERE p.Addr = s.Paddr
    AND p.Addr = b.Paddr And (s.sid=&1 or p.spid=&1)
    UNION ALL
    SELECT p.Spid "OS Thread", s.Username "Name-User", s.Program, s.Sid,s.Serial#, s.Osuser, s.Machine
    FROM V$process p, V$session s
    WHERE p.Addr = s.Paddr
    And (s.sid=&1 or p.spid=&1)
    AND s.Username IS NOT NULL;

    --查看锁(lock)情况
    SELECT /*+ RULE */ Ls.Osuser Os_User_Name, Ls.Username User_Name,Decode(Ls.TYPE,
    'RW', 'Row wait enqueue lock', 'TM', 'DML enqueue lock','TX', 'Transaction enqueue lock', 'UL', 'User supplied lock') Lock_Type,o.Object_Name OBJECT,Decode(Ls.Lmode,1, NULL, 2, 'Row Share', 3, 'Row Exclusive',
    4, 'Share', 5, 'Share Row Exclusive', 6, 'Exclusive',NULL) Lock_Mode,o.Owner, Ls.Sid, Ls.Serial# Serial_Num, Ls.Id1, Ls.Id2 FROM Sys.Dba_Objects o,
    (SELECT s.Osuser, s.Username, l.TYPE, l.Lmode, s.Sid, s.Serial#, l.Id1,l.Id2 FROM V$session s, V$lock l
    WHERE s.Sid = l.Sid) Ls
    WHERE o.Object_Id = Ls.Id1
    AND o.Owner <> 'SYS'
    ORDER BY o.Owner, o.Object_Name;

    --查看等待(wait)情况
    SELECT Ws.CLASS, Ws.COUNT COUNT, SUM(Ss.VALUE) Sum_Value
    FROM V$waitstat Ws, V$sysstat Ss
    WHERE Ss.NAME IN ('db block gets', 'consistent gets')
    GROUP BY Ws.CLASS, Ws.COUNT;

    --求process/session的状态
    SELECT p.Pid, p.Spid, s.Program, s.Sid, s.Serial#
    FROM V$process p, V$session s
    WHERE s.Paddr = p.Addr;

    --查看表空间的名称及大小
    select t.tablespace_name, round(sum(bytes/(1024*1024)),0) ts_size
    from dba_tablespaces t, dba_data_files d
    where t.tablespace_name = d.tablespace_name
    group by t.tablespace_name;

    --查看表空间物理文件的名称及大小
    select tablespace_name, file_id, file_name,
    round(bytes/(1024*1024),0) total_space
    from dba_data_files
    order by tablespace_name;

    --查看回滚段名称及大小
    select segment_name, tablespace_name, r.status,
    (initial_extent/1024) InitialExtent,(next_extent/1024) NextExtent,

    --查看控制文件
    select name from v$controlfile;

    --查看日志文件
    select member from v$logfile;

    --查看表空间的使用情况
    select sum(bytes)/(1024*1024) as free_space,tablespace_name
    from dba_free_space
    group by tablespace_name;
    SELECT A.TABLESPACE_NAME,A.BYTES TOTAL,B.BYTES USED, C.BYTES FREE,
    (B.BYTES*100)/A.BYTES "% USED",(C.BYTES*100)/A.BYTES "% FREE"
    FROM SYS.SM$TS_AVAIL A,SYS.SM$TS_USED B,SYS.SM$TS_FREE C
    WHERE A.TABLESPACE_NAME=B.TABLESPACE_NAME AND A.TABLESPACE_NAME=C.TABLESPACE_NAME;

    --捕捉运行很久的SQL
    select username,sid,opname,
    round(sofar*100 / totalwork,0) || '%' as progress,
    time_remaining,sql_text
    from v$session_longops , v$sql
    where time_remaining <> 0
    and sql_address = address
    and sql_hash_value = hash_value

    --耗资源的进程(top session)
    select s.schemaname schema_name, decode(sign(48 - command), 1,
    to_char(command), 'Action Code #' || to_char(command) ) action, status
    session_status, s.osuser os_user_name, s.sid, p.spid , s.serial# serial_num,
    nvl(s.username, '[Oracle process]') user_name, s.terminal terminal,
    s.program program, st.value criteria_value from v$sesstat st, v$session s , v$process p
    where st.sid = s.sid and st.statistic# = to_number('38') and ('ALL' = 'ALL'
    or s.status = 'ALL') and p.addr = s.paddr order by st.value desc, p.spid asc, s.username asc, s.osuser asc

    --查看有哪些实例在运行:
    select * from v$active_instances;

    ]]>
    + + db + + + oracle + +
    + + String 和 StringBuilder、StringBuffer的区别 + /20170208-String-%E5%92%8C-StringBuilder%E3%80%81StringBuffer%E7%9A%84%E5%8C%BA%E5%88%AB/ + String和StringBuilder、StringBuffer的区别?

    答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,

    它们可以储存和操作字符串。其中String是只读字符串,

    也就意味着String引用的字符串内容是不能被改变的。

    而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。

    StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,

    区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,

    因此它的效率也比StringBuffer要高。

    简而言之:

    String:不能被修改

    StringBuffer:可以随意修改,有synchronized修饰,是线程安全的,效率略低

    StringBuilder:可以随意修改,无synchronized修饰,不是线程安全的,效率高

    面试题1:说出程序的输出结果

    classStringEqualTest {

    publicstaticvoidmain(String[] args) {
    String s1 = "Programming";
    String s2 = new String("Programming");
    String s3 = "Program" + "ming";
    System.out.println(s1 == s2);//false
    System.out.println(s1 == s3);//true
    System.out.println(s1 == s1.intern());//true
    }
    }

    存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。

    String的intern()方法就是扩充常量池的一个方法;

    当一个String实例str调用intern()方法时,

    Java查找常量池中是否有相同Unicode的字符串常量,

    如果有,则返回其的引用,

    如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用

    面试题2

    什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append方法连接字符串性能更好?

    答:

    如果使用少量的字符串操作,使用 (+运算符)连接字符串;

    如果频繁的对大量字符串进行操作,则使用

    1:全局变量或者需要多线程支持则使用StringBuffer;

    2:局部变量或者单线程不涉及线程安全则使有StringBuilder。

    ]]>
    + + java + StringBuilder + StringBuffer的区别 + +
    + + 介绍 | RASP + /20211105-RASP-introduce/ + 一、背景

    翻看书签的时候偶然发现 RASP 两个收藏
    脑子里毫无相关内容,细看之下,收获不小
    ps:是不是看看收藏夹,能有意外收获

    安全无小事,从点滴做起。要有安全意识
    安全投入的比例,可以根据公司所处的行业公司规模进行相应调整。
    安全投入包括:软件安全设计、招人、买设备、买服务、做评估。

    二、介绍

    2.1 RASP 是什么

    RASP( Runtime Application Self-Protection )是一种在运行时检测攻击并且进行自我保护的一种技术。
    OpenRASP 架构

    2.2 RASP 优缺点

    化繁为简,抓住事情的本质,这就是优势。

    做一件事,方式方法有很多,最终的目的都一样。
    对于安全来说,攻击方式千变万化,但是”攻击动作”万变不离其宗。
    RASP 通过监控”攻击动作”进行自我保护,而不是对攻击方式的识别与防御。

    2.2.1 优势

    • 通过 hook,深度监控应用执行。
    • 运行时自动识别处理用户输入威胁。
    • 以 Agent 方式运行,不侵入应用程序。

    攻击动作

    • 文件上传
    • 文件读取
    • 文件写入
    • 文件重命名
    • 文件遍历
    • 命令执行
    • 反序列化
    • …更多请看

    2.2.2 劣势

    • 性能有一定影响,单次请求影响 1~7 ms

    2.3 RASP 与 WAF、IDS

    当发生 SQL 注入攻击时,WAF 和 IDS 只能看到 HTTP 请求。
    而 RASP 不但能看到完整 SQL 语句,还可以和 HTTP 请求关联,
    并结合语义引擎、用户输入识别等能力,实现对 SQL 注入的检测。
    ps:WAF 可以理解为安全网关,门卫。IDS 入侵检测系统。

    三、参考

    ]]>
    + + 安全 + + + java + soft + +
    + + Shell 批量解压文件并重命名 - 解决文件名冲突 + /20201107-Shell-batch-decompression-and-rename/ + 一、背景

    由于某种原因,需要手工处理错误日志提取某些信息。
    下载下来的日志文件是压缩包

    1.1 文件信息

    system_error.log.2020-11-01.20201105200433.zip
    system_error.log.2020-11-01.20201105195953.zip
    system_error.log.2020-11-01.20201105200830.zip
    ...省略

    1.2 压缩包信息

    $ unzip -v system_error.log.2020-11-01.20201105200830.zip
    Archive: system_error.log.2020-11-01.20201105200830.zip
    Length Method Size Cmpr Date Time CRC-32 Name
    -------- ------ ------- ---- ---------- ----- -------- ----
    4495560 Defl:N 78526 98% 11-01-2020 23:43 504551c6 system_error.log.2020-11-01
    -------- ------- --- -------
    4495560 78526 98% 1 file

    压缩包文件名不一样,但是压缩包中的文件有一样的名字。
    例如:system_error.log.2020-11-01

    尝试使用如下方式,提示有重复,需要挨个选择如何处理,极度不便。
    于是想使用 shell 来解决( 之前没写过 shell )

    1.2 尝试过程

    提示有文件重复,需要处理。

    $ unzip '*.zip'
    Archive: system_error.log.2020-10-15.20201105200943.zip
    replace system_error.log.2020-10-15? [y]es, [n]o, [A]ll, [N]one, [r]ename:

    尝试使用 -n 参数,不覆盖

    $ unzip -n '*.zip'
    Archive: system_error.log.2020-10-15.20201105200943.zip
    ...省略
    100 archives were successfully processed.

    -n 表示不覆盖,有重名的直接跳过,结果就是 100 个文件只解压的 10 个。

    1.3 怎么解压并且重命名?

    mv A B
    # 把 A 改名为 B

    怎么拿到压缩包内的文件名?

    # 输出压缩包内的信息
    $ unzip -v system_error.log.2020-11-01.20201105200830.zip
    Archive: system_error.log.2020-11-01.20201105200830.zip
    Length Method Size Cmpr Date Time CRC-32 Name
    -------- ------ ------- ---- ---------- ----- -------- ----
    4495560 Defl:N 78526 98% 11-01-2020 23:43 504551c6 system_error.log.2020-11-01
    -------- ------- --- -------
    4495560 78526 98% 1 file

    # 使用 awk 截取相关信息
    # NR 行号,NF 最后一列
    # unzip -v 的输出方式可能有变动,需要自己修改位置,以得到正确的结果
    $ unzip -v system_error.log.2020-11-01.20201105200830.zip | awk '{if(NR==4) print $NF}'
    system_error.log.2020-11-01

    二、脚本信息

    1.3 的问题解决了就可以开始写脚本

    2.1 脚本

    文件名 uf.sh

    #!/bin/sh
    # 压缩包目录
    srcDir=$1
    # 解压后文件存放目录
    dstDir=$2
    # 文件名序号
    seq=1
    for file in ${srcDir}/*
    do
    # 判断是否为文件
    if test -f $file
    then
    # 获取压缩包名称,例如:system_error.log.2020-11-01.20201105200830.zip
    filename=$(basename $file)
    # 解压文件,移动文件,并且改名(这里使用的是序号,可以截取原文件名拼接时间戳等避免重名)
    unzip $filename && mv `unzip -v $filename | awk '{if(NR==4){print $8}}'` ${dstDir}/$((seq++))
    fi
    done

    2.2 使用方法

    注意,目前在目标目录下才能正确运行

    chmod 775 uf.sh
    sh uf.sh ./src ./dst

    然后解决问题,解压所有 100 个压缩包,并且得到 100 个文件(文件名为 1~100)。

    三、总结

    不得不说解决问题是最快的学习方式
    第一次写脚本相对耗时,也是一个不错的尝试过程。
    对linux系统相关的命令/函数不熟悉,导致到处查资料,有必要系统化的看看书。

    四、参考信息

    ]]>
    + + linux + + + shell + +
    + + PLSQL注册码 - 9999/12/31 + /20170214-PLSQL%E6%B3%A8%E5%86%8C%E7%A0%81-9999-12-31/ + PLSQL注册码
    过期时间为:9999/12/31

    Registration successful

    01.601769 - unlimited user licence
    service contract:9999/12/31


    Product Code:

    4t46t6vydkvsxekkvf3fjnpzy5wbuhphqz

    serial Number:

    601769

    password:

    xs374ca

    ]]>
    + + 软件 + + + java + PLSQL + +
    + + SpringMVC redirect & return JSON & set HTTP code + /20180713-SpringMVC%20redirect%20&%20return%20JSON%20&%20set%20HTTP%20code/ + springmvc正常情况下redirect并且设置指定响应码,异常情况下返回json数据

    背景介绍

    需求就是正常情况下能redirect到指定的页面
    异常的情况下,能够返回JSON格式的错误信息
    正常情况和异常情况都需要设置HTTP Code

    代码实现

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public String webCharge(HttpServletRequest request, HttpServletResponse response) {
    if (1==1) {
    response.setStatus(200);
    return "redirect:https://github.com/hisenyuan";
    } else {
    try {
    response.setStatus(405);
    response.getWriter().write(JSON.toJSONString("hisenyuan"));
    return null;
    } catch (IOException e) {
    e.printStackTrace();
    return "";
    }
    }
    }
    ]]>
    + + java + + + java + +
    + + SpringMVC把jsp改为Thymeleaf模版引擎 + /20170801-SpringMVC%E6%8A%8Ajsp%E6%94%B9%E4%B8%BAThymeleaf%E6%A8%A1%E7%89%88%E5%BC%95%E6%93%8E/ + Thymeleaf简介

    前面的例子我们使用的视图技术主要是JSP。JSP的优点是它是Java EE容器的一部分,几乎所有java EE服务器都支持JSP。缺点就是它在视图表现方面的功能很少,假如我们想迭代一个数组之类的,只能使用<% %>来包括Java语句进行。虽然有标准标签库(JSTL)的补足,但是使用仍然不太方便。另外JSP只能在Java EE容器中使用,如果我们希望渲染电子邮件之类的,JSP就无能为力了。

    Java生态圈广泛,自然有很多视图框架,除了JSP之外,还有Freemarker、Velocity、Thymeleaf等很多框架。Thymeleaf的优点是它是基于HTML的,即使视图没有渲染成功,也是一个标准的HTML页面。因此它的可读性很不错,也可以作为设计原型来使用。而且它是完全独立于java ee容器的,意味着我们可以在任何需要渲染HTML的地方使用Thymeleaf。

    Thymeleaf也提供了spring的支持,我们可以非常方便的在Spring配置文件中声明Thymeleaf Beans,然后用它们渲染视图。

    改造 - 由jsp到Thymeleaf

    1. 引入依赖

      <!--thymeleaf模版 spring4.x-->
      <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf-spring4</artifactId>
      <version>3.0.5.RELEASE</version>
      </dependency>
    2. 配置ViewResolver(在spring的xml文件里)

      <!-- 配置ViewResolver 使用:thymeleaf 模版引擎-->
      <bean id="templateResolver"
      class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
      <property name="prefix" value="/WEB-INF/templates/"/>
      <property name="suffix" value=".html"/>
      <property name="templateMode" value="HTML5"/>
      <!-- 缓存-->
      <property name="cacheable" value="false"/>
      </bean>
      <bean id="templateEngine"
      class="org.thymeleaf.spring4.SpringTemplateEngine">
      <property name="templateResolver" ref="templateResolver"/>
      </bean>
      <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
      <property name="templateEngine" ref="templateEngine"/>
      <property name="order" value="1"/>
      <property name="characterEncoding" value="UTF-8"/>
      </bean>
    3. 接下来就可以直接使用了,跟之前的jsp没有什么不同

      @RequestMapping(value = "/listpageplug/{start}", method = RequestMethod.GET)
      private String listPagePlug(@PathVariable("start") String start, Model model) {
      PageHelper.startPage(Integer.valueOf(start), 20);
      List<Book> readingList = bookService.getListPlug();
      model.addAttribute("books", readingList);
      return "readingList";
      }

    到这里就改造完了,接下来就是Thymeleaf的各种用法了

    这里举一个循环遍历的例子,后台返回了books对象集合

    <!--判断是否为空-->
    <tbody th:unless="${#lists.isEmpty(books)}">
    <!--循环-->
    <tr th:each="book : ${books}">
    <td th:text="${book.bookId}"></td>
    <td th:text="${book.name}"></td>
    <td th:text="${book.number}"></td>
    <td>
    <a href="<%=appPath%>/book/detail/${book.bookId}">详情</a> |
    <a href="<%=appPath%>/book/del/${book.bookId}">删除</a>
    </td>
    </tr>
    </tbody>

    参考

    1. thymeleaf官方指南
    2. 新一代Java模板引擎Thymeleaf
    3. Thymeleaf基本知识
    4. thymeleaf总结文章
    5. Thymeleaf 模板的使用
    6. thymeleaf 学习笔记
    ]]>
    + + java + + + java + spring + +
    + + Ubuntu 16 安装IDEA 并且设置快捷启动 + /20170304-Ubuntu%2016%20%E5%AE%89%E8%A3%85IDEA%20%E5%B9%B6%E4%B8%94%E8%AE%BE%E7%BD%AE%E5%BF%AB%E6%8D%B7%E5%90%AF%E5%8A%A8/ + 安装简单,下载官网的文件(with java的比较方便)

    解压之后在bin目录下执行

    sudo sh idea.sh

    就会进入安装程序,接下来会跳出图形界面,跟windows差不多的步骤

    没有激活码可以看之前的文章

    关键的一个是我发现网上说的建立桌面快捷方式不行

    就这样弄个方便的

    cd ~
    ln -s /idea home/bin/idea.sh idea
    #接下来执行 idea & 就可以打开,如果提示权限不够
    #就执行 sudo idea &
    sudo idea &
    #后面的 & 代表后台运行的意思,不影响控制台

    ]]>
    + + java + + + java + idea + +
    + + Redis counter demo - redis并发计数器 + /20180111-Redis%20counter%20demo%20-%20%20redis%E5%B9%B6%E5%8F%91%E8%AE%A1%E6%95%B0%E5%99%A8/ + 一、说明

    利用redis操作的原子性,实现java 多线程并发的情况下实现计数器。

    我本机测试多个线程操作之后,结果会出现一定的延迟,但是最终数字是ok的

    应该是redis内部做了一个类似于队列的功能。

    需要注意的是,得使用redis连接的线程池,不然会出现异常

    这里有一个:JedisUtil 下面用到了

    二、 代码实现

    2.1 redis操作类

    package com.hisen.thread.count_click_by_redis;

    import com.hisen.utils.JedisUtil;
    import redis.clients.jedis.JedisPool;
    /**
    * @author hisenyuan
    * @description 操作redis的线程类
    */
    public class ClickRedis {

    /**
    * 必须使用线程池,而且线程池要大于并发数,否则会出现redis超时
    */
    private static JedisPool jedis = JedisUtil.getPool();

    public static void click() {
    jedis.getResource().incrBy("hisen", 1);
    }

    public static int getCount() {
    return Integer.parseInt(jedis.getResource().get("hisen"));
    }

    public static void declare() {
    jedis.getResource().del("hisen");
    jedis.close();
    }
    }

    2.2 线程类,模拟点击

    package com.hisen.thread.count_click_by_redis;

    /**
    * @author hisenyuan
    * @description 执行点击的线程类
    */
    public class CountClickByRedisThread extends Thread{

    private int id;
    public CountClickByRedisThread(int id) {
    this.id = id;
    }

    @Override
    public void run() {
    super.run();
    ClickRedis.click();
    int count = ClickRedis.getCount();
    System.out.println("task:" + id + "\t 执行完毕\t线程编号:" + this.getId() + "\t当前值:" + count);
    }
    }

    2.3 主线程 - 启动类

    package com.hisen.thread.count_click_by_redis;

    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;

    public class Main {

    public static void main(String[] args) throws InterruptedException {
    /**
    * 5 - corePoolSize:核心池的大小
    * 10 - maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程
    * 200 - keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。
    * unit - unit:参数keepAliveTime的时间单位,有7种取值
    * workQueue:一个阻塞队列,用来存储等待执行的任务
    */
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
    50,
    100,
    200,
    TimeUnit.MICROSECONDS,
    new ArrayBlockingQueue<Runnable>(50));
    // 开启50个线程
    for (int i = 0; i < 50; i++) {
    executor.execute(new CountClickByRedisThread(i));
    }
    System.out.println("已经开启所有的子线程");
    // 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
    executor.shutdown();
    // 判断所有线程是否已经执行完毕
    while (true) {
    if (executor.isTerminated()) {
    System.out.println("所有的子线程都结束了!");
    // 清除redis数据
    ClickRedis.declare();
    break;
    }
    Thread.sleep(100);
    }
    }

    }

    ]]>
    + + sql + + + redis + +
    + + Test Java Code + /20170120-Test-Java-Code/ + 这里我只是贴一段代码测试一下

    package com.hisen.interview;

    /**
    * 变量不能被重写
    *
    * @author hisenyuan 2017年1月18日 下午10:33:33
    */
    public class AboutExtends {
    public static class A {
    public int a = 0;

    public void fun() {
    System.out.println("A");
    }

    static {
    System.out.println("Astatic");
    }
    {
    System.out.println("I'm A class");
    }
    }

    public static class B extends A {
    public int a = 1;

    public void fun() {
    System.out.println("B");
    }

    static {
    System.out.println("Bstatic");
    }
    {
    System.out.println("I'm B class");
    }
    }

    public static void main(String[] args) {
    // 里面的static块方法,new了就会执行
    // new new B()两个都执行,new new A()执行A的
    //static代码块在{}代码块后面执行
    A classA = new B();
    System.out.println(classA.a);
    classA.fun();
    // 输出信息
    // Astatic
    // Bstatic
    // I'm A class
    // I'm B class
    // 1
    // B

    // 多态记忆口诀
    // 变量多态看左边
    // 方法多态看右边
    // 静态多态看左边
    }
    }
    ]]>
    + + java + +
    + + Ubuntu 16 LTS 安装redis - apt-get install redis-server + /20170223-Ubuntu-16-LTS-%E5%AE%89%E8%A3%85redis-ape-get-install-redis-server/ + 配置文件的路径: /etc/redis/redis.conf

    redis服务路径: /etc/init.d/redis-server

    默认是开机启动

    #安装
    hisen@hisen-server:/$ sudo apt-get install redis-server
    #打开服务
    hisen@hisen-server:/$ service redis start
    ==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
    Authentication is required to start 'redis-server.service'.
    Authenticating as: hisen,,, (hisen)
    Password:
    ==== AUTHENTICATION COMPLETE ===
    #打开客户端
    hisen@hisen-server:/$ redis-cli
    #操作数据库
    127.0.0.1:6379> set hisen hisen.me
    OK
    #获取数据
    127.0.0.1:6379> get hisen
    "hisen.me"
    127.0.0.1:6379>
    #停止数据库
    hisen@hisen-server:/$ service redis stop
    ==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
    Authentication is required to stop 'redis-server.service'.
    Authenticating as: hisen,,, (hisen)
    Password:
    ==== AUTHENTICATION COMPLETE ===
    hisen@hisen-server:/$
    ]]>
    + + linux + + + redis + +
    + + Thread Pool Monitor - 线程池监控 + /20190905-Thread-Pool-Monitor/ + 一、背景

    业务当中多处用到线程池进行异步处理;
    为了得知线程池设置是否合理,故需要增加线程池监控;
    常见的实现方式:

    1. org.springframework.scheduling.concurrent.ScheduledExecutorTask
    2. org.springframework.scheduling.annotation.Scheduled

    本文使用1的方式实现,主要是方便进行配置,可以托管多个任务;

    二、效果预览

    taskName:pool1-monitor. taskCount:820, completedTaskCount:820, largestPoolSize:30, poolSize:30, activeCount:0, corePoolSize:30, maximumPoolSize:50, queueSize:0
    taskName:pool2-monitor. taskCount:1703, completedTaskCount:1703, largestPoolSize:30, poolSize:30, activeCount:0, corePoolSize:30, maximumPoolSize:50, queueSize:0
    taskName:pool3-monitor. taskCount:820, completedTaskCount:448, largestPoolSize:30, poolSize:30, activeCount:30, corePoolSize:30, maximumPoolSize:50, queueSize:342

    三、监控逻辑

    代码如下:

    package com.hisen.thread.monitor.threadpool;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

    import java.util.List;
    import java.util.concurrent.ThreadPoolExecutor;

    /**
    * @author hisenyuan
    * @date 2019-09-05 19:45
    */
    public class ThreadPoolMonitor implements Runnable {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitor.class);

    // xml中注入
    private List<ThreadPoolTaskExecutor> pools;

    @Override
    public void run() {
    monitorHandle(pools);
    }

    private void monitorHandle(List<ThreadPoolTaskExecutor> pools) {
    pools.forEach(this::handleExecutor);
    }

    private void handleExecutor(ThreadPoolTaskExecutor p) {
    ThreadPoolExecutor threadPoolExecutor = p.getThreadPoolExecutor();
    // 线程池需要执行的任务数
    long taskCount = threadPoolExecutor.getTaskCount();

    // 线程池在运行过程中已完成的任务数
    long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();

    // 曾经创建过的最大线程数
    long largestPoolSize = threadPoolExecutor.getLargestPoolSize();

    // 线程池里的线程数量
    long poolSize = threadPoolExecutor.getPoolSize();

    // 线程池里活跃的线程数量
    long activeCount = threadPoolExecutor.getActiveCount();

    // 配置的核心线程数
    int corePoolSize = threadPoolExecutor.getCorePoolSize();

    // 配置的最大线程数
    int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();

    // 当前线程池队列的个数
    int queueSize = threadPoolExecutor.getQueue().size();

    // 线程池前缀名称
    String threadNamePrefix = p.getThreadNamePrefix();

    LOGGER.info("taskName:{}monitor. taskCount:{}, completedTaskCount:{}, largestPoolSize:{}, poolSize:{}, activeCount:{}, corePoolSize:{}, maximumPoolSize:{}, queueSize:{}"
    , threadNamePrefix
    , taskCount
    , completedTaskCount
    , largestPoolSize
    , poolSize
    , activeCount
    , corePoolSize
    , maximumPoolSize
    , queueSize);
    }

    public List<ThreadPoolTaskExecutor> getPools() {
    return pools;
    }

    public void setPools(List<ThreadPoolTaskExecutor> pools) {
    this.pools = pools;
    }
    }

    四、spring xml配置

    <!-- ### 线程池监控 开始 ### -->
    <bean id="threadPoolMonitorRunnable" class="com.hisen.thread.monitor.threadpool.ThreadPoolMonitor">
    <property name="pools">
    <list>
    <ref bean="pool1" />
    <ref bean="pool2" />
    <ref bean="pool3" />
    </list>
    </property>
    </bean>

    <!-- 定时执行的线程池 -->
    <bean id="threadPoolMonitorTask" class="org.springframework.scheduling.concurrent.ScheduledExecutorTask">
    <property name="runnable" ref="threadPoolMonitorRunnable"/>
    <!-- 延迟时间,单位ms -->
    <property name="delay" value="150000"/>
    <!-- 间隔时间,单位ms -->
    <property name="period" value="5000"/>
    </bean>

    <!-- 任务调度 -->
    <bean id="scheduledExecutorFactoryBean" class="org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean">
    <!-- 执行任务的线程池个数 -->
    <property name="poolSize" value="1" />
    <!-- 任务列表 -->
    <property name="scheduledExecutorTasks">
    <list>
    <ref bean="threadPoolMonitorTask"/>
    </list>
    </property>
    </bean>
    <!-- ### 线程池监控 结束 ### -->
    ]]>
    + + java + + + java + +
    + + Ubuntu 16 LTS 安装zookeeper并开机启动 + /20170224-Ubuntu-16-LTS-%E5%AE%89%E8%A3%85zookeeper%E5%B9%B6%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8/ + 一、安装
    sudo apt-get install zookeeper

    默认信息

    #安装路径
    /usr/share/zookeeper
    #配置文件
    /etc/zookeeper/conf/zoo.cfg

    二、启动服务端

    hisen@hisen-server:/usr/share/zookeeper/bin$ sudo sh zkServer.sh

    如果报错

    zkServer.sh: 81: /home/xxx/zookeeper-3.4.6/bin/zkEnv.sh: Syntax error: "(" unexpected (expecting "fi")
    网上找了一圈原因,大概意思就是脚本里面用到的shell版本与系统当前使用的shell版本不兼容,导致异常。
    查看当前ubuntu系统的shell,默认是使用dash,但是脚本里面是使用的bash,问题就在这里了。
    解决步骤:
    修改当前系统的shell版本:
    dpkg-reconfigure dash
    Tab 移动到NO 回车即可(选择否)

    三、验证是否成功

    hisen@hisen-server:/usr/share/zookeeper/bin$ sudozkCli.sh -server localhost:2181
    WatchedEvent state:SyncConnected type:None path:null
    [zk: localhost:2181(CONNECTED) 0]

    出现上面的信息说明成功了

    四、设置开机启动

    1.创建配置文件

    sudo vi /etc/init.d/zookeeper

    添加以下信息,注意自己的相关路径是否相同,不同修改之

    #!/bin/sh
    #Configurations injected by install_server below....
    EXEC=/usr/share/zookeeper/bin/zkServer.sh
    ZOO_LOG_DIR="/var/zookeeper"
    JAVA_HOME=/usr/hisen/soft/jdk8
    PATH=${JAVA_HOME}/bin:$PATH
    ###############
    # SysV Init Information
    # chkconfig: - 58 74
    # description: zookeeper is the zookeeper daemon.
    ### BEGIN INIT INFO
    # Provides: zookeeper
    # Required-Start: $network $local_fs $remote_fs
    # Required-Stop: $network $local_fs $remote_fs
    # Default-Start: 2 3 4 5
    # Default-Stop: 0 1 6
    # Should-Start: $syslog $named
    # Should-Stop: $syslog $named
    # Short-Description: start and stop zookeeper
    # Description: zookeeper daemon
    ### END INIT INFO
    case $1 in
    start) /usr/share/zookeeper/bin/zkServer.sh start;;
    stop) /usr/share/zookeeper/bin/zkServer.sh stop;;
    status) /usr/share/zookeeper/bin/zkServer.sh status;;
    restart) /usr/share/zookeeper/bin/zkServer.sh restart;;
    *) echo "require start|stop|status|restart" ;;
    esac

    2.授权

    sudo chmod +x zookeeper

    3.安装开机启动管理软件(一般自带)

    sudo apt-get install rcconf

    4.进入管理界面

    sudo rcconf

    ↑ ↓ 移动光标,空格键选中zookeeper

    Tab 使光标移动到OK 回车即可

    ]]>
    + + linux + + + zookeeper + +
    + + Ubuntu 16 换阿里云源 + /20170218-Ubuntu-16-%E6%8D%A2%E9%98%BF%E9%87%8C%E4%BA%91%E6%BA%90/ +
  • sudo vi /etc/apt/sources.list
  • 删除里面所有的内容,添加下面的内容
  • sudo apt-get update
  • 三步完成之后即可!

    deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
    deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
    deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
    ]]>
    + + linux + + + ubuntu + 阿里云 + +
    + + Ubuntu 16 LTS 关闭图形界面 + /20170218-Ubuntu-16-LTS-%E5%85%B3%E9%97%AD%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2/ + 有两个方法。

    第一个方法

    到了图形化界面,打开terminal(终端)执行

    sudo init 3

    就会跳转到命令行界面,并且只有命令行

    就是一个全屏的terminal。

    第二个方法

    在安装ubuntu的时候,有选择是否安装图形化界面的选项,选择不安装,那么系统将不带有图形化界面而默认进入命令行界面。

    第三个方法

    http://hisen.me/20170219-Oracle-VM-VirtualBox%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%89%93%E5%BC%80%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%B9%B6%E4%B8%94%E5%90%8E%E5%8F%B0%E8%BF%90%E8%A1%8C/

    第四个方法

    直接安装Ubuntu Server,目前我就是这样

    不过装了之后还是推荐第三个方法!!!

    ]]>
    + + linux + + + linux + Ubuntu + +
    + + ubuntu root su 密码 + /20170704-Ubuntu%20root%20%E5%AF%86%E7%A0%81/ + 在安装完ubuntu的时候

    只有自己设置的非root的帐号和密码

    但是又要用root密码怎么办呢?

    hisen@ubuntu-1:~$ sudo passwd
    Enter new UNIX password:
    Retype new UNIX password:
    passwd: password updated successfully

    上面输入的密码就是你的root密码

    检测一下

    hisen@ubuntu-1:~$ su
    Password:
    root@ubuntu-1:/home/hisen#

    通过,至此结束

    ]]>
    + + linux + + + linux + +
    + + Ubuntu16.04使用阿里云镜像安装Mongodb & 配置 + /20170219-Ubuntu16-04%E4%BD%BF%E7%94%A8%E9%98%BF%E9%87%8C%E4%BA%91%E9%95%9C%E5%83%8F%E5%AE%89%E8%A3%85Mongodb-%E9%85%8D%E7%BD%AE/ + 一、使用阿里云镜像安装Mongodb

    1 > 添加 MongoDB 公共GPG钥匙

    sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

    2 > 创建列表文件

    这里把官网repo.mongodb.org

    换成了mirrors.aliyun.com

    echo "deb http://mirrors.aliyun.com/mongodb/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

    3 > 重新加载本地包数据库

    sudo apt-get update

    4 > 安装MongoDB

    sudo apt-get install -y mongodb-org

    5 > 启动MongoDB

    sudo service mongod start

    6 > 打开MongoDB客户端

    sudo mongo

    7 > 关闭MongoDB

    sudo service mongod stop

    展示一下

    hisen@hisen-server:~$ sudo service mongod start
    hisen@hisen-server:~$ mongo
    MongoDB shell version: 3.2.12
    connecting to: test
    > 1 + 1
    2
    >

    安装成功

    MongoDB默认的数据文件和日志文件分别存储在下面的位置

    数据文件:/var/lib/mongodb

    日志文件:/var/log/mongodb

    你可以修改/etc/mongod.conf 文件来改变相应的存储位置。

    如果你想改变运行MongoDB的用户

    你必须把 /var/lib/mongodb

    和 /var/log/mongodb 2个目录的访问权限付给该用户

    二、配置MongoDB

    1允许远程访问

    绑定ip

    $ sudo vim /etc/mongod.conf

    打开配置文件,添加需要增加的

    不建议采用注释掉 bindIP 的方案,非常容易受到攻击

    # network interfaces  
    net:
    port: 27017
    bindIp: 0.0.0.0

    接受所有ip

    重启

    $ sudo service mongod restart

    2.配置防火墙 (不配置也行)
    Ubuntu16.04 桌面版默认没有安装好 ipTable,用如下命令安装

    sudo apt-get update
    sudo apt-get upgrade
    sudo apt-get install iptables-persistent

    安装过程中,弹窗选择YES

    安装完成后:

    sudo iptables -A INPUT -p tcp --dport 27017 -j ACCEPT

    ]]>
    +
    + + Ubuntu虚拟机安装MySQL并且开启root远程访问 + /20170311-Ubuntu%E5%AE%89%E8%A3%85MySQL%E5%B9%B6%E4%B8%94%E5%BC%80%E5%90%AFroot%E8%BF%9C%E7%A8%8B%E8%AE%BF%E9%97%AE/ + 安装mysql很简单,关键是开启这个远程很坑!!!

    一、安装

    1.安装

    sudo apt-get install mysql-server

    等待完成即可,过程中需要设置密码

    2.查看是否成功

    sudo netstat -tap | grep mysql

    3.登陆mysql

    mysql -u root -p

    这条命令回车之后需要输入mysql密码

    二、开启远程访问

    $ sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
    #找到bind-address=127.0.0.1直接注释
    $ mysql -u root -p -h
    #登陆mysql
    mysql> use mysql;
    #使用mysql这个库
    mysql> GRANT ALL PRIVILEGES ON *.* TO root@"%" IDENTIFIED BY "hisen";
    #把root用户改成可以在任何ip上登陆,并且密码为:hisen
    mysql> flush privileges;
    #刷新

    重启:service mysql restart

    接下来就可以在navicat里面连接了

    三、注意事项

    因为在网上找的很多教程,都是说改这个配置文件:这个是错误的

    /etc/mysql/my.cf

    如果是通过apt-get方式安装的,默认的是第二步那个配置文件

    ]]>
    + + sql + + + mysql + +
    + + Linux-Ubuntu安装:JDK & Tomcat & Maven - 简单教程 + /20170218-Ubuntu%E5%AE%89%E8%A3%85JDK8-Tomcat-%E7%AE%80%E5%8D%95%E6%95%99%E7%A8%8B/ + 在网速搜索很多教程,感觉写的都太难了我去

    准备工作:

    1. 下载JDK,并解压(选择适合自己的版本:地址)
    2. 下载Tomcat,并解压(选择适合自己的版本:地址)
    3. 下载Maven,并解压(选择适合自己的版本:地址)

    目录约定:

    1. java路径:/usr/hisen/soft/java/jdk8
    2. tomcat路径:/usr/hisen/soft/tomcat/tomcat8
    3. maven路径:/usr/hisen/soft/maven/maven-3.5.0

    说明以上路径都是解压之后的,请解压之后自行重命名文件夹等工作

    下面开始配置环境变量:

    sudo vi /etc/profile

    底部添加:

    #java的环境变量配置
    export JAVA_HOME=/usr/hisen/soft/java/jdk8
    export JRE_HOME=$JAVA_HOME/jre
    export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib
    export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin

    #tomcat的环境变量配置
    export CATALINA_HOME=/usr/hisen/soft/tomcat/tomcat8
    export CLASSPATH=.:$JAVA_HOME/lib:$CATALINA_HOME/lib
    export PATH=$PATH:$CATALINA_HOME/bin

    #maven环境变量
    export MAVEN_HOME=/usr/hisen/soft/maven/maven-3.5.0
    export MAVEN_OPTS="-Xms256m -Xmx512m"
    export PATH=${MAVEN_HOME}/bin:$PATH

    让刚刚的配置生效:

    source /etc/profile

    查看maven

    mvn -v

    查看java版本

    java -version

    如果还是默认的OpenJDK

    sudo update-alternatives --install /usr/bin/java java /usr/hisen/soft/java/jdk8/bin/java 300  
    sudo update-alternatives --install /usr/bin/javac javac /usr/hisen/soft/java/jdk8/bin/javac 300

    #选择默认JDK即可
    sudo update-alternatives --config java

    进入tomcat的bin目录

    sudo vi catalina.sh

    顶部添加

    #让tomcat知道java在哪里
    JAVA_HOME=/usr/hisen/soft/java/jdk8
    JRE_HOME=$JAVA_HOME/jre

    之后进入在tomcat bin目录执行

    hisen@hisen:/usr/hisen/soft/tomcat/tomcat8/bin$ sudo sh startup.sh
    Using CATALINA_BASE: /usr/hisen/soft/tomcat/tomcat8
    Using CATALINA_HOME: /usr/hisen/soft/tomcat/tomcat8
    Using CATALINA_TMPDIR: /usr/hisen/soft/tomcat/tomcat8/temp
    Using JRE_HOME: /usr/hisen/soft/java/jdk8/jre
    Using CLASSPATH: /usr/hisen/soft/tomcat/tomcat8/bin/bootstrap.jar:/usr/hisen/soft/tomcat/tomcat8/bin/tomcat-juli.jar
    Tomcat started.

    这样就启动了tomcat!!!!

    注意

    如果没有在 catalina.sh 添加java路径,会报错

    hisen@hisen-VirtualBox:/usr/hisen/soft/tomcat/tomcat8/bin$ sudo sh startup.sh
    Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
    At least one of these environment variable is needed to run this program

    ]]>
    + + linux + + + linux + +
    + + Ubuntu安装nginx + /20170321-Ubuntu%E5%AE%89%E8%A3%85nginx/ + Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。

    Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,

    第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,

    因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。

    说明:这只是一个初步的安装,后续进一步实践

    安装Nginx依赖库

    #安装gcc g++的依赖库
    sudo apt-get install build-essential
    sudo apt-get install libtool
    #安装 pcre依赖库
    sudo apt-get update
    sudo apt-get install libpcre3 libpcre3-dev
    #安装 zlib依赖库(http://www.zlib.net)
    sudo apt-get install zlib1g-dev
    #安装 ssl依赖库
    sudo apt-get install openssl
    #下载最新版本:
    wget http://nginx.org/download/nginx-1.9.9.tar.gz
    #解压
    tar -zxvf nginx-1.9.9.tar.gz
    #进入解压目录:
    cd nginx-1.9.9
    #配置:
    sudo ./configure --prefix=/usr/local/nginx
    #编辑nginx:
    sudo make
    #注意:这里可能会报错,提示“pcre.h No such file or directory”,具体详见:http://stackoverflow.com/questions/22555561/error-building-fatal-error-pcre-h-no-such-file-or-directory
    #需要安装 libpcre3-dev,命令为:sudo apt-get install libpcre3-dev
    #安装nginx:
    sudo make install
    #启动nginx:
    sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    #注意:-c 指定配置文件的路径,不加的话,nginx会自动加载默认路径的配置文件,可以通过 -h查看帮助命令。
    #查看nginx进程:
    ps -ef|grep nginx

    Nginx常用命令

    #启动
    sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    #进入目录
    cd /usr/local/nginx/
    #关闭
    sudo ./sbin/nginx -s quit
    #Nginx重新加载配置
    sudo ./sbin/nginx -s reload
    #指定配置文件
    sudo ./sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    #查看版本
    sudo ./sbin/nginx -v
    #显示帮助
    sudo ./sbin/nginx -h

    安装之后就直接监听80端口,浏览器打开127.0.0.1即可访问出现如下页面:

    ]]>
    + + 软件 + + + nginx + +
    + + Unable to ping server at localhost 1099 - 出现的原因 + /20170330-Unable%20to%20ping%20server%20at%20localhost%201099%20-%20%E5%87%BA%E7%8E%B0%E7%9A%84%E5%8E%9F%E5%9B%A0/ + 之前老是出现

    Application Server was not connected before run configuration stop, 
    reason: Unable to ping server at localhost:1099

    我遇到这个问题一般是这些原因:

    1. 这个端口被占用,一般进程管理把所有java进程杀了可以解决
    2. 由于在IDEA中错误的给tomcat添加了参数,比如下面这个。去掉即可

    这是下VM option中加了:-URIEncoding=UTF-8

    Error: Could not create the Java Virtual Machine.
    Error: A fatal exception has occurred. Program will exit.
    Unrecognized option: -URIEncoding=UTF-8

    ]]>
    + + idea + + + java + idea + +
    + + Ubuntu安装RocketMQ - Quick Start + /20170702-Ubuntu%E5%AE%89%E8%A3%85RocketMQ%20-%20Quick%20Start/ + 安装需求:
    1. 64位的系统 Linux/Unix/Mac
    2. 64bit JDK 1.8+;
    3. Maven 3.2.x;
    4. Git (一般自带)

    如果还未安装jdk、maven建议查看教程:点击查看

    Clone & Build

    git clone -b develop https://github.com/apache/incubator-rocketmq.git
    cd incubator-rocketmq
    mvn -Prelease-all -DskipTests clean install -U
    [INFO] ------------------------------------------------------------------------
    [INFO] Reactor Summary:
    [INFO]
    [INFO] Apache RocketMQ 4.2.0-incubating-SNAPSHOT .......... SUCCESS [01:07 min]
    [INFO] rocketmq-remoting 4.2.0-incubating-SNAPSHOT ........ SUCCESS [ 15.749 s]
    [INFO] rocketmq-common 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 10.243 s]
    [INFO] rocketmq-client 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 11.638 s]
    [INFO] rocketmq-store 4.2.0-incubating-SNAPSHOT ........... SUCCESS [ 13.108 s]
    [INFO] rocketmq-srvutil 4.2.0-incubating-SNAPSHOT ......... SUCCESS [ 2.051 s]
    [INFO] rocketmq-filter 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 3.917 s]
    [INFO] rocketmq-broker 4.2.0-incubating-SNAPSHOT .......... SUCCESS [ 8.726 s]
    [INFO] rocketmq-tools 4.2.0-incubating-SNAPSHOT ........... SUCCESS [ 6.002 s]
    [INFO] rocketmq-namesrv 4.2.0-incubating-SNAPSHOT ......... SUCCESS [ 2.726 s]
    [INFO] rocketmq-logappender 4.2.0-incubating-SNAPSHOT ..... SUCCESS [ 3.514 s]
    [INFO] rocketmq-openmessaging 4.2.0-incubating-SNAPSHOT ... SUCCESS [ 2.668 s]
    [INFO] rocketmq-example 4.2.0-incubating-SNAPSHOT ......... SUCCESS [ 2.390 s]
    [INFO] rocketmq-filtersrv 4.2.0-incubating-SNAPSHOT ....... SUCCESS [ 2.145 s]
    [INFO] rocketmq-test 4.2.0-incubating-SNAPSHOT ............ SUCCESS [ 5.428 s]
    [INFO] rocketmq-distribution 4.2.0-incubating-SNAPSHOT .... SUCCESS [ 27.002 s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 03:07 min
    [INFO] Finished at: 2017-07-02T01:35:40+08:00
    [INFO] Final Memory: 60M/247M
    [INFO] ------------------------------------------------------------------------
    cd distribution/target/apache-rocketmq

    Start Name Server

    nohup sh bin/mqnamesrv &
    tail -f ~/logs/rocketmqlogs/namesrv.log
    The Name Server boot success...

    Start Broker

    nohup sh bin/mqbroker -n localhost:9876 &
    tail -f ~/logs/rocketmqlogs/broker.log
    The broker[%s, 172.30.30.233:10911] boot success...

    Send & Receive Messages

    Before sending/receiving messages, we need to tell clients the location of name servers. RocketMQ provides multiple ways to achieve this. For simplicity, we use environment variable NAMESRV_ADDR
    export NAMESRV_ADDR=localhost:9876
    sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
    SendResult [sendStatus=SEND_OK, msgId= ...

    sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
    ConsumeMessageThread_%d Receive New Messages: [MessageExt...

    Shutdown Servers

    sh bin/mqshutdown broker
    The mqbroker(36695) is running...
    Send shutdown request to mqbroker(36695) OK

    sh bin/mqshutdown namesrv
    The mqnamesrv(36664) is running...
    Send shutdown request to mqnamesrv(36664) OK

    ]]>
    + + linux + + + linux + rocketmq + +
    + + Unix文件超过大小限制移动到某目录 + /20190408-Unix%E6%96%87%E4%BB%B6%E8%B6%85%E8%BF%87%E5%A4%A7%E5%B0%8F%E9%99%90%E5%88%B6%E7%A7%BB%E5%8A%A8%E5%88%B0%E6%9F%90%E7%9B%AE%E5%BD%95/ + 某个文件夹下面有n个文件,需要移动大于10M的文件到/tmp/目录下

    实现命令

    find . -type f -size +100M -exec mv {} /tmp/ \;

    find 查找
    . 当前目录
    -type 文件类型:f 文件,d 目录
    -size 文件大小:+100M +:大于 -:小于 空:等于
    -exec 管道命令,将前面的查询结果传递给后面的命令
    {} 指前面传递过来的的查询结果
    \; 结束管道命令

    ]]>
    + + linux + + + linux + +
    + + ubuntu重装系统 - 在ubuntu系统下重新安装 + /20170305-Ubuntu%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%20-%20%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E9%87%8D%E6%96%B0%E5%AE%89%E8%A3%85/ + 有时候在linux环境下需要重新安装一下系统

    这里我就说一下今天我安装的方法。

    下载好ubuntu的镜像,随便放在一个非系统盘的根目录下

    改名为:ubuntu.iso

    sudo chmod 777 /boot/grub/grub.cfg
    sudo vi /boot/grub/grub.cfg
    #在 export linux_gfx_mode 下面添加如下内容
    menuentry "install ubuntu powered by hisen" {
    search --set -f /ubuntu.iso
    loopback loop /ubuntu.iso
    set root=(loop)
    linux /casper/vmlinuz.efi boot=casper iso-scan/filename=/ubuntu.iso
    initrd /casper/initrd.lz
    boot
    }

    保存退出,重启就会进入系统。

    桌面上点击那个安装的图标即可完成重装

    ]]>
    + + linux + + + linux + +
    + + Windows ssh连接 VirtualBox Ubuntu 16虚拟机 + /20170218-Windows%20ssh%E8%BF%9E%E6%8E%A5VirtualBox-Ubuntu-16%E8%99%9A%E6%8B%9F%E6%9C%BA/ + 安装完虚拟机之后想在windows下用xshell链接Ubuntu虚拟机

    这种配置下,虚拟机能上网,又能跟win连接,感觉很完美

    VirtualBox的端口转发很不错,可以转发tomcat什么的

    准备工作

    1.给Ubuntu安装openssh-server

    sudo apt-get install openssh-server

    2.查看虚拟机ip 我的是:10.0.2.15(看上面那段)

    hisen@hisen-VirtualBox:/$ ifconfig -a
    enp0s3 Link encap:以太网 硬件地址 08:00:27:45:f3:35
    inet 地址:10.0.2.15 广播:10.0.2.255 掩码:255.255.255.0
    inet6 地址: fe80::ed57:82d2:60cb:7f96/64 Scope:Link
    UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
    接收数据包:169175 错误:0 丢弃:0 过载:0 帧数:0
    发送数据包:34436 错误:0 丢弃:0 过载:0 载波:0
    碰撞:0 发送队列长度:1000
    接收字节:210738565 (210.7 MB) 发送字节:3633455 (3.6 MB)

    lo Link encap:本地环回
    inet 地址:127.0.0.1 掩码:255.0.0.0
    inet6 地址: ::1/128 Scope:Host
    UP LOOPBACK RUNNING MTU:65536 跃点数:1
    接收数据包:1718 错误:0 丢弃:0 过载:0 帧数:0
    发送数据包:1718 错误:0 丢弃:0 过载:0 载波:0
    碰撞:0 发送队列长度:1
    接收字节:234769 (234.7 KB) 发送字节:234769 (234.7 KB)

    VirtualBox设置端口转发

    1. 在VirtualBox启动页面,右键Ubuntu —>设置
    2. 网络 —> 连接方式 —> 网络地址转换(NAT)
    3. 高级 —> 端口转发 —> 点击添加按钮
    名称协议主机IP主机端口子系统IP子系统端口
    sshTCP127.0.0.1222210.0.2.1522

    子系统ip写你的虚拟机ip即可

    xshell链接Ubuntu虚拟机

    在xshell链接Ubuntu虚拟机的时候

    ip写上麦的主机ip:127.0.0.1

    端口写上面的主机端口:2222

    然后上面配置的端口转发就可以转发到虚拟机上,顺利连接!!!

    ]]>
    + + linux + + + linux + shell + ssh + +
    + + 理解 Linux Load Averages + /20201026-Understanding-Linux-Load-Averages/ + 零、背景

    最近做压力测试,不同的系统的机器监控数据差异明显
    A 系统:CPU 高 load 低
    B 系统:CPU 低 load 高

    那么是什么导致 A B 系统出现这种情况?
    CPU 高了系统肯定跑不动了,那么 load 多高代表系统跑不动呢?

    一、解释

    1. A 系统的原因
      CPU 很忙,没有等待其它资源,瓶颈在 CPU。
    2. B 系统的原因
      等待磁盘 I/O 完成的进程过多,导致进程队列长度过大,
      但是 CPU 运行的进程却很少,这样就导致负载过大,但 CPU 使用率低,瓶颈不在 CPU,可能在 I/O。

    二、load averages 知识

    1. Linux 系统下代表的是 system load averages。
    2. Linux load averages track not just runnable tasks, but also tasks in the uninterruptible sleep state.
      Linux 平均负载不仅跟踪可运行的任务,还跟踪处于不可中断睡眠状态的任务。

    3. On Linux, load averages are (or try to be) “system load averages”, for the system as a whole, measuring the number of threads that are working and waiting to work (CPU, disk, uninterruptible locks). Put differently, it measures the number of threads that aren’t completely idle. Advantage: includes demand for different resources.
      在 Linux 上,负载平均值是(或试图是)“系统负载平均值” ,对于整个系统来说,测量正在工作和等待工作的线程数(CPU、磁盘、不可中断锁)。换句话说,它测量的是没有完全空闲的线程数量。优势: 包括对不同资源的需求。

    4. In 1993, a Linux engineer found a nonintuitive case with load averages, and with a three-line patch changed them forever from “CPU load averages” to what one might call “system load averages.” His change included tasks in the uninterruptible state, so that load averages reflected demand for disk resources and not just CPUs.
      1993年,一位 Linux 工程师发现了一个与平均负载不直观的案例,一个三行补丁永远地将它们从“ CPU 负载平均值”改变为人们可能称之为“系统负载平均值”他的更改包括处于不可中断状态的任务,因此平均负载反映了对磁盘资源的需求,而不仅仅是 cpu。

    三、总结

    1. linux load averages 数字大,代表系统压力大;
    2. load 高可能的原因有(CPU、I/O、不可中断锁);
    3. load 1min load 5min load 15min,从左到右递增说明负载在增加,递减说明负载在降低;
    4. 负载多大才算高,没有定论,得看是否影响业务,如果业务正常那多半是没有问题的;

    四、load 优化

    1. 优化算法,减少 CPU 计算;
    2. 减少 I/O 交互的次数,以及大小;
    3. 分布式系统水平扩展更多的机器;

    五、参考

    1. Linux Load Averages: Solving the Mystery (发表于 2017 年)
    2. 理解Linux系统负荷 (发表于 2011 年)
    3. Linux中CPU使用率低负载高
    ]]>
    + + linux + + + linux + +
    + + ValidSudoku - leetCode - 算法 + /20191103-ValidSudoku%20-%20leetCode%20-%20%E7%AE%97%E6%B3%95/ + 一、题目

    Valid Sudoku链接

    题目要求:
    Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

    • Each row must contain the digits 1-9 without repetition.
    • Each column must contain the digits 1-9 without repetition.
    • Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition.

    每一行都不能重复1-9
    每一列都不能重复1-9
    每个33的小格子(99分为9个3*3)不能重复

    二、解题分析

    • 每一行每一列的数据,我们可以通过遍历二维数组解决;
    • 每个33的怎么解决呢?还是遍历二维数组,循环大(99)的二维数组时,符合条件再循环小(3*3)数组;
    • 怎么判断没有重复?方法很多,这里通过数组下标计数,数组初始值为0,根据下标+1,+1之后如果发现>1那么返回错误;

    开始解题的时候可以一个大循环解决一个小问题,后续再把循环合并即可;
    开始的时候我是写了三个双重for循环:

    1. 解决行重复判断问题;
    2. 解决列重复判断问题;
    3. 解决子矩阵重复判断问题;

    当发现每一步都可行之后,尝试着合并,以减少时间复杂度;

    三、代码样例

    public static boolean isValidSudoku(char[][] board) {
    // 处理 9 * 9
    for (int i = 0; i < board.length; i++) {
    int[] rowFlag = new int[10];
    int[] columnFlag = new int[10];
    for (int j = 0; j < board.length; j++) {
    char row = board[i][j];
    char column = board[j][i];
    if (check(rowFlag, row)) {
    return false;
    }
    if (check(columnFlag, column)) {
    return false;
    }
    // 处理 3 * 3
    if (i % 3 == 0 && j % 3 == 0) {
    int[] smallFlag = new int[10];
    for (int k = i; k < i + 3; k++) {
    for (int l = j; l < j + 3; l++) {
    char now = board[k][l];
    if (check(smallFlag, now)) {
    return false;
    }
    }
    }
    }
    }
    }
    return true;
    }

    四、代码详情

    可运行demo

    github地址:ValidSudoku.java

    ]]>
    + + java + + + leetCode + +
    + + Windows10开机bat启动VirtualBox虚拟机并且后台运行 + /20170220-Windows10%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8VirtualBox%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%B9%B6%E4%B8%94%E5%90%8E%E5%8F%B0%E8%BF%90%E8%A1%8C/ + 1.制作启动脚本

    新建一个start.bat文件,内容如下

    @echo off

    echo 本命令可让us在后台运行
    echo 启动之后可以关闭本窗口

    ::进入虚拟机目录
    cd C:\"Program Files"\Oracle\VirtualBox

    ::执行相关命令 同时启动两台虚拟机
    VBoxManage startvm "us" --type headless
    ::VBoxManage startvm "ubuntu" --type headless

    ::执行完之后按回车退出窗口
    pause

    2.设置开机启动

    把start.bat文件复制到[启动]文件夹里面

    [启动]文件夹路径

    C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp

    文件管理器地址栏显示大概是这样

    Windows > [开始]菜单 > 程序 > 启动

    放进去之后就可以开机启动了!

    启动之后Xshell连接即可

    ]]>
    + + 软件 + + + linux + VirtualBox + bat + +
    + + windows10 专业版激活密钥 + /20170210-Windows10-%E4%B8%93%E4%B8%9A%E7%89%88%E6%BF%80%E6%B4%BB%E5%AF%86%E9%92%A5/ + 2017年2月10日 16:03:48
    亲测可用,这比激活工具方便多了

    VK7JG-NPHTM-C97JM-9MPGT-3V66T

    NPPR9-FWDCX-D2C8J-H872K-2YT43

    W269N-WFGWX-YVC9B-4J6C9-T83GX

    NYW94-47Q7H-7X9TT-W7TXD-JTYPM

    NJ4MX-VQQ7Q-FP3DB-VDGHX-7XM87

    MH37W-N47XK-V7XM9-C7227-GCQG9
    ]]>
    + + soft + + + soft + +
    + + Spring - IOC和AOP的原理 + /20171120-Spring%20-%20IOC%E5%92%8CAOP%E7%9A%84%E5%8E%9F%E7%90%86/ + 简单理解IOC和AOP

    IOC:Inversion of Control,依赖倒置

    生活场景助记

    如有有天你想喝一瓶矿泉水,你可以去小区便利店,告诉老板你要买矿泉水,然后老板卖给你。

    但是你可能需要想这下雨天怎么去小卖部?是否要带伞?去了之后是否有我想要的水等一系列问题。

    解决这个问题:

    1. 使用外卖!到平台注册,告诉平台你需要什么水。
    2. 平台给你送到家,你只管付钱拿到水之后直接喝,不用考虑上述问题。

    是不是和Spring的做法很类似呢?Spring就是小卖部,你就是A对象,水就是B对象
    第一:在Spring中声明一个类:A
    第二:告诉Spring,A需要B

    假设A是UserAction类,而B是UserService类

    <bean id="userService" class="org.leadfar.service.UserService"/>
    <bean id="documentService" class="org.leadfar.service.DocumentService"/>
    <bean id="orgService" class="org.leadfar.service.OrgService"/>

    <bean id="userAction" class="org.leadfar.web.UserAction">
    <property name="userService" ref="userService"/>
    </bean>

    在Spring这个商店(工厂)中,有很多对象/服务:userService,documentService,orgService

    也有很多会员:userAction等等

    声明userAction需要userService即可,

    Spring将通过你给它提供的通道主动把userService送上门来,因此UserAction的代码示例类似如下所示:

    package org.leadfar.web;
    public class UserAction{
    private UserService userService;
    public String login(){
    userService.valifyUser(xxx);
    }
    public void setUserService(UserService userService){
    this.userService = userService;
    }
    }

    在这段代码里面,你无需自己创建UserService对象(Spring作为背后无形的手,把UserService对象通过你定义的setUserService()方法把它主动送给了你,这就叫依赖注入!)

    Spring依赖注入的实现技术是:动态代理

    AOP:Aspect Oriented Programming,面向切面编程

    你只要做你关注的事情,其他的事情一概不管,让AOP帮你去做

    你可以灵活组合各种杂七杂八的事情交给AOP去做,而不会干扰你关注的事情。

    从Spring的角度看,AOP最大的用途就在于提供了事务管理的能力。

    事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦)

    所以,Spring在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务!

    ICO 和 AOP的原理

    IoC的实现形式有两种:

    1. 依赖查找:容器提供回调接口和上下文环境给组件。EJB和Apache Avalon都是使用这种方式。
    2. 依赖注入:组件不做定位查询,只是提供普通的Java方法让容器去决定依赖关系。
      容器全权负责组件的装配,它会把符合依赖关系的对象通过JavaBean属性或者构造子传递给需要的对象。

    通过JavaBean属性注射依赖关系的做法称为设值方法注入(Setter Injection);

    将依赖关系作为构造子参数传入的做法称为构造子注入(Constructor Injection)。

    AP实现有多种方案,主要包括:

    1. AspectJ (TM):创建于Xerox PARC. 有近十年历史,技术成熟。但其过于复杂;破坏封装;而且需要专门的Java编译器,易用性较差。
    2. 动态代理AOP:使用JDK提供的动态代理API或字节码Bytecode处理技术来实现。基于动态代理API的具体项目有: JBoss 4.0 JBoss 4.0服务器。
    3. 基于字节码的AOP,例如:Aspectwerkz、CGlib、Spring等。

      后期准备补充

      用过spring的朋友都知道spring的强大和高深,都觉得深不可测,

    其实当你真正花些时间读一读源码就知道它的一些技术实现其实是建立在一些最基本的技术之上而已;
    例如:

    1. AOP(面向方面编程)的实现是建立在CGLib提供的类代理和jdk提供的接口代理
    2. IOC(控制反转)的实现建立在工厂模式、Java反射机制和jdk的操作XML的DOM解析方式.

      参考

    3. https://www.cnblogs.com/damowang/p/4305107.html
    4. http://blog.csdn.net/linxijun120903/article/details/56487073
    ]]>
    + + java + + + spring + +
    + + windows任务栏由底部改到左边,并自动隐藏 + /20170215-Windows%E4%BB%BB%E5%8A%A1%E6%A0%8F%E7%94%B1%E5%BA%95%E9%83%A8%E6%94%B9%E5%88%B0%E5%B7%A6%E8%BE%B9%EF%BC%8C%E5%B9%B6%E8%87%AA%E5%8A%A8%E9%9A%90%E8%97%8F/ + windows默认是在底部的

    在底部任务栏空白处:

    右键—设置—任务栏—在桌面模式下自动隐藏任务栏(开)—任务栏在屏幕上的位置(上 | 下 | 左 | 右)

    这样就设置完毕了,毕竟笔记本太小
    用idea的时候居然有些界面很难点击OK什么的!!!


    知乎有专门讨论任务栏放置位置分析,有兴趣的可以看看:

    Windows 任务栏放在窗口左边和下面哪一种更合理?各有什么利弊?

    ]]>
    + + 软件 + + + idea + +
    + + WebSocket - Java & html & JavaScript - 单发 & 群发 + /20180728-WebSocket%20-%20Java%20&%20html%20&%20JavaScript%20-%20%E5%8D%95%E5%8F%91%20&%20%E7%BE%A4%E5%8F%91/ + 一、背景说明

    最近在做app后台相关接口

    自建通知中心目前不能很好的支持给APP推送消息

    长连接可以保持推送速度,目前app中内嵌了H5,所以考虑使用websocket

    之前没有接触过websocket,百度了一堆之后,页面上可以正常使用

    但是没有发现可用使用Java后台进行消息的发送,于是乎就琢磨了一上午,解决了这个问题

    现在把这个小工程分享给大家,少走点弯路==

    ps:很多不能在后台发送消息,是因为缺少java的客户端

    二、准备工作

    建立一个maven web 工程

    添加依赖

    <dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    </dependency>
    <dependency>
    <groupId>com.neovisionaries</groupId>
    <artifactId>nv-websocket-client</artifactId>
    <version>1.13</version>
    </dependency>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
    </dependency>

    三、主要代码

    websocket服务端主逻辑

    为了实现简单的非群发操作,在连接websocket的时候,加上了一些get参数

    例如:ws://localhost:8080/websocket?sendTo=hisen&method=methodSingle&user=hisenyuan

    然后在后端判断,根据参数做出不同的动作

    demo完整工程:https://github.com/hisenyuan/IDEAPractice/tree/master/websocket-demo

    配置完Tomcat,即可使用,在java后台运行测试类(com.hisen.ws.client.ClientApp4Java)可发送消息到页面

    package com.hisen.ws.server;

    import com.hisen.ws.util.Constants;

    import javax.websocket.*;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.Optional;
    import java.util.concurrent.ConcurrentHashMap;

    /**
    * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
    * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
    */
    @ServerEndpoint("/websocket")
    public class WebSocketServer {
    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    // 实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    // 当前用户
    private String user;

    /**
    * 客户端可以是web页面,也可以是Java后台
    * <p>
    * 通过连接或者message可以控制发送给谁
    *
    * @param message 客户端发送过来的消息
    * @param session 可选的参数
    */
    @OnMessage
    public void onMessage(String message, Session session) {
    System.out.println("来自客户端的消息:" + message);
    // 获取url传过来的参数
    Map<String, List<String>> parameterMap = session.getRequestParameterMap();
    // 发送方式
    String method = null;
    // 发送给哪些人
    List<String> receivers = new ArrayList<>();
    // 发送者
    String sernder = null;
    if (parameterMap.containsKey(Constants.METHOD)) {
    method = parameterMap.get(Constants.METHOD).get(0);
    }
    if (parameterMap.containsKey(Constants.SEND_TO)) {
    receivers = parameterMap.get(Constants.SEND_TO);
    }
    if (parameterMap.containsKey(Constants.USER)) {
    sernder = parameterMap.get(Constants.USER).get(0);
    }

    System.out.println("sender:" + sernder + ",receivers:" + receivers.toString() + ",method:" + method);
    if (method == null || method.equals(Constants.METHOD_ALL)) {
    //发送所有
    send2All(message);
    } else {
    //单发
    send2Users(receivers, message);
    }

    }

    /**
    * 连接建立成功调用的方法
    *
    * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
    */
    @OnOpen
    public void onOpen(Session session) {
    this.session = session;
    this.user = session.getRequestParameterMap().get(Constants.USER).get(0);
    // 放入map
    webSocketMap.put(user, this);
    //在线数加1
    addOnlineCount();
    System.out.println("有新连接加入!当前在线人数为" + getOnlineCount() + ",session:" + session.getId() + ",user:" + this.user);
    }

    /**
    * 连接关闭调用的方法
    */
    @OnClose
    public void onClose() {
    // 移除
    webSocketMap.remove(this.user);
    //在线数减1
    subOnlineCount();
    System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount() + ",user:" + this.user);
    }


    private void send2Users(List<String> receivers, String message) {
    receivers.forEach(e -> {
    System.out.println("single receiver:" + e);
    Optional.ofNullable(webSocketMap.get(e))
    .filter(webSocketServer -> webSocketServer.session.isOpen())
    .ifPresent(webSocketServer -> sendOnce(message, e, webSocketServer));
    });
    }

    private void send2All(String message) {
    webSocketMap.forEach((key, value) -> {
    sendOnce(message, key, value);
    });
    }

    private void sendOnce(String message, String e, WebSocketServer webSocketServer) {
    try {
    webSocketServer.sendMessage(message);
    } catch (IOException exp) {
    System.out.println("发送出错,receiver:" + e);
    }
    }

    /**
    * 发生错误时调用
    *
    * @param session
    * @param error
    */
    @OnError
    public void onError(Session session, Throwable error) {
    System.out.println("发生错误,user:" + this.user);
    error.printStackTrace();
    }

    /**
    * 自定义的方法
    *
    * @param message
    * @throws IOException
    */
    public void sendMessage(String message) throws IOException {
    this.session.getBasicRemote().sendText(message);
    //this.session.getAsyncRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
    return onlineCount;
    }

    public static synchronized void addOnlineCount() {
    WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
    WebSocketServer.onlineCount--;
    }
    }
    ]]>
    + + java + +
    + + WeightAlgorithm - 权重算法 - Random、HashCode对比 + /20190322-WeightAlgorithm%20-%20%E6%9D%83%E9%87%8D%E7%AE%97%E6%B3%95%20-%20Random%E3%80%81HashCode%E5%AF%B9%E6%AF%94/ + 一、权重算法

    权重算法一般在路由里面用的比较多,分布式环境下对等的服务有多个,加权随机选出一个服务来调用;

    可能还有其他方面的用途,下面的代码简单的实现了这个权重,本质上就用到了数组,随机下标;

    二、代码概览

    public static void main(String[] args) {
    String[] weight = {"A", "A", "A", "A", "A", "B", "B", "B", "C", "C"};
    final int times = 500000;
    final long hashStart = System.currentTimeMillis();
    List<String> hashRes = getList4Hash(weight, times);
    printRes("Hash", hashStart, hashRes);

    final long randomStart = System.currentTimeMillis();
    List<String> randomRes = getList4Random(weight, times);
    printRes("Random", randomStart, randomRes);
    // Hash use millis: 931
    // A:49.92%
    // B:29.99%
    // C:20.09%
    // Random use millis: 50
    // A:50.08%
    // B:29.93%
    // C:19.99%
    }

    三、完整代码

    github:show all code

    package com.hisen.algorithms;

    import com.google.common.collect.Lists;

    import java.text.NumberFormat;
    import java.util.List;
    import java.util.Map;
    import java.util.Random;
    import java.util.UUID;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
    * @Author hisenyuan
    * @Description $end$
    * @Date 2019/3/21 21:04
    */
    public class WeightAlgorithm {
    public static void main(String[] args) {
    String[] weight = {"A", "A", "A", "A", "A", "B", "B", "B", "C", "C"};
    final int times = 500000;
    final long hashStart = System.currentTimeMillis();
    List<String> hashRes = getList4Hash(weight, times);
    printRes("Hash", hashStart, hashRes);

    final long randomStart = System.currentTimeMillis();
    List<String> randomRes = getList4Random(weight, times);
    printRes("Random", randomStart, randomRes);
    // Hash use millis: 931
    // A:49.92%
    // B:29.99%
    // C:20.09%
    // Random use millis: 50
    // A:50.08%
    // B:29.93%
    // C:19.99%
    }

    private static void printRes(String method, long start, List<String> resList) {
    final Map<String, Long> collect = resList.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
    final double a = collect.get("A");
    final double b = collect.get("B");
    final double c = collect.get("C");
    final double sum = a + b + c;

    NumberFormat nt = NumberFormat.getPercentInstance();
    nt.setMinimumFractionDigits(2);

    System.out.println(method + "\t use millis: " + (System.currentTimeMillis() - start));
    System.out.println("A" + ":" + nt.format(a / sum));
    System.out.println("B" + ":" + nt.format(b / sum));
    System.out.println("C" + ":" + nt.format(c / sum));
    }

    private static List<String> getList4Hash(String[] weight, int times) {
    List<String> result = Lists.newArrayList();
    for (int i = 0; i < times; i++) {
    final String a = UUID.randomUUID().toString() + System.currentTimeMillis();
    final int hash = a.hashCode();
    final int index = hash > 0 ? hash : -hash;
    final String res = weight[index % weight.length];
    result.add(res);
    }
    return result;
    }

    private static List<String> getList4Random(String[] weight, int times) {
    List<String> result = Lists.newArrayList();
    Random random = new Random();
    for (int i = 0; i < times; i++) {
    final int index = random.nextInt(weight.length);
    final String res = weight[index];
    result.add(res);
    }
    return result;
    }
    }
    ]]>
    + + java + + + java + 算法 + +
    + + 关于数据一致性 + /20220717-about-data-consistency/ + 1. 背景

    电商场景下的订单系统
    往往会有很多查询需求
    单体数据库无法满足大量数据存储、各种复杂查询

    2. 方案

    待更新

    3. 总结

    待更新

    ]]>
    + + database + + + db + +
    + + 关于学习 Rust + /20220206-about-learning-rust/ + 零、背景

    过年期间,看到 github 关注的人的动态当中
    有人 star 了这个项目 Rust语言圣经
    这些年也听说过 Rust,一直没有特意去了解
    当我看到大佬也关注了这个课程的时候
    感觉应该是一个不错的学习资料

    前面介绍学习的好处让我心动了
    就跟着学习了前面 4 节课
    目前感觉良好
    有空继续学

    不得不说,环境搭建特别方便!

    一、实践

    1.1 code

    fn greet_world(){
    let southern_germany = "Grüß Gott!";
    let chinese = "世界,你好";
    let english = "World, hello";
    let regions = [southern_germany, chinese, english];
    for region in regions.iter() {
    println!("{}", &region);
    }
    }

    fn more_than_hello(){
    let penguin_data = "\
    common name,length (cm)
    Little penguin,33
    Yellow-eyed penguin,65
    Fiordland penguin,60
    Invalid,data
    ";

    let records = penguin_data.lines();

    for (i, record) in records.enumerate() {
    if i == 0 || record.trim().len() == 0 {
    continue;
    }

    let fields: Vec<_> = record
    .split(",")
    .map(|field| field.trim())
    .collect();

    if cfg!(debug_assertions) {
    eprintln!("debug: {:?} -> {:?}", record, fields);
    }

    let name = fields[0];
    if let Ok(length) = fields[1].parse::<f32>() {
    println!("{}, {}cm", name, length);
    }
    }
    }

    fn main() {
    greet_world();
    more_than_hello();
    }

    1.2 out put

    $ cargo run --release
    Finished release [optimized] target(s) in 0.02s
    Running `target/release/world_hello`
    Grüß Gott!
    世界,你好
    World, hello
    Little penguin, 33cm
    Yellow-eyed penguin, 65cm
    Fiordland penguin, 60cm

    二、资源

    ]]>
    + + rust + + + rust + +
    + + apt-get 常用命令 + /20170815-apt-get%20%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/ + apt-get应该debian内核系列的系统都能用:

    比如:debian、ubuntu、deepin等

    apt-cache search package    #搜索包(相当于yum list | grep pkg)
    apt-cache show package #显示包的相关信息,如说明、大小、版本等
    apt-cache showpg package #显示包的相关信息,如Reverse Depends(反向依赖)、依赖等
    apt-get install package #安装包
    apt-get reinstall package #重新安装包
    apt-get -f install package #强制安装
    apt-get remove package #删除包(只是删掉数据和可执行文件,不删除配置文件)
    apt-get remove --purge package #删除包,包括删除配置文件等
    apt-get autoremove --purge package #删除包及其依赖的软件包+配置文件等
    apt-get update #更新源
    apt-get upgrade #更新已安装的包
    apt-get dist-upgrade #升级系统
    apt-get dselect-upgrade #使用 dselect 升级
    apt-cache depends package #了解使用依赖
    apt-cache rdepends package #查看该包被哪些包依赖
    apt-get build-dep package #安装相关的编译环境
    apt-get source package #下载该包的源代码
    apt-get clean && apt-get autoclean #清理下载文件的存档 && 只清理过时的包
    apt-get check #检查是否有损坏的依赖
    dpkg -S filename #查找filename属于哪个软件包
    apt-file search filename #查找filename属于哪个软件包
    apt-file list packagename #列出软件包的内容
    apt-file update #更新apt-file的数据库

    dpkg -l #列出当前系统中所有的包.可以和参数less一起使用在分屏查看(类似于rpm -qa)
    dpkg -l |grep -i "pkg" #查看系统中与"pkg"相关联的包(类似于rpm -qa | grep pkg)
    dpkg -s pkg #查询一个已安装的包的详细信息(类似于rpm -qi)
    dpkg -L pkg #查询一个已安装的软件包释放了哪些文件(类似于rpm -ql)
    dpkg -S file #查询系统中某个文件属于哪个软件包(类似于rpm -qf)
    dpkg -c pkg.deb #查询一个未安装的deb包将会释放哪些文件(类似于rpm -qpl)
    dpkg -I pkg.deb #查看一个未安装的deb包的详细信息(类似于rpm -qpi)
    dpkg -i pkg.deb #手动安装软件包(不能解决软依赖性问题,可以用apt-get -f install解决)
    dpkg -r pkg #卸载软件包(不是完全的卸载,它的配置文件还存在)
    dpkg -P pkg #全部卸载(不能解决依赖性的问题)
    dpkg-reconfigure pkg #重新配置
    dpkg -x pkg.deb dir #将一个deb包解开至dir目录
    dpkg --pending --remove #移除多余的软件

    # 强制安装一个包(忽略依赖及其它问题)
    dpkg --force-all -i pkg.deb #可以参考dpkg --force-help

    # 强制卸载一个包
    dpkg --force-all -P pkg.deb

    aptitude update #更新可用的包列表
    aptitude upgrade #升级可用的包
    aptitude dist-upgrade #将系统升级到新的发行版
    aptitude install pkgname #安装包
    aptitude remove pkgname #删除包
    aptitude purge pkgname #删除包及其配置文件
    aptitude search string #搜索包(相当于yum list | grep pkg,重要)
    aptitude show pkgname #显示包的详细信息 (相当于yum info pkg,重要)
    aptitude clean #删除下载的包文件
    aptitude autoclean #仅删除过期的包文件
    apt-get install xrdp #安装图形化

    ]]>
    + + linux + + + linux + +
    + + 关于 Java static + /20211112-about-static/ + 零、背景

    同事分享《Effective Java》
    其中第十章,并发部分例子有争议
    变量是否需要(代码如下) static?
    几个大佬说需要加,我众目睽睽下反驳不需要,略尴尬

    // 是否需要加 static?才能保证单例正确
    private volatile Singleton singleton;

    一、程序关键代码

    1.1 原程序(错误)

    public class Singleton {
    private volatile Singleton singleton;

    public Singleton() {
    }

    private Singleton getInstance() {
    if (singleton == null) {
    synchronized (Singleton.class) {
    if (singleton == null) {
    singleton = new Singleton();
    }
    }
    }
    return singleton;
    }
    }

    1.2 正确程序


    public class Singleton {
    private static volatile Singleton singleton;

    public Singleton() {
    }

    private Singleton getInstance() {
    if (singleton == null) {
    synchronized (Singleton.class) {
    if (singleton == null) {
    singleton = new Singleton();
    }
    }
    }
    return singleton;
    }
    }

    二、验证

    2.1 验证程序

    public class Singleton {
    private static final AtomicInteger COUNT = new AtomicInteger(0);
    // 通过volatile关键字来确保安全
    private volatile Singleton singleton;

    public Singleton() {
    }

    private Singleton getInstance() {
    if (singleton == null) {
    synchronized (Singleton.class) {
    if (singleton == null) {
    System.out.println("new:" + COUNT.addAndGet(1));
    singleton = new Singleton();
    }
    }
    }
    return singleton;
    }

    @Test
    public void test() {
    int taskCount = 700;
    // 锁住所有线程,等待并发执行
    final CountDownLatch begin = new CountDownLatch(1);

    final ExecutorService exec = Executors.newFixedThreadPool(taskCount);

    for (int index = 0; index < taskCount; index++) {
    submitTask(begin, exec);
    }
    System.out.println("开始执行");
    // begin 减1 ,开始并发执行
    begin.countDown();
    //关闭执行
    exec.shutdown();
    }

    private void submitTask(CountDownLatch begin, ExecutorService exec) {
    Runnable run = () -> {
    try {
    // 等待,所有一起执行
    begin.await();
    //开始模拟等待。。。
    Singleton singleton = new Singleton();
    Singleton instance = singleton.getInstance();
    System.out.println(Objects.isNull(instance));
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    };
    exec.submit(run);
    }
    }

    2.2 验证结果

    带 static 的结果符合预期
    对象只创建一次,并且返回结果均不为 null

    2.2.1 不带 static

    开始执行
    new:1
    new:2
    new:3
    false
    true
    省略几十行….

    2.2.2 带 static

    开始执行
    new:1
    false

    三、原因分析

    static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
    而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

    关键:static 变量在内存中只有一个副本,由所有对象共享。

    四、参考

    ]]>
    + + java + + + java + +
    + + 陆奇 | 年轻人的榜样 + /20211109-about-luqi/ + 零、背景

    不知在哪听说过陆奇的传说
    对于一位地位如此之高的华人
    甚是敬仰,奈何相关资料甚少,很难深入了解
    不像李开复、吴军那样,出过一些书,了解可以多些

    榜样的力量或许很虚,关键看自己能悟多少,是否坚持行动。共勉!

    一、工程领导(Engineering Leadership)

    • Believe in 技术
      未来任何一个工业都会变成软件工业。
    • 站在巨人的肩膀上做创新
      你所写的每一行代码是否值得?
    • 追求 Engineering Excellence
      追求工程技术的卓越的能力。
    • 每天学习
      每天学习,每天都争取变地更好。
      学经济、学产品、懂商业、懂生态。
    • Ownership
      把公司的事业,当成是自己的事业,own everything。从我做起,从身边的每一件事做起。

    二、关于陆奇的文章

    2.1 摘要

    • 20岁:做让自己走得快的事情
    • 30岁:做让自己走得远的事情:搭建系统
      • 身体系统
      • 家庭系统
      • 人脉系统
    • 40岁:找一个可以让你淋漓尽致发挥的舞台

    2.2 原文

    2.2.1 介绍

    大部分人还不了解陆奇,但他值得让我们好好了解一下:

    这几年陆奇被格外关注,始于2017年1月17日他被百度任命为百度总裁。这次任命份量极高,到今天百度创立19年,李彦宏给出如此高的权力,陆奇是唯一一个。

    但陆奇值得。

    陆奇在硅谷非常有名,拥有不错的人脉和江湖地位,凡接触过陆奇的人提起他,几乎是众口一词地称赞。

    1998年陆奇加入雅虎,2007年任雅虎执行副总裁。

    2008年他辞职时,杨致远当场洒泪,告别会上所有工程师穿上统一的T恤,T恤上印着“我曾与陆奇一起工作,你呢?”,以此纪念陆奇在雅虎的日子。

    2008年加入微软,任全球执行副总裁,这是有史以来,华人在科技领域获得的最高职位。

    2016年9月离职时,比尔·盖茨极力挽留甚至承诺:“你想要做什么业务,我们去搞个业务给你。或者你先休假一年两年,然后再回来当首席技术官,我们等着你就是了。”

    微软现任 CEO 纳德拉曾对微软员工说:“五个人,对微软贡献巨大。一是创始人比尔·盖茨,二是CEO史蒂夫·鲍尔默,三是董事会主席约翰·汤普森,四是诗人奥斯卡·王尔德,最后一个就是陆奇。”

    其实早在2005年前后,李彦宏就曾试图说服陆奇加入百度,只是当时并未如愿。

    2017年陆奇加入百度后,李彦宏曾公开给了他很高的评价:陆奇上上下下有口皆碑,大家都很喜欢他,他有非常强的技术能力,又有很强的管理能力,并且工作极其玩命。

    陆奇身上有太多值得我们学习的地方。

    2.2.2 采访

    前段时间《晚点》采访时就问陆奇:

    你对20、30、40岁的年轻人各有什么建议?

    陆奇:

    20岁需要做让你可以走得很快的事情,快速学、快速失败。30岁你要让自己可以走得远,建立一个核心支撑体系能让你走得很远。这个体系包括你的身体、你厚实的家庭基础和几个志同道合你可以信任的朋友。

    一个人到了35岁,到了打造产品的黄金时段,我已前很关注这个年龄阶段的人才,因为他/她已经犯过不少错,他/她最需要做一个好产品让他/她的职业生涯有一个本质提升。

    40岁后,理想情况是找到一个可以让你淋漓尽致去发挥的舞台,一个人的才华和一个公司的才华只有在真正被释放的情况下才能实现它的价值。如果这个舞台是你自己的最好。

    这个回答很经典,我反复看了很多遍。但要真的看懂看透,我们还必须要放在陆奇的整个人生系统里看,我一口气翻看了所有关于陆奇重要的中文报道,从中得到了一个更完整清晰的答案。

    20岁:做让自己走得快的事情

    陆奇:“20岁需要做让你可以走得很快的事情,快速学、快速失败。”

    1、20多岁,习得什么能力最重要?

    陆奇认为有两个:

    学习能力:在这个变化速度越来越快的世界里,拥有学习和持续学习的能力,不断提高自己在某一个专业领域以及在企业内开发产品和业务的认知能力是基础。

    原因很简单,因为创新,世界变化的速度越来越快。唯一的应对,是与它共跑甚至赶超。方法就是学习,要持续不断的学习,让自己拥有更多更深的专业知识和技能,更强的在企业内开发产品和业务的能力。

    交流能力:这是一个人与人之间愈发紧密联结的世界,通过各种数字通讯服务与工具,社交网络,人们能够以文字、图片乃至视频,与更多的人保持交流。

    从长远角度看,一个人越擅长结识他人,表达自己,形成相互学习并共同完成某些理想的关系和友谊,对这个人就越好。

    2、20多岁,去创业公司还是大公司?

    陆奇认为,要看个人追求。

    如果你的长远理想是自己开公司,那加入创业公司,甚至直接创业,都是最好的选择。加入创业公司或自己创业会愈发成为年轻人学习并拥有真正创新能力的重要途径。因为在创业企业,每天做的事就是为了生存而战斗,一个人的真实作战能力测试来的很快。创新或死亡,是创业企业每天面临的现实。就学习和人才发展角度,一个人能获得这样的学习机会是非常宝贵的。

    如果你的长远理想是成为一家公司某个职能的高管,那就进入一家可以提供你学习和成长机会的大企业。

    但陆奇认为,以上仅限于少部分清楚的知道自己长远目标的人,他对女儿说:大部分人在25岁之前,对于人生想要做什么,其实只有模糊的感觉。

    所以,20多岁,要学会快速失败,尝试,反馈,改变。把自己的时间投资在更大的学习发展空间机会中,可能为未来创造更高的收入。

    3、陆奇的20多岁,在做什么?

    读书,以及继续读书。

    1978年,全国恢复高考,17岁的陆奇埋头狠狠啃了两年书,考入复旦大学计算机专业。读书期间,陆奇特别用功。他的同学们回忆说:

    “他瘦瘦小小的身影,每天穿行在教室和图书馆之间,夜里图书馆熄灯,他才穿过农田,回到另一区的寝室。”

    “他背着大书包在校园里穿梭,在林荫道上反反复复背着单词。他是全年级最瘦小的男生,但扛着全年级最大的书包。”

    凭着超凡的努力,陆奇顺利考取复旦的研究生。凭着读研期间优异的成绩,他毕业后留校任教。

    勤奋学习和扎实的专业知识带给陆奇的远不止一份稳定而又体面的工作,还包括更多的人生选项。

    1989 年,卡耐基梅隆大学教授克拉克到复旦交流讲学,讲学时间选在了周末。不巧地是,当天的天气很不给力,暴雨倾盆,周末加坏天气使得来听讲座的学生寥寥无几。校方为了不让教授尴尬,就安排周末留校的师生去听讲座,因为暴雨取消回家计划的陆奇就是其中之一。

    克拉克教授讲完后,让在场师生提问。陆奇抓住这个机会,接连向教授提了几个专业问题,克拉克听完,立刻就对这个提问的复旦老师的学术水平刮目相看。讲座过后,他专门翻阅了陆奇写的论文,看过之后,克拉克就邀请陆奇去卡耐基梅隆大学读博。

    那年陆奇28岁,有点犹豫:“在大学当老师已经是很不错的职业了”。

    克拉克只回了一句:“你是鹰,不应该局限在笼子里。”

    陆奇去了美国。

    走上卡耐基梅隆大学这个更大的平台,才有了陆奇对计算机科学更深入的研究与理解、才有了陆奇与李开复的相识以及后面的开挂人生。

    4、59岁的陆奇还在试图重构知识体系,20多岁怎能不学?

    2016年陆奇在硅谷接受采访时说:“最近几年我重新觉悟——你必须要重新学习,以前学的东西不光过时了,而且现在很多理论包括物理学都已经有了全新的认识,所有你要从根本上重新构建知识、认识世界。”

    陆奇从微软离职原因之一是练习倒骑自行车摔伤了腿,那辆车是他与同事改造的、反向骑行的自行车,骑车时人和身体反应全部是倒置,所以要忘记过去学习到的全部经验。

    就连之前选择加入YC,其中一个原因也是学习:我还希望我的工作不能只利用过往的经验,而是要每天学新东西。不学新东西的话,就没什么乐趣。

    这几年陆奇观察到人才市场里一个非常重要的宏观趋势,整体的需求从原来的技能驱动型人才,越来越往知识和创新驱动型人才转移。所以他认为,一个人的能否快速学习,并将学到的知识应用于创造新的价值也变得愈发重要。

    现在他仍旧保持着每天学习英语、阅读计算机领域最前沿论文的习惯。他说:“我把自己想象成一个软件,今天的版本一定要比昨天的版本好,明天的版本一定要比今天的版本好。”

    年近60岁,陆奇还试图重构知识体系,20多岁的我们又怎么能不学习?

    30岁:做让自己走得远的事情:搭建系统

    陆奇说:30岁你要让自己可以走得远。建立一个核心支撑体系,能让你走得很远。这个体系包括你的身体、你厚实的家庭基础和几个志同道合、可以信任的朋友。

    陆奇是一个寻求最优解的人,方式就是建立系统。

    任何一个问题他都有系统的固定方式去解决——就是把它变成一个非常理性化、可以拆解成任务的方程。

    比如,陆奇的人生里几乎看不冒险,因为他只把机会成本的8%用来冒险,就算这8%,他还是会建立一个决策系统,理性决定——快速反馈——如果方向不对,立即掉头甚至放弃。

    比如,看待自己在微软的贡献,他不计较几个产品的得失,还是要回到系统上:比起一个产品的成败,帮助企业建造长期的创新的生命力才是最重要的。

    我们再看一下他上面着重提到的三个系统。

    1、身体系统

    陆奇极度自律,坚持了十几年早上4点起床。

    过去十几年他是这样的:“4点起来先弄Email,弄完了以后跑步,跑30分钟,洗个澡,去办公室。跑步第一对呼吸系统是很好的锻炼,第二可以出一身汗,第三是我可以边跑边听书不浪费时间,有时候我会把PPT放在跑步机上翻着看。”

    从微软离开后有所变化,他现在对自己的生活效率很不满意,因为骑反向自行车把腿摔伤后,跑步根本不行了,目前他正在找一个让自己感觉每天都很顺畅的方式。

    不过他依然精力旺盛,百度期间,同事说他每日长时间工作,无论周末、假期,无论在北京还是在国外,只要开会,他总是准时出现在视频会议的另外一端。他有依然有自己严格的作息制度,有自己严格的生活习惯,也有非常严格的饮食习惯。

    陆奇认为,每个人有不同的身体状况,他自己的做法不建议其他人学习。但是他有一套核心的方法论可以介绍给大家:

    找到问题的核心,即:无论创业还是在大企业里从业,在工作中如何进行时间管理来让产出最大化,同时也能获得自我满足?

    再找到核心的解决办法,即:

    设计一个“马拉松快步跑”的时间管理方法和心态。

    第一:要意识到这是一场马拉松,不是一场短跑。

    第二:这场马拉松的速度需要很快,因为现实世界中,任何高价值的东西——创业公司、大企业的好岗位等——都会有非常激烈的竞争,你需要保持速度并持续领先。

    设计这样一个工作节奏和时间管理方式,很类似在高速公路开车。

    陆奇说:“你需要保持一个均匀的高速,然后时不时的加速一下,再回到之前均匀的高速。你要避免过度频繁的加速、减速。就像一辆车,如果一直都是高速前进,只是偶尔减速一下,这对与一辆车的损耗是很低的。但如果一辆车过度频繁的突然加速、减速,会对这辆车带来巨大损耗,不用多久车就可能垮掉。

    因此,你需要设计属于你自己的一个工作和生活节奏,这种节奏是你可以保持住的高速,而这个高速可以给你带来最大的效率。同时,你也需要设计这个日程节奏,让它可以应对突发变化,可以时不时的冲刺一下,比如偶尔过度加班让工作在截止日期前完成,然后迅速回归之前的速度。必须避免经常性的透支,经常性的拼命追赶截止日期,经常性的处于疲累状态。身体和精神上偶尔透支可以补回,不可长期透支。”

    跑一个高效率、可持续、并且可以应对临时突发状况的马拉松才是关键。

    2、家庭系统

    陆奇的家庭很少出现在媒体中,公开信息绝少,但陆奇在讲搭建系统时特意提到“厚实的家庭基础”,说明他的个人成功受“家庭系统”支持颇多。

    2018年5月8日,百度宣布陆奇因“个人和家庭原因”离开百度。

    这其实是拿陆奇最不可能的原因当了最常见的公关话术,陆奇的家庭一直是全力支持陆奇做他想做的事业,百度的一位高层说,陆奇就职微软期间,其长期在美国西雅图工作,家人为了陆奇就定居硅谷,“他早就把家庭的事情处理好了”。

    陆奇回国加入百度后,为了照顾陆奇的饮食起居,陆奇家人曾一度迁居北京,百度还为陆奇的夫人配备了出入百度大厦的证件。

    2018年8月16日,YC媒体沟通会上,主持人官宣陆奇加盟YC担任YC中国首任CEO。陆奇和太太、女儿都在现场。

    陆奇的太太说:“我们有一半的时间在美国,他去哪里,我们就跟到哪里。”

    陆奇现场致辞的最后,也特别感谢了他的太太和家人。而且说希望在美国、中国多花时间,为了家庭。

    写这部分内容时,我突然想起小晚的《晚点》团队采访陆奇时曾问过这么一个问题:你是否经常会太过相信自己的力量?

    陆奇说:会。我太太一直觉得我太过于自信了。她老觉得我自以为是。

    这就是最好的支持吧,即便有时认为你的理想“不现实”,但只要你想做,我愿意陪你。这就是陆奇所说的“厚实的家庭基础”对一个人事业的重要性吧,如果你的梦想和成长能得到最亲近的人的支持,应该是最幸福的事了。

    3、人脉系统

    陆奇的人脉有多强?

    陆奇的人脉有多好,我相信在开头介绍中,那些顶级大佬的评价就是最好的答案,接触过的人都愿意帮他,合作过的人都愿意捧他。

    这里再讲几个点。

    1996年,陆奇博士毕业,摆在他面前的有三个选项:回国;华尔街;硅谷。

    他不知道该如何选择,就去请教李开复自己该去哪里、进哪个公司工作,李开复给出了自己的建议:“去硅谷吧,找一家技术类的公司。”

    于是陆奇就去了IBM的Almaden研究实验室,在那里工作了两年。

    之后,陆奇跳槽到雅虎,李开复又一次提醒他:“雅虎的股价不会一直在这个水平上,它要么会上涨5倍,要么下跌5倍。”

    陆奇职业生涯的两次关键时刻,李开复都给出了自己的建议和判断,一次让他看清了自己的优势和发展方向,一次让他看清了一家公司的发展空间。

    2008年,微软收购雅虎未果,当时已在微软任职的师兄沈向阳向鲍尔默引荐了陆奇,于是有了之后鲍尔默与陆奇长达6个小时的会谈。就这样,微软成功挖到了雅虎搜索引擎业务第一人——陆奇,陆奇也开启了他在微软的执行副总裁生涯。

    沈向阳的引荐,让陆奇成为“硅谷最有权势的华人”。

    再到2019年,YC撤出中国,陆奇独立运营新基金奇绩创坛,很快首期美元基金募集完成约1亿美金,出资人都是谁呢?

    陆奇的朋友们,阵容相当豪华:比尔盖茨、孙正义、红杉中国、高瓴资本、北极光创投等。

    陆奇如何看待人脉?

    中国人常说,做成一件事就是:天时地利人和。

    人和就是人脉。陆奇喜欢那句“机会总是留给有准备的人”,但他喜欢那句“机会是留给广结良友的人。”

    陆奇说:我个人的经验里,除了准备充分,你所拥有的人脉的宽度和广度也很重要,要通过交流结识更多人,最好是拥有不同职业背景的人,并跟他们保持关系)。美国有一句谚语,你获得一份工作不是因为你知道什么,而是因为你认识谁。这句话在某种角度上其实很正确,至少在我个人的经验里,这句话很正确。

    陆奇为什么有如此好的人脉资源?

    这句按照查理芒格的说法,当然是陆奇配得上。

    他做了什么让自己配得上?

    我们来分析几点。

    陆奇的人品。

    小晚的团队采访他时,多次试图让他聊百度,我们看陆奇的表现。

    比如问“回头来看,加入百度是一个错误的选择吗?”,陆奇回答“不好意思,我真的不想讲百度”。

    记者追问“你可以谈微软,但是避免谈百度,为什么?”,陆奇回答“是职业道德,我不想为它带来任何Distraction(干扰)”。

    我看完这句,佩服。

    谈及老东家YC,陆奇说:我个人很感激YC,我一定要强调这一点,他们送我们到了他们能送到的最远的地方。

    记者问:看着雅虎一步步走向衰落,为什么没有更早的离开?

    陆奇答:因为我答应杨致远做10年。当时雅虎进入了一个危机,杨致远找我说,中国有一个传统,朋友有难的时候不应该离开。我说那我就不离开了。

    膜拜。

    陆奇的情商。

    小晚团队问:你心中优秀的CEO是什么样子?你的榜样是谁?

    陆奇答:最强的人愿意为你而来,这是我觉得最好的CEO。在中国我觉得马云做得不错。

    后面的一个问题中,他特意提及了张小龙,“张小龙是我非常敬仰的人。中国的移动生态走得很远,很大因素是微信,微信可能是当代做得最出色的一个产品”。

    赞美阿里的组织,赞美腾讯的产品。

    陆奇的品质。

    一个浑身闪耀着发光品质的人怎么会没朋友?

    陆奇身上的优秀品质太多,略举一二。

    比如准时。自媒体人辉哥曾和陆奇在百度共事,他说某次出差,他们先到楼下大堂等,距离预定出发的时间还有1分钟时,他有一些着急,问要不要给陆奇打个电话。熟悉他的人说“他会准时的”,话音刚落,电梯门打开,陆奇出现,一分不差他极度守时。

    比如来信必回。无论是邮件还是微信,你只要发给他,他一定会回复,时间不确定,有时候是凌晨1-2点,有时是早晨5-6点左右。

    比如永远正向。辉哥说,工作中永远难免会遇到困难,但陆奇任何时候都保持正向的态度。无论是在公司开会,还是做大会的 MC,或是回邮件,永远是用简短有力,但充满了力量和鼓舞人上进的精神。大家最累最困难的时候,只要陆奇在那里振臂一挥,大家顿时又像打了鸡血。

    40岁:找一个可以让你淋漓尽致发挥的舞台

    陆奇:“40岁后,理想情况是找到一个可以让你淋漓尽致去发挥的舞台,一个人的才华和一个公司的才华只有在真正被释放的情况下才能实现它的价值。如果这个舞台是你自己的最好。”

    陆奇说过一句广为流传的话:

    “人生不是线性的,不要以为一班车就能把你从现在的位置带到你自己所期望的位置。”

    什么阶段做什么,40岁以后,就要找到一个可以让你淋漓尽致发挥的舞台。

    为什么不是20岁?

    前文也说了,陆奇认为那个阶段,绝大部分人对于人生想要做什么,其实只有模糊的感觉。

    而且陆奇认为,20多岁你要考虑一些现实因素,比如财务方面,工作收入需要满足自己生活的需求以及其他潜在财务责任,比如资助父母兄妹等,理想情况下,还可以有一些存款。

    20多岁多学多做多试,30多岁搭建系统,正是为了40岁以后真正有属于自己的舞台。

    微软属于他的舞台,但不够,所以他离开,回到中国,找更合适的舞台。

    比尔·盖茨一直挽留他,说:百度能给你什么,我都给你。

    陆奇说:你不能给我中国。

    中国是他现在更好的舞台,一个更大意义上的舞台,他要借助一家合适的平台在这个舞台上跳舞。很可惜,百度没能最终成为那个合适的舞台。后来他加入YC,很可惜YC也没最终成为那个合适的舞台。

    后来陆奇认为:属于自己的舞台,如果真的“属于自己”,那是最好的。

    “如果你想真正大规模改变世界,那你必须是这个企业的创始人,否则你永远受限于你的雇主。当这个平台是你亲手建造,那你可以发挥的能量和范围是最充分的。”

    陆奇的的签名是:“Do more, know more, be more”。

    这大概就是追求“be more”吧,40岁以后,自我更重要了。

    这个问题,《晚点》和陆奇的对话很精彩:

    《晚点》:甘地说,be the change you want to see in the world。成为更好的自我和建立一个更好的世界,后者难道不会推动人走得更远吗?

    陆奇:自我才会让人更永久。赚很多钱、建设一个成功公司,甚至建造一个国家、世界,都是外在目标。如果一个目标是外在的,你永远会在达到目标之后变得一片空虚。

    《晚点》:有一些商人,他们的目标就是赢,他们乐此不疲,似乎并不空虚。

    陆奇:你的追求是建立在别人输的基础之上,为什么世界上一定要有人输你才觉得你的生命是有价值的?我认为人的目标是你自己而不是任何外在的因素。

    如今他终于有了属于自己的舞台,奇绩创坛,也真的“属于自己”。

    三、参考

    ]]>
    + + 成长 + + + cs + +
    + + 关于中断 + /20210306-about-interrupt/ + 一、中断的解释

    网络

    中断(Interrupt)是指 处理器接收到来自硬件或软件的信号,提示发生了某个事件,应该被注意,这种情况就称为中断。

    软中断 (form 《UNIX 操作系统设计》)

    内核在收到软中断信号的进程上下文中处理软中断信号,因此进程必须运行以便处理信号。
    处理软中断信号的方式:

    1. 进程忽略软中断信号;
    2. 进程收到软中断信号后退出;
    3. 进程收到信号后执行一个特殊的(用户)函数;

    二、Java 的中断

    Java API 中线程相关的方法主要有三个:

    // 中断当前线程,仅设置中断标识位。
    public void interrupt()
    // 线程的中断标识位是否被标记
    public static boolean interrupted()
    // 类似 interrupted,无论之前是否被中断,都不会清空中断标识位
    public boolean isInterrupted()

    Java 中不推荐使用抢断式中断,倡导:

    一个线程的生命不应该由其他线程终止,应当由它自己选择是否停止。

    try {
    // 业务代码
    } catch (InterruptedException e) {
    // 可选:退出 | 忽略 | 执行相关逻辑
    }

    三、扩展

    • 中断有优先级,在处理高优先级中断时,会屏蔽低优先级的中断。

    四、参考

    ]]>
    + + 计算机科学 + + + cs + +
    + + 关于互联网医疗 + /20210718-about-internet-medicine/ + 零、背景

    由于近期换工作,停下了技术书籍,去了解行业
    《移动健康和智慧医疗》算是互联网医疗的科普资料
    前面部分的内容已经后面部分国际案例,了解之用足够
    里面提到的『量化自我』,如果把世界量化分析,岂不是美哉?
    随说:前提是大家相信分析出来的结论,以及按建议行事。

    一、微博读后感

    《移动健康和智慧医疗》0711~0717
    过去人口红利式的告诉发展逐渐降速
    老龄化突显使疾病预防和控制更重要

    医疗信息化建设提升医疗系统效率
    医疗数据收集与分析改进医疗方案
    多维度健康数据分析建议促进健康

    减少医疗信息不对称
    降低患者再次入院率
    早运动早发现早治疗
    有效地减少医疗支出

    互联网医疗典型方向:

    1. 促进健康
    2. 慢性病管理
    3. 诊断治疗(非急症性疾病)
    4. 院外康复指导和干预

    二、摘抄(医疗相关)

    《移动健康和智慧医疗》0711~0717
    过去中国高速发展,目前人口红利逐渐消失,老龄化日益突显,使得以传染病为主,转向非传染性疾病的预防和控制 。

    利用现代技术加强相关人员交流和互动

    1. 加强医疗保健机构的信息化程度,降本增效
    2. 从 cure 为主转化为 cure + care,减少病患
    3. 从生到死一条龙健康医护服务,预防为主

    慢性病管理
    血糖控制,通过监测行为特征,配合辅助功能,加强用户掌控能力,医生,家人远程支持与提醒。
    随说:收集足够信息,给出专业建议

    骨科手术后居家康复指导,尽快出院,获取足够信息与支持。
    随说:给予高质量内容,提升自我恢复能力。

    服务设计、实现、患者使用过程,均反应出一系列信息技术和预防、临床医学(流程、知识)之间的相互作用和支持,提现了互联网+多学科交叉,跨行业深度融合的思想。
    随说:降本增效,沉淀数据,线上线下无缝对接

    互联网医疗典型方向:健康促进、慢性病管理、诊断治疗、术后康复。

    健康医疗APP重要因素:专业性、相关性、有效性、趣味性、社交化。
    随说:社交?相互鼓励,交流心得

    医疗云平台:数据汇集分发、电子健康档案、业务管理、安全体系、运维系统。

    对多元异构多模态大数据进行处理、分析、挖掘,从中获取新知识和洞察,优化经营、管理,提升患者体验,提取最佳临床路径,辅助临床决策,实行计算机自动筛查和诊断,为其它利益相关方提供未知的信息资源等,这是互联网医疗追求的理想和目标。

    研究发现我国近2/3接受糖尿病治疗的患者未能适当控制血糖,因此会出现各种并发症,如:心脏病、中风、失明、肾功能衰竭等。

    2014年,慢性疾病导致的死亡占中国总死亡人数的 85%,导致的疾病负担占总疾病负担的 70%。

    虽然中国经济的增长速度赢得了世界的瞩目,但是如何提高慢性病是综合防治能力,减少慢性病的发病率、致残率和死亡率,降低费用支出,仍是健康医疗服务需要应对的巨大挑战。

    著名卫生经济学家普林斯顿大学教授 WR 提出,各国医疗体系的本质在于国家价值观和国家性格决定其系统如何运行。

    低功耗可穿戴设备使得随时随地采集健康医疗数据成为可能。这些大量的、连续的、包含上下文情境的健康医疗数据,为健康医疗提供决策依据、促进健康生活方式的养成、改善疾病监护、诊治状况。

    移动互联网具有用户身份、位置可识别,随时交互、多元数据可采集、用户可高度参与等一系列技术特征,使得移动互联网与其他行业融合时带来新的解决方案、服务模式和发展机遇。

    医护路径全流程服务:促进健康、预防/慢性病管理、院前急救、诊断治疗、院外康复/干预。

    院前急救:伤者历史数据、辅助诊断、车载数据同步医院,医院提前准备。

    远程问诊成本低,灵活性强,出现一些早期症状可以及时获得指导。
    随说:小毛病挂专家号,会诊几分钟…

    量化自我:2007年凯文凯利提出,通过设备和技术持续跟踪、采集自己的生理心理特征,形成生活日志,探索身体健康的奥秘。

    可穿戴设备的数据如果作为对患者实施诊断、治疗等环节的重要依据,则属于医疗器械,需要监管。

    智能服装才是可穿戴设备的终极形式。

    单点登录的目的是为了让多个相关联的应用使用相同的登录过程,代码复用,提升体验。

    CAS 是开源的企业级单点登录解决方案。

    根据密钥的职责和重要性,一般分为:主密钥、二级密钥、初级密钥。初级密钥是直接使用的密钥。高级密钥对低一级密钥做加密,保护低一级的密钥。

    不同级别的密钥应该分开存放,最好是异地、异设备。

    密钥不以明文形式存储在数据库或传输媒介中:避免密钥被截获后直接用来解密数据,增强安全性。

    HIS:医院信息系统
    PACS:影像系统
    LIS:检验信息系统
    RIS:放射信息系统
    EMR:电子病历
    HIMSS:美国医疗信息和管理协会
    EMRAM:电子病历采纳模型
    CHA:康体佳健康联盟

    流式计算的数据来自一个最近的时间窗口,数据延迟短但精度可能较低。

    一个完全测序的人类基因组包含 100~1000G的数据。

    精准医疗:根据每个人的基因、生活环境、生活方式等的不同,提供相适应的个性化疾病治疗和预防

    健康医疗大数据的收集、分析、整理和挖掘对于患者健康医护路径的不同环节都有非常重要的价值。

    任何疾病最有效的治疗方式是预防保健。

    每年心脑血管医疗费用占比搞,且再次入院率高,如何有效管理心血管病、节省医疗开支、降低出院患者再次入院率已经成为社会各界关注的重点。

    美国的分级诊疗制度、医生多点职业、医疗信息化、商业保险、医院集团管理等一系列成熟体系,为美国移动健康医疗的发展提供了肥沃土壤。

    远程医疗服务提供简单、方便、低成本的方式为患者远程诊治感冒、流感、喉咙痛或其它简单非急症性疾病。

    2014 年 5 月,阿里旗下的支付宝推出“未来医院计划”,支付宝对医疗机构开放自己的平台能力,包括:账户体系、移动平台、支付及金融解决方案、云计算能力、大数据平台等,旨在优化患者在医院的就医流程,提高就医体验,提升医院的管理效率。

    2015年阿里健康业务

    1. 药品零售
    2. 医疗服务(远程医疗平台,家庭医生等)
    3. 药监码(电子监管平台)
    4. 健康保险

    北京大学人民医院是中国最具实力的三级甲等研究型医院之一,积极推进医疗信息化建设,2014 年通过了国际上衡量医院信息化水准的 HIMSS EMRAM 最高等级(7级)的评审,成为亚洲第二,中国第一家。

    ]]>
    + + read + + + read + +
    + + count函数效率问题 - 了解count函数 + /20170303-count%E5%87%BD%E6%95%B0%E6%95%88%E7%8E%87%E9%97%AE%E9%A2%98%20-%20%E4%BA%86%E8%A7%A3count%E5%87%BD%E6%95%B0/ + count函数的作用

    想要真正的理解count函数,我们就必须明白count函数的作用。

    作用一:统计某一列非空(not null)值得数量,即统计某列有值得结果数,使用count(col)。

    作用二:统计结果集的行数,此时不用管某列是否为null值。即使用count(*).

    明白了这点,我们就应该知道MySQL的count(*)并不是想象中的那样,统计每一列的值,而是直接忽视掉所有列,直接统计行数,那么它的效率肯定是很高的。

    但是有一点,当col指定了该字段为NOT NULL时实际上,MySQL会自动将count(col)转为count(*),但是这样也同样耗费了些时间,如果col没有指定为NOT NULL的话,那么效率就更低了,MySQL就必须要判断每一行的值是否为空。

    所以综上所述,如果是要统计行数最好优先使用select count(*)

    当统计某一列等于多少的值得时候可以使用下面两种方法:

    SELECT SUM(IF(id = 23,1,0)) FROM table 
    SELECT COUNT(id = 23 OR NULL) FROM table

    ]]>
    + + java + + + mysql + +
    + + 优雅地分桶 - 数据分片 - list 拆分 + /20220218-divide-barrels-gracefully/ + 零、背景

    最近在做数据迁移
    为了加速迁移速度
    其中就需要把查询到的数据( max 100 条)
    拆分成 5 份,然后执行 5 个子任务,加速处理

    一、代码

    1.1 常规做法

    public static <T> List<List<T>> split2(List<T> lists, int subCount) {
    if (CollectionUtils.isEmpty(lists) || subCount < 1) {
    return null;
    }

    List<List<T>> result = new ArrayList<>();

    int size = lists.size();
    int count = (size + subCount - 1) / subCount;

    for (int i = 0; i < count; i++) {
    List<T> subList = lists.subList(i * subCount, (Math.min((i + 1) * subCount, size)));
    result.add(subList);
    }
    return result;
    }

    1.2 新颖做法

    public static <T> List<List<T>> split(List<T> lists, int subCount) {
    if (CollectionUtils.isEmpty(lists) || subCount < 1) {
    return null;
    }
    // 简约的计算分桶次数,避免判断是否有余数,再+1
    int splitTimes = (lists.size() + subCount - 1) / subCount;
    return Stream
    // 每次递增指定子列表个数
    .iterate(0, n -> n + subCount)
    // 限制循环次数
    .limit(splitTimes)
    // 转换成想要的结果
    .map(item -> lists.stream()
    // 跳过之前的下标
    .skip(item)
    // 限制每次的个数为子表个数
    .limit(subCount)
    // 转换为子列表
    .collect(Collectors.toList()))
    // 转换为主列表
    .collect(Collectors.toList());
    }

    三、性能

    3.1 结果

    从结果可以看出,一味地追求新颖也不是好事,需要知其然
    java 8 的 stream 并不占优势,性能相差好几个数量级…

    Benchmark              Mode  Cnt       Score       Error  Units
    ListSplit.split2Test thrpt 10 306050.155 ± 24646.689 ops/s
    ListSplit.splitTest thrpt 10 353.048 ± 122.423 ops/s

    3.3 压测代码

    压测教程:JMH 使用参考

    import com.google.api.client.util.Lists;
    import org.openjdk.jmh.annotations.Benchmark;
    import org.openjdk.jmh.annotations.BenchmarkMode;
    import org.openjdk.jmh.annotations.Fork;
    import org.openjdk.jmh.annotations.Measurement;
    import org.openjdk.jmh.annotations.Mode;
    import org.openjdk.jmh.annotations.Scope;
    import org.openjdk.jmh.annotations.Setup;
    import org.openjdk.jmh.annotations.State;
    import org.openjdk.jmh.annotations.Warmup;
    import org.springframework.util.CollectionUtils;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;

    /**
    * @author hisenyuan
    * @date 2022/2/18 20:05
    */
    // 默认的 State,每个测试线程分配一个实例
    @State(Scope.Thread)
    // 如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。
    @Fork(1)
    // 预热的次数 3 次基准测试(不是执行三次方法)
    @Warmup(iterations = 1)
    // 基准执行次数 10 次(参数含义同上)
    @Measurement(iterations = 10)
    @BenchmarkMode(Mode.Throughput)
    public class ListSplit {
    private static final List<Integer> INTEGERS = Lists.newArrayList();
    @Setup
    public void init() {
    List<Integer> list = Stream.iterate(0, n -> n + 1)
    .limit(1000)
    .collect(Collectors.toList());
    INTEGERS.addAll(list);
    }

    @Benchmark
    public void splitTest(){
    List<List<Integer>> listList = split(INTEGERS, 3);
    }
    @Benchmark
    public void split2Test(){
    List<List<Integer>> listList = split2(INTEGERS, 3);
    }

    public static <T> List<List<T>> split(List<T> lists, int subCount) {
    // 简约的计算分桶次数,避免判断是否有余数,再+1
    int splitTimes = (lists.size() + subCount - 1) / subCount;
    return Stream
    // 每次递增指定子列表个数
    .iterate(0, n -> n + subCount)
    // 限制循环次数
    .limit(splitTimes)
    // 转换成想要的结果
    .map(item -> lists.stream()
    // 跳过之前的下标
    .skip(item)
    // 限制每次的个数为子表个数
    .limit(subCount)
    // 转换为子列表
    .collect(Collectors.toList()))
    // 转换为主列表
    .collect(Collectors.toList());
    }

    public static <T> List<List<T>> split2(List<T> list, int len) {
    if (CollectionUtils.isEmpty(list) || len < 1) {
    return null;
    }

    List<List<T>> result = new ArrayList<>();

    int size = list.size();
    int count = (size + len - 1) / len;

    for (int i = 0; i < count; i++) {
    List<T> subList = list.subList(i * len, (Math.min((i + 1) * len, size)));
    result.add(subList);
    }
    return result;
    }

    四、后话

    其中不错的一点我觉得是

    int splitTimes = (lists.size() + subCount - 1) / subCount;

    这样巧妙的计算,避免了先取模,后判断是否有余数的繁杂。
    这个不是我想到的,也不知道怎么通过数学证明,但是这样就是对的 ==

    ps:是谁说数学没用的 ????

    ]]>
    + + java + + + java + +
    + + Spark第一个java程序 - 字词统计 - Spark java idea + /20170802-Spark%E7%AC%AC%E4%B8%80%E4%B8%AAjava%E7%A8%8B%E5%BA%8F%20-%20%E5%AD%97%E8%AF%8D%E7%BB%9F%E8%AE%A1%20-%20Spark%20java%20idea/ + 在本地利用idea,java开发spark程序

    原来并不用安装spark什么的这些东西

    这样就不会那么繁琐,门槛也低了点

    具体过程如下

    一、创建工程

    1. idea -> new Project -> maven -> create from archetype -> maven-archetype-quickstart
    2. pom.xml添加依赖

      <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-core_2.11</artifactId>
      <version>2.0.1</version>
      </dependency>
    3. 编写java代码

      package com.hisen.spark;

      import org.apache.spark.SparkConf;
      import org.apache.spark.api.java.JavaRDD;
      import org.apache.spark.api.java.JavaSparkContext;
      import org.apache.spark.api.java.function.Function;

      /**
      * 第一个spark程序
      * Created by hisenyuan on 2017/8/2 at 16:36.
      */
      public class SimpleApp {

      public static void main(String[] args) {
      // Should be some file on your system
      String logFile = "D:\\logs\\boss_debug.log";
      //设置本地运行,设置名称(在spark web ui上显示)
      SparkConf sparkConf = new SparkConf().setMaster("local").setAppName("Simple Application");
      JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
      JavaRDD<String> logData = javaSparkContext.textFile(logFile).cache();

      //统计包含a的次数
      long countA = logData.filter(new Function<String, Boolean>() {
      public Boolean call(String s) throws Exception {
      return s.contains("a");
      }
      }).count();

      //统计包含b的次数
      long countB = logData.filter(new Function<String, Boolean>() {
      public Boolean call(String s) throws Exception {
      return s.contains("b");
      }
      }).count();
      System.out.printf("Lines with a: %d, lines with b: %d\n", countA, countB);
      //Lines with a: 11146, lines with b: 10760
      }
      }
    4. 运行main方法:结果:Lines with a: 11146, lines with b: 10760

    5. 日志如下:
      C:\hisenwork\soft\jdk8\bin\java -Dspark.master=local "-javaagent:C:\hisenwork\IntelliJ IDEA 2017.1.1\lib\idea_rt.jar=59366:C:\hisenwork\IntelliJ IDEA 2017.1.1\bin" -Dfile.encoding=UTF-8 -classpath C:\hisenwork\soft\jdk8\jre\lib\charsets.jar;C:\hisenwork\soft\jdk8\jre\lib\deploy.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\access-bridge-64.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\cldrdata.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\dnsns.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\jaccess.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\jfxrt.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\localedata.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\nashorn.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunec.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunjce_provider.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunmscapi.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\sunpkcs11.jar;C:\hisenwork\soft\jdk8\jre\lib\ext\zipfs.jar;C:\hisenwork\soft\jdk8\jre\lib\javaws.jar;C:\hisenwork\soft\jdk8\jre\lib\jce.jar;C:\hisenwork\soft\jdk8\jre\lib\jfr.jar;C:\hisenwork\soft\jdk8\jre\lib\jfxswt.jar;C:\hisenwork\soft\jdk8\jre\lib\jsse.jar;C:\hisenwork\soft\jdk8\jre\lib\management-agent.jar;C:\hisenwork\soft\jdk8\jre\lib\plugin.jar;C:\hisenwork\soft\jdk8\jre\lib\resources.jar;C:\hisenwork\soft\jdk8\jre\lib\rt.jar;C:\hisenwork\code\rmitec\SparkTest\target\classes;C:\hisenwork\soft\maven\org\apache\spark\spark-core_2.11\2.0.1\spark-core_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\apache\avro\avro-mapred\1.7.7\avro-mapred-1.7.7-hadoop2.jar;C:\hisenwork\soft\maven\org\apache\avro\avro-ipc\1.7.7\avro-ipc-1.7.7.jar;C:\hisenwork\soft\maven\org\apache\avro\avro\1.7.7\avro-1.7.7.jar;C:\hisenwork\soft\maven\org\apache\avro\avro-ipc\1.7.7\avro-ipc-1.7.7-tests.jar;C:\hisenwork\soft\maven\org\codehaus\jackson\jackson-core-asl\1.9.13\jackson-core-asl-1.9.13.jar;C:\hisenwork\soft\maven\org\codehaus\jackson\jackson-mapper-asl\1.9.13\jackson-mapper-asl-1.9.13.jar;C:\hisenwork\soft\maven\com\twitter\chill_2.11\0.8.0\chill_2.11-0.8.0.jar;C:\hisenwork\soft\maven\com\esotericsoftware\kryo-shaded\3.0.3\kryo-shaded-3.0.3.jar;C:\hisenwork\soft\maven\com\esotericsoftware\minlog\1.3.0\minlog-1.3.0.jar;C:\hisenwork\soft\maven\org\objenesis\objenesis\2.1\objenesis-2.1.jar;C:\hisenwork\soft\maven\com\twitter\chill-java\0.8.0\chill-java-0.8.0.jar;C:\hisenwork\soft\maven\org\apache\xbean\xbean-asm5-shaded\4.4\xbean-asm5-shaded-4.4.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-client\2.2.0\hadoop-client-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-common\2.2.0\hadoop-common-2.2.0.jar;C:\hisenwork\soft\maven\commons-cli\commons-cli\1.2\commons-cli-1.2.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-math\2.1\commons-math-2.1.jar;C:\hisenwork\soft\maven\xmlenc\xmlenc\0.52\xmlenc-0.52.jar;C:\hisenwork\soft\maven\commons-io\commons-io\2.1\commons-io-2.1.jar;C:\hisenwork\soft\maven\commons-lang\commons-lang\2.5\commons-lang-2.5.jar;C:\hisenwork\soft\maven\commons-configuration\commons-configuration\1.6\commons-configuration-1.6.jar;C:\hisenwork\soft\maven\commons-collections\commons-collections\3.2.1\commons-collections-3.2.1.jar;C:\hisenwork\soft\maven\commons-digester\commons-digester\1.8\commons-digester-1.8.jar;C:\hisenwork\soft\maven\commons-beanutils\commons-beanutils\1.7.0\commons-beanutils-1.7.0.jar;C:\hisenwork\soft\maven\commons-beanutils\commons-beanutils-core\1.8.0\commons-beanutils-core-1.8.0.jar;C:\hisenwork\soft\maven\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-auth\2.2.0\hadoop-auth-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-compress\1.4.1\commons-compress-1.4.1.jar;C:\hisenwork\soft\maven\org\tukaani\xz\1.0\xz-1.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-hdfs\2.2.0\hadoop-hdfs-2.2.0.jar;C:\hisenwork\soft\maven\org\mortbay\jetty\jetty-util\6.1.26\jetty-util-6.1.26.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-app\2.2.0\hadoop-mapreduce-client-app-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-common\2.2.0\hadoop-mapreduce-client-common-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-client\2.2.0\hadoop-yarn-client-2.2.0.jar;C:\hisenwork\soft\maven\com\google\inject\guice\3.0\guice-3.0.jar;C:\hisenwork\soft\maven\javax\inject\javax.inject\1\javax.inject-1.jar;C:\hisenwork\soft\maven\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-server-common\2.2.0\hadoop-yarn-server-common-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-shuffle\2.2.0\hadoop-mapreduce-client-shuffle-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-api\2.2.0\hadoop-yarn-api-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-core\2.2.0\hadoop-mapreduce-client-core-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-yarn-common\2.2.0\hadoop-yarn-common-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-mapreduce-client-jobclient\2.2.0\hadoop-mapreduce-client-jobclient-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\hadoop\hadoop-annotations\2.2.0\hadoop-annotations-2.2.0.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-launcher_2.11\2.0.1\spark-launcher_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-network-common_2.11\2.0.1\spark-network-common_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\fusesource\leveldbjni\leveldbjni-all\1.8\leveldbjni-all-1.8.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\core\jackson-annotations\2.6.5\jackson-annotations-2.6.5.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-network-shuffle_2.11\2.0.1\spark-network-shuffle_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-unsafe_2.11\2.0.1\spark-unsafe_2.11-2.0.1.jar;C:\hisenwork\soft\maven\net\java\dev\jets3t\jets3t\0.7.1\jets3t-0.7.1.jar;C:\hisenwork\soft\maven\commons-codec\commons-codec\1.3\commons-codec-1.3.jar;C:\hisenwork\soft\maven\commons-httpclient\commons-httpclient\3.1\commons-httpclient-3.1.jar;C:\hisenwork\soft\maven\org\apache\curator\curator-recipes\2.4.0\curator-recipes-2.4.0.jar;C:\hisenwork\soft\maven\org\apache\curator\curator-framework\2.4.0\curator-framework-2.4.0.jar;C:\hisenwork\soft\maven\org\apache\curator\curator-client\2.4.0\curator-client-2.4.0.jar;C:\hisenwork\soft\maven\org\apache\zookeeper\zookeeper\3.4.5\zookeeper-3.4.5.jar;C:\hisenwork\soft\maven\com\google\guava\guava\14.0.1\guava-14.0.1.jar;C:\hisenwork\soft\maven\javax\servlet\javax.servlet-api\3.1.0\javax.servlet-api-3.1.0.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-lang3\3.3.2\commons-lang3-3.3.2.jar;C:\hisenwork\soft\maven\org\apache\commons\commons-math3\3.4.1\commons-math3-3.4.1.jar;C:\hisenwork\soft\maven\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;C:\hisenwork\soft\maven\org\slf4j\slf4j-api\1.7.16\slf4j-api-1.7.16.jar;C:\hisenwork\soft\maven\org\slf4j\jul-to-slf4j\1.7.16\jul-to-slf4j-1.7.16.jar;C:\hisenwork\soft\maven\org\slf4j\jcl-over-slf4j\1.7.16\jcl-over-slf4j-1.7.16.jar;C:\hisenwork\soft\maven\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\hisenwork\soft\maven\org\slf4j\slf4j-log4j12\1.7.16\slf4j-log4j12-1.7.16.jar;C:\hisenwork\soft\maven\com\ning\compress-lzf\1.0.3\compress-lzf-1.0.3.jar;C:\hisenwork\soft\maven\org\xerial\snappy\snappy-java\1.1.2.6\snappy-java-1.1.2.6.jar;C:\hisenwork\soft\maven\net\jpountz\lz4\lz4\1.3.0\lz4-1.3.0.jar;C:\hisenwork\soft\maven\org\roaringbitmap\RoaringBitmap\0.5.11\RoaringBitmap-0.5.11.jar;C:\hisenwork\soft\maven\commons-net\commons-net\2.2\commons-net-2.2.jar;C:\hisenwork\soft\maven\org\scala-lang\scala-library\2.11.8\scala-library-2.11.8.jar;C:\hisenwork\soft\maven\org\json4s\json4s-jackson_2.11\3.2.11\json4s-jackson_2.11-3.2.11.jar;C:\hisenwork\soft\maven\org\json4s\json4s-core_2.11\3.2.11\json4s-core_2.11-3.2.11.jar;C:\hisenwork\soft\maven\org\json4s\json4s-ast_2.11\3.2.11\json4s-ast_2.11-3.2.11.jar;C:\hisenwork\soft\maven\com\thoughtworks\paranamer\paranamer\2.6\paranamer-2.6.jar;C:\hisenwork\soft\maven\org\scala-lang\scalap\2.11.0\scalap-2.11.0.jar;C:\hisenwork\soft\maven\org\scala-lang\scala-compiler\2.11.0\scala-compiler-2.11.0.jar;C:\hisenwork\soft\maven\org\scala-lang\modules\scala-parser-combinators_2.11\1.0.1\scala-parser-combinators_2.11-1.0.1.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\core\jersey-client\2.22.2\jersey-client-2.22.2.jar;C:\hisenwork\soft\maven\javax\ws\rs\javax.ws.rs-api\2.0.1\javax.ws.rs-api-2.0.1.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\hk2-api\2.4.0-b34\hk2-api-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\hk2-utils\2.4.0-b34\hk2-utils-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\external\aopalliance-repackaged\2.4.0-b34\aopalliance-repackaged-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\external\javax.inject\2.4.0-b34\javax.inject-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\hk2-locator\2.4.0-b34\hk2-locator-2.4.0-b34.jar;C:\hisenwork\soft\maven\org\javassist\javassist\3.18.1-GA\javassist-3.18.1-GA.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\core\jersey-common\2.22.2\jersey-common-2.22.2.jar;C:\hisenwork\soft\maven\javax\annotation\javax.annotation-api\1.2\javax.annotation-api-1.2.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\bundles\repackaged\jersey-guava\2.22.2\jersey-guava-2.22.2.jar;C:\hisenwork\soft\maven\org\glassfish\hk2\osgi-resource-locator\1.0.1\osgi-resource-locator-1.0.1.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\core\jersey-server\2.22.2\jersey-server-2.22.2.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\media\jersey-media-jaxb\2.22.2\jersey-media-jaxb-2.22.2.jar;C:\hisenwork\soft\maven\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\containers\jersey-container-servlet\2.22.2\jersey-container-servlet-2.22.2.jar;C:\hisenwork\soft\maven\org\glassfish\jersey\containers\jersey-container-servlet-core\2.22.2\jersey-container-servlet-core-2.22.2.jar;C:\hisenwork\soft\maven\org\apache\mesos\mesos\0.21.1\mesos-0.21.1-shaded-protobuf.jar;C:\hisenwork\soft\maven\io\netty\netty-all\4.0.29.Final\netty-all-4.0.29.Final.jar;C:\hisenwork\soft\maven\io\netty\netty\3.8.0.Final\netty-3.8.0.Final.jar;C:\hisenwork\soft\maven\com\clearspring\analytics\stream\2.7.0\stream-2.7.0.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-core\3.1.2\metrics-core-3.1.2.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-jvm\3.1.2\metrics-jvm-3.1.2.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-json\3.1.2\metrics-json-3.1.2.jar;C:\hisenwork\soft\maven\io\dropwizard\metrics\metrics-graphite\3.1.2\metrics-graphite-3.1.2.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\core\jackson-databind\2.6.5\jackson-databind-2.6.5.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\core\jackson-core\2.6.5\jackson-core-2.6.5.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\module\jackson-module-scala_2.11\2.6.5\jackson-module-scala_2.11-2.6.5.jar;C:\hisenwork\soft\maven\org\scala-lang\scala-reflect\2.11.7\scala-reflect-2.11.7.jar;C:\hisenwork\soft\maven\com\fasterxml\jackson\module\jackson-module-paranamer\2.6.5\jackson-module-paranamer-2.6.5.jar;C:\hisenwork\soft\maven\org\apache\ivy\ivy\2.4.0\ivy-2.4.0.jar;C:\hisenwork\soft\maven\oro\oro\2.0.8\oro-2.0.8.jar;C:\hisenwork\soft\maven\net\razorvine\pyrolite\4.9\pyrolite-4.9.jar;C:\hisenwork\soft\maven\net\sf\py4j\py4j\0.10.3\py4j-0.10.3.jar;C:\hisenwork\soft\maven\org\apache\spark\spark-tags_2.11\2.0.1\spark-tags_2.11-2.0.1.jar;C:\hisenwork\soft\maven\org\scalatest\scalatest_2.11\2.2.6\scalatest_2.11-2.2.6.jar;C:\hisenwork\soft\maven\org\scala-lang\modules\scala-xml_2.11\1.0.2\scala-xml_2.11-1.0.2.jar;C:\hisenwork\soft\maven\org\spark-project\spark\unused\1.0.0\unused-1.0.0.jar com.hisen.spark.SimpleApp
      Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
      17/08/02 17:00:09 INFO SparkContext: Running Spark version 2.0.1
      17/08/02 17:00:10 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
      17/08/02 17:00:10 INFO SecurityManager: Changing view acls to: Administrator
      17/08/02 17:00:10 INFO SecurityManager: Changing modify acls to: Administrator
      17/08/02 17:00:10 INFO SecurityManager: Changing view acls groups to:
      17/08/02 17:00:10 INFO SecurityManager: Changing modify acls groups to:
      17/08/02 17:00:10 INFO SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users with view permissions: Set(Administrator); groups with view permissions: Set(); users with modify permissions: Set(Administrator); groups with modify permissions: Set()
      17/08/02 17:00:11 INFO Utils: Successfully started service 'sparkDriver' on port 59388.
      17/08/02 17:00:11 INFO SparkEnv: Registering MapOutputTracker
      17/08/02 17:00:11 INFO SparkEnv: Registering BlockManagerMaster
      17/08/02 17:00:11 INFO DiskBlockManager: Created local directory at C:\Users\Administrator\AppData\Local\Temp\blockmgr-630d8294-e5cb-428d-8189-4c3313e46fa3
      17/08/02 17:00:12 INFO MemoryStore: MemoryStore started with capacity 894.3 MB
      17/08/02 17:00:12 INFO SparkEnv: Registering OutputCommitCoordinator
      17/08/02 17:00:12 INFO Utils: Successfully started service 'SparkUI' on port 4040.
      17/08/02 17:00:12 INFO SparkUI: Bound SparkUI to 0.0.0.0, and started at http://169.254.90.236:4040
      17/08/02 17:00:12 INFO Executor: Starting executor ID driver on host localhost
      17/08/02 17:00:12 INFO Utils: Successfully started service 'org.apache.spark.network.netty.NettyBlockTransferService' on port 59397.
      17/08/02 17:00:12 INFO NettyBlockTransferService: Server created on 169.254.90.236:59397
      17/08/02 17:00:12 INFO BlockManagerMaster: Registering BlockManager BlockManagerId(driver, 169.254.90.236, 59397)
      17/08/02 17:00:12 INFO BlockManagerMasterEndpoint: Registering block manager 169.254.90.236:59397 with 894.3 MB RAM, BlockManagerId(driver, 169.254.90.236, 59397)
      17/08/02 17:00:12 INFO BlockManagerMaster: Registered BlockManager BlockManagerId(driver, 169.254.90.236, 59397)
      17/08/02 17:00:14 INFO MemoryStore: Block broadcast_0 stored as values in memory (estimated size 127.1 KB, free 894.2 MB)
      17/08/02 17:00:14 INFO MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 14.3 KB, free 894.2 MB)
      17/08/02 17:00:14 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on 169.254.90.236:59397 (size: 14.3 KB, free: 894.3 MB)
      17/08/02 17:00:14 INFO SparkContext: Created broadcast 0 from textFile at SimpleApp.java:19
      17/08/02 17:00:14 ERROR Shell: Failed to locate the winutils binary in the hadoop binary path
      java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
      at org.apache.hadoop.util.Shell.getQualifiedBinPath(Shell.java:278)
      at org.apache.hadoop.util.Shell.getWinUtilsPath(Shell.java:300)
      at org.apache.hadoop.util.Shell.<clinit>(Shell.java:293)
      at org.apache.hadoop.util.StringUtils.<clinit>(StringUtils.java:76)
      at org.apache.hadoop.mapred.FileInputFormat.setInputPaths(FileInputFormat.java:362)
      at org.apache.spark.SparkContext$$anonfun$hadoopFile$1$$anonfun$29.apply(SparkContext.scala:992)
      at org.apache.spark.SparkContext$$anonfun$hadoopFile$1$$anonfun$29.apply(SparkContext.scala:992)
      at org.apache.spark.rdd.HadoopRDD$$anonfun$getJobConf$6.apply(HadoopRDD.scala:176)
      at org.apache.spark.rdd.HadoopRDD$$anonfun$getJobConf$6.apply(HadoopRDD.scala:176)
      at scala.Option.map(Option.scala:146)
      at org.apache.spark.rdd.HadoopRDD.getJobConf(HadoopRDD.scala:176)
      at org.apache.spark.rdd.HadoopRDD.getPartitions(HadoopRDD.scala:195)
      at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:248)
      at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:246)
      at scala.Option.getOrElse(Option.scala:121)
      at org.apache.spark.rdd.RDD.partitions(RDD.scala:246)
      at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
      at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:248)
      at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:246)
      at scala.Option.getOrElse(Option.scala:121)
      at org.apache.spark.rdd.RDD.partitions(RDD.scala:246)
      at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
      at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:248)
      at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:246)
      at scala.Option.getOrElse(Option.scala:121)
      at org.apache.spark.rdd.RDD.partitions(RDD.scala:246)
      at org.apache.spark.SparkContext.runJob(SparkContext.scala:1930)
      at org.apache.spark.rdd.RDD.count(RDD.scala:1134)
      at org.apache.spark.api.java.JavaRDDLike$class.count(JavaRDDLike.scala:454)
      at org.apache.spark.api.java.AbstractJavaRDDLike.count(JavaRDDLike.scala:45)
      at com.hisen.spark.SimpleApp.main(SimpleApp.java:26)
      17/08/02 17:00:14 INFO FileInputFormat: Total input paths to process : 1
      17/08/02 17:00:14 INFO SparkContext: Starting job: count at SimpleApp.java:26
      17/08/02 17:00:15 INFO DAGScheduler: Got job 0 (count at SimpleApp.java:26) with 1 output partitions
      17/08/02 17:00:15 INFO DAGScheduler: Final stage: ResultStage 0 (count at SimpleApp.java:26)
      17/08/02 17:00:15 INFO DAGScheduler: Parents of final stage: List()
      17/08/02 17:00:15 INFO DAGScheduler: Missing parents: List()
      17/08/02 17:00:15 INFO DAGScheduler: Submitting ResultStage 0 (MapPartitionsRDD[2] at filter at SimpleApp.java:22), which has no missing parents
      17/08/02 17:00:15 INFO MemoryStore: Block broadcast_1 stored as values in memory (estimated size 3.2 KB, free 894.2 MB)
      17/08/02 17:00:15 INFO MemoryStore: Block broadcast_1_piece0 stored as bytes in memory (estimated size 1966.0 B, free 894.2 MB)
      17/08/02 17:00:15 INFO BlockManagerInfo: Added broadcast_1_piece0 in memory on 169.254.90.236:59397 (size: 1966.0 B, free: 894.3 MB)
      17/08/02 17:00:15 INFO SparkContext: Created broadcast 1 from broadcast at DAGScheduler.scala:1012
      17/08/02 17:00:15 INFO DAGScheduler: Submitting 1 missing tasks from ResultStage 0 (MapPartitionsRDD[2] at filter at SimpleApp.java:22)
      17/08/02 17:00:15 INFO TaskSchedulerImpl: Adding task set 0.0 with 1 tasks
      17/08/02 17:00:15 INFO TaskSetManager: Starting task 0.0 in stage 0.0 (TID 0, localhost, partition 0, PROCESS_LOCAL, 5324 bytes)
      17/08/02 17:00:15 INFO Executor: Running task 0.0 in stage 0.0 (TID 0)
      17/08/02 17:00:15 INFO HadoopRDD: Input split: file:/D:/logs/boss_debug.log:0+4059273
      17/08/02 17:00:15 INFO deprecation: mapred.tip.id is deprecated. Instead, use mapreduce.task.id
      17/08/02 17:00:15 INFO deprecation: mapred.task.id is deprecated. Instead, use mapreduce.task.attempt.id
      17/08/02 17:00:15 INFO deprecation: mapred.task.is.map is deprecated. Instead, use mapreduce.task.ismap
      17/08/02 17:00:15 INFO deprecation: mapred.task.partition is deprecated. Instead, use mapreduce.task.partition
      17/08/02 17:00:15 INFO deprecation: mapred.job.id is deprecated. Instead, use mapreduce.job.id
      17/08/02 17:00:15 INFO MemoryStore: Block rdd_1_0 stored as values in memory (estimated size 5.7 MB, free 888.4 MB)
      17/08/02 17:00:15 INFO BlockManagerInfo: Added rdd_1_0 in memory on 169.254.90.236:59397 (size: 5.7 MB, free: 888.5 MB)
      17/08/02 17:00:15 INFO Executor: Finished task 0.0 in stage 0.0 (TID 0). 1842 bytes result sent to driver
      17/08/02 17:00:16 INFO TaskSetManager: Finished task 0.0 in stage 0.0 (TID 0) in 691 ms on localhost (1/1)
      17/08/02 17:00:16 INFO TaskSchedulerImpl: Removed TaskSet 0.0, whose tasks have all completed, from pool
      17/08/02 17:00:16 INFO DAGScheduler: ResultStage 0 (count at SimpleApp.java:26) finished in 0.726 s
      17/08/02 17:00:16 INFO DAGScheduler: Job 0 finished: count at SimpleApp.java:26, took 1.078428 s
      17/08/02 17:00:16 INFO SparkContext: Starting job: count at SimpleApp.java:33
      17/08/02 17:00:16 INFO DAGScheduler: Got job 1 (count at SimpleApp.java:33) with 1 output partitions
      17/08/02 17:00:16 INFO DAGScheduler: Final stage: ResultStage 1 (count at SimpleApp.java:33)
      17/08/02 17:00:16 INFO DAGScheduler: Parents of final stage: List()
      17/08/02 17:00:16 INFO DAGScheduler: Missing parents: List()
      17/08/02 17:00:16 INFO DAGScheduler: Submitting ResultStage 1 (MapPartitionsRDD[3] at filter at SimpleApp.java:29), which has no missing parents
      17/08/02 17:00:16 INFO MemoryStore: Block broadcast_2 stored as values in memory (estimated size 3.2 KB, free 888.4 MB)
      17/08/02 17:00:16 INFO MemoryStore: Block broadcast_2_piece0 stored as bytes in memory (estimated size 1967.0 B, free 888.4 MB)
      17/08/02 17:00:16 INFO BlockManagerInfo: Added broadcast_2_piece0 in memory on 169.254.90.236:59397 (size: 1967.0 B, free: 888.5 MB)
      17/08/02 17:00:16 INFO SparkContext: Created broadcast 2 from broadcast at DAGScheduler.scala:1012
      17/08/02 17:00:16 INFO DAGScheduler: Submitting 1 missing tasks from ResultStage 1 (MapPartitionsRDD[3] at filter at SimpleApp.java:29)
      17/08/02 17:00:16 INFO TaskSchedulerImpl: Adding task set 1.0 with 1 tasks
      17/08/02 17:00:16 INFO TaskSetManager: Starting task 0.0 in stage 1.0 (TID 1, localhost, partition 0, PROCESS_LOCAL, 5324 bytes)
      17/08/02 17:00:16 INFO Executor: Running task 0.0 in stage 1.0 (TID 1)
      17/08/02 17:00:16 INFO BlockManager: Found block rdd_1_0 locally
      17/08/02 17:00:16 INFO Executor: Finished task 0.0 in stage 1.0 (TID 1). 954 bytes result sent to driver
      17/08/02 17:00:16 INFO DAGScheduler: ResultStage 1 (count at SimpleApp.java:33) finished in 0.100 s
      17/08/02 17:00:16 INFO DAGScheduler: Job 1 finished: count at SimpleApp.java:33, took 0.139033 s
      17/08/02 17:00:16 INFO TaskSetManager: Finished task 0.0 in stage 1.0 (TID 1) in 99 ms on localhost (1/1)
      17/08/02 17:00:16 INFO TaskSchedulerImpl: Removed TaskSet 1.0, whose tasks have all completed, from pool

      Lines with a: 11146, lines with b: 10760

      17/08/02 17:00:16 INFO SparkContext: Invoking stop() from shutdown hook
      17/08/02 17:00:16 INFO SparkUI: Stopped Spark web UI at http://169.254.90.236:4040
      17/08/02 17:00:16 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped!
      17/08/02 17:00:16 INFO MemoryStore: MemoryStore cleared
      17/08/02 17:00:16 INFO BlockManager: BlockManager stopped
      17/08/02 17:00:16 INFO BlockManagerMaster: BlockManagerMaster stopped
      17/08/02 17:00:16 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped!
      17/08/02 17:00:16 INFO SparkContext: Successfully stopped SparkContext
      17/08/02 17:00:16 INFO ShutdownHookManager: Shutdown hook called
      17/08/02 17:00:16 INFO ShutdownHookManager: Deleting directory C:\Users\Administrator\AppData\Local\Temp\spark-c40f2015-170f-4633-89dd-44dcd5bacfec

      Process finished with exit code 0
    ]]>
    + + spark + + + java + spark + +
    + + 百度地图 API - 地址 转 经纬度 + /20220528-baidu-map-api-address-to-longitude-and-latitude/ + 0. 背景

    某做科研的朋友
    需要对一些地点的坐标
    然后在 WGS-84 坐标系的底图上呈现相关内容

    BD09 vs WGS84
    北京韩美林艺术馆,BD09:116.68347847243588,39.88148624000483
    北京韩美林艺术馆,WGS84:116.67097966259838,39.87446583754102

    1. 准备

    1.1 寻找成品

    找了几个网址,反馈说之前用过坐标不准确

    1.2 使用百度地图 API

    1.2.1 注册百度地图开放平台

    服务免费
    需要实名认证
    网址:lbsyun.baidu.com

    1.2.2 创建应用

    我没有选择 IP 白名单
    选择的是 SN 签名
    获取 AK、SK

    1.2.3 文档

    接口文档
    之前 V2 版本的接口已经停用了
    地址:地理编码文档
    SN 生成
    注意文档的生成方式还是 V2 的例子
    地址:SN 生成文档

    2. 代码

    简单说明
    地址文件格式一行一个
    转换后会输出文件:地址,精度,纬度

    CoordinateTransformUtil 在参考文档

    /**
    * Baidu.com Inc.
    * Copyright (c) 2022 All Rights Reserved.
    */
    package com.hisen.api.baidu.map;

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.google.common.base.Charsets;
    import com.google.common.io.Files;
    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.util.EntityUtils;
    import org.joda.time.DateTime;
    import org.springframework.util.DigestUtils;

    import java.io.File;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;

    /**
    * @author hisenyuan
    * @date 2022/5/28 10:55
    */
    public class SnCal {
    private static final String URL = "https://api.map.baidu.com";
    private static final String PATH = "/geocoding/v3/?";
    /**
    * 百度地图 AK
    */
    private static final String AK = "your ak";
    /**
    * 百度地图 SK
    */
    private static final String SK = "your sk";
    private static final String LINE_BREAK = "\n";
    private static final String FORMAT = "yyyyMMdd_HH_mm_ss_SSS";

    public static void main(String[] args) throws Exception {
    long start = System.currentTimeMillis();
    // 需要转换的文件,一行一个
    String file = "/Users/hisenyuan/Downloads/address.txt";
    // 结果输出文件夹
    String resDir = "/Users/hisenyuan/Downloads/";
    File resFile = getResFile(resDir);
    // 读取文件
    List<String> addressList = getAddressList(file);
    for (int i = 0; i < addressList.size(); i++) {
    convertLocation(resFile, addressList, i);
    System.out.println("current line:" + (i + 1));
    }
    long use = System.currentTimeMillis() - start;
    // 打印耗时
    System.out.println("time us(ms):" + use);
    }

    private static void convertLocation(File resFile, List<String> addressList, int index) throws IOException {
    String address = addressList.get(index);
    String reqUrl = getReqUrl(address);
    // 返回结果
    String res = sendUrlGet(reqUrl);
    double[] doubles = getWgs84(res);
    String wgsRes = address + "," + doubles[0] + "," + doubles[1];
    Files.append(wgsRes + LINE_BREAK, resFile, Charsets.UTF_8);
    }

    private static List<String> getAddressList(String file) throws IOException {
    List<String> addressList = Files.readLines(new File(file), StandardCharsets.UTF_8);
    System.out.println("sum line:" + addressList.size());
    return addressList;
    }

    private static File getResFile(String resDir) throws IOException {
    DateTime dateTime = new DateTime();
    File resFile = new File(resDir + "处理结果_" + dateTime.toString(FORMAT) + ".txt");
    if (!resFile.exists()) {
    boolean newFile = resFile.createNewFile();
    System.out.println("create file, result:" + (newFile ? "success" : "fail"));
    }
    return resFile;
    }

    private static double[] getWgs84(String res) {
    JSONObject jsonObject = JSON.parseObject(res);
    JSONObject result = jsonObject.getJSONObject("result");
    JSONObject location = result.getJSONObject("location");
    Double lng = location.getDouble("lng");
    Double lat = location.getDouble("lat");
    return CoordinateTransformUtil.bd09towgs84(lng, lat);
    }

    private static String getReqUrl(String address) throws UnsupportedEncodingException {
    Map<String, String> paramsMap = new LinkedHashMap<>();
    paramsMap.put("address", address);
    paramsMap.put("output", "json");
    paramsMap.put("ak", AK);
    // 参数值转为 UTF-8
    String paramsStr = toQueryString(paramsMap);
    // 参与 MD5 计算的参数
    String wholeStr = PATH + paramsStr + SK;
    // UTF-8
    String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
    // Md5 摘要( SN 计算)
    String md5 = md5(tempStr);
    // 请求地址
    return URL + PATH + paramsStr + "&sn=" + md5;
    }

    /**
    * 值 UTF-8
    */
    public static String toQueryString(Map<?, ?> data)
    throws UnsupportedEncodingException {
    StringBuilder queryString = new StringBuilder();
    for (Map.Entry<?, ?> pair : data.entrySet()) {
    queryString.append(pair.getKey()).append("=");
    queryString
    .append(URLEncoder.encode((String) pair.getValue(), StandardCharsets.UTF_8.name()))
    .append("&");
    }
    if (queryString.length() > 0) {
    queryString.deleteCharAt(queryString.length() - 1);
    }
    return queryString.toString();
    }

    /**
    * md5 摘要
    */
    public static String md5(String str) {
    return DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8));
    }

    /**
    * 发起 http 请求
    *
    * @param url 请求地址
    * @return 返回数据
    */
    public static String sendUrlGet(String url) {
    String responseContent = null;
    try {
    CloseableHttpClient client = HttpClientBuilder.create().build();
    HttpGet httpGet = new HttpGet(url);
    CloseableHttpResponse response = client.execute(httpGet);
    HttpEntity resEntity = response.getEntity();
    responseContent = EntityUtils.toString(resEntity, StandardCharsets.UTF_8);
    // close resources
    response.close();
    client.close();
    } catch (Exception e) {
    System.out.println(e.getMessage());
    }
    return responseContent;
    }
    }

    3. 参考

    注意,第一个文档的接口已经过时,新申请的用户无法使用

    ]]>
    + + java + + + java + +
    + + 北京联通-光猫改桥接-并开启IPv6 + /20220604-bj-unicom-optical-modem-change-to-bridge-and-set-ipv6/ + 0. 背景

    现有的长城 100M 宽带太寒酸了
    之前的住户说想升级光纤都没办法
    于是乎咨询了下联通可否装光纤
    答案是可以,换个套餐就行

    桥接的好处就是,光猫只做光猫该做的事情。
    其它事情由自己的路由器进行设置并且管理。
    这样即使你搬家什么的,路由器搬走,
    任何设备都不用动,包括固定内网 IP 等
    我主要的点是固定内网 IP,因为我由 NAS

    1. 操作

    1.1 改为桥接

    这一步是需要联系安装宽带的师傅
    他们远程可以直接修改
    如果改为桥接,那么原有网络无法使用

    重点

    • 联系师傅索要宽带账号密码
    • 要安装师傅修改光猫为桥接
    • 修改之后进光猫后台较麻烦(不过用不到,都在路由器操作了)

    1.2 路由器拨号

    我的路由器是网件 R8000,输入账号密码等配置生效。
    完事之后就可以上午,但是,这时候 IPv6 是不好使的。

    1.3 IPv6

    找到 IPv6 相关设置(高级—高级设置—IPv6)
    选择 PPPoE,使用 IPv4 一样的账号密码拨号
    等待配置生效,即可发现 IPv6 正常
    IPv6 测试网址:test-ipv6.com

    至于为什么要 IPv6,那只能说懂得都懂,啊哈哈。

    2. 参考

    桥接上网
    这篇文章的一些观点我可能不太认同,
    最重要的是,里面提到可以联系客服修改为桥接
    这就很方便了,网上很多都是各种破解,挺麻烦。

    电信光猫用桥接模式好还是路由模式好?

    ]]>
    + + 其它 + + + IPv6 + ip + +
    + + dubbo与zookeeper的关系 + /20171115-dubbo%E4%B8%8Ezookeeper%E7%9A%84%E5%85%B3%E7%B3%BB/ + Zookeeper的作用

    zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。

    当然也可以 通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。

    zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务对应关系从列表中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码 的情况通过添加机器来提高运算能力。

    通过添加新的机器向zookeeper注册服务,服务的提供者多了能服务的客户就多了。

    dubbo

    是管理中间层的工具,在业务层到数据仓库间有非常多服务的接入和服务提供者需要调度,dubbo提供一个框架解决这个问题。

    注意这里的dubbo只是一个框架,至于你架子上放什么是完全取决于你的,就像一个汽车骨架,你需要配你的轮子引擎。

    这个框架中要完成调度必须要有一个分布式的注册中心,储存所有服务的元数据,你可以用zk,也可以用别的,只是大家都用zk。

    zookeeper和dubbo的关系

    Dubbo的将注册中心进行抽象,是得它可以外接不同的存储媒介给注册中心提供服务,有ZooKeeper,Memcached,Redis等。

    引入了ZooKeeper作为存储媒介,也就把ZooKeeper的特性引进来。

    1. 负载均衡:单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;
    2. 资源同步:单单有负载均衡还不 够,节点之间的数据和资源需要同步,ZooKeeper集群就天然具备有这样的功能;
      1. 命名服务:将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布
      2. Mast选举,分布式锁等。

    参考

    https://www.cnblogs.com/xiaofei1208/p/7077733.html

    ]]>
    + + java + + + java + +
    + + 通过 OpenWrt 利用 Cloudflare tunnel 回家 - 连上家庭内网 + /20221114-go_home_by_cloudflare_via_openwrt/ + 1. 写在前面

    需要注册 Cloudflare 账号
    这个服务目前是可以免费使用

    速度的话一般,勉强可以接受。
    后续再折腾看看是什么问题。

    2. 主要操作步骤

    # 安装客户端
    opkg install cloudflared
    # 登录, 返回一个 cloudflare 官方连接,点击会生成密钥
    cloudflared tunnel login
    # 创建一条隧道,<NAME> 替换成你喜欢的名字
    cloudflared tunnel create <NAME>
    # 创建一个域名解析
    cloudflared tunnel route dns hisen home.hisenyuan.xyz
    # 配置 xxx.yml
    url: http://192.168.0.10 # 内网的网关,注意做好鉴权
    tunnel: d42a48c9-26da-4734-b249-1b9eee69ba8c
    credentials-file: /root/.cloudflared/d42a48c9-26da-4734-b249-1b9eee69ba8c.json
    # 启动一个内网穿透服务
    cloudflared tunnel --config xxx.yml run

    3. 参考文档

    ]]>
    + + homelab + + + OpenWrt + +
    + + 开源画图工具(各种图):draw.io 支持 Web、macOS、Windows、Linux + /20200406-draw.io-open-source-drawing-tools-support-web-mac-windows-linux/ + draw.io 一个很棒的开源画图工具,可以导出 xml、pdf、html、png 等各种格式
    其实重点是开源,目前好几个地方看到有在用,之前用网页版,现在用桌面版多些

    网页版:draw.io
    桌面版:github 下载支持:Windows、macOS、Linux、Google Chrome OS

    ]]>
    + + soft + + + soft + +
    + + github api - github api中文说明 + /20170428-github%20api%20-%20github%20api%E4%B8%AD%E6%96%87%E8%AF%B4%E6%98%8E/ + github api的网址

    https://api.github.com/

    这里介绍两个api

    #获取个人信息
    https://api.github.com/users/{user};

    key含义value
    login登录名称hisen-yuan
    id数字编号16789019
    avatar_url头像地址https://avatars1.githubusercontent.com/u/16789019?v=3
    name用户昵称hisenyuan
    blog博客地址http://hisen.me
    location地理位置China
    bio个人说明Java R & D
    public_repos仓库个数11
    created_at创建时间2016-01-20 01:57:15Z
    updated_at最后更新2017-04-20 14:03:27Z
    #获取项目信息
    https://api.github.com/users/{user}/repos
    key含义value
    id项目编号88646378
    name项目名称dubbo
    html_url项目地址https://github.com/hisen-yuan/dubbo
    created_at创建时间2017-04-18T16:21:57Z
    updated_at更新时间2017-04-18T16:23:16Z
    pushed_at提交时间2017-04-19T02:33:33Z
    size项目大小6514
    language编程语言Java

    github提供的所有api

    current_user_url: "https://api.github.com/user",
    current_user_authorizations_html_url: "https://github.com/settings/connections/applications{/client_id}",
    authorizations_url: "https://api.github.com/authorizations",
    code_search_url: "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
    commit_search_url: "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
    emails_url: "https://api.github.com/user/emails",
    emojis_url: "https://api.github.com/emojis",
    events_url: "https://api.github.com/events",
    feeds_url: "https://api.github.com/feeds",
    followers_url: "https://api.github.com/user/followers",
    following_url: "https://api.github.com/user/following{/target}",
    gists_url: "https://api.github.com/gists{/gist_id}",
    hub_url: "https://api.github.com/hub",
    issue_search_url: "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
    issues_url: "https://api.github.com/issues",
    keys_url: "https://api.github.com/user/keys",
    notifications_url: "https://api.github.com/notifications",
    organization_repositories_url: "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
    organization_url: "https://api.github.com/orgs/{org}",
    public_gists_url: "https://api.github.com/gists/public",
    rate_limit_url: "https://api.github.com/rate_limit",
    repository_url: "https://api.github.com/repos/{owner}/{repo}",
    repository_search_url: "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
    current_user_repositories_url: "https://api.github.com/user/repos{?type,page,per_page,sort}",
    starred_url: "https://api.github.com/user/starred{/owner}{/repo}",
    starred_gists_url: "https://api.github.com/gists/starred",
    team_url: "https://api.github.com/teams",
    user_url: "https://api.github.com/users/{user}",
    user_organizations_url: "https://api.github.com/user/orgs",
    user_repositories_url: "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
    user_search_url: "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"

    ]]>
    +
    + + eclipse无法链接github + /20170208-eclipse%E6%97%A0%E6%B3%95%E9%93%BE%E6%8E%A5github/ + 浏览器什么的都能打开github.com
    就是eclipse无法提交到github,每次都是连接超时
    然后就直接修改host了,目前有效
    2017年1月14日 18:01:34

    host位置:

    C:\Windows\System32\drivers\etc

    host文件最后一行加上下面内容即可

    192.30.253.112       github.com

    ]]>
    + + eclipse + github + +
    + + dubbo transport Data length too large-14277263-max payload-8388608 channel-NettyChannel + /20190307-dubbo%20transport%20Data%20length%20too%20large-14277263-max%20payload-8388608%20channel-NettyChannel/ + 一、报错信息
    com.alibaba.dubbo.remoting.transport.ExceedPayloadLimitException: Data length too large: 14277263, max payload: 8388608, channel: NettyChannel [channel=[id: 0x12a13c8f, /172.0.0.1:49402 => /172.0.0.2:23888]]

    二、报错原因

    dubbo默认使用Netty传输协议
    并且默认的大小限制为:默认为8M,即8388608

    三、解决办法

    1. 修改接口
      出现这种情况是因为一个接口查某个表的所有数据(几万条)
      一般这种接口肯定是需要分页的

    2. 更改配置信息
      在dubbo.properties 中增加如下

      dubbo.protocol.dubbo.payload=11557050
    ]]>
    + + java + + + java + netty + dubbo + +
    + + hexo安装过程 + /20170120-hexo%E5%AE%89%E8%A3%85%E8%BF%87%E7%A8%8B/ + 准备工作
    1. Node.js:点击下载
    2. git:点击下载
    3. MarkdownPad:点击下载

    安装好上面三个工具
    可能会遇到的问题:

    1、Git Bash执行node -v提示无效 或者 npm install 报 command not found

    解决办法:在环境变量 - 用户变量中 - 新建用户变量 - 添加nodejs安装路径

    如:C:\tool\nodejs

    2、ERROR Deployer not found : github

    解决办法:

    1. 配置文件有问题,冒号后面都有一个空格的
    2. 执行:npm install hexo-deployer-git –save (这命令是为了解决hexo新版本的部署问题)

    3使用淘宝镜像加快安装速度
    安装cnpm,使用命令:

    npm install cnpm -g --registry=https://registry.npm.taobao.org

    安装过程

    1. 打开Git Bash
    2. 进入nodejs安装目录
    3. 开始安装hexo,输入下面代码
    4. npm install -g hexo#等待安装完成,这个过程可能会快也可能很慢,耐心等待
    5. mkdir blog && cd blog #上面这个代码是创建一个博客存放的目录
    6. hexo init#初始化
    7. cnpm install #安装依赖包
    8. 完成之后,本地博客就搭建完成
    9. hexo g #生成静态页面
    10. hexo s #启动服务器,打开http://localhost:4000 就是本地博客

    本地博客安装完成,下面介绍发布到github上

    1. 登陆github,没有就注册
    2. 点击右上角加号+
    3. Create a new repository
    4. 名字写:yourgithubname.github.io
    5. 创建完成
    6. 点击Setting
    7. 选择一个主题,然后就好了
    8. 编辑blog文件夹里面的_config.yml配置文件
    9. 最后面添加
      deploy:
      type: git
      repository: http://github.com/yourname/yourname.github.io.git
      branch: master

    最后执行

    1. hexo g#重新生成静态博客
    2. hexo d#将本地静态博客部署到github

    现在你在浏览器打开:http://yourname.github.io就可以访问你的博客了
    到此为止就搭建完了一个博客

    开始写第一篇文章:
    执行:hexo new “你的文章标题”
    然后你在blog/source/_posts文件夹下面有文件,用markdownpad打开编辑
    执行:

    1. hexo g#重新生成
    2. hexo s#本地查看效果
    3. hexo d#上传到github
    4. 或者不预览,直接一步上传到github:hexo d -g
    ]]>
    + + hexo + github + +
    + + git版本差异报告 - git difftool分支对比 - gitLab compare + /20190309-git%E7%89%88%E6%9C%AC%E5%B7%AE%E5%BC%82%E6%8A%A5%E5%91%8A%20-%20git%20difftool%E5%88%86%E6%94%AF%E5%AF%B9%E6%AF%94%20-%20gitLab%20compare/ + 一、初衷

    有时候上线会出现合错代码,比如功能,或者maven的pom文件;

    人都不是十全十美的人,只要是人就会犯错
    关键是要想办法去避免,只有工具才不犯错

    所以尽量想办法利用工具来防止我们犯错,git来说有很多办法;
    简单的命令,或者gitlab的compare报告;
    每次上线之前,开发自己看一遍当前版本与线上版本的区别,确认每一个点都是正确的改动;

    二、gitlab compare功能(推荐)

    2.1 入口:工程首页 -> Repository -> Compare
    2.2 选择:上一个版本,当前版本,点击Compare
    2.2 结果:一次能看到两个版本的所有commit、改动点

    三、git命令

    # 显示版本之间改动的文件名
    git difftool master 1.2.4 --stat

    # 在命令行挨个显示版本某个文件具体差异
    git difftool master 1.2.4
    ]]>
    + + java + + + java + gitLab + git + +
    + + hexo新建文章时候默认带上categories,tags + /20170209-hexo%E6%96%B0%E5%BB%BA%E6%96%87%E7%AB%A0%E6%97%B6%E5%80%99%E9%BB%98%E8%AE%A4%E5%B8%A6%E4%B8%8Acategories%EF%BC%8Ctags/ + 在博客的 scaffolds 文件夹里有个post.md 添加上需要的配置就行

    这里是创建post的模板。

    我的默认设置成这样:

    ---
    title: {{ title }}
    date: {{ date }}
    tags: []
    categories:
    ---

    tags: [关键词1,关键词2]

    ]]>
    + + hexo + + + hexo + +
    + + 春节期间的收获 | 记第一次在外过年 + /20210218-harvest-during-chinese-spring-festival/ + 零、摘要

    响应国家就地过年的号召,今年第一次在外过年。
    弹指一挥间,12 天的假期已经成为过去。
    期间还是有不少的收获,最主要的是看了 4 本书。
    以及在微博上面看到了不少的人和事,甚是触动。

    找到自己的兴趣,追求精进,坚持做对的事情。

    一、阅读

    1.1 《 UML 和模式应用》0113~0207

    这本书是 leader 推荐给大家的
    基于职责去做设计的理念确实很棒,值得观摩实践。

    1.2 《段永平投资问答录(商业逻辑篇)》0120~0214

    春节之前断断续续看了一部分,后面主要是春节在看。
    关键是:要做对的事情,把事情做对。(前者更重要)
    创始人对公司的文化影响很大,文化又对公司影响大。

    有自己清晰的不为清单,发现错了立马纠正,因为这时候止损成本最低。

    段永平的投资问答录其实很多时候也适合于人生建议,都是相通的。
    很幸运有如此成功的人愿意与普通人进行交流,传授经验。

    书本当中也能接触到不少成功人士,正所谓近朱者赤,
    还有所谓的圈子,多看多想多学,总是会进步的。

    1.3 《代码精进之路:从码农到工匠》0216~0217

    实践出真知,作者成就非凡
    其中的很多理念深度赞同
    是一本值得翻阅的书,收益颇多

    eg:

    1. 写好代码的技艺不是一蹴而就的,它是一个系统化的工程,不是看几本书、写几年代码就能轻松习得的,而需要我们对自己的思维习惯、学习方法和工程实践进行彻底的反省和重构。
    2. 在计算机科学中有两件难事:缓存失效和命名。
    3. 通常,如果你无法想出一个合适的名字,很可能意味着代码“坏味道”、设计有问题。
    4. 写好代码、追求卓越和工匠精神是每个程序员都应该具备的优秀品质。
    5. 管理者的一个很重要的使命就是帮助团队成长,包括制定规范和技术传承。
    6. 没有抽象思维,就没有人类今天灿烂的文明。
    7. 分治和抽象一样,都是人类进化过程中形成的伟大智慧,也是我们解决复杂问题的不二选择。
    8. 在学习之前,我们一定要问自己,这次学习的目标是什么?
    9. 技术 leader 要以德服人+以技服人

    1.4 《硅谷钢铁侠:埃隆·马斯克的冒险人生》0217~0218

    看完对马斯克以及他的企业和野心有更多的了解
    羡慕他精力旺盛、超强的学习能力、以及追求梦想的执着

    二、微博

    触动最大的就是@纯银V 一位产品经理的微博
    特别是关于一个 95 后实习生的故事
    也很佩服纯银从一个法警做到如此高阶的产品经理
    羡慕他把看产品的思路套用在看公司上,然后 2020 投资中概股收益颇丰

    感悟就是:积极、主动、为自己工作,找到喜欢的事情,持续精进。

    三、生活

    和家里亲戚去郊区玩了几天,还是比较放松。
    有时候不管是家人之间的沟通,还是朋友之间,可能更多的时候需要的是倾听,而不是讲道理。
    犹如《代码精进之路》里讲的”不过在家里,我依然是输多赢少,后来我才发现,原来家不是一个讲逻辑的地方。”

    安排好自己的时间,加强执行力!

    ]]>
    + + 随说 + + + 随说 + +
    + + hexo百度抓取失败解决办法 + /20170216-hexo%E7%99%BE%E5%BA%A6%E6%8A%93%E5%8F%96%E5%A4%B1%E8%B4%A5%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/ + 上一篇帖子说明了一下百度抓取不到的原因是因为github把百度爬虫给屏蔽了

    这里给出的解决办法是用hexo自动提交插件

    需要获取一个自动提交的token

    1. 注册百度站长工具:http://zhanzhang.baidu.com
    2. 添加你的hexo域名
    3. 找到网页抓取 - 链接提交 - 下拉选择你的hexo站点
    4. 数据提交方式 - 自动提交 - 主动推送(实时)
    5. 推送接口
    6. 接口调用地址: http://data.zz.baidu.com/urls?site=hisen.me&token=XmxXyESxyz1hANxE
    7. 复制上面token=后面的内容,那就是你的token

    安装自动提交插件:

    1. npm install hexo-baidu-url-submit –save
    2. 编辑站点配置文件_config.yml,添加一下内容
      baidu_url_submit:
      count: 1 ## 提交最新的一个链接
      host: www.hui-wang.info ## 在百度站长平台中注册的域名
      token: your_token ## 请注意这是您的秘钥, 所以请不要把博客源代码发布在公众仓库里!
      path: baidu_urls.txt ## 文本文档的地址, 新链接会保存在此文本文档里

    加入新的deployer: 原来type前面是没有 - 的

    但是不这样处理执行hexo g 会报错

    deploy:
    - type: git
    repository: yoururl
    branch: master
    - type: baidu_url_submitter

    生成效果如下:

    $ hexo g
    INFO Start processing
    INFO Generating Baidu urls for last 1 posts
    INFO Posts urls generated in baidu_urls.txt
    http://hisen.me/20170216-hexo百度抓取失败解决办法/
    INFO Files loaded in 1.31 s

    $ hexo d
    INFO Deploy done: git
    INFO Deploying: baidu_url_submitter
    INFO Submitting urls
    http://hisen.me/20170216-hexo百度抓取失败解决办法/
    {"remain":1,"success":1}
    INFO Deploy done: baidu_url_submitter

    感谢插件作者:王辉的博客

    ]]>
    + + 软件 + + + hexo + +
    + + centos更换yum源为阿里云 - 三步搞定 + /20170217-centos%E6%9B%B4%E6%8D%A2yum%E6%BA%90%E4%B8%BA%E9%98%BF%E9%87%8C%E4%BA%91-%E4%B8%89%E9%83%A8%E6%90%9E%E5%AE%9A/ + 阿里云是最近新出的一个镜像源。得益于阿里云的高速发展,这么大的需求,肯定会推出自己的镜像源。
    阿里云Linux安装镜像源地址:http://mirrors.aliyun.com/

    CentOS系统更换软件安装源

    第一步:备份你的原镜像文件,以免出错后可以恢复。

    1、备份
    mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

    2、下载新的CentOS-Base.repo 到/etc/yum.repos.d/

    (如果无wget命令,底部有具体说明)

    CentOS 5

    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-5.repo

    CentOS 6

    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo

    CentOS 7

    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

    3、之后运行yum makecache生成缓存


    ps:如果你跟我一样苦逼:

    -bash: wget: command not found

    然后:

    yum -y install wget #失败

    那么你可以直接选择上面对应系统的文件下载链接

    下载好文件之后改名为CentOS-Base.repo

    直接放到/etc/yum.repos.d/目录下即可

    ]]>
    + + linux + + + linux + centos + +
    + + go程序设计语言练习 - 同统计重复 - gop1.io/ch1/dup2/dup2.go + /20181027-go%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E8%AF%AD%E8%A8%80%E7%BB%83%E4%B9%A0%20-%20%E5%90%8C%E7%BB%9F%E8%AE%A1%E9%87%8D%E5%A4%8D/ + 一直在写java,看语法逻辑什么的没有问题

    但是刚刚看书上写出来的这个程序,居然不知道怎么在goland里面运行…

    于是曲线救国,了解到打印输入的参数。

    这断代码就是为了找出输入数据中的重复行

    1. 直接启动,不带参数,启动之后输入参数

      参数0: /private/var/folders/lk/p8gbq87n6rvfndk7wlk23y8r0000gn/T/___go_build_dup2_go
      1 # 一行输入一个
      1 # 一行输入一个
      ^D #这是command + D
      2 1 # 输出的结果:个数 输入值
    2. 在命令行启动,带

      $ go run dup2.go 'hisen.txt'
      参数0: /var/folders/lk/p8gbq87n6rvfndk7wlk23y8r0000gn/T/go-build525680776/b001/exe/dup2
      参数1: hisen.txt #代码同级目录的文件名
      2 hisen
      2 hisenyuan
      2 123

    代码如下:

    package main

    import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    )

    /**
    本程序只能在终端运行:go run dup2.go 'hisen.txt'
    hisen.txt 为本文件同级目录的文件
    */
    func main() {
    // 遍历输出的参数
    for idx, args := range os.Args {
    fmt.Println("参数"+strconv.Itoa(idx)+":", args)
    }
    // map[string,int]
    counts := make(map[string]int)
    // 拿到文件名,从第二个参数开始
    files := os.Args[1:]
    if len(files) == 0 {
    countLines(os.Stdin, counts)
    } else {
    // 遍历文件
    for _, arg := range files {
    // 读取文件内容
    f, err := os.Open(arg)
    if err != nil {
    fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
    continue
    }
    countLines(f, counts)
    f.Close()
    }
    }

    // 遍历map
    for line, n := range counts {
    if n > 1 {
    fmt.Printf("%d\t%s\n", n, line)
    }
    }
    }

    // 把数据装入map
    func countLines(file *os.File, counts map[string]int) {
    // 从文件中加载数据
    input := bufio.NewScanner(file)
    // 遍历文件内容
    for input.Scan() {
    counts[input.Text()]++
    }
    }

    ]]>
    + + go + + + go + +
    + + IDEA failed to create jvm:error code -1 + /20170303-idea%20failed%20to%20create%20jvm%20error%20code%20-1/ + 今天先更改了 idea64.exe.vmoptions 这个配置文件

    一直么有重启,后来就安装了个插件重启一下,结果就泪崩了

    一直出现这个错误
    IDEA failed to create jvm:error code -1,jvm:error code -1
    总以为是环境变量配置的问题,或者是文件损坏了什么

    重启,重装jdk,重新配置什么都试过,不管用。

    后来替换了配置文件就好了!!!

    解决方案

    配置文件路径:

    \IDEA HOME\bin\idea64.exe.vmoptions
    或者
    \IDEA HOME\bin\idea.exe.vmoptions

    默认配置文件内容如下:

    32bit

    -server
    -Xms128m
    -Xmx512m
    -XX:ReservedCodeCacheSize=240m
    -XX:+UseConcMarkSweepGC
    -XX:SoftRefLRUPolicyMSPerMB=50
    -ea
    -Dsun.io.useCanonCaches=false
    -Djava.net.preferIPv4Stack=true
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:-OmitStackTraceInFastThrow

    64bit

    -Xms128m
    -Xmx750m
    -XX:ReservedCodeCacheSize=240m
    -XX:+UseConcMarkSweepGC
    -XX:SoftRefLRUPolicyMSPerMB=50
    -ea
    -Dsun.io.useCanonCaches=false
    -Djava.net.preferIPv4Stack=true
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:-OmitStackTraceInFastThrow

    by the way:

    IDEA 写博客真是舒服啊~

    完全不用切换来切换去的!

    ]]>
    + + java + + + java + +
    + + iTerm2自动连接远程服务器 + /20180720-iTerm2%E8%87%AA%E5%8A%A8%E8%BF%9E%E6%8E%A5%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8/ + mac自带的终端总感觉不大好用,于是乎就被安利了iTerm2

    然后就搜了下自动连接远程服务,于是就发现了一个不错的脚步

    整个设置过程还是比较顺畅,

    操作步骤:

    1. 新建一个sh文件

      vi auto_ssh.sh
    2. 输入如下内容,[lindex $argv 0] 这个为第一个参数的占位符

      #!/usr/bin/expect

      set timeout 30
      spawn ssh -p[lindex $argv 0] [lindex $argv 1]@[lindex $argv 2]
      expect {
      "(yes/no)?"
      {send "yes\n";exp_continue}
      "password:"
      {send "[lindex $argv 3]\n"}
      }
      interact
    3. 复制脚本到bin下并且赋予执行权限

      sudo cp auto_ssh.sh /usr/local/bin/
      cd /usr/local/bin/
      sudo chmod +x auto_ssh.sh
    4. 设置
      在iTerm2中Command+o呼出profile

      # 界面右下角,点击:
      edit profiles
      # 界面左下角,点击:
      +
      # 界面上方,点击:
      General
      # 界面靠上,写这个登录的名字:
      name
      # 界面中间,点击选择:
      Login shell
      # 找到这个提示符:Send text at start,输入如下字符
      auto_ssh.sh 8022 root 10.10.20.20 hisenyuan
      # 脚本名称 端口 用户名 ip地址 密码
      1. 运行
        设置完了之后,关闭窗口,重新command+o呼出配置文件,双击刚刚配置的即可自动登录
    ]]>
    + + linux + +
    + + 如何做一个让人讨厌的产品经理 - 《人人都是产品经理 2.0》读后感 + /20210130-how-to-be-a-disgusting-product-manager/ + 零、背景

    本文灵感来自《人人都是产品经理 2.0》
    位置:7.4.2 如何做一个让 Ta 们讨厌的人

    作为一个研发,工作过程中如果能及时发现如下场景,
    及时给对方负反馈,否则受伤的是整个团队。
    看了这本书之后,感觉对产品有新的认知,
    知道他们在做什么,怎么做,后续可以更好的与他们沟通。
    而且里面的内容对于研发来讲也是适用的。

    一、开始实施之前

    1.1 不说清需求价值

    技术问”为什么要做”时:
    1、时支支吾吾
    2、这是老板(XXX)要的,假装自己是个传话筒
    3、我接的是二手需求,什么都不知道
    随说:其实正确的做法是追溯这个需求的初衷,有利于评估 ROI (投入产出比),以及排优先级,以及增进对业务的理解。

    1.2 不去想细节功能

    技术问细节(业务细节,非技术细节)时:
    1、装作自己完全没有想过
    2、那就这样做
    3、肯能那样做也可以
    4、要不你来定吧
    随说:这些表现其实都是不负责的!没有明确的需要,后期容易扯皮,也可能做出来的东西有问题。

    1.3 帮技术评估工作量

    1、这不是很简单嘛,就改个 XXX,几行代码就搞定
    2、这些我都评估过了,都能做
    3、不要偷懒,不要忽悠我,我抖动
    随说:评估真是个高难度的活,往往只见树木不见森林

    1.4 逼着技术团队承诺

    1、任何时候只知道公事公办,技术承诺了却做不到,自己就没有责任了。
    随说:互联网企业更多的只是一个”预测”,而非”承诺”,因为变化太快,承诺是技术对产品的责任,而预测是产研一起担责。

    二、实施过程中

    2.1 做了一半改需求

    1、经常在某个迭代周期内做”非受迫的需求变更”,这招杀伤力很大,技术同学一般受不了。
    随说:非受迫,意味着是产品没有想清楚,然后导致研发无谓劳动。万万不可取。

    2.2 开发过程中消失

    1、可以想各种办法隔绝与研发的联系。
    随说:一般都需要多次沟通,反复确认细节。

    2.3 过度关注细节

    1、帮技术确定各种技术方案,这个应该这样实现…
    随说:这种一般少见,研发转岗产品的多一些

    三、产品发布之后

    3.1 没有发布后的反馈

    1、发布之后,犹如石沉大海,不告诉大家任何结果,甚至庆功宴都不叫 Ta 们,紧接着继续安排他们干活。
    随说:技术人员也需要从市场、用户那里获得反馈,从而对自己做的事情产生价值感和成就感。

    3.2 任务无节奏感

    1、让研发一阵忙一阵闲,发布之后再开始研究接下来做什么。
    2、一会儿让研发有着天天通宵的高强度,给 deadline,下死命令,做完之后不知道做什么。
    随说:保持合理的安排,让研发有个合理的预期比较好。而不是等你们上了大学就自由了这种…

    四、全程适用

    4.1 优柔挂断

    1、你定吧,你说往哪儿走我们照办
    2、啊…那个…方案各有利弊,我也不知道怎么办,你们有什么好想法
    随说:自己左手和右手互搏,思考清楚之后给大家一个明确的结果。

    4.2 报喜不报忧

    1、藏着噎着一些坏消息,eg:老板正考虑干掉这个项目等,大家只能通过小道消息得知
    随说:这样很破坏信任感

    4.3 不把 Ta 们当人看

    1、只关注结果,不关注人的成长,永远把合作伙伴当”资源”
    随说:结果导向没有问题,但是没有成长,留不住人。

    ]]>
    + + 随说 + + + 随说 + +
    + + html - 原生javascript动态显示当前时间和日期 + /20170412-html%20-%20%E5%8E%9F%E7%94%9Fjavascript%E5%8A%A8%E6%80%81%E6%98%BE%E7%A4%BA%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%E5%92%8C%E6%97%A5%E6%9C%9F/ + 显示当前时间:

    2017年04月12日 18:26:37 星期三

    示例如下:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>显示当前时间 - 原生javascript</title>
    <script type="text/javascript" language="javascript">
    function show_cur_times(){
    //获取当前日期
    var date_time = new Date();
    //定义星期
    var week;
    //switch判断
    switch (date_time.getDay()){
    case 1: week="星期一"; break;
    case 2: week="星期二"; break;
    case 3: week="星期三"; break;
    case 4: week="星期四"; break;
    case 5: week="星期五"; break;
    case 6: week="星期六"; break;
    default:week="星期天"; break;
    }
    //年
    var year = date_time.getFullYear();
    //判断小于10,前面补0
    if(year<10){
    year="0"+year;
    }
    //月
    var month = date_time.getMonth()+1;
    //判断小于10,前面补0
    if(month<10){
    month="0"+month;
    }
    //日
    var day = date_time.getDate();
    //判断小于10,前面补0
    if(day<10){
    day="0"+day;
    }
    //时
    var hours =date_time.getHours();
    //判断小于10,前面补0
    if(hours<10){
    hours="0"+hours;
    }
    //分
    var minutes =date_time.getMinutes();
    //判断小于10,前面补0
    if(minutes<10){
    minutes="0"+minutes;
    }
    //秒
    var seconds=date_time.getSeconds();
    //判断小于10,前面补0
    if(seconds<10){
    seconds="0"+seconds;
    }
    //拼接年月日时分秒
    var date_str = year+"年"+month+"月"+day+"日 "+hours+":"+minutes+":"+seconds+" "+week;
    //显示在id为showtimes的容器里
    document.getElementById("showtimes").innerHTML= date_str;
    }
    //设置1秒调用一次show_cur_times函数
    setInterval("show_cur_times()",100);
    </script>
    </head>
    <body>
    显示当前时间:<p id="showtimes"></p>
    </body>
    </html>

    ]]>
    +
    + + idea启动tomcat服务失败 SEVERE [RMI TCP Connection(3)-127.0.0.1] + /20170215-idea%E5%90%AF%E5%8A%A8tomcat%E6%9C%8D%E5%8A%A1%E5%A4%B1%E8%B4%A5-SEVERE-RMI-TCP-Connection-3-127-0-0-1/ + 工程是从eclipse生成的,用idea开发。

    重复了一遍以往正常的不能再正常了的导入配置,结果遇到了如下问题:

    SEVERE [RMI TCP Connection(3)-127.0.0.1]

    15-Feb-2017 11:05:25.993 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.listenerStart Skipped installing application listeners due to previous error(s)

    删除导入项目中的web.xml文件

    因为idea要用的东西自己会自动生成

    然后就搞定了这个纠结我一天的问题

    我是百度搜索这个错误:RMI TCP Connection(3)-127.0.0.1

    得到的答案,感谢10100:查看原文

    ]]>
    + + java + + + idea + tomcat + +
    + + 水族入门 + /20220612-introduction-to-aquarium/ + 0. 概览

    0.1 简介

    入坑水族几年,和年纪无关…
    修生养性,培养一个兴趣爱好,毕竟生命在于折腾
    下班回家,看着鱼儿在水中游,喂喂乌龟,也蛮有趣

    最近几天在折腾过滤,
    进一步了解了一下过滤系统,
    也有朋友在问养鱼养龟方面的问题,
    于是就想着写一篇博客简单的记录一下。

    不懂的多百度,设备什么的购物平台搜搜。
    有兴趣可以逛逛:南美水族论坛、乌龟吧(百度贴吧),等水族相关内容。

    0.2 预防针

    需要有一定的金钱投入,更重要的是精力投入,长期维护。

    0.3 我的水族

    • 鱼缸
      • 动物:宝莲灯、孔雀鱼、苹果螺、杀手螺、黑壳虾
      • 植物:小水兰、珍珠草
      • 过滤:伊罕滤桶
      • 灯具:LED 水草灯
    • 乌龟缸
      • 动物:鳄鱼龟(互动性不错)、苹果螺、黑壳虾
      • 植物:石菖蒲、水竹、紫芋
      • 过滤:侧滤 + 沼泽 + 滴滤
      • 灯具:LED 水草灯

    1. 动物选择

    养宠物之前
    一定要先了解清楚
    自己喜欢什么,适合什么
    先了解学习一些经验,不要着急入手

    鳄鱼龟:互动性不错,有点危险,长的很快.
    宝莲灯:个人比较喜欢的热带灯科鱼,小型的.

    2. 植物选择

    鱼缸

    • 阴性草
      • 优点:简单好养,不需要水草灯、二氧化氮设备
      • 缺点:不太好看
    • 阳性草
      • 优点:好看
      • 缺点:需要一定的养护技巧,还有相关的设备

    龟缸
    主要考虑过滤使用
    选择根系强大的挺水植物

    3. 容器选择

    选择透明的玻璃容器,观赏性比较好
    鱼缸

    • 选择 5 面超白长方体鱼缸,尽量避免异形鱼缸
    • 尺寸(cm 长宽高):60x45x45、90x45x45、120x45x45、等等常规尺寸

    龟缸

    • 和鱼缸类似
    • 入门也可以考虑乌龟周转箱

    4. 过滤选择

    俗话说,养鱼先养水。
    要想养好水,就需要过滤器材。

    关于自来水
    开缸尽量不要用自来水,
    使用自来水最好是晾晒 2 天,
    水质稳定之后,换水的时候也可以直接注入少量自来水。

    4.1 建议

    使用过滤器

    • 上滤:安全、方便,不美观
    • 底滤:有点麻烦,美观
    • 侧滤:有点麻烦,不美观
    • 滤桶:安全,美观,不够强大

    尽量增大水体体积
    尽量增大水与滤材的接触面积(过滤效果好)

    • 装更多的滤材
    • 使用接触面积更大的滤材

    滤材表面积大利于细菌进行生长繁殖,可保持干净稳定的水。
    滤材选择:表面积越大越好,一般比较贵;便宜的可以以量取胜。

    4.2 背景知识

    异养细菌,把食物残渣、动物粪便等,转化成无机物,氨气(对动物有害)等。
    硝化细菌,把氨气转换为硝酸盐(对动物无害),可以供植物吸收。

    水循环大概过程:
    『有机物(残渣、粪便)』 一异养细菌一> 『氨(有害)等其它无害物质』 一硝化细菌一> 『硝酸』 一> 『植物吸收』

    4.2.1 详细解释

    硝化细菌:属于生产者,包括亚硝酸细菌(将氨氧化成亚硝酸),硝酸细菌(将亚硝酸氧化成硝酸),
    硝化细菌(英语:nitrifying bacteria)是一群好氧的化能自养生物之统称,属于生产者,
    细菌能通过食用无机氮化合物生长。硝化细菌以二氧化碳为碳源,通过代谢将氨或铵盐氧化成硝酸盐。

    自养生物(英语:autotroph)也称为生产者(producer)
    指食物链底端可以利用阳光(光合作用)或无机物氧化配以地热能(化能合成)
    将空气和周边环境中的二氧化碳、水以及无机盐等制造成有机物来提供代谢底物并存储化学能的生物,
    主要包括绿色植物、海藻和少数微生物(浮游植物)。

    异养生物(英语:heterotroph),也称为消费者( consumer)
    指不能直接利用无机物或有机物维生,必须摄取现成的养分来维持生存机能的生物。

    5. 参考

    ]]>
    + + 其它 + + + 水族 + +
    + + Java受检异常和非受检异常 - uncheckedException checkedException + /20190429-java%20-%20%E5%8F%97%E6%A3%80%E5%BC%82%E5%B8%B8%E5%92%8C%E9%9D%9E%E5%8F%97%E6%A3%80%E5%BC%82%E5%B8%B8%20-%20uncheckedException%20checkedException/ + 一、简单介绍

    除了runtimeException以外的异常,都属于checkedException。

    CheckedException(受检异常):编译器会检查这类异常,需要强制捕获,否则无法编译通过。
    UnCheckedException(非受检异常):编译器不会检查这类异常,可以不用捕获,可以编译通过。

    二、常见异常

    受检:IOException、SQLException、NumberFormatException、IllegalArgumentException
    非受检:OutOfMemoryError、StackOverflowError、NullPointerException、IndexOutOfBoundsException

    点击查看所有jdk8 api文档
    进去可以看到java.lang.RuntimeException下有很多子类异常

    暂时写这些,日后再完善

    ]]>
    + + java + + + java + 异常 + +
    + + java.sql.SQLException Connections could not be acquired from the underlying database + /20170602-java.sql.SQLException%20Connections%20could%20not%20be%20acquired%20from%20the%20underlying%20database/ + java.sql.SQLException: Connections could not be acquired from the underlying database

    这个错误出现的原因有的说是因为jdbc配置文件写错了

    正确的写法是:

    c3p0.driverClass=com.mysql.jdbc.Driver
    c3p0.jdbcUrl=jdbc:mysql://localhost:3306/test
    c3p0.user=root
    c3p0.password=root

    而不是

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/test
    user=root
    password=root

    但是我遇到的不是如此

    后来发现是因为maven出问题了,jar包没有加载进项目

    这是在starkoverflow上看到的回答

    In my case it was problem that c3p0-0.9.2.1.jar file was not copied by maven into src/main/webapp/web-inf/libs

    最后maven重新install之后就解决了

    ]]>
    + + java + + + java + +
    + + java编译错误:程序包javax.servlet不存在javax.servlet.* + /20170217-java%E7%BC%96%E8%AF%91%E9%94%99%E8%AF%AF%EF%BC%9A%E7%A8%8B%E5%BA%8F%E5%8C%85javax-servlet%E4%B8%8D%E5%AD%98%E5%9C%A8javax-servlet/ + 之前用myeclipse开发的项目

    今天导入到IDEA中去,发现编译出问题

    ava编译错误:程序包javax.servlet不存在javax.servlet.*


    原因大概是myeclipse中可以选择Java EE项目

    而idea没有,缺少 servlet-api.jar 这个jar包


    解决办法:

    1. 复制tomcat文件夹下lib—>servlet-api.jar 这个jar包
    2. 添加到IDEA项目中去:粘贴到External Library目录下

    即可

    ]]>
    + + java + + + java + +
    + + idea jrebel 7.0 破解 - 获取JRebel激活码 + /20170303-idea%20jrebel%207.0%20%E7%A0%B4%E8%A7%A3%20-%20%E8%8E%B7%E5%8F%96JRebel%E6%BF%80%E6%B4%BB%E7%A0%81/ + 其实这玩意完全不要破解,直接官网注册就会给一个注册码

    注册地址:https://zeroturnaround.com

    注册完了之后在IDEA里面去设置,会提醒激活。

    tomcat部署了项目之后,点击JR启动是可以热部署的!!!

    改了java代码都不要重新启动项目,哈哈!!!

    ]]>
    + + java + + + java + idea + +
    + + 英语文档:how-to-manage-a-redis-database.pdf + /20201010-how-to-manage-a-redis-database/ + 当初这个文档应该是在『科技爱好者周刊』上还是哪里发现的
    看这个文档本来的目的是每天坚持阅读英文文档
    技术文档一般都比较简洁易懂,英语是一个长期的过程,还得继续坚持阅读~

    看的过程中发现,结合一定的场景,和示例
    和官方文档相比,没有那么枯燥,也更好理解
    甚至有一些之前未曾关注的用法让我感到惊艳

    所以推荐一下

    下载地址:how-to-manage-a-redis-database.pdf

    ]]>
    + + database + + + redis + database + +
    + + Java 反序列化漏洞 + /20200510-java-deserialize-security-hole/ + 微博目前关注了好几个做安全的大佬
    不要问我为什么关注
    问就是想观摩下有钱人的微博!

    其中有一个经常发 Java 相关的内容
    今天看到一个稍微熟悉一点关于 FastJson 漏洞

    窃以为Fastjson入门的最佳篇章非Mi1k7ea这个系列莫属,郑重推荐: Mi1k7ea
    共有5篇,从第3篇开始,成体系地由旧到新展示了1.2.25之后各版本的补丁绕过,
    直至思路惊艳的1.2.47绕过方案及1.2.48的修补方案,相当完备。
    可以看出此君是动手实践派、整理归纳派,要我说,新学东西时就得这套路,无它。

    今天看了这两篇文章,不得不说对 Java 是异常的熟悉。
    想做好安全,想必要对用的东西了如指掌,知道坑在哪里,怎么填坑。

    后记:
    很多时候大部分人都只关注自己工作职责内的那一亩三分地
    其实很多时候横向观察一下会发现很多靓丽的风景
    至今没有如此深入的去看过底层甚是愧疚
    用最多的时间去做最正确的事情!

    ]]>
    + + java + + + java + +
    + + java.lang.NoClassDefFoundError错误解决 + /20190223-java.lang.NoClassDefFoundError%E9%94%99%E8%AF%AF%E8%A7%A3%E5%86%B3/ + maven项目昨天遇到的一个错误:java.lang.NoClassDefFoundError

    理论原因:JVM在编译的时候能找到调用方法或静态变量所在的类,但在运行的时候找不到此类而引发的错误。

    真实原因:(仅针对当前问题)
    项目A依赖公共服务C-1.0.1版本
    项目B依赖项目A,B中dependencyManagement强制指定C-1.0.0

    此时编译可以通过,运行时如果有用的C的新版本功能就会会出现上述问题

    解决办法:对项目B中C的版本升级,使用1.0.1

    扩展阅读:
    https://blog.csdn.net/qq_27576335/article/details/77102385
    https://blog.csdn.net/jamesjxin/article/details/46606307

    ]]>
    + + java + + + java + +
    + + linux cal date命令详解 + /20170314-linux%20cal%20date%E5%91%BD%E4%BB%A4%E8%AF%A6%E8%A7%A3/ +
    ##显示当月的日历
    hisen@ubuntu:~$ cal
    March 2017
    Su Mo Tu We Th Fr Sa
    1 2 3 4
    5 6 7 8 9 10 11
    12 13 14 15 16 17 18
    19 20 21 22 23 24 25
    26 27 28 29 30 31

    ##指定显示1997年11月当月的日历
    hisen@ubuntu:~$ cal 11 1997
    November 1997
    Su Mo Tu We Th Fr Sa
    1
    2 3 4 5 6 7 8
    9 10 11 12 13 14 15
    16 17 18 19 20 21 22
    23 24 25 26 27 28 29
    30
    ##显示2018年全年12个月的日历
    hisen@ubuntu:~$ cal -m 12 -y 2018
    2018
    January February March
    Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
    1 2 3 4 5 6 1 2 3 1 2 3
    7 8 9 10 11 12 13 4 5 6 7 8 9 10 4 5 6 7 8 9 10
    14 15 16 17 18 19 20 11 12 13 14 15 16 17 11 12 13 14 15 16 17
    21 22 23 24 25 26 27 18 19 20 21 22 23 24 18 19 20 21 22 23 24
    28 29 30 31 25 26 27 28 25 26 27 28 29 30 31


    April May June
    Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
    1 2 3 4 5 6 7 1 2 3 4 5 1 2
    8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9
    15 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16
    22 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23
    29 30 27 28 29 30 31 24 25 26 27 28 29 30


    July August September
    Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
    1 2 3 4 5 6 7 1 2 3 4 1
    8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8
    15 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15
    22 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22
    29 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29
    30

    October November December
    Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
    1 2 3 4 5 6 1 2 3 1
    7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8
    14 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15
    21 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22
    28 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29
    30 31
    ##输出当前日期
    hisen@ubuntu:~$ date
    Tue Mar 14 11:48:14 CST 2017
    ##格式化输出年月日
    hisen@ubuntu:~$ date "+%Y-%m-%d"
    2017-03-14
    ##格式化输出当前时间
    hisen@ubuntu:~$ date "+%H:%M:%S"
    11:49:10
    ##格式化输出年月日时间
    hisen@ubuntu:~$ date "+%Y-%m-%d %H:%M:%S"
    2017-03-14 11:49:18
    ]]>
    + + linux + + + linux + +
    + + java多姿势读写文件 + /20180718-java%E5%A4%9A%E5%A7%BF%E5%8A%BF%E8%AF%BB%E5%86%99%E6%96%87%E4%BB%B6/ + 重温经典,特意看了下为什么读文件非得不让等于 -1

    fileChannelIn.read(byteBuffer) != -1

    EOF = End Of File,其默认值就是-1

    这是一个约定好的结束符,不是认为改变的

    代码

    建议使用大文件进行测试,看效果比较明显

    @Test
    public void testCopy() {
    String oldFileName = "/Users/hisenyuan/hisen/blog/source/_posts/Test-Java-Code.md";
    String newFileName = "/Users/hisenyuan/hisen/test/Test-Java-Code.md";
    nioCopy(oldFileName, newFileName);
    ioCopy(oldFileName, newFileName.replace(".md", ".md.bak"));
    ioCopyByLine(oldFileName,newFileName.replace(".md",".bak." + System.currentTimeMillis()));
    }

    /**
    * 利用NIO进行读写文件
    *
    * @param oldFileName 原文件的路径
    * @param newFileName 新文件的路径
    */
    public static void nioCopy(String oldFileName, String newFileName) {
    try {
    FileChannel fileChannelIn = new FileInputStream(new File(oldFileName)).getChannel();
    FileChannel fileChannelOut = new FileOutputStream(new File(newFileName)).getChannel();
    //获取文件大小
    long size = fileChannelIn.size();
    System.out.printf("文件大小为:%s byte \n", size);
    //缓冲
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

    long start = System.currentTimeMillis();
    while (fileChannelIn.read(byteBuffer) != -1) {
    //准备写
    byteBuffer.flip();
    fileChannelOut.write(byteBuffer);
    //准备读
    byteBuffer.clear();
    }
    long end = System.currentTimeMillis();
    System.out.printf("NIO方式复制完成,耗时 %s 秒\n", (end - start) / 1000);
    //关闭
    fileChannelIn.close();
    fileChannelOut.close();
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    /**
    * IO方式复制文件
    *
    * @param oldFileName
    * @param newFileName
    */
    public static void ioCopy(String oldFileName, String newFileName) {
    try {
    FileInputStream fileInputStream = new FileInputStream(new File(oldFileName));
    FileOutputStream fileOutputStream = new FileOutputStream(new File(newFileName));

    long length = new File(oldFileName).length();

    System.out.printf("文件大小为:%s byte \n", length);
    byte[] buffer = new byte[1024];

    long start = System.currentTimeMillis();
    int len = 0;
    //EOF = End Of File,其默认值就是-1
    while ((len = fileInputStream.read(buffer)) != -1) {
    fileOutputStream.write(buffer, 0, len);
    }
    long end = System.currentTimeMillis();
    System.out.printf("IO方式复制完成,耗时 %s 秒\n", (end - start) / 1000);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    public static void ioCopyByLine(String oldFileName, String newFileName) {
    try {
    BufferedReader reader = new BufferedReader(new FileReader(oldFileName));
    BufferedWriter writer = new BufferedWriter(new FileWriter(newFileName));
    String line;
    while ((line = reader.readLine()) != null) {
    writer.write(line);
    writer.newLine();
    writer.flush();
    }
    } catch (IOException e) {
    System.out.println("error:" + e);
    }
    }

    ]]>
    + + java + + + java + +
    + + Java JVM 工具与知识 | JVM 调优 + /20211009-jvm-tool-and-knowledge/ + 一、背景

    曾经整理过一个帖子,不过是在公司内网。
    今天突然想起,博客上零散的 jvm 相关内容,
    但未系统整理相关知识和工具,遂写一篇文章。

    学习的过程需要不断发现好的资源,深入钻研某个领域。

    二、知识

    2.1 图书

    • 《深入理解 Java 虚拟机》
    • 《Java 性能优化权威指南》
    • 《性能之巅》

    2.2 文档

    2.3 文章

    三、工具

    ]]>
    + + java + + + java + jvm + +
    + + Java HotSpot 虚拟机 CMS、G1 参数优化对比记录 + /20201019-java-hotspot-vm-cms-g1-setting-log/ + 零、背景说明

    目前测试机器为 4C8G
    两台机器完全处理一样的工作
    大部分时间对象朝生夕死,很少进入老年代
    CMS 指定了新生代最大 1536M,略微有点浪费
    于是设置 G1 自动调节各个区域大小,看看能否有所改善
    也因为最近重温了两本 JVM 相关的书籍,找机会进行实践看看

    一、G1 设置

    -Xms4096M
    -Xmx4096M
    -XX:+UseG1GC
    -XX:+UnlockDiagnosticVMOptions
    -XX:SurvivorRatio=8
    -XX:+ParallelRefProcEnabled
    -XX:MaxTenuringThreshold=6
    -XX:ParGCCardsPerStrideChunk=4096
    -XX:MaxGCPauseMillis=100
    -XX:MaxMetaspaceSize=256M
    -XX:MetaspaceSize=256M
    -XX:+PrintGCDateStamps
    -XX:+PrintGCDetails
    -Xloggc:/home/hisen/gc.log

    二、CMS 设置

    最大最小设置成一致的值,是为了不让堆大小进行收缩(缩的过程会 GC)

    # 初始堆大小
    -Xms4096M
    # 最大堆大小
    -Xmx4096M
    # 年轻代的大小
    -Xmn1536M
    # eden 区比例,此处为 8:1:1
    -XX:SurvivorRatio=8
    # 最大元空间大小
    -XX:MaxMetaspaceSize=256M
    # 最小元空间大小
    -XX:MetaspaceSize=256M
    # 使用 CMS 垃圾收集器
    -XX:+UseConcMarkSweepGC
    # 只使用占用率作为启动 CMS GC 的标准,配合 CMSInitiatingOccupancyFraction 使用
    -XX:+UseCMSInitiatingOccupancyOnly
    # CMS垃圾收集器,当老年代达一定比例,触发CMS垃圾回收。此处为 70%
    -XX:CMSInitiatingOccupancyFraction=70
    # 默认为 false,并行的处理 Reference 对象,如 WeakReference,除非在 GC log 里出现 Reference 处理时间较长的日志,否则效果不会很明显。
    -XX:+ParallelRefProcEnabled
    # 开启或关闭在CMS重新标记阶段之前的 YGC 尝试
    -XX:+CMSScavengeBeforeRemark
    # 晋升老年代的阈值
    -XX:MaxTenuringThreshold=6
    # 解锁诊断参数,否则 ParGCCardsPerStrideChunk 不生效
    -XX:+UnlockDiagnosticVMOptions
    # 每个线程扫描 old gen 的 CardTable (一定大小的内存块) 个数
    -XX:ParGCCardsPerStrideChunk=4096
    # 打印 GC 的时间戳
    -XX:+PrintGCDateStamps
    # 打印 GC 明细日志
    -XX:+PrintGCDetails
    # 存放 GC 日志的路径
    -Xloggc:/home/hisen/gc.log

    三、结果对比

    TypeDurationXmnYGC countYGC avgInterval Time
    G172 hrs 21 min 8 sec1.66G759619.6 ms34 sec 294 ms
    CMS70 hrs 24 min 51 sec1.5G702914.4 ms36 sec 68 ms

    目前应用堆响应时间比较敏感,追求低延迟。
    就上表来看,G1 新生代的大小确实上去了,但并不尽如人意。
    但是随着而来的是 G1 『更频繁的YGC』以及『更长的停顿时间』
    在《深入理解JVM虚拟机》第三版看到,
    由于 G1 处理跨 region 需要耗费额外 10% ~ 20% 的资源,小堆 (堆大小<8G) 上没有优势。

    JVM 新生代大小与老年代大小默认为 1:2,
    大部分应用按这个设置是 OK 的,
    虽然有时候看着老年代一直很小,
    设置那么大浪费内存,但是当调用量大的时候,就能派上用场,减少 Full GC。

    先占个坑,后续继续填内容。

    ]]>
    + + java + + + java + +
    + + JVM系列-MetaSpace(元空间) + /20210430-jvm-series-metaspace/ + 一、结论

    JDK8 因未指定 MetaSpace 大小,程序启动过程中元空间不够用,触发 full gc。

    详细如下:
    JDK8 因未指定 MetaSpace 大小,默认初始大小约 21M
    程序启动,元空间大小占用稳定在 90M
    因为超过了默认元空间大小,导致元空间扩容(每次扩容会 full gc)
    从 GC 日志来看,每次元空间扩容都是增加 20M 左右,所以程序启动时 full gc 4 次

    二、问题

    应用启动时出现 full gc;

    gc日志重点:GC (Metadata GC Threshold) [PSYoungGen: 354024K->15340K(1376256K)

    三、排查过程

    启动过程中,出现了 JVM Full GC 告警。
    由于启动过程中之前没有遇到这种情况,找不到其它原因。
    于是修改 JVM 参数,增加 GC 日志,于是就看到了『二』中的内容。

    看原因是元空间内存不够大导致的
    通过 GC 日志可以看到元空间实际占用大小,100M
    于是调整 JVM 元空间大小至 128M,问题解决。

    问题是解决了,但是原因是什么呢?
    在结论部分已经讲了,这里不重复。

    所谓知其然,更要知其所以然。
    只有这样才能明白问题的本质,学到知识。

    四、背景知识

    Metaspace整体介绍

    ]]>
    + + java + + + java + jvm + +
    + + Linux查看日志文件常用命令 + /20170407-linux%E6%9F%A5%E7%9C%8B%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/ + linux查看日志文件内容命令tail、cat、tac、head、echo

    tail -f test.log

    你会看到屏幕不断有内容被打印出来. 这时候中断第一个进程Ctrl-C,

    cat -n hisen.log | grep ‘907’

    在文件当中查找指定的内容,这里是查询:907


    linux 如何显示一个文件的某几行(中间几行)

    从第3000行开始,显示1000行。即显示3000~3999行

    cat filename | tail -n +3000 | head -n 1000

    显示1000行到3000行

    cat filename| head -n 3000 | tail -n +1000

    *注意两种方法的顺序
    分解:

    1. tail -n 1000:显示最后1000行
    2. tail -n +1000:从1000行开始显示,显示1000行以后的
    3. head -n 1000:显示前面1000行

    用sed命令
    sed -n ‘5,10p’ filename 这样你就可以只查看文件的第5行到第10行。

    例:cat mylog.log | tail -n 1000 #输出mylog.log 文件最后一千行


    cat主要有三大功能:

    1.一次显示整个文件。$ cat filename

    2.从键盘创建一个文件。$ cat > filename

    只能创建新文件,不能编辑已有文件.

    3.将几个文件合并为一个文件: $cat file1 file2 > file

    参数:

    -n 或 –number 由 1 开始对所有输出的行数编号

    -b 或 –number-nonblank 和 -n 相似,只不过对于空白行不编号

    -s 或 –squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行

    -v 或 –show-nonprinting

    例:

    把 textfile1 的档案内容加上行号后输入 textfile2 这个档案里

    cat -n textfile1 > textfile2

    把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3 里。

    cat -b textfile1 textfile2 >> textfile3

    把test.txt文件扔进垃圾箱,赋空值test.txt

    cat /dev/null > /etc/test.txt

    注意:>意思是创建,>>是追加。千万不要弄混了。


    tac (反向列示)

    tac 是将 cat 反写过来,所以他的功能就跟 cat 相反, cat 是由第一行到最后一行连续显示在萤幕上,

    而 tac 则是由最后一行到第一行反向在萤幕上显示出来!


    在Linux中echo命令用来在标准输出上显示一段字符,比如:
    echo “the echo command test!”

    这个就会输出“the echo command test!”这一行文字!

    echo “the echo command test!”>a.sh

    这个就会在a.sh文件中输出“the echo command test!”这一行文字!

    该命令的一般格式为: echo [ -n ] 字符串其中选项n表示输出文字后不换行;字符串能加引号,也能不加引号。

    用echo命令输出加引号的字符串时,将字符串原样输出;

    用echo命令输出不加引号的字符串时,将字符串中的各个单词作为字符串输出,各字符串之间用一个空格分割。

    一些实例

    #在整个文件搜索含有907的内容
    hisen@ubuntu:~/dl$ cat -n hisen.log | grep '907'
    25 [2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Art
    26 [2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Dep

    #从第3行开始,显示10行 即:3~12 并且显示行号
    hisen@ubuntu:~/dl$ cat -n hisen.log | tail -n +3 | head -n 10
    3 07-Apr-2017 15:48:50.072 信息 [main] org.apache.coyote.Abstrac
    4 07-Apr-2017 15:48:50.137 信息 [main] Using a shared selector f
    5 07-Apr-2017 15:48:50.172 测试 [main] Initializing ProtocolHand
    6 07-Apr-2017 15:48:50.186 信息 [main] Using a shared selector f
    7 07-Apr-2017 15:48:50.187 你猜 [main] load Initialization proce
    8 07-Apr-2017 15:48:50.261 信息 [main] StandardService.startInte
    9 07-Apr-2017 15:48:50.261 信息 [main] Servlet Engine: Apache To
    10 07-Apr-2017 15:48:50.293 信息 [main] Starting ProtocolHandler
    11 07-Apr-2017 15:48:50.318 哪里 [main] ProtocolHandler [ajp-nio-
    12 07-Apr-2017 15:48:50.328 信息 [main] start Server startup in 1
    #显示10行前三行
    hisen@ubuntu:~/dl$ cat -n hisen.log | head -n +10 | tail -n 3
    8 07-Apr-2017 15:48:50.261 信息 [main] StandardService.startInte
    9 07-Apr-2017 15:48:50.261 信息 [main] Servlet Engine: Apache To
    10 07-Apr-2017 15:48:50.293 信息 [main] Starting ProtocolHandler

    #显示3-10行
    hisen@ubuntu:~/dl$ cat -n hisen.log | head -n 10 | tail -n +3
    3 07-Apr-2017 15:48:50.072 信息 [main] org.apache.coyote.Abstrac
    4 07-Apr-2017 15:48:50.137 信息 [main] Using a shared selector f
    5 07-Apr-2017 15:48:50.172 测试 [main] Initializing ProtocolHand
    6 07-Apr-2017 15:48:50.186 信息 [main] Using a shared selector f
    7 07-Apr-2017 15:48:50.187 你猜 [main] load Initialization proce
    8 07-Apr-2017 15:48:50.261 信息 [main] StandardService.startInte
    9 07-Apr-2017 15:48:50.261 信息 [main] Servlet Engine: Apache To
    10 07-Apr-2017 15:48:50.293 信息 [main] Starting ProtocolHandler

    #显示倒数第二行
    hisen@ubuntu:~/dl$ cat -n hisen.log | tail -n 2
    25 [2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Art
    26 [2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Dep

    #从24行开始,显示到最后
    hisen@ubuntu:~/dl$ cat -n hisen.log | tail -n +24
    24 07-Apr-2017 15:49:04.585 信息 during time.
    25 [2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Art
    26 [2017-04-07 03:49:04,907] Artifact ssm_study:war exploded: Dep
    ]]>
    + + linux + + + linux + +
    + + log4j日志信息插入mysql数据库 + /20170321-log4j%E6%97%A5%E5%BF%97%E4%BF%A1%E6%81%AF%E6%8F%92%E5%85%A5mysql%E6%95%B0%E6%8D%AE%E5%BA%93/ + 以前不知道这玩意还能直接插入MySQL数据库

    我是使用IDEA,maven

    具体的目录结果见github:github

    log4j配置文件

    #可以设置级别:debug>info>error
    #debug:显示debug、info、error
    #info:显示info、error
    #error:只error
    log4j.rootLogger=debug,info,database
    #注意的地方database 对应 log4j.appender.database.URL的database 若认log4j.rootLogger=debug,info,db 那么 log4j.appender.database.URL的database 要改成db
    #log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.logfile.DatePattern=.yyyy-MM-dd
    #log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
    #输出到控制台
    #log4j.appender.appender1=org.apache.log4j.ConsoleAppender
    #样式为TTCCLayout
    #log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout
    #设置级别:
    #log4j.rootLogger=debug,appender1

    #输出到文件(这里默认为追加方式)
    #log4j.appender.appender1=org.apache.log4j.FileAppender
    #设置文件输出路径
    #【1】文本文件
    #log4j.appender.appender1.File=c:/Log4JDemo02.log
    #【2】HTML文件
    log4j.appender.appender1.File=c:/Log4JDemo02.html
    #设置文件输出样式
    #log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout
    log4j.appender.appender1.layout=org.apache.log4j.HTMLLayout
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%c] - - <%m>%n
    log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
    log4j.appender.logfile.DatePattern=.yyyy-MM-dd
    log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
    log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] wang- <%m>%n
    #下面是配置将日志信息插入数据库,
    #配置输出目标为数据库(假如要将日志在控制台输出,配置为log4j.appender. stdout =org.apache.log4j.ConsoleAppender;将日志写入文件,配置为log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
    #这样的配置在许多地方都要有,需要可查有关资料),当然你也可以自己扩展org.apache.log4j.jdbc.JDBCAppender这个类,只需要在这里配置就可以了例如我们配置我自己扩展的MyJDBCAppender,配置为#log4j.appender.db=com.neam.commons.MyJDBCAppender
    log4j.appender.database.Threshold=info
    #定义什么级别的错误将写入到数据库中
    log4j.appender.database.BufferSize=1
    #设置缓存大小,就是当有1条日志信息是才往数据库插一次
    log4j.appender.database=org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.database.driver=com.mysql.jdbc.Driver
    #设置要将日志插入到数据库的驱动
    log4j.appender.database.URL=jdbc:mysql://127.0.0.1:3306/log4j?useUnicode=true&characterEncoding=UTF-8
    log4j.appender.database.user=root
    log4j.appender.database.password=hisen

    log4j.appender.database.sql=insert into log (Class,Mothod,createTime,LogLevel,MSG) values ('%C','%M','%d{yyyy-MM-dd HH:mm:ss}','%p','%m')
    log4j.appender.database.layout=org.apache.log4j.PatternLayout

    测试类

    package com.hisen.log4j.log4j2MySQL;

    import org.apache.log4j.Logger;

    /**
    * 测试一下log4j把日志插入到mysql数据库
    * 插入语句和数据库配置在log4j的配置文件中
    * Created by hisenyuan on 2017/3/21 at 14:23.
    */
    public class Log4jDemo {
    private static Logger logger = Logger.getLogger(Log4jDemo.class);

    public static void main(String[] args) {
    logger.debug("这是debug信息");
    // 记录info级别的信息
    logger.info("这是info信息");
    logger.info("这里做了一个XX操作,入库,做操作日志");
    // 记录error级别的信息
    logger.error("这是error信息");
    hisen();
    }

    public static void hisen() {
    logger.debug("这是方法中debug信息");
    // 记录info级别的信息
    logger.info("这是方法中info信息");
    // 记录error级别的信息
    logger.error("这是方法中error信息");
    }
    }
    ]]>
    + + java + + + java + +
    + + log4j:WARN No appenders could be found for logger + /20170208-log4j-WARN-No-appenders-could-be-found-for-logger/ + 解决办法为:在项目的src下面新建file名为log4j.properties文件

    ###设置
    log4j.rootLogger = debug,stdout,D,E

    ###输出信息到控制抬
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

    ###输出DEBUG 级别以上的日志到=E://logs/error.log
    log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.D.File = E://logs/log.log
    log4j.appender.D.Append = true
    log4j.appender.D.Threshold = DEBUG
    log4j.appender.D.layout = org.apache.log4j.PatternLayout
    log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

    ###输出ERROR 级别以上的日志到=E://logs/error.log
    log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.E.File =E://logs/error.log
    log4j.appender.E.Append = true
    log4j.appender.E.Threshold = ERROR
    log4j.appender.E.layout = org.apache.log4j.PatternLayout
    log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

    log4j详细使用方法:点击查看

    ]]>
    + + java + log4j + +
    + + mongodb - failed with errno13 Permission denied + /20170712-mongodb%20-%20failed%20with%20errno%2013%20Permission%20denied/ + 在命令行输入:mongo报错

    hisen@ubuntu:~$ mongo
    MongoDB shell version: 3.2.13
    connecting to: test
    2017-07-11T23:11:23.827+0800 I STORAGE [main] In File::open(), ::open for '/home/hisen/.mongorc.js' failed with errno:13 Permission denied
    The ".mongorc.js" file located in your home folder could not be executed

    看倒数第二行,应该是权限的问题,于是

    hisen@ubuntu:~$ sudo chown -R hisen /home/hisen/.mongorc.js
    hisen@ubuntu:~$ mongo
    MongoDB shell version: 3.2.13
    connecting to: test
    Server has startup warnings:
    2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten]
    2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
    2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
    2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten]
    2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
    2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
    2017-07-11T23:11:15.845+0800 I CONTROL [initandlisten]

    完美解决,用户权限的问题。

    ]]>
    + + sql + + + mongodb + +
    + + 找到好的学习方法 + /20200226-find-a-good-way-to-learn/ + 时间是公平的,对每个人都一样,不同之处在于每个人单位时间的产出值会有差异。
    一直在寻找适合于自己的学习方法以提高时间的利用效率,以便不影响诗和远方==
    奈何目前也没有太多满意的答案,或许生命的意义就是在于探索未知而又刺激的世界。
    分享一些文字,提到了一些好的学习方法,抛砖引玉,共勉。

    《贫穷的本质》告诉我们,贫穷的本质是认知。其实很多时候真的是认知,方法论决定了一个人可以走多远。

    《刻意练习》原地踏步的练习是没意义的,大众眼里一万小时定律基本上是错误的,需要的是有进步练习;

    《学习之道》十个不错的方法:运用回想、知识组块、简化类比、方法交替、间隔重复、注意休息、心理对照、自我测试、保持专注、困难先行;

    《如何阅读一本书》里面有提到主题阅读,不同的人对同一主题会有不同的看法,可以对比吸收;

    《程序员的职业素养》有提到卡塔练习,提高我们的肌肉反应,让写代码像聊天打字一遍的顺畅;

    早些天看到一篇不错的帖子
    对我感触最深的就是:4
    标题:如果高效学习有什么秘诀的话,那就都在这里了:)

    1)不要完美主义!
    2)不要过度“学习路径依赖”,学习要冲着自己的目标去。
    3)不要迷信权威的“好”教材。
    4)不要看不起“薄薄”的“傻”教材,这些你看不起的学习材料,可能是你入门某个领域的关键。
    5)不要迷信单一教材。
    6)实践!
    7)debug非常非常重要。
    8)量变到质变。
    9)最后,一定要相信时间的力量。

    参考:

    1. 《贫穷的本质》
    2. 《刻意练习》
    3. 《学习之道》
    4. 《如果阅读一本书》
    5. 如果高效学习有什么秘诀的话,那就都在这里了:)
    ]]>
    + + 随说 + + + 随说 + +
    + + mybatis - Invalid bound statement (not found) + /20170802-mybatis%20-%20Invalid%20bound%20statement%20(not%20found)/ + 运行报错

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hisen.dao.UserMapper.insert

    使用mybatis生成插件,产生的mapper,由于路径不对移动了一下mapper.java文件

    所以造成mapper.xml里面的namespace错误,无法映射

    所以把namespace改为正确的即可

    例如:

    mapper.UserMapper

    改为

    com.hisen.dao.UserMapper

    ]]>
    + + java + + + mybatis + +
    + + mac docker compose timezone 问题解决 + /20181221-mac%20docker%20compose%20timezone%20%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/ + 一、背景介绍

    想用docker搭建一个lnmp环境

    使用这个脚本:

    https://github.com/buxiaomo/docker-compose/tree/master/lnmp

    执行命令之后报错

    docker-compose -f lnmp.yml up -d
    WARNING: Some services (mysql, nginx, php, redis) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
    WARNING: Some services (mysql, nginx, php) use the 'configs' key, which will be ignored. Compose does not support 'configs' configuration - use `docker stack deploy` to deploy to a swarm.
    lnmp_mysql_1 is up-to-date
    Starting lnmp_redis_1 ...
    lnmp_nginx_1 is up-to-date
    Starting lnmp_redis_1 ... error

    ERROR: for lnmp_redis_1 Cannot start service redis: b'Mounts denied: \r\nThe path /usr/share/zoneinfo/Asia/Shanghai\r\nis not shared from OS X and is not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\nSee https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.\r\n.'

    memory: 100M
    ERROR: for redis Cannot start service redis: b'Mounts denied: \r\nThe path /usr/share/zoneinfo/Asia/Shanghai\r\nis not shared from OS X and is not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\nSee https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.\r\n.'
    ERROR: Encountered errors while bringing up the project.

    二、报错原因

    1. mac机器的时间路径与linux不一样
    2. lnmp.yml redis用到了这个时间

    三、解决办法

    1. 查看当前机器时间文件真实位置(/etc/localtime 这个路径是不让共享给docker的)

      ls -la /etc/localtime
      lrwxr-xr-x 1 root wheel 39 10 19 11:13 /etc/localtime -> /var/db/timezone/zoneinfo/Asia/Shanghai
    2. 设置位置 :Docker -> Preferences… -> File Sharing

    3. 新增共享目录:/var/db/timezone
    4. apply & restart
    5. 修改lnmp.yml配置文件 全局替换:/usr/share/zoneinfo/Asia/Shanghai 为:/var/db/timezone/zoneinfo/Asia/Shanghai

    四、成功信息

    docker-compose -f lnmp.yml up -d
    WARNING: Some services (mysql, nginx, php, redis) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
    WARNING: Some services (mysql, nginx, php) use the 'configs' key, which will be ignored. Compose does not support 'configs' configuration - use `docker stack deploy` to deploy to a swarm.
    Removing lnmp_redis_1
    Starting lnmp_nginx_1 ... done
    Starting lnmp_mysql_1 ... done
    Recreating f5b9db566588_lnmp_redis_1 ... done
    Starting lnmp_php_1 ... done
    ]]>
    + + docker + + + docker + +
    + + mac安装Go环境 & IDE(GoLand) & 基本运行方法(标准输入) + /20180908-mac%E5%AE%89%E8%A3%85Go%E7%8E%AF%E5%A2%83%20&%20IDE(GoLand)%20&%20%E5%9F%BA%E6%9C%AC%E8%BF%90%E8%A1%8C%E6%96%B9%E6%B3%95/ + 一、安装GO

    1.1 使用Homebrew安装go环境(如果很慢,可以换个源)

    brew install go

    1.2 查看安装信息

    go env

    主要关注如下输出

    GOROOT="/usr/local/Cellar/go/1.10.3/libexec" # 安装目录

    1.3 配置环境变量

    vi ~/.bash_profile # 没有的话会新建一个文件

    输入如下内容,第一行是安装的目录,第三行是工作目录(可以改成自己喜欢的路径)

    GOROOT=/usr/local/Cellar/go/1.10.3/libexec
    export GOROOT
    export GOPATH=/Users/hisenyuan/golang
    export GOBIN=$GOPATH/bin
    export PATH=$PATH:$GOBIN:$GOROOT/bin

    1.4 让配置文件生效并且查看环境变量

    source ~/.bash_profile
    go env # 这时你会发现环境变量已经有改变

    二、安装GoLand

    我是习惯了用jetbrains的idea

    发现它家也有go语言的IDE GoLand

    于是就去官网下载,安装,找个注册码,修改一下host防止注册码失效

    这里就不再累赘了

    三、运行需要输入的程序

    买了一本《Go语言程序设计》

    有些程序需要从标准输入获取信息,然后进行处理,例如dup2

    运行之后不知道该肿么办,百度一番之后发现了方法

    命令行:Ctl + D

    GoLand:command + D

    贴一段程序和运行的结果

    package main

    import (
    "bufio"
    "fmt"
    "os"
    )

    // dup2 打印输入中多次出现的文本和行数
    // 它从stdin或指定的文件到文件列表读取

    func main() {
    counts := make(map[string]int)
    files := os.Args[1:]
    if len(files) == 0 {
    countLines(os.Stdin, counts)
    } else {
    for _, arg := range files {
    f, err := os.Open(arg)
    if err != nil {
    fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
    continue
    }
    countLines(f, counts)
    f.Close()
    }
    }
    for line, n := range counts {
    if n > 1 {
    fmt.Printf("%d\t%s\n", n, line)
    }
    }
    }
    func countLines(f *os.File, counts map[string]int) {
    input := bufio.NewScanner(f)
    for input.Scan(){
    counts[input.Text()]++
    }
    }
    // 执行的过程中,得按command d
    //123
    //123
    //123
    //111
    //222
    //222
    //112
    //111^D
    //3 123
    //2 222

    ]]>
    + + go + + + go + +
    + + mybatis常用jdbctype + /20170310-mybatis%E5%B8%B8%E7%94%A8jdbctype/ + Mybatis中javaType和jdbcType对应关系

    JDBC TypeJava Type
    CHARString
    VARCHARString
    LONGVARCHARString
    NUMERICjava.math.BigDecimal
    DECIMALjava.math.BigDecimal
    BITboolean
    BOOLEANboolean
    TINYINTbyte
    SMALLINTshort
    INTEGERint
    BIGINTlong
    REALfloat
    FLOATdouble
    DOUBLEdouble
    BINARYbyte[]
    VARBINARYbyte[]
    LONGVARBINARYbyte[]
    DATEjava.sql.Date
    TIMEjava.sql.Time
    TIMESTAMPjava.sql.Timestamp
    CLOBClob
    BLOBBlob
    ARRAYArray
    DISTINCTmapping of underlying type
    STRUCTStruct
    REFRef
    DATALINKjava.net.URL
    ]]>
    + + java + + + java + mybatis + +
    + + 我是怎样爱上阅读的 + /20200201-how-i-fell-in-love-with-reading/ + 零、概览

    2015 年刚毕业的我懵懵懂懂看书都想睡觉
    很开心来到北京遇到了许多美好的人和事
    受到环境的影响一路走来成长加速收获颇丰

    从 2016 开始慢慢喜欢上了阅读
    从开始看书就想睡觉到现在一个6层书架都放不下的纸质书
    从开始的『kindle voyage』到现在的『kindle oasis』第三代
    从开始的只是在北京地铁5号线看下电子书到现在有点时间就看
    慢慢地养成了阅读的习惯,生活也没有那么无聊,工作慢慢步入正轨
    非技术书籍喜欢用 kindle 看,技术书还是纸书比较合适翻阅

    简单统计
    2020 51本
    2019 71本
    2018 38本
    2017 48本(2017以及之前)

    一、常见问题

    Q:看书想睡觉怎么办?
    A:先找有兴趣/好奇的主题

    Q:看书记不住怎么办?
    A:能不能记住无所谓,看的过程中有思考即可,好的书多看几遍

    Q:哪来这么多时间?
    A:合理安排时间,少浪费时间即可(通勤+早晚+中午,最少有3个小时时间)

    二、为什么要阅读

    读书使人充实,读史使人明智,读诗使人灵秀,
    数学使人周密,科学使人深刻,伦理使人庄重,逻辑修辞学使人善辩

    古人说开卷有益(无营养的除外)
    通过阅读我们可以了解他人的经历
    在他人的经历中我们可以得到经验
    在他人的经验当中我们可以得到思想
    一个人毕竟阅历有限,站在巨人的肩膀上你可以看的更高

    科普性的书很多时候会恍然大悟
    技术性的书能获得很多优秀的设计和实践以及学习新的技能
    人文历史地里这些基本上是扩展知识面、提高个人综合素养、探索人生存在的意义

    三、怎么开始阅读

    从我的书单(hisen.me/booklist/)当中可以看出
    刚开始的时候基本上都是兴趣导向
    俗话说兴趣是最好的老师,有些好书恨不得立马就看完,
    接触很多人说看书就想睡觉,其实那不是我们的错,
    是书没有写好,换一本就好了,如果还是不行,那就继续换。

    当找到感兴趣的主题,看了一段时间之后
    会发现阅读速度会有所提升,也会有很强的满足感
    毕竟看的过程中总会有所收获,有所思考

    兴趣是最好的老师,但是兴趣不能当饭吃
    这个时候应该开始找一些通俗易懂的技术书,而不是那些很难啃的枕头
    还是以有趣,通俗易懂为主
    坚持一段时间,技术上也会有所收获
    慢慢的就可以驰骋技术书籍的战场了
    如果有时候发现看不下去了
    那么就切换其它类型的书籍(我一般都是一本技术一本非技术)

    建议看看《如何阅读一本书》

    四、怎么沉迷于阅读

    当通过阅读,名利双收的时候,我想没有几个人会不沉迷于阅读
    想做到名利双收,需要养成好的习惯,挑选好的书籍
    建议看看相关领域大佬分享的书单,或者直接咨询
    书籍看多了实践也得跟上
    有条件可以合志同道合的人一起看同一本书,看的时候多交流探讨,会有意想不到的效果

    五、去哪儿找书单

    1. hisen.me/booklist/
    2. 去问身边比自己强的人
    3. 查看业界大佬的书单(比如他们的博客/世界读书日分享等)
    4. 电子商务网站是看相关领域的排行榜
    5. GitHub 上各种学习类型的仓库之推荐书籍

    六、小结

    贫穷的本质是认知的差异
    阅读、反馈、实践慢慢迭代认知
    有长远的目标,不负韶华,坚持学习,努力提高

    ]]>
    + + 成长 + + + read + +
    + + new ImageIcon()无法加载同目录图片 + /20170208-new-ImageIcon-%E6%97%A0%E6%B3%95%E5%8A%A0%E8%BD%BD%E5%90%8C%E7%9B%AE%E5%BD%95%E5%9B%BE%E7%89%87/ + 错误:

    new ImageIcon("1.jpg")

    正确:

    new ImageIcon("src/com/hisen/thread/progressbar/1.jpg")

    图片路径:

    test\src\com\hisen\thread\progressbar\1.jpg

    所谓的相对路径,是相对于这个工程而言的,而不是当前文件夹而言。

    ]]>
    + + java + jframe + +
    + + java.lang.IllegalArgumentException: addChild: Child name '/hisen' is not unique + /20170227-java-lang-IllegalArgumentException-addChild-Child-name-hisen-is-not-unique/ + 报错如下:

    27-Feb-2017 12:53:31.268 严重 [RMI TCP Connection(13)-127.0.0.1] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method manageApp
    java.lang.IllegalArgumentException: addChild: Child name '/hisen' is not unique
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:738)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:728)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
    at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1702)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:482)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:431)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
    at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
    at sun.rmi.transport.Transport$1.run(Transport.java:177)
    at sun.rmi.transport.Transport$1.run(Transport.java:174)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

    重要的是:

    org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method manageApp
    java.lang.IllegalArgumentException: addChild: Child name '/hisen' is not unique

    IDEA解决办法

    Project Structure -> Artifacts

    查看里面是否有配置相同的Artifacts

    删除即可

    ]]>
    +
    + + powerdesigner Oracle mysql comment 缺少右括号 + /20170622-powerdesigner%20Oracle%20mysql%20comment%20%E7%BC%BA%E5%B0%91%E5%8F%B3%E6%8B%AC%E5%8F%B7/ + powerdesigner Oracle mysql comment 缺少右括号

    今天人家给我个powerdesigner设计好的表

    要我去间数据库,直接复制出来去oracle执行,结果报错。

    在comment附近,如果把comment去掉则可以正确执行

    最后找到罪魁祸首:powerdesigner没有设置对数据库

    解决办法,在powerdesigner页面

    database-->change curren DBMS

    上面是设置你需要的数据库

    下面是为更改前的数据库

    ]]>
    + + sql + + + mysql + oracle + +
    + + maven环境搭建 + /20170214-maven%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/ + 一、环境的配置
    1. 下载maven:http://maven.apache.org/download.cgi
    2. 解压到相关目录
    3. 配置环境变量:计算机—系统属性—高级系统设置—环境变量
    4. 系统变量新建:M2_HOME 变量值:C:\tool\JAVA\apache-maven-3.3.9
    5. path中添加:%M2_HOME%\bin; (注意看看前面有没有分号隔开,没有添上)
    6. 启动cmd,输入mvm -v可以查看版本
      Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)
      Maven home: C:\tool\JAVA\apache-maven-3.3.9
      Java version: 1.7.0_51, vendor: Oracle Corporation
      Java home: C:\hisenwork\Java\jdk1.7.0_51\jre
      Default locale: zh_CN, platform encoding: GBK
      OS name: "windows 8", version: "6.2", arch: "amd64", family: "windows"

    二、自定义设置(优化)

    1.自定义下载目录,修改配置文件

    C:\hisenwork\soft\maven-3.3.9\conf\settings.xml

    C:\hisenwork\soft\maven-3.3.9为你解压的maven路径


    ps:如果不想自己设置,我有现成的settings.xml,直接复制粘贴,覆盖原来的即可
    现成的settings.xml内容我放在底部

    搜索:localRepository

    在注释外添加以下代码,以后下载maven相关文件就会在这

    <!--自定义存放目录-->
    <localRepository>C:\hisenwork\soft\maven
    </localRepository>

    2.自定义镜像(推荐阿里云,速度飞快)

    搜索:mirrors

    在里面添加:

    <!--阿里云-->
    <mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>*</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>

    3.运行命令初始化maven

    mvn  help:system

    然后你会看到命令行飞快的移动,在你刚刚设置的目录下会出现很多东西

    类似于这样

    $ mvn  help:system
    [INFO] Scanning for projects...
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom (4 KB at 1.8 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 15.9 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom (26 KB at 15.5 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom (15 KB at 19.3 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar (25 KB at 30.6 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.pom (7 KB at 5.5 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom (9 KB at 8.2 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/22/maven-parent-22.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/22/maven-parent-22.pom (30 KB at 7.9 KB/sec)
    Downloading: https://repo.maven.apache.org/maven2/org/apache/apache/11/apache-11.pom
    Downloaded: https://repo.maven.apache.org/maven2/org/apache/apache/11/apache-11.pom (15 KB at 12.8 KB/sec)
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 49.223 s
    [INFO] Finished at: 2017-02-14T13:09:33+08:00
    [INFO] Final Memory: 11M/152M
    [INFO] ------------------------------------------------------------------------

    三、maven相关命令

    创建 Maven 项目

    mvn archetype:create

    编译源代码(编译到target文件夹中)

    mvn compile

    编译测试代码

    mvn test-compile

    运行应用程序中的单元测试

    mvn test

    生成项目相关信息的网站

    mvn site

    清除目标目录中的生成结果(把默认target文件夹中的数据清理)

    mvn clean

    项目打包

    mvn package

    将打包好的包安装到本地仓库中,以使其塔项目能够调用

    mvn install

    生成 Eclipse 项目文件

    mvn eclipse:eclipse

    忽略测试文档编译

    mvn -Dmaven.test.skip=true

    部署到私有服务器

    cargo:deploy

    四、settings.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!--
    author:HiSEN
    website:http://hisen.me
    -->
    <!--
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements. See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership. The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied. See the License for the
    specific language governing permissions and limitations
    under the License.
    -->

    <!--
    | This is the configuration file for Maven. It can be specified at two levels:
    |
    | 1. User Level. This settings.xml file provides configuration for a single user,
    | and is normally provided in ${user.home}/.m2/settings.xml.
    |
    | NOTE: This location can be overridden with the CLI option:
    |
    | -s /path/to/user/settings.xml
    |
    | 2. Global Level. This settings.xml file provides configuration for all Maven
    | users on a machine (assuming they're all using the same Maven
    | installation). It's normally provided in
    | ${maven.home}/conf/settings.xml.
    |
    | NOTE: This location can be overridden with the CLI option:
    |
    | -gs /path/to/global/settings.xml
    |
    | The sections in this sample file are intended to give you a running start at
    | getting the most out of your Maven installation. Where appropriate, the default
    | values (values used when the setting is not specified) are provided.
    |
    |-->
    <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <!-- localRepository
    | The path to the local repository maven will use to store artifacts.
    |
    | Default: ${user.home}/.m2/repository
    <localRepository>/path/to/local/repo</localRepository>
    -->
    <!--自定义存放目录-->
    <localRepository>C:\hisenwork\soft\maven
    </localRepository>

    <!-- interactiveMode
    | This will determine whether maven prompts you when it needs input. If set to false,
    | maven will use a sensible default value, perhaps based on some other setting, for
    | the parameter in question.
    |
    | Default: true
    <interactiveMode>true</interactiveMode>
    -->

    <!-- offline
    | Determines whether maven should attempt to connect to the network when executing a build.
    | This will have an effect on artifact downloads, artifact deployment, and others.
    |
    | Default: false
    <offline>false</offline>
    -->

    <!-- pluginGroups
    | This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e.
    | when invoking a command line like "mvn prefix:goal". Maven will automatically add the group identifiers
    | "org.apache.maven.plugins" and "org.codehaus.mojo" if these are not already contained in the list.
    |-->
    <pluginGroups>
    <!-- pluginGroup
    | Specifies a further group identifier to use for plugin lookup.
    <pluginGroup>com.your.plugins</pluginGroup>
    -->
    </pluginGroups>

    <!-- proxies
    | This is a list of proxies which can be used on this machine to connect to the network.
    | Unless otherwise specified (by system property or command-line switch), the first proxy
    | specification in this list marked as active will be used.
    |-->
    <proxies>
    <!-- proxy
    | Specification for one proxy, to be used in connecting to the network.
    |
    <proxy>
    <id>optional</id>
    <active>true</active>
    <protocol>http</protocol>
    <username>proxyuser</username>
    <password>proxypass</password>
    <host>proxy.host.net</host>
    <port>80</port>
    <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
    -->
    </proxies>

    <!-- servers
    | This is a list of authentication profiles, keyed by the server-id used within the system.
    | Authentication profiles can be used whenever maven must make a connection to a remote server.
    |-->
    <servers>
    <!-- server
    | Specifies the authentication information to use when connecting to a particular server, identified by
    | a unique name within the system (referred to by the 'id' attribute below).
    |
    | NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are
    | used together.
    |
    <server>
    <id>deploymentRepo</id>
    <username>repouser</username>
    <password>repopwd</password>
    </server>
    -->

    <!-- Another sample, using keys to authenticate.
    <server>
    <id>siteServer</id>
    <privateKey>/path/to/private/key</privateKey>
    <passphrase>optional; leave empty if not used.</passphrase>
    </server>
    -->
    </servers>

    <!-- mirrors
    | This is a list of mirrors to be used in downloading artifacts from remote repositories.
    |
    | It works like this: a POM may declare a repository to use in resolving certain artifacts.
    | However, this repository may have problems with heavy traffic at times, so people have mirrored
    | it to several places.
    |
    | That repository definition will have a unique id, so we can create a mirror reference for that
    | repository, to be used as an alternate download site. The mirror site will be the preferred
    | server for that repository.
    |-->
    <mirrors>
    <!-- mirror
    | Specifies a repository mirror site to use instead of a given repository. The repository that
    | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
    | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
    |
    <mirror>
    <id>mirrorId</id>
    <mirrorOf>repositoryId</mirrorOf>
    <name>Human Readable Name for this Mirror.</name>
    <url>http://my.repository.com/repo/path</url>
    </mirror>
    -->
    <!--阿里云-->
    <mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>*</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
    </mirrors>

    <!-- profiles
    | This is a list of profiles which can be activated in a variety of ways, and which can modify
    | the build process. Profiles provided in the settings.xml are intended to provide local machine-
    | specific paths and repository locations which allow the build to work in the local environment.
    |
    | For example, if you have an integration testing plugin - like cactus - that needs to know where
    | your Tomcat instance is installed, you can provide a variable here such that the variable is
    | dereferenced during the build process to configure the cactus plugin.
    |
    | As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles
    | section of this document (settings.xml) - will be discussed later. Another way essentially
    | relies on the detection of a system property, either matching a particular value for the property,
    | or merely testing its existence. Profiles can also be activated by JDK version prefix, where a
    | value of '1.4' might activate a profile when the build is executed on a JDK version of '1.4.2_07'.
    | Finally, the list of active profiles can be specified directly from the command line.
    |
    | NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact
    | repositories, plugin repositories, and free-form properties to be used as configuration
    | variables for plugins in the POM.
    |
    |-->
    <profiles>
    <!-- profile
    | Specifies a set of introductions to the build process, to be activated using one or more of the
    | mechanisms described above. For inheritance purposes, and to activate profiles via <activatedProfiles/>
    | or the command line, profiles have to have an ID that is unique.
    |
    | An encouraged best practice for profile identification is to use a consistent naming convention
    | for profiles, such as 'env-dev', 'env-test', 'env-production', 'user-jdcasey', 'user-brett', etc.
    | This will make it more intuitive to understand what the set of introduced profiles is attempting
    | to accomplish, particularly when you only have a list of profile id's for debug.
    |
    | This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo.
    <profile>
    <id>jdk-1.4</id>

    <activation>
    <jdk>1.4</jdk>
    </activation>

    <repositories>
    <repository>
    <id>jdk14</id>
    <name>Repository for JDK 1.4 builds</name>
    <url>http://www.myhost.com/maven/jdk14</url>
    <layout>default</layout>
    <snapshotPolicy>always</snapshotPolicy>
    </repository>
    </repositories>
    </profile>
    -->

    <!--
    | Here is another profile, activated by the system property 'target-env' with a value of 'dev',
    | which provides a specific path to the Tomcat instance. To use this, your plugin configuration
    | might hypothetically look like:
    |
    | ...
    | <plugin>
    | <groupId>org.myco.myplugins</groupId>
    | <artifactId>myplugin</artifactId>
    |
    | <configuration>
    | <tomcatLocation>${tomcatPath}</tomcatLocation>
    | </configuration>
    | </plugin>
    | ...
    |
    | NOTE: If you just wanted to inject this configuration whenever someone set 'target-env' to
    | anything, you could just leave off the <value/> inside the activation-property.
    |
    <profile>
    <id>env-dev</id>

    <activation>
    <property>
    <name>target-env</name>
    <value>dev</value>
    </property>
    </activation>

    <properties>
    <tomcatPath>/path/to/tomcat/instance</tomcatPath>
    </properties>
    </profile>
    -->
    </profiles>

    <!-- activeProfiles
    | List of profiles that are active for all builds.
    |
    <activeProfiles>
    <activeProfile>alwaysActiveProfile</activeProfile>
    <activeProfile>anotherAlwaysActiveProfile</activeProfile>
    </activeProfiles>
    -->
    </settings>
    ]]>
    + + java + + + java + maven + +
    + + 读后感:《凤凰架构》 + /20210613-reaction-of-the-fenix-project/ + 一、简短感受

    简单介绍,《凤凰架构》作者,周志明。
    他最出名的书籍非《深入理解Java虚拟机》莫属了
    书中了解到他对技术的态度,以及持续奋战一线值得学习,《程序员之路》值得一看(底部有链接)。

    阅读时间:0531~0612
    构建凤凰磐涅般的系统
    介绍了一整套技术体系
    穿插着技术的来龙去脉
    着实是一部不错的书籍
    架构的前提是足够了解
    综合实际情况做出权衡
    给人感觉不太像架构书
    总的来说还是值得一看

    用输出倒逼输入
    这就是写博客等其它输出手段的作用

    目前的软件没有烟囱式的,都是金字塔类型,所以底层基础要牢固!

    二、部分摘抄

    四层负载均衡的优势是性能高
    主要工作在
    数据链路层(2层),改写Mac地址
    网络层(3层),改写 IP 地址

    七层负载均衡的优势是功能强

    将简单的校验交给 bean validation,
    而把复杂校验留给自己,简直就是买株还珠。
    可以使用自定义注解,优雅地解决校验问题。
    检查校验项预置好默认提示信息。
    基础校验直接放在 bean 属性上,业务的单开类。

    能够使用确定的操作,促使状态间产生确定的转移结果的计算模型,在计算机科学中被称为状态机。

    状态机特性:任何初始状态一样的状态机,如果执行的命令序列一样,则最终达到的状态也一样。(可用于分布式协商…)

    广播指令、状态机复制。

    让各个系统节点不受局部网络分区、机器崩溃、执行性能、其它因素影响,都能最终表现出整体一致的过程,被称为各个节点的协商共识。

    一致性是指数据不同副本之间的差异,
    共识是指达成一致性的方法与过程。

    足可见技术圈里即使再有本事,也需要好好包装一下。paxos 论文发表三次后,被谷歌实现之后,凭着谷歌大拿的高度评价,获得了极大的关注。

    操作转移模型
    状态转移模型

    达成共识的三步

    1. 如何选主
    2. 如何把数据复制到各个节点
    3. 如何保证过程是安全的

    网关 = 路由器(基础职能) + 过滤器(可选职能)

    打饭解释各种 I/O 模型
    异步 I/O :下完外卖订单后干别的去,骑手送上门
    同步 I/O
    阻塞:打饭发现饭还没好,就一直干等着
    非阻塞:打饭发现饭还没好,per 3min 看好了没
    多路复用:同上,只是一个人可以处理多个请求
    信号驱动:发现饭没好,让厨师好了通知你

    BFF网关:backends for frontends,针对网关这种边缘节点,对同样的后端集群,裁剪、适配、聚合出不一样的前端服务,有助于后端的稳定,也有助于前端的赋能。

    由于服务随时都有可能崩溃,因此快速的失败检测和自动恢复就显得至关重要。

    lstio(mesh) 在 1.5 之前是使用微服务架构开发
    模块如下

    1. mixer:鉴权策略与遥测
    2. pilot:对接 envoy 的数据平面,xds 策略分发
    3. galley:配置管理,提供外部配置感知能离
    4. citadel:安全加密,RABC 权限控制

    微服务的目的是有效拆分应用,实现敏捷开发和部署。

    凡事总该现有目的,有预期收益再谈行动才显得合理。

    《没有银弹》:硬件的成本能够持续稳定地下降,而软件开发的成本则不可能。

    系统的架构趋同于系统设计团队的沟通解构。
    康威定律核心观点:沟通决定设计

    大厂研发收到追捧,出除了企业本身的光环外(来自于哪里?),有大型系统浸染的经验,更有可能属于技术专家,也是其中的原因。

    微服务对普通业务研发友好,但是对架构要求极高。

    微服务的前提

    1. 决策者与执行者都能意识到康威定律在软件设计中的关键作用
    2. 组织中具备对微服务有充分理解、有一定实战经验的技术专家
    3. 追求以自治为目标的监控、度量能力
    4. 复杂性已经成为制约生产力的主要矛盾

    需要想清楚做一件事的目的是什么?考虑 ROI

    长期来看,多数服务端结局都是报废而非演进–Martin Fowler。
    随说:良好设计的系统,应该是能够报废的,而非一味追求一步到位。

    微服务拆分粒度
    下界:独立(发布 / 部署 / 运行 / 测试),内聚(本地事务)
    上界:2 披萨团队 (6~12)在一个研发周期完成全部需求。

    治理,系统复杂性下的产物。

    软件研发的认知负荷,本质上是来自技术的认知复杂度。

    分布式系统早已放弃 Unix 所追求的简单性设计哲学。

    治理架构腐化唯一有效的办法就是演进式设计。
    开发过程中少妥协,否则会形成破窗效应。

    Architect 架构师一词从建筑行业借鉴而来,让人容易误解架构师类似于给建筑设计骨架、绘制图纸的建筑架构师。演进式设计更像是“换房子”而不是“造房子”。

    先进的生产力都伴随着更高的复杂性,需要有与生产力符合的生产关系来匹配,敏锐地捕捉到生产力的变化,随时调整生产关系,这才是架构师治理复杂性的终极方法。

    1992 Java write once,run anywhere。
    2018 graal vm,run programs faster anywhere。

    目前 graal vm
    优势:启动时间快,打包小,内存消耗少
    劣势:无法无缝支持动态代理技术,延迟、吞吐量、可监控方面略差,性能劣于 hotspot jit 优化。毕竟是运行时优化,各种分支预测~

    将思考具象化。
    如果不把自己思考的内容输出给他人,
    我们很容易就被自己所欺骗,
    误以为自己已经理解得足够完备了。
    随说:用输出倒逼输入。

    三、图书资源

    纸书作者应该还在初版中,目前看的是 PDF.
    这本书属于开源书籍,开源地址:《凤凰架构》
    凤凰架构附录的一篇文章不错:《程序员之路》

    ]]>
    + + read + + + read + +
    + + mybatis:No constructor found in xxx matching + /20170317-mybatis%EF%BC%9ANo%20constructor%20found%20in%20xxx%20matching%20%5Bjava.lang.Integer,%20java.lang.String,%20java.lang.Integer%5D/ + 如下错误提示:

    mybatis:No constructor found in xxx matching [java.lang.Integer, java.lang.String, java.lang.Integer]

    原因:xxx 这个bean缺少一个默认的构造方法!


    解决:加上默认的构造方法即可


    我是在单元测试的时候遇到这个问题

    ]]>
    + + java + + + java + +
    + + rabbitMQ的安装和Demo + /20180111-rabbitMQ%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8CDemo/ + 零、rabbitMQ介绍

    rabbitMQ详细介绍

    1. 如果某个queue有多个订阅,消息分均分到消费者,而不是所有人都收到全部
    2. 接收消息有ack(acknowledgment)机制,发送消息是没有这个机制的
    3. 生产者将消息发送到Exchange(交换器),由Exchange将消息路由到一个或多个Queue中(或者丢弃)。

      一、在ununtu上安装

      1.1 安装

      echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list

      wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -

      sudo apt-get update

      sudo apt-get install rabbitmq-server

    1.2 配置

    # 打开管理页面功能
    sudo rabbitmq-plugins enable rabbitmq_management
    # 查看安装的插件
    sudo rabbitmqctl list_users
    # 查看用户
    sudo rabbitmqctl list_users
    # 新增管理员用户
    sudo rabbitmqctl add_user admin admin
    # 授予管理员权限
    sudo rabbitmqctl set_user_tags admin administrator
    # 管理页面地址,用刚设置的账户登录管理页面

    http://127.0.0.1:15672

    二、Java小Demo

    2.1 遇到的问题

    1. ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN.
      帐号密码错误,建议使用2.3配置的账户,guest账户不靠谱

    2. connection error
      ip或者port错误,确认信息是否正确,虚拟机的话看看端口映射是否正常

    2.2 配置用户

    # 添加普通用户
    sudo rabbitmqctl add_user hisen hisen
    # 添加权限
    rabbitmqctl set_permissions -p "/" hisen ".*" ".*" ".*"
    # 列出用户权限
    rabbitmqctl list_user_permissions hisen

    2.3 添加maven依赖

    <dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.0.0</version>
    </dependency>

    2.4 发送端代码

    package com.hisen.jars.rabbitmq;

    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;

    public class Send {
    // 定义队列名字
    public static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
    // 创建连接工厂
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("127.0.0.1");
    factory.setPort(5672);
    factory.setUsername("hisen");
    factory.setPassword("hisen");
    Connection connection = null;
    Channel channel = null;
    try {
    // 创建连接
    connection = factory.newConnection();
    // 创建信道
    channel = connection.createChannel();
    // 声明一个队列:名称、持久性的(重启仍存在此队列)、非私有的、非自动删除的
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);
    for (int i = 0; i < 10; i++) {
    // 定义消息内容
    String message = "Hello World - " + i;
    // 通过信道发布内容
    channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
    System.out.println("send : " + message);
    }
    } catch (IOException e) {
    e.printStackTrace();
    } catch (TimeoutException e) {
    e.printStackTrace();
    } finally {
    if (null!=channel) {
    channel.close();
    }
    if (null!= connection ) {
    connection.close();
    }
    }
    }
    }

    2.5 接收端代码

    package com.hisen.jars.rabbitmq;

    import com.rabbitmq.client.AMQP.BasicProperties;
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    import com.rabbitmq.client.DefaultConsumer;
    import com.rabbitmq.client.Envelope;
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    import org.joda.time.DateTime;

    public class Receive{
    // 定义队列名字
    public static final String QUEUE_NAME = "hello";

    public static void main(String[] args) {
    // 创建连接工厂
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("127.0.0.1");
    factory.setUsername("hisen");
    factory.setPassword("hisen");
    try {
    // 创建连接
    Connection connection = factory.newConnection();
    // 创建信道
    Channel channel = connection.createChannel();
    // 声明一个队列:名称、持久性的(重启仍存在此队列)、非私有的、非自动删除的
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);
    System.out.println("watting for message");

    /* 定义消费者 */
    DefaultConsumer consumer = new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope,
    BasicProperties properties, byte[] body)
    throws IOException {
    String message = new String(body, "UTF-8");
    System.out.println("Received time:" + new DateTime().toString("yyyy-MM-dd HH:mm:ss:SSS EE")+ " the message is -> " + message);
    }
    };
    // 将消费者绑定到队列,并设置自动确认消息(即无需显示确认,如何设置请慎重考虑)
    channel.basicConsume(QUEUE_NAME, true, consumer);
    } catch (IOException e) {
    e.printStackTrace();
    } catch (TimeoutException e) {
    e.printStackTrace();
    }
    }
    }
    ]]>
    + + java + + + rabbitMQ + +
    + + Redis 开发与运维 + /20220523-redis_dev_ops/ + 零、背景

    鉴于目前 Redis 使用广泛
    虽然数据结构、API 比较简单
    但是想使用好,还是有一定难度
    建议了解下《Redis 开发与运维》
    以达到知其然且知其所以然的境界

    一、使用场景

    大规模互联网在线应用
    流量比较大,响应时间要求高
    Redis 作为一款流行的高性能数据库

    对于读多写少的场景,一般作为缓存使用
    某大型电商订单系统读写比大概在 10:1
    并且订单读取 90% 的流量都是创建订单当天查询

    对于可容忍丢数据,但是对性能有极致要求,
    比如优惠券发放,流量高,这种情况下当做 DB 用也挺好。

    二、问题与建议

    2.1 常见问题

    • 网络抖动导致 redis 操作失败
    • 定时任务清理过期 key 导致 IOPS 高
      • redis 做了二次开发,更激进的惰性删除( 针对大面积过期场景 )
    • 热 key 导致流量倾斜
      • 考虑 Server 端旁路监听,做统计,然后推送到客户端做内存缓存
      • 京东有开源热 Key 方案
    • redis slot 分配不均匀,导致某节点提前内存告警
      • 建议分配内存按 slot 分配,而不是节点

    2.2 建议

    • 缓存时间动态可配
      • 上线后根据业务灵活调整,节约资源
      • 大促过程中,扩容来不及,可以根据趋势缩短缓存时间,避免淘汰
    • 使用 ProtoStuff 之类的高效序列化工具
    • 使用 snappy 等高效压缩算法

    三、阅读摘抄

    《Redis开发与运维》20 1121~1122

    dbsize 获取当前数据库中键总数,复杂度 O(1)

    keys * 复杂度 O(n)

    type key 返回 redis 外部数据结构

    object encoding key 返回 redis 内部编码实现

    redis 为单线程模型,所有命令都进队列。

    redis 单线程为何还这么快?

    1. 纯内存访问,内存响应约 100 ns,快的基础
    2. 非阻塞 I/O,事件驱动
    3. 单线程避免了线程切换和竞态产生的消耗

    锁和线程切换通常是性能杀手。

    单线程可以简化数据结构和算法。

    由于是单线程,所以对每个命令执行时间有要求。
    redis 是面向快速执行场景的数据库。

    mget 结果是按照传入键的顺序返回。

    redis 中每个中文占 3 个字节
    随说:utf-8 汉字 3 ,ascii unicode 2

    字符串类型的内部编码有 3 种:

    1. int:8 字节的长整形
    2. embstr:≤ 39 字节的字符串
    3. raw:> 39 字节的字符串

    list 的 lrange 操作获取指定索引范围的元素。

    • 索引下标从左到右是 0~N-1
    • 索引下标从右到左是 -1~-N
    • 并且 end 包含了自身,很多编程语言不包含

    对于字符串类型的键,再次执行 set 命令会将上次的过期时间去掉。

    纯内存存储、I/O多路复用技术、单线程架构是 redis 高性能的三个因素。

    了解每个命令的时间复杂度在

    redis 命令真正执行的时间通常在微妙级别,所以才会有 redis 性能瓶颈是网络的说法。

    原生批命令与 pipeline 的区别:

    1. 原生批命令是原子的,pipeline 是非原子的
    2. 原生一命令多 key,pipeline 支持多命令
    3. 原生是 redis 服务端实现,pipeline 客户端+服务端 实现

    pipeline 只能操作一个 redis 实例,但即使在分布式 redis 场景中,也可以作为批量操作的重要优化手段。

    redis 3.2 版本提供了 geo 功能,支持 LBS 服务。
    geo 功能是 redis 借鉴了 ardb 功能实现,ardb 的作者来自中国。

    输出缓冲区由两部分组成:

    1. 固定缓冲区 16KB,返回比较小的执行结果
    2. 动态缓冲区,列表实现,返回较大的结果

    RESP(Redis Serialization Protocol Redis),协议。
    保证客户端与服务端的正常通信,是各种编程语言开发客户端的基础。

    jedis 没有内置序列化工具,需要自己选用。
    page 121 有使用 protostuff (protobuf的Java客户端)进行操作的例子。
    随说:这块 21 年出实际场景测试过,时间、空间复杂度优化幅度都蛮大

    理解 redis 通信原理和建立完善的监控系统对快速定位客户端常见问题非常有帮助。

    redis 默认采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小。

    正常情况下,fork 操作耗时应该是每 GB 消耗 20 毫秒左右。

    redis 是 CPU 密集型服务,不要做绑定单核 CPU 操作。
    由于子进程非常消耗 CPU,会和父进程产生单核资源竞争。

    子进程通过 fork 操作产生,理论上需要两倍的内存来完成持久化,
    但 Linux 有写时复制机制(copy-on-write),
    父子进程会共享相同的物理内存页,
    当父进程处理写请求时会把要修改的页创建副本,
    而子进程在 fork 操作过程中共享整个父进程内存快照。

    复制功能是高可用 redis 的基础。

    主从复制延迟,redis 提供了 repl-disable-tcp-nodelay 参数控制是否关闭,
    TCP_NODELAY,默认为 no,即开启 TCP_NODELAY 功能,
    这时主节点会合并较小的 TCP 数据包节省带宽但是增大了主从之间的延迟,
    适用于跨机房部署。如果为 yes,那么会立即发送,适合同机房网络。

    对于写并发量较高的场景,
    多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,
    同时也加重了主节点的负载影响服务稳定性。

    psync 2.8 以上支持,有全量复制,部分复制
    需要组件支持:

    1. 主从节点各自复制偏移量
    2. 主节点复制积压缓冲区
    3. 主节点运行 id

    redis 支持无盘复制,repl-diskless-sync,试验阶段,
    rdb 文件不保存到硬盘而直接通过网络发送给从节点。

    为了降低主从延迟,
    一般把 redis 主从部署在同机房 / 同城机房,
    避免网络延迟和网络分区造成的心跳中断等情况。

    对于读写分离,会造成从节点数据延迟,
    可以编写外部监控程序监听主从节点的复制偏移量,
    当延迟较大时出发报警或者通知客户端避免读取延迟过高的从节点。

    为了保证复制一致性,从节点自身永远不会主动删除超时数据。

    建议做读写分离之前,
    可以考虑使用 redis cluster 等分布式解决方案,
    这样不止扩展了读性能还可以扩展写性能和可支撑数据规模,
    并且一致性和故障转移也可以得到保证,
    对客户端的维护逻辑也相对容易。读写分离成本太高

    redis-cli –bigkeys
    把历史扫描过的最大对象统计出来,分析优化

    带宽瓶颈通常出现在以下几个方面:

    1. 机器网卡带宽
    2. 机架交换机带宽
    3. 房之间专线带宽

    网络快慢:同物理机 > 同机架 > 跨机架 > 同机房 > 同城机房 > 异地机房
    随说:但它们的容灾性正好相反

    redis 进程的内存消耗主要包括:自身内存 + 对象内存 + 缓冲内存 + 内存碎片。

    对象内存是 redis 内存占用最大的一块,存储着用户所有的数据。

    输入输出缓冲区在大流量的场景中容易失控,造成 redis 内存的不稳定,需要重点关注。

    由于进程内保存大量的键,
    维护每个键精准的过期删除机制会导致消耗大量的 CPU,
    对于单线程的 redis 来说成本过高,
    因此 redis 采用惰性删除和定时任务删除机制实现过期键的内存回收。

    在高并发放场景下,建议字符串长度控制在 39 字节以内,
    以减少 redisObject 内存分配次数,从而提高性能。

    尽量减少字符串频繁修改操作如 append、setrange,
    改为直接使用 set 修改字符串,降低预分配带来的内存浪费和内存碎片化。

    redis sentinel 有一套合理的监控机来判断节点不可达,有三个定时任务:

    1. 每 10s 向主节点发送 info 信息获取最新拓扑
    2. 每 2s 向 sentinel 频道发送对主节点的判断以及自身的信息
    3. 每 1s sentinel 向主节点、从节点、其余 sentinel 发送 ping 做心跳检测,来确认节点是否可达。

    redis 使用 raft 算法实现领导者的选举。

    redis sentinel 是 redis 的高可用方案实现:故障发现、故障自动转移、配置中心、客户端通知

    redis sentinel 模式下,客户端初始化连接的是 sentinel 节点集合,
    不再是具体的 redis 节点,但 sentinel 只是配置中心不是代理。

    redis cluster 是 redis 的分布式解决方案。当遇到单机内存、并发、流量等瓶颈时,
    可以采用 cluster 架构方案达到负载均衡的目的。

    redis cluster 之前的分布式方案:

    1. 客户端分片,客户端比较重,需要处理路由、高可用、故障转移等问题。
    2. 代理方案,部署麻烦,性能损耗。

    数据分区是分布式存储的核心,
    理解和灵活运用数据分区规则对于掌握 redis cluster 非常有帮助。

    redis cluster 限制:

    1. 批量操作支持有限,目前 mget、mset 只支持具有相同 slot 值的 key 执行批量操作。
    2. key 事务操作支持有限
    3. key为数据分区最小力度,hash list 在单机
    4. 不支持多数据库空间
    5. 复制结构只支持一层,不支持嵌套树状

    在分布式存储中需要提供维护节点元数据信息的机制,
    所谓元数据是指:节点负责哪些数据,是否出现故障等状态信息。

    常见的元数据维护方式分为:集中式、P2P方式

    redis cluster 采用 P2P 的 Gossip(流言) 协议,
    Gossip 协议工作原理就是节点彼此不断通信交换信息,
    一段时间之后所有节点都会知道集群完整信息。

    虽然 Gossip 协议的信息交换机制具有天然的分布式特性,但他是有成本的。
    redis 集群内节点通信频率为每秒 10次。单次通信消息 > 2KB,
    消息体携带的数据量根集群节点数息息相关,更大的集群代表更大的通信成本。
    因此对于 redis 集群来说并不是大而全的集群更好,需要对集群的规模做限制。
    随说:大规模集群可以考虑使用哨兵模式、或者拆分 redis cluster

    集群内 Gossip 消息通信本身会消耗带宽,官方建议集群最大规模在 1000 以内。

    redis cluster 可以实现对节点的灵活上下线控制。
    其中原理可以抽象为槽和对应数据在不同节点之间的灵活移动。

    集群伸缩=槽和数据在节点之间移动。

    MOVED 重定向:在集群模式下 redis 接收 key 相关命令先计算对应的槽,再根据槽找到节点,
    如果节点是自身,则处理命令,否则返回 MOVED 重定向错误,通知客户端请求正确的节点。

    键使用大括号包含的内容又叫做 hash_tag,它提供不同的键可以具备相同的 slot 功能,
    常用于 redis I/O 优化。在集群模式下实现 mget mset pipeline 等操作。

    smart 客户端通过在内部维护
    slot → node 的映射关系,本地就可以实现
    key → node 的查找,从而保证 I/O 效率低最大化,而 MOVED 重定向负责协助 smart 客户端更新 slot → node 映射。

    jediscluster 解析 cluster slots 结果缓存到本地,为每个节点创建唯一的 jedispool 连接池。

    cluster slots 风暴:

    1. 重试机制导致 I/O 通信放大问题
    2. 个别节点异常导致频繁获取 slots 信息
    3. 频繁触发更新本地 slots 缓存操作,内部用了写锁,阻塞对集群所有的命令调用。

    针对以上问题,jedis 2.8.2 版本做了改进:

    1. 当接收到 JedisConnectionException 时不再轻易初始化 slots 缓存,大幅降低内部 I/O 次数。
    2. 当更新 slots 缓存时,不再使用 ping 检测节点活跃度,并且使用 redis covering 变量保证同一时刻只有一个线程更新 slots 缓存,其它线程忽略,优化了写锁阻塞和 cluster slots 调用次数

    jedis cluster 中由于 key 分布在不同的节点上,会造成无法实现 mget、mset 等功能。
    但是可以利用 CRC16 计算出 key → slot,以及 smart 客户端保存 slot → node 的特性,
    将属于同一个 redis 节点的 key 进行归类,
    然后分别对每个节点对应的子 key 列表执行 mget 或者 pipeline 操作。

    ASK 与 MOVED 都是对客户端重定向控制,ASK 表示集群正在进行 slot 迁移,
    不知道什么时候完成。MOVED 明确表示 slot 对应的节点,因此需要更新 slot 缓存。

    配置纪元的应用场景有:

    1. 新节点加入
    2. 槽节点映射冲突检测
    3. 从节点投票选举冲突检测

    配置纪元的主要作用:

    1. 标示集群内每个主节点的不同版本和当前集群最大版本。
    2. 每次集群发生重要事件,都会递增纪元
    3. 大的纪元表示更新的状态

    唯品会开发的 redis-migrate-tool 支持在线迁移,
    采用多线程加速,提供数据校验和查看迁移状态等功能。
    随说:在 github 开源了

    缓存更新策略最佳实践

    1. 低一致性业务建议配置最大内存和淘汰策略
    2. 高一致性业务结合 expire 和主动更新

    缓存粒度问题

    1. 全部字段缓存通用易维护,报文大性能低
    2. 部分字段缓存通用性低,维护复杂,高效
      随说:考虑使用 hash 全量存,按需取

    使用布隆过滤器减少缓存击穿,page 351
    随说:订单业务很难做到,特别是订单量大

    无底洞:投入越多不一定产出越多

    1. 客户端一次批量操作会涉及多次网络开销,节点越多越耗时
    2. 网络连接数变多,对性能也会有影响

    无底洞优化

    1. 命令本身的优化,例如优化 SQL 语句
    2. 减少网络通信次数
    3. 降低接入成本,使用连接池 / NIO 等
    4. 客户端 n 次 get,n 次网络 + n 次 get 命令
    5. 客户端 1 次 pipeline get,1 网络 + n get
    6. 客户端 1 次 mget,1 网络 + 1 mget
      随说:以上针对单个 redis 节点,非 cluster

    NTP 是一种保证不同时钟一致性的服务。

    redis cluster | sentinel 如果多个节点时间不一致,
    会影响日志排查,但不会影响功能,节点依赖各自的时钟。

    redis 公网无密码情况下,黑客可以写入公钥,通过设置 rdb 文件目录到 /root/.ssh 目录,
    并且文件名设置为 authorized_keys 即可实现免密登录。爆破主机。

    redis bind 参数指定的是 redis 和哪个网卡绑定(建议绑定内网),和客户端什么网段没有关系。

    bigkey,一般字符串类型 value 超过 10KB 就是 bigkey 了,但这个值和具体的 OPS 相关。

    redis 查看 key 的大小:debug object key
    看其中的 serializedlength 即可。

    ]]>
    + + database + + + java + redis + +
    + + MySQL分页查询 + /20200712-mysql-page-query/ + 零、分页查询

    分页查询一般都会想到 offset limit

    问题1

    当不停机需要全量同步数据时
    有可能会漏掉或者重复处理很多数据
    因为中途会有数据改动
    每次分页内的数据与预期的会有出入

    问题2

    offset 会进行全表扫描

    一、推荐方案

    通过不变的字段进行排序
    最好是使用递增的主键索引

    select *
    from hisen
    where id > ${lastId}
    order by id
    limit ${pageSize};

    二、参考

    请不要将OFFSET和LIMIT用于分页

    ]]>
    + + mysql + + + mysql + +
    + + Java 日志配置文件 - self4j 之 logback 和 log4j + /20200406-self4j-logback-log4j-log-properties/ + 写这种博客总感觉很低级
    但是不得不承认自己从来没有认真的了解过打日志这件事
    这里开个头,先从常见的日志配置文件开始,后续再深入了解

    “I wish i had” vs “I’m glad i did”
    人生会不会留下遗憾,在 20 年之后的这两句话中就能看出。
    希望 20 年后的自己,能够很自豪的说:I’m glad i did.

    一、self4j

    这是一个只定义了标准的日志组件

    二、logback

    常见配置:logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
    <property name="APP" value="hisenyuan-log-project"/>
    <!--文件输出-->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--存储路径-->
    <file>/export/log/${APP}/${APP}_detail.log</file>
    <!--按天分割-->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <FileNamePattern>/www/log/${APP}/${APP}_detail.%d{yyyy-MM-dd}.log</FileNamePattern>
    </rollingPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0} %X{trace-id} - %m%n</pattern>
    </encoder>
    </appender>
    <!--控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0} %X{trace-id} - %m%n</pattern>
    </encoder>
    </appender>

    <!--你想排除xxx包下面的日志-->
    <logger name="kafka.utils.Logging" additivity="false">
    <appender-ref ref="FILE"/>
    </logger>
    <!--允许WARN以及以上的打印-->
    <logger name="org.apache.http" additivity="false">
    <level value="WARN" />
    <appender-ref ref="FILE"/>
    </logger>

    <!--日志级别-->
    <root level="INFO">
    <!--启用日志文件打印日志-->
    <appender-ref ref="FILE"/>
    <!---->
    <appender-ref ref="CONSOLE"/>
    </root>
    </configuration>

    三、log4j

    常见配置:log4j.xml

    <?xml version='1.0' encoding='UTF-8' ?>
    <!DOCTYPE log4j:configuration SYSTEM
    "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
    <log4j:configuration>
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{1} - %m%n"/>
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
    <param name="LevelMin" value="INFO"/>
    <param name="LevelMax" value="ERROR"/>
    </filter>
    </appender>

    <appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
    <param name="DatePattern" value="'.'yyyy-MM-dd"/>
    <param name="Append" value="true"/>
    <param name="file" value="/www/log/hisenyuan-log-project.log"/>
    <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{1} - %m%n"/>
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
    <param name="LevelMin" value="INFO"/>
    <param name="LevelMax" value="ERROR"/>
    </filter>
    </appender>

    <logger name="kafka.utils.Logging" additivity="false">
    <appender-ref ref="FILE"/>
    </logger>

    <logger name="org.apache.http" additivity="false">
    <appender-ref ref="FILE"/>
    </logger>

    <root>
    <priority value="INFO"/>
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="FILE"/>
    </root>

    </log4j:configuration>

    ]]>
    + + java + + + java + +
    + + shell & expect 实现 MySQL 命令行自动登录 + /20220603-shell-and-expect-for-mysql-auto-login/ + 0. 背景

    由于目前经常需要登录数据库查询相关数据
    每次进行登录一系列操作,有点费劲
    于是乎想着看看怎么自动化

    限制:需要在堡垒机进行操作

    1. 知识

    1.1 expcet

    expect是一个自动化交互套件,
    主要应用于执行命令和程序时,
    系统以交互形式要求输入指定字符串,实现交互通信。

    1.2 awk

    Awk是一种便于使用且表达能力强的程序设计语言,
    可应用于各种计算和数据处理任务。
    入门指南

    2. 脚本

    2.1 自动登录脚本

    说明:基于 expect
    文件:mysqlLogin.sh

    #!/usr//bin/expect -f
    set ip [lindex $argv 0]
    set port [lindex $argv 1]
    set user [lindex $argv 2]
    set password [lindex $argv 3]

    spawn mysql -h$ip -u$user -P$port -p
    set timeout 1000
    expect "Enter password:"
    send "$password\r"
    expect ">"
    send "use xxx_database\r"
    interact

    2.2 获取信息并登录

    说明:通过 shell 动态获取登录信息
    文件:autologin.sh

    #!/usr/bin/bash
    ips=`get_single_ip_by_domain hisen.me`
    ip=($(echo $ips | awk '{print $2}'))
    port=($(echo $ips | awk '{print $3}'))
    echo "+---------------------------+"
    echo "| Login to MySQL offline |"
    echo "+---------------------------+"
    # 调用上面的脚本,包含 4 个参数
    expect /root/soft/login/mysqlLogin.sh $ip $port user pwd

    2.3 快捷命令设置

    设置完成之后,输入 ma 命令,即可自动登录到 MySQL 客户端

    cat ~/.bashrc
    alias ma='sh /root/soft/login/autologin.sh'

    3. 参考文档

    ]]>
    + + shell + + + mysql + shell + +
    + + sql行转列,列转行 + /20170613-sql%E8%A1%8C%E8%BD%AC%E5%88%97%EF%BC%8C%E5%88%97%E8%BD%AC%E8%A1%8C/ + 有时候需要行转列或者列转行

    在Oracle 11g中,Oracle 又增加了2个查询:pivot(行转列) 和unpivot(列转行)

    下面是在mysql中的操作:

    mysql> select * from test;
    +------+------+--------+
    | id | age | weigth |
    +------+------+--------+
    | 1 | 1 | 3 |
    | 1 | 2 | 2 |
    | 1 | 3 | 1 |
    | 2 | 5 | 6 |
    +------+------+--------+
    4 rows in set (0.00 sec)

    #根据ID分组
    mysql> select group_concat(age) from test group by id;
    +-------------------+
    | group_concat(age) |
    +-------------------+
    | 1,2,3 |
    | 5 |
    +-------------------+
    2 rows in set (0.01 sec)

    #不分组
    mysql> select group_concat(age) from test;
    +-------------------+
    | group_concat(age) |
    +-------------------+
    | 1,2,3,5 |
    +-------------------+
    1 row in set (0.00 sec)

    ]]>
    + + sql + + + mysql + oracle + +
    + + 计算机科学的自我修炼 + /20210219-teach-or-improve-yourself-computer-science/ + 零、背景

    在近两年高并发系统 DevOps 的过程中,
    遇到了很多底层的问你题,eg: 网络、硬件、虚拟机等,
    有些现象虽然知其然,但是不知其所以然,书到用时方恨少!
    抱着深入学习的心态这两年看了一些相关书籍:

    《计算机网络:自顶向下方法》
    《操作系统精髓与设计原理》
    《Java性能优化权威指南》
    《Redis运维与开发》
    《性能之巅》

    收获甚大 所以想继续深入学习
    误打误撞,看到了之前在 GitHub 关注的一个『自学计算机科学』仓库,很赞同下面这个观点

    软件工程师分为两种:

    1. 一种充分理解了计算机科学,从而有能力应对充满挑战的创造性工作;
    2. 另一种仅仅凭着对一些高级工具的熟悉而勉强应付。

    这两种人都自称软件工程师,都能在职业生涯早期挣到差不多的工资。
    然而,随着时间流逝,第一种工程师不断成长,所做的事情将会越来越有意义且更为高薪,
    不论是有价值的商业工作、突破性的开源项目、技术上的领导力或者高质量的个人贡献。

    一、资源

    1.1 摘要

    科目为何要学最佳书籍最佳视频
    编程不要做一个“永远没彻底搞懂”诸如递归等概念的程序员。《计算机程序的构造和解释》Brian Harvey’s Berkeley CS 61A
    计算机架构如果你对于计算机如何工作没有具体的概念,那么你所做出的所有高级抽象都是空中楼阁。《深入理解计算机系统》Berkeley CS 61C
    算法与数据结构如果你不懂得如何使用栈、队列、树、图等常见数据结构,遇到有难度的问题时,你将束手无策。《算法设计手册》Steven Skiena’s lectures
    数学知识计算机科学基本上是应用数学的一个“跑偏的”分支,因此学习数学将会给你带来竞争优势。《计算机科学中的数学》Tom Leighton’s MIT 6.042J
    操作系统你所写的代码,基本上都由操作系统来运行,因此你应当了解其运作的原理。《操作系统导论》Berkeley CS 162
    计算机网络互联网已然势不可挡:理解工作原理才能解锁全部潜力。《计算机网络:自顶向下方法》Stanford CS 144
    数据库对于多数重要程序,数据是其核心,然而很少人理解数据库系统的工作原理。《Readings in Database Systems》 (暂无中译本)Joe Hellerstein’s Berkeley CS 186
    编程语言与编译器若你懂得编程语言和编译器如何工作,你就能写出更好的代码,更轻松地学习新的编程语言。《Crafting Interpreters》Alex Aiken’s course on Lagunita
    分布式系统如今,多数 系统都是分布式的。《数据密集型应用系统设计》MIT 6.824

    1.2 详情

    内容值得一看,真心建议多花功夫学好底层知识。
    原文:Teach Yourself Computer Science
    翻译:自学计算机科学

    来自亚马逊 CTO 的博文也值得一看,操作系统经典书籍
    The OS Classics

    三、相关资源

    软件工程师面试指引

    ]]>
    + + 成长 + 计算机科学 + + + cs + +
    + + resteasy fastjson版本冲突问题 + /20190804-resteasy%20fastjson%E7%89%88%E6%9C%AC%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/ + 零、相关介绍

    关键错误信息:
    java.lang.RuntimeException: RESTeasy Provider Factory is null, do you have the ResteasyBootstrap listener configured?
    java.lang.RuntimeException: Illegal to inject a message body into a singleton into public com.alibaba.fastjson.support.jaxrs.FastJsonProvider(java.lang.String)

    resteasy:JBoss的一个开源项目,提供一套完整的框架帮助开发人员构建RESTful Web Service和RESTful Java应用程序。
    fastjson:由阿里开发的一个性能很好的Java JSON 解析器和生成器。

    引起错误的依赖

    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.58</version><!--改为:1.1.34.sec01相安无事-->
    </dependency>
    <dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxrs</artifactId>
    <version>2.2.1.GA</version>
    </dependency>
    <dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>jaxrs-api</artifactId>
    <version>2.2.1.GA</version>
    </dependency>
    <dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-spring</artifactId>
    <version>2.2.1.GA</version>
    </dependency>

    一、错误日志

    本地启动Server报错

    2019-08-03 11:51:22,491 ERROR - [RMI TCP Connection(2)-127.0.0.1] - Context initialization failed
    java.lang.RuntimeException: RESTeasy Provider Factory is null, do you have the ResteasyBootstrap listener configured?
    at org.jboss.resteasy.plugins.spring.SpringContextLoaderSupport.customizeContext(SpringContextLoaderSupport.java:53)
    at org.jboss.resteasy.plugins.spring.SpringContextLoader.customizeContext(SpringContextLoader.java:30)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:382)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.jboss.resteasy.plugins.spring.SpringContextLoaderListener.contextInitialized(SpringContextLoaderListener.java:44)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5197)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5720)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1018)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662)
    at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1899)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:619)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:566)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
    at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1427)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
    at sun.rmi.transport.Transport$2.run(Transport.java:202)
    at sun.rmi.transport.Transport$2.run(Transport.java:199)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

    tomcat localhost long

    八月 03, 2019 11:51:22 上午 org.apache.catalina.core.ApplicationContext log
    信息: No Spring WebApplicationInitializer types detected on classpath
    八月 03, 2019 11:51:22 上午 org.apache.catalina.core.StandardContext listenerStart
    严重: Exception sending context initialized event to listener instance of class org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
    java.lang.RuntimeException: java.lang.RuntimeException: Unable to instantiate MessageBodyReader
    at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:35)
    at org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:211)
    at org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap.contextInitialized(ResteasyBootstrap.java:28)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5197)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5720)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1018)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662)
    at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1899)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:619)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:566)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
    at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1427)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
    at sun.rmi.transport.Transport$2.run(Transport.java:202)
    at sun.rmi.transport.Transport$2.run(Transport.java:199)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
    Caused by: java.lang.RuntimeException: Unable to instantiate MessageBodyReader
    at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:760)
    at org.jboss.resteasy.plugins.providers.RegisterBuiltin.registerProviders(RegisterBuiltin.java:70)
    at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:31)
    ... 51 more
    Caused by: java.lang.RuntimeException: Illegal to inject a message body into a singleton into public com.alibaba.fastjson.support.jaxrs.FastJsonProvider(java.lang.String)
    at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:209)
    at org.jboss.resteasy.core.ConstructorInjectorImpl.injectableArguments(ConstructorInjectorImpl.java:63)
    at org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:129)
    at org.jboss.resteasy.spi.ResteasyProviderFactory.getProviderInstance(ResteasyProviderFactory.java:1038)
    at org.jboss.resteasy.spi.ResteasyProviderFactory.addMessageBodyReader(ResteasyProviderFactory.java:478)
    at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:756)
    ... 53 more

    八月 03, 2019 11:51:22 上午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring root WebApplicationContext
    八月 03, 2019 11:51:22 上午 org.apache.catalina.core.StandardContext listenerStart
    严重: Exception sending context initialized event to listener instance of class org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
    java.lang.RuntimeException: RESTeasy Provider Factory is null, do you have the ResteasyBootstrap listener configured?
    at org.jboss.resteasy.plugins.spring.SpringContextLoaderSupport.customizeContext(SpringContextLoaderSupport.java:53)
    at org.jboss.resteasy.plugins.spring.SpringContextLoader.customizeContext(SpringContextLoader.java:30)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:382)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.jboss.resteasy.plugins.spring.SpringContextLoaderListener.contextInitialized(SpringContextLoaderListener.java:44)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5197)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5720)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1018)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662)
    at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1899)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:619)
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:566)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
    at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1427)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
    at sun.rmi.transport.Transport$2.run(Transport.java:202)
    at sun.rmi.transport.Transport$2.run(Transport.java:199)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

    三、解决办法

    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.1.34.sec01</version><!--改为:1.1.34.sec01相安无事-->
    </dependency>
    <dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxrs</artifactId>
    <version>2.2.1.GA</version>
    </dependency>
    <dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>jaxrs-api</artifactId>
    <version>2.2.1.GA</version>
    </dependency>
    <dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-spring</artifactId>
    <version>2.2.1.GA</version>
    </dependency>

    四、细节说明

    暂时不知道引起的原因,后期补充

    ]]>
    + + java + + + java + +
    + + 缓存那些事儿 - 从组件到实践 + /20190429-things-about-caching/ + 一、为什么用缓存

    1.1 空间换时间:

    缓存是针对读多写少的场景典型的以空间换时间的操作
    空间:内存
    时间:读内存速度快(相对于读磁盘)

    1.2 局部性原理:

    这个世界很多事情都符合 2/8 原则
    把热点数据缓存起来就大大提高系统效率

    二、缓存组件介绍

    2.1 Ehcache

    1. 快速,针对大型高并发系统场景,Ehcache 的多线程机制有相应的优化改善;
    2. 简单,很小的 jar 包,简单配置就可直接使用,单机场景下无需过多的其他服务依赖;
    3. 支持多种的缓存策略,灵活;
    4. 缓存数据有两级:内存和磁盘,与一般的本地内存缓存相比,有了磁盘的存储空间,将可以支持更大量的数据缓存需求;
    5. 具有缓存和缓存管理器的侦听接口,能更简单方便的进行缓存实例的监控管理;
    6. 支持多缓存管理器实例,以及一个实例的多个缓存区域;

    2.2 Guava

    1. 自动将 entry 节点加载进缓存结构中;
    2. 当缓存的数据超过设置的最大值时,使用 LRU 算法移除;
    3. 具备根据 entry 节点上次被访问或者写入时间计算它的过期机制;
    4. 缓存的 key 被封装在 WeakReference 引用内;
    5. 缓存的 Value 被封装在W eakReference 或 SoftReference 引用内;
    6. 统计缓存使用过程中命中率、异常率、未命中率等统计数据;

    2.3 Memcache

    1. memcache 使用预分配内存池的方式管理内存;
    2. 所有数据存储在物理内存里;
    3. 非阻塞 I/O 复用模型,纯 KV 存取操作;
    4. 多线程,效率高,会遇到锁等,上下文切换问题;
    5. 只支持简单 KV 数据类型;
    6. 数据不支持持久化;

    2.4 Redis

    1. 临时申请空间,可能导致碎片;
    2. 有 VM 机制,能存储更多数据,超过内存空间后会导致 swap,降低效率;
    3. 非阻塞 I/O 复用模型,支持额外 CPU 计算:排序、聚合,会影响 I/O 性能;
    4. 单线程,无锁,无上下文切换,单实例无法利用多核性能;
    5. 支持多种数据类型:string / hash / list / set / sorted set
    6. 数据支持持久化:AOF(语句增量) / RDB(fork全量)
    7. 天然支持高可用分布式方案 sentinel + cluster (故障自动转移+集群)

    三、正确使用缓存

    3.1 读场景

    先读缓存、再读DB
    如果是并发读缓存失效,使用分布式锁只允许单次查询,其它等待,超时返回失败

    3.2 写场景

    cache 指的是删除『缓存』
    db 指的是『更新/删除数据库』

    3.2.1 先 cache 后 db

    删除缓存成功,更新数据库失败,不影响数据准确性。
    如果在 cache 与 db 短暂的时间内
    有访问查询动作(先查缓存,后查 db 并且设置缓存)
    那么还缓存中还是会存在过期的数据

    3.2.2 先 db 后 cache

    如果 db 成功,cache 失败,会导致数据不一致。
    可以让 db cache 在一个事务,cache 失败回滚 db,保证一致性。

    3.2.3 方案总结

    其实本质上就是个分布式事务问题
    怎么保证两个操作同时成功/失败
    通过本地事务 / 补偿机制 实现会比较好

    四、缓存问题集合

    4.1 缓存穿透

    解决方案:当查询到某数据不存在时,缓存假数据,例如:(key,key#);

    4.2 缓存雪崩

    缓存高可用(集群+主备)
    循环一致性 Hash,节点组成一个环,如果一个节点挂了,顺着往下走;

    五、参考链接

    1. 架构师之路18年精选100篇 | 缓存部分 - 58沈剑
    2. 缓存那些事 | 美团技术博客
    ]]>
    + + java + + + java + cache + +
    + + su Authentication failure + /20170401-su%20%20Authentication%20failure/ + 想要获取root权限,提示如下

    hisen@ubuntu:/var/lib$ su
    Password:
    su: Authentication failure

    解决办法

    hisen@ubuntu:$ sudo passwd root
    Enter new UNIX password:
    Retype new UNIX password:
    passwd: password updated successfully
    hisen@ubuntu:$ su
    Password:
    root@ubuntu:# cd mysql

    重新设置一下密码即可,我这边装的时候设置的用户是:hisen

    刚刚重新设置的密码就是你装系统的时候设置的用户密码。

    ]]>
    + + linux + + + linux + +
    + + 二进制的应用 + /20210726-use-of-binary/ + 一、背景

    之前京东组里有同事使用二进制优化支付密码打标性能(大促 QPS 数百万),节省内存资源。
    随说:存二进制报文小,传输快,反序列化快(之前存 JSON 对象),节省缓存。

    目前公司遇到个套餐打标,也通过二进制实现简单高效得解决掉了。
    随说:目前倒不是要求性能,只是这么设计扩展性好,操作简单。

    知识点:二进制、与运算

    二、设计

    打标,无非就是识别某个东西是不是包含某些属性。
    那么有什么好的办法能做到通用与高效?
    如果固定映射,扩展性不好,查询逻辑费劲,存储成本偏高。

    目前相对较好的方案是通过二进制位来做标记,再结合与运算,快速找出数据。
    随说:Java MySQL 均支持与运算

    套餐VIP1VIP2VIP3标记值
    A1117
    B1106
    C1004

    如上表所示,相应套餐的购买资格标记。

    A 套餐所有会有均可购买
    B 套餐 VIP3 不能购买
    C 套餐仅 VIP1 可购买

    对相关标记进行入库处理:A=7,B=6,C=4;

    正常思维,需要存三个字段,没有扩展性,性能还差。
    使用二进制,结合与运算,降本(计算、传输、匹配)增效(性能提升)。

    三、代码

    public static void main(String[] args) {
    Integer a = 7;
    Integer b = 6;
    Integer c = 4;
    List<Integer> suit = Lists.newArrayList(a, b, c);
    System.out.printf("支持 VIP1 (0100=4)的套餐:" + Arrays.toString(suit.stream().filter(e -> (4 & e) != 0).toArray()));
    System.out.printf("支持 VIP2 (0010=2)的套餐:" + Arrays.toString(suit.stream().filter(e -> (2 & e) != 0).toArray()));
    System.out.printf("支持 VIP3 (0001=1)的套餐:" + Arrays.toString(suit.stream().filter(e -> (1 & e) != 0).toArray()));

    // 支持 VIP1 (0100=4)的套餐:[7, 6, 4]
    // 支持 VIP2 (0010=2)的套餐:[7, 6]
    // 支持 VIP3 (0001=1)的套餐:[7]
    }

    四、总结

    系统流量小的时候
    粗糙烂制也不是不能用
    但是当系统流量大了就得想办法优化:CPU、传输、存储
    很多时候往往利用简单的原理解决大的问题,只是很多时候限于认知不知道可以这么用。

    说到底还是要知其然,更要知其所以然。

    ]]>
    + + java + + + java + +
    + + try redis - redis官方教程练习 + /20170519-try%20redis%20-%20redis%E5%AE%98%E6%96%B9%E6%95%99%E7%A8%8B%E7%BB%83%E4%B9%A0/ + 这里主要是介绍了几种redis支持的数据结构,以及操作方法

    官网地址:http://try.redis.io/

    我的redis是安装在linux虚拟机,通过Xshell操作,显示可能跟cmd不大一样

    但是操作都是一样的

    具体操作如下:

    #连接redis客户端
    hisen@ubuntu:~$ redis-cli
    #存放数据
    127.0.0.1:6379> set 'connections' '10'
    OK
    127.0.0.1:6379> get connections
    "10"
    #自增1方法:incr
    127.0.0.1:6379> incr connections
    (integer) 11
    127.0.0.1:6379> incr connections
    (integer) 12
    #删除
    127.0.0.1:6379> del conncetions
    (integer) 0
    #不存在才存放数据 setnx:SET-if-not-exists
    127.0.0.1:6379> SETNX hisen hello
    (integer) 1
    127.0.0.1:6379> get hisen
    "hello"
    127.0.0.1:6379> SETNX hisen hello
    (integer) 0
    #添加数据
    127.0.0.1:6379> set resource:lock "redis demo"
    OK
    #设置超时时间,单位秒
    127.0.0.1:6379> expire resource:lock 120
    (integer) 1
    #查看剩余时间
    127.0.0.1:6379> ttl resource:lock
    (integer) 85
    127.0.0.1:6379> ttl resource:lock
    (integer) 81
    127.0.0.1:6379> ttl resource:lock
    (integer) 57

    #List集合
    #放在list最后(right 右边)
    127.0.0.1:6379> rpush friends "Alice"
    (integer) 1
    127.0.0.1:6379> rpush friends "Bob"
    (integer) 2
    #放在最前(left 左边)
    127.0.0.1:6379> lpush friends "Sam"
    (integer) 3
    #获取所有数据
    127.0.0.1:6379> lrange friends 0 -1
    1) "Sam"
    2) "Alice"
    3) "Bob"
    #获取下标0-1的数据
    127.0.0.1:6379> lrange friends 0 1
    1) "Sam"
    2) "Alice"
    #获取下标1-2的数据
    127.0.0.1:6379> lrange friends 1 2
    1) "Alice"
    2) "Bob"
    #获取长度
    127.0.0.1:6379> llen friends
    (integer) 3
    #删除第一个数据(左边)
    127.0.0.1:6379> lpop friends
    "Sam"
    #删除最后一个数据(右边)
    127.0.0.1:6379> rpop friends
    "Bob"
    127.0.0.1:6379> llen friends
    (integer) 1
    #输出所有
    127.0.0.1:6379> lrange friends 0 -1
    1) "Alice"

    #Set集合
    #添加
    127.0.0.1:6379> sadd superpowers "flight"
    (integer) 1
    127.0.0.1:6379> sadd superpowers "x-ray vision"
    (integer) 1
    127.0.0.1:6379> sadd superpowers "reflexes"
    (integer) 1
    #删除
    127.0.0.1:6379> srem superpowers "reflexes"
    (integer) 1
    #判断数据是否存在set中
    127.0.0.1:6379> sismember superpowers "flight"
    (integer) 1
    #输出所有
    127.0.0.1:6379> smembers superpowers
    1) "flight"
    2) "x-ray vision"

    127.0.0.1:6379> sadd birdpowers "pecking"
    (integer) 1
    127.0.0.1:6379> sadd birdpowers "flight"
    (integer) 1
    #合并两个SET,会过滤重复
    127.0.0.1:6379> sunion superpowers birdpowers
    1) "pecking"
    2) "flight"
    3) "x-ray vision"

    #有序集合,按照数字排序
    127.0.0.1:6379> zadd hackers 1940 "Alan Kay"
    (integer) 1
    127.0.0.1:6379> zadd hackers 1906 "Grace Hopper"
    (integer) 1
    127.0.0.1:6379> zadd hackers 1953 "Richard Stallman"
    (integer) 1
    127.0.0.1:6379> zadd hackers 1965 "Yukihiro Mastsumoto"
    (integer) 1
    127.0.0.1:6379> zadd hackers 1916 "Claude Shannon"
    (integer) 1
    127.0.0.1:6379> zadd hackers 1969 "Linus Torvalds"
    (integer) 1
    127.0.0.1:6379> ZADD hackers 1957 "Sophie Wilson"
    (integer) 1
    127.0.0.1:6379> ZADD hackers 1912 "Alan Turing"
    (integer) 1
    #输出
    127.0.0.1:6379> zrange hackers 2 4
    1) "Claude Shannon"
    2) "Alan Kay"
    3) "Richard Stallman"
    127.0.0.1:6379> zrange hackers 0 -1
    1) "Grace Hopper"
    2) "Alan Turing"
    3) "Claude Shannon"
    4) "Alan Kay"
    5) "Richard Stallman"
    6) "Sophie Wilson"
    7) "Yukihiro Mastsumoto"
    8) "Linus Torvalds"
    127.0.0.1:6379>

    #哈希集合
    127.0.0.1:6379> hset user:1000 name "hisen"
    (integer) 1
    127.0.0.1:6379> hset user:1000 email "hisen@hisen.com"
    (integer) 1
    127.0.0.1:6379> hset user:1000 pwassword "pswd"
    (integer) 1
    127.0.0.1:6379> hgetall user:1000
    1) "name"
    2) "hisen"
    3) "email"
    4) "hisen@hisen.com"
    5) "pwassword"
    6) "pswd"

    #自增
    27.0.0.1:6379> hset user:1000 visits 10
    (integer) 1
    127.0.0.1:6379> hset user:1000 visits 1
    (integer) 0
    127.0.0.1:6379> hincrby user:1000 visits 1
    (integer) 2
    127.0.0.1:6379> hincrby user:1000 visits 1
    (integer) 3
    127.0.0.1:6379> hincrby user:1000 visits 10
    (integer) 13
    127.0.0.1:6379> hdel user:1000 visits
    (integer) 1
    127.0.0.1:6379> hincrby user:1000 visits 1
    (integer) 1

    ]]>
    + + sql + + + java + sql + +
    + + ubuntu安装docker-ce并配置国内源和加速器 + /20170417-ubuntu%E5%AE%89%E8%A3%85docker-ce%E5%B9%B6%E9%85%8D%E7%BD%AE%E5%9B%BD%E5%86%85%E6%BA%90%E5%92%8C%E5%8A%A0%E9%80%9F%E5%99%A8/ + 一、配置ubuntu国内镜像,这里推荐阿里云,右上角搜索:换阿里云源

    二、安装docker

    sudo apt-get update
    sudo apt-get install \
    linux-image-extra-$(uname -r) \
    linux-image-extra-virtual

    安装docker包

    sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

    添加docker官方GPG秘钥,留意最后那个符号也要复制

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

    安装稳定版仓库

    sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

    再次更新源

    sudo apt-get update

    安装docker-ce

    sudo apt-get install docker-ce

    三、给docker添加国内加速器

    在阿里云申请一个账号,打开连接https://cr.console.aliyun.com/#/accelerator

    拷贝您的专属加速器地址(每个人专属的,登陆需要密码),然后

    vi /etc/systemd/system/multi-user.target.wants/docker.service

    可以看到如下内容

    [Service]
    Type=notify
    # the default is not to use systemd for cgroups because the delegate issues still
    # exists and systemd currently does not support the cgroup feature set required
    # for containers run by docker
    #下面这行是默认的,我注释了,添加了下面一行
    #ExecStart=/usr/bin/dockerd -H fd://
    ExecStart=/usr/bin/dockerd -H fd:// --registry-mirror=https://9s3ekxxx.mirror.aliyuncs.com
    ExecReload=/bin/kill -s HUP $MAINPID
    LimitNOFILE=1048576

    找到 ExecStart= 这一行,在这行最后添加加速器地址 –registry-mirror=<加速器地址>

    如:ExecStart=/usr/bin/dockerd -H fd:// –registry-mirror=https://xxxxxx.mirror.aliyuncs.com

    四、重新加载配置并且重新启动

    $ sudo systemctl daemon-reload
    $ sudo systemctl restart docker

    至此docker安装及国内加速器都好了,开始你的docker之旅吧。

    sudo docker run hello-world

    看到如下信息

    hisen@ubuntu:/$ sudo docker run hello-world
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    78445dd45222: Pull complete
    Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
    Status: Downloaded newer image for hello-world:latest

    Hello from Docker!
    This message shows that your installation appears to be working correctly.

    To generate this message, Docker took the following steps:
    1. The Docker client contacted the Docker daemon.
    2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
    4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

    To try something more ambitious, you can run an Ubuntu container with:
    $ docker run -it ubuntu bash

    Share images, automate workflows, and more with a free Docker ID:
    https://cloud.docker.com/

    For more examples and ideas, visit:
    https://docs.docker.com/engine/userguide/

    到此就圆满结束

    最后给个彩蛋,阿里云一键安装脚本,执行下面命令即可安装最新版docker

    curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

    详情:http://mirrors.aliyun.com/help/docker-engine

    ]]>
    + + linux + + + docker + ubuntu + +
    + + 关于部署软系统的思考 + /20200627-thinking-of-deploy-soft-system/ + 在 敏捷 + devops 盛行的年代
    软件部署的频率一般都比较高,一周一迭代、一周两迭代
    这就需要有良好的工具来支持,更需要良好的机制来避免人为犯错

    工作当中会遇到很多部署导致的问题
    其中大部分的问题原因是配置问题

    关于部署

    1. 开发的时候记录大致改动点
    2. 上线之前按检查列表(团队或个人定义一些常规检查项目)挨个检查
    3. 上线之前写上线步骤列表,部署时做一个人肉机器人,按步骤操作即可
    4. 上线之前写好验证步骤,验证需要的各种权限
    5. 以上做完了最好找相关人员核对一遍,以免遗漏(互备很重要)
    6. 灰度发布(应用启动期间性能抖动问题值得研究)

    互备很重要,哪怕对方没有介入开发,简单的按正常流程问你几个问题,也许就会发现漏了什么。

    ]]>
    + + soft + + + soft + +
    + + zookeeper & dubbo搭建 - 在IDEA上运行阿里巴巴Dubbo-demo + /20170419-zookeeper%20&%20dubbo%E6%90%AD%E5%BB%BA%20-%20%E5%9C%A8IDEA%E4%B8%8A%E8%BF%90%E8%A1%8C%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Dubbo-demo/ +

    IDEA上搭建dubbo服务的简单过程

    只是简单的让例子在IntelliJ IDEA跑起来

    目前是最新的版本:2.5.4-SNAPSHOT

    本文档更新时间:2017年04月19日01:08:02

    一 、安装zookeeper

    参考链接:ubuntu apt-get安装zookeeper

    二、Idea clone本项目

    导出项目之后,配置一下tomcat,添加dubbo-admin:war到tomcat中

    项目github地址:https://github.com/hisen-yuan/dubbo

    三、启动tomcat,即可访问dubbo管理后台

    默认账号:root

    默认密码:root

    四、启动服务提供者&消费者demo

    1. 修改dubbo-demo-consumer配置文件中的注册中心地址
      /dubbo/dubbo-demo/dubbo-demo-consumer/src/test/resources/dubbo.properties
    #dubbo.registry.address=multicast://224.5.6.7:1234
    #使用本地的zookeeper做注册中心
    dubbo.registry.address=zookeeper://127.0.0.1:2181
    1. 修改ubbo-demo-provider配置文件中的注册中心地址
      /dubbo/dubbo-demo/dubbo-demo-provider/src/test/resources/dubbo.properties
    #dubbo.registry.address=multicast://224.5.6.7:1234
    #使用本地的zookeeper做注册中心
    dubbo.registry.address=zookeeper://127.0.0.1:2181
    1. 分别启动dubbo-demo下ubbo-demo-provider、dubbo-demo-consumer下的测试方法

    即可在后台看到有服务在运行

    ]]>
    + + java + + + java + zookeeper + dubbo + +
    + + Redis Cluster slot 分布不均匀问题排查 + /20210518-troubleshooting-unbalanced-of-redis-cluster-slots/ + 零、背景

    在压测过程中发现有部分 redis cluster 节点内存占用比其它节点高(来自监控)

    内存倾斜的隐患

    1. 更早达到容量瓶颈,触发淘汰策略
    2. 承担更多的负载( 读 / 写 ),导致节点压力增大,可能触发宕机。

    一、问题

    redis cluster slot 分配不均匀
    redis cluster 集群内存分配算法的缺陷
    问题算法:单节点内存 = 集群总内存 / 节点数

    合理算法:单节点内存 = (集群总内容 / slot 数量) * 当前节点 slot 数量

    二、排查

    2.1 key 分布问题?

    key 根据 crc16 计算之后比较均匀,排除 key 分布不均
    在 key 均匀的情况下,考虑 slot 分配问题

    2.2 slot 分布问题?

    查找 slot 分布
    redis 控制台执行:cluster slots
    取最后一个节点的 rely 日志如下(日志含义见参考链接)

    (0)(0)10137
    (1)10463
    (2)(0)192.25.238.97
    (1)2048
    (2)1ab1735417e34e1fb38aa19958e2375498158be8
    (3)(0)192.25.239.118
    (1)2049
    (2)2b444bb8ece6edcf4881cfacb60def2eb3bfba85
    (1)(0)6540
    (1)6866
    (2)(0)192.168.184.118
    (1)2048
    (2)464c059fd4354b72cd277a7aaf6159a2682e47ff
    (3)(0)192.168.198.232
    (1)2048
    (2)c4ae0536b5a9157b43516e26c4c77ca9e431005a
    (2)(0)1308
    (1)1634
    (2)(0)192.168.197.41
    (1)2048
    (2)bb0b682b4eed16f773ed7b0d2cf430ac4e776624
    (3)(0)192.25.222.250
    (1)2048
    (2)098251c97fb147b7aee8143ced13c742ec3115a9
    (3)(0)8502
    (1)8828
    (2)(0)192.168.184.111
    (1)2048
    (2)3680ab0d0961e4dda6f9a35eb4c72cbcc04bba3b
    (3)(0)192.168.176.75
    (1)2048
    (2)99d64ca4bf375460537a296ce0bdab2374c8487c
    (4)(0)14388
    (1)14714
    (2)(0)192.25.239.116
    (1)2048
    (2)37d438fc43097bab896e04589b10659c2898c56b
    (3)(0)192.168.184.113
    (1)2048
    (2)2697f554dac4c6b6452239b5f3f192052504431f
    (5)(0)1962
    (1)2288
    (2)(0)192.168.198.241
    (1)2049
    (2)7c323af2d42df59cb172c1f2989fdb47972c4b6b
    (3)(0)192.168.184.112
    (1)2048
    (2)94f67ff376efbecf3d84905dbfe6d9e8a5c9cf83
    (6)(0)0
    (1)326
    (2)(0)192.168.197.9
    (1)2048
    (2)510edbf404ed2b24aeeff53f9d8f43c6404d62c0
    (3)(0)192.168.198.249
    (1)5010
    (2)a150d1f3671d8a5e42c238e900a23031693ddd32
    (7)(0)12426
    (1)12752
    (2)(0)192.168.198.240
    (1)5010
    (2)cb2c653239757df27da883dc024e87a8f11890f0
    (3)(0)192.168.197.41
    (1)2049
    (2)ed4a2925e913bc2773cd3a4c13d48deac7f423fa
    (8)(0)4251
    (1)4577
    (2)(0)192.25.239.105
    (1)2048
    (2)f2eb3bb1afc0d9345173d1eb2aaa675161f5d675
    (3)(0)192.168.198.242
    (1)2048
    (2)67a2926615eb6e4e4f850f59f78f670010ef573a
    (9)(0)9156
    (1)9482
    (2)(0)192.25.239.107
    (1)5010
    (2)4806e385ba1b193b48c0c6a8dd35e5d8a5738257
    (3)(0)192.25.239.105
    (1)2048
    (2)c1490e0c9963628972be63339f032c63191f1897
    (10)(0)6867
    (1)7193
    (2)(0)192.168.184.115
    (1)2048
    (2)2339d951c551c45daf84c147acaf6e6854f66f8c
    (3)(0)192.25.222.250
    (1)2049
    (2)cd6e7d09828b8d795afe671c39a0312b3d6859fe
    (11)(0)2616
    (1)2942
    (2)(0)192.168.198.234
    (1)2048
    (2)e5bfd81c7a0df2ec29b3ac4ce188795881b2bc1e
    (3)(0)192.25.238.96
    (1)2049
    (2)513edbe433f180924500db83ef155426e7f2f040
    (12)(0)5559
    (1)5885
    (2)(0)192.168.198.252
    (1)2048
    (2)515990729e830f0f75e3dc8179438d43c6c7c9b7
    (3)(0)192.25.238.92
    (1)2048
    (2)5328403d6ecd5272c51250977cc1af7de3f16407
    (13)(0)15369
    (1)15695
    (2)(0)192.168.176.78
    (1)2049
    (2)a140264d0aefdbb9b99c961c0c223266fa90129d
    (3)(0)192.168.197.32
    (1)2049
    (2)5fbc13a478470e14006500e38fa032472ec0da50
    (14)(0)12753
    (1)13079
    (2)(0)192.168.198.249
    (1)2049
    (2)120c04813a4f2db04bfc890874a5cf4e7ce47e15
    (3)(0)192.168.197.9
    (1)2048
    (2)e8ca0053b9a99684f683202d68994cf8b28f26e0
    (15)(0)4905
    (1)5231
    (2)(0)192.25.239.120
    (1)2048
    (2)e1a5f230ad1a7716c4e04bc785fcb001e7841b7f
    (3)(0)192.168.176.76
    (1)2049
    (2)c370b87def3a5533ca436ac17ed22d2d5ef64cf7
    (16)(0)15696
    (1)16022
    (2)(0)192.168.197.24
    (1)2048
    (2)ebad5e02b596b81431e7e35d80687b94eb4f495b
    (3)(0)192.168.176.77
    (1)5013
    (2)96771bd26c4f0f3af5a1cb3de4de6467e07f1a6d
    (17)(0)11118
    (1)11444
    (2)(0)192.168.198.147
    (1)2049
    (2)5e16b0661d43a03254fe0c9a97a21beada5665eb
    (3)(0)192.168.184.109
    (1)5010
    (2)c132bdd8ab5c38d4cce4bee4ffc1a298e75d5538
    (18)(0)16023
    (1)16383
    (2)(0)192.168.176.76
    (1)2048
    (2)e9b9b9354d6c77a980c0da35bef89b26042b716d
    (3)(0)192.168.198.146
    (1)2048
    (2)1ca744d6e5c24d3f2c64eca1dec65eea8545ce8b
    (19)(0)4578
    (1)4904
    (2)(0)192.25.239.104
    (1)2048
    (2)0aea965323c78cdefb3e8b4e03955b9651c719b6
    (3)(0)192.168.198.240
    (1)2049
    (2)365350cfa7621749e2c696d84d32d2228122fdc6
    (20)(0)10464
    (1)10790
    (2)(0)192.168.198.232
    (1)2048
    (2)2fe68c5065aefc24ddcc747071dd65c6cadd427b
    (3)(0)192.168.184.118
    (1)2049
    (2)bdc2191de10b7d825f359922e3e31ce328c0a972
    (21)(0)14061
    (1)14387
    (2)(0)192.168.198.231
    (1)2049
    (2)7a47f437951b03a95460924ffd349b3ecfb05bc7
    (3)(0)192.168.184.114
    (1)2048
    (2)bfebcd3837ccf9df6989be91951087cb7351404c
    (22)(0)9810
    (1)10136
    (2)(0)192.168.184.116
    (1)2048
    (2)3caff203db4214bea1f2bcbc2935bb3e2273ac72
    (3)(0)192.25.223.2
    (1)2048
    (2)d2fe724704165a35eef6541f75cb2ba5245472fa
    (23)(0)327
    (1)653
    (2)(0)192.25.238.90
    (1)2049
    (2)6254a81f003cce2cb66222dc312c0ad4ee0a900a
    (3)(0)192.25.223.2
    (1)2049
    (2)7781d87ca77b51fcd07c8d0956ab350e3db3ba1c
    (24)(0)9483
    (1)9809
    (2)(0)192.168.198.254
    (1)2048
    (2)a0acf25a9487c3fa6fc74af8ba57cc50d1b1a95c
    (3)(0)192.25.239.119
    (1)2048
    (2)e05f23f53bad3f8ccc7ae4f9080426e289350557
    (25)(0)12099
    (1)12425
    (2)(0)192.168.184.110
    (1)2048
    (2)39d6559849aa2d00b560cb554cfa79a30ee3ede1
    (3)(0)192.168.198.247
    (1)5010
    (2)fe6696fa4333b4776ed5ee38b93380a21755eaa0
    (26)(0)3924
    (1)4250
    (2)(0)192.168.198.180
    (1)2048
    (2)ba944b0643a2ad68033d129d78c1dee805d2191a
    (3)(0)192.25.239.121
    (1)2049
    (2)b5b020b3105be1e4a49c57d54647da6797a0a3cc
    (27)(0)14715
    (1)15041
    (2)(0)192.168.198.147
    (1)2048
    (2)937e58683236b2708fdc6ef77dd08e66ef7e7ad5
    (3)(0)192.25.239.120
    (1)2048
    (2)274760f1e7e84bd14593b43f95965546f4cb34f5
    (28)(0)1635
    (1)1961
    (2)(0)192.168.198.239
    (1)2048
    (2)ae4dc5d4d544dde49f177554c89e2001727d99e4
    (3)(0)192.168.198.250
    (1)2048
    (2)bea5260033bac6fb7ab67a444f9a1d0e8c9f93b9
    (29)(0)5886
    (1)6212
    (2)(0)192.25.239.116
    (1)2049
    (2)7c9f26573b5a79b77d9089ed7e5783f47f6d030d
    (3)(0)192.25.239.104
    (1)2048
    (2)5c999bc681e6751ec576049db73c3bee2717f8b6
    (30)(0)3270
    (1)3596
    (2)(0)192.168.198.179
    (1)2048
    (2)addeda3c397b1d6ca21996be33435f4a65ca7686
    (3)(0)192.168.198.231
    (1)5010
    (2)1d78a81025acdc123f04680a30a80cff6258e909
    (31)(0)2289
    (1)2615
    (2)(0)192.25.239.117
    (1)2049
    (2)b8ff018e1810697710963e0c4a17faca2b3b6421
    (3)(0)192.168.184.111
    (1)2049
    (2)f7cc831c676ec19560f6ea779f86971bd227ba7f
    (32)(0)6213
    (1)6539
    (2)(0)192.168.184.113
    (1)2048
    (2)ee943d8aec010e34fe1b573826d65cc9e1f14fce
    (3)(0)192.168.198.242
    (1)2049
    (2)bfa424b2f9d4e476c3985025f5bf8bdb6625b74a
    (33)(0)654
    (1)980
    (2)(0)192.25.239.106
    (1)5010
    (2)fa13eedd26af4e0364f56b1604cef9865afde98d
    (3)(0)192.168.198.251
    (1)2049
    (2)ea87dd7cbf8b8cbeee37a3d482493b5fc41a755b
    (34)(0)2943
    (1)3269
    (2)(0)192.25.239.118
    (1)5010
    (2)150932a7e4f0020b99c50312c8cec9d07d7aabcf
    (3)(0)192.25.239.121
    (1)5010
    (2)816d511dcab3f7d9e2068401ba5958e01f2212dd
    (35)(0)8175
    (1)8501
    (2)(0)192.168.198.148
    (1)2048
    (2)34d51673796ab652da7e312cff119296dae1d3bb
    (3)(0)192.168.197.28
    (1)2048
    (2)e1f48544722bbb62d1fb8910a6a4b00d9ccbd09b
    (36)(0)981
    (1)1307
    (2)(0)192.168.197.32
    (1)5010
    (2)41ea02b87652650c3ba2f085849fb6ac2169d897
    (3)(0)192.168.197.28
    (1)2049
    (2)9717393eb89ab32126263741fd250198e378023c
    (37)(0)5232
    (1)5558
    (2)(0)192.168.184.109
    (1)2049
    (2)96a728eece26f6f200b26ff9ddecae3fb5b8df11
    (3)(0)192.25.239.117
    (1)2048
    (2)69f84dd28d8c254732a4f0d5e5fd56c60c58b7fa
    (38)(0)3597
    (1)3923
    (2)(0)192.168.198.146
    (1)2048
    (2)526a0bc43f480738c704ea745f5377258c0e3cdb
    (3)(0)192.168.176.78
    (1)5010
    (2)f54b7898236acf827c53277f22fc3dcadcd37602
    (39)(0)15042
    (1)15368
    (2)(0)192.168.197.37
    (1)2048
    (2)57ceee2905d66bd5863acae5422bf9e39afa971d
    (3)(0)192.168.198.179
    (1)2048
    (2)55192b8693c15df7f11cffc788f2043275886002
    (40)(0)10791
    (1)11117
    (2)(0)192.25.222.249
    (1)2048
    (2)3c9b5f682360324951435b987489fbc2e4e5bfc3
    (3)(0)192.168.198.251
    (1)2048
    (2)2dcf8603ecbdf43d18112dcaf06d3f9321bc2e0c
    (41)(0)7194
    (1)7520
    (2)(0)192.168.184.112
    (1)2048
    (2)8a77d41c2bb0a0672654baecfdee52592bb04050
    (3)(0)192.25.238.96
    (1)2048
    (2)8632629188cb269a900677ec0dc395cb292c1e2e
    (42)(0)8829
    (1)9155
    (2)(0)192.168.198.241
    (1)2048
    (2)37684e5201b0fb50eaf184235a87f2e6a359dd01
    (3)(0)192.168.184.115
    (1)2048
    (2)4684a38fa0b4df0b21f0d777b91300afcace551a
    (43)(0)7848
    (1)8174
    (2)(0)192.168.184.114
    (1)2048
    (2)a6f9c01f4b3b2df4b4993f5745b78d1307d6ea81
    (3)(0)192.168.197.37
    (1)2049
    (2)51e3568c115be10067d9e808d094f71f96c92647
    (44)(0)13734
    (1)14060
    (2)(0)192.168.198.182
    (1)2048
    (2)6acca7f88ea98f8ba073f423707d7692592053f3
    (3)(0)192.25.238.90
    (1)2048
    (2)84965ce2c21634a7ce9405374e36e80fa0e4521b
    (45)(0)13407
    (1)13733
    (2)(0)192.168.184.117
    (1)2048
    (2)2fad26dc888fc776dec4fd33e167c860bd76d64e
    (3)(0)192.25.239.181
    (1)5090
    (2)c91e8c9f8aea8424683258e18c5aa93ec45bb216
    (46)(0)11772
    (1)12098
    (2)(0)192.168.199.1
    (1)2048
    (2)77c4678d6eddc2f8eb328fa02705d99a6c27a1ca
    (3)(0)192.25.239.176
    (1)5074
    (2)21106c8a361da9c3cf494857e168c5b640eca234
    (47)(0)7521
    (1)7847
    (2)(0)192.25.223.1
    (1)2048
    (2)ef990477dd7eb4b3c9915bf31b300e87d8fa9b29
    (3)(0)192.25.239.106
    (1)2049
    (2)b0a2975c5cc56a69b3bc63b348a88252fa09ec1a
    (48)(0)13080
    (1)13406
    (2)(0)192.168.198.182
    (1)2049
    (2)01baf618cc0945c5a379a37ff92e4b1e1eeefac8
    (3)(0)192.168.184.116
    (1)2048
    (2)041464f26f3e7bdb277d0624ba7003bb447f227b
    (49)(0)11445
    (1)11771
    (2)(0)192.25.239.107
    (1)2049
    (2)0495711f467db7ef747eeb9a24f16b7a5ce0c4ff
    (3)(0)192.168.198.234
    (1)2048
    (2)34d285e446013db478a030f3172fce256b6d4ba8
    reply from: 192.168.197.41:2048

    通过 awk 计算每个节点管理的槽 ( slot ) 数量

    # 把上面的日志粘贴到 slots 文件中
    $ vi /tmp/slots
    # 计算每个节点的槽数量并且排序,输出结果
    $ cat /tmp/slots | awk '{if((NR % 8 == 1) || (NR % 8 == 2)) print $0}' | awk '{if(NR % 2 == 1) print $0,getline,$0}' | sed 's/)/ /g' | awk '{print $6,$3,$6-$3}' | sort -k 3 -r | head -n 3
    16383 16023 360
    9809 9483 326
    980 654 326

    # 根据槽号反向查找
    $ cat /tmp/slots| grep 16023 -A8
    (18)(0)16023
    (1)16383
    (2)(0)192.168.176.76
    (1)2049
    (2)e9b9b9354d6c77a980c0da35bef89b26042b716d
    (3)(0)192.168.198.146
    (1)2048
    (2)1ca744d6e5c24d3f2c64eca1dec65eea8545ce8b
    (19)(0)4578

    由于本案例当中,大部分节点都是包含 326 个 slot,问题节点明显偏多 360

    2.2.1 相关命令解释

    awk NR 代表行号
    awk getline 获取下一行,并且赋值到 $0 (本意是代表当前行数据)。
    sort -k 3 -r,以第三列排序(-k 3),倒序输出(-r)
    head -n 3,输出前三行

    三、结论

    根据上述假设,经过验证,发现确实是由于 slot 分配不均匀导致

    四、改进

    人工手动调整 slot 较多节点的内存,使之达到与其它节点内存占用水平。
    提示:redis 可以通过修改参数( maxmemory,单位 byte )调整最大内存。

    五、参考

    ]]>
    + + database + + + redis + +
    + + 《Go语言程序设计》 - ch1/lissajous - GIF动画 + /20180908-%E3%80%8AGo%E8%AF%AD%E8%A8%80%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B%20-%20ch1:lissajous%20-%20GIF%E5%8A%A8%E7%94%BB/ + 一、程序说明
    1. 可以以web的方式查看,也可以生成一个图片
    2. 直接go run输出的是一丢乱码(图片)
    3. 需要go build 然后运行程序(具体看代码里面注释)

    具体的内容可以看下面的程序代码(虽然是抄书)

    二、程序代码

    2.1 基本程序

    package main

    import (
    "image"
    "image/color"
    "image/gif"
    "io"
    "log"
    "math"
    "math/rand"
    "net/http"
    "os"
    "time"
    )

    var palette = []color.Color{color.White, color.Black}

    const (
    whiteIndex = 0 // 画板中的第一种颜色
    blackIndex = 1 // 画板中的下一种颜色
    )

    func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    if len(os.Args) > 1 && os.Args[1] == "web" {
    handler := func(w http.ResponseWriter, r *http.Request) {
    lissajous(w)
    }
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
    return
    }
    lissajous(os.Stdout)
    }
    func lissajous(out io.Writer) {
    const (
    cycles = 5 // 完整的x振荡器变化的个数
    res = 0.001 // 角度分辨率
    size = 100 // 图像画布包含
    nframes = 64 // 动画中的帧数
    delay = 8 // 以10ms为单位的帧间延迟
    )

    freq := rand.Float64() * 3.0 // y振荡器的相对频率
    anim := gif.GIF{LoopCount: nframes}
    phase := 0.0 // phase difference
    for i := 1; i < nframes; i++ {
    rect := image.Rect(0, 0, 2*size+1, 2*size+1)
    img := image.NewPaletted(rect, palette)
    for t := 0.0; t < cycles*2*math.Pi; t += res {
    x := math.Sin(t)
    y := math.Sin(t*freq + phase)
    img.SetColorIndex(size+int(x*size+0.5),size+int(y*size+0.5),blackIndex)
    }
    phase +=0.1
    anim.Delay = append(anim.Delay,delay)
    anim.Image = append(anim.Image,img)
    }
    gif.EncodeAll(out,&anim) // 注意:忽略编码错误(直接运行输出的是一堆乱码)
    // go build lissajous.go
    // ./lissajous >out.gif # 生成一个图片
    // ./lissajous web # 开启一个web服务,可以浏览器访问 localhost:8000
    }

    2.2 改进型程序,也是课后练习

    package main

    import (
    "image"
    "image/color"
    "image/gif"
    "io"
    "log"
    "math"
    "math/rand"
    "net/http"
    "os"
    "time"
    )
    // 颜色代码数组
    var palette = []color.Color{color.Black, color.RGBA{199, 237, 204, 0xff}, color.RGBA{102, 53, 204, 0xff}}


    func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    // 判断传入参数 如果有web,就启动一个web服务器
    if len(os.Args) > 1 && os.Args[1] == "web" {
    handler := func(w http.ResponseWriter, r *http.Request) {
    lissajous(w)
    }
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
    return
    }
    lissajous(os.Stdout)
    }
    func lissajous(out io.Writer) {
    const (
    cycles = 5 // 完整的x振荡器变化的个数
    res = 0.001 // 角度分辨率
    size = 100 // 图像画布包含
    nframes = 64 // 动画中的帧数
    delay = 8 // 以10ms为单位的帧间延迟
    )

    freq := rand.Float64() * 3.0 // y振荡器的相对频率
    anim := gif.GIF{LoopCount: nframes}
    phase := 0.0 // phase difference
    for index,i :=0, 1; i < nframes; i++ {
    rect := image.Rect(0, 0, 2*size+1, 2*size+1)
    img := image.NewPaletted(rect, palette)
    for t := 0.0; t < cycles*2*math.Pi; t += res {
    x := math.Sin(t)
    y := math.Sin(t*freq + phase)
    img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), uint8(index))
    }
    // 控制变色频率,生成颜色数组下标
    index = i % 3
    phase += 0.1
    anim.Delay = append(anim.Delay, delay)
    anim.Image = append(anim.Image, img)
    }
    gif.EncodeAll(out, &anim) // 注意:忽略编码错误(直接运行输出的是一堆乱码)
    // go build lissajous.go
    // ./lissajous >out.gif # 生成一个图片
    // ./lissajous web # 开启一个web服务,可以浏览器访问 localhost:8000
    }
    ]]>
    + + go + + + go + +
    + + 赢得财富:用独到的知识/责任感/杠杆 + /20200517-specific-knowledge-accountability-leverage/ + 零、读后感

    美国风险投资家 Naval Ravikant 通过一个很长的推特阐明了他的商业观,感觉挺精辟。
    积累独到的知识(无法批量复制的),为社会提供他们需要的。
    财富不是靠出卖时间获得,应该通过杠杆(资本、人力、一本万利的工具<写书等>)去积累。
    选择一个可以长期从事的行业,寻找一批可以长期共事的人。

    一、原文(及翻译)

    1. Seek wealth, not money or status. Wealth is having assets that earn while you sleep. Money is how we transfer time and wealth. Status is your place in the social hierarchy.

      去寻求财富,而非金钱或地位。财富就是你拥有资产,而资产在你睡觉的时候都还在为你赚钱;金钱是我们转换时间和财富的工具;身份是你在社会等级体系里所处的位置。

    2. Understand that ethical wealth creation is possible. If you secretly despise wealth, it will elude you.

      要明白一件事:一个人完全可以不靠坑蒙拐骗站着赚取财富。如果你在暗中鄙视财富,那么财富也会躲着你。

    3. Ignore people playing status games. They gain status by attacking people playing wealth creation games.

      别去理会那些热衷于玩身份游戏的人,他们通过攻击那些创造财富的人以获得自己的身份。

    4. You’re not going to get rich renting out your time. You must own equity — a piece of a business — to gain your financial freedom.

      你不会通过出租自己的时间而变得富有。你必须拥有产权,也就是生意的一部分,以此才能赢得个人财务自由。

    1. You will get rich by giving society what it wants but does not yet know how to get. At scale.

      提供社会大众想要但是他们还不知道如何获取的东西,你就会因此而致富。但有一点:你必须规模化地供应社会。

    2. Pick an industry where you can play long term games with long term people.

      选择一个你可以长期从事的产业,寻找一批可以一起长期共事的人。

    3. The Internet has massively broadened the possible space of careers. Most people haven’t figured this out yet.

      互联网极大拓展了一个人职业生涯的可能性。绝大多数人对此毫无认知。

    4. Play iterated games. All the returns in life, whether in wealth, relationships, or knowledge, come from compound interest.

      玩就玩复利游戏。无论是财富,人际关系或者是知识,所有你人生里获得的回报,都来自复利。

    5. Pick business partners with high intelligence, energy, and, above all, integrity.

      在选择商业合作伙伴的时候,选择那些高智商、精力旺盛的家伙,但在这一切之上,他应该是个正直诚实的人。

    6. Don’t partner with cynics and pessimists. Their beliefs are self-fulfilling.

      不要和愤世嫉俗者和悲观主义者合作,因为他们会任由坏事发生,以此证明他们的负面看法是正确的。

    7. Learn to sell. Learn to build. If you can do both, you will be unstoppable.

      学会如何销售,学会如何创建。如果你同时能做到这两件事,你的成功将无可阻挡。

    8. Arm yourself with specific knowledge, accountability, and leverage.

      用独到知识,责任感和杠杆武装自己。

    9. Specific knowledge is knowledge that you cannot be trained for. If society can train you, it can train someone else, and replace you.

      独到知识是那种不可以通过培训而获得的知识。这是因为,如果这种知识可以经由培训而得,那么其他人同样也可以,并且以此取代你。

    10. Specific knowledge is found by pursuing your genuine curiosity and passion rather than whatever is hot right now.

      在真正的好奇心和热情驱使你前进的路上,你更有可能获得独到知识,而不是在追逐潮流热点的闻风起舞脚步里。

    11. Building specific knowledge will feel like play to you but will look like work to others.

      创建独到知识的过程对于你就像是在玩,而对于别人则像是工作。

    12. When specific knowledge is taught, it’s through apprenticeships, not schools.

      不能通过学校教育教会一个人独到知识,它只能通过学徒制口传身教。

    13. Specific knowledge is often highly technical or creative. It cannot be outsourced or automated.

      独到知识通常极富技术性和创造性,因此它不能被外包或自动实现。

    14. Embrace accountability, and take business risks under your own name. Society will reward you with responsibility, equity, and leverage.

      拥抱责任感,押上自己的声誉以承担商业风险。社会也会以责任,产权和杠杆作为回报。

    15. The most accountable people have singular, public, and risky brands: Oprah, Trump, Kanye, Elon.

      最具责任感的人都具有独一无二的、世人皆知的、敢于冒险的个性特征,如奥普拉、川普、坎耶、埃隆。

    16. “Give me a lever long enough, and a place to stand, and I will move the earth.” — Archimedes

      只要给我一根足够长的杠杆,一处可以立足的地方,我就能撬起地球。——阿基米德

    17. Fortunes require leverage. Business leverage comes from capital, people, and products with no marginal cost of replication (code and media).

      财富增长需要使用杠杆。商业杠杆有三个来源:1、资本;2、人力;3、复制起来边际成本为零的产品(如:代码和媒体)。

    18. Capital means money. To raise money, apply your specific knowledge, with accountability, and show resulting good judgment.

      资本的意思就是钱。想要融资,那就运用你的独到知识,配合你责任感,展示出你良好的判断力。

    19. Labor means people working for you. It’s the oldest and most fought-over form of leverage. Labor leverage will impress your parents, but don’t waste your life chasing it.

      人力指的就是为你干活的人,它是最古老也是争夺最激烈的杠杆。人力杠杆会让你父母因为你手下有许多人为你工作而感到骄傲,但你不要浪费生命去追求这一点。

    20. Capital and labor are permissioned leverage. Everyone is chasing capital, but someone has to give it to you. Everyone is trying to lead, but someone has to follow you.

      资本和劳动力是需要征得许可才能使用的杠杆。每个人都在追逐资本,但总得有个什么人给你才行;每个人都想要领导其它人,但总得有什么人愿意跟着你才行。

    21. Code and media are permissionless leverage. They’re the leverage behind the newly rich. You can create software and media that works for you while you sleep.

      代码和媒体是无需要许可即可使用的杠杆。它们是新贵人群背后的杠杆,你可以通过自己创建的软件和媒体,在睡觉时仍然为你干活。

    22. An army of robots is freely available — it’s just packed in data centers for heat and space efficiency. Use it.

      一支机器人军团已经集结待命,只是为了节约空间和热效能,它们被打包放进数据中心。去用吧。

    23. If you can’t code, write books and blogs, record videos and podcasts.

      如果你不会编程,那你还可以写书和博客,或者做视频或者音频节目。

    24. Leverage is a force multiplier for your judgement.

      杠杆能够成倍地放大你的判断力(所产生的效能)。

    25. Judgement requires experience, but can be built faster by learning foundational skills.

      判断力需要经验,但它可以通过学习基本技能的方法更快速地建立起来。

    26. There is no skill called “business.” Avoid business magazines and business classes.

      并不存在一种叫做“商业”的能力。尽量避开商业杂志和商业课程。

    27. Study microeconomics, game theory, psychology, persuasion, ethics, mathematics, and computers.

      去学习微观经济学、博弈论、心理学、说服术、伦理学、数学和计算机科学。

    28. Reading is faster than listening. Doing is faster than watching.

      读比听快,做比看快。

    29. You should be too busy to “do coffee,” while still keeping an uncluttered calendar.

      你应该忙得没有社交的时间才对,与此同时你应该始终保证日程安排井井有条。

    30. Set and enforce an aspirational personal hourly rate. If fixing a problem will save less than your hourly rate, ignore it. If outsourcing a task will cost less than your hourly rate, outsource it.

      你应该为自己设定一个有抱负的个人时薪数,并且坚持执行。如果解决一个问题所能节省下来的成本低于你的个人时薪,那就忽略这个问题好了;如果一项任务的外包成本低于你的个人时薪,就把它外包出去。

    31. Work as hard as you can. Even though who you work with and what you work on are more important than how hard you work.

      尽管你跟谁一起工作、做什么工作,要远比你的努力程度更加重要。但还是要倾尽全力去工作。

    32. Become the best in the world at what you do. Keep redefining what you do until this is true.

      你所做的事情,要努力做到世界最好。不断重新定义你在做什么,直到真的做到世界最好。

    33. There are no get rich quick schemes. That’s just someone else getting rich off you.

      这个世界上并没有快速赚钱致富的方法,如果你想要找寻这种方法,那它只会让别人从你身上赚钱致富。

    34. Apply specific knowledge, with leverage, and eventually you will get what you deserve.

      运用你的独到知识,配合上杠杆,最终你会得到你应该得到的东西。

    35. When you’re finally wealthy, you’ll realize that it wasn’t what you were seeking in the first place. But that’s for another day.

      终有一天当你变得富有,你会发现那一切并不是你最开始想要的东西。但是那就是另外一回事了。

    二、译者注释

    1. 财富就是你睡着觉,你的资产也在为你继续赚钱。这是一个越来越被广泛接受的定义。Naval Ravikant 是硅谷狂热的数字货币支持者,所以,他的话另有所指。从前后文来看,他所谓的资产并不等于是传统意义上的房产、股票、收藏,而是偏向于他反复提及的:软件和媒体。

    2. 出租时间概念,许多人理解为打工,认为打工就是出租自己的时间以换取金钱。其实并非如此,Naval 所指的出租时间概念,指的是一个人的财富增长,是否直接关系到他的时间。一个小卖部的老板,他并不为谁打工,但是他的财富增长需要他长时间守在店里,因此,他依然是出租时间换钱。但一个淘宝点卡店老板则不同,他的点卡销售是全自动的,不需要 24 小时守着,而且也不需要只做这一样生意。这就是Naval所谓互联网拓宽了个人职业生涯的一个例子。

    3. equity 我翻译为产权,不是一个很好的翻法。但是 Naval 前文提到 assets,很明显,作为投资人他非常清楚地知道这两个字眼之间的区别。equity 无论是翻译为股票、权益或者是资产,原文说“You must own equity — a piece of a business — to gain your financial freedom.”,这是和出租时间概念做对应的。出租时间的人,在商业链条里作为生产资料出现,不拥有任何产权,也就无法通过商业行为获利,所以,我这里勉强翻译为产权。

    4. specific knowledge 我翻译为独到知识,没有翻译为特定知识、专业知识或者是特殊知识。原因是在我的理解中,specific knowledge 不是书本知识,也不是学校教授的知识,更不可能在网上免费获取。一方面,它只能提供自己实践来获取;另一方面,它只能通过前人口耳相传。这种知识是做成一件事情的关键,属于知识体系中不共的那一部分。所以,我翻译为独到知识。

    5. “Give me a lever long enough, and a place to stand, and I will move the earth.” — Archimedes 这话不像是阿基米德说的。更像是一次抬杠的结果:

      “给我一个支点,我就能撬起地球!”

      “那么,您站在哪儿呢?”

      “好吧,给我一个支点,再给我一个站立的地方,我就能撬起地球。”

      “那么,您用空气就能撬起地球了?”

      “好吧,给我一根足够长的杠杆,一处可以立足的地方,我就可以翘起地球!”

      “那么,阿基米德先生,支点又不需要了吗?”

      “滚!”
    6. accountability 我本想翻译为“靠谱程度”,想想还是算了。

    7. 号称是“四十条语录”,但是我就找见了 39 条。

    8. 结合上下文看,Leverage 一词始终翻译为“杠杆”其实也不大对头。Naval 一再强调代码、博客、播客、视频节目,我觉得 Leverage 在他那里,有些时候应该相当于是个人影响力的代名词,或者可以简单理解为放大器。

    三、参考:

    1. 科技爱好者周刊-107期
    2. 如何不靠运气致富(和菜头翻译)
    ]]>
    + + 随说 + + + 财富 + +
    + + MySQL查询违反最左匹配原则,但 explain 显示走索引的疑惑 + /20200915-the-puzzle-of-mysql-using-index-which-violation-of-leftmost-matching-principle/ + 零、本文背景

    有个朋友抛出一个问题,明显不符合最左匹配原则的 SQL,居然走索引了
    兜兜转转,嘀咕了好几天,期间也和几个朋友讨论了一下
    都没有结果,最后还是在 MySQL 的官方文档中找到了原因
    记录下,也算是一次不错的探索。

    一、问题描述

    1.1 表结构

    CREATE TABLE `people_new` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `last_name` varchar(255) NOT NULL,
    `first_name` varchar(255) NOT NULL,
    `bob` date NOT NULL,
    PRIMARY KEY (`id`),
    KEY `index_union` (`last_name`,`first_name`,`bob`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='人员-新'

    1.2 数据

    mysql> select * from people_new;
    +----+-----------+------------+------------+
    | id | last_name | first_name | bob |
    +----+-----------+------------+------------+
    | 1 | hisen | yuan | 2008-08-08 |
    +----+-----------+------------+------------+
    1 row in set (0.00 sec)

    1.3 SQL 分析

    可以看到 Using index
    但是 possible_keys null 而 key 显示 index_union

    mysql> explain select * from people_new  where bob = '2008-08-08' and first_name = 'yuan';
    +----+-------------+------------+------------+-------+---------------+-------------+---------+------+------+----------+--------------------------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+------------+------------+-------+---------------+-------------+---------+------+------+----------+--------------------------+
    | 1 | SIMPLE | people_new | NULL | index | NULL | index_union | 1537 | NULL | 1 | 100.00 | Using where; Using index |
    +----+-------------+------------+------------+-------+---------------+-------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)

    二、原因

    1.1 的表结构显示,除了 id,其余三个属性都在 联合索引
    所以通过任何字段查询,返回的字段都被索引覆盖了,构成 覆盖索引
    由于扫描全部索引会快于全表扫描,所以这时候 sql 不管是不是最左匹配都会走索引(应该是『全索引扫描』)。

    3.1 的表结构中,除了 id ,还有 area 不在联合索引当中,此时破坏了 覆盖索引 ,故不走索引。

    If the index is a covering index for the queries and can be used to satisfy all data required from the table,
    only the index tree is scanned. In this case, the Extra column says Using index.
    An index-only scan usually is faster than ALL because the size of the index usually is smaller than the table data.

    详情:dev.mysql.com

    三、验证

    3.1 修改后的表结构

    CREATE TABLE `people_new` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `last_name` varchar(255) NOT NULL,
    `first_name` varchar(255) NOT NULL,
    `bob` date NOT NULL,
    `area` varchar(256) DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `index_union` (`last_name`,`first_name`,`bob`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='人员-新'

    3.2 修改表结构后的数据

    mysql> select * from people_new;
    +----+-----------+------------+------------+------+
    | id | last_name | first_name | bob | area |
    +----+-----------+------------+------------+------+
    | 1 | hisen | yuan | 2008-08-08 | NULL |
    +----+-----------+------------+------------+------+
    1 row in set (0.00 sec)

    3.2 SQL 分析

    mysql> explain select * from people_new  where bob = '2008-08-08' and first_name = 'yuan';
    +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | 1 | SIMPLE | people_new | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where |
    +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)

    四、总结

    有时候多和同行交流也能学到很多。
    带着问题去看官方文档收获会比较大。
    遇到问题,找到原因,解决问题,印象会深刻。

    ]]>
    + + 数据库 + + + mysql + index + +
    + + 计算机存储单位 - 以及常识 + /20201030-understanding-computer-storage-unit/ + 一、常见单位
    单位英文全称中文全称转换
    bbit-
    BByte字节1B=8b
    KBKilo Byte千字节1KB=1024B
    MBMega Byte兆字节1MB=1024KB
    GBGiga Byte千兆1GB=1024MB
    TBTrillion Byte万亿字节1TB=1024GB
    PBPeta Byte千万亿字节1PB=1024TB
    EBExa Byte百亿亿字节1EB=1024PB
    ZBZetta Byte十万亿亿字节1ZB=1024EB
    YBYotta Byte一亿亿亿字节1YB=1024ZB
    BBBronto Byte一千亿亿亿字节1BB=1024YB
    NBNona Byte1NB=1024BB
    DBDogga Byte1DB=1024NB
    CBCorydon Byte1CB=1024DB

    进制除了 Byte 与 bit 之间是 8,其它的都是 1024,但是目前很多时候习惯用 1000,比如 1T ≈ 1000G;

    二、带宽/网速

    2.1 带宽

    运营商(ISP)带宽宣传常见的有:50M、100M、500M、1000M…
    注意:这是传输速率,而不是下载速度。

    它们的单位其实是:bps (全称 bit per second,b/s)
    50M 中 M 的含义是:million 即 百万。
    翻译过来就是:50Mbps = 50,000,000 b/s;

    2.2 网速

    各种下载软件,比如迅雷会现在下载速度:12M/s
    这个 12M 的单位是 byte 也就是字节。
    由表常见单位可知,1byte = 8bit

    所以由带宽传输速率转换为下载速度一般需要除以 8
    即 100M 带宽,理论上最大的下载速度为:100Mbps / 8 = 12.5Mbyte / s(俗称网速 12.5M);

    三、字节占用

    关于字节的占用,最明显的就是文本编辑器,写入文本后保存,在文件管理器中能看到具体占用了多少存储空间。
    问:一个英文(或标点)占用多少字节?一个汉字(或标点)占用多少字节?
    答:与所使用的编码有关系,具体如下表:

    编码英文汉字
    ASCII1byte2byte
    UTF-81byte3byte
    Unicode2byte2byte
    ]]>
    + + java + + + java + +
    + + 软件工程师面试指引 + /20200805-software-engineer-interview-guide/ + 零、关于面试

    面试决定因子:70% 能力、20% 缘分、10% 行情。
    软件工程师是条不归路,每天进步一点点,早日走上人生巅峰。

    一、面经/知识点

    仓库的内容更多是抛砖引玉,提供通用与重要的技术,
    真正掌握还得贴合自身需要,花时间持续深入地学习。

    基础指引:https://github.com/Snailclimb/JavaGuide
    进阶之路:https://github.com/doocs/advanced-java
    编程书籍:https://github.com/jobbole/awesome-programming-books
    算法小抄:https://labuladong.gitbook.io/algo/
    大厂试题:https://github.com/0voice/interview_internal_reference
    简历打磨:https://github.com/geekcompany/ResumeSample/blob/master/java.md

    后续持续更新,多交流,共同成长。

    二、相关内容

    计算机科学的自我修炼
    技术人的自我修炼(书单)

    三、更新记录

    2020-12-18

    1. 新增『算法小抄』,很佩服作者的计划、执行能力。关于作者部分很认同。

    2021-02-18

    1. 新增『计算机科学的自我修炼』相关链接
    ]]>
    + + 成长 + + + java + 面试 + +
    + + 给群晖 NAS DS918+ 添加 RTL-8156B 2.5G 无线网卡 + /20221119-synology-DS918-add-2500M-RTL8156B-usb-network-cart/ + 0. 前言

    就这个群晖的网卡,困扰了我两天。
    因为按照之后没法在系统控制面板里面找到对应的网卡。
    就连 GitHub 上驱动的作者都说不支持 RTL-8156B(详见:GitHub-issue) 的外置网卡。
    但是我想那么多人都买了这种网卡,并且成功了,于是周末到处搜,最终找到了办法。

    可能这是 DS918+ 之类才会遇到的问题

    1. 获取 root 权限

    搜索引擎很多

    2. 安装驱动

    2.1 查询群晖架构

    群晖官网查询NASCPU架构

    2.2 下载对应驱动

    https://github.com/bb-qq/r8152/releases
    我这里是 DM7 的系统,下载的最新版本
    下载到 PC 上即可,不用下载到 NAS

    2.3 安装

    下载完成,在群晖套件中心,手动安装,选择刚刚下载的驱动文件安装
    第一次安装会失败(GitHub 上也有说明),需要执行如下命令

    sudo install -m 4755 -o root -D /var/packages/r8152/target/r8152/spk_su /opt/sbin/spk_su

    修改之后,再次安装即可完成。

    3. 修改配置

    由于群晖918+等型号对 lan 口限制了最大 2 个,这里需要修改两个配置文件。
    都是找到 maxlanport=“2” 修改数字为大于 2 的数字即可。

    3.1 其一

    vi /etc.defaults/synoinfo.conf
    # 我这里的修改
    maxlanport=“4”

    3.2 其二

    vi /etc/synoinfo.conf
    # 这个文件有点大,好好找找,我的是在 77% 左右
    maxlanport="4"

    4. 信息中心-网络

    群晖 DS918+,USB 外置网卡安装好驱动,设置好 maxlan 之后,
    系统命令行可以识别网卡,并且可以获取 IPv6 地址,但是没有 IPv4 的地址

    root@HiSEN-DS:~# ifconfig
    eth2 Link encap:Ethernet HWaddr 00:xx:xx:xx:xx:03
    inet6 addr: fd7e:a5c4:268d:0:2e0:ffff:fe68:80/64 Scope:Global
    inet6 addr: fe80::2e0:ffff:fe68:80/64 Scope:Link
    inet6 addr: 2408:8207:ffff:6700:2e0:ffff:fe68:80/64 Scope:Global
    UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
    RX packets:4 errors:0 dropped:0 overruns:0 frame:0
    TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:1000
    RX bytes:1175 (1.1 KiB) TX bytes:1446 (1.4 KiB)

    5. 控制面板-网络-网络界面

    设置局域网 3 (外置网卡) 为自动获取 IP 后,网络正常,系统控制面积显示正常

    root@DS:~# ifconfig
    eth2 Link encap:Ethernet HWaddr 00:xx:xx:xx:xx:03
    inet addr:10.0.0.xxx Bcast:10.0.0.255 Mask:255.255.255.0
    inet6 addr: fd7e:a5c4:268d:0:2e0:ffff:fe68:80/64 Scope:Global
    inet6 addr: fe80::2e0:ffff:fe68:80/64 Scope:Link
    inet6 addr: 2408:8207:ffff:6700:2e0:ffff:fe68:80/64 Scope:Global
    UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
    RX packets:1419 errors:0 dropped:0 overruns:0 frame:0
    TX packets:312 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:1000
    RX bytes:465252 (454.3 KiB) TX bytes:71449 (69.7 KiB)

    6. 参考

    群晖DSM系统“套件法”安装8156B芯片网卡驱动
    升級2.5G網卡的一些疑難雜症

    ]]>
    + + homelab + + + NAS + +
    + + 为什么要使用队列 - Java + /20170801-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8%E9%98%9F%E5%88%97%20-%20Java/ + 一、java中的队列:Queue接口

    Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。

    队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素(注意不要弄混队列的头部和尾部)就是说,队列以一种先进先出的方式管理数据,如果你试图向一个 已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线程把中间结果取出并在将来修改它们。队列会自动平衡负载。如果第一个线程集运行得比第二个慢,则第二个 线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来。下表显示了jdk1.5中的阻塞队列的操作:

    排序方法平均情况最好情况
    add增加一个元素如果队列已满,则抛出一个IllegalSlabEepeplian异常
    remove移除并返回队列头部的元素如果队列为空,则抛出一个NoSuchElementException异常
    element返回队列头部的元素如果队列为空,则抛出一个NoSuchElementException异常
    offer添加一个元素并返回true如果队列已满,则返回false
    poll移除并返问队列头部的元素如果队列为空,则返回null
    peek返回队列头部的元素如果队列为空,则返回null
    put返回队列头部的元素如果队列满,则阻塞
    take返回队列头部的元素如果队列为空,则阻塞

    二、消息队列

    介绍:

    消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。

    实现高性能,高可用,可伸缩和最终一致性架构。

    是大型分布式系统不可缺少的中间件。

    目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。

    场景:异步处理、应用解耦、流量削锋、日志处理


    以上就是关于为什么要使用队列的大致说明


    参考:

    1. java中队列的使用
    2. 消息队列的使用场景
    ]]>
    + + java + + + java + 队列 + +
    + + 企业邮箱签名模版 + /20170317-%E4%BC%81%E4%B8%9A%E9%82%AE%E7%AE%B1%E7%AD%BE%E5%90%8D%E6%A8%A1%E7%89%88/ + 下面是源码,有些邮箱可以直接用html源码设置。

    我用得是网易邮箱大师,把代码存为本地网页打开全选复制

    粘贴到邮箱大师的签名里面即可!亲测有效,还挺好看的

    代码如下:

    <html>
    <head></head>
    <body>
    <div>
    <style>.g {clear:both; height:0;}.mailsign {font-size:12px; color:#808080; margin:0 35px; line-height:22px;}.logo {width:305px; height:35px; margin:20px 0 10px -15px;}.name {font-weight:700; font-size:14px; float:left; color:#808080; height:30px; line-height:30px;}.position {float:left; margin-left:15px; color:#808080; height:30px; line-height:30px;}.company1 {clear:both; font-weight:700; font-size:14px; color:#808080; margin-top:10px;}.company2 {clear:both; font-weight:700; font-size:14px; color:#808080; margin-bottom:10px;}.add{}.add .zip { margin-left:5px; color:#a0a0a0; font-size:10px;}.tel {}.fax {}.phone {}.website {}.website a {color:#808080; text-decoration:none !important;}.eng {}.state {color:#a0a0a0; margin-top:20px; padding:15px; border:1px solid #CCC; border-radius:10px}.state h4 { margin:0;}.state p {margin:0; font-size:7.5pt;}</style>

    <div class="mailsign">
    <hr />
    <div class="g">
    <div class="name">
    <t id="tname">
    HiSEN
    </t>
    </div>
    <div class="position">
    <t id="tposition">
    中级java开发工程师
    </t>
    </div>
    </div>
    <div class="g"></div>
    <div class="company1">
    HiSEN网络在线技术有限公司
    </div>
    <div class="company2">
    <t id="tcompany">
    技术部
    </t>
    </div>
    <div class="add">
    <span>地址/Add :</span>
    <t id="tadd">
    北京朝阳区xxx
    </t>
    <div class="mob">
    <span>手机/Mob:</span>
    <t id="tmob">
    15555555555
    </t>
    </div>
    <div class="website">
    <span>网址/URL :</span>
    <a href="http://hisen.me/" target="_blank">hisen.me</a>
    </div>
    <div class="state">
    <h4>保密声明:</h4>
    <p>此文件中可能含有机密类信息,仅限于上方提到的人员使用。若非以上人员或负责将该信息传送给上述人员的职员或代理人,严禁对此文件作任何形式的汇报、散布、传播及复制。若非此文件的指定收件人,请立即以邮件形式联系发件人并销毁所有原始文件的拷贝。</p>
    <h4>CONFIDENTIALITY NOTICE:</h4>
    <p>The information contained in this transmission may contain privileged and confidential information and is intended only for the use of the person(s) named above. If you are not the intended recipient, or an employee or agent responsible for delivering this message to the intended recipient, any review, dissemination, distribution or duplication of this communication is strictly prohibited. If you are not the intended recipient, please contact the sender immediately by reply e-mail and destroy all copies of the original message.</p>
    </div>
    </div>
    </div>
    </body>
    </html>

    ]]>
    + + 其他 + + + 模版 + +
    + + 修改路由表:网线接内网、无线走外网 - 以及带来的问题 + /20171031-%E4%BF%AE%E6%94%B9%E8%B7%AF%E7%94%B1%E8%A1%A8%EF%BC%9A%E7%BD%91%E7%BA%BF%E6%8E%A5%E5%86%85%E7%BD%91%E3%80%81%E6%97%A0%E7%BA%BF%E8%B5%B0%E5%A4%96%E7%BD%91%20-%20%E4%BB%A5%E5%8F%8A%E5%B8%A6%E6%9D%A5%E7%9A%84%E9%97%AE%E9%A2%98/ +
    #删除原有的规则
    route delete 0.0.0.0

    #新增外网 172.16.188.254为网关(修改之前先看好)
    route add 0.0.0.0 mask 0.0.0.0 172.16.188.254 metric 30 -p

    #新增内网 16.0.0.0为内网网段 17.82.200.254为网关
    route add 16.0.0.0 mask 255.0.0.0 17.82.200.254 metric 10 -p

    带来的问题就是:
    内网的数据库,在启动之后。时不时会自动断开,导致影响正常工作,时不时得重启程序才能测试

    ]]>
    + + 软件 + + + 软件 + +
    + + 利用IDEA写Hexo博客的一些技巧 + /20170303-%E5%88%A9%E7%94%A8IDEA%E5%86%99Hexo%E5%8D%9A%E5%AE%A2%E7%9A%84%E4%B8%80%E4%BA%9B%E6%8A%80%E5%B7%A7/ + 今天偶然看到有人说用idea写博客

    刚开始我觉得这样会很麻烦,后来想想以前写博客也是醉了

    先新建一个 _post 的快捷方式

    进去,然后到博客根目录

    打开Git Bash,然后执行

    hexo n "你要写的文章题目"

    然后在 _post 快捷方式打开刚刚新建的markdown文件,用markdownpad打开编辑。。。

    编辑完了回到Git Bash。。。。想想就很麻烦


    于是乎用IDEA打开博客根目录

    sources -> _post -> new -> Edit File Templates

    name:markdown extension:md
    内容:

    ---
    title: ${NAME}
    keywords: []
    date: ${DATE} ${TIME}
    tags: []
    categories:
    ---

    接下来apply -> *.md -> 下面选择markdown

    以后新建markdown文件就会默认带上这个模版

    效果

    title: 利用IDEA写Hexo博客的一些技巧
    keywords: []
    date: 2017/3/3 10:51
    tags: []
    categories: hexo

    接下来到了发布的时间,于是我们可以设置一下Terminal(在setting里面),设置为bash(git目录下)

    设置完了之后Alt + F12 调出 Terminal 即可进行git操作

    到此,大功告成,我要去更新博客了

    ]]>
    + + hexo + + + hexo + idea + +
    + + 多线程基础 & 进阶 丨 系统架构 - CSDN博主:说好不能打脸 + /20170818-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80%20&%20%E8%BF%9B%E9%98%B6%20-%20CSDN%E5%8D%9A%E4%B8%BB%EF%BC%9A%E8%AF%B4%E5%A5%BD%E4%B8%8D%E8%83%BD%E6%89%93%E8%84%B8/ + 发现一个不错的技术博客,分享一下

    有时间多学习
    多线程基础 & 进阶
    系统架构

    ]]>
    + + java + + + java + 多线程 + +
    + + 如何学习NIO - NIO简单的例子 + /20170227-%E5%A6%82%E4%BD%95%E5%AD%A6%E4%B9%A0NIO-NIO%E7%AE%80%E5%8D%95%E7%9A%84%E4%BE%8B%E5%AD%90/ + 这里参照一些例子写了个简单的CS模型

    例子代码:NIO应用之简单的CS模型

    可以用来简单的理解一下java nio


    深入的理解可以看看下面的链接。

    Java NIO 系列教程:点击查看

    如何学习Java的NIO?:点击查看


    ]]>
    + + java + + + java + nio + +
    + + 如何检测Linux VPS系统架构是Xen、KVM还是OpenVZ + /20170208-%E5%A6%82%E4%BD%95%E6%A3%80%E6%B5%8BLinux-VPS%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E6%98%AFXen%E3%80%81KVM%E8%BF%98%E6%98%AFOpenVZ/ + 结果
    [root]# virt-what
    xen
    xen-hvm

    Centos

    wget http://people.redhat.com/~rjones/virt-what/files/virt-what-1.12.tar.gz
    tar zxvf virt-what-1.12.tar.gz
    cd virt-what-1.12/
    ./configure
    make && make install
    virt-what

    Ubuntu/debian

    apt-get install virt-what
    virt-what
    ]]>
    + + linux + +
    + + 如何线程安全的使用HashMap + /20171114-%E5%A6%82%E4%BD%95%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E4%BD%BF%E7%94%A8HashMap/ + 线程不安全的原因
    1. HashMap底层是一个Entry数组,一旦发生Hash冲突的的时候,HashMap采用拉链法解决碰撞冲突.
    2. put方法也不是同步的
    3. 扩容的方法也不是同步的

    参考:https://www.cnblogs.com/qiumingcheng/p/5259892.html

    如何线程安全的使用

    1. Hashtable
    2. ConcurrentHashMap
    3. SynchronizedMap

    ####例子

    //Hashtable
    Map<String, String> hashtable = new Hashtable<>();
    //synchronizedMap
    Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
    //ConcurrentHashMap
    Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

    ####性能对比
    使用ExecutorService来并发运行5个线程,每个线程添加/获取500K个元素。

    从数据可以看出,ConcurrentHashMap效率最高

    代码如下

    package com.hisen.collection.map;

    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Hashtable;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;

    /**
    * @author : yhx
    * @date : 2017/11/14 23:36
    * @descriptor : 测试不同的Map - 使用ExecutorService来并发运行5个线程,每个线程添加/获取500K个元素。
    */
    public class CrunchifyConcurrentHashMapVsSynchronizedMap {
    private static final int THREAD_POOL_SIZE = 5;
    public static Map<String, Integer> crunchifyHashTableObject = null;
    public static Map<String, Integer> crunchifySynchronizedMapObject = null;
    public static Map<String, Integer> crunchifyConcurrentHashMapObject = null;

    public static void main(String[] args) throws InterruptedException {
    // Test with Hashtable Object
    crunchifyHashTableObject = new Hashtable<>();
    crunchifyPerformTest(crunchifyHashTableObject);

    // Test with synchronizedMap Object
    crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>());
    crunchifyPerformTest(crunchifySynchronizedMapObject);

    // Test with ConcurrentHashMap Object
    crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>();
    crunchifyPerformTest(crunchifyConcurrentHashMapObject);

    /**
    * 测试结果
    Test start for:class java.util.Hashtable
    2500K entried added/retrieved in 2953 ms
    2500K entried added/retrieved in 4649 ms
    2500K entried added/retrieved in 2736 ms
    2500K entried added/retrieved in 2628 ms
    2500K entried added/retrieved in 2621 ms
    For class java.util.Hashtable the average time is 3117 ms

    Test start for:class java.util.Collections$SynchronizedMap
    2500K entried added/retrieved in 3036 ms
    2500K entried added/retrieved in 2881 ms
    2500K entried added/retrieved in 2692 ms
    2500K entried added/retrieved in 3020 ms
    2500K entried added/retrieved in 2806 ms
    For class java.util.Collections$SynchronizedMap the average time is 2887 ms

    Test start for:class java.util.concurrent.ConcurrentHashMap
    2500K entried added/retrieved in 4378 ms
    2500K entried added/retrieved in 1126 ms
    2500K entried added/retrieved in 1008 ms
    2500K entried added/retrieved in 935 ms
    2500K entried added/retrieved in 1069 ms
    For class java.util.concurrent.ConcurrentHashMap the average time is 1703 ms
    */
    }

    private static void crunchifyPerformTest(Map<String, Integer> crunchifyThreads)
    throws InterruptedException {
    System.out.println("Test start for:" + crunchifyThreads.getClass());
    long avgTime = 0;
    for (int i = 0; i < 5; i++) {
    long startTime = System.nanoTime();

    ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
    for (int j = 0; j < THREAD_POOL_SIZE; j++) {
    executorService.execute(new Runnable() {
    @Override
    public void run() {
    for (int k = 0; k < 500000; k++) {
    Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000);
    // Retrieve value. We are not using it anywhere
    Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));
    // Put value
    crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);
    }
    }
    });
    }
    // Make sure executor stops
    executorService.shutdown();
    // Blocks until all tasks have completed execution after a shutdown request
    executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    long entTime = System.nanoTime();
    long totalTime = (entTime - startTime) / 1000000L;
    avgTime += totalTime;
    System.out.println("2500K entried added/retrieved in " + totalTime + " ms");
    }
    System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + avgTime / 5 + " ms\n");
    }
    }

    ]]>
    + + java + + + HashMap + +
    + + 各系统查询数据时间分布情况统计-日志处理 + /20191025-%E5%90%84%E7%B3%BB%E7%BB%9F%E6%9F%A5%E8%AF%A2%E6%95%B0%E6%8D%AE%E6%97%B6%E9%97%B4%E5%88%86%E5%B8%83%E6%83%85%E5%86%B5%E7%BB%9F%E8%AE%A1-%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/ + 一、说明

    做这件事的目的是为了了解一条数据库记录从创建到使用的一个情况;
    查询分布时间计算方式采用Top Percentile方式,就是按一定排序的数据,前面xx%的最大值是多少;
    TP999 1ms 代表某接口99.9%的响应都在1ms之内;

    最终的目的也就是为了知道数据多久之后可以打入冷宫,使用廉价存储;
    冷热数据分级处理有利于在性能和成本上达到一定的平衡;
    如把内存缓存时间设置为tp90所处的时间,那么90%的数据都能快速返回,其它少量数据回源处理;

    关键字
    java格式化输出
    java8
    stream
    parallelStream
    分组
    排序
    DoubleSummaryStatistics数据分析
    TP999

    二、效果

    日志源数据预览

    19-10-24.14:47:12.721 [THREAD-22000-18-T-17] INFO  FacadeImpl        - response yw:jiaoyi, orderId:123456, time:2019-10-24T14:46:44

    ywcountmin(ms)max(ms)tp50(ms)tp90(ms)tp99(ms)tp999(ms)
    hisen1000110000206090130
    hisen-1200216000165070110

    ps 输出是格式化的数据,并不是表格,可以通过:

    world->粘贴输出文本->插入->表格->文本转换成表格->空格

    即可完成文字到表格转换

    这种过程可能比较low,但是也需要时间去处理,过程中还得配合linux命令等整合文本;

    三、代码

    完整:github-CallerAnalyze.java
    摘要如下:

    /**
    * @author hisenyuan
    * @date 2019-10-25 23:20
    */
    public class CallerAnalyze {
    public static void main(String[] args) throws IOException {
    String filePath = "/Users/hisenyuan/yw/yw.log";
    ArrayList<CallerTimeVo> callerTimeVos = getCallerTimeVos(filePath);

    System.out.println("size:" + callerTimeVos.size());
    System.out.printf("%-20s%-20s%-20s%-20s%-20s%-20s%-20s%-20s", "yw", "count", "min(ms)", "max(ms)", "tp50(ms)", "tp90(ms)", "tp99(ms)", "tp999(ms)");
    System.out.println();


    Map<String, List<CallerTimeVo>> callerMap = callerTimeVos.parallelStream().collect(Collectors.groupingBy(CallerTimeVo::getCaller));

    callerMap.entrySet()
    .stream()
    .sorted(Comparator.comparingInt(value -> value.getValue().size()))
    .forEach(stringListEntry -> {
    String caller = stringListEntry.getKey();
    System.out.printf("%-20s", caller);
    List<Long> sorted = stringListEntry.getValue()
    .parallelStream()
    .map(CallerTimeVo::getDuration)
    .sorted(Long::compareTo)
    .collect(Collectors.toList());
    calTime(sorted);
    });
    }
    private static ArrayList<CallerTimeVo> getCallerTimeVos(String filePath) throws IOException {
    ArrayList<CallerTimeVo> callerTimeVos = Lists.newArrayList();
    Files.asCharSource(new File(filePath), Charset.forName("UTF-8")).readLines(new LineProcessor<String>() {
    @Override
    public boolean processLine(String line) {
    // 处理每一行
    CallerTimeVo vo = getCreateTimeVo(line);
    callerTimeVos.add(vo);
    // false 会中断操作
    return true;
    }

    @Override
    public String getResult() {
    return null;
    }
    });
    return callerTimeVos;
    }
    }

    ]]>
    + + java + + + java + +
    + + 多线程:优雅的使用ExecutorService进行压力测试 + /20180714-%E5%A4%9A%E7%BA%BF%E7%A8%8B%EF%BC%9A%E4%BC%98%E9%9B%85%E7%9A%84%E4%BD%BF%E7%94%A8ExecutorService%E8%BF%9B%E8%A1%8C%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95/ + 很多时候写功能或者接口需要进行压力测试,
    今天发现jwt在生成token的时候,如果输入都是一样的
    仅有一个签发时间不一样,生成的token是有可能是一样的

    public void testCreate() {
    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("hisenyuan").build();
    ExecutorService pool = new ThreadPoolExecutor(
    20,
    50,
    10000L,
    TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<>(10240),
    namedThreadFactory,
    new ThreadPoolExecutor.AbortPolicy());
    for (int i = 0; i < 50; i++) {
    // 需要提交的内容
    pool.execute(this::createTokenTest);
    }
    pool.shutdown();
    try {
    while (!pool.awaitTermination(500, TimeUnit.MILLISECONDS)) {
    LOGGER.debug("Waiting for terminate");
    }
    } catch (InterruptedException e) {
    LOGGER.error(e);
    }
    }
    ]]>
    + + java + + + java + +
    + + 安全的重叠构造器 - 最佳实践:Build Pattern + /20180128-%E5%AE%89%E5%85%A8%E7%9A%84%E9%87%8D%E5%8F%A0%E6%9E%84%E9%80%A0%E5%99%A8%20-%20%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%EF%BC%9ABuild%20Pattern/ +
    effective java # 2

    Builder模式,不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,

    得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,

    来设置每个相关可选的参数,最后调用无参的build来生成不可变的对象。

    完整代码+测试:github:完整代码+测试

    public class NutritionFacts {

    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    private NutritionFacts(Builder builder) {
    servingSize = builder.servingSize;
    servings = builder.servings;
    calories = builder.calories;
    fat = builder.fat;
    sodium = builder.sodium;
    carbohydrate = builder.carbohydrate;
    }

    public static class Builder {

    // Required parameters
    private final int servingSize;
    private final int servings;

    private int calories = 0;
    private int fat = 0;
    private int carbohydrate = 0;
    private int sodium = 0;

    public Builder(int servingSize, int servings) {
    this.servingSize = servingSize;
    this.servings = servings;
    }

    public Builder calories(int val) {
    calories = val;
    return this;
    }

    public Builder fat(int val) {
    this.fat = val;
    return this;
    }

    public Builder carbohydrate(int val) {
    this.carbohydrate = val;
    return this;
    }

    public Builder sodium(int val) {
    this.sodium = val;
    return this;
    }

    public NutritionFacts build() {
    return new NutritionFacts(this);
    }
    }



    @Override
    public String toString() {
    return "NutritionFacts{" +
    "servingSize=" + servingSize +
    ", servings=" + servings +
    ", calories=" + calories +
    ", fat=" + fat +
    ", sodium=" + sodium +
    ", carbohydrate=" + carbohydrate +
    '}';
    }
    }

    ]]>
    + + java + + + java + +
    + + 《穿布鞋的马云》 - 部分文字摘录 + /20180908-%E3%80%8A%E7%A9%BF%E5%B8%83%E9%9E%8B%E7%9A%84%E9%A9%AC%E4%BA%91%E3%80%8B%20-%20%E9%83%A8%E5%88%86%E6%96%87%E5%AD%97%E6%91%98%E5%BD%95/ + 《穿布鞋的马云》 2018.10.01 ~ 2018.10.02

    国庆假期没有什么安排,看了几部电影之后感觉蛮愧疚,又浪费了大把的时间

    于是乎在书架上找了本决定看起来压力不那么大的书来看

    这本书感觉整体上写的一般,可能定位就是通俗易懂吧

    但是对于了解一些细节,还是很有帮助

    由于目前待在创业公司,看到很多文字的时候还是很有感触的

    如果你想成功,积极乐观地看待任何问题

    以下为摘抄:

    马云在校教书,兼职创业:海博翻译社

    92派:1992年后创业的企业家

    想清楚干什么,然后就要清楚该干什么;知道该干什么之后,要明白自己不该干什么
    ——>能干多久?想干多久?这件事情该干多久就多久。

    记住自己第一天的梦想,至关重要。

    海博翻译社的网页是第一个外国人能在互联网上搜到的第一个中国的网页。

    1995年是全世界互联网商用的第一年。

    只有启蒙才会有更大的市场。

    资本永远不能控制一家公司,资本只能为创业服务,而不能控制公司。

    一个创业公司的创始人能够取得权威媒体的认可,也是一件惊人的事情。

    创业者最重要的是创造条件,如果等到时机成熟的话,一定轮不到我们。

    有了理想之后,我觉得,最重要的是给自己一个承诺,承诺自己要把这个事情做出来。

    如果你不行动,不给自己的梦想一个实践的机会,你永远没有机会。

    创立阿里巴巴之前,三家公司马云得到的创业智慧:
    第一,选对创业项目很重要
    第二,即便是几个人的小公司,管理制度的建设也很重要
    第三,资本控制了你的公司时,你是没有希望的。
    第四,创业团队没有相同的理念和共同的目标会导致分裂。
    第五,对国有股东有了近距离的认识。

    1998年,阿里巴巴做bbs,长城是最早的bbs,xxx到此一游。

    阿里巴巴,在美国看到的名字,是“open sesame”芝麻开门的意思。

    合作都是团队做出来的,如果别人把你当英雄,你千万不要把自己当英雄,如果自己把自己当英雄必然要走下坡路。

    有几种人是难以在团队中培养的,一种是懂资本的人才,其他是财务、法律人才。因为这样的人才不仅要懂专业,而且需要经验。一般的公司多半是在上市之前引进这样的人才。

    在我擅长的世界里,我非常自信,非常自如。我没有想过要大包大揽,我知道自己的角色是什么。

    对于公众舆论而言,公司只能用一个声音说话,太多声音,只会让这个公司的形象变的模糊。

    世界上最不缺的就是钱,缺的是能用资本创造价值的企业家和企业家精神。

    1999.10高盛投资阿里巴巴使得后者可以对媒体讲一个好的故事:全世界最好的投资银行看好阿里巴巴的长期发展。它让外界对这家初创公司刮目相看。

    永远不要让资本说话,让资本赚钱。

    1999.10,拿到高盛500万(40%)投资的马云,随后孙正义想4000万占有49%的股份,后来3000万美元得到30%的股份,再后来,马云马云觉得钱太多反悔,最后孙正义投资2000万获得20%股份。

    在所有的创业公司发展中,扩张规模是最容易犯错误的时候,而这时犯的错还都是不小的错误。

    人多财务成本高,这并不是主要的,重要的是那些闲人会让全心全意投入加班加点的人感到不平衡,久而久之,公司的文化风气就不行了。

    2009.10 西湖论剑,马云请金庸拉拢当时互联网行业的佼佼者参与。前来的有:金庸,新浪王志东,搜狐张朝阳,网易丁磊,my8848王俊涛,加拿大驻华大使,英国驻沪总理事及50多家国际跨国公司在华代表

    在创业初期要寻找这些梦之队:没有成功,渴望成功,平凡,团结,有共同理想的人。

    2003年每天收入100万元,2004年每天盈利100万元,2005年每天纳税100万元。

    拥有好的业绩和产品,同事技巧性地为自己选定一个对标物,这会让你的公司和产品赢得大量关注。

    “裸奔”和淘宝的倒立与武侠一样,都是极易被识别的与众不同的公司文化符号,也极易产生传播效应。

    好的商人不在于他的梦想多么伟大,但是他的梦想必须是独特的,任何一个成功的企业家,从第一天起都有一个独特的梦想。

    2005.8.11雅虎中国并入阿里巴巴。资产技术+10亿美元,获得阿里40%股份。

    一个公司如果并购另外一个公司的话,文化融合是一件很难的事情。

    聪明是智慧者的天敌,傻瓜用嘴说话,聪明的人用脑子说话,智慧的人用心讲话。所以永远要记住,不要把自己当成最聪明的人,最聪明的人相信总有别人比自己更聪明。

    作为一个领导人,应该控制自己的情绪,很多时候发脾气是无能的表现,合理的情绪控制对于团队的和谐,稳定军心有很大作用。

    改变别人,先改变自己;要完善世界,先完善自己;要帮助好别人,先帮助自己;如果你不能帮助好自己,那一切都是瞎扯。

    人与人之间最本质的区别不是技能、专业、能力、情商,而是人生的使命感和价值观,而企业同理。

    先付出,再得到。

    没有使命感,人生会找不到奋斗的意义;
    没价值诶观,奋斗的方式会扭曲。

    80后,90后,我觉得是我的产品,我们没有理由、权利和责任区批判我们的产品,我们唯一有的权利和责任是完善我们的产品。

    真正的幸福感是你知道自己在做什么,知道别人在做什么,你会逐渐从痛苦中找到那些快乐。

    如果你想成功,积极乐观地看待任何问题

    ]]>
    + + read + + + 穿布鞋的马云 + +
    + + 如何成为专业的技术从业者 - 《程序员的职业素养》 - Bob大叔出品 + /20191008-%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E4%B8%93%E4%B8%9A%E7%9A%84%E6%8A%80%E6%9C%AF%E4%BB%8E%E4%B8%9A%E8%80%85%20-%20%E3%80%8A%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84%E8%81%8C%E4%B8%9A%E7%B4%A0%E5%85%BB%E3%80%8B/ + 很不错的一本书,作为程序员都值得去看一看,170+页周末一天可以看完, 还包括做笔记

    医学专业已经建立起一套严密的辅导体系
    软件行业建立一种包含学徒期、实习期、和长期指引的机制已是迫在眉睫

    有人指导大多数人都可以快速的成长,节省很多走弯路的时间
    当然,事在人为,只是说掌握了书中的那些要领,成为专业人员的几率更高,做更好的自己
    如果从小学开始就一直有人引路并且自己也愿意跟着走的话,应该会很棒,现在也不晚,抓住时间就好

    主要内容:
    专业主义
    学会说“不”,学会说“是”
    编码的正确姿势
    TDD
    卡塔练习很重要,肌肉反应
    验收测试(各方都一致同一的检验方式)
    测试策略,自动化测试是趋势
    时间管理,番茄工作法,注意力点数
    预估的概念以及方法
    压力,避免与面对
    协作,学会与人交流
    团队与项目,有凝聚力的团队战斗力强
    软件开发如医生一样培训更佳
    合适的工具事半功倍

    豆瓣链接:《代码简洁之道:程序员的职业素养》

    ]]>
    + + read + + + read + +
    + + Java实现Singleton最佳方法 - Enum + /20180127-%E5%AE%9E%E7%8E%B0Singleton%E6%9C%80%E4%BD%B3%E6%96%B9%E6%B3%95%20-%20Enum/ + effective java:

    单元素的枚举类型已经成为实现Singleton的最佳方法

    理由:

    1. 因为枚举单例有序列化和线程安全的保证
    2. 避免反射和并发困扰

    单例模式模式:完整代码+测试

    主要代码:

    public class EnumSingleton {

    private EnumSingleton() {
    }

    public static EnumSingleton getInstance() {
    return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton {
    INSTANCE;
    private EnumSingleton singleton;

    Singleton() {
    singleton = new EnumSingleton();
    }

    public EnumSingleton getInstance() {
    return singleton;
    }
    }
    }
    ]]>
    + + java + + + java + +
    + + 尝试使用NIO读取文件 + /20170601-%E5%B0%9D%E8%AF%95%E4%BD%BF%E7%94%A8NIO%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6/ + 具体代码如下,这里获取出来的文件大小是准确的

    FileInputStream方式获取出来的大小会有差异

    NIO与IO的对比详见:NIOCopy.java

    /**
    * 利用NIO进行读写文件
    *
    * @param oldFileName 原文件的路径
    * @param newFileName 新文件的路径
    */
    public static void nioCopy(String oldFileName, String newFileName) {
    try {
    FileChannel fileChannelIn = new FileInputStream(new File(oldFileName)).getChannel();
    FileChannel fileChannelOut = new FileOutputStream(new File(newFileName)).getChannel();
    //获取文件大小
    long size = fileChannelIn.size();
    System.out.printf("文件大小为:%s byte \n",size);
    //缓冲
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

    long start = System.currentTimeMillis();
    while (fileChannelIn.read(byteBuffer) != -1) {
    //准备写
    byteBuffer.flip();
    fileChannelOut.write(byteBuffer);
    //准备读
    byteBuffer.clear();
    }
    long end = System.currentTimeMillis();
    System.out.printf("NIO方式复制完成,耗时 %s 秒\n",(end-start)/1000);
    //关闭
    fileChannelIn.close();
    fileChannelOut.close();
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    ]]>
    + + java + + + java + +
    + + 基本排序算法的时间/空间复杂度表 + /20170120-%E5%9F%BA%E6%9C%AC%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%97%B6%E9%97%B4-%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E8%A1%A8/ + 排序方法平均情况最好情况最坏情况辅助空间稳定性冒泡排序O(n²)O(nlogn)O(n²)O(1)稳定简单选择O(n²)O(n²)O(n²)O(1)稳定直接插入O(n²)O(n)O(n²)O(1)稳定希尔排序O(nlogn)~O(n²)O(n^1.3)O(n²)O(1)不稳定堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定归并排序O(nlogn)O(nlogn)O(nlogn)O(n)不稳定快速排序O(nlogn)O(nlogn)O(n²)O(nlogn)~O(n)不稳定

    以上

    ]]>
    + + java + + + java + 算法 + 排序 + +
    + + 定时任务的一点思考 - Java + /20181027-%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E7%9A%84%E4%B8%80%E7%82%B9%E6%80%9D%E8%80%83%20-%20Java/ + 一、背景交代
    1. 部署方式:多点
    2. 业务概要:处理一批数据,可以失败重试

    二、实现方式

    1. 主表入一条控制数据,两个控制字段:next,process(0:空闲,1:处理中),子表具体处理业务
    2. 定时任务只扫描主表,通过其他业务字段,加上主表两个控制字段(now>netx,process=0),捞出需要处理的数据
    3. 当开始处理数据时候,先更新process=1,如果更新成功,说明拿到锁(假装是一个分布式锁,因为两台机器)
    4. 如果拿到3的锁,则开始处理子表的业务数据,一次查100条(limit 0,100 desc update_time),直到子表全部处理完成
    5. 如果遇到错误,把错误数据的id存起来,更新子表重试次数,和更新时间,如果达到最大失败次数,更改状态(让4查不出这条数据)
    6. 如果遇到当前处理数据的id存在List中,那么说明当前所有的数据都处理过一次了,退出(如果不判断,失败后会无限循环,直到全部成功)

    三、伪代码

    public void process(MainTable mainTable) {

    // 1. 锁控制表
    boolean lock = service.lock(mainTable);
    LOGGER.info("lockFlag:,", lock, ",mainTableId:", mainTable.getId());
    if (!lockFlag) {
    return;
    }
    List<Long> repeatIds = new ArrayList<>();
    // 2. 根据主表id,下次扫描时间,每次查询100条
    List<SubTable> subTables;
    do {
    //扫描子表中的数据
    subTables = service.querySub(mainTable.getId(), 100);
    for (SubTable sub : subTables) {
    if (repeatIds.contains(batch.getId())) {
    subTables = null;
    break;
    }
    // 处理业务
    handle(mainTable, subTables, repeatIds);
    }
    } while (!CollectionUtils.isEmpty(subTables));

    // 5. 检查是否全部终态
    checkHandleDone(mainTable);
    // 7. 释放锁
    boolean unLock = service.unLock(mainTable);
    LOGGER.info("lockFlag:,", unLock, ",mainTableId:", mainTable.getId());
    }
    ]]>
    + + java + + + java + 定时任务 + +
    + + 常见算法:Java求最小公倍数和最大公约数三种算法 + /20171021-%E5%B8%B8%E8%A7%81%E7%AE%97%E6%B3%95%EF%BC%9AJava%E6%B1%82%E6%9C%80%E5%B0%8F%E5%85%AC%E5%80%8D%E6%95%B0%E5%92%8C%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0%E4%B8%89%E7%A7%8D%E7%AE%97%E6%B3%95/ + 最小公倍数:
    数论中的一种概念,两个整数公有的倍数成为他们的公倍数

    其中一个最小的公倍数是他们的最小公倍数

    同样地,若干个整数公有的倍数中最小的正整数称为它们的最小公倍数


    求最小公倍数算法:

    最小公倍数=两整数的乘积÷最大公约数


    求最大公约数算法:

    (1) 辗转相除法

    有两整数a和b:

    1. a%b得余数c
    2. 若c=0,则b即为两数的最大公约数
    3. 若c≠0,则a=b,b=c,再回去执行1

    例如:求27和15的最大公约数过程为

    1. 27÷15余12
    2. 5÷12余3
    3. 12÷3余0

    因此,3即为最大公约数


    代码实现:

    /**
    * 最大公约数
    */
    public int getGCD(int m, int n) {
    if (n == 0) {
    return m;
    }
    return getGCD(n, m % n);
    }

    /**
    * 最小公倍数
    * @param m
    * @param n
    * @return
    */
    public int getLCM(int m, int n) {
    int mn = m * n;
    return mn / getGCD(m, n);
    }

    /**
    * 辗转相除求最大公约数
    * 有两整数a和b:
    * ① a%b得余数c
    * ② 若c=0,则b即为两数的最大公约数
    * ③ 若c≠0,则a=b,b=c,再回去执行①
    */
    public int divisionGCD(int m, int n) {
    int a;
    while (n != 0) {
    a = m % n;
    m = n;
    n = a;
    }
    return m;
    }
    /**
    * 相减法求最大公约数
    * 有两整数a和b:
    * ① 若a>b,则a=a-b
    * ② 若a<b,则b=b-a
    * ③ 若a=b,则a(或b)即为两数的最大公约数
    * ④ 若a≠b,则再回去执行①
    */
    public int subtractionGCD(int m,int n){
    while(m != n){
    if (m>n){
    m = m-n;
    }else {
    n = n - m;
    }
    }
    return m;
    }

    ]]>
    + + 算法 + + + java + 算法 + +
    + + 布隆过滤器 丨 简介 - Java demo + /20170907-%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%20%E4%B8%A8%20%E7%AE%80%E4%BB%8B%20-%20Java%20demo/ + 概述

    布隆过滤器的作用是加快判定一个元素是否在集合中出现的方法。

    因为其主要是过滤掉了大部分元素间的精确匹配,故称为过滤器。


    其应用场景为需要频繁在一个海量的集合中查找某个元素是否存在。

    并且通常,这个值不在集合中。

    比如Google chrome用此方法检查一个url是否在恶意url库中。

    简单的例子

    假设有一些字符串,假设有一个字符串a,要在集合B中查找其是否在集合B中。最笨的方法是遍历集合B中的每个元素bi,精确匹配a是否等于bi。若集合B中有N个元素,则最坏情况下需要执行N次精确匹配。

    一个改进的方法是将a和B中每个字符串按照特定规则映射为数字,称为hash值。规则可以任意设置。比如取各字符串的首字母和尾字母的编码之乘积,取奇数个字符的编码执行异或,等。将比较字符串问题变成一个比较数字的问题。比较字符串需要从头到尾比较,而数字的比较会快很多。

    需要注意的是,当两个字符串相同时,采用相同的映射规则得到的数字一定相同。但当两个字符串不同时,得到的字符串不一定不同。所以,当我们发现两个字符串的hash值相同时,两个字符串不一定相同,所以需要进一步去精确匹配两个字符串是否相同。但采用hash值方法已经能够过滤掉一部分以前需要精确匹配的计算量。仅当hash值相同(假设hash值通过字符串首尾字母计算得来,则当两个字符串首尾字母相同时hash值相同)时才去比较字符串本身。若选择hash值合理,则性能将大幅提高。

    布隆过滤器通过将一个字符串使用多个不同的hash值计算方法,映射为多个不同的hash值,当所有这些hash值完全相同时,才认为两个字符串相同。从而进一步降低了放生hash值相同的可能性,从而进一步提高了过滤的性能。

    Java代码实现

    算法使用了md5值来生成n个不同的hash值

    package com.hisen.interview;

    import java.math.BigInteger;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.ArrayList;

    /**
    * 布隆过滤器 - 测试某个元素不存在集合中
    * Created by hisenyuan on 2017/9/7 at 8:50.
    */
    public class BloomFilter {
    public static final int NUM_SLOTS = 1024 * 1024 * 8;
    public static final int NUM_HASH = 8;
    private BigInteger bitmap = new BigInteger("0");

    public static void main(String[] args) {
    BloomFilter bf = new BloomFilter();
    ArrayList<String> contents = new ArrayList<>();
    contents.add("sldkjelsjf");
    contents.add("ggl;ker;gekr");
    contents.add("wieoneomfwe");
    contents.add("sldkjelsvrnlkjf");
    contents.add("ksldkflefwefwefe");
    for (int i = 0; i < contents.size(); i++) {
    bf.adElement(contents.get(i));
    }
    System.out.println(bf.check("sldkjelsvrnlkjf"));
    System.out.println(bf.check("sldkjelsvrnkjf"));
    }
    private void adElement(String message) {
    for (int i = 0; i < NUM_HASH; i++) {
    int hashCode = getHash(message, i);
    if (!bitmap.testBit(hashCode)) {
    bitmap = bitmap.or(new BigInteger("1").shiftLeft(hashCode));
    }
    }
    }
    private boolean check(String message) {
    for (int i = 0; i < NUM_HASH; i++) {
    int hashCode = getHash(message,i);
    if (this.bitmap.testBit(hashCode)){
    return false;
    }
    }
    return true;
    }

    private int getHash(String message, int i) {
    try {
    MessageDigest md5 = MessageDigest.getInstance("md5");
    message = message + String.valueOf(i);
    byte[] bytes = message.getBytes();
    md5.update(bytes);
    BigInteger bi = new BigInteger(md5.digest());
    return Math.abs(bi.intValue()) % NUM_SLOTS;
    } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
    }
    return -1;
    }
    }

    ]]>
    + + java + + + 算法 + +
    + + 最近在看的Java系列文章 - 计算机程序的思维逻辑 + /20170814-%E6%9C%80%E8%BF%91%E5%9C%A8%E7%9C%8B%E7%9A%84Java%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0%20-%20%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%80%9D%E7%BB%B4%E9%80%BB%E8%BE%91/ + 计算机程序的思维逻辑

    这个系列的文章还不错

    目录—微信公众号

    目录—掘金(1-6缺失)

    相关笔记或者代码

    相关笔记或者代码 - github地址

    说明

    版权归原作者所有

    感觉文章不错,一边看一边实践。

    有时候从头开始学习也会有另一番风味。

    兴趣是最好的老师,唯有坚持才是实现梦想的唯一途径。

    ]]>
    + + java + + + java + +
    + + 有道云笔记支持的markdown语法 - hexo试一试 + /20170807-%E6%9C%89%E9%81%93%E4%BA%91%E7%AC%94%E8%AE%B0%E6%94%AF%E6%8C%81%E7%9A%84markdown%E8%AF%AD%E6%B3%95%20-%20hexo%E8%AF%95%E4%B8%80%E8%AF%95/ + markdown语法

    分割线


    1. 列表1
    2. 列表2
    3. 列表3
    //插入代码
    public void main(String[] args){
    System.out.println("Hello World!");
    }

    一级标题

    二级标题

    三级标题

    四级标题

    五级标题
    六级标题

    删除线

    引用,只能写在一行

    • [ ] 未勾选
    • [x] 勾选的

    梦殇国际

    image

    markdown源码

    markdown语法
    --

    分割线

    ---

    1. 列表1
    2. 列表2
    3. 列表3

    //插入代码
    public void main(String[] args){
    System.out.println("Hello World!");
    }

    # 一级标题
    ## 二级标题
    ### 三级标题
    #### 四级标题
    ##### 五级标题
    ###### 六级标题

    ~~删除线~~

    > 引用,只能写在一行

    - [ ] 未勾选
    - [x] 勾选的

    [梦殇国际](http://www.714.hk)

    ![image](http://www.714.hk/logo.png)
    ]]>
    +
    + + 手机平板电脑原样显示html效果 - html知识 + /20170208-%E6%89%8B%E6%9C%BA%E5%B9%B3%E6%9D%BF%E7%94%B5%E8%84%91%E5%8E%9F%E6%A0%B7%E6%98%BE%E7%A4%BAhtml%E6%95%88%E6%9E%9C-html%E7%9F%A5%E8%AF%86/ + 有时候可能你会发现
    在电脑上显示300x300大小的东西看起来很正常
    但是用手机去访问的话,就出现等比例缩小了
    但是300x300的大小完全不用缩小
    直接等大不是更好?

    在head加上这两行代码,可以做到原样输出

    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    ]]>
    + + html + +
    + + 性能指标的含义与计算方式:TP50、TP90、TP99、TP999 + /20190801-%E6%80%A7%E8%83%BD%E6%8C%87%E6%A0%87%E7%9A%84%E5%90%AB%E4%B9%89%E4%B8%8E%E8%AE%A1%E7%AE%97%E6%96%B9%E5%BC%8F:TP50%E3%80%81TP90%E3%80%81TP99%E3%80%81TP999/ + 零、本文背景

    一个接口的关键指标应该就是响应速度,要想提高响应速度,结果在缓存中最好;
    所以通过查询时间与缓存中订单的时间进行对比,得出缓存过期时间的最佳值,以平衡性能与成本;
    近期在做日志分析,找到比较理想的一个缓存过期时间,使90%的查询都能被缓存覆盖;

    ps:提取日志的关键是要日志规范,方便切割,才好算出调用时间(适用于暴利分析,而不是通过调用链等方式);

    一、性能指标含义

    常见指标:TP50、TP90、TP99、TP999
    正式解释:TP=Top Percentile,Top百分数,是一个统计学里的术语,与平均数、中位数都是一类;
    通俗理解:TP99 100ms,99%的查询都能在100ms内返回;

    二、性能指标计算

    计算方式:拿100次调用的耗时,排序,取第99个的耗时,这就是TP99的值;

    具体的代码方式就是,先算出耗时,放入List,然后排序,按指定的下标取值即可;

    代码如下:

    private void calTime(List<Long> timeDurations) {
    timeDurations.sort(Long::compareTo);
    double tp50 = timeDurations.size() * 0.5;
    double tp90 = timeDurations.size() * 0.9;
    double tp99 = timeDurations.size() * 0.99;
    double tp999 = timeDurations.size() * 0.999;

    // 基础统计,包含max、min、count、avg信息;
    DoubleSummaryStatistics doubleSummaryStatistics = timeDurations.stream().mapToDouble(Long::longValue).summaryStatistics();
    System.out.println(JSON.toJSONString(doubleSummaryStatistics));

    // Math.ceil() 向上取整
    System.out.println("tp50:" + timeDurations.get((int) Math.ceil(tp50)));
    System.out.println("tp90:" + timeDurations.get((int) Math.ceil(tp90)));
    System.out.println("tp99:" + timeDurations.get((int) Math.ceil(tp99)));
    System.out.println("tp999:" + timeDurations.get((int) Math.ceil(tp999)));
    }

    ]]>
    + + java + + + java + +
    + + 查看linux进程 - 多重方式 + /20170227-%E6%9F%A5%E7%9C%8Blinux%E8%BF%9B%E7%A8%8B-%E5%A4%9A%E9%87%8D%E6%96%B9%E5%BC%8F/ + 可以使用ps命令。
    它能显示当前运行中进程的相关信息,包括进程的PID。

    Linux和UNIX都支持ps命令,显示所有运行中进程的相关信息。

    ps命令能提供一份当前进程的快照。如果想状态可以自动刷新,可以使用top命令。

    ps命令

    输入下面的ps命令,显示所有运行中的进程:

    ps aux | less

    这个命令按 q 退出
    后面加了“| less”就会分页显示,如果去掉会一次性显示出所有结果

    输出:

    hisen@hisen-server:~$ ps aux | less
    USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
    root 1 0.2 0.5 37956 6028 ? Ss 09:03 0:02 /sbin/init
    root 2 0.0 0.0 0 0 ? S 09:03 0:00 [kthreadd]
    root 3 0.0 0.0 0 0 ? S 09:03 0:00 [ksoftirqd/0]
    root 4 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/0:0]
    root 5 0.0 0.0 0 0 ? S< 09:03 0:00 [kworker/0:0H]
    root 6 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/u2:0]
    root 7 0.0 0.0 0 0 ? S 09:03 0:00 [rcu_sched]
    root 8 0.0 0.0 0 0 ? S 09:03 0:00 [rcu_bh]
    root 9 0.0 0.0 0 0 ? S 09:03 0:00 [migration/0]
    root 10 0.0 0.0 0 0 ? S 09:03 0:00 [watchdog/0]
    root 11 0.0 0.0 0 0 ? S 09:03 0:00 [kdevtmpfs]
    root 12 0.0 0.0 0 0 ? S< 09:03 0:00 [netns]
    root 13 0.0 0.0 0 0 ? S< 09:03 0:00 [perf]
    root 14 0.0 0.0 0 0 ? S 09:03 0:00 [khungtaskd]
    root 15 0.0 0.0 0 0 ? S< 09:03 0:00 [writeback]
    root 16 0.0 0.0 0 0 ? SN 09:03 0:00 [ksmd]
    root 17 0.0 0.0 0 0 ? SN 09:03 0:00 [khugepaged]
    root 18 0.0 0.0 0 0 ? S< 09:03 0:00 [crypto]
    root 19 0.0 0.0 0 0 ? S< 09:03 0:00 [kintegrityd]
    root 20 0.0 0.0 0 0 ? S< 09:03 0:00 [bioset]
    root 21 0.0 0.0 0 0 ? S< 09:03 0:00 [kblockd]
    root 22 0.0 0.0 0 0 ? S< 09:03 0:00 [ata_sff]
    root 23 0.0 0.0 0 0 ? S< 09:03 0:00 [md]
    root 24 0.0 0.0 0 0 ? S< 09:03 0:00 [devfreq_wq]
    root 25 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/u2:1]
    root 26 0.0 0.0 0 0 ? S 09:03 0:00 [kworker/0:1]
    root 28 0.0 0.0 0 0 ? S 09:03 0:00 [kswapd0]
    root 29 0.0 0.0 0 0 ? S< 09:03 0:00 [vmstat]
    root 30 0.0 0.0 0 0 ? S 09:03 0:00 [fsnotify_mark]
    root 31 0.0 0.0 0 0 ? S 09:03 0:00 [ecryptfs-kthrea]
    :


    查看系统中的每个进程

    ps -A
    ps -e

    -A:显示所有进程

    a:显示终端中包括其它用户的所有进程

    x:显示无控制终端的进程

    显示进程的树状图

    pstree

    输出

    hisen@hisen-server:~$ pstree
    systemd─┬─accounts-daemon─┬─{gdbus}
    │ └─{gmain}
    ├─acpid
    ├─agetty
    ├─atd
    ├─cron
    ├─dbus-daemon
    ├─dhclient
    ├─2*[iscsid]
    ├─java───14*[{java}]
    ├─lvmetad
    ├─lxcfs───2*[{lxcfs}]
    ├─mdadm
    ├─polkitd─┬─{gdbus}
    │ └─{gmain}
    ├─redis-server───2*[{redis-server}]
    ├─rsyslogd─┬─{in:imklog}
    │ ├─{in:imuxsock}
    │ └─{rs:main Q:Reg}
    ├─snapd───5*[{snapd}]
    ├─sshd───sshd───sshd───bash───pstree
    ├─systemd───(sd-pam)
    ├─systemd-journal
    ├─systemd-logind
    ├─systemd-timesyn───{sd-resolve}
    └─systemd-udevd

    ]]>
    + + linux + + + linux + +
    + + 笔记 - 读后感:《第一本Docker书》 + /20180127-%E7%AC%94%E8%AE%B0%20-%20%E8%AF%BB%E5%90%8E%E6%84%9F%EF%BC%9A%E3%80%8A%E7%AC%AC%E4%B8%80%E6%9C%ACDocker%E4%B9%A6%E3%80%8B/ + 忘记开始是在什么地方看到docker这个东西

    后面觉得挺好玩,也试了很多次,找过很多的教程。

    搭建了一个zookeeper的集群(docker-compose)

    然后当我需要搭建redis集群的时候,发现里面很多的概念还不是很懂

    比如:volume network

    然后加了docker的群,遇到了在以前idea群里面熟的一个人

    花了一天的时间看完他传的一本书,昨天买的几本书晚上也到了。
    (docker从入门到实践、java并发编程的艺术、effective java中文版 2)

    这本书总体来说还行,就是过时了,还在用link,毕竟三年前的东西

    有些例子也报错,主要是ruby相关的不行,提醒需要2.2以上的版本

    下面是随便做的一点笔记

    # 所有容器,不加-a只显示正在运行的;-1 列出最后一次运行的
    docker ps -a
    # 启动一个已经停止的容器(id,--name 都可以)
    docker start hisen_container
    # 进入容器(容器重启之后进入交换行)
    docker attach hisen_container
    # 创建一个守护式容器
    docker -d
    # 查看日志 可选 -f 类似tail -f Ctrl+c 退出
    docker logs hisen_container
    # 查看容器内的进程
    docker top hisen_container
    # 在容器内部运行进程
    docker exec -d hisen_container touch /etc/new_conf_file
    # 停止守护式容器
    docker stop hisen_container
    # 自动重启容器
    docker run --restart=always ....
    =on-failure:5 当容器退出代码为非0时,docker会自动重启,最多重试5次
    # 深入容器,会以json串显示更多容器信息
    docker inspect hisen_container
    # 删除容器,要先stop才行
    docker rm hisen_container
    # 删除所有容器 -a:所有容器 -q:只返回容器的ID
    docker rm 'docker ps -a -q'
    # 启动新的名字为other_hisen_container的容器,并且进入bash shell
    docker run -i -t --name other_hisen_container ubuntu /bin/bash
    # docker文件系统层
    可写容器
    镜像(比如tomcat)
    基础镜像(比如ubuntu)
    引导文件系统(容器组、命名空间、设备映射)
    内核
    # docker简单构成
    顶层的读写层+底部不可变的可读层还有一些配置,就成了一个容器。
    # 列出所有镜像
    docker images
    # 仓库名:tag 如果没有指定,默认latest
    # 推荐使用Dockerfile构建镜像
    FROM ubuntu:14.04
    MAINTAINER hisenyuan "hisenyuan@gmail.com"
    RUN apt-get update \
    && apt-getinstall -y nginx \
    && echo 'Hi,I am in your container' \
    /usr/share/nginx/html/index.html
    EXPOSE 80
    # 构建镜像,在上面的dockerfile目录
    docker build -t="hisenyuan/static_web" .
    # 查看构建过程,以及镜像的每一层
    docker history 791397495f7a
    # 运行构建的镜像 -p端口映射 nginx -g "daemon off;" 前台运行nginx
    docker run -d -p 80:80 --name static_web hisenyuan/static_web nginx -g "daemon off;"
    # Dockerfile指令
    RUN 镜像构建的时候才会执行
    CMD 启动一个容器的时候要运行的命令,如有多个,只会生效最后一个,前面的多个会被忽略
    ENTRYPOINT 例子:ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"],这个命令不容易覆盖,CMD容易被RUN覆盖
    WORKDIR 设定工作目录,ENTRYPOINT CMD 会在这个目录执行
    ENV 设置构建时的环境变量,设置之后可以在后续的RUN命令使用
    USER 设置运行的用户
    VOLUME 向基于镜像创建的容器添加卷。
    1.卷可以在容器间共享和重用
    2.一个容器不是必须和其他容器共享卷
    3.对卷的修改是立即生效的
    4.对卷的修改不会对更新镜像产生影响
    5.卷会一直存在,直到没有任何容器再使用他
    VOLUME ["/opt/project"] 创建一个名为/opt/project的挂载点
    ADD 添加上下文目录的文件到构架容器中,如果是压缩包,会自动解压。会使构建缓存无效
    COPY 类似ADD、只会在上下文复制文件、也不会解压
    ONBUILD 为镜像添加触发器,当一个镜像被用作其他镜像的基础镜像时,该镜像中的触发器将会被执行。
    # 删除镜像,会删除这个镜像的每一层
    docker rmi hisenyuan/static_web
    # 运行一个registry
    docker run -p 5000:5000 registry
    # 推送镜像到registry
    docker tag 791397495f7a yourip:5000/hisenyuan/static_web #打标签
    docker push yourip:5000/hisenyuan/static_web #推送到本地注册容器
    docker run -t -i docker yourip:5000/hisenyuan/static_web /bin/bash #从registry运行一个容器

    ==========第五章 在测试中使用docker==========
    5.1 使用docker测试静态网站
    5.1.1 sample网站的初始Dockerfile
    hisen@docker1:~/docker$ mkdir sample
    hisen@docker1:~/docker$ cd sample/
    hisen@docker1:~/docker/sample$ touch Dockerfile
    hisen@docker1:~/docker/sample$ mkdir nginx && cd nginx
    hisen@docker1:~/docker/sample/nginx$ vi global.conf
    server {
    listen 0.0.0.0:80;
    server_name _;

    root /var/www/html/website;
    index index.html index.htm;

    access_log /var/log/nginx/default_access.log;
    error_log /var/log/nginx/default_error.log;
    }

    hisen@docker1:~/docker/sample/nginx$ vi nginx.conf
    user www-data;
    worker_processes 4;
    pid /run/nginx.pid;
    daemon off;

    events { }

    http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    gzip on;
    gzip_disable "msie6";
    include /etc/nginx/conf.d/*.conf;
    }
    hisen@docker1:~/docker/sample/nginx$ vi Dockerfile
    FROM ubuntu:14.04
    MAINTAINER hisenyuan "hisenyuan@gmail.com"
    ENV REFRESHED_AT 2018-01-27
    RUN apt-get update \
    && apt-get -y -q install nginx \
    && mkdir -p /var/www/html
    ADD nginx/global.conf /etc/nginx/conf.d/
    ADD nginx/nginx.conf /etc/nginx/nginx.conf
    EXPOSE 80
    hisen@docker1:~/docker/sample/nginx/website$ vi index.html
    <head>
    <title>Test website</title>
    </head>
    <body>
    <h1>This is a test website</h1>
    </body>
    hisen@docker1:~/docker/sample/nginx$ docker build -t hisenyuan/nginx .
    hisen@docker1:~/docker/sample/nginx$ docker run -d -p 8080:80 --name website \
    > -v $PWD/website:/var/www/html/website \
    > hisenyuan/nginx nginx # 因为已经在配置文件指定了daemon off,所以能直接启动
    # -v 指定了卷的源目录,挂载本地$PWD/website到容器/var/www/html/website目录,conf配置文件指定了工作目录

    # 修改之后,刷新浏览器,立马就生效
    hisen@docker1:~/docker/sample/nginx$ vi $PWD/website/index.html
    <head>
    <title>Test website - hisenyuan</title>
    </head>
    <body>
    <h1>This is a test website</h1>
    <h2>2018年1月27日 13:20:13</h2>
    </body>

    5.2 使用docker构建并测试web应用程序
    5.2.1 构建sinatra应用程序
    # 创建Dockerfile
    hisen@docker1:~/docker/sinatra$ vi Dockerfile
    FROM ubuntu:14.04
    MAINTINER hisenyuan "hisenyuan@gmail.com"
    ENV REFRESHED 2018-01-27
    RUN apt-get upate \
    && apt-get -y install ruby ruby-dev build-essential redis-tools \
    && gem install --no-rdoc --no-ri sinatra json redis \
    && mkdir -p /opt/webapp
    EXPOSE 4567

    # 代码网站:https://dockerbook.com/code/5/sinatra/webapp/
    wget --cut-dir=3 -nH -r --no-parent https://dockerbook.com/code/5/sinatra/webapp/

    5.3 docker用于持续集成

    ==========第六章 使用docker构建服务==========
    6.1 构建第一个应用
    ==========第七章 Consul、服务发现与注册==========

    ]]>
    + + docker + + + docker + +
    + + 利用Java构造二叉树 - 前序、中序、后续、层次遍历 + /20171113-%E5%88%A9%E7%94%A8Java%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91%20-%20%E5%89%8D%E5%BA%8F%E3%80%81%E4%B8%AD%E5%BA%8F%E3%80%81%E5%90%8E%E7%BB%AD%E3%80%81%E5%B1%82%E6%AC%A1%E9%81%8D%E5%8E%86/ + 定义

    最多有两棵子树的有序树,称为二叉树。二叉树是一种特殊的树。

    性质

    这里规定二叉树的根结点的层次为1。

    1. 性质1:则二叉树的第i 层最多有2i-1个结点(在此二叉树的层次从1开始,i≥1)
    2. 性质2:深度为k的二叉树最多有2k-1个结点。(k≥1)
    3. 性质3:对任何一棵二叉树T, 如果其叶结点个数为n0, 度为2的非叶结点个数为n2, 则有
      n0 = n2 + 1
      +
    4. 性质4:具有 n(n>0)个结点的完全二叉树的深度为⎣log2n⎦+1;⎦x⎦表示不超过x的最大整数。
    5. 性质5:如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第⎣l og2n⎦ +1层,每层从左到右),则对任一结点i(1≤i≤n),有:
      5.1 (1)如果i=1,则结点i无双亲,是二叉树的根;如果i>1,则其双亲是结点⎣i/2⎦。
      5.2 (2) 如果2i<=n, 则结点i的左孩子结点是2i;否则,结点i为叶子结点,无左孩子结点。
      5.3 (3)如果2i+1<=n,则结点i的右孩子是结点2i+1; 否则,结点i为叶子结点,无右孩子结点。

    完整代码

    https://github.com/hisenyuan/btree

    二叉链表的实现

    package com.hisen.interview.tiger20171110.btree;

    /**
    * @author : yhx
    * @date : 2017/11/10 18:42
    * @descriptor : 二叉树的 - 二叉链表实现
    */
    public class LinkBTree implements BTree {

    private Object data;
    private BTree lChild;
    private BTree rChild;

    public LinkBTree() {
    this.clearTree();
    }

    public LinkBTree(Object data) {
    this.data = data;
    this.rChild = null;
    this.lChild = null;
    }

    @Override
    public void addLfetTree(BTree lChild) {
    this.lChild = lChild;
    }

    @Override
    public BTree getLfetTree() {
    return lChild;
    }

    @Override
    public void addRightTree(BTree rChild) {
    this.rChild = rChild;
    }

    @Override
    public BTree getRightTree() {
    return rChild;
    }

    @Override
    public void clearTree() {
    this.data = null;
    this.rChild = null;
    this.lChild = null;
    }

    @Override
    public int getDeep() {
    return deep(this);
    }


    @Override
    public Object getRootData() {
    return data;
    }

    @Override
    public boolean hasLeftTree() {
    if (lChild != null) {
    return true;
    }
    return false;
    }

    @Override
    public boolean hasRightTree() {
    if (rChild != null) {
    return true;
    }
    return false;
    }

    @Override
    public boolean isEmptyTree() {
    if ((lChild == null && rChild == null && data == null) || this == null) {
    return true;
    }
    return false;
    }

    @Override
    public boolean isLeaf() {
    if (lChild == null && rChild == null) {
    return true;
    }
    return false;
    }

    @Override
    public void removeLeftTree() {
    lChild = null;
    }

    @Override
    public void removeRightTree() {
    rChild = null;
    }

    @Override
    public BTree getRoot() {
    return this;
    }

    @Override
    public void setRootData() {
    this.data = data;
    }

    @Override
    public int size() {
    return size(this);
    }

    private int size(BTree bTree) {
    if (bTree == null) {
    return 0;
    } else if (bTree.isLeaf()) {
    return 1;
    } else {
    if (bTree.getLfetTree() == null) {
    return size(bTree.getRightTree()) + 1;
    } else if (bTree.getRightTree() == null) {
    return size(bTree.getLfetTree()) + 1;
    } else {
    return size(bTree.getLfetTree()) + size(bTree.getRightTree()) + 1;
    }
    }
    }

    /**
    * 计算二叉树的高度
    */
    private int deep(BTree bTree) {
    if (bTree.isEmptyTree()) {
    return 0;
    } else if (bTree.isLeaf()) {
    return 1;
    } else {
    if (bTree.getLfetTree() == null) {
    return deep(bTree.getRightTree()) + 1;
    } else if (bTree.getRightTree() == null) {
    return deep(bTree.getLfetTree()) + 1;
    } else {
    return Math.max(deep(bTree.getLfetTree()), deep(bTree.getRightTree())) + 1;
    }
    }
    }
    }

    二叉树的各种遍历

    遍历方式:前序、中序、后序、层次

    package com.hisen.interview.tiger20171110.btree;

    import java.util.LinkedList;

    /**
    * @author : yhx
    * @date : 2017/11/13 12:15
    * @descriptor : 二叉树的遍历
    */
    public class OrderBTree implements Visit {

    /**
    * 前序遍历
    *
    * @param root 根节点
    */
    public void preOrder(BTree root) {
    visit(root);
    if (root.getLfetTree() != null) {
    preOrder(root.getLfetTree());
    }
    if (root.getRightTree() != null) {
    preOrder(root.getRightTree());
    }
    }

    /**
    * 中序遍历
    *
    * @param root 根节点
    */
    public void inOrder(BTree root) {
    if (root.getLfetTree() != null) {
    inOrder(root.getLfetTree());
    }
    visit(root);
    if (root.getRightTree() != null) {
    inOrder(root.getRightTree());
    }
    }

    /**
    * 后序遍历
    * @param root 根节点
    */
    public void postOrder(BTree root) {

    if (root.getLfetTree() != null) {
    postOrder(root.getLfetTree());
    }
    if (root.getRightTree() != null) {
    postOrder(root.getRightTree());
    }
    visit(root);
    }

    /**
    * 层次遍历 - 利用队列
    * @param bTree 根节点
    */
    public void levelOrder(BTree bTree){
    if (bTree == null){
    return;
    }
    LinkedList<BTree> queue = new LinkedList<>();
    BTree current = null;
    // 将根节点入队列
    queue.offer(bTree);
    while (!queue.isEmpty()){
    // 队头元素出队列
    current = queue.poll();
    visit(current);
    // 如果左节点不为空,入队列
    if (current.getLfetTree() != null){
    queue.offer(current.getLfetTree());
    }
    // 如果右节点不为空,入队列
    if (current.getRightTree() != null){
    queue.offer(current.getRightTree());
    }
    }
    }
    /**
    * 访问二叉树的节点
    * @param bTree 树的节点
    */
    @Override
    public void visit(BTree bTree) {
    System.out.print(bTree.getRootData() + "\t");
    }
    }

    参考

    http://blog.csdn.net/luoweifu/article/details/9077521

    http://blog.csdn.net/snow_7/article/details/51815787

    ]]>
    + + java + + + java + 二叉树 + 数据结构 + +
    + + 笔记本在BIOS Setup里面设置双显卡模式 + /20170509-%E7%AC%94%E8%AE%B0%E6%9C%AC%E5%9C%A8BIOS%20Setup%E9%87%8C%E9%9D%A2%E8%AE%BE%E7%BD%AE%E5%8F%8C%E6%98%BE%E5%8D%A1%E6%A8%A1%E5%BC%8F/ + 我的本子是双显卡的,英特尔的核芯显卡和英伟达的独显

    但是之前完全没有把独显用上,在英伟达的设置里选择使用显卡感觉也没有用

    后来找到在BIOS里面设置,貌似管用,联想的机子可以看看

    其他的机子应该也差不多

    连接:http://iknow.lenovo.com/detail/dc_102471.html

    6. IdeaPad Z380/Z480/Z580/U310/U410,Lenovo G480A/V370A/V470A/V570A
    依次选择“Configuration”、“Graphic Device”,其中有两个选项:Optimus表示可切换显卡模式;
    UMA Only表示集显模式。选择好后,按F10并根据提示保存退出即可。

    我的默认居然是:UMA Only

    ]]>
    + + 软件 + + + 软件 + +
    + + 数组中只有一个数出现奇数次 | 利用异或最高效 + /20190404-%E6%95%B0%E7%BB%84%E4%B8%AD%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E6%95%B0%E5%87%BA%E7%8E%B0%E5%A5%87%E6%95%B0%E6%AC%A1%20%7C%20%E5%88%A9%E7%94%A8%E5%BC%82%E6%88%96%E6%9C%80%E9%AB%98%E6%95%88/ + 一、背景

    题意:一个数组有奇数个元素,其中只有一个元素出现一次,其它都出现了2次

    方案A:利用hashMap存储出现的次数,然后遍历,复杂度O(3/2N)=O(N);
    方案B:利用bitmap,根据数组元素的大小确定位置,做取反(0->1->0);
    方案C:利用异或的特性快速找出,最简单,高效;

    二、异或

    a^a=0
    0^a=a
    异或支持:交换率、结合率
    因此:
    a^b^c^b^c=a^(b^b)^(c^c)=a

    三、代码

    public static void main(String[] args) {
    int[] mayDup = {1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1};
    int res = mayDup[0];
    for (int i = 1; i < mayDup.length; i++) {
    System.out.print("res^mayDup[i]: " + res + "^" + mayDup[i]);
    res ^= mayDup[i];
    System.out.println("=" + res);
    }
    System.out.println(res);
    }

    四、后记

    这只是一个场景的妙用,实际当中很难遇到这种情况。
    所以不管什么时候还是得多去刷题,没事刷几道,熟然生巧。

    ]]>
    + + java + + + java + 技巧 + +
    + + 浏览器出现:Reimage Repair - zh.reimageplus.com + /20170705-%E6%B5%8F%E8%A7%88%E5%99%A8%E5%87%BA%E7%8E%B0%EF%BC%9AReimage%20Repair%20-%20zh.reimageplus.com/ + 近期使用chrome出现打开一些网站老是弹出莫名其妙的网址

    最终指向的都是Reimage Repair,网址zh.reimageplus.com
    Reimage Repair
    网上一查,貌似因为插件被污染的原因

    我关闭chrome所有的插件,一个一个排查,最后找到了一个插件出问题

    本来还想去举报,发现这个插件被下架了

    问题排查

    右上角菜单按钮 ---更多工具 --- 扩展程序 --- 关闭所有插件
    然后一个个打开,测试某些网页是否还会继续跳出那恶心的东西
    然后就找到问题了。

    ]]>
    + + 软件 + + + 病毒 + +
    + + 浅谈volatile + /20190612-%E6%B5%85%E8%B0%88volatile/ + 一、volatile是什么

    volatile是Java提供的一种轻量级的同步机制
    保证变量的修改其它线程立马可见,解决部分并发问题

    二、volatile局限性

    无法解决复合操作,例如 i++ i–这种操作
    原因:i++操作分三步

    1. 读取i=1;
    2. i=i+1=2;
    3. i=2写入主存
      有可能在3写入之前,其它线程已经对i进行了修改,比如改为100了,结果覆盖写入了2

    三、volatile原理

    volatile底层依靠指令重排序来实现内存可见性的
    具体的规则如下

    1. 当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序
    2. 当第一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序
    3. 当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序

    四、 参考

    https://www.cnblogs.com/chengxiao/p/6528109.html

    ]]>
    + + java + + + java + +
    + + 线上事故记录 - 死锁 + /20180809-%E7%BA%BF%E4%B8%8A%E4%BA%8B%E6%95%85%E8%AE%B0%E5%BD%95%20-%20%E6%AD%BB%E9%94%81/ + 一、背景描述

    在支付业务当中,每一笔交易都得进行记账。

    两种情况:

    1. 第一步先冻结,交易成功,解冻并扣款(A账户->B账户->C账户);
    2. 第一步先冻结,交易失败,解冻并归还(A账户->B账户->A账户);

    上面的两种情况各自都是在一个事物当中。

    二、问题描述

    在同一个商户进行并发操作的时候,交易有成功有失败;

    1. 成功的时候钱是:A账户->B账户;
    2. 失败的时候钱是:B账户->A账户;

    因为在各自的事物当中更新两条记录的信息,并且使用了for update(innodb引擎)
    ß
    在某一瞬间:成功的先锁A账户,失败的先锁了B记录

    接下来就两个事物各自持有对方想要的资源,并且不释放已经占有的资源,就造成了死锁

    三、解决方法

    在程序里面,更新两个账户的钱的时候,始终先更新ID更小的那条记录,那样不管多少个事务同时进来

    都会按照固定的顺序去持有资源,比如先A再B,这样就不会出现各自持有对方想要的资源

    ID ACCOUNT
    10 A
    11 B

    每个事务都是先锁定A再锁定B,拿不到锁就一直等待

    ]]>
    + + java + +
    + + 解决:eclipse下*.properties显示Unicode乱码 + /20170208-%E8%A7%A3%E5%86%B3%EF%BC%9Aeclipse%E4%B8%8B-properties%E6%98%BE%E7%A4%BAUnicode%E4%B9%B1%E7%A0%81/ + eclipse的*.properties文件,默认的编码方式是iso-8859-1

    Window -> preferences -> general -> Contents Types -> Text(展开)
    -> Java Aroperties File(点击) -> *.properties(locked)(点击)
    -> 把iso-8859-1改为 UTF-8 -> Update -> OK

    然后就可以正常显示中文了

    ]]>
    + + java + 乱码 + properties + +
    + + 聊聊MySQL的隔离级别 | MySQL隔离级别原理 + /20190413-%E8%81%8A%E8%81%8AMySQL%E7%9A%84%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB/ + 这是之前的一个帖子:oracle - mysql - 数据库事务隔离级别介绍
    写的不全面, 按现在的理解,重新写一个;

    一、名词解释

    脏读:在一个查询事务过程中,读到了其它事务没有提交的数据;
    不可重复读:一个事务查询过程中,多次查询得到了不一致的结果,原因是:有别的更新事务提交了;
    幻读:一个事务查询过程中,多次查询得到了不一致的结果,原因是:有别的删除事务/插入事务提交了;

    二、数据库隔离级别

    name名称脏读不可重复读幻读加锁读
    Read uncommitted读未提交YesYesYesNo
    Read committed读已提交NoYesYesNo
    Repeatable read可重复读NoNoYesNo
    Serializable序列化NoNoNoYes

    默认的隔离级别为:RR,原因:5.1之后版本,如果Binlogog开启语句级别,必须为RR,RC可能会导致Binlog数据错误(详情);

    三、控制方式

    读未提交:每次都是读数据最新的版本(包括事务未提交的数据);
    读已提交:MVCC控制;
    可重复读:MVCC控制;
    序列化:在读取的每一行上加锁,只能按顺序进行读写;

    四、InnoDB MVCC(多版本并发控制)原理

    InnoDB引擎下的MVCC,是通过在每一行上增加两个隐藏列实现的;
    每发生一个新的事务,系统版本都会递增;
    当UPDATE数据的时候,都是先copy出来一行操作,不影响原表数据,提交之后才影响;

    一个保存创建时间(非时间值,而是系统版本号);
    一个保存删除时间(非时间值,而是系统版本号);

    4.1 SELECT

    当前查询事务系统ID:5
    结果条件:

    1. CREATE VERSION <= 5(RR级别为 <= 5,RC级别应该是直接取最大值);
    2. DELETE VERSION == null or DELETE VERSION > 5 (RR级别应该只有为空,RC级别可能有 > 5的情况);

    1可以保证读取到的行,在事务开始之前就已经存在,要么是事务自身插入或者修改的;
    2可以保证读取到的行,在事务开始之前未被删除;

    4.2 INSERT

    为新插入的每一行保存当前系统版本号作为CREATE VERSION;

    4.3 DELETE

    为删除的每一行保存当前系统版本号作为DELETE VERSION;

    4.4 UPDATE

    插入一行新的记录,保存当前系统版本号作为CREATE VERSION;
    同事保存当前系统版本号到原来的行上作为DELETE VERSION;

    五、参考:

    1. 《高性能MySQL》
    2. MySQL的四种事务隔离级别(ps:此处有测试截图,比较直观)
    ]]>
    + + sql + + + mysql + +
    + + 计算长度最大的子串长度(按空格分割) - 多种方式 + /20190326-%E8%AE%A1%E7%AE%97%E9%95%BF%E5%BA%A6%E6%9C%80%E5%A4%A7%E7%9A%84%E5%AD%90%E4%B8%B2%E9%95%BF%E5%BA%A6(%E6%8C%89%E7%A9%BA%E6%A0%BC%E5%88%86%E5%89%B2)/ + 一、背景

    今天中午一个朋友问我一个问题没来得及回,现在折腾一下;
    问题:有一个包含以N个空格分割的字符串,求最大子串的长度,要求时间空间复杂度最优;

    二、方案:

    1. 朋友给人方案:直接split分割遍历数组,找出最大值;
    2. 面试官的方案:使用StringTokenizer,找出最大值;
    3. 我想到的方案:indexOf计算index差值,找出最大值;

    三、结果:

    封装的代码比较复杂,目前来看不在么好分析时间复杂度和空间复杂度;
    于是就直观得对比了一下时间;

    //        output
    // use(nano): 15486049, by 方案3
    // use(nano): 66060102, by 方案2
    // use(nano): 80844275, by 方案1

    有时间再深入了解下StringTokenizer的源码

    四、代码对比

    String oriStr = "aa aa aaaa aaa a a a aa aa a";
    int times = 100000;
    // 自定义实现
    final long start1 = System.nanoTime();
    for (int i = 0; i < times; i++) {
    String splitStr = " ";
    findMaxLength(oriStr, splitStr);
    }
    final long end1 = System.nanoTime();

    // 利用 StringTokenizer
    final long start2 = System.nanoTime();
    for (int i = 0; i < times; i++) {
    findMaxLength4StringTokenizer(oriStr);
    }
    final long end2 = System.nanoTime();

    // 利用 Split
    final long start3 = System.nanoTime();
    for (int i = 0; i < times; i++) {
    String splitStr = " ";
    findMaxLength4Split(oriStr, splitStr);
    }
    final long end3 = System.nanoTime();

    System.out.println("use(nano): " + (end1 - start1) + ", by findMaxLength");
    System.out.println("use(nano): " + (end2 - start2) + ", by findMaxLength4StringTokenizer");
    System.out.println("use(nano): " + (end3 - start3) + ", by findMaxLength4Split");
    // output
    // use(nano): 15486049, by findMaxLength
    // use(nano): 66060102, by findMaxLength4StringTokenizer
    // use(nano): 80844275, by findMaxLength4Split

    五、完整代码

    github也有:https://github.com/hisenyuan/IDEAPractice/blob/master/src/main/java/com/hisen/interview/TestStringTokenizer.java

    /**
    * @Author hisenyuan
    * @Description 计算长度最大的子串长度(按空格分割), 自定义实现比较优
    * @Date 2019/3/26 20:53
    */
    public class TestStringTokenizer {
    public static void main(String[] args) {
    String oriStr = "aa aa aaaa aaa a a a aa aa a";
    int times = 100000;
    // 自定义实现
    final long start1 = System.nanoTime();
    for (int i = 0; i < times; i++) {
    String splitStr = " ";
    findMaxLength(oriStr, splitStr);
    }
    final long end1 = System.nanoTime();

    // 利用 StringTokenizer
    final long start2 = System.nanoTime();
    for (int i = 0; i < times; i++) {
    findMaxLength4StringTokenizer(oriStr);
    }
    final long end2 = System.nanoTime();

    // 利用 Split
    final long start3 = System.nanoTime();
    for (int i = 0; i < times; i++) {
    String splitStr = " ";
    findMaxLength4Split(oriStr, splitStr);
    }
    final long end3 = System.nanoTime();

    System.out.println("use(nano): " + (end1 - start1) + ", by findMaxLength");
    System.out.println("use(nano): " + (end2 - start2) + ", by findMaxLength4StringTokenizer");
    System.out.println("use(nano): " + (end3 - start3) + ", by findMaxLength4Split");
    // output
    // use(nano): 15486049, by findMaxLength
    // use(nano): 66060102, by findMaxLength4StringTokenizer
    // use(nano): 80844275, by findMaxLength4Split
    }

    private static void findMaxLength4StringTokenizer(String oriStr) {
    int maxLength = 0;
    final StringTokenizer stringTokenizer = new StringTokenizer(oriStr);
    while (stringTokenizer.hasMoreElements()) {
    final int length = stringTokenizer.nextToken().length();
    maxLength = length > maxLength ? length : maxLength;
    }
    // System.out.println("max: " + maxLength);
    }

    private static void findMaxLength(String oriStr, String splitStr) {
    int index = 0;
    final int maxIndex = oriStr.lastIndexOf(splitStr);
    int maxLength = oriStr.length() - maxIndex;
    while (index < maxIndex) {
    final int currentIndex = oriStr.indexOf(splitStr, index);
    int currLength = currentIndex - index;
    maxLength = currLength > maxLength ? currLength : maxLength;
    index = currentIndex + splitStr.length();
    }
    // System.out.println("max: " + maxLength);
    }

    private static void findMaxLength4Split(String oriStr, String splitStr) {
    int maxLength = 0;
    final String[] strings = oriStr.split(splitStr);
    for (String str : strings) {
    final int length = str.length();
    maxLength = length > maxLength ? length : maxLength;
    }
    // System.out.println("max: " + maxLength);
    }
    }

    ]]>
    + + java + + + java + +
    + + 认识Java I/O | BIO NIO AIO | 同步与异步 | 阻塞与非阻塞 | I/O多路复用 + /20190413-%E8%AE%A4%E8%AF%86Java%20IO%20%7C%20BIO-NIO-AIO/ + 一、名词解释

    1.1 同步与异步

    同步与异步主要是针对CPU来说的

    1. 同步:CPU必须等着结果返回,这期间不能干别的;
    2. 异步:CPU不必等着结果返回,这期间可以干别的;

    1.2 阻塞与非阻塞

    阻塞与非阻塞主要是针对I/O来说的

    1. 阻塞:I/O线程会挂起,需等待结果;
    2. 非阻塞:I/O线程不必挂起,不等待结果;

    1.3 伪异步I/O

    本质上还是同步阻塞I/O
    不过是在服务器把socket链接封装成Task提交给线程池处理
    因为有队列,所以可以突破C:S=1:1的比例

    1.4 I/O多路复用

    通过把多个I/O的阻塞复用到一个阻塞上,从而使得系统在单线程情况下可以处理多个客户端的请求。
    类似于linux的epoll、select

    1.5 多路复用器

    Selector,核心是通过Selector来轮询注册在其上的Channel
    当发现有Channel就绪就返回Channel的选择键集合,进行I/O操作;

    二、不同I/O模型对比表格

    对比项同步阻塞I/O(BIO)伪异步IO非阻塞I/O(NIO)异步I/O(AIO)
    客户端个数:I/O线程数1:1M:N(M>=N)M:1M:O
    I/O类型(是否同步)同步同步同步(I/O多路复用)异步
    I/O类型(是否阻塞)阻塞阻塞阻塞非阻塞
    API使用难度简单简单非常复杂复杂
    调试难度简单简单复杂复杂
    可靠性非常差
    吞吐量

    三、参考

    1. 《Netty权威指南 2th》
    ]]>
    + + java + + + java + +
    + + 该Java语句创建了多少个对象? + /20170120-%E8%AF%A5Java%E8%AF%AD%E5%8F%A5%E5%88%9B%E5%BB%BA%E4%BA%86%E5%A4%9A%E5%B0%91%E4%B8%AA%E5%AF%B9%E8%B1%A1%EF%BC%9F/ +
    String str = new String("java");

    答案:最少一个,最多两个

    1. java中有常量池的概念,常量池和类文件相关,其数据存放的区域是在方法区中(方法区是jvm中内存模型的概念)
    2. 因为当你使用关键字new的时候是一定会生成一个String类的实例,当你使用直接量的方式定义了一个字符串时,假如这个字符串在常量池中,则不会去实例化String,反之则会生成一个String类的实例,并置入常量池
    ]]>
    + + java + +
    + + 网关、快捷支付,代收代付,账户托管,二维码扫码支付 + /20170213-%E7%BD%91%E5%85%B3%E3%80%81%E5%BF%AB%E6%8D%B7%E6%94%AF%E4%BB%98%EF%BC%8C%E4%BB%A3%E6%94%B6%E4%BB%A3%E4%BB%98%EF%BC%8C%E8%B4%A6%E6%88%B7%E6%89%98%E7%AE%A1%EF%BC%8C%E4%BA%8C%E7%BB%B4%E7%A0%81%E6%89%AB%E7%A0%81%E6%94%AF%E4%BB%98/ + 一、网关支付

    这是在线支付的最普遍形式。
    大致支付过程:第三方支付公司作为代理(网关),接入一堆银行。用户在网关页面(可以在商户端,也可以第三方支付平台端)选择银行,页面跳转到第三方支付平台,然后重定向到对应的银行,用户在银行电子银行官网,采用网银(个人网银或企业网银)完成支付。

    网关支付分为:B2C、B2B两类。
    涉及的概念:网银支付、银行卡支付。

    我们一般说的网关支付是指在PC上的在线支付,由于国内银行基本上都要求安装对应的安全控件,且需要银行的网银客户端,这也是大家经常抱怨网银不支持MAC/Linux等操作系统、不支持除IE外的浏览器等兼容性问题。
    在手机端也有类似网关支付的形态,但由于操作过程较为麻烦,体验不好,一般都采用快捷支付等支付形式。

    二、快捷支付

    快捷支付一般是指首次需要验证卡要素,生成协议号或者TOKEN,后面支付直接凭协议号扣款。走的交易形式是消费。快捷支付相比于我们原先说的无磁无密支付[MOTO]在限额上有劣势,体验上有优势。
    一个相当于长期关系,MOTO相当于一次性关系,每次来都要输入卡要素。

    三、代收代付

    代收代付业务是我社利用自身的结算便利,接受客户的委托代为办理指定款项的收付事宜的业务。

    由中介公司或第三方代为收取和支付费用。

    顾名思义,代收代付是指先付出去,然后再收回来,金额必须相等。

    比如代办运输业务,如果是收取一定比率的手续费,就改变了性质。

    代收代付业务分录:

    代付时,借:其他应收款 贷:银行存款

    收回时,借:银行存款 贷:其他应收款

    四、账户托管

    待完善

    五、二维码扫码支付

    微信扫码业务流程说明:

    1. 商户后台系统根据用户选购的商品生成订单。
    2. 用户确认支付后调用微信支付【统一下单API】生成预支付交易;
    3. 微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
    4. 商户后台系统根据返回的code_url生成二维码。
    5. 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
    6. 微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
    7. 用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
    8. 微信支付系统根据用户授权完成支付交易。
    9. 微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
    10. 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
    11. 未收到支付通知的情况,商户后台系统调用【查询订单API】。
    12. 商户确认订单已支付后给用户发货。

    日后需要用到这方面的东西,先整理一番

    系统的学习或者理解之后再来说说深层次的东西。

    ]]>
    + + biz + + + 支付 + +
    + + 《重塑大脑,重塑人生》读后感 + 摘抄 + /20190903-%E3%80%8A%E9%87%8D%E5%A1%91%E5%A4%A7%E8%84%91%EF%BC%8C%E9%87%8D%E5%A1%91%E4%BA%BA%E7%94%9F%E3%80%8B%E8%AF%BB%E5%90%8E%E6%84%9F%20+%20%E6%91%98%E6%8A%84/ + 一、内容简介

    这是一本人类大脑可塑性研究先驱与翘楚的故事书,正是让我们用触觉看到世界的巴赫-利塔这类先驱,使得我们正在成为来自地球的神。

    二、读后感

    这本书对我来讲还是很棒的,刷新了我对大脑的认知;
    以前还停留在左右脑分工上,殊不知这是日本鬼子整出来的不严谨的概念;
    看完书,感觉对个人技术、学习方面都有帮助,增加信心,因为之前错过了学校的大把学习时间;
    运动和学习是互补的,前者产生新的神经干细胞,后者使它们的寿命延长;

    这本书我是很推荐看的;
    犹如最近看完的reids英文版文档;
    每天看一点点,持续的刺激大脑相关区域,收获还是可以;
    之前也咨询过英语比较好的同事;
    反馈说目前想提高,就拿着领域内的文档硬看,坚持下来会有收获的;

    每次看完书都会发个微博,避开朋友圈的尴尬,扩大散播范围,与更多的同好交流;

    三、内容摘要

    《重塑大脑,重塑人生》0822~0901
    在持续不断噪声环境中长大的孩子都好动和吵闹,白噪声对大脑发育也有影响。

    大脑重塑性和多巴胺有关,多巴胺可以使达成目标的那个行为的神经回路固话,连接得更紧。

    有人说A片提供的是健康的快乐,使人从性的紧张中解放,其实A片提供的是上瘾、耐药性,他会降低快乐的感受。

    所以学习没有一蹴而就之事,它是要下苦功的,我们的每一个经验都在改变大脑的连联结。

    一定要指出一点:台湾地区一直受日本的影响,社会上流行着日本人说的右脑革命、右脑开发的谬论。婴儿发展初期,大脑两边很相似,只是后期专职分化了。因此绝对没有日本人七田真说的“右脑先发展到三岁才长出脑梁到左脑”

    上学太早对身心情绪发展不好。

    大脑每做一次不同的活动时,这些活动都改变了大脑的结构,每次练习都改变了大脑神经回路,使他更适合手边的作业,假如某些部件坏掉了,其他部件有时可以接管这项工作。(有时候头皮有反应、头胀、有点不舒服的感觉就是这个原因?)

    大脑比我们能想象的还更开放。

    大脑练习:找出弱点区域然后强化这个区域的功能。

    一个认知能力的测试可以帮人们更加了解自己的大脑。

    他们发现小猫的大脑有关键期:从3~8周,在这期间接受刺激才会正常地发展。

    人的大脑也有关键期,例如语言发展有关键期,始于出生,终止于8岁到青春期之间。

    当大脑在分配处理的资源时,大脑地图遵循的法则是竞争。资源不足时,大家会抢珍贵的资源,用进废退是唯一的法则。

    当我们年龄越大,我们使用母语的频率就越高,母语占据我们语言地图的空间就越大,这也是因为我们的大脑有可塑性,我们学新语言才这么难。

    使用双语的孩子两种语言的语音都共享一个大的语言地图。

    但事实上,当我们学会一个坏习惯时,它占据了大脑地铁的空间,每次我们重复这个坏习惯,它又占据多点,让好习惯难以立足,这是为什么戒掉一个坏习惯比学它时难10倍,也是为什么同年的教育这么重要:最好一开始就教对,不要等到坏习惯已经做大有竞争优势了再去拔除它。

    训练让神经元效率更高。

    当动物有动机要学习时,大脑会弹性地对学习的需求作出反应。

    一心多用不会使你的大脑地图产生永久改变。

    多巴胺增强回馈报酬
    乙酰胆碱帮助大脑加深印象,增强记忆。

    都是噪声惹的祸:越靠近机场、公路的孩子智商越低。(持续的背景噪声对听觉皮质有很大的刺激)

    把20~20000Hz的声音集合起来就是所谓的白噪声,昼夜播放白噪声会使人失去理智,进而发疯。

    打开成年人的关键期:关键是要有激励,多巴胺、乙酰胆碱。

    有些人只有在看色情影片时才会勃起,他们很少会想不举根他们爱看色情影片有关系。

    恋人可以通过以下方式突破耐受性:浪漫的度个假,尝试新奇的活动,穿新的活动,想办法让对方惊喜。

    我们的大脑就是演化来对新奇的东西起反应了。

    当一个人成为父亲时,会分泌血管压缩素,会改变我们的大脑使你变得适合做父亲。

    催产素通常是在草原田鼠交配时分泌(人性交也会),这使它们白头偕老,不会花心。

    没有安全感的男人在做完爱后,会迅速离开,因为他害怕留下来,会被她影响;女人比较容易爱上与她性交的男人。

    爱情的“去学习”也使我们改变了对自己形象的看法。在爱上一个很有权利欲、喜欢操控和贬低别人以抬高自我的人后,会失去所有的自我,变成自我怀疑、对自己没有信心的人。

    《洛丽塔》

    把猴子一只手神经切断,另外一只手绑起来,结果后面猴子用切断了神经的手进食。

    “大量练习”(两周内集中训练)是为了引发启动大脑的可塑性改变,帮助大脑重新组织。

    利用大脑可塑性停止忧虑、偏执想法、强迫性行为和坏习惯。

    强迫症是:
    你越做,就越想做;
    越不做就越不会去想做。

    幻肢是来自大脑的重组,脸和手的神经比较靠近。

    有些女性的耳朵、锁骨和胸骨受到刺激会感到性兴奋,这三者的大脑地图都跟乳头的大脑地图紧邻;有些因阴茎癌而把阴茎切除的男性不但体验到幻阴茎,而且阴茎还会勃起。(性器官的大脑地图是在脚的旁边)

    成功切除幻肢,通过镜子让患者看到幻肢,然后就会有信号传回大脑。

    控制阀门理论,用电流刺激抑制痛的神经元,帮助疼痛阀门关闭。这个理论让西方科学家比较能接受针灸。

    仅仅用想象和视觉错觉来重建构造大脑地图,没有打针、吃药、电流刺激,就将其痛苦、难以忍受的长期痛苦减轻或治愈了

    经颅磁刺激可以无痛刺激脑神经。

    实验表明,心智练习(靠想象)是用最少的实际练习来学习新肢体技术的有效方式。

    用进废退的大脑需要外界刺激来维持它的地图。(心智下棋的例子最多)

    专家不存储答案,但是存储重要的事实和策略,用长期记忆来解决问题是很多领域专家的共同特点。

    从神经科学的观点来看,想象一个动作和执行其实没有很大差别。

    一个人想象他在使用自己的肌肉可以增加肌肉的强度。

    要发展一个新的回路,就必须阻挡或管制它的竞争者,即那些通常最常使用这个回路的信息。例如;想提高触觉等能力,就蒙住眼睛。

    通常我们会重组我们的记忆以符合新的环境。

    孩子要了解情绪,调节情绪,而产生社会化联结,必须在关键期经历几百次互动(母亲通过触觉 听觉 视觉 表现情绪)

    两三岁以前靠内隐记忆,非语言的,例如骑自行车。
    两三岁以后外显记忆开始发展,收集各种事实,事件,需要语言支持。

    有几十个研究显示睡眠帮助我们巩固学习和记忆,而这影响大脑的可塑性改变。

    婴儿时期的快速眼动睡眠是大脑可塑性发展的必要条件。

    我们常常不自觉得被过去重要人际关系的魅影缠绕而影响现在的人际关系。

    计算机听觉训练程序可以帮助老年人训练大脑。

    不要因为小事而钻牛角尖,因为压力会产生皮质激素,而这会杀死海马回的神经细胞,海马回有助于短期记忆转为长期记忆

    海马回中有神经干细胞。

    在刺激丰富的环境中(球类、跑步机等各种玩具)生长的老鼠海马回容积增大15%,神经元数目增加了15%。长期丰富刺激环境对老年人的大脑神经再生有巨大影响。

    为了使大脑保持最佳状态,我们必须学习新东西,而不是每天重复已经做的很熟练的事情,终身学习是很有必要的。

    运动和学习是互补的,前者产生新的神经干细胞,后者使它们的寿命延长。

    学一种新乐器、玩桥牌、打麻将、阅读和跳舞都可以帮助神经元的活化(需要全神贯注的活动才行)。

    大脑最怕的就是人呆在相同的环境不动,这样会使大脑萎缩加速,单调不动会减少多巴胺的分泌,破坏维持大脑可塑性的注意力系统。

    只有做自己想做的事情才会产生强烈的动机。

    半个大脑也可以活的很好。

    洗脑遵循了神经可塑性原则,可以用奖励、严厉惩罚以及大量训练的方式达到制约目的。

    刚会走路的婴儿每天看电视的时间每增加一小时,他们在7岁时有注意力缺失问题的概率就会增加10%

    ]]>
    + + read + + + 读书 + +
    + + 记录一次面试过程中交流的一些题 + /20170126-%E8%AE%B0%E5%BD%95%E4%B8%80%E6%AC%A1%E9%9D%A2%E8%AF%95%E8%BF%87%E7%A8%8B%E4%B8%AD%E4%BA%A4%E6%B5%81%E7%9A%84%E4%B8%80%E4%BA%9B%E9%A2%98/ + 以下是面试一家支付类公司的过程当中遇到的面试题

    主要是交流比较多,不是先笔试

    直接是把这些问题带入到具体的情景当中去

    可能这样更能考验出一个人真正的技术水平

    1,很多文件,读出数字,加1写回,谈谈你的想法

    2,能继承string类?

    不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变。

    3,能有个包名一样的String类?如果有一样的会调用哪个?

    4,一个主线程等待其他线程完成,如果其中有线程出错怎么办?

    把线程可能会出现的问题处理掉
    出错了能保证让他重新执行

    5,Oracle默认端口?

    1521
    +

    6,b继承a,b的对象能强转成a嘛?

    不能把一个对象强制转换成另外一个对象
    +

    7,数据库去重,删除所有重复记录,只留下一条

    DELETE
    FROM EMP E
    WHERE E.ROWID >
    (SELECT MIN(X.ROWID)
    FROM EMP X
    WHERE X.EMP_NO = E.EMP_NO);

    8,try c里面,没打印出错误来,是为什么?

    1.可能是程序执行是正确的
    2.可能存在调用,但是调用的方法里面出现了错误,没有抛出或者是运行时错误

    9,JAVA数据类型

    简单类型二进制位数封装器类
    boolean1Boolean
    byte8Byte
    char16Character
    short16Short
    Int32Integer
    long64Long
    float32Float
    double64Double
    voidVoid

    10,银行金额字段

    金融数字是BigDecimal类型
    +

    11,用什么解析XML,有什么优缺点

    DOM4J(Document Object Model for Java)
    虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支。
    它合并了许多超出基本XML文档表示的功能,
    包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。
    它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能。
    从2000下半年开始,它就一直处于开发之中。

    为支持所有这些功能,DOM4J使用接口和抽象基本类方法。
    DOM4J大量使用了API中的Collections类,但是在许多情况下,
    它还提供一些替代方法以允许更好的性能或更直接的编码方法。
    直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性。

    在添加灵活性、XPath集成和对大文档处理的目标时,
    DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作。
    它还致力于成为比JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标。
    在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为。

    DOM4J是一个非常非常优秀的Java XML API,
    具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。
    如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,
    特别值得一提的是连Sun的JAXM也在用DOM4J.

    【优点】
    ①大量使用了Java集合类,方便Java开发人员,同时提供一些提高性能的替代方法。
    ②支持XPath。
    ③有很好的性能。

    【缺点】
    ①大量使用了接口,API较为复杂。

    12,阿贾克斯熟悉吗?能发起请求下载文档吗?返回类型有哪些,遇到错误怎么提示用户

    不能发起文档下载,返回的类型只有字符型,
    出错了alert出错误

    13,jquery选择器

    jQuery 元素选择器
    jQuery 使用 CSS 选择器来选取 HTML 元素。
    $("p") 选取 <p> 元素。
    $("p.intro") 选取所有 class="intro" 的 <p> 元素。
    $("p#demo") 选取所有 id="demo" 的 <p> 元素。

    jQuery 属性选择器
    jQuery 使用 XPath 表达式来选择带有给定属性的元素。
    $("[href]") 选取所有带有 href 属性的元素。
    $("[href='#']") 选取所有带有 href 值等于 "#" 的元素。
    $("[href!='#']") 选取所有带有 href 值不等于 "#" 的元素。
    $("[href$='.jpg']") 选取所有 href 值以 ".jpg" 结尾的元素。

    14,空指针异常,怎么定位错误

    定位到出现错误的行数
    看看附近的各种调用是否存可能出现空指针异常,再慢慢排除

    ]]>
    + + java + 面试 + +
    + + 解决:html rendering error - MarkdownPad 2 + /20170119-%E8%A7%A3%E5%86%B3%EF%BC%9Ahtml-rendering-error-MarkdownPad-2/ + MarkdownPad 2在windows 10上会遇到这个错误

    官方发布了这个问题的解决办法

    详见:点击前往 页面中搜索:This view has crashed

    解决办法

    windows 10系统 需要下载 一个 awesomium_v1.6.6_sdk_win

    这是一个 HTML UI ENGINE

    下载地址:http://markdownpad.com/download/awesomium_v1.6.6_sdk_win.exe

    ]]>
    + + MarkdownPad + html rendering error + +
    + + 阿里巴巴Java开发手册,摘要&下载链接 + /20170213-%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%8C%E6%91%98%E8%A6%81-%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5/ + 阿里巴巴java开发手册

    看了几个小时,感觉还是不错

    都按规范来写,可以避免很多错误

    下载链接

    点击下载

    ]]>
    + + java + + + java + +
    + + 面试题收集 - Java - 2017 + /20170808-%E9%9D%A2%E8%AF%95%E9%A2%98%E6%94%B6%E9%9B%86%20-%20Java%20-%202017/ + 我只是收集一下题目,检查一下自己。

    京东平台产品研发部 - Java

    背景:一年工作经验,做电子政务

    1. 一个数组 i[] = [-1000 ~ 1000] 中的任意一些数字,求乘积最大的三个数。
    2. static都能用在哪里?
    3. 1M 等于多少个1
    4. 面向对象的特性
    5. 写出两种单例模式
    6. HashMap有哪些方法
    7. HashMap的底层实现
    8. List和Set的区别
    9. 重载和重写的区别,多态如何体现
    10. 判断字符串是否含有此Url:www.jd.com
      11.

      Long l1 = new Long(1024L);
      Long l2 = new Long(1024L);
      System.out.println(l1 == l2);//false
      Integer i1 = 100;
      Integer i2 = 100;
      System.out.println(i1 == i2);//true
      i1 = 100;
      i2 = 100;
      System.out.println(i1 == i2);//true
    11. Spring自定义注解、拦截器相关

    12. 为什么使用注解、有什么好处
    13. Spring中运用了哪些设计模式
    14. Sping和SpringBoot各有什么优缺点
    15. Spring的事物隔离机制
    16. 说说AOP、DI、IOC
    17. redis等用过没
    18. 缓存和memorycache
    19. http://www.jd.com/...username=... 为了防止sql注入,怎么写正则。
    20. mybatis如何防止sql注入
    21. mybatis使用变量时怎么表示
    22. 使用js遍历json数据
    23. 使用Map遍历json数据
    24. elasticsearch相关
    25. 你曾经在社区接触过的技术有哪些
    26. 项目相关:做了哪些部分、职责、用到哪些技术、设计了哪些
    27. 介绍一下RPC调用

    谷歌淘来的一个总结

    面试题总结 —— JAVA高级工程师

    部分答案

    1. 一个数组 i[] = [-1000 ~ 1000] 中的任意一些数字,求乘积最大的三个数。

      /**
      *
      * 功能:求解一维无序数组中三个数字乘积最大值(正负零均存在)
      */
      @Test
      public void getMaxThreeNum() {
      // 首先对数组进行升序排序
      // 1. 数组左端是0,那么不存在负数,最大值为:i[-1]*i[-2]*i[-3]
      // 2. 数组左端是正数,同上
      // 3. 数组左端是负数,那么可能会负负得正,由于是升序,最大值为:i[0]*i[1]*i[-1]
      // 4. 去上面两个的最大值即可
      int[] i = {0,-10, -9, -8, 7, 6, 5, 4, 3, 2, 1};
      Arrays.sort(i);
      int length = i.length;
      int a = i[0] * i[1] * i[length - 1];
      int b = i[length - 1] * i[length - 2] * i[length - 3];
      if (a > b) {//-10 * -9 * 7 = 630
      System.out.println(i[0] + " * " + i[1] + " * " + i[length - 1] + " = " + a);
      } else {
      System.out.println(i[length - 1] + " * " + i[length - 2] + " * " + i[length - 3] + " = " + b);
      }
      }
    2. 1M 等于多少个1

      @Test
      public void get1McanHoldInt(){
      // int 占用4个字节 4Byte = 4 * 8bit
      System.out.println(1*1024*1024/4);
      }
    3. 写出两种单例模式

      //懒汉模式
      public class LazySingleton {
      private static LazySingleton instance = null;
      private LazySingleton(){
      super();
      }
      public static LazySingleton getInstance(){
      if(instance == null){
      // 静态方法,使用当前类本身充当进程锁
      synchronized(LazySingleton.class){
      instance = new LazySingleton();
      }
      }
      return instance;
      }
      }

      // 饿汉式单例模式
      public class EagerSingleton {
      private static EagerSingleton instance = new EagerSingleton();
      private EagerSingleton(){
      super();
      }
      // 不需要同步(类加载时已经初始化,不存在多线程的问题)
      // 始终只有一个对象
      public EagerSingleton getInstance(){
      return instance;
      }
      }
    4. 判断字符串是否含有此Url:www.jd.com

      @Test
      public void isHaveUrl(){
      String url = "这是京东官网:www.jd.com";
      Pattern pattern = Pattern.compile(".*www.jd.com.*");
      Matcher matcher = pattern.matcher(url);
      if (matcher.find()){
      System.out.println(matcher.group());
      }else {
      System.out.println("未匹配");
      }
      }
    5. http://www.jd.com/...username=... 为了防止sql注入,怎么写正则。

      将包含有 单引号('),分号(;) 和 注释符号(--)的语句给替换掉来防止SQL注入
      str.replaceAll("([';])+|(--)+","");
    6. mybatis如何防止sql注入

      防护一:sql语句不在使用${}表达式,改为#{}
      <select id="getBlogById" resultType="Blog" parameterType=”int”>
      SELECT id,title,author,content
      FROM blog
      WHERE id=#{id}
      </select>

      编译后的SQL:SELECT id,title,author,content FROM blog WHERE id = ? (能避免sql注入)

      <select id="getBlogById" resultType="Blog" parameterType=”int”>
      SELECT id,title,author,content
      FROM blog
      WHERE id=${id} <!--假如id= 3 -->
      </select>

      编译后的SQL:SELECT id,title,author,content FROM blog WHERE id = 3 (无法避免sql注入)

      SQL注入只能对编译过程起作用
      #{}:相当于JDBC中的PreparedStatement
      ${}:是输出变量的值

      #{}是经过预编译的,是安全的;
      ${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。


      防护二:采用阿里的druid数据连接池,添加如下配置
      <!-- 配置监控统计拦截的filters,和防sql注入 -->
      <property name="filters" value="stat,wall" />

      防护三:使用正则表达式过滤输入参数
    7. mybatis使用变量时怎么表示

      #与$的区别
      1.#是把传入的数据当作字符串,如#field#传入的是id,则sql语句生成是这样,order by "id",这当然会报错..
      2.$传入的数据直接生成在sql里,如$field$传入的是id,则sql语句生成是这样,order by id, 这就对了.
      3.#方式能够很大程度防止sql注入.
      4.$方式无法防止sql注入.
      5.$方式一般用于传入数据库对象.例如传入表名.
      6.一般能用#的就别用$.
    8. 使用js遍历json数据

      var data=[{name:"a",age:12},{name:"b",age:11},{name:"c",age:13},{name:"d",age:14}];  
      for(var o in data){
      alert("text:"+data[o].name+" value:"+data[o].age );
      }
    ]]>
    + + java + + + java + 面试 + +
    + + 读取文件路径相关问题 + /20190716-%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/ + 零、本文由来

    感觉从业这么久以来,读取文件路径相关问题一直是一个痛
    用的很少,之前也解决过相关的问题
    但是得用这种路径

    src/test/resources/test/test.txt

    上面这种路径如果在代码中的话,src/test/resources是不会存在的,会出现问题
    本次学到了一个新的方法,Class.getResourceAsStream();
    需要的路径在资源文件夹下面即可,填写路径:/test/test.txt
    打包最终的路径会在:WEB-INF/classes/test/test.txt

    ps:因maven打包配置不同,最终resources资源文件夹下的路径是不同的,注意src的坑即可

    一、文件结构

    │   └── test
    │   ├── java
    │   │   └── mq
    │   │   └── Test.java
    │   └── resources
    │   ├── test
    │   │   └── test.txt

    二、读取代码

    @RunWith(value = SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"/spring-config.xml"})
    public class Test {
    @Test
    public void test() throws IOException {
    InputStream inputStream = Test.class.getResourceAsStream("/test/test.txt");
    byte[] buffer = new byte[1024];
    int len;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    while ((len = inputStream.read(buffer)) != -1) {
    bos.write(buffer, 0, len);
    }
    bos.close();
    String res = bos.toString();
    System.out.println(res);
    }
    }
    ]]>
    + + java + + + java + +
    + + 业务安全的资源层攻防介绍 + /20191023-%E4%B8%9A%E5%8A%A1%E5%AE%89%E5%85%A8%E7%9A%84%E8%B5%84%E6%BA%90%E5%B1%82%E6%94%BB%E9%98%B2%E4%BB%8B%E7%BB%8D/ + 一些简单的黑产攻防介绍

    1. 修改前置摄像头,直接播放本地视频或者图片(色情诈骗、网约车司机人脸识别);
    2. 恶意注册靶机系统:黑卡猫池、群控、云控、箱控(12个主板,模拟120台手机);
    3. 模拟点击脚本在黑产中使用比例逐年增高,主要是效果好很难区分是否为真实用户;
    4. ROS软路由(可管理250个IP)、秒拨(每次拨号IP改变);
    5. 黑产变现的渠道主要有:羊毛、引流(注册账号发布信息);
    6. 很多黑产行为在单个平台来讲都是一次性活动;
    7. 改机工具:类沙箱,可以更改大量手机信息,提供快照功能,方便上下游复现现场环境,权限高,欺骗效果好;
    8. 针对改机工具一般都进行恶意环境检测,指令集中是否有hook、cpu架构、文件目录等;
    9. 防护方针:增加黑产的量化攻击成本(时间、资金);
    10. 给出模糊的错误提示,而不是准确的,否则黑产很容易试出策略;
    11. 不要在注册环节就大量拦截,要考虑用户体验,也要收集黑产行为信息,杀量化攻击;
    12. 忘记密码可能会被利用,找回密码的机制要严格审核(匹配通讯录等);
    13. 黑产资源成本高,全网来讲资源重复利用率高;
    14. IP资源、定位、手机、脚本、改机工具、云控等方便规模化,精细化运作;

    从中不难看出源头是黑卡、被利用的宽带账号、也有可能是路由器、物联网设备等;
    运营商加强实名认证
    宽带拨号频率限制
    网络设备安全加固,关闭特殊权限端口,禁用弱密码

    打击网络黑产交易,防止黑产规模化,产业化趋势;

    蜜罐系统收集黑产IP
    提供IP代理给黑产使用了解对方信息

    ]]>
    + + biz + + + 安全 + +