diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/404.html b/404.html new file mode 100644 index 000000000..74a2ba82a --- /dev/null +++ b/404.html @@ -0,0 +1 @@ + 404: Page not found | Blogs
Home 404: Page not found
404: Page not found
Cancel

404: Page not found

Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.

diff --git a/about/index.html b/about/index.html new file mode 100644 index 000000000..b7fcb49e6 --- /dev/null +++ b/about/index.html @@ -0,0 +1 @@ + About | Blogs
Home About
About
Cancel

About

(。・∀・)ノ゙Hi. My name is Linze. We can make a friend, I suppose. I hope you can enjoy my blogs.

Personal summary

My research interests are …

Education

  • September 2022 - June 2025(expected) M.E. Software Engineering, East Normal University of China
    • Expected to graduate in June 2025
  • September 2018 - June 2022 B.E. Computer Science, Zhejiang University of Technology
    • Rank: 4 %, CET-4: 536, CET-6: 516

Skills

  • C++, Python, Markdown, LaTex

Employment

  • December 2021 - February 2022 Crawler Engineer, Mogujie

In this role I was doing the job with crawling the commodity information of foreign e-commerce websites, such as size or color, etc.

Publications

  • YYYY: Name et al. Article title. Journal name. Link/DOI

  • To be continued…

Awards

  • YYYY: Description of award, certificate, supporting info etc.

  • To be continued…

Contact

diff --git a/app.js b/app.js new file mode 100644 index 000000000..c6d1832d6 --- /dev/null +++ b/app.js @@ -0,0 +1 @@ +const $notification = $('#notification'); const $btnRefresh = $('#notification .toast-body>button'); if ('serviceWorker' in navigator) { /* Registering Service Worker */ navigator.serviceWorker.register('/sw.js') .then(registration => { /* in case the user ignores the notification */ if (registration.waiting) { $notification.toast('show'); } registration.addEventListener('updatefound', () => { registration.installing.addEventListener('statechange', () => { if (registration.waiting) { if (navigator.serviceWorker.controller) { $notification.toast('show'); } } }); }); $btnRefresh.click(() => { if (registration.waiting) { registration.waiting.postMessage('SKIP_WAITING'); } $notification.toast('hide'); }); }); let refreshing = false; /* Detect controller change and refresh all the opened tabs */ navigator.serviceWorker.addEventListener('controllerchange', () => { if (!refreshing) { window.location.reload(); refreshing = true; } }); } diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 000000000..690f88020 --- /dev/null +++ b/archives/index.html @@ -0,0 +1 @@ + Archives | Blogs
Home Archives
Archives
Cancel
diff --git a/assets/attachment/Software_copyright.zip b/assets/attachment/Software_copyright.zip new file mode 100644 index 000000000..50bd02be1 Binary files /dev/null and b/assets/attachment/Software_copyright.zip differ diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 000000000..2db4373b1 --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,11 @@ +/*! + * The styles for Jekyll theme Chirpy + * + * Chirpy v5.6.1 (https://github.com/cotes2020/jekyll-theme-chirpy) + * © 2019 Cotes Chung + * MIT Licensed + */#search-results a,h5,h4,h3,h2,h1{color:var(--heading-color);font-weight:400;font-family:Lato,"Microsoft Yahei",sans-serif}#core-wrapper h5,#core-wrapper h4,#core-wrapper h3,#core-wrapper h2{margin-top:2.5rem;margin-bottom:1.25rem}#core-wrapper h5:focus,#core-wrapper h4:focus,#core-wrapper h3:focus,#core-wrapper h2:focus{outline:none}h5 .anchor,h4 .anchor,h3 .anchor,h2 .anchor{font-size:80%}@media(hover: hover){h5 .anchor,h4 .anchor,h3 .anchor,h2 .anchor{visibility:hidden;opacity:0;transition:opacity .25s ease-in,visibility 0s ease-in .25s}h5:hover .anchor,h4:hover .anchor,h3:hover .anchor,h2:hover .anchor{visibility:visible;opacity:1;transition:opacity .25s ease-in,visibility 0s ease-in 0s}}.post-tag:hover,.tag:hover{background:var(--tag-hover);transition:background .35s ease-in-out}.table-wrapper>table tbody tr td,.table-wrapper>table thead th{padding:.4rem 1rem;font-size:95%;white-space:nowrap}#page-category a:hover,#page-tag a:hover,.post-tail-wrapper .license-wrapper>a:hover,#search-results a:hover,#topbar #breadcrumb a:hover,.post-content a:not(.img-link):hover,.post-meta a:not([class]):hover,#access-lastmod a:hover,footer a:hover{color:#d2603a !important;border-bottom:1px solid #d2603a;text-decoration:none}#search-results a,#search-hints .post-tag,a{color:var(--link-color)}.post-tail-wrapper .post-meta a:not(:hover),.post-content a:not(.img-link){border-bottom:1px solid var(--link-underline-color)}#sidebar .sidebar-bottom .mode-toggle>i,#sidebar .sidebar-bottom a,#sidebar .nav-item:not(.active)>a,#sidebar .site-title a{transition:color .35s ease-in-out}#sidebar .sidebar-bottom .icon-border,.post-content a.popup,i.far,i.fas,.code-header{-webkit-user-select:none;-moz-user-select:none;user-select:none}#page-category ul>li>a,#page-tag ul>li>a,#core-wrapper .categories a:not(:hover),#core-wrapper #tags a:not(:hover),#core-wrapper #archives a:not(:hover),#search-results a,#access-lastmod a{border-bottom:none}.post-tail-wrapper .share-wrapper .share-icons>i,#search-cancel,.code-header button{cursor:pointer}#related-posts em,#post-list .post-preview .post-meta em,.post-meta em{font-style:normal}.preview-img img,.preview-img,.embed-video,blockquote[class^=prompt-],.code-header button,.highlight,.highlighter-rouge{border-radius:6px}.post-content a.popup+em,img[data-src]+em{display:block;text-align:center;font-style:normal;font-size:80%;padding:0;color:#6d6c6c}#sidebar .sidebar-bottom .mode-toggle,#sidebar a{color:rgba(117,117,117,.9);-webkit-user-select:none;-moz-user-select:none;user-select:none}@media(prefers-color-scheme: light){html:not([data-mode]),html[data-mode=light]{--highlight-bg-color: #f7f7f7;--highlighter-rouge-color: #2f2f2f;--highlight-lineno-color: #c2c6cc;--inline-code-bg: #f3f3f3;--code-header-text-color: #a3a3b1;--code-header-muted-color: #ebebeb;--code-header-icon-color: #d1d1d1;--clipboard-checked-color: #43c743}html:not([data-mode]) .highlight .hll,html[data-mode=light] .highlight .hll{background-color:#ffc}html:not([data-mode]) .highlight .c,html[data-mode=light] .highlight .c{color:#998;font-style:italic}html:not([data-mode]) .highlight .err,html[data-mode=light] .highlight .err{color:#a61717;background-color:#e3d2d2}html:not([data-mode]) .highlight .k,html[data-mode=light] .highlight .k{color:#000;font-weight:bold}html:not([data-mode]) .highlight .o,html[data-mode=light] .highlight .o{color:#000;font-weight:bold}html:not([data-mode]) .highlight .cm,html[data-mode=light] .highlight .cm{color:#998;font-style:italic}html:not([data-mode]) .highlight .cp,html[data-mode=light] .highlight .cp{color:#999;font-weight:bold;font-style:italic}html:not([data-mode]) .highlight .c1,html[data-mode=light] .highlight .c1{color:#998;font-style:italic}html:not([data-mode]) .highlight .cs,html[data-mode=light] .highlight .cs{color:#999;font-weight:bold;font-style:italic}html:not([data-mode]) .highlight .gd,html[data-mode=light] .highlight .gd{color:#d01040;background-color:#fdd}html:not([data-mode]) .highlight .ge,html[data-mode=light] .highlight .ge{color:#000;font-style:italic}html:not([data-mode]) .highlight .gr,html[data-mode=light] .highlight .gr{color:#a00}html:not([data-mode]) .highlight .gh,html[data-mode=light] .highlight .gh{color:#999}html:not([data-mode]) .highlight .gi,html[data-mode=light] .highlight .gi{color:teal;background-color:#dfd}html:not([data-mode]) .highlight .go,html[data-mode=light] .highlight .go{color:#888}html:not([data-mode]) .highlight .gp,html[data-mode=light] .highlight .gp{color:#555}html:not([data-mode]) .highlight .gs,html[data-mode=light] .highlight .gs{font-weight:bold}html:not([data-mode]) .highlight .gu,html[data-mode=light] .highlight .gu{color:#aaa}html:not([data-mode]) .highlight .gt,html[data-mode=light] .highlight .gt{color:#a00}html:not([data-mode]) .highlight .kc,html[data-mode=light] .highlight .kc{color:#000;font-weight:bold}html:not([data-mode]) .highlight .kd,html[data-mode=light] .highlight .kd{color:#000;font-weight:bold}html:not([data-mode]) .highlight .kn,html[data-mode=light] .highlight .kn{color:#000;font-weight:bold}html:not([data-mode]) .highlight .kp,html[data-mode=light] .highlight .kp{color:#000;font-weight:bold}html:not([data-mode]) .highlight .kr,html[data-mode=light] .highlight .kr{color:#000;font-weight:bold}html:not([data-mode]) .highlight .kt,html[data-mode=light] .highlight .kt{color:#458;font-weight:bold}html:not([data-mode]) .highlight .m,html[data-mode=light] .highlight .m{color:#099}html:not([data-mode]) .highlight .s,html[data-mode=light] .highlight .s{color:#d01040}html:not([data-mode]) .highlight .na,html[data-mode=light] .highlight .na{color:teal}html:not([data-mode]) .highlight .nb,html[data-mode=light] .highlight .nb{color:#0086b3}html:not([data-mode]) .highlight .nc,html[data-mode=light] .highlight .nc{color:#458;font-weight:bold}html:not([data-mode]) .highlight .no,html[data-mode=light] .highlight .no{color:teal}html:not([data-mode]) .highlight .nd,html[data-mode=light] .highlight .nd{color:#3c5d5d;font-weight:bold}html:not([data-mode]) .highlight .ni,html[data-mode=light] .highlight .ni{color:purple}html:not([data-mode]) .highlight .ne,html[data-mode=light] .highlight .ne{color:#900;font-weight:bold}html:not([data-mode]) .highlight .nf,html[data-mode=light] .highlight .nf{color:#900;font-weight:bold}html:not([data-mode]) .highlight .nl,html[data-mode=light] .highlight .nl{color:#900;font-weight:bold}html:not([data-mode]) .highlight .nn,html[data-mode=light] .highlight .nn{color:#555}html:not([data-mode]) .highlight .nt,html[data-mode=light] .highlight .nt{color:navy}html:not([data-mode]) .highlight .nv,html[data-mode=light] .highlight .nv{color:teal}html:not([data-mode]) .highlight .ow,html[data-mode=light] .highlight .ow{color:#000;font-weight:bold}html:not([data-mode]) .highlight .w,html[data-mode=light] .highlight .w{color:#bbb}html:not([data-mode]) .highlight .mf,html[data-mode=light] .highlight .mf{color:#099}html:not([data-mode]) .highlight .mh,html[data-mode=light] .highlight .mh{color:#099}html:not([data-mode]) .highlight .mi,html[data-mode=light] .highlight .mi{color:#099}html:not([data-mode]) .highlight .mo,html[data-mode=light] .highlight .mo{color:#099}html:not([data-mode]) .highlight .sb,html[data-mode=light] .highlight .sb{color:#d01040}html:not([data-mode]) .highlight .sc,html[data-mode=light] .highlight .sc{color:#d01040}html:not([data-mode]) .highlight .sd,html[data-mode=light] .highlight .sd{color:#d01040}html:not([data-mode]) .highlight .s2,html[data-mode=light] .highlight .s2{color:#d01040}html:not([data-mode]) .highlight .se,html[data-mode=light] .highlight .se{color:#d01040}html:not([data-mode]) .highlight .sh,html[data-mode=light] .highlight .sh{color:#d01040}html:not([data-mode]) .highlight .si,html[data-mode=light] .highlight .si{color:#d01040}html:not([data-mode]) .highlight .sx,html[data-mode=light] .highlight .sx{color:#d01040}html:not([data-mode]) .highlight .sr,html[data-mode=light] .highlight .sr{color:#009926}html:not([data-mode]) .highlight .s1,html[data-mode=light] .highlight .s1{color:#d01040}html:not([data-mode]) .highlight .ss,html[data-mode=light] .highlight .ss{color:#990073}html:not([data-mode]) .highlight .bp,html[data-mode=light] .highlight .bp{color:#999}html:not([data-mode]) .highlight .vc,html[data-mode=light] .highlight .vc{color:teal}html:not([data-mode]) .highlight .vg,html[data-mode=light] .highlight .vg{color:teal}html:not([data-mode]) .highlight .vi,html[data-mode=light] .highlight .vi{color:teal}html:not([data-mode]) .highlight .il,html[data-mode=light] .highlight .il{color:#099}html:not([data-mode]) [class^=prompt-],html[data-mode=light] [class^=prompt-]{--inline-code-bg: #fbfafa;--highlighter-rouge-color: rgb(82, 82, 82)}html[data-mode=dark]{--highlight-bg-color: #252525;--highlighter-rouge-color: #de6b18;--highlight-lineno-color: #6c6c6d;--inline-code-bg: #272822;--code-header-text-color: #6a6a6a;--code-header-muted-color: rgb(60, 60, 60);--code-header-icon-color: rgb(86, 86, 86);--clipboard-checked-color: #2bcc2b;--filepath-text-color: #bdbdbd}html[data-mode=dark] pre{color:#bfbfbf}html[data-mode=dark] .highlight .gp{color:#818c96}html[data-mode=dark] .highlight pre{background-color:var(--highlight-bg-color)}html[data-mode=dark] .highlight .hll{background-color:var(--highlight-bg-color)}html[data-mode=dark] .highlight .c{color:#75715e}html[data-mode=dark] .highlight .err{color:#960050;background-color:#1e0010}html[data-mode=dark] .highlight .k{color:#66d9ef}html[data-mode=dark] .highlight .l{color:#ae81ff}html[data-mode=dark] .highlight .n{color:#f8f8f2}html[data-mode=dark] .highlight .o{color:#f92672}html[data-mode=dark] .highlight .p{color:#f8f8f2}html[data-mode=dark] .highlight .cm{color:#75715e}html[data-mode=dark] .highlight .cp{color:#75715e}html[data-mode=dark] .highlight .c1{color:#75715e}html[data-mode=dark] .highlight .cs{color:#75715e}html[data-mode=dark] .highlight .ge{color:inherit;font-style:italic}html[data-mode=dark] .highlight .gs{font-weight:bold}html[data-mode=dark] .highlight .kc{color:#66d9ef}html[data-mode=dark] .highlight .kd{color:#66d9ef}html[data-mode=dark] .highlight .kn{color:#f92672}html[data-mode=dark] .highlight .kp{color:#66d9ef}html[data-mode=dark] .highlight .kr{color:#66d9ef}html[data-mode=dark] .highlight .kt{color:#66d9ef}html[data-mode=dark] .highlight .ld{color:#e6db74}html[data-mode=dark] .highlight .m{color:#ae81ff}html[data-mode=dark] .highlight .s{color:#e6db74}html[data-mode=dark] .highlight .na{color:#a6e22e}html[data-mode=dark] .highlight .nb{color:#f8f8f2}html[data-mode=dark] .highlight .nc{color:#a6e22e}html[data-mode=dark] .highlight .no{color:#66d9ef}html[data-mode=dark] .highlight .nd{color:#a6e22e}html[data-mode=dark] .highlight .ni{color:#f8f8f2}html[data-mode=dark] .highlight .ne{color:#a6e22e}html[data-mode=dark] .highlight .nf{color:#a6e22e}html[data-mode=dark] .highlight .nl{color:#f8f8f2}html[data-mode=dark] .highlight .nn{color:#f8f8f2}html[data-mode=dark] .highlight .nx{color:#a6e22e}html[data-mode=dark] .highlight .py{color:#f8f8f2}html[data-mode=dark] .highlight .nt{color:#f92672}html[data-mode=dark] .highlight .nv{color:#f8f8f2}html[data-mode=dark] .highlight .ow{color:#f92672}html[data-mode=dark] .highlight .w{color:#f8f8f2}html[data-mode=dark] .highlight .mf{color:#ae81ff}html[data-mode=dark] .highlight .mh{color:#ae81ff}html[data-mode=dark] .highlight .mi{color:#ae81ff}html[data-mode=dark] .highlight .mo{color:#ae81ff}html[data-mode=dark] .highlight .sb{color:#e6db74}html[data-mode=dark] .highlight .sc{color:#e6db74}html[data-mode=dark] .highlight .sd{color:#e6db74}html[data-mode=dark] .highlight .s2{color:#e6db74}html[data-mode=dark] .highlight .se{color:#ae81ff}html[data-mode=dark] .highlight .sh{color:#e6db74}html[data-mode=dark] .highlight .si{color:#e6db74}html[data-mode=dark] .highlight .sx{color:#e6db74}html[data-mode=dark] .highlight .sr{color:#e6db74}html[data-mode=dark] .highlight .s1{color:#e6db74}html[data-mode=dark] .highlight .ss{color:#e6db74}html[data-mode=dark] .highlight .bp{color:#f8f8f2}html[data-mode=dark] .highlight .vc{color:#f8f8f2}html[data-mode=dark] .highlight .vg{color:#f8f8f2}html[data-mode=dark] .highlight .vi{color:#f8f8f2}html[data-mode=dark] .highlight .il{color:#ae81ff}html[data-mode=dark] .highlight .gu{color:#75715e}html[data-mode=dark] .highlight .gd{color:#f92672;background-color:#561c08}html[data-mode=dark] .highlight .gi{color:#a6e22e;background-color:#0b5858}}@media(prefers-color-scheme: dark){html:not([data-mode]),html[data-mode=dark]{--highlight-bg-color: #252525;--highlighter-rouge-color: #de6b18;--highlight-lineno-color: #6c6c6d;--inline-code-bg: #272822;--code-header-text-color: #6a6a6a;--code-header-muted-color: rgb(60, 60, 60);--code-header-icon-color: rgb(86, 86, 86);--clipboard-checked-color: #2bcc2b;--filepath-text-color: #bdbdbd}html:not([data-mode]) pre,html[data-mode=dark] pre{color:#bfbfbf}html:not([data-mode]) .highlight .gp,html[data-mode=dark] .highlight .gp{color:#818c96}html:not([data-mode]) .highlight pre,html[data-mode=dark] .highlight pre{background-color:var(--highlight-bg-color)}html:not([data-mode]) .highlight .hll,html[data-mode=dark] .highlight .hll{background-color:var(--highlight-bg-color)}html:not([data-mode]) .highlight .c,html[data-mode=dark] .highlight .c{color:#75715e}html:not([data-mode]) .highlight .err,html[data-mode=dark] .highlight .err{color:#960050;background-color:#1e0010}html:not([data-mode]) .highlight .k,html[data-mode=dark] .highlight .k{color:#66d9ef}html:not([data-mode]) .highlight .l,html[data-mode=dark] .highlight .l{color:#ae81ff}html:not([data-mode]) .highlight .n,html[data-mode=dark] .highlight .n{color:#f8f8f2}html:not([data-mode]) .highlight .o,html[data-mode=dark] .highlight .o{color:#f92672}html:not([data-mode]) .highlight .p,html[data-mode=dark] .highlight .p{color:#f8f8f2}html:not([data-mode]) .highlight .cm,html[data-mode=dark] .highlight .cm{color:#75715e}html:not([data-mode]) .highlight .cp,html[data-mode=dark] .highlight .cp{color:#75715e}html:not([data-mode]) .highlight .c1,html[data-mode=dark] .highlight .c1{color:#75715e}html:not([data-mode]) .highlight .cs,html[data-mode=dark] .highlight .cs{color:#75715e}html:not([data-mode]) .highlight .ge,html[data-mode=dark] .highlight .ge{color:inherit;font-style:italic}html:not([data-mode]) .highlight .gs,html[data-mode=dark] .highlight .gs{font-weight:bold}html:not([data-mode]) .highlight .kc,html[data-mode=dark] .highlight .kc{color:#66d9ef}html:not([data-mode]) .highlight .kd,html[data-mode=dark] .highlight .kd{color:#66d9ef}html:not([data-mode]) .highlight .kn,html[data-mode=dark] .highlight .kn{color:#f92672}html:not([data-mode]) .highlight .kp,html[data-mode=dark] .highlight .kp{color:#66d9ef}html:not([data-mode]) .highlight .kr,html[data-mode=dark] .highlight .kr{color:#66d9ef}html:not([data-mode]) .highlight .kt,html[data-mode=dark] .highlight .kt{color:#66d9ef}html:not([data-mode]) .highlight .ld,html[data-mode=dark] .highlight .ld{color:#e6db74}html:not([data-mode]) .highlight .m,html[data-mode=dark] .highlight .m{color:#ae81ff}html:not([data-mode]) .highlight .s,html[data-mode=dark] .highlight .s{color:#e6db74}html:not([data-mode]) .highlight .na,html[data-mode=dark] .highlight .na{color:#a6e22e}html:not([data-mode]) .highlight .nb,html[data-mode=dark] .highlight .nb{color:#f8f8f2}html:not([data-mode]) .highlight .nc,html[data-mode=dark] .highlight .nc{color:#a6e22e}html:not([data-mode]) .highlight .no,html[data-mode=dark] .highlight .no{color:#66d9ef}html:not([data-mode]) .highlight .nd,html[data-mode=dark] .highlight .nd{color:#a6e22e}html:not([data-mode]) .highlight .ni,html[data-mode=dark] .highlight .ni{color:#f8f8f2}html:not([data-mode]) .highlight .ne,html[data-mode=dark] .highlight .ne{color:#a6e22e}html:not([data-mode]) .highlight .nf,html[data-mode=dark] .highlight .nf{color:#a6e22e}html:not([data-mode]) .highlight .nl,html[data-mode=dark] .highlight .nl{color:#f8f8f2}html:not([data-mode]) .highlight .nn,html[data-mode=dark] .highlight .nn{color:#f8f8f2}html:not([data-mode]) .highlight .nx,html[data-mode=dark] .highlight .nx{color:#a6e22e}html:not([data-mode]) .highlight .py,html[data-mode=dark] .highlight .py{color:#f8f8f2}html:not([data-mode]) .highlight .nt,html[data-mode=dark] .highlight .nt{color:#f92672}html:not([data-mode]) .highlight .nv,html[data-mode=dark] .highlight .nv{color:#f8f8f2}html:not([data-mode]) .highlight .ow,html[data-mode=dark] .highlight .ow{color:#f92672}html:not([data-mode]) .highlight .w,html[data-mode=dark] .highlight .w{color:#f8f8f2}html:not([data-mode]) .highlight .mf,html[data-mode=dark] .highlight .mf{color:#ae81ff}html:not([data-mode]) .highlight .mh,html[data-mode=dark] .highlight .mh{color:#ae81ff}html:not([data-mode]) .highlight .mi,html[data-mode=dark] .highlight .mi{color:#ae81ff}html:not([data-mode]) .highlight .mo,html[data-mode=dark] .highlight .mo{color:#ae81ff}html:not([data-mode]) .highlight .sb,html[data-mode=dark] .highlight .sb{color:#e6db74}html:not([data-mode]) .highlight .sc,html[data-mode=dark] .highlight .sc{color:#e6db74}html:not([data-mode]) .highlight .sd,html[data-mode=dark] .highlight .sd{color:#e6db74}html:not([data-mode]) .highlight .s2,html[data-mode=dark] .highlight .s2{color:#e6db74}html:not([data-mode]) .highlight .se,html[data-mode=dark] .highlight .se{color:#ae81ff}html:not([data-mode]) .highlight .sh,html[data-mode=dark] .highlight .sh{color:#e6db74}html:not([data-mode]) .highlight .si,html[data-mode=dark] .highlight .si{color:#e6db74}html:not([data-mode]) .highlight .sx,html[data-mode=dark] .highlight .sx{color:#e6db74}html:not([data-mode]) .highlight .sr,html[data-mode=dark] .highlight .sr{color:#e6db74}html:not([data-mode]) .highlight .s1,html[data-mode=dark] .highlight .s1{color:#e6db74}html:not([data-mode]) .highlight .ss,html[data-mode=dark] .highlight .ss{color:#e6db74}html:not([data-mode]) .highlight .bp,html[data-mode=dark] .highlight .bp{color:#f8f8f2}html:not([data-mode]) .highlight .vc,html[data-mode=dark] .highlight .vc{color:#f8f8f2}html:not([data-mode]) .highlight .vg,html[data-mode=dark] .highlight .vg{color:#f8f8f2}html:not([data-mode]) .highlight .vi,html[data-mode=dark] .highlight .vi{color:#f8f8f2}html:not([data-mode]) .highlight .il,html[data-mode=dark] .highlight .il{color:#ae81ff}html:not([data-mode]) .highlight .gu,html[data-mode=dark] .highlight .gu{color:#75715e}html:not([data-mode]) .highlight .gd,html[data-mode=dark] .highlight .gd{color:#f92672;background-color:#561c08}html:not([data-mode]) .highlight .gi,html[data-mode=dark] .highlight .gi{color:#a6e22e;background-color:#0b5858}html[data-mode=light]{--highlight-bg-color: #f7f7f7;--highlighter-rouge-color: #2f2f2f;--highlight-lineno-color: #c2c6cc;--inline-code-bg: #f3f3f3;--code-header-text-color: #a3a3b1;--code-header-muted-color: #ebebeb;--code-header-icon-color: #d1d1d1;--clipboard-checked-color: #43c743}html[data-mode=light] .highlight .hll{background-color:#ffc}html[data-mode=light] .highlight .c{color:#998;font-style:italic}html[data-mode=light] .highlight .err{color:#a61717;background-color:#e3d2d2}html[data-mode=light] .highlight .k{color:#000;font-weight:bold}html[data-mode=light] .highlight .o{color:#000;font-weight:bold}html[data-mode=light] .highlight .cm{color:#998;font-style:italic}html[data-mode=light] .highlight .cp{color:#999;font-weight:bold;font-style:italic}html[data-mode=light] .highlight .c1{color:#998;font-style:italic}html[data-mode=light] .highlight .cs{color:#999;font-weight:bold;font-style:italic}html[data-mode=light] .highlight .gd{color:#d01040;background-color:#fdd}html[data-mode=light] .highlight .ge{color:#000;font-style:italic}html[data-mode=light] .highlight .gr{color:#a00}html[data-mode=light] .highlight .gh{color:#999}html[data-mode=light] .highlight .gi{color:teal;background-color:#dfd}html[data-mode=light] .highlight .go{color:#888}html[data-mode=light] .highlight .gp{color:#555}html[data-mode=light] .highlight .gs{font-weight:bold}html[data-mode=light] .highlight .gu{color:#aaa}html[data-mode=light] .highlight .gt{color:#a00}html[data-mode=light] .highlight .kc{color:#000;font-weight:bold}html[data-mode=light] .highlight .kd{color:#000;font-weight:bold}html[data-mode=light] .highlight .kn{color:#000;font-weight:bold}html[data-mode=light] .highlight .kp{color:#000;font-weight:bold}html[data-mode=light] .highlight .kr{color:#000;font-weight:bold}html[data-mode=light] .highlight .kt{color:#458;font-weight:bold}html[data-mode=light] .highlight .m{color:#099}html[data-mode=light] .highlight .s{color:#d01040}html[data-mode=light] .highlight .na{color:teal}html[data-mode=light] .highlight .nb{color:#0086b3}html[data-mode=light] .highlight .nc{color:#458;font-weight:bold}html[data-mode=light] .highlight .no{color:teal}html[data-mode=light] .highlight .nd{color:#3c5d5d;font-weight:bold}html[data-mode=light] .highlight .ni{color:purple}html[data-mode=light] .highlight .ne{color:#900;font-weight:bold}html[data-mode=light] .highlight .nf{color:#900;font-weight:bold}html[data-mode=light] .highlight .nl{color:#900;font-weight:bold}html[data-mode=light] .highlight .nn{color:#555}html[data-mode=light] .highlight .nt{color:navy}html[data-mode=light] .highlight .nv{color:teal}html[data-mode=light] .highlight .ow{color:#000;font-weight:bold}html[data-mode=light] .highlight .w{color:#bbb}html[data-mode=light] .highlight .mf{color:#099}html[data-mode=light] .highlight .mh{color:#099}html[data-mode=light] .highlight .mi{color:#099}html[data-mode=light] .highlight .mo{color:#099}html[data-mode=light] .highlight .sb{color:#d01040}html[data-mode=light] .highlight .sc{color:#d01040}html[data-mode=light] .highlight .sd{color:#d01040}html[data-mode=light] .highlight .s2{color:#d01040}html[data-mode=light] .highlight .se{color:#d01040}html[data-mode=light] .highlight .sh{color:#d01040}html[data-mode=light] .highlight .si{color:#d01040}html[data-mode=light] .highlight .sx{color:#d01040}html[data-mode=light] .highlight .sr{color:#009926}html[data-mode=light] .highlight .s1{color:#d01040}html[data-mode=light] .highlight .ss{color:#990073}html[data-mode=light] .highlight .bp{color:#999}html[data-mode=light] .highlight .vc{color:teal}html[data-mode=light] .highlight .vg{color:teal}html[data-mode=light] .highlight .vi{color:teal}html[data-mode=light] .highlight .il{color:#099}html[data-mode=light] [class^=prompt-]{--inline-code-bg: #fbfafa;--highlighter-rouge-color: rgb(82, 82, 82)}}figure.highlight,.highlight,.highlighter-rouge{background:var(--highlight-bg-color)}td.rouge-code{padding-left:1rem;padding-right:1.5rem}.highlighter-rouge{color:var(--highlighter-rouge-color);margin-top:.5rem;margin-bottom:1.2em}.highlight{overflow:auto;padding-top:.5rem;padding-bottom:1rem}.highlight pre{margin-bottom:0;font-size:.85rem;line-height:1.4rem;word-wrap:normal}.highlight table td pre{overflow:visible;word-break:normal}.highlight .lineno{padding-right:.5rem;min-width:2.2rem;text-align:right;color:var(--highlight-lineno-color);-webkit-user-select:none;-moz-user-select:none;-o-user-select:none;-ms-user-select:none;user-select:none}code{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code.highlighter-rouge{font-size:.85rem;padding:3px 5px;word-break:break-word;border-radius:4px;background-color:var(--inline-code-bg)}code.filepath{background-color:inherit;color:var(--filepath-text-color);font-weight:600;padding:0}a>code.highlighter-rouge{padding-bottom:0;color:inherit}a:hover>code.highlighter-rouge{border-bottom:none}blockquote code{color:inherit}.highlight>code{color:rgba(0,0,0,0)}td.rouge-code a{color:inherit !important;border-bottom:none !important;pointer-events:none}div[class^=highlighter-rouge] pre.lineno,div.nolineno pre.lineno,div.language-plaintext.highlighter-rouge pre.lineno,div.language-console.highlighter-rouge pre.lineno,div.language-terminal.highlighter-rouge pre.lineno{display:none}div[class^=highlighter-rouge] td.rouge-code,div.nolineno td.rouge-code,div.language-plaintext.highlighter-rouge td.rouge-code,div.language-console.highlighter-rouge td.rouge-code,div.language-terminal.highlighter-rouge td.rouge-code{padding-left:1.5rem}.code-header{display:flex;justify-content:space-between;align-items:center;height:2.25rem}.code-header::before{content:"";display:inline-block;margin-left:1rem;width:.75rem;height:.75rem;border-radius:50%;background-color:var(--code-header-muted-color);box-shadow:1.25rem 0 0 var(--code-header-muted-color),2.5rem 0 0 var(--code-header-muted-color)}.code-header span i{font-size:1rem;margin-right:.4rem;color:var(--code-header-icon-color)}.code-header span i.small{font-size:70%}[file] .code-header span>i{position:relative;top:1px}.code-header span::after{content:attr(data-label-text);font-size:.85rem;font-weight:600;color:var(--code-header-text-color)}.code-header button{border:1px solid rgba(0,0,0,0);height:2.25rem;width:2.25rem;padding:0;background-color:inherit}.code-header button i{color:var(--code-header-icon-color)}.code-header button[timeout]:hover{border-color:var(--clipboard-checked-color)}.code-header button[timeout] i{color:var(--clipboard-checked-color)}.code-header button:focus{outline:none}.code-header button:not([timeout]):hover{background-color:rgba(128,128,128,.37)}.code-header button:not([timeout]):hover i{color:#fff}@media all and (max-width: 576px){.post-content>div[class^=language-]{margin-left:-1.25rem;margin-right:-1.25rem;border-radius:0}.post-content>div[class^=language-] .highlight{padding-left:.25rem}.post-content>div[class^=language-] .code-header{border-radius:0;padding-left:.4rem;padding-right:.5rem}}html{font-size:16px}@media(prefers-color-scheme: light){html:not([data-mode]),html[data-mode=light]{--main-bg: white;--mask-bg: #c1c3c5;--main-border-color: #f3f3f3;--text-color: #34343c;--text-muted-color: gray;--heading-color: black;--blockquote-border-color: #eeeeee;--blockquote-text-color: #9a9a9a;--link-color: #2a408e;--link-underline-color: #dee2e6;--button-bg: #ffffff;--btn-border-color: #e9ecef;--btn-backtotop-color: #686868;--btn-backtotop-border-color: #f1f1f1;--btn-box-shadow: #eaeaea;--checkbox-color: #c5c5c5;--checkbox-checked-color: #07a8f7;--img-bg: radial-gradient( circle, rgb(255, 255, 255) 0%, rgb(249, 249, 249) 100% );--shimmer-bg: linear-gradient( 90deg, rgba(250, 250, 250, 0) 0%, rgba(232, 230, 230, 1) 50%, rgba(250, 250, 250, 0) 100% );--sidebar-bg: #eeeeee;--sidebar-muted-color: #a2a19f;--sidebar-active-color: #424242;--nav-cursor-color: #757575;--sidebar-btn-bg: white;--topbar-text-color: rgb(78, 78, 78);--topbar-wrapper-bg: white;--search-wrapper-bg: rgb(245, 245, 245, 0.5);--search-wrapper-border-color: rgb(245, 245, 245);--search-tag-bg: #f8f9fa;--search-icon-color: #c2c6cc;--input-focus-border-color: var(--btn-border-color);--post-list-text-color: dimgray;--btn-patinator-text-color: #555555;--btn-paginator-hover-color: var(--sidebar-bg);--btn-paginator-border-color: var(--sidebar-bg);--btn-text-color: #676666;--pin-bg: #f5f5f5;--pin-color: #999fa4;--toc-highlight: #563d7c;--btn-share-hover-color: var(--link-color);--card-hovor-bg: #eeeeee;--card-border-color: #ececec;--card-box-shadow: rgba(234, 234, 234, 0.76);--label-color: #616161;--relate-post-date: rgba(30, 55, 70, 0.4);--footnote-target-bg: lightcyan;--tag-bg: rgba(0, 0, 0, 0.075);--tag-border: #dee2e6;--tag-shadow: var(--btn-border-color);--tag-hover: rgb(222, 226, 230);--tb-odd-bg: #fbfcfd;--tb-border-color: #eaeaea;--dash-color: silver;--kbd-wrap-color: #bdbdbd;--kbd-text-color: var(--text-color);--kbd-bg-color: white;--prompt-text-color: rgb(46, 46, 46, 0.77);--prompt-tip-bg: rgb(123, 247, 144, 0.2);--prompt-tip-icon-color: #03b303;--prompt-info-bg: #e1f5fe;--prompt-info-icon-color: #0070cb;--prompt-warning-bg: rgb(255, 243, 205);--prompt-warning-icon-color: #ef9c03;--prompt-danger-bg: rgb(248, 215, 218, 0.56);--prompt-danger-icon-color: #df3c30;--categories-hover-bg: var(--btn-border-color);--categories-icon-hover-color: darkslategray;--timeline-color: rgba(0, 0, 0, 0.075);--timeline-node-bg: #c2c6cc;--timeline-year-dot-color: #ffffff}html:not([data-mode]) [class^=prompt-],html[data-mode=light] [class^=prompt-]{--link-underline-color: rgb(219, 216, 216)}html:not([data-mode]) .dark,html[data-mode=light] .dark{display:none}html[data-mode=dark]{--main-bg: rgb(27, 27, 30);--mask-bg: rgb(68, 69, 70);--main-border-color: rgb(44, 45, 45);--text-color: rgb(175, 176, 177);--text-muted-color: rgb(107, 116, 124);--heading-color: #cccccc;--blockquote-border-color: rgb(66, 66, 66);--blockquote-text-color: rgb(117, 117, 117);--link-color: rgb(138, 180, 248);--link-underline-color: rgb(82, 108, 150);--button-bg: rgb(39, 40, 43);--btn-border-color: rgb(63, 65, 68);--btn-backtotop-color: var(--text-color);--btn-backtotop-border-color: var(--btn-border-color);--btn-box-shadow: var(--main-bg);--card-header-bg: rgb(51, 50, 50);--label-color: rgb(108, 117, 125);--checkbox-color: rgb(118, 120, 121);--checkbox-checked-color: var(--link-color);--img-bg: radial-gradient(circle, rgb(22, 22, 24) 0%, rgb(32, 32, 32) 100%);--shimmer-bg: linear-gradient( 90deg, rgba(255, 255, 255, 0) 0%, rgba(58, 55, 55, 0.4) 50%, rgba(255, 255, 255, 0) 100% );--sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);--sidebar-muted-color: #6d6c6b;--sidebar-active-color: rgb(255, 255, 255, 0.8);--nav-cursor-color: rgb(183, 182, 182);--sidebar-btn-bg: rgb(117, 116, 116, 0.2);--topbar-text-color: var(--text-color);--topbar-wrapper-bg: rgb(39, 40, 43);--search-wrapper-bg: rgb(34, 34, 39);--search-wrapper-border-color: rgb(34, 34, 39);--search-icon-color: rgb(100, 102, 105);--input-focus-border-color: rgb(112, 114, 115);--post-list-text-color: rgb(175, 176, 177);--btn-patinator-text-color: var(--text-color);--btn-paginator-hover-color: rgb(64, 65, 66);--btn-paginator-border-color: var(--btn-border-color);--btn-text-color: var(--text-color);--pin-bg: rgb(34, 35, 37);--pin-color: inherit;--toc-highlight: rgb(116, 178, 243);--tag-bg: rgb(41, 40, 40);--tag-hover: rgb(43, 56, 62);--tb-odd-bg: rgba(42, 47, 53, 0.52);--tb-even-bg: rgb(31, 31, 34);--tb-border-color: var(--tb-odd-bg);--footnote-target-bg: rgb(63, 81, 181);--btn-share-color: #6c757d;--btn-share-hover-color: #bfc1ca;--relate-post-date: var(--text-muted-color);--card-bg: #212121;--card-hovor-bg: #3a3a3a;--card-border-color: rgb(53, 53, 60);--card-box-shadow: var(--main-bg);--kbd-wrap-color: #6a6a6a;--kbd-text-color: #d3d3d3;--kbd-bg-color: #242424;--prompt-text-color: rgb(216, 212, 212, 0.75);--prompt-tip-bg: rgba(77, 187, 95, 0.2);--prompt-tip-icon-color: rgb(5, 223, 5, 0.68);--prompt-info-bg: rgb(7, 59, 104, 0.8);--prompt-info-icon-color: #0075d1;--prompt-warning-bg: rgb(90, 69, 3, 0.95);--prompt-warning-icon-color: rgb(255, 165, 0, 0.8);--prompt-danger-bg: rgb(86, 28, 8, 0.8);--prompt-danger-icon-color: #cd0202;--tag-border: rgb(59, 79, 88);--tag-shadow: rgb(32, 33, 33);--search-tag-bg: var(--tag-bg);--dash-color: rgb(63, 65, 68);--categories-border: rgb(64, 66, 69);--categories-hover-bg: rgb(73, 75, 76);--categories-icon-hover-color: white;--timeline-node-bg: rgb(150, 152, 156);--timeline-color: rgb(63, 65, 68);--timeline-year-dot-color: var(--timeline-color);color-scheme:dark}html[data-mode=dark] .post img[data-src].lazyloaded{-webkit-filter:brightness(95%);filter:brightness(95%)}html[data-mode=dark] .light{display:none}html[data-mode=dark] hr{border-color:var(--main-border-color)}html[data-mode=dark] .categories.card,html[data-mode=dark] .list-group-item{background-color:var(--card-bg)}html[data-mode=dark] .categories .card-header{background-color:var(--card-header-bg)}html[data-mode=dark] .categories .list-group-item{border-left:none;border-right:none;padding-left:2rem;border-color:var(--categories-border)}html[data-mode=dark] .categories .list-group-item:last-child{border-bottom-color:var(--card-bg)}html[data-mode=dark] #archives li:nth-child(odd){background-image:linear-gradient(to left, rgb(26, 26, 30), rgb(39, 39, 45), rgb(39, 39, 45), rgb(39, 39, 45), rgb(26, 26, 30))}html[data-mode=dark] #disqus_thread{color-scheme:none}}@media(prefers-color-scheme: dark){html:not([data-mode]),html[data-mode=dark]{--main-bg: rgb(27, 27, 30);--mask-bg: rgb(68, 69, 70);--main-border-color: rgb(44, 45, 45);--text-color: rgb(175, 176, 177);--text-muted-color: rgb(107, 116, 124);--heading-color: #cccccc;--blockquote-border-color: rgb(66, 66, 66);--blockquote-text-color: rgb(117, 117, 117);--link-color: rgb(138, 180, 248);--link-underline-color: rgb(82, 108, 150);--button-bg: rgb(39, 40, 43);--btn-border-color: rgb(63, 65, 68);--btn-backtotop-color: var(--text-color);--btn-backtotop-border-color: var(--btn-border-color);--btn-box-shadow: var(--main-bg);--card-header-bg: rgb(51, 50, 50);--label-color: rgb(108, 117, 125);--checkbox-color: rgb(118, 120, 121);--checkbox-checked-color: var(--link-color);--img-bg: radial-gradient(circle, rgb(22, 22, 24) 0%, rgb(32, 32, 32) 100%);--shimmer-bg: linear-gradient( 90deg, rgba(255, 255, 255, 0) 0%, rgba(58, 55, 55, 0.4) 50%, rgba(255, 255, 255, 0) 100% );--sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);--sidebar-muted-color: #6d6c6b;--sidebar-active-color: rgb(255, 255, 255, 0.8);--nav-cursor-color: rgb(183, 182, 182);--sidebar-btn-bg: rgb(117, 116, 116, 0.2);--topbar-text-color: var(--text-color);--topbar-wrapper-bg: rgb(39, 40, 43);--search-wrapper-bg: rgb(34, 34, 39);--search-wrapper-border-color: rgb(34, 34, 39);--search-icon-color: rgb(100, 102, 105);--input-focus-border-color: rgb(112, 114, 115);--post-list-text-color: rgb(175, 176, 177);--btn-patinator-text-color: var(--text-color);--btn-paginator-hover-color: rgb(64, 65, 66);--btn-paginator-border-color: var(--btn-border-color);--btn-text-color: var(--text-color);--pin-bg: rgb(34, 35, 37);--pin-color: inherit;--toc-highlight: rgb(116, 178, 243);--tag-bg: rgb(41, 40, 40);--tag-hover: rgb(43, 56, 62);--tb-odd-bg: rgba(42, 47, 53, 0.52);--tb-even-bg: rgb(31, 31, 34);--tb-border-color: var(--tb-odd-bg);--footnote-target-bg: rgb(63, 81, 181);--btn-share-color: #6c757d;--btn-share-hover-color: #bfc1ca;--relate-post-date: var(--text-muted-color);--card-bg: #212121;--card-hovor-bg: #3a3a3a;--card-border-color: rgb(53, 53, 60);--card-box-shadow: var(--main-bg);--kbd-wrap-color: #6a6a6a;--kbd-text-color: #d3d3d3;--kbd-bg-color: #242424;--prompt-text-color: rgb(216, 212, 212, 0.75);--prompt-tip-bg: rgba(77, 187, 95, 0.2);--prompt-tip-icon-color: rgb(5, 223, 5, 0.68);--prompt-info-bg: rgb(7, 59, 104, 0.8);--prompt-info-icon-color: #0075d1;--prompt-warning-bg: rgb(90, 69, 3, 0.95);--prompt-warning-icon-color: rgb(255, 165, 0, 0.8);--prompt-danger-bg: rgb(86, 28, 8, 0.8);--prompt-danger-icon-color: #cd0202;--tag-border: rgb(59, 79, 88);--tag-shadow: rgb(32, 33, 33);--search-tag-bg: var(--tag-bg);--dash-color: rgb(63, 65, 68);--categories-border: rgb(64, 66, 69);--categories-hover-bg: rgb(73, 75, 76);--categories-icon-hover-color: white;--timeline-node-bg: rgb(150, 152, 156);--timeline-color: rgb(63, 65, 68);--timeline-year-dot-color: var(--timeline-color);color-scheme:dark}html:not([data-mode]) .post img[data-src].lazyloaded,html[data-mode=dark] .post img[data-src].lazyloaded{-webkit-filter:brightness(95%);filter:brightness(95%)}html:not([data-mode]) .light,html[data-mode=dark] .light{display:none}html:not([data-mode]) hr,html[data-mode=dark] hr{border-color:var(--main-border-color)}html:not([data-mode]) .categories.card,html:not([data-mode]) .list-group-item,html[data-mode=dark] .categories.card,html[data-mode=dark] .list-group-item{background-color:var(--card-bg)}html:not([data-mode]) .categories .card-header,html[data-mode=dark] .categories .card-header{background-color:var(--card-header-bg)}html:not([data-mode]) .categories .list-group-item,html[data-mode=dark] .categories .list-group-item{border-left:none;border-right:none;padding-left:2rem;border-color:var(--categories-border)}html:not([data-mode]) .categories .list-group-item:last-child,html[data-mode=dark] .categories .list-group-item:last-child{border-bottom-color:var(--card-bg)}html:not([data-mode]) #archives li:nth-child(odd),html[data-mode=dark] #archives li:nth-child(odd){background-image:linear-gradient(to left, rgb(26, 26, 30), rgb(39, 39, 45), rgb(39, 39, 45), rgb(39, 39, 45), rgb(26, 26, 30))}html:not([data-mode]) #disqus_thread,html[data-mode=dark] #disqus_thread{color-scheme:none}html[data-mode=light]{--main-bg: white;--mask-bg: #c1c3c5;--main-border-color: #f3f3f3;--text-color: #34343c;--text-muted-color: gray;--heading-color: black;--blockquote-border-color: #eeeeee;--blockquote-text-color: #9a9a9a;--link-color: #2a408e;--link-underline-color: #dee2e6;--button-bg: #ffffff;--btn-border-color: #e9ecef;--btn-backtotop-color: #686868;--btn-backtotop-border-color: #f1f1f1;--btn-box-shadow: #eaeaea;--checkbox-color: #c5c5c5;--checkbox-checked-color: #07a8f7;--img-bg: radial-gradient( circle, rgb(255, 255, 255) 0%, rgb(249, 249, 249) 100% );--shimmer-bg: linear-gradient( 90deg, rgba(250, 250, 250, 0) 0%, rgba(232, 230, 230, 1) 50%, rgba(250, 250, 250, 0) 100% );--sidebar-bg: #eeeeee;--sidebar-muted-color: #a2a19f;--sidebar-active-color: #424242;--nav-cursor-color: #757575;--sidebar-btn-bg: white;--topbar-text-color: rgb(78, 78, 78);--topbar-wrapper-bg: white;--search-wrapper-bg: rgb(245, 245, 245, 0.5);--search-wrapper-border-color: rgb(245, 245, 245);--search-tag-bg: #f8f9fa;--search-icon-color: #c2c6cc;--input-focus-border-color: var(--btn-border-color);--post-list-text-color: dimgray;--btn-patinator-text-color: #555555;--btn-paginator-hover-color: var(--sidebar-bg);--btn-paginator-border-color: var(--sidebar-bg);--btn-text-color: #676666;--pin-bg: #f5f5f5;--pin-color: #999fa4;--toc-highlight: #563d7c;--btn-share-hover-color: var(--link-color);--card-hovor-bg: #eeeeee;--card-border-color: #ececec;--card-box-shadow: rgba(234, 234, 234, 0.76);--label-color: #616161;--relate-post-date: rgba(30, 55, 70, 0.4);--footnote-target-bg: lightcyan;--tag-bg: rgba(0, 0, 0, 0.075);--tag-border: #dee2e6;--tag-shadow: var(--btn-border-color);--tag-hover: rgb(222, 226, 230);--tb-odd-bg: #fbfcfd;--tb-border-color: #eaeaea;--dash-color: silver;--kbd-wrap-color: #bdbdbd;--kbd-text-color: var(--text-color);--kbd-bg-color: white;--prompt-text-color: rgb(46, 46, 46, 0.77);--prompt-tip-bg: rgb(123, 247, 144, 0.2);--prompt-tip-icon-color: #03b303;--prompt-info-bg: #e1f5fe;--prompt-info-icon-color: #0070cb;--prompt-warning-bg: rgb(255, 243, 205);--prompt-warning-icon-color: #ef9c03;--prompt-danger-bg: rgb(248, 215, 218, 0.56);--prompt-danger-icon-color: #df3c30;--categories-hover-bg: var(--btn-border-color);--categories-icon-hover-color: darkslategray;--timeline-color: rgba(0, 0, 0, 0.075);--timeline-node-bg: #c2c6cc;--timeline-year-dot-color: #ffffff}html[data-mode=light] [class^=prompt-]{--link-underline-color: rgb(219, 216, 216)}html[data-mode=light] .dark{display:none}}body{background:var(--main-bg);padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);color:var(--text-color);-webkit-font-smoothing:antialiased;font-family:"Source Sans Pro","Microsoft Yahei",sans-serif;line-height:1.75}h1{font-size:1.9rem}h2{font-size:1.5rem}h3{font-size:1.2rem}h4{font-size:1.15rem}h5{font-size:1.1rem}img{max-width:100%;height:auto}img[data-src].lazyloaded{-webkit-animation:fade-in .4s ease-in;animation:fade-in .4s ease-in}img[data-src][data-lqip=true].lazyload,img[data-src][data-lqip=true].lazyloading{-webkit-filter:blur(20px);filter:blur(20px)}img[data-src]:not([data-lqip=true]).lazyload,img[data-src]:not([data-lqip=true]).lazyloading{background:var(--img-bg)}img[data-src].shadow{-webkit-filter:drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));filter:drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));box-shadow:none !important}@-webkit-keyframes fade-in{from{opacity:0}to{opacity:1}}@keyframes fade-in{from{opacity:0}to{opacity:1}}blockquote{border-left:5px solid var(--blockquote-border-color);padding-left:1rem;color:var(--blockquote-text-color)}blockquote[class^=prompt-]{border-left:0;position:relative;padding:1rem 1rem 1rem 3rem;color:var(--prompt-text-color)}blockquote[class^=prompt-]::before{text-align:center;width:3rem;position:absolute;left:.25rem;margin-top:.4rem;text-rendering:auto;-webkit-font-smoothing:antialiased}blockquote[class^=prompt-]>p:last-child{margin-bottom:0}blockquote.prompt-tip{background-color:var(--prompt-tip-bg)}blockquote.prompt-tip::before{content:"";color:var(--prompt-tip-icon-color);font:var(--fa-font-regular)}blockquote.prompt-info{background-color:var(--prompt-info-bg)}blockquote.prompt-info::before{content:"";color:var(--prompt-info-icon-color);font:var(--fa-font-solid)}blockquote.prompt-warning{background-color:var(--prompt-warning-bg)}blockquote.prompt-warning::before{content:"";color:var(--prompt-warning-icon-color);font:var(--fa-font-solid)}blockquote.prompt-danger{background-color:var(--prompt-danger-bg)}blockquote.prompt-danger::before{content:"";color:var(--prompt-danger-icon-color);font:var(--fa-font-solid)}kbd{font-family:inherit;display:inline-block;vertical-align:middle;line-height:1.3rem;min-width:1.75rem;text-align:center;margin:0 .3rem;padding-top:.1rem;color:var(--kbd-text-color);background-color:var(--kbd-bg-color);border-radius:.25rem;border:solid 1px var(--kbd-wrap-color);box-shadow:inset 0 -2px 0 var(--kbd-wrap-color)}footer{font-size:.8rem;background-color:var(--main-bg)}footer div.d-flex{height:5rem;line-height:1.2rem;padding-bottom:1rem;border-top:1px solid var(--main-border-color)}footer div.d-flex>div{width:350px}footer a:link{text-decoration:none}footer a:hover{text-decoration:none}footer .footer-right{text-align:right}.access{top:2rem;transition:top .2s ease-in-out;margin-top:3rem;margin-bottom:4rem}.access:only-child{position:-webkit-sticky;position:sticky}.access>div{padding-left:1rem;border-left:1px solid var(--main-border-color)}.access>div:not(:last-child){margin-bottom:4rem}.access .post-content{font-size:.9rem}#panel-wrapper .panel-heading{color:var(--label-color);font-size:inherit;font-weight:600}#panel-wrapper .post-tag{display:inline-block;line-height:1rem;font-size:.85rem;background:none;border:1px solid var(--btn-border-color);border-radius:.8rem;padding:.3rem .5rem;margin:0 .35rem .5rem 0}#panel-wrapper .post-tag:hover{background-color:#2a408e;border-color:#2a408e;color:#fff;transition:none}[data-topbar-visible=true] #panel-wrapper>div{top:6rem}#access-lastmod li{height:1.8rem;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;list-style:none}#access-lastmod a{color:inherit}.footnotes>ol{padding-left:2rem;margin-top:.5rem}.footnotes>ol>li:not(:last-child){margin-bottom:.3rem}.footnotes>ol>li>p{margin-left:.25em;margin-top:0;margin-bottom:0}.footnotes>ol>li:target:not([scroll-focus]),.footnotes>ol>li[scroll-focus=true]>p{background-color:var(--footnote-target-bg);width:-moz-fit-content;width:-webkit-fit-content;width:fit-content;transition:background-color 1.5s ease-in-out}a.footnote{margin-left:1px;margin-right:1px;padding-left:2px;padding-right:2px;border-bottom-style:none !important;transition:background-color 1.5s ease-in-out}sup:target:not([scroll-focus]),sup[scroll-focus=true]>a.footnote{background-color:var(--footnote-target-bg)}a.reversefootnote{font-size:.6rem;line-height:1;position:relative;bottom:.25em;margin-left:.25em;border-bottom-style:none !important}.table-wrapper{overflow-x:auto;margin-bottom:1.5rem}.table-wrapper>table{min-width:100%;overflow-x:auto;border-spacing:0}.table-wrapper>table thead{border-bottom:solid 2px rgba(210,215,217,.75)}.table-wrapper>table tbody tr{border-bottom:1px solid var(--tb-border-color)}.table-wrapper>table tbody tr:nth-child(2n){background-color:var(--tb-even-bg)}.table-wrapper>table tbody tr:nth-child(2n+1){background-color:var(--tb-odd-bg)}.post h1{margin-top:3rem;margin-bottom:1.5rem}.post p>img[data-src]:not(.normal):not(.left):not(.right),.post p>a.popup:not(.normal):not(.left):not(.right){position:relative;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.pageviews .fa-spinner{font-size:80%}.post-meta{font-size:.85rem;word-spacing:1px}.post-content{font-size:1.08rem;margin-top:2rem;overflow-wrap:break-word}.post-content a.popup{margin-top:.5rem;margin-bottom:.5rem;cursor:zoom-in}.post-content ol:not([class]),.post-content ol.task-list,.post-content ul:not([class]),.post-content ul.task-list{-webkit-padding-start:1.75rem;padding-inline-start:1.75rem}.post-content ol:not([class]) li,.post-content ol.task-list li,.post-content ul:not([class]) li,.post-content ul.task-list li{margin:.25rem 0;padding-left:.25rem}.post-content ol:not([class]) ol,.post-content ol:not([class]) ul,.post-content ol.task-list ol,.post-content ol.task-list ul,.post-content ul:not([class]) ol,.post-content ul:not([class]) ul,.post-content ul.task-list ol,.post-content ul.task-list ul{-webkit-padding-start:1.25rem;padding-inline-start:1.25rem;margin:.5rem 0}.post-content ul.task-list{-webkit-padding-start:1.25rem;padding-inline-start:1.25rem}.post-content ul.task-list li{list-style-type:none;padding-left:0}.post-content ul.task-list li>i{width:2rem;margin-left:-1.25rem;color:var(--checkbox-color)}.post-content ul.task-list li>i.checked{color:var(--checkbox-checked-color)}.post-content ul.task-list li ul{-webkit-padding-start:1.75rem;padding-inline-start:1.75rem}.post-content ul.task-list input[type=checkbox]{margin:0 .5rem .2rem -1.3rem;vertical-align:middle}.post-content dl>dd{margin-left:1rem}.post-tag{display:inline-block;min-width:2rem;text-align:center;background:var(--tag-bg);border-radius:.3rem;padding:0 .4rem;color:inherit;line-height:1.3rem}.post-tag:not(:last-child){margin-right:.2rem}.post-tag:hover{border-bottom:none;text-decoration:none;color:#d2603a}.rounded-10{border-radius:10px !important}.img-link{color:rgba(0,0,0,0);display:inline-flex;overflow:hidden}.shimmer{overflow:hidden;position:relative;background:var(--img-bg)}.shimmer::before{content:"";position:absolute;background:var(--shimmer-bg);height:100%;width:100%;-webkit-animation:shimmer 1s infinite;animation:shimmer 1s infinite}@-webkit-keyframes shimmer{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}@keyframes shimmer{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}.embed-video{width:100%;height:100%;margin-bottom:1rem}.embed-video.youtube{aspect-ratio:16/9}.embed-video.twitch{aspect-ratio:310/189}.btn-lang{border:1px solid !important;padding:1px 3px;border-radius:3px;color:var(--link-color)}.btn-lang:focus{box-shadow:none}.loaded{display:block !important}.d-flex.loaded{display:flex !important}.unloaded{display:none !important}.visible{visibility:visible !important}.hidden{visibility:hidden !important}.flex-grow-1{flex-grow:1 !important}.btn-box-shadow{box-shadow:0 0 8px 0 var(--btn-box-shadow) !important}.no-text-decoration{text-decoration:none}.tooltip-inner{font-size:.7rem;max-width:220px;text-align:left}.disabled{color:#cec4c4;pointer-events:auto;cursor:not-allowed}.hide-border-bottom{border-bottom:none !important}.input-focus{box-shadow:none;border-color:var(--input-focus-border-color) !important;background:center !important;transition:background-color .15s ease-in-out,border-color .15s ease-in-out}.left{float:left;margin:.75rem 1rem 1rem 0 !important}.right{float:right;margin:.75rem 0 1rem 1rem !important}figure .mfp-title{text-align:center;padding-right:0;margin-top:.5rem}.mermaid{text-align:center}mjx-container{overflow-y:hidden;min-width:auto !important}#sidebar{padding-left:0;padding-right:0;position:fixed;top:0;left:0;height:100%;overflow-y:auto;width:260px;z-index:99;background:var(--sidebar-bg);-ms-overflow-style:none;scrollbar-width:none}#sidebar::-webkit-scrollbar{display:none}#sidebar a:hover{text-decoration:none;color:var(--sidebar-active-color) !important}#sidebar #avatar>a{display:block;width:6rem;height:6rem;border-radius:50%;border:2px solid rgba(222,222,222,.7);overflow:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:border-color .35s ease-in-out}#sidebar #avatar>a:hover{border-color:#fff}#sidebar #avatar img{width:100%;height:100%;transition:-webkit-transform .5s;transition:transform .5s;transition:transform .5s,-webkit-transform .5s}#sidebar #avatar img:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}#sidebar .site-title{margin-top:.55rem}#sidebar .site-title a{font-weight:900;font-size:1.5rem;letter-spacing:.5px;color:rgba(134,133,133,.99)}#sidebar .site-subtitle{font-size:95%;color:var(--sidebar-muted-color);line-height:1.25rem;word-spacing:1px;margin:.2rem 1.5rem .5rem 1.5rem;min-height:3rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#sidebar .nav-link{border-radius:0;font-size:.95rem;font-weight:600;letter-spacing:1px;display:table-cell;vertical-align:middle}#sidebar .nav-item{text-align:center;display:table;height:3rem}#sidebar .nav-item.active .nav-link{color:var(--sidebar-active-color)}#sidebar ul{height:15rem;margin-bottom:2rem;padding-left:0}#sidebar ul li{width:100%}#sidebar ul li:last-child a{position:relative;left:1px;width:100%}#sidebar ul li:last-child::after{display:table;visibility:hidden;content:"";position:relative;right:1px;width:2px;height:1.6rem;border-radius:1px;background-color:var(--nav-cursor-color);pointer-events:none}#sidebar ul>li.active:nth-child(1)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(1):hover~li:last-child::after{top:-11.3rem;visibility:visible}#sidebar ul>li.active:nth-child(2)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(2):hover~li:last-child::after{top:-8.3rem;visibility:visible}#sidebar ul>li.active:nth-child(3)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(3):hover~li:last-child::after{top:-5.3rem;visibility:visible}#sidebar ul>li.active:nth-child(4)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(4):hover~li:last-child::after{top:-2.3rem;visibility:visible}#sidebar ul>li.active:nth-child(5):last-child::after,#sidebar ul>li.nav-item:nth-child(5):last-child:hover::after{top:.7rem;visibility:visible}#sidebar .sidebar-bottom{margin-bottom:2.1rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}#sidebar .sidebar-bottom .mode-toggle,#sidebar .sidebar-bottom a{width:2.4rem;text-align:center}#sidebar .sidebar-bottom i{font-size:1.2rem;line-height:1.75rem}#sidebar .sidebar-bottom .mode-toggle{padding:0;border:0;margin-bottom:1px;background-color:rgba(0,0,0,0)}#sidebar .sidebar-bottom .mode-toggle:hover>i{color:var(--sidebar-active-color)}#sidebar .sidebar-bottom .icon-border{background-color:var(--sidebar-muted-color);content:"";width:3px;height:3px;border-radius:50%}@media(hover: hover){#sidebar ul>li:last-child::after{transition:top .5s ease}}.profile-wrapper{margin-top:2rem;width:100%}#search-result-wrapper{display:none;height:100%;width:100%;overflow:auto}#search-result-wrapper .post-content{margin-top:2rem}#topbar-wrapper{height:3rem;position:fixed;top:0;left:260px;right:0;transition:top .2s ease-in-out;z-index:50;border-bottom:1px solid rgba(0,0,0,.07);background-color:var(--topbar-wrapper-bg)}[data-topbar-visible=false] #topbar-wrapper{top:-3rem}#topbar i{color:#999}#topbar #breadcrumb{font-size:1rem;color:gray;padding-left:.5rem}#topbar #breadcrumb span:not(:last-child)::after{content:"›";padding:0 .3rem}#sidebar-trigger,#search-trigger{display:none}#search-wrapper{display:flex;width:100%;border-radius:1rem;border:1px solid var(--search-wrapper-border-color);background:var(--search-wrapper-bg);padding:0 .5rem}#search-wrapper i{z-index:2;font-size:.9rem;color:var(--search-icon-color)}#search-cancel{color:var(--link-color);margin-left:1rem;display:none}#search-input{background:center;border:0;border-radius:0;padding:.18rem .3rem;color:var(--text-color);height:auto}#search-input:focus{box-shadow:none;background:center}#search-input:focus.form-control::-moz-placeholder{opacity:.6}#search-input:focus.form-control::-webkit-input-placeholder{opacity:.6}#search-input:focus.form-control:-ms-input-placeholder{opacity:.6}#search-input:focus.form-control::-ms-input-placeholder{opacity:.6}#search-input:focus.form-control::placeholder{opacity:.6}#search-hints{padding:0 1rem}#search-hints h4{margin-bottom:1.5rem}#search-hints .post-tag{display:inline-block;line-height:1rem;font-size:1rem;background:var(--search-tag-bg);border:none;padding:.5rem;margin:0 1.25rem 1rem 0}#search-hints .post-tag::before{content:"#";color:var(--text-muted-color);padding-right:.2rem}#search-results{padding-bottom:3rem}#search-results a{font-size:1.4rem;line-height:2.5rem}#search-results>div{width:100%}#search-results>div:not(:last-child){margin-bottom:1rem}#search-results>div i{color:#818182;margin-right:.15rem;font-size:80%}#search-results>div>p{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}#topbar-title{display:none;font-size:1.1rem;font-weight:600;font-family:sans-serif;color:var(--topbar-text-color);text-align:center;width:70%;overflow:hidden;text-overflow:ellipsis;word-break:keep-all;white-space:nowrap}.row:only-child>#core-wrapper{padding-bottom:3rem}#mask{display:none;position:fixed;inset:0 0 0 0;height:100%;width:100%;z-index:1}[sidebar-display] #mask{display:block !important}#main-wrapper{background-color:var(--main-bg);position:relative;min-height:calc(100vh - 5rem);padding-left:0;padding-right:0}#core-wrapper,#panel-wrapper{margin-top:3rem}#topbar-wrapper.row,#main>.row,#search-result-wrapper>.row{margin-left:0;margin-right:0}#back-to-top{display:none;z-index:1;cursor:pointer;position:fixed;background:var(--button-bg);color:var(--btn-backtotop-color);padding:0;width:2.7em;height:2.7em;border-radius:50%;border:1px solid var(--btn-backtotop-border-color);transition:-webkit-transform .2s ease-out;transition:transform .2s ease-out;transition:transform .2s ease-out,-webkit-transform .2s ease-out;-webkit-transition:transform .2s ease-out}#back-to-top i{line-height:2.7em;position:relative;bottom:2px}#back-to-top:hover{transform:translate3d(0, -5px, 0);-webkit-transform:translate3d(0, -5px, 0)}@-webkit-keyframes popup{from{opacity:0;bottom:0}}@keyframes popup{from{opacity:0;bottom:0}}#notification .toast-header{background:none;border-bottom:none;color:inherit}#notification .toast-body{font-family:Lato,sans-serif;line-height:1.25rem}#notification .toast-body button{font-size:90%;min-width:4rem}#notification.toast{display:none}#notification.toast.show{display:block;min-width:20rem;border-radius:.5rem;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background-color:rgba(255,255,255,.5);color:rgba(27,27,30,.7294117647);position:fixed;left:50%;bottom:20%;-webkit-transform:translateX(-50%);transform:translateX(-50%);-webkit-animation:popup .8s;animation:popup .8s}@media all and (max-width: 576px){footer{height:6rem}footer div.d-flex{padding:1.5rem 0;line-height:1.65;flex-wrap:wrap;justify-content:space-around !important}footer .footer-left,footer .footer-right{text-align:center}#main-wrapper{min-height:calc(100vh - 6rem)}#core-wrapper{min-height:calc( + 100vh - 3rem - 6rem + ) !important}#core-wrapper h1{margin-top:2.2rem;font-size:1.75rem}#core-wrapper .post-content>blockquote[class^=prompt-]{margin-left:-1.25rem;margin-right:-1.25rem;border-radius:0;max-width:none}#avatar>a{width:5rem;height:5rem}.site-subtitle{margin-left:1.8rem;margin-right:1.8rem}}@media all and (max-width: 768px){#main,#topbar{max-width:100%}#main{padding-left:0;padding-right:0}}@media all and (max-width: 849px){html,body{overflow-x:hidden}footer{transition:transform .4s ease}[sidebar-display] #sidebar{-webkit-transform:translateX(0);transform:translateX(0)}[sidebar-display] #topbar-wrapper,[sidebar-display] #main-wrapper,[sidebar-display] footer{-webkit-transform:translateX(260px);transform:translateX(260px)}#sidebar{transition:transform .4s ease;transform:translateX(-260px);-webkit-transform:translateX(-260px)}#sidebar .cursor{transition:none}#main-wrapper{transition:transform .4s ease;padding-top:3rem}#topbar,#main,footer>.container{max-width:100%}#search-result-wrapper{width:100%}#breadcrumb,#search-wrapper{display:none}#topbar-wrapper{transition:transform .4s ease,top .2s ease;left:0}#core-wrapper,#panel-wrapper{margin-top:0}#topbar-title,#sidebar-trigger,#search-trigger{display:block}#search-result-wrapper .post-content{letter-spacing:0}#tags{justify-content:center !important}h1.dynamic-title{display:none}h1.dynamic-title~.post-content{margin-top:3rem}}@media all and (max-width: 849px)and (orientation: portrait){[data-topbar-visible=false] #topbar-wrapper{top:0}}@media all and (min-width: 577px)and (max-width: 1199px){footer .d-flex>div{width:312px}}@media all and (min-width: 850px){html{overflow-y:scroll}#main-wrapper,footer{margin-left:260px}.profile-wrapper{margin-top:3rem}#search-hints{display:none}#search-wrapper{max-width:210px}#search-result-wrapper{margin-top:3rem;max-width:1250px}div.post-content .table-wrapper>table{min-width:70%}#back-to-top{bottom:5.5rem;right:5%}#topbar-title{text-align:left}}@media all and (min-width: 992px)and (max-width: 1199px){#main .col-lg-11{flex:0 0 96%;max-width:96%}}@media all and (min-width: 850px)and (max-width: 1199px){#sidebar{width:210px}#sidebar .site-subtitle{margin-left:1rem;margin-right:1rem}#sidebar .sidebar-bottom a,#sidebar .sidebar-bottom span{width:2rem}#sidebar .sidebar-bottom .icon-border{left:-3px}#topbar-wrapper{left:210px}#search-results>div{max-width:700px}.site-title{font-size:1.3rem;margin-left:0 !important}.site-subtitle{margin-left:1rem;margin-right:1rem;font-size:90%}#main-wrapper,footer{margin-left:210px}#breadcrumb{width:65%;overflow:hidden;text-overflow:ellipsis;word-break:keep-all;white-space:nowrap}}@media all and (max-width: 1199px){#panel-wrapper{display:none}#main>div.row{justify-content:center !important}}@media all and (min-width: 1200px){#back-to-top{bottom:6.5rem}#search-wrapper{margin-right:4rem}#search-input{transition:all .3s ease-in-out}#search-results>div{width:46%}#search-results>div:nth-child(odd){margin-right:1.5rem}#search-results>div:nth-child(even){margin-left:1.5rem}#search-results>div:last-child:nth-child(odd){position:relative;right:24.3%}.post-content{font-size:1.03rem}footer div.d-felx{width:85%}}@media all and (min-width: 1400px){#back-to-top{right:calc((100vw - 260px - 1140px)/2 + 3rem)}}@media all and (min-width: 1650px){#main-wrapper,footer{margin-left:350px}#topbar-wrapper{left:350px}#search-wrapper{margin-right:calc( + 1250px * 0.25 - 210px + )}#topbar,#main,footer>.container{max-width:1250px}#core-wrapper,#tail-wrapper{padding-right:4.5rem !important}#back-to-top{right:calc((100vw - 350px - 1250px)/2 + 2rem)}#sidebar{width:350px}#sidebar .profile-wrapper{margin-top:4rem;margin-bottom:1rem}#sidebar .profile-wrapper.text-center{text-align:left !important}#sidebar .profile-wrapper .site-subtitle,#sidebar .profile-wrapper .site-title,#sidebar .profile-wrapper #avatar{margin-left:4.5rem}#sidebar .profile-wrapper #avatar>a{width:6.2rem;height:6.2rem}#sidebar .profile-wrapper #avatar>a.mx-auto{margin-left:0 !important}#sidebar .profile-wrapper .site-title{margin-top:.4rem}#sidebar .profile-wrapper .site-title a{font-size:1.7rem;letter-spacing:1px}#sidebar .profile-wrapper .site-subtitle{word-spacing:0;margin-top:0}#sidebar ul{padding-left:2.5rem}#sidebar ul>li:last-child>a{position:static}#sidebar ul .nav-item{text-align:left}#sidebar ul .nav-item .nav-link>span{letter-spacing:2px}#sidebar ul .nav-item .nav-link>i.unloaded{display:inline-block !important}#sidebar .sidebar-bottom{padding-left:3.5rem;width:100%}#sidebar .sidebar-bottom.justify-content-center{justify-content:flex-start !important}#sidebar .sidebar-bottom>span,#sidebar .sidebar-bottom>button.mode-toggle,#sidebar .sidebar-bottom>a{margin-left:.15rem;margin-right:.15rem;height:2rem;margin-bottom:.5rem}#sidebar .sidebar-bottom i{background-color:var(--sidebar-btn-bg);font-size:1rem;width:2rem;height:2rem;border-radius:50%;position:relative}#sidebar .sidebar-bottom i::before{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}#sidebar .sidebar-bottom .icon-border{top:.9rem}}.pagination{color:var(--btn-patinator-text-color);font-family:Lato,sans-serif}.pagination a:hover{text-decoration:none}.pagination .page-item .page-link{color:inherit;width:2.5rem;height:2.5rem;padding:0;display:-webkit-box;-webkit-box-pack:center;-webkit-box-align:center;border-radius:50%;border:1px solid var(--btn-paginator-border-color);background-color:var(--button-bg)}.pagination .page-item .page-link:hover{background-color:var(--btn-paginator-hover-color)}.pagination .page-item.active .page-link{background-color:var(--btn-paginator-hover-color);color:var(--btn-text-color)}.pagination .page-item.disabled{cursor:not-allowed}.pagination .page-item.disabled .page-link{color:rgba(108,117,125,.57);border-color:var(--btn-paginator-border-color);background-color:var(--button-bg)}.pagination .page-item:first-child .page-link,.pagination .page-item:last-child .page-link{border-radius:50%}#post-list{margin-top:1.75rem;padding-right:.5rem}#post-list a:hover{text-decoration:none}#post-list .post-preview{padding:.25rem;border-radius:.75rem;border:1px solid var(--card-border-color);background:var(--card-bg)}#post-list .post-preview:hover{background:var(--card-hovor-bg);box-shadow:0 .125rem .25rem rgba(0,0,0,.075)}#post-list .post-preview:not(:last-child){margin-bottom:1.75rem}#post-list .post-preview h1{font-size:1.4rem;margin:0}#post-list .post-preview .post-meta i{font-size:.73rem}#post-list .post-preview .post-meta i:not(:first-child){margin-left:1.2rem}#post-list .post-preview .post-meta .pin i{-webkit-transform:rotate(45deg);transform:rotate(45deg);padding-left:3px;color:var(--pin-color)}#post-list .post-preview .post-meta .pin span{display:none}#post-list .post-preview .post-content{margin-top:.6rem;margin-bottom:.6rem;color:var(--post-list-text-color)}#post-list .post-preview .post-content>p{margin:0;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}@media(hover: hover){.post-preview{transition:all .35s ease-in-out}}@media all and (max-width: 830px){.post-preview{margin-left:-0.5rem;margin-right:-0.5rem}.pagination{justify-content:space-evenly}.pagination .page-item:not(:first-child):not(:last-child){display:none}}@media all and (min-width: 831px){#post-list{margin-top:3rem}#post-list .post-preview{padding:.5rem}#post-list .post-preview .post-meta .pin{background:var(--pin-bg);border-radius:5px;line-height:1.4rem;height:1.3rem;margin-top:3px;padding-left:1px;padding-right:6px}#post-list .post-preview .post-meta .pin>span{display:inline}.pagination{font-size:.85rem}.pagination .page-item:not(:last-child){margin-right:.7rem}.pagination .page-item .page-link{width:2rem;height:2rem}.pagination .page-index{display:none}}@media all and (max-width: 1200px){#post-list{padding-right:0}}#related-posts .card h3,h1+.post-meta em a,h1+.post-meta em,footer a{color:var(--text-color)}.preview-img:not(.no-bg) img.lazyloaded{background:var(--img-bg)}.preview-img img{aspect-ratio:40/21;-o-object-fit:cover;object-fit:cover}h1+.post-meta span+span::before{content:"•";padding-left:.25rem;padding-right:.25rem}.post-tail-wrapper{margin-top:6rem;border-bottom:1px double var(--main-border-color);font-size:.85rem}.post-tail-wrapper .post-tail-bottom a{color:inherit}.post-tail-wrapper .license-wrapper{line-height:1.2rem}.post-tail-wrapper .license-wrapper>a{color:var(--text-color)}.post-tail-wrapper .license-wrapper span:last-child{font-size:.85rem}.post-tail-wrapper .share-wrapper{vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.post-tail-wrapper .share-wrapper .share-icons{font-size:1.2rem}.post-tail-wrapper .share-wrapper .share-icons>i{position:relative;bottom:1px}.post-tail-wrapper .share-wrapper .share-icons a:not(:last-child){margin-right:.25rem}.post-tail-wrapper .share-wrapper .share-icons a:hover{text-decoration:none}.post-tail-wrapper .share-wrapper .share-icons .fab.fa-twitter{color:var(--btn-share-color, rgb(29, 161, 242))}.post-tail-wrapper .share-wrapper .share-icons .fab.fa-facebook-square{color:var(--btn-share-color, rgb(66, 95, 156))}.post-tail-wrapper .share-wrapper .share-icons .fab.fa-telegram{color:var(--btn-share-color, rgb(39, 159, 217))}.post-tail-wrapper .share-wrapper .share-icons .fab.fa-linkedin{color:var(--btn-share-color, rgb(0, 119, 181))}.post-tail-wrapper .share-wrapper .share-icons .fab.fa-weibo{color:var(--btn-share-color, rgb(229, 20, 43))}.post-tail-wrapper .share-wrapper .fas.fa-link{color:var(--btn-share-color, rgb(171, 171, 171))}.post-tags{line-height:2rem}.post-navigation{padding-top:3rem;padding-bottom:4rem}.post-navigation .btn{width:50%;position:relative;border-color:var(--btn-border-color);color:var(--link-color)}.post-navigation .btn:hover{background:#2a408e;color:#fff;border-color:#2a408e}.post-navigation .btn.disabled{width:50%;position:relative;border-color:var(--btn-border-color);pointer-events:auto;cursor:not-allowed;background:none;color:gray}.post-navigation .btn.disabled:hover{border-color:none}.post-navigation .btn.btn-outline-primary.disabled:focus{box-shadow:none}.post-navigation .btn::before{color:var(--text-muted-color);font-size:.65rem;text-transform:uppercase;content:attr(prompt)}.post-navigation .btn:first-child{border-top-right-radius:0;border-bottom-right-radius:0;left:.5px}.post-navigation .btn:last-child{border-top-left-radius:0;border-bottom-left-radius:0;right:.5px}.post-navigation p{font-size:1.1rem;line-height:1.5rem;margin-top:.3rem;white-space:normal}@-webkit-keyframes fade-up{from{opacity:0;position:relative;top:2rem}to{opacity:1;position:relative;top:0}}@keyframes fade-up{from{opacity:0;position:relative;top:2rem}to{opacity:1;position:relative;top:0}}#toc-wrapper{border-left:1px solid rgba(158,158,158,.17);position:-webkit-sticky;position:sticky;top:4rem;transition:top .2s ease-in-out;-webkit-animation:fade-up .8s;animation:fade-up .8s}#toc-wrapper ul{list-style:none;font-size:.85rem;line-height:1.25;padding-left:0}#toc-wrapper ul li:not(:last-child){margin:.4rem 0}#toc-wrapper ul li a{padding:.2rem 0 .2rem 1.25rem}#toc-wrapper ul .toc-link{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#toc-wrapper ul .toc-link:hover{color:var(--toc-highlight);text-decoration:none}#toc-wrapper ul .toc-link::before{display:none}#toc-wrapper ul .is-active-link{color:var(--toc-highlight) !important;font-weight:600}#toc-wrapper ul .is-active-link::before{display:inline-block;width:1px;left:-1px;height:1.25rem;background-color:var(--toc-highlight) !important}#toc-wrapper ul ul a{padding-left:2rem}#related-posts>h3{color:var(--label-color);font-size:1.1rem;font-weight:600}#related-posts em{color:var(--relate-post-date)}#related-posts p{font-size:.9rem;margin-bottom:.5rem;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}#related-posts a:hover{text-decoration:none}#related-posts .card{border-color:var(--card-border-color);background-color:var(--card-bg);box-shadow:0 0 5px 0 var(--card-box-shadow);transition:all .3s ease-in-out}#related-posts .card:hover{-webkit-transform:translate3d(0, -3px, 0);transform:translate3d(0, -3px, 0);box-shadow:0 10px 15px -4px rgba(0,0,0,.15)}#tail-wrapper{min-height:2rem}#tail-wrapper>div:last-of-type{margin-bottom:2rem}#tail-wrapper #disqus_thread{min-height:8.5rem}.post-tail-wrapper .share-wrapper .share-icons>i:hover,.post-tail-wrapper .share-wrapper .share-icons a:hover>i{color:var(--btn-share-hover-color) !important}.share-label{color:inherit;font-size:inherit;font-weight:400}.share-label::after{content:":"}@media all and (max-width: 576px){.preview-img[data-src]{margin-top:2.2rem}.post-tail-bottom{flex-wrap:wrap-reverse !important}.post-tail-bottom>div:first-child{width:100%;margin-top:1rem}}@media all and (max-width: 768px){.post-content>p>img{max-width:calc(100% + 1rem)}}@media all and (max-width: 849px){.post-navigation{padding-left:0;padding-right:0;margin-left:-0.5rem;margin-right:-0.5rem}.preview-img[data-src]{max-width:100vw;border-radius:0}}.tag{border-radius:.7em;padding:6px 8px 7px;margin-right:.8rem;line-height:3rem;letter-spacing:0;border:1px solid var(--tag-border) !important;box-shadow:0 0 3px 0 var(--tag-shadow)}.tag span{margin-left:.6em;font-size:.7em;font-family:Oswald,sans-serif}#archives{letter-spacing:.03rem}#archives ul li::before,#archives .year:first-child::before,#archives .year::before{content:"";width:4px;position:relative;float:left;background-color:var(--timeline-color)}#archives .year{height:3.5rem;font-size:1.5rem;position:relative;left:2px;margin-left:-4px}#archives .year::before{height:72px;left:79px;bottom:16px}#archives .year:first-child::before{height:32px;top:24px}#archives .year::after{content:"";display:inline-block;position:relative;border-radius:50%;width:12px;height:12px;left:21.5px;border:3px solid;background-color:var(--timeline-year-dot-color);border-color:var(--timeline-node-bg);box-shadow:0 0 2px 0 #c2c6cc;z-index:1}#archives ul li{font-size:1.1rem;line-height:3rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#archives ul li:nth-child(odd){background-color:var(--main-bg, #ffffff);background-image:linear-gradient(to left, #ffffff, #fbfbfb, #fbfbfb, #fbfbfb, #ffffff)}#archives ul li::before{top:0;left:77px;height:3.1rem}#archives ul:last-child li:last-child::before{height:1.5rem}#archives .date{white-space:nowrap;display:inline-block;position:relative;right:.5rem}#archives .date.month{width:1.4rem;text-align:center}#archives .date.day{font-size:85%;font-family:Lato,sans-serif}#archives a{margin-left:2.5rem;position:relative;top:.1rem}#archives a:hover{border-bottom:none}#archives a::before{content:"";display:inline-block;position:relative;border-radius:50%;width:8px;height:8px;float:left;top:1.35rem;left:71px;background-color:var(--timeline-node-bg);box-shadow:0 0 3px 0 #c2c6cc;z-index:1}@media all and (max-width: 576px){#archives{margin-top:-1rem}#archives ul{letter-spacing:0}}.categories i{color:gray}.categories{margin-bottom:2rem}.categories .card-header{padding-right:12px}.categories i{font-size:86%}.categories .list-group-item{border-left:none;border-right:none;padding-left:2rem}.categories .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.category-trigger{width:1.7rem;height:1.7rem;border-radius:50%;text-align:center;color:#6c757d !important}.category-trigger i{position:relative;height:.7rem;width:1rem;transition:-webkit-transform 300ms ease;transition:transform 300ms ease;transition:transform 300ms ease,-webkit-transform 300ms ease}.category-trigger:hover i{color:var(--categories-icon-hover-color)}@media(hover: hover){.category-trigger:hover{background-color:var(--categories-hover-bg)}}.rotate{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.dash{margin:0 .5rem .6rem .5rem;border-bottom:2px dotted var(--dash-color)}#page-category ul>li,#page-tag ul>li{line-height:1.5rem;padding:.6rem 0}#page-category ul>li::before,#page-tag ul>li::before{background:#999;width:5px;height:5px;border-radius:50%;display:block;content:"";position:relative;top:.6rem;margin-right:.5rem}#page-category ul>li>a,#page-tag ul>li>a{font-size:1.1rem}#page-category ul>li>span:last-child,#page-tag ul>li>span:last-child{white-space:nowrap}#page-tag h1>i{font-size:1.2rem}#page-category h1>i{font-size:1.25rem}#page-category a:hover,#page-tag a:hover,#access-lastmod a:hover{margin-bottom:-1px}@media all and (max-width: 576px){#page-category ul>li::before,#page-tag ul>li::before{margin:0 .5rem}#page-category ul>li>a,#page-tag ul>li>a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/assets/css/style.css.map b/assets/css/style.css.map new file mode 100644 index 000000000..daffd9e28 --- /dev/null +++ b/assets/css/style.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/jekyll-theme-chirpy.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/addon/module.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/addon/syntax.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/colors/light-syntax.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/colors/dark-syntax.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/addon/variables.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/addon/commons.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/colors/light-typography.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/colors/dark-typography.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/layout/home.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/layout/post.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/layout/tags.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/layout/archives.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/layout/categories.scss","../../vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.6.1/_sass/layout/category-tag.scss"],"names":[],"mappings":"CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GCMA,iCACE,2BACA,gBACA,8CAIA,oEACE,kBACA,sBAEA,4FACE,aAMJ,4CACE,cAGF,qBACE,4CACE,kBACA,UACA,2DAIA,oEACE,mBACA,UACA,0DAMR,2BACE,4BACA,uCAGF,+DACE,mBACA,cACA,mBAGF,qPACE,yBACA,gCACA,qBAGF,4CACE,wBAGF,2EACE,oDAGF,4HACE,kCAGF,qFACE,yBACA,sBACA,iBAGF,6LACE,mBAGF,oFACE,eAGF,uEACE,kBAGF,wHACE,kBAIA,0CACE,cACA,kBACA,kBACA,cACA,UACA,cAIJ,iDACE,2BACA,yBACA,sBACA,iBCvGA,oCACE,4CC4DF,8BACA,mCACA,kCACA,0BACA,kCACA,mCACA,kCACA,mCAtEA,kGACA,qGACA,mHACA,oGACA,oGACA,uGACA,wHACA,uGACA,wHACA,8GACA,uGACA,qFACA,qFACA,2GACA,qFACA,qFACA,2FACA,qFACA,qFACA,sGACA,sGACA,sGACA,sGACA,sGACA,sGACA,mFACA,sFACA,qFACA,wFACA,sGACA,qFACA,yGACA,uFACA,sGACA,sGACA,sGACA,qFACA,qFACA,qFACA,sGACA,mFACA,qFACA,qFACA,qFACA,qFACA,wFACA,wFACA,wFACA,wFACA,wFACA,wFACA,wFACA,wFACA,wFACA,wFACA,wFACA,qFACA,qFACA,qFACA,qFACA,qFAYA,8EACE,0BACA,2CDlEA,qBETF,8BACA,mCACA,kCACA,0BACA,kCACA,2CACA,0CACA,mCACA,+BAGA,yBACE,cAGF,oCACE,cAKF,+EACA,gFACA,iDACA,4EACA,iDACA,iDACA,iDACA,iDACA,iDACA,kDACA,kDACA,kDACA,kDACA,oEACA,qDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,iDACA,iDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,iDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,kDACA,2EACA,4EFrEA,mCACE,2CEfF,8BACA,mCACA,kCACA,0BACA,kCACA,2CACA,0CACA,mCACA,+BAGA,mDACE,cAGF,yEACE,cAKF,oHACA,sHACA,qFACA,kHACA,qFACA,qFACA,qFACA,qFACA,qFACA,uFACA,uFACA,uFACA,uFACA,yGACA,0FACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,qFACA,qFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,qFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,uFACA,gHACA,gHF/DE,sBC4CF,8BACA,mCACA,kCACA,0BACA,kCACA,mCACA,kCACA,mCAtEA,4DACA,iEACA,6EACA,gEACA,gEACA,kEACA,mFACA,kEACA,mFACA,yEACA,kEACA,gDACA,gDACA,sEACA,gDACA,gDACA,sDACA,gDACA,gDACA,iEACA,iEACA,iEACA,iEACA,iEACA,iEACA,+CACA,kDACA,gDACA,mDACA,iEACA,gDACA,oEACA,kDACA,iEACA,iEACA,iEACA,gDACA,gDACA,gDACA,iEACA,+CACA,gDACA,gDACA,gDACA,gDACA,mDACA,mDACA,mDACA,mDACA,mDACA,mDACA,mDACA,mDACA,mDACA,mDACA,mDACA,gDACA,gDACA,gDACA,gDACA,gDAYA,uCACE,0BACA,4CD/CJ,+CACE,qCAGF,cACE,kBACA,qBAGF,mBAIE,qCACA,iBACA,oBAGF,WAQE,cACA,kBACA,oBAEA,eACE,gBACA,UGpCa,OHqCb,mBACA,iBAIA,wBACE,iBACA,kBAIJ,mBACE,oBACA,iBACA,iBACA,oCACA,yBACA,sBACA,oBACA,qBACA,iBAIJ,KACE,qBACA,iBACA,aAEA,uBACE,UGnEa,OHoEb,gBACA,sBACA,kBACA,uCAGF,cACE,yBACA,iCACA,gBACA,UAGF,yBACE,iBACA,cAGF,+BACE,mBAGF,gBACE,cAGF,gBACE,oBAWF,gBACE,yBACA,8BACA,oBAWA,0NACE,aAGF,yOACE,oBAKN,aAKE,aACA,8BACA,mBACA,OALqB,QAOrB,qBAIE,WACA,qBACA,iBACA,MANW,OAOX,OAPW,OAQX,kBACA,gDACA,gGAOA,oBACE,eACA,mBACA,oCAEA,0BACE,cAIK,2BACP,kBACA,QAIF,yBACE,8BACA,iBACA,gBACA,oCAKJ,oBAIE,+BACA,OAvDmB,QAwDnB,MAxDmB,QAyDnB,UACA,yBAEA,sBACE,oCAIA,mCACE,4CAGF,+BACE,qCAIJ,0BACE,aAGF,yCACE,uCAEA,2CACE,WAMR,kCAEI,oCD9HF,YC+HmB,SD9HnB,aC8HmB,SAEf,gBAEA,+CACE,oBAGF,iDACE,gBACA,mBACA,qBIpQR,KAuBE,eAtBA,oCACE,6DCCF,mBACA,6BAGA,sBACA,yBACA,uBACA,mCACA,iCACA,sBACA,gCACA,qBACA,4BACA,+BACA,sCACA,0BACA,0BACA,kCACA,oFAKA,2HAQA,sBACA,+BACA,gCACA,4BACA,wBAGA,qCACA,2BACA,6CACA,kDACA,yBACA,6BACA,oDAGA,gCACA,oCACA,+CACA,gDACA,0BACA,kBACA,qBAGA,yBACA,2CACA,yBACA,6BACA,6CACA,uBACA,0CACA,gCACA,+BACA,sBACA,sCACA,gCACA,qBACA,2BACA,qBACA,0BACA,oCACA,sBACA,2CACA,yCACA,iCACA,0BACA,kCACA,wCACA,qCACA,6CACA,oCAWA,+CACA,6CAGA,uCACA,4BACA,mCAfA,8EACE,2CAGF,wDACE,aDrFA,qBELF,2BACA,2BACA,qCAGA,iCACA,uCACA,yBACA,2CACA,4CACA,iCACA,0CACA,6BACA,oCACA,yCACA,sDACA,iCACA,kCACA,kCACA,qCACA,4CACA,4EACA,0HAQA,gEACA,+BACA,gDACA,uCACA,0CAGA,uCACA,qCACA,qCACA,+CACA,wCACA,+CAGA,2CACA,8CACA,6CACA,sDACA,oCACA,0BACA,qBAGA,oCACA,0BACA,6BACA,oCACA,8BACA,oCACA,uCACA,2BACA,iCACA,4CACA,mBACA,yBACA,qCACA,kCACA,0BACA,0BACA,wBACA,8CACA,wCACA,8CACA,uCACA,kCACA,0CACA,mDACA,wCACA,oCAGA,8BACA,8BACA,+BACA,8BAGA,qCACA,uCACA,qCAGA,uCACA,kCACA,iDAmDA,kBAhDE,oDACE,+BACA,uBAIJ,4BACE,aAGF,wBACE,sCAIF,4EAEE,gCAIA,8CACE,uCAGF,kDACE,iBACA,kBACA,kBACA,sCAEA,6DACE,mCAKN,iDACE,+HAaF,oCACE,mBF5IF,mCACE,2CEXF,2BACA,2BACA,qCAGA,iCACA,uCACA,yBACA,2CACA,4CACA,iCACA,0CACA,6BACA,oCACA,yCACA,sDACA,iCACA,kCACA,kCACA,qCACA,4CACA,4EACA,0HAQA,gEACA,+BACA,gDACA,uCACA,0CAGA,uCACA,qCACA,qCACA,+CACA,wCACA,+CAGA,2CACA,8CACA,6CACA,sDACA,oCACA,0BACA,qBAGA,oCACA,0BACA,6BACA,oCACA,8BACA,oCACA,uCACA,2BACA,iCACA,4CACA,mBACA,yBACA,qCACA,kCACA,0BACA,0BACA,wBACA,8CACA,wCACA,8CACA,uCACA,kCACA,0CACA,mDACA,wCACA,oCAGA,8BACA,8BACA,+BACA,8BAGA,qCACA,uCACA,qCAGA,uCACA,kCACA,iDAmDA,kBAhDE,yGACE,+BACA,uBAIJ,yDACE,aAGF,iDACE,sCAIF,0JAEE,gCAIA,6FACE,uCAGF,qGACE,iBACA,kBACA,kBACA,sCAEA,2HACE,mCAKN,mGACE,+HAaF,yEACE,kBFtIA,sBChBF,iBACA,mBACA,6BAGA,sBACA,yBACA,uBACA,mCACA,iCACA,sBACA,gCACA,qBACA,4BACA,+BACA,sCACA,0BACA,0BACA,kCACA,oFAKA,2HAQA,sBACA,+BACA,gCACA,4BACA,wBAGA,qCACA,2BACA,6CACA,kDACA,yBACA,6BACA,oDAGA,gCACA,oCACA,+CACA,gDACA,0BACA,kBACA,qBAGA,yBACA,2CACA,yBACA,6BACA,6CACA,uBACA,0CACA,gCACA,+BACA,sBACA,sCACA,gCACA,qBACA,2BACA,qBACA,0BACA,oCACA,sBACA,2CACA,yCACA,iCACA,0BACA,kCACA,wCACA,qCACA,6CACA,oCAWA,+CACA,6CAGA,uCACA,4BACA,mCAfA,uCACE,2CAGF,4BACE,cDlEJ,KACE,0BACA,kHAEA,wBACA,mCACA,2DACA,iBAKF,GAGE,iBAGF,GAKE,iBAGF,GAKE,iBAGF,GAKE,kBAGF,GAKE,iBAOF,IACE,eACA,YAGE,yBACE,sCACA,8BAIA,iFAEE,0BACA,kBAKF,6FAEE,yBAIJ,qBACE,4DACA,oDACA,2BAMJ,2BACE,KACE,UAEF,GACE,WAIJ,mBACE,KACE,UAEF,GACE,WAKN,WACE,qDACA,kBACA,mCAEA,2BACE,cACA,kBACA,4BACA,+BAIA,mCACE,kBACA,WACA,kBACA,YACA,iBACA,oBACA,mCAGF,wCACE,gBLRJ,sBACE,sCAEA,8BACE,QKQmB,ILPnB,mCACA,4BANJ,uBACE,uCAEA,+BACE,QKSoB,ILRpB,oCACA,0BANJ,0BACE,0CAEA,kCACE,QKUuB,ILTvB,uCACA,0BANJ,yBACE,yCAEA,iCACE,QKWsB,ILVtB,sCACA,0BKYN,IACE,oBACA,qBACA,sBACA,mBACA,kBACA,kBACA,eACA,kBACA,4BACA,qCACA,qBACA,uCACA,gDAGF,OACE,gBACA,gCAEA,kBACE,OD3KY,KC4KZ,mBACA,oBACA,8CAEA,sBACE,YAOF,cLxFF,qBK4FE,eL5FF,qBKkGA,qBACE,iBAcJ,QACE,SACA,+BACA,gBACA,mBAEA,mBACE,wBACA,gBAGF,YACE,kBACA,+CAEA,6BACE,mBAIJ,sBACE,gBAMF,8BLrHA,MADwD,mBAExD,UKqHiB,QLpHjB,YAH2C,IK0H3C,yBACE,qBACA,iBACA,iBACA,gBACA,yCACA,oBACA,oBACA,wBAEA,+BACE,yBACA,qBACA,WACA,gBAIJ,8CACE,SAKF,mBACE,cACA,gBACA,uBACA,oBACA,qBACA,4BACA,gBAGF,kBAOE,cAIJ,cACE,kBACA,iBAGE,kCACE,oBAGF,mBACE,kBACA,aACA,gBAIF,kFAEE,2CACA,uBACA,0BACA,kBACA,6CAMK,WLhNT,YKiNiB,ILhNjB,aKgNiB,IL5MjB,aK6MiB,IL5MjB,cK4MiB,IAEf,oCACA,6CAIO,iEAEP,2CAKO,kBACP,gBACA,cACA,kBACA,aACA,kBACA,oCAOJ,eACE,gBACA,qBAEA,qBACE,eACA,gBACA,iBAEA,2BACE,8CAQA,8BACE,+CAEA,4CACE,mCAGF,8CACE,kCAcR,SACE,gBACA,qBAME,8GLzQJ,kBACA,SACA,mCACA,2BK6QF,uBACE,cAGF,WACE,iBACA,iBAaF,cACE,kBACA,gBACA,yBAGE,sBLlUF,WKqUmB,MLpUnB,cKoUmB,MAEf,eAcF,kHAEE,8BACA,6BAEA,8HACE,gBACA,oBAGF,4PAEE,8BACA,6BACA,eAKN,2BACE,8BACA,6BAEA,8BACE,qBACA,eAGA,gCACE,WACA,qBACA,4BAEA,wCACE,oCAIJ,iCACE,8BACA,6BAIJ,gDACE,6BACA,sBAIJ,oBACE,iBAQJ,UACE,qBACA,eACA,kBACA,yBACA,oBACA,gBACA,cACA,mBAEA,2BACE,mBAGF,gBAGE,mBACA,qBACA,cAIJ,YACE,8BAGF,UACE,oBACA,oBACA,gBAGF,SACE,gBACA,kBACA,yBAEA,iBACE,WACA,kBACA,6BACA,YACA,WACA,sCACA,8BAGF,2BACE,GACE,oCACA,4BAEF,KACE,mCACA,4BAIJ,mBACE,GACE,oCACA,4BAEF,KACE,mCACA,4BAKN,aACE,WACA,YACA,mBAIA,qBACE,kBAGF,oBACE,qBAKJ,UACE,4BACA,gBACA,kBACA,wBAEA,gBACE,gBAMJ,QACE,yBAES,eACP,wBAIJ,UACE,wBAGF,SACE,8BAGF,QACE,6BAGF,aACE,uBAGF,gBACE,sDAGF,oBLrhBE,qBK0hBF,eACE,gBACA,gBACA,gBAGF,UACE,cACA,oBACA,mBAGF,oBACE,8BAGF,aACE,gBACA,wDACA,6BACA,2EAGF,MACE,WACA,qCAGF,OACE,YACA,qCAMF,kBACE,kBACA,gBACA,iBAIF,SACE,kBAIF,cACE,kBACA,0BAOF,SLrkBE,aKskBe,ELrkBf,cKqkBe,EAEf,eACA,MACA,OACA,YACA,gBACA,MD1sBc,MC2sBd,WACA,6BAQA,wBACA,qBANA,4BACE,aAUA,iBL3mBF,qBK8mBI,6CAKF,mBACE,cACA,WACA,YACA,kBACA,sCACA,gBACA,gCACA,wBACA,yCAEA,yBACE,kBAIJ,qBACE,WACA,YACA,iCACA,yBACA,+CAEA,2BACE,6BACA,qBAKN,qBACE,kBAEA,uBAGE,gBACA,iBACA,oBACA,4BAIJ,wBACE,cACA,iCACA,oBACA,iBACA,iCACA,gBACA,yBACA,sBACA,qBACA,iBAGF,mBACE,gBACA,iBACA,gBACA,mBACA,mBACA,sBAGF,mBACE,kBACA,cACA,OD/xBS,KCkyBP,oCACE,kCASN,YACE,aACA,mBACA,eAEA,eACE,WAGE,4BACE,kBACA,SACA,WAIF,iCACE,cACA,kBACA,WACA,kBACA,UACA,MDh0BK,ICi0BL,ODl0BU,OCm0BV,kBACA,yCACA,oBAkBA,wHAZJ,IAMM,SALN,mBAWI,wHAZJ,IAMM,QALN,mBAWI,wHAZJ,IAMM,QALN,mBAWI,wHAZJ,IAMM,QALN,mBAgBE,kHAjBF,IAMM,MALN,mBAwBJ,yBACE,qBLrvBF,YKuvBiB,KLtvBjB,aKsvBiB,KLlvBjB,aKmvBiB,KLlvBjB,cKkvBiB,KAEf,iEACE,aACA,kBAQF,2BACE,iBACA,oBAGF,sCACE,UACA,SACA,kBACA,+BASA,8CACE,kCAIJ,sCAGE,4CACA,WACA,UACA,WACA,kBAKN,qBACE,iCACE,yBAIJ,iBACE,gBACA,WAGF,uBACE,aACA,YACA,WACA,cAEA,qCACE,gBAMJ,gBACE,OD36Bc,KC46Bd,eACA,MACA,KD37Bc,MC47Bd,QACA,+BACA,WACA,wCACA,0CAEA,4CACE,UAMF,UACE,WAGF,oBACE,eACA,WACA,mBAQI,iDACE,YACA,gBAOV,iCAEE,aAGF,gBACE,aACA,WACA,mBACA,oDACA,oCACA,gBAEA,kBACE,UACA,gBACA,+BAKJ,eACE,wBACA,iBACA,aAKF,cACE,kBACA,SACA,gBACA,qBACA,wBACA,YAEA,oBACE,gBACA,kBAGE,mDLz4BJ,WK44BI,4DL54BJ,WK+4BI,uDL/4BJ,WKk5BI,wDLl5BJ,WKq5BI,8CLr5BJ,WK45BF,cACE,eAEA,iBACE,qBAGF,wBACE,qBACA,iBACA,eACA,gCACA,YACA,cACA,wBAEA,gCACE,YACA,8BACA,oBAON,gBACE,oBAEA,kBASE,iBACA,mBAGF,oBACE,WAEA,qCACE,mBAIF,sBACE,cACA,oBACA,cAGF,sBACE,gBACA,uBACA,oBACA,qBACA,4BAKN,cACE,aACA,iBACA,gBACA,uBACA,+BACA,kBACA,UACA,gBACA,uBACA,oBACA,mBAYS,8BACP,oBAIJ,MACE,aACA,eACA,cACA,YACA,WACA,UAES,wBACP,yBAMJ,cACE,gCACA,kBACA,8BLjhCA,aKmhCe,ELlhCf,cKkhCe,EAGjB,6BAEE,WDxoCc,KC2oChB,2DLhiCE,YKmiCe,ELliCf,aKkiCe,EAKjB,aAGE,aACA,UACA,eACA,eACA,4BACA,iCACA,UACA,MATO,MAUP,OAVO,MAWP,kBACA,mDACA,0CACA,kCACA,iEACA,0CAEA,eACE,YAnBK,MAoBL,kBACA,WAIJ,mBACE,kCACA,0CAIA,yBACE,KACE,UACA,UAIJ,iBACE,KACE,UACA,UAIJ,4BACE,gBACA,mBACA,cAGF,0BACE,4BACA,oBAEA,iCACE,cACA,eAIJ,oBACE,aAEA,yBACE,cACA,gBACA,oBACA,mCACA,2BACA,sCACA,iCACA,eACA,SACA,WACA,mCACA,2BACA,4BACA,oBAcN,kCACE,OACE,OD/uCmB,KCivCnB,kBACE,iBACA,iBACA,eACA,wCAGF,yCAEE,kBAIJ,cACE,8BAGF,cACE;AAAA;AAAA,iBAIA,iBACE,kBACA,kBAIA,uDLrqCJ,YKsqCqB,SLrqCrB,aKqqCqB,SAEf,gBACA,eAKN,UACE,WACA,YAGF,eLnrCA,YKorCiB,OLnrCjB,aKmrCiB,QAInB,kCACE,cACE,eAOF,ML5rCA,aK8rCiB,EL7rCjB,cK6rCiB,GAKnB,kCAWE,UAEE,kBAGF,OATI,WALM,mBAmBR,2BACE,gCACA,wBAGF,2FAGE,oCACA,4BAIJ,SA3BI,WALM,mBAmCR,6BACA,qCAEA,iBACE,gBAIJ,cAtCI,WALM,mBA8CR,YDn2CY,KCs2Cd,gCAGE,eAGF,uBACE,WAGF,4BAEE,aAGF,gBA7DI,2CAgEF,OAGF,6BAEE,aAGF,+CAGE,cAGF,qCACE,iBAGF,MACE,kCAGF,iBACE,aAEA,+BACE,iBAKN,6DACE,4CACE,OAKJ,yDACE,mBACE,aAKJ,kCAEE,KACE,kBAGF,qBAEE,YD17CY,MC67Cd,iBACE,gBAGF,cACE,aAGF,gBACE,UDx7Ce,MC27CjB,uBACE,gBACA,UDz7CqB,OC47CvB,sCACE,cAIF,aACE,cACA,SAGF,cACE,iBAKJ,yDACE,iBACE,aACA,eAKJ,yDACE,SACE,MDv+CkB,MCy+ClB,wBACE,iBACA,kBAIA,yDAEE,WAGF,sCACE,UAKN,gBACE,KD3/CkB,MC8/CpB,oBACE,gBAGF,YACE,iBACA,yBAGF,eLh5CA,YKi5CiB,KLh5CjB,aKg5CiB,KAEf,cAGF,qBAEE,YD/gDkB,MCkhDpB,YACE,UACA,gBACA,uBACA,oBACA,oBAKJ,mCACE,eACE,aAGF,cACE,mCAMJ,mCACE,aACE,cAGF,gBACE,kBAGF,cACE,+BAGF,oBACE,UAEA,mCACE,oBAGF,oCACE,mBAGF,8CACE,kBACA,YAIJ,cACE,kBAIA,kBACE,WAKN,mCACE,aACE,+CAIJ,mCACE,qBAEE,YDzlDkB,MC4lDpB,gBACE,KD7lDkB,MCgmDpB,gBACE;AAAA;AAAA,MAKF,gCAGE,UDzlDqB,OC4lDvB,4BAEE,gCAGF,aACE,8CAKF,SACE,MDxnDkB,MC0nDlB,0BACE,gBACA,mBAEA,sCACE,2BAGF,iHACE,mBAMA,oCACE,aACA,cAEA,4CACE,yBAKN,sCAGE,iBAEA,wCACE,iBACA,mBAIJ,yCAGE,eACA,aAIJ,YACE,oBAGE,4BACE,gBAIJ,sBACE,gBAGE,qCACE,mBAIA,2CACE,gCAOV,yBACE,oBACA,WAIA,gDACE,sCAGF,qGLplDJ,YKulDqB,OLtlDrB,aKslDqB,OAEf,OAXgB,KAYhB,oBAGF,2BACE,uCACA,eACA,MAlBgB,KAmBhB,OAnBgB,KAoBhB,kBACA,kBAEA,mCACE,kBACA,QACA,SACA,wCACA,gCAIJ,sCACE,WGzuDR,YACE,sCACA,4BAEA,oBACE,qBAIA,kCACE,cACA,aACA,cACA,UACA,oBACA,wBACA,yBACA,kBACA,mDACA,kCAEA,wCACE,kDAKF,yCACE,kDACA,4BAIJ,gCACE,mBAEA,2CACE,4BACA,+CACA,kCAIJ,2FAEE,kBAKN,WACE,mBACA,oBAEA,mBACE,qBAGF,yBACE,eACA,qBACA,0CACA,0BAEA,+BACE,gCACA,6CAGF,0CACE,sBAGF,4BACE,iBACA,SAIA,sCACE,iBAEA,wDACE,mBASF,2CACE,gCACA,wBACA,iBACA,uBAGF,8CACE,aAKN,uCACE,iBACA,oBACA,kCAEA,yCACE,SACA,gBACA,uBACA,oBACA,qBACA,4BAMR,qBACE,cACE,iCAKJ,kCACE,cACE,oBACA,qBAGF,YACE,6BAGE,0DACE,cAOR,kCACE,WACE,gBAEA,yBACE,cAGE,yCACE,yBACA,kBACA,mBACA,cACA,eACA,iBACA,kBAEA,8CACE,eAOV,YACE,iBAGE,wCACE,mBAGF,kCACE,WACA,YAIJ,wBACE,cAMN,mCACE,WACE,iBC/KJ,qEACE,wBAOE,wCACE,yBAIJ,iBACE,mBACA,oBACA,iBAOF,gCA5BA,YACA,aAFc,OAGd,cAH4B,OA0C9B,mBACE,gBACA,kDACA,iBAEA,uCACE,cAGF,oCACE,mBAEA,sCACE,wBAOF,oDACE,iBAQJ,kCACE,sBACA,yBACA,sBACA,qBACA,iBAEA,+CACE,iBAEA,iDACE,kBACA,WAUA,kEACE,oBAGF,uDACE,qBASF,+DArHJ,gDAyHI,uEAzHJ,+CA6HI,gEA7HJ,gDAiII,gEAjIJ,+CAqII,6DArIJ,+CA2IA,+CA3IA,iDAiJJ,WACE,iBAGF,iBACE,iBACA,oBAEA,sBApJA,UACA,kBACA,qCAqJE,wBAEA,4BACE,mBACA,WACA,qBAGF,+BA/JF,UACA,kBACA,qCAgKI,oBACA,mBACA,gBACA,WAEA,qCACE,kBAIJ,yDACE,gBAGF,8BACE,8BACA,iBACA,yBACA,qBAGF,kCACE,0BACA,6BACA,UAGF,iCACE,yBACA,4BACA,WAIJ,mBACE,iBACA,mBACA,iBACA,mBAIJ,2BACE,KACE,UACA,kBACA,SAGF,GACE,UACA,kBACA,OAIJ,mBACE,KACE,UACA,kBACA,SAGF,GACE,UACA,kBACA,OAIJ,aACE,4CACA,wBACA,gBACA,SACA,+BACA,8BACA,sBAEA,gBACE,gBACA,iBACA,iBACA,eAGE,oCACE,eAGF,qBACE,8BAMJ,0BACE,cACA,mBACA,gBACA,uBAEA,gCACE,2BACA,qBAGF,kCACE,aAIJ,gCACE,sCACA,gBAEA,wCACE,qBACA,UACA,UACA,eACA,iDAKF,qBACE,kBASN,kBT5KA,MADwD,mBAExD,US4KiB,OT3KjB,YS2KyB,IAGzB,kBAGE,8BAGF,iBACE,gBACA,oBACA,gBACA,uBACA,oBACA,qBACA,4BAGF,uBACE,qBAGF,qBACE,sCACA,gCACA,4CACA,+BAMA,2BACE,0CACA,kCACA,4CAKN,cACE,gBAEA,+BACE,mBAIF,6BACE,kBAIJ,gHACE,8CAGF,aTvOE,MSwO6B,QTvO7B,USuOe,QTtOf,YSsOwB,IAExB,oBACE,YAIJ,kCACE,uBACE,kBAGF,kBACE,kCAEA,kCACE,WACA,iBAKN,kCACE,oBACE,6BAKJ,kCACE,iBACE,eACA,gBACA,oBACA,qBAGF,uBACE,gBACA,iBCvZJ,KACE,mBACA,oBACA,mBACA,iBACA,iBACA,8CACA,uCAEA,UACE,iBACA,eACA,8BCZJ,UACE,sBAIA,oFACE,WACA,MAJe,IAKf,kBACA,WACA,uCAGF,gBACE,cACA,iBACA,kBACA,SACA,iBAEA,wBAGE,YACA,UACA,YAGF,oCAGE,YACA,SAIF,uBACE,WACA,qBACA,kBACA,kBACA,WACA,YACA,YACA,iBACA,gDACA,qCACA,6BACA,UAKF,gBACE,iBACA,iBACA,mBACA,gBACA,uBAEA,+BACE,yCACA,uFAUF,wBAGE,MACA,UACA,cAIJ,8CACE,cAIJ,gBACE,mBACA,qBACA,kBACA,YAEA,sBACE,aACA,kBAGF,oBACE,cACA,4BAIJ,YAEE,mBACA,kBACA,UAEA,kBACE,mBAGF,oBAEE,WACA,qBACA,kBACA,kBACA,UACA,WACA,WACA,YACA,UACA,yCACA,6BACA,UAKN,kCACE,UACE,iBAEA,aACE,kBCxIN,cACE,WAGF,YACE,mBAEA,yBACE,mBAGF,cAGE,cAGF,6BACE,iBACA,kBACA,kBAEA,yCACE,yBACA,0BAKN,kBACE,aACA,cACA,kBACA,kBACA,yBAEA,oBACE,kBACA,aACA,WACA,wCACA,gCACA,6DAIA,0BACE,yCAMN,qBACE,wBACE,6CAIJ,QACE,iCACA,yBC7DF,MACE,2BACA,2CAKA,qCACE,mBACA,gBAGA,qDACE,gBACA,UACA,WACA,kBACA,cACA,WACA,kBACA,UACA,mBAIF,yCAGE,iBAIF,qEACE,mBAMN,eACE,iBAGF,oBACE,kBAMA,iEAGE,mBAIJ,kCAIM,qDACE,eAGF,yCACE,mBACA,gBACA","sourcesContent":["/*!\n * The styles for Jekyll theme Chirpy\n *\n * Chirpy v5.6.1 (https://github.com/cotes2020/jekyll-theme-chirpy)\n * © 2019 Cotes Chung\n * MIT Licensed\n */\n\n@import\n \"colors/light-typography\",\n \"colors/dark-typography\",\n\n \"addon/module\",\n \"addon/variables\",\n \"variables-hook\",\n \"addon/syntax\",\n \"addon/commons\",\n\n \"layout/home\",\n \"layout/post\",\n \"layout/tags\",\n \"layout/archives\",\n \"layout/categories\",\n \"layout/category-tag\";\n","/*\n* Mainly scss modules, only imported to `assets/css/main.scss`\n*/\n\n/* ---------- scss placeholder --------- */\n\n%heading {\n color: var(--heading-color);\n font-weight: 400;\n font-family: Lato, 'Microsoft Yahei', sans-serif;\n}\n\n%section {\n #core-wrapper & {\n margin-top: 2.5rem;\n margin-bottom: 1.25rem;\n\n &:focus {\n outline: none; /* avoid outline in Safari */\n }\n }\n}\n\n%anchor {\n .anchor {\n font-size: 80%;\n }\n\n @media (hover: hover) {\n .anchor {\n visibility: hidden;\n opacity: 0;\n transition: opacity 0.25s ease-in, visibility 0s ease-in 0.25s;\n }\n\n &:hover {\n .anchor {\n visibility: visible;\n opacity: 1;\n transition: opacity 0.25s ease-in, visibility 0s ease-in 0s;\n }\n }\n }\n}\n\n%tag-hover {\n background: var(--tag-hover);\n transition: background 0.35s ease-in-out;\n}\n\n%table-cell {\n padding: 0.4rem 1rem;\n font-size: 95%;\n white-space: nowrap;\n}\n\n%link-hover {\n color: #d2603a !important;\n border-bottom: 1px solid #d2603a;\n text-decoration: none;\n}\n\n%link-color {\n color: var(--link-color);\n}\n\n%link-underline {\n border-bottom: 1px solid var(--link-underline-color);\n}\n\n%clickable-transition {\n transition: color 0.35s ease-in-out;\n}\n\n%no-cursor {\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n\n%no-bottom-border {\n border-bottom: none;\n}\n\n%cursor-pointer {\n cursor: pointer;\n}\n\n%normal-font-style {\n font-style: normal;\n}\n\n%rounded {\n border-radius: 6px;\n}\n\n%img-caption {\n + em {\n display: block;\n text-align: center;\n font-style: normal;\n font-size: 80%;\n padding: 0;\n color: #6d6c6c;\n }\n}\n\n%sidebar-links {\n color: rgba(117, 117, 117, 0.9);\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n\n/* ---------- scss mixin --------- */\n\n@mixin no-text-decoration {\n text-decoration: none;\n}\n\n@mixin mt-mb($value) {\n margin-top: $value;\n margin-bottom: $value;\n}\n\n@mixin ml-mr($value) {\n margin-left: $value;\n margin-right: $value;\n}\n\n@mixin pl-pr($val) {\n padding-left: $val;\n padding-right: $val;\n}\n\n@mixin input-placeholder {\n opacity: 0.6;\n}\n\n@mixin label($font-size: 1rem, $font-weight: 600, $color: var(--label-color)) {\n color: $color;\n font-size: $font-size;\n font-weight: $font-weight;\n}\n\n@mixin align-center {\n position: relative;\n left: 50%;\n -webkit-transform: translateX(-50%);\n transform: translateX(-50%);\n}\n\n@mixin prompt($type, $fa-content, $fa-style: 'solid') {\n &.prompt-#{$type} {\n background-color: var(--prompt-#{$type}-bg);\n\n &::before {\n content: $fa-content;\n color: var(--prompt-#{$type}-icon-color);\n font: var(--fa-font-#{$fa-style});\n }\n }\n}\n","/*\n* The syntax highlight.\n*/\n\n@import 'colors/light-syntax';\n@import 'colors/dark-syntax';\n\nhtml {\n @media (prefers-color-scheme: light) {\n &:not([data-mode]),\n &[data-mode='light'] {\n @include light-syntax;\n }\n\n &[data-mode='dark'] {\n @include dark-syntax;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n &:not([data-mode]),\n &[data-mode='dark'] {\n @include dark-syntax;\n }\n\n &[data-mode='light'] {\n @include light-syntax;\n }\n }\n}\n\n/* -- code snippets -- */\n\n%code-snippet-bg {\n background: var(--highlight-bg-color);\n}\n\n%code-snippet-padding {\n padding-left: 1rem;\n padding-right: 1.5rem;\n}\n\n.highlighter-rouge {\n @extend %code-snippet-bg;\n @extend %rounded;\n\n color: var(--highlighter-rouge-color);\n margin-top: 0.5rem;\n margin-bottom: 1.2em; /* Override BS Inline-code style */\n}\n\n.highlight {\n @extend %rounded;\n @extend %code-snippet-bg;\n\n @at-root figure#{&} {\n @extend %code-snippet-bg;\n }\n\n overflow: auto;\n padding-top: 0.5rem;\n padding-bottom: 1rem;\n\n pre {\n margin-bottom: 0;\n font-size: $code-font-size;\n line-height: 1.4rem;\n word-wrap: normal; /* Fixed Safari overflow-x */\n }\n\n table {\n td pre {\n overflow: visible; /* Fixed iOS safari overflow-x */\n word-break: normal; /* Fixed iOS safari linenos code break */\n }\n }\n\n .lineno {\n padding-right: 0.5rem;\n min-width: 2.2rem;\n text-align: right;\n color: var(--highlight-lineno-color);\n -webkit-user-select: none;\n -moz-user-select: none;\n -o-user-select: none;\n -ms-user-select: none;\n user-select: none;\n }\n} /* .highlight */\n\ncode {\n -webkit-hyphens: none;\n -ms-hyphens: none;\n hyphens: none;\n\n &.highlighter-rouge {\n font-size: $code-font-size;\n padding: 3px 5px;\n word-break: break-word;\n border-radius: 4px;\n background-color: var(--inline-code-bg);\n }\n\n &.filepath {\n background-color: inherit;\n color: var(--filepath-text-color);\n font-weight: 600;\n padding: 0;\n }\n\n a > &.highlighter-rouge {\n padding-bottom: 0; /* show link's underlinke */\n color: inherit;\n }\n\n a:hover > &.highlighter-rouge {\n border-bottom: none;\n }\n\n blockquote & {\n color: inherit;\n }\n\n .highlight > & {\n color: transparent;\n }\n}\n\ntd.rouge-code {\n @extend %code-snippet-padding;\n\n /*\n Prevent some browser extends from\n changing the URL string of code block.\n */\n a {\n color: inherit !important;\n border-bottom: none !important;\n pointer-events: none;\n }\n}\n\n/* Hide line numbers for default, console, and terminal code snippets */\ndiv {\n &[class^='highlighter-rouge'],\n &.nolineno,\n &.language-plaintext.highlighter-rouge,\n &.language-console.highlighter-rouge,\n &.language-terminal.highlighter-rouge {\n pre.lineno {\n display: none;\n }\n\n td.rouge-code {\n padding-left: 1.5rem;\n }\n }\n}\n\n.code-header {\n @extend %no-cursor;\n\n $code-header-height: 2.25rem;\n\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: $code-header-height;\n\n &::before {\n $dot-size: 0.75rem;\n $dot-margin: 0.5rem;\n\n content: '';\n display: inline-block;\n margin-left: 1rem;\n width: $dot-size;\n height: $dot-size;\n border-radius: 50%;\n background-color: var(--code-header-muted-color);\n box-shadow: ($dot-size + $dot-margin) 0 0 var(--code-header-muted-color),\n ($dot-size + $dot-margin) * 2 0 0 var(--code-header-muted-color);\n }\n\n /* the label block */\n span {\n /* label icon */\n i {\n font-size: 1rem;\n margin-right: 0.4rem;\n color: var(--code-header-icon-color);\n\n &.small {\n font-size: 70%;\n }\n }\n\n @at-root [file] #{&} > i {\n position: relative;\n top: 1px; /* center the file icon */\n }\n\n /* label text */\n &::after {\n content: attr(data-label-text);\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--code-header-text-color);\n }\n }\n\n /* clipboard */\n button {\n @extend %cursor-pointer;\n @extend %rounded;\n\n border: 1px solid transparent;\n height: $code-header-height;\n width: $code-header-height;\n padding: 0;\n background-color: inherit;\n\n i {\n color: var(--code-header-icon-color);\n }\n\n &[timeout] {\n &:hover {\n border-color: var(--clipboard-checked-color);\n }\n\n i {\n color: var(--clipboard-checked-color);\n }\n }\n\n &:focus {\n outline: none;\n }\n\n &:not([timeout]):hover {\n background-color: rgba(128, 128, 128, 0.37);\n\n i {\n color: white;\n }\n }\n }\n}\n\n@media all and (max-width: 576px) {\n .post-content {\n > div[class^='language-'] {\n @include ml-mr(-1.25rem);\n\n border-radius: 0;\n\n .highlight {\n padding-left: 0.25rem;\n }\n\n .code-header {\n border-radius: 0;\n padding-left: 0.4rem;\n padding-right: 0.5rem;\n }\n }\n }\n}\n","/*\n * The syntax light mode code snippet colors.\n */\n\n@mixin light-syntax {\n /* see: */\n .highlight .hll { background-color: #ffffcc; }\n .highlight .c { color: #999988; font-style: italic; } /* Comment */\n .highlight .err { color: #a61717; background-color: #e3d2d2; } /* Error */\n .highlight .k { color: #000000; font-weight: bold; } /* Keyword */\n .highlight .o { color: #000000; font-weight: bold; } /* Operator */\n .highlight .cm { color: #999988; font-style: italic; } /* Comment.Multiline */\n .highlight .cp { color: #999999; font-weight: bold; font-style: italic; } /* Comment.Preproc */\n .highlight .c1 { color: #999988; font-style: italic; } /* Comment.Single */\n .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } /* Comment.Special */\n .highlight .gd { color: #d01040; background-color: #ffdddd; } /* Generic.Deleted */\n .highlight .ge { color: #000000; font-style: italic; } /* Generic.Emph */\n .highlight .gr { color: #aa0000; } /* Generic.Error */\n .highlight .gh { color: #999999; } /* Generic.Heading */\n .highlight .gi { color: #008080; background-color: #ddffdd; } /* Generic.Inserted */\n .highlight .go { color: #888888; } /* Generic.Output */\n .highlight .gp { color: #555555; } /* Generic.Prompt */\n .highlight .gs { font-weight: bold; } /* Generic.Strong */\n .highlight .gu { color: #aaaaaa; } /* Generic.Subheading */\n .highlight .gt { color: #aa0000; } /* Generic.Traceback */\n .highlight .kc { color: #000000; font-weight: bold; } /* Keyword.Constant */\n .highlight .kd { color: #000000; font-weight: bold; } /* Keyword.Declaration */\n .highlight .kn { color: #000000; font-weight: bold; } /* Keyword.Namespace */\n .highlight .kp { color: #000000; font-weight: bold; } /* Keyword.Pseudo */\n .highlight .kr { color: #000000; font-weight: bold; } /* Keyword.Reserved */\n .highlight .kt { color: #445588; font-weight: bold; } /* Keyword.Type */\n .highlight .m { color: #009999; } /* Literal.Number */\n .highlight .s { color: #d01040; } /* Literal.String */\n .highlight .na { color: #008080; } /* Name.Attribute */\n .highlight .nb { color: #0086b3; } /* Name.Builtin */\n .highlight .nc { color: #445588; font-weight: bold; } /* Name.Class */\n .highlight .no { color: #008080; } /* Name.Constant */\n .highlight .nd { color: #3c5d5d; font-weight: bold; } /* Name.Decorator */\n .highlight .ni { color: #800080; } /* Name.Entity */\n .highlight .ne { color: #990000; font-weight: bold; } /* Name.Exception */\n .highlight .nf { color: #990000; font-weight: bold; } /* Name.Function */\n .highlight .nl { color: #990000; font-weight: bold; } /* Name.Label */\n .highlight .nn { color: #555555; } /* Name.Namespace */\n .highlight .nt { color: #000080; } /* Name.Tag */\n .highlight .nv { color: #008080; } /* Name.Variable */\n .highlight .ow { color: #000000; font-weight: bold; } /* Operator.Word */\n .highlight .w { color: #bbbbbb; } /* Text.Whitespace */\n .highlight .mf { color: #009999; } /* Literal.Number.Float */\n .highlight .mh { color: #009999; } /* Literal.Number.Hex */\n .highlight .mi { color: #009999; } /* Literal.Number.Integer */\n .highlight .mo { color: #009999; } /* Literal.Number.Oct */\n .highlight .sb { color: #d01040; } /* Literal.String.Backtick */\n .highlight .sc { color: #d01040; } /* Literal.String.Char */\n .highlight .sd { color: #d01040; } /* Literal.String.Doc */\n .highlight .s2 { color: #d01040; } /* Literal.String.Double */\n .highlight .se { color: #d01040; } /* Literal.String.Escape */\n .highlight .sh { color: #d01040; } /* Literal.String.Heredoc */\n .highlight .si { color: #d01040; } /* Literal.String.Interpol */\n .highlight .sx { color: #d01040; } /* Literal.String.Other */\n .highlight .sr { color: #009926; } /* Literal.String.Regex */\n .highlight .s1 { color: #d01040; } /* Literal.String.Single */\n .highlight .ss { color: #990073; } /* Literal.String.Symbol */\n .highlight .bp { color: #999999; } /* Name.Builtin.Pseudo */\n .highlight .vc { color: #008080; } /* Name.Variable.Class */\n .highlight .vg { color: #008080; } /* Name.Variable.Global */\n .highlight .vi { color: #008080; } /* Name.Variable.Instance */\n .highlight .il { color: #009999; } /* Literal.Number.Integer.Long */\n\n /* --- custom light colors --- */\n --highlight-bg-color: #f7f7f7;\n --highlighter-rouge-color: #2f2f2f;\n --highlight-lineno-color: #c2c6cc;\n --inline-code-bg: #f3f3f3;\n --code-header-text-color: #a3a3b1;\n --code-header-muted-color: #ebebeb;\n --code-header-icon-color: #d1d1d1;\n --clipboard-checked-color: #43c743;\n\n [class^='prompt-'] {\n --inline-code-bg: #fbfafa;\n --highlighter-rouge-color: rgb(82, 82, 82);\n }\n} /* light-syntax */\n","/*\n * The syntax dark mode styles.\n */\n\n@mixin dark-syntax {\n --highlight-bg-color: #252525;\n --highlighter-rouge-color: #de6b18;\n --highlight-lineno-color: #6c6c6d;\n --inline-code-bg: #272822;\n --code-header-text-color: #6a6a6a;\n --code-header-muted-color: rgb(60, 60, 60);\n --code-header-icon-color: rgb(86, 86, 86);\n --clipboard-checked-color: #2bcc2b;\n --filepath-text-color: #bdbdbd;\n\n /* override Bootstrap */\n pre {\n color: #bfbfbf;\n }\n\n .highlight .gp {\n color: #818c96;\n }\n\n /* syntax highlight colors from https://raw.githubusercontent.com/jwarby/pygments-css/master/monokai.css */\n\n .highlight pre { background-color: var(--highlight-bg-color); }\n .highlight .hll { background-color: var(--highlight-bg-color); }\n .highlight .c { color: #75715e; } /* Comment */\n .highlight .err { color: #960050; background-color: #1e0010; } /* Error */\n .highlight .k { color: #66d9ef; } /* Keyword */\n .highlight .l { color: #ae81ff; } /* Literal */\n .highlight .n { color: #f8f8f2; } /* Name */\n .highlight .o { color: #f92672; } /* Operator */\n .highlight .p { color: #f8f8f2; } /* Punctuation */\n .highlight .cm { color: #75715e; } /* Comment.Multiline */\n .highlight .cp { color: #75715e; } /* Comment.Preproc */\n .highlight .c1 { color: #75715e; } /* Comment.Single */\n .highlight .cs { color: #75715e; } /* Comment.Special */\n .highlight .ge { color: inherit; font-style: italic; } /* Generic.Emph */\n .highlight .gs { font-weight: bold; } /* Generic.Strong */\n .highlight .kc { color: #66d9ef; } /* Keyword.Constant */\n .highlight .kd { color: #66d9ef; } /* Keyword.Declaration */\n .highlight .kn { color: #f92672; } /* Keyword.Namespace */\n .highlight .kp { color: #66d9ef; } /* Keyword.Pseudo */\n .highlight .kr { color: #66d9ef; } /* Keyword.Reserved */\n .highlight .kt { color: #66d9ef; } /* Keyword.Type */\n .highlight .ld { color: #e6db74; } /* Literal.Date */\n .highlight .m { color: #ae81ff; } /* Literal.Number */\n .highlight .s { color: #e6db74; } /* Literal.String */\n .highlight .na { color: #a6e22e; } /* Name.Attribute */\n .highlight .nb { color: #f8f8f2; } /* Name.Builtin */\n .highlight .nc { color: #a6e22e; } /* Name.Class */\n .highlight .no { color: #66d9ef; } /* Name.Constant */\n .highlight .nd { color: #a6e22e; } /* Name.Decorator */\n .highlight .ni { color: #f8f8f2; } /* Name.Entity */\n .highlight .ne { color: #a6e22e; } /* Name.Exception */\n .highlight .nf { color: #a6e22e; } /* Name.Function */\n .highlight .nl { color: #f8f8f2; } /* Name.Label */\n .highlight .nn { color: #f8f8f2; } /* Name.Namespace */\n .highlight .nx { color: #a6e22e; } /* Name.Other */\n .highlight .py { color: #f8f8f2; } /* Name.Property */\n .highlight .nt { color: #f92672; } /* Name.Tag */\n .highlight .nv { color: #f8f8f2; } /* Name.Variable */\n .highlight .ow { color: #f92672; } /* Operator.Word */\n .highlight .w { color: #f8f8f2; } /* Text.Whitespace */\n .highlight .mf { color: #ae81ff; } /* Literal.Number.Float */\n .highlight .mh { color: #ae81ff; } /* Literal.Number.Hex */\n .highlight .mi { color: #ae81ff; } /* Literal.Number.Integer */\n .highlight .mo { color: #ae81ff; } /* Literal.Number.Oct */\n .highlight .sb { color: #e6db74; } /* Literal.String.Backtick */\n .highlight .sc { color: #e6db74; } /* Literal.String.Char */\n .highlight .sd { color: #e6db74; } /* Literal.String.Doc */\n .highlight .s2 { color: #e6db74; } /* Literal.String.Double */\n .highlight .se { color: #ae81ff; } /* Literal.String.Escape */\n .highlight .sh { color: #e6db74; } /* Literal.String.Heredoc */\n .highlight .si { color: #e6db74; } /* Literal.String.Interpol */\n .highlight .sx { color: #e6db74; } /* Literal.String.Other */\n .highlight .sr { color: #e6db74; } /* Literal.String.Regex */\n .highlight .s1 { color: #e6db74; } /* Literal.String.Single */\n .highlight .ss { color: #e6db74; } /* Literal.String.Symbol */\n .highlight .bp { color: #f8f8f2; } /* Name.Builtin.Pseudo */\n .highlight .vc { color: #f8f8f2; } /* Name.Variable.Class */\n .highlight .vg { color: #f8f8f2; } /* Name.Variable.Global */\n .highlight .vi { color: #f8f8f2; } /* Name.Variable.Instance */\n .highlight .il { color: #ae81ff; } /* Literal.Number.Integer.Long */\n .highlight .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */\n .highlight .gd { color: #f92672; background-color: #561c08; } /* Generic.Deleted & Diff Deleted */\n .highlight .gi { color: #a6e22e; background-color: #0b5858; } /* Generic.Inserted & Diff Inserted */\n}\n","/*\n * The SCSS variables\n */\n\n/* sidebar */\n\n$sidebar-width: 260px !default; /* the basic width */\n$sidebar-width-small: 210px !default; /* screen width: >= 850px, <= 1199px (iPad landscape) */\n$sidebar-width-large: 350px !default; /* screen width: >= 1650px */\n\n/* tabs of sidebar */\n\n$tab-count: 5 !default; /* backward compatible (version <= 4.0.2) */\n$tab-height: 3rem !default;\n$tab-cursor-height: 1.6rem !default;\n$cursor-width: 2px !default; /* the cursor width of the selected tab */\n\n/* other framework sizes */\n\n$topbar-height: 3rem !default;\n$search-max-width: 210px !default;\n$footer-height: 5rem !default;\n$footer-height-mobile: 6rem !default; /* screen width: <= 576px */\n\n$main-content-max-width: 1250px !default;\n$bottom-min-height: 35rem !default;\n\n/* syntax highlight */\n\n$code-font-size: 0.85rem !default;\n","/*\n The common styles\n*/\n\nhtml {\n @media (prefers-color-scheme: light) {\n &:not([data-mode]),\n &[data-mode='light'] {\n @include light-scheme;\n }\n\n &[data-mode='dark'] {\n @include dark-scheme;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n &:not([data-mode]),\n &[data-mode='dark'] {\n @include dark-scheme;\n }\n\n &[data-mode='light'] {\n @include light-scheme;\n }\n }\n\n font-size: 16px;\n}\n\nbody {\n background: var(--main-bg);\n padding: env(safe-area-inset-top) env(safe-area-inset-right)\n env(safe-area-inset-bottom) env(safe-area-inset-left);\n color: var(--text-color);\n -webkit-font-smoothing: antialiased;\n font-family: 'Source Sans Pro', 'Microsoft Yahei', sans-serif;\n line-height: 1.75;\n}\n\n/* --- Typography --- */\n\nh1 {\n @extend %heading;\n\n font-size: 1.9rem;\n}\n\nh2 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.5rem;\n}\n\nh3 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.2rem;\n}\n\nh4 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.15rem;\n}\n\nh5 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.1rem;\n}\n\na {\n @extend %link-color;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n\n &[data-src] {\n &.lazyloaded {\n -webkit-animation: fade-in 0.4s ease-in;\n animation: fade-in 0.4s ease-in;\n }\n\n &[data-lqip='true'] {\n &.lazyload,\n &.lazyloading {\n -webkit-filter: blur(20px);\n filter: blur(20px);\n }\n }\n\n &:not([data-lqip='true']) {\n &.lazyload,\n &.lazyloading {\n background: var(--img-bg);\n }\n }\n\n &.shadow {\n -webkit-filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));\n filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));\n box-shadow: none !important; /* cover the Bootstrap 4.6.1 styles */\n }\n\n @extend %img-caption;\n }\n\n @-webkit-keyframes fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n @keyframes fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n}\n\nblockquote {\n border-left: 5px solid var(--blockquote-border-color);\n padding-left: 1rem;\n color: var(--blockquote-text-color);\n\n &[class^='prompt-'] {\n border-left: 0;\n position: relative;\n padding: 1rem 1rem 1rem 3rem;\n color: var(--prompt-text-color);\n\n @extend %rounded;\n\n &::before {\n text-align: center;\n width: 3rem;\n position: absolute;\n left: 0.25rem;\n margin-top: 0.4rem;\n text-rendering: auto;\n -webkit-font-smoothing: antialiased;\n }\n\n > p:last-child {\n margin-bottom: 0;\n }\n }\n\n @include prompt('tip', '\\f0eb', 'regular');\n @include prompt('info', '\\f06a');\n @include prompt('warning', '\\f06a');\n @include prompt('danger', '\\f071');\n}\n\nkbd {\n font-family: inherit;\n display: inline-block;\n vertical-align: middle;\n line-height: 1.3rem;\n min-width: 1.75rem;\n text-align: center;\n margin: 0 0.3rem;\n padding-top: 0.1rem;\n color: var(--kbd-text-color);\n background-color: var(--kbd-bg-color);\n border-radius: 0.25rem;\n border: solid 1px var(--kbd-wrap-color);\n box-shadow: inset 0 -2px 0 var(--kbd-wrap-color);\n}\n\nfooter {\n font-size: 0.8rem;\n background-color: var(--main-bg);\n\n div.d-flex {\n height: $footer-height;\n line-height: 1.2rem;\n padding-bottom: 1rem;\n border-top: 1px solid var(--main-border-color);\n\n > div {\n width: 350px;\n }\n }\n\n a {\n @extend %text-color;\n\n &:link {\n @include no-text-decoration;\n }\n\n &:hover {\n @extend %link-hover;\n @include no-text-decoration;\n }\n }\n\n .footer-right {\n text-align: right;\n }\n}\n\n/* fontawesome icons */\ni {\n &.far,\n &.fas {\n @extend %no-cursor;\n }\n}\n\n/* --- Panels --- */\n\n.access {\n top: 2rem;\n transition: top 0.2s ease-in-out;\n margin-top: 3rem;\n margin-bottom: 4rem;\n\n &:only-child {\n position: -webkit-sticky;\n position: sticky;\n }\n\n > div {\n padding-left: 1rem;\n border-left: 1px solid var(--main-border-color);\n\n &:not(:last-child) {\n margin-bottom: 4rem;\n }\n }\n\n .post-content {\n font-size: 0.9rem;\n }\n}\n\n#panel-wrapper {\n /* the headings */\n .panel-heading {\n @include label(inherit);\n }\n\n .post-tag {\n display: inline-block;\n line-height: 1rem;\n font-size: 0.85rem;\n background: none;\n border: 1px solid var(--btn-border-color);\n border-radius: 0.8rem;\n padding: 0.3rem 0.5rem;\n margin: 0 0.35rem 0.5rem 0;\n\n &:hover {\n background-color: #2a408e;\n border-color: #2a408e;\n color: #ffffff;\n transition: none;\n }\n }\n\n [data-topbar-visible='true'] & > div {\n top: 6rem;\n }\n}\n\n#access-lastmod {\n li {\n height: 1.8rem;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n list-style: none;\n }\n\n a {\n &:hover {\n @extend %link-hover;\n }\n\n @extend %no-bottom-border;\n\n color: inherit;\n }\n}\n\n.footnotes > ol {\n padding-left: 2rem;\n margin-top: 0.5rem;\n\n > li {\n &:not(:last-child) {\n margin-bottom: 0.3rem;\n }\n\n > p {\n margin-left: 0.25em;\n margin-top: 0;\n margin-bottom: 0;\n }\n\n /* [scroll-focus] added by `smooth-scroll.js` */\n &:target:not([scroll-focus]),\n &[scroll-focus='true'] > p {\n background-color: var(--footnote-target-bg);\n width: -moz-fit-content;\n width: -webkit-fit-content;\n width: fit-content;\n transition: background-color 1.5s ease-in-out;\n }\n }\n}\n\n.footnote {\n @at-root a#{&} {\n @include ml-mr(1px);\n @include pl-pr(2px);\n\n border-bottom-style: none !important;\n transition: background-color 1.5s ease-in-out;\n }\n\n /* [scroll-focus] added by `smooth-scroll.js` */\n @at-root sup:target:not([scroll-focus]),\n sup[scroll-focus='true'] > a#{&} {\n background-color: var(--footnote-target-bg);\n }\n}\n\n.reversefootnote {\n @at-root a#{&} {\n font-size: 0.6rem;\n line-height: 1;\n position: relative;\n bottom: 0.25em;\n margin-left: 0.25em;\n border-bottom-style: none !important;\n }\n}\n\n/* --- Begin of Markdown table style --- */\n\n/* it will be created by Liquid */\n.table-wrapper {\n overflow-x: auto;\n margin-bottom: 1.5rem;\n\n > table {\n min-width: 100%;\n overflow-x: auto;\n border-spacing: 0;\n\n thead {\n border-bottom: solid 2px rgba(210, 215, 217, 0.75);\n\n th {\n @extend %table-cell;\n }\n }\n\n tbody {\n tr {\n border-bottom: 1px solid var(--tb-border-color);\n\n &:nth-child(2n) {\n background-color: var(--tb-even-bg);\n }\n\n &:nth-child(2n + 1) {\n background-color: var(--tb-odd-bg);\n }\n\n td {\n @extend %table-cell;\n }\n }\n } /* tbody */\n } /* table */\n}\n\n/* --- post --- */\n\n.post {\n h1 {\n margin-top: 3rem;\n margin-bottom: 1.5rem;\n }\n\n p {\n > img[data-src],\n > a.popup {\n &:not(.normal):not(.left):not(.right) {\n @include align-center;\n }\n }\n }\n}\n\n.pageviews .fa-spinner {\n font-size: 80%;\n}\n\n.post-meta {\n font-size: 0.85rem;\n word-spacing: 1px;\n\n a {\n &:not([class]):hover {\n @extend %link-hover;\n }\n }\n\n em {\n @extend %normal-font-style;\n }\n}\n\n.post-content {\n font-size: 1.08rem;\n margin-top: 2rem;\n overflow-wrap: break-word;\n\n a {\n &.popup {\n @extend %no-cursor;\n @extend %img-caption;\n @include mt-mb(0.5rem);\n\n cursor: zoom-in;\n }\n\n &:not(.img-link) {\n @extend %link-underline;\n\n &:hover {\n @extend %link-hover;\n }\n }\n }\n\n ol,\n ul {\n &:not([class]),\n &.task-list {\n -webkit-padding-start: 1.75rem;\n padding-inline-start: 1.75rem;\n\n li {\n margin: 0.25rem 0;\n padding-left: 0.25rem;\n }\n\n ol,\n ul {\n -webkit-padding-start: 1.25rem;\n padding-inline-start: 1.25rem;\n margin: 0.5rem 0;\n }\n }\n }\n\n ul.task-list {\n -webkit-padding-start: 1.25rem;\n padding-inline-start: 1.25rem;\n\n li {\n list-style-type: none;\n padding-left: 0;\n\n /* checkbox icon */\n > i {\n width: 2rem;\n margin-left: -1.25rem;\n color: var(--checkbox-color);\n\n &.checked {\n color: var(--checkbox-checked-color);\n }\n }\n\n ul {\n -webkit-padding-start: 1.75rem;\n padding-inline-start: 1.75rem;\n }\n }\n\n input[type='checkbox'] {\n margin: 0 0.5rem 0.2rem -1.3rem;\n vertical-align: middle;\n }\n } /* ul */\n\n dl > dd {\n margin-left: 1rem;\n }\n} /* .post-content */\n\n.tag:hover {\n @extend %tag-hover;\n}\n\n.post-tag {\n display: inline-block;\n min-width: 2rem;\n text-align: center;\n background: var(--tag-bg);\n border-radius: 0.3rem;\n padding: 0 0.4rem;\n color: inherit;\n line-height: 1.3rem;\n\n &:not(:last-child) {\n margin-right: 0.2rem;\n }\n\n &:hover {\n @extend %tag-hover;\n\n border-bottom: none;\n text-decoration: none;\n color: #d2603a;\n }\n}\n\n.rounded-10 {\n border-radius: 10px !important;\n}\n\n.img-link {\n color: transparent;\n display: inline-flex;\n overflow: hidden;\n}\n\n.shimmer {\n overflow: hidden;\n position: relative;\n background: var(--img-bg);\n\n &::before {\n content: '';\n position: absolute;\n background: var(--shimmer-bg);\n height: 100%;\n width: 100%;\n -webkit-animation: shimmer 1s infinite;\n animation: shimmer 1s infinite;\n }\n\n @-webkit-keyframes shimmer {\n 0% {\n -webkit-transform: translateX(-100%);\n transform: translateX(-100%);\n }\n 100% {\n -webkit-transform: translateX(100%);\n transform: translateX(100%);\n }\n }\n\n @keyframes shimmer {\n 0% {\n -webkit-transform: translateX(-100%);\n transform: translateX(-100%);\n }\n 100% {\n -webkit-transform: translateX(100%);\n transform: translateX(100%);\n }\n }\n}\n\n.embed-video {\n width: 100%;\n height: 100%;\n margin-bottom: 1rem;\n\n @extend %rounded;\n\n &.youtube {\n aspect-ratio: 16 / 9;\n }\n\n &.twitch {\n aspect-ratio: 310 / 189;\n }\n}\n\n/* --- buttons --- */\n.btn-lang {\n border: 1px solid !important;\n padding: 1px 3px;\n border-radius: 3px;\n color: var(--link-color);\n\n &:focus {\n box-shadow: none;\n }\n}\n\n/* --- Effects classes --- */\n\n.loaded {\n display: block !important;\n\n @at-root .d-flex#{&} {\n display: flex !important;\n }\n}\n\n.unloaded {\n display: none !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.hidden {\n visibility: hidden !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.btn-box-shadow {\n box-shadow: 0 0 8px 0 var(--btn-box-shadow) !important;\n}\n\n.no-text-decoration {\n @include no-text-decoration;\n}\n\n/* Overrided BS4 Tooltip */\n.tooltip-inner {\n font-size: 0.7rem;\n max-width: 220px;\n text-align: left;\n}\n\n.disabled {\n color: rgb(206, 196, 196);\n pointer-events: auto;\n cursor: not-allowed;\n}\n\n.hide-border-bottom {\n border-bottom: none !important;\n}\n\n.input-focus {\n box-shadow: none;\n border-color: var(--input-focus-border-color) !important;\n background: center !important;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n}\n\n.left {\n float: left;\n margin: 0.75rem 1rem 1rem 0 !important;\n}\n\n.right {\n float: right;\n margin: 0.75rem 0 1rem 1rem !important;\n}\n\n/* --- Overriding --- */\n\n/* magnific-popup */\nfigure .mfp-title {\n text-align: center;\n padding-right: 0;\n margin-top: 0.5rem;\n}\n\n/* mermaid */\n.mermaid {\n text-align: center;\n}\n\n/* MathJax */\nmjx-container {\n overflow-y: hidden;\n min-width: auto !important;\n}\n\n/* --- sidebar layout --- */\n\n$sidebar-display: 'sidebar-display';\n\n#sidebar {\n @include pl-pr(0);\n\n position: fixed;\n top: 0;\n left: 0;\n height: 100%;\n overflow-y: auto;\n width: $sidebar-width;\n z-index: 99;\n background: var(--sidebar-bg);\n\n /* Hide scrollbar for Chrome, Safari and Opera */\n &::-webkit-scrollbar {\n display: none;\n }\n\n /* Hide scrollbar for IE, Edge and Firefox */\n -ms-overflow-style: none; /* IE and Edge */\n scrollbar-width: none; /* Firefox */\n\n a {\n @extend %sidebar-links;\n\n &:hover {\n @include no-text-decoration;\n\n color: var(--sidebar-active-color) !important;\n }\n }\n\n #avatar {\n > a {\n display: block;\n width: 6rem;\n height: 6rem;\n border-radius: 50%;\n border: 2px solid rgba(222, 222, 222, 0.7);\n overflow: hidden;\n -webkit-transform: translateZ(0);\n transform: translateZ(0); /* fixed the zoom in Safari */\n transition: border-color 0.35s ease-in-out;\n\n &:hover {\n border-color: white;\n }\n }\n\n img {\n width: 100%;\n height: 100%;\n transition: -webkit-transform 0.5s;\n transition: transform 0.5s;\n transition: transform 0.5s, -webkit-transform 0.5s;\n\n &:hover {\n -webkit-transform: scale(1.2);\n transform: scale(1.2);\n }\n }\n } /* #avatar */\n\n .site-title {\n margin-top: 0.55rem;\n\n a {\n @extend %clickable-transition;\n\n font-weight: 900;\n font-size: 1.5rem;\n letter-spacing: 0.5px;\n color: rgba(134, 133, 133, 0.99);\n }\n }\n\n .site-subtitle {\n font-size: 95%;\n color: var(--sidebar-muted-color);\n line-height: 1.25rem;\n word-spacing: 1px;\n margin: 0.2rem 1.5rem 0.5rem 1.5rem;\n min-height: 3rem; /* avoid vertical shifting in multi-line words */\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n }\n\n .nav-link {\n border-radius: 0;\n font-size: 0.95rem;\n font-weight: 600;\n letter-spacing: 1px;\n display: table-cell;\n vertical-align: middle;\n }\n\n .nav-item {\n text-align: center;\n display: table;\n height: $tab-height;\n\n &.active {\n .nav-link {\n color: var(--sidebar-active-color);\n }\n }\n\n &:not(.active) > a {\n @extend %clickable-transition;\n }\n }\n\n ul {\n height: $tab-height * $tab-count;\n margin-bottom: 2rem;\n padding-left: 0;\n\n li {\n width: 100%;\n\n &:last-child {\n a {\n position: relative;\n left: calc($cursor-width / 2);\n width: 100%;\n }\n\n /* the cursor */\n &::after {\n display: table;\n visibility: hidden;\n content: '';\n position: relative;\n right: 1px;\n width: $cursor-width;\n height: $tab-cursor-height;\n border-radius: 1px;\n background-color: var(--nav-cursor-color);\n pointer-events: none;\n }\n }\n } /* li */\n\n @mixin fix-cursor($top) {\n top: $top;\n visibility: visible;\n }\n\n @for $i from 1 through $tab-count {\n $offset: $tab-count - $i;\n $top: (-$offset * $tab-height) +\n (($tab-height - $tab-cursor-height) * 0.5);\n\n @if $i < $tab-count {\n > li.active:nth-child(#{$i}),\n > li.nav-item:nth-child(#{$i}):hover {\n ~ li:last-child::after {\n @include fix-cursor($top);\n }\n }\n } @else {\n > li.active:nth-child(#{$i}):last-child::after,\n > li.nav-item:nth-child(#{$i}):last-child:hover::after {\n @include fix-cursor($top);\n }\n }\n } /* @for */\n } /* ul */\n\n .sidebar-bottom {\n margin-bottom: 2.1rem;\n\n @include ml-mr(auto);\n @include pl-pr(1rem);\n\n %icon {\n width: 2.4rem;\n text-align: center;\n }\n\n a {\n @extend %icon;\n @extend %clickable-transition;\n }\n\n i {\n font-size: 1.2rem;\n line-height: 1.75rem;\n }\n\n .mode-toggle {\n padding: 0;\n border: 0;\n margin-bottom: 1px;\n background-color: transparent;\n\n @extend %icon;\n @extend %sidebar-links;\n\n > i {\n @extend %clickable-transition;\n }\n\n &:hover > i {\n color: var(--sidebar-active-color);\n }\n }\n\n .icon-border {\n @extend %no-cursor;\n\n background-color: var(--sidebar-muted-color);\n content: '';\n width: 3px;\n height: 3px;\n border-radius: 50%;\n }\n } /* .sidebar-bottom */\n} /* #sidebar */\n\n@media (hover: hover) {\n #sidebar ul > li:last-child::after {\n transition: top 0.5s ease;\n }\n}\n\n.profile-wrapper {\n margin-top: 2rem;\n width: 100%;\n}\n\n#search-result-wrapper {\n display: none;\n height: 100%;\n width: 100%;\n overflow: auto;\n\n .post-content {\n margin-top: 2rem;\n }\n}\n\n/* --- top-bar --- */\n\n#topbar-wrapper {\n height: $topbar-height;\n position: fixed;\n top: 0;\n left: $sidebar-width; /* same as sidebar width */\n right: 0;\n transition: top 0.2s ease-in-out;\n z-index: 50;\n border-bottom: 1px solid rgba(0, 0, 0, 0.07);\n background-color: var(--topbar-wrapper-bg);\n\n [data-topbar-visible='false'] & {\n top: -$topbar-height; /* same as topbar height. */\n }\n}\n\n#topbar {\n /* icons */\n i {\n color: #999999;\n }\n\n #breadcrumb {\n font-size: 1rem;\n color: gray;\n padding-left: 0.5rem;\n\n a:hover {\n @extend %link-hover;\n }\n\n span {\n &:not(:last-child) {\n &::after {\n content: '›';\n padding: 0 0.3rem;\n }\n }\n }\n }\n} /* #topbar */\n\n#sidebar-trigger,\n#search-trigger {\n display: none;\n}\n\n#search-wrapper {\n display: flex;\n width: 100%;\n border-radius: 1rem;\n border: 1px solid var(--search-wrapper-border-color);\n background: var(--search-wrapper-bg);\n padding: 0 0.5rem;\n\n i {\n z-index: 2;\n font-size: 0.9rem;\n color: var(--search-icon-color);\n }\n}\n\n/* 'Cancel' link */\n#search-cancel {\n color: var(--link-color);\n margin-left: 1rem;\n display: none;\n\n @extend %cursor-pointer;\n}\n\n#search-input {\n background: center;\n border: 0;\n border-radius: 0;\n padding: 0.18rem 0.3rem;\n color: var(--text-color);\n height: auto;\n\n &:focus {\n box-shadow: none;\n background: center;\n\n &.form-control {\n &::-moz-placeholder {\n @include input-placeholder;\n }\n &::-webkit-input-placeholder {\n @include input-placeholder;\n }\n &:-ms-input-placeholder {\n @include input-placeholder;\n }\n &::-ms-input-placeholder {\n @include input-placeholder;\n }\n &::placeholder {\n @include input-placeholder;\n }\n }\n }\n}\n\n#search-hints {\n padding: 0 1rem;\n\n h4 {\n margin-bottom: 1.5rem;\n }\n\n .post-tag {\n display: inline-block;\n line-height: 1rem;\n font-size: 1rem;\n background: var(--search-tag-bg);\n border: none;\n padding: 0.5rem;\n margin: 0 1.25rem 1rem 0;\n\n &::before {\n content: '#';\n color: var(--text-muted-color);\n padding-right: 0.2rem;\n }\n\n @extend %link-color;\n }\n}\n\n#search-results {\n padding-bottom: 3rem;\n\n a {\n &:hover {\n @extend %link-hover;\n }\n\n @extend %link-color;\n @extend %no-bottom-border;\n @extend %heading;\n\n font-size: 1.4rem;\n line-height: 2.5rem;\n }\n\n > div {\n width: 100%;\n\n &:not(:last-child) {\n margin-bottom: 1rem;\n }\n\n /* icons */\n i {\n color: #818182;\n margin-right: 0.15rem;\n font-size: 80%;\n }\n\n > p {\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n }\n }\n} /* #search-results */\n\n#topbar-title {\n display: none;\n font-size: 1.1rem;\n font-weight: 600;\n font-family: sans-serif;\n color: var(--topbar-text-color);\n text-align: center;\n width: 70%;\n overflow: hidden;\n text-overflow: ellipsis;\n word-break: keep-all;\n white-space: nowrap;\n}\n\n#core-wrapper {\n .categories,\n #tags,\n #archives {\n a:not(:hover) {\n @extend %no-bottom-border;\n }\n }\n\n @at-root .row:only-child > #{&} {\n padding-bottom: 3rem;\n }\n}\n\n#mask {\n display: none;\n position: fixed;\n inset: 0 0 0 0;\n height: 100%;\n width: 100%;\n z-index: 1;\n\n @at-root [#{$sidebar-display}] & {\n display: block !important;\n }\n}\n\n/* --- main wrapper --- */\n\n#main-wrapper {\n background-color: var(--main-bg);\n position: relative;\n min-height: calc(100vh - #{$footer-height});\n\n @include pl-pr(0);\n}\n\n#core-wrapper,\n#panel-wrapper {\n margin-top: $topbar-height; /* same as the height of topbar */\n}\n\n#topbar-wrapper.row,\n#main > .row,\n#search-result-wrapper > .row {\n @include ml-mr(0);\n}\n\n/* --- button back-to-top --- */\n\n#back-to-top {\n $size: 2.7em;\n\n display: none;\n z-index: 1;\n cursor: pointer;\n position: fixed;\n background: var(--button-bg);\n color: var(--btn-backtotop-color);\n padding: 0;\n width: $size;\n height: $size;\n border-radius: 50%;\n border: 1px solid var(--btn-backtotop-border-color);\n transition: -webkit-transform 0.2s ease-out;\n transition: transform 0.2s ease-out;\n transition: transform 0.2s ease-out, -webkit-transform 0.2s ease-out;\n -webkit-transition: transform 0.2s ease-out;\n\n i {\n line-height: $size;\n position: relative;\n bottom: 2px;\n }\n}\n\n#back-to-top:hover {\n transform: translate3d(0, -5px, 0);\n -webkit-transform: translate3d(0, -5px, 0);\n}\n\n#notification {\n @-webkit-keyframes popup {\n from {\n opacity: 0;\n bottom: 0;\n }\n }\n\n @keyframes popup {\n from {\n opacity: 0;\n bottom: 0;\n }\n }\n\n .toast-header {\n background: none;\n border-bottom: none;\n color: inherit;\n }\n\n .toast-body {\n font-family: Lato, sans-serif;\n line-height: 1.25rem;\n\n button {\n font-size: 90%;\n min-width: 4rem;\n }\n }\n\n &.toast {\n display: none;\n\n &.show {\n display: block;\n min-width: 20rem;\n border-radius: 0.5rem;\n -webkit-backdrop-filter: blur(10px);\n backdrop-filter: blur(10px);\n background-color: rgba(255, 255, 255, 0.5);\n color: #1b1b1eba;\n position: fixed;\n left: 50%;\n bottom: 20%;\n -webkit-transform: translateX(-50%);\n transform: translateX(-50%);\n -webkit-animation: popup 0.8s;\n animation: popup 0.8s;\n }\n }\n}\n\n/*\n Responsive Design:\n\n {sidebar, content, panel} >= 1120px screen width\n {sidebar, content} >= 850px screen width\n {content} <= 849px screen width\n\n*/\n\n@media all and (max-width: 576px) {\n footer {\n height: $footer-height-mobile;\n\n div.d-flex {\n padding: 1.5rem 0;\n line-height: 1.65;\n flex-wrap: wrap;\n justify-content: space-around !important;\n }\n\n .footer-left,\n .footer-right {\n text-align: center;\n }\n }\n\n #main-wrapper {\n min-height: calc(100vh - #{$footer-height-mobile});\n }\n\n #core-wrapper {\n min-height: calc(\n 100vh - #{$topbar-height} - #{$footer-height-mobile}\n ) !important;\n\n h1 {\n margin-top: 2.2rem;\n font-size: 1.75rem;\n }\n\n .post-content {\n > blockquote[class^='prompt-'] {\n @include ml-mr(-1.25rem);\n\n border-radius: 0;\n max-width: none;\n }\n }\n }\n\n #avatar > a {\n width: 5rem;\n height: 5rem;\n }\n\n .site-subtitle {\n @include ml-mr(1.8rem);\n }\n}\n\n@media all and (max-width: 768px) {\n %full-width {\n max-width: 100%;\n }\n\n #topbar {\n @extend %full-width;\n }\n\n #main {\n @extend %full-width;\n @include pl-pr(0);\n }\n}\n\n/* hide sidebar and panel */\n@media all and (max-width: 849px) {\n @mixin slide($append: null) {\n $basic: transform 0.4s ease;\n\n @if $append {\n transition: $basic, $append;\n } @else {\n transition: $basic;\n }\n }\n\n html,\n body {\n overflow-x: hidden;\n }\n\n footer {\n @include slide;\n }\n\n [#{$sidebar-display}] {\n #sidebar {\n -webkit-transform: translateX(0);\n transform: translateX(0);\n }\n\n #topbar-wrapper,\n #main-wrapper,\n footer {\n -webkit-transform: translateX(#{$sidebar-width});\n transform: translateX(#{$sidebar-width});\n }\n }\n\n #sidebar {\n @include slide;\n\n transform: translateX(-#{$sidebar-width}); /* hide */\n -webkit-transform: translateX(-#{$sidebar-width});\n\n .cursor {\n transition: none;\n }\n }\n\n #main-wrapper {\n @include slide;\n\n padding-top: $topbar-height;\n }\n\n #topbar,\n #main,\n footer > .container {\n max-width: 100%;\n }\n\n #search-result-wrapper {\n width: 100%;\n }\n\n #breadcrumb,\n #search-wrapper {\n display: none;\n }\n\n #topbar-wrapper {\n @include slide(top 0.2s ease);\n\n left: 0;\n }\n\n #core-wrapper,\n #panel-wrapper {\n margin-top: 0;\n }\n\n #topbar-title,\n #sidebar-trigger,\n #search-trigger {\n display: block;\n }\n\n #search-result-wrapper .post-content {\n letter-spacing: 0;\n }\n\n #tags {\n justify-content: center !important;\n }\n\n h1.dynamic-title {\n display: none;\n\n ~ .post-content {\n margin-top: 3rem;\n }\n }\n} /* max-width: 849px */\n\n@media all and (max-width: 849px) and (orientation: portrait) {\n [data-topbar-visible='false'] #topbar-wrapper {\n top: 0;\n }\n}\n\n/* Phone & Pad */\n@media all and (min-width: 577px) and (max-width: 1199px) {\n footer .d-flex > div {\n width: 312px;\n }\n}\n\n/* Sidebar is visible */\n@media all and (min-width: 850px) {\n /* Solved jumping scrollbar */\n html {\n overflow-y: scroll;\n }\n\n #main-wrapper,\n footer {\n margin-left: $sidebar-width;\n }\n\n .profile-wrapper {\n margin-top: 3rem;\n }\n\n #search-hints {\n display: none;\n }\n\n #search-wrapper {\n max-width: $search-max-width;\n }\n\n #search-result-wrapper {\n margin-top: 3rem;\n max-width: $main-content-max-width;\n }\n\n div.post-content .table-wrapper > table {\n min-width: 70%;\n }\n\n /* button 'back-to-Top' position */\n #back-to-top {\n bottom: 5.5rem;\n right: 5%;\n }\n\n #topbar-title {\n text-align: left;\n }\n}\n\n/* Pad horizontal */\n@media all and (min-width: 992px) and (max-width: 1199px) {\n #main .col-lg-11 {\n flex: 0 0 96%;\n max-width: 96%;\n }\n}\n\n/* Compact icons in sidebar & panel hidden */\n@media all and (min-width: 850px) and (max-width: 1199px) {\n #sidebar {\n width: $sidebar-width-small;\n\n .site-subtitle {\n margin-left: 1rem;\n margin-right: 1rem;\n }\n\n .sidebar-bottom {\n a,\n span {\n width: 2rem;\n }\n\n .icon-border {\n left: -3px;\n }\n }\n }\n\n #topbar-wrapper {\n left: $sidebar-width-small;\n }\n\n #search-results > div {\n max-width: 700px;\n }\n\n .site-title {\n font-size: 1.3rem;\n margin-left: 0 !important;\n }\n\n .site-subtitle {\n @include ml-mr(1rem);\n\n font-size: 90%;\n }\n\n #main-wrapper,\n footer {\n margin-left: $sidebar-width-small;\n }\n\n #breadcrumb {\n width: 65%;\n overflow: hidden;\n text-overflow: ellipsis;\n word-break: keep-all;\n white-space: nowrap;\n }\n}\n\n/* panel hidden */\n@media all and (max-width: 1199px) {\n #panel-wrapper {\n display: none;\n }\n\n #main > div.row {\n justify-content: center !important;\n }\n}\n\n/* --- desktop mode, both sidebar and panel are visible --- */\n\n@media all and (min-width: 1200px) {\n #back-to-top {\n bottom: 6.5rem;\n }\n\n #search-wrapper {\n margin-right: 4rem;\n }\n\n #search-input {\n transition: all 0.3s ease-in-out;\n }\n\n #search-results > div {\n width: 46%;\n\n &:nth-child(odd) {\n margin-right: 1.5rem;\n }\n\n &:nth-child(even) {\n margin-left: 1.5rem;\n }\n\n &:last-child:nth-child(odd) {\n position: relative;\n right: 24.3%;\n }\n }\n\n .post-content {\n font-size: 1.03rem;\n }\n\n footer {\n div.d-felx {\n width: 85%;\n }\n }\n}\n\n@media all and (min-width: 1400px) {\n #back-to-top {\n right: calc((100vw - #{$sidebar-width} - 1140px) / 2 + 3rem);\n }\n}\n\n@media all and (min-width: 1650px) {\n #main-wrapper,\n footer {\n margin-left: $sidebar-width-large;\n }\n\n #topbar-wrapper {\n left: $sidebar-width-large;\n }\n\n #search-wrapper {\n margin-right: calc(\n #{$main-content-max-width} * 0.25 - #{$search-max-width}\n );\n }\n\n #topbar,\n #main,\n footer > .container {\n max-width: $main-content-max-width;\n }\n\n #core-wrapper,\n #tail-wrapper {\n padding-right: 4.5rem !important;\n }\n\n #back-to-top {\n right: calc(\n (100vw - #{$sidebar-width-large} - #{$main-content-max-width}) / 2 + 2rem\n );\n }\n\n #sidebar {\n width: $sidebar-width-large;\n\n .profile-wrapper {\n margin-top: 4rem;\n margin-bottom: 1rem;\n\n &.text-center {\n text-align: left !important;\n }\n\n %profile-ml {\n margin-left: 4.5rem;\n }\n\n #avatar {\n @extend %profile-ml;\n\n > a {\n width: 6.2rem;\n height: 6.2rem;\n\n &.mx-auto {\n margin-left: 0 !important;\n }\n }\n }\n\n .site-title {\n @extend %profile-ml;\n\n margin-top: 0.4rem;\n\n a {\n font-size: 1.7rem;\n letter-spacing: 1px;\n }\n }\n\n .site-subtitle {\n @extend %profile-ml;\n\n word-spacing: 0;\n margin-top: 0;\n }\n } /* .profile-wrapper (min-width: 1650px) */\n\n ul {\n padding-left: 2.5rem;\n\n > li:last-child {\n > a {\n position: static;\n }\n }\n\n .nav-item {\n text-align: left;\n\n .nav-link {\n > span {\n letter-spacing: 2px;\n }\n\n > i {\n &.unloaded {\n display: inline-block !important;\n }\n }\n }\n }\n }\n\n .sidebar-bottom {\n padding-left: 3.5rem;\n width: 100%;\n\n $icon-block-size: 2rem;\n\n &.justify-content-center {\n justify-content: flex-start !important;\n }\n\n > span,\n > button.mode-toggle,\n > a {\n @include ml-mr(0.15rem);\n\n height: $icon-block-size;\n margin-bottom: 0.5rem; /* wrap line */\n }\n\n i {\n background-color: var(--sidebar-btn-bg);\n font-size: 1rem;\n width: $icon-block-size;\n height: $icon-block-size;\n border-radius: 50%;\n position: relative;\n\n &::before {\n position: absolute;\n top: 50%;\n left: 50%;\n -webkit-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n }\n }\n\n .icon-border {\n top: 0.9rem;\n }\n } /* .sidebar-bottom */\n } /* #sidebar */\n} /* min-width: 1650px */\n","/*\n * The syntax light mode typography colors\n */\n\n@mixin light-scheme {\n /* Framework color */\n --main-bg: white;\n --mask-bg: #c1c3c5;\n --main-border-color: #f3f3f3;\n\n /* Common color */\n --text-color: #34343c;\n --text-muted-color: gray;\n --heading-color: black;\n --blockquote-border-color: #eeeeee;\n --blockquote-text-color: #9a9a9a;\n --link-color: #2a408e;\n --link-underline-color: #dee2e6;\n --button-bg: #ffffff;\n --btn-border-color: #e9ecef;\n --btn-backtotop-color: #686868;\n --btn-backtotop-border-color: #f1f1f1;\n --btn-box-shadow: #eaeaea;\n --checkbox-color: #c5c5c5;\n --checkbox-checked-color: #07a8f7;\n --img-bg: radial-gradient(\n circle,\n rgb(255, 255, 255) 0%,\n rgb(249, 249, 249) 100%\n );\n --shimmer-bg: linear-gradient(\n 90deg,\n rgba(250, 250, 250, 0) 0%,\n rgba(232, 230, 230, 1) 50%,\n rgba(250, 250, 250, 0) 100%\n );\n\n /* Sidebar */\n --sidebar-bg: #eeeeee;\n --sidebar-muted-color: #a2a19f;\n --sidebar-active-color: #424242;\n --nav-cursor-color: #757575;\n --sidebar-btn-bg: white;\n\n /* Topbar */\n --topbar-text-color: rgb(78, 78, 78);\n --topbar-wrapper-bg: white;\n --search-wrapper-bg: rgb(245, 245, 245, 0.5);\n --search-wrapper-border-color: rgb(245, 245, 245);\n --search-tag-bg: #f8f9fa;\n --search-icon-color: #c2c6cc;\n --input-focus-border-color: var(--btn-border-color);\n\n /* Home page */\n --post-list-text-color: dimgray;\n --btn-patinator-text-color: #555555;\n --btn-paginator-hover-color: var(--sidebar-bg);\n --btn-paginator-border-color: var(--sidebar-bg);\n --btn-text-color: #676666;\n --pin-bg: #f5f5f5;\n --pin-color: #999fa4;\n\n /* Posts */\n --toc-highlight: #563d7c;\n --btn-share-hover-color: var(--link-color);\n --card-hovor-bg: #eeeeee;\n --card-border-color: #ececec;\n --card-box-shadow: rgba(234, 234, 234, 0.76);\n --label-color: #616161;\n --relate-post-date: rgba(30, 55, 70, 0.4);\n --footnote-target-bg: lightcyan;\n --tag-bg: rgba(0, 0, 0, 0.075);\n --tag-border: #dee2e6;\n --tag-shadow: var(--btn-border-color);\n --tag-hover: rgb(222, 226, 230);\n --tb-odd-bg: #fbfcfd;\n --tb-border-color: #eaeaea;\n --dash-color: silver;\n --kbd-wrap-color: #bdbdbd;\n --kbd-text-color: var(--text-color);\n --kbd-bg-color: white;\n --prompt-text-color: rgb(46, 46, 46, 0.77);\n --prompt-tip-bg: rgb(123, 247, 144, 0.2);\n --prompt-tip-icon-color: #03b303;\n --prompt-info-bg: #e1f5fe;\n --prompt-info-icon-color: #0070cb;\n --prompt-warning-bg: rgb(255, 243, 205);\n --prompt-warning-icon-color: #ef9c03;\n --prompt-danger-bg: rgb(248, 215, 218, 0.56);\n --prompt-danger-icon-color: #df3c30;\n\n [class^='prompt-'] {\n --link-underline-color: rgb(219, 216, 216);\n }\n\n .dark {\n display: none;\n }\n\n /* Categories */\n --categories-hover-bg: var(--btn-border-color);\n --categories-icon-hover-color: darkslategray;\n\n /* Archive */\n --timeline-color: rgba(0, 0, 0, 0.075);\n --timeline-node-bg: #c2c6cc;\n --timeline-year-dot-color: #ffffff;\n} /* light-scheme */\n","/*\n * The main dark mode styles\n */\n\n@mixin dark-scheme {\n /* Framework color */\n --main-bg: rgb(27, 27, 30);\n --mask-bg: rgb(68, 69, 70);\n --main-border-color: rgb(44, 45, 45);\n\n /* Common color */\n --text-color: rgb(175, 176, 177);\n --text-muted-color: rgb(107, 116, 124);\n --heading-color: #cccccc;\n --blockquote-border-color: rgb(66, 66, 66);\n --blockquote-text-color: rgb(117, 117, 117);\n --link-color: rgb(138, 180, 248);\n --link-underline-color: rgb(82, 108, 150);\n --button-bg: rgb(39, 40, 43);\n --btn-border-color: rgb(63, 65, 68);\n --btn-backtotop-color: var(--text-color);\n --btn-backtotop-border-color: var(--btn-border-color);\n --btn-box-shadow: var(--main-bg);\n --card-header-bg: rgb(51, 50, 50);\n --label-color: rgb(108, 117, 125);\n --checkbox-color: rgb(118, 120, 121);\n --checkbox-checked-color: var(--link-color);\n --img-bg: radial-gradient(circle, rgb(22, 22, 24) 0%, rgb(32, 32, 32) 100%);\n --shimmer-bg: linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(58, 55, 55, 0.4) 50%,\n rgba(255, 255, 255, 0) 100%\n );\n\n /* Sidebar */\n --sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);\n --sidebar-muted-color: #6d6c6b;\n --sidebar-active-color: rgb(255, 255, 255, 0.8);\n --nav-cursor-color: rgb(183, 182, 182);\n --sidebar-btn-bg: rgb(117, 116, 116, 0.2);\n\n /* Topbar */\n --topbar-text-color: var(--text-color);\n --topbar-wrapper-bg: rgb(39, 40, 43);\n --search-wrapper-bg: rgb(34, 34, 39);\n --search-wrapper-border-color: rgb(34, 34, 39);\n --search-icon-color: rgb(100, 102, 105);\n --input-focus-border-color: rgb(112, 114, 115);\n\n /* Home page */\n --post-list-text-color: rgb(175, 176, 177);\n --btn-patinator-text-color: var(--text-color);\n --btn-paginator-hover-color: rgb(64, 65, 66);\n --btn-paginator-border-color: var(--btn-border-color);\n --btn-text-color: var(--text-color);\n --pin-bg: rgb(34, 35, 37);\n --pin-color: inherit;\n\n /* Posts */\n --toc-highlight: rgb(116, 178, 243);\n --tag-bg: rgb(41, 40, 40);\n --tag-hover: rgb(43, 56, 62);\n --tb-odd-bg: rgba(42, 47, 53, 0.52); /* odd rows of the posts' table */\n --tb-even-bg: rgb(31, 31, 34); /* even rows of the posts' table */\n --tb-border-color: var(--tb-odd-bg);\n --footnote-target-bg: rgb(63, 81, 181);\n --btn-share-color: #6c757d;\n --btn-share-hover-color: #bfc1ca;\n --relate-post-date: var(--text-muted-color);\n --card-bg: #212121;\n --card-hovor-bg: #3a3a3a;\n --card-border-color: rgb(53, 53, 60);\n --card-box-shadow: var(--main-bg);\n --kbd-wrap-color: #6a6a6a;\n --kbd-text-color: #d3d3d3;\n --kbd-bg-color: #242424;\n --prompt-text-color: rgb(216, 212, 212, 0.75);\n --prompt-tip-bg: rgba(77, 187, 95, 0.2);\n --prompt-tip-icon-color: rgb(5, 223, 5, 0.68);\n --prompt-info-bg: rgb(7, 59, 104, 0.8);\n --prompt-info-icon-color: #0075d1;\n --prompt-warning-bg: rgb(90, 69, 3, 0.95);\n --prompt-warning-icon-color: rgb(255, 165, 0, 0.8);\n --prompt-danger-bg: rgb(86, 28, 8, 0.8);\n --prompt-danger-icon-color: #cd0202;\n\n /* tags */\n --tag-border: rgb(59, 79, 88);\n --tag-shadow: rgb(32, 33, 33);\n --search-tag-bg: var(--tag-bg);\n --dash-color: rgb(63, 65, 68);\n\n /* categories */\n --categories-border: rgb(64, 66, 69);\n --categories-hover-bg: rgb(73, 75, 76);\n --categories-icon-hover-color: white;\n\n /* archives */\n --timeline-node-bg: rgb(150, 152, 156);\n --timeline-color: rgb(63, 65, 68);\n --timeline-year-dot-color: var(--timeline-color);\n\n .post img[data-src] {\n &.lazyloaded {\n -webkit-filter: brightness(95%);\n filter: brightness(95%);\n }\n }\n\n .light {\n display: none;\n }\n\n hr {\n border-color: var(--main-border-color);\n }\n\n /* categories */\n .categories.card,\n .list-group-item {\n background-color: var(--card-bg);\n }\n\n .categories {\n .card-header {\n background-color: var(--card-header-bg);\n }\n\n .list-group-item {\n border-left: none;\n border-right: none;\n padding-left: 2rem;\n border-color: var(--categories-border);\n\n &:last-child {\n border-bottom-color: var(--card-bg);\n }\n }\n }\n\n #archives li:nth-child(odd) {\n background-image: linear-gradient(\n to left,\n rgb(26, 26, 30),\n rgb(39, 39, 45),\n rgb(39, 39, 45),\n rgb(39, 39, 45),\n rgb(26, 26, 30)\n );\n }\n\n color-scheme: dark;\n\n /* stylelint-disable-next-line selector-id-pattern */\n #disqus_thread {\n color-scheme: none;\n }\n} /* dark-scheme */\n","/*\n Style for Homepage\n*/\n\n.pagination {\n color: var(--btn-patinator-text-color);\n font-family: Lato, sans-serif;\n\n a:hover {\n text-decoration: none;\n }\n\n .page-item {\n .page-link {\n color: inherit;\n width: 2.5rem;\n height: 2.5rem;\n padding: 0;\n display: -webkit-box;\n -webkit-box-pack: center;\n -webkit-box-align: center;\n border-radius: 50%;\n border: 1px solid var(--btn-paginator-border-color);\n background-color: var(--button-bg);\n\n &:hover {\n background-color: var(--btn-paginator-hover-color);\n }\n }\n\n &.active {\n .page-link {\n background-color: var(--btn-paginator-hover-color);\n color: var(--btn-text-color);\n }\n }\n\n &.disabled {\n cursor: not-allowed;\n\n .page-link {\n color: rgba(108, 117, 125, 0.57);\n border-color: var(--btn-paginator-border-color);\n background-color: var(--button-bg);\n }\n }\n\n &:first-child .page-link,\n &:last-child .page-link {\n border-radius: 50%;\n }\n } /* .page-item */\n} /* .pagination */\n\n#post-list {\n margin-top: 1.75rem;\n padding-right: 0.5rem;\n\n a:hover {\n text-decoration: none;\n }\n\n .post-preview {\n padding: 0.25rem;\n border-radius: 0.75rem;\n border: 1px solid var(--card-border-color);\n background: var(--card-bg);\n\n &:hover {\n background: var(--card-hovor-bg);\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);\n }\n\n &:not(:last-child) {\n margin-bottom: 1.75rem;\n }\n\n h1 {\n font-size: 1.4rem;\n margin: 0;\n }\n\n .post-meta {\n i {\n font-size: 0.73rem;\n\n &:not(:first-child) {\n margin-left: 1.2rem;\n }\n }\n\n em {\n @extend %normal-font-style;\n }\n\n .pin {\n i {\n -webkit-transform: rotate(45deg);\n transform: rotate(45deg);\n padding-left: 3px;\n color: var(--pin-color);\n }\n\n span {\n display: none;\n }\n }\n }\n\n .post-content {\n margin-top: 0.6rem;\n margin-bottom: 0.6rem;\n color: var(--post-list-text-color);\n\n > p {\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n }\n } /* .post-preview */\n} /* #post-list */\n\n@media (hover: hover) {\n .post-preview {\n transition: all 0.35s ease-in-out;\n }\n}\n\n/* Hide SideBar and TOC */\n@media all and (max-width: 830px) {\n .post-preview {\n margin-left: -0.5rem;\n margin-right: -0.5rem;\n }\n\n .pagination {\n justify-content: space-evenly;\n\n .page-item {\n &:not(:first-child):not(:last-child) {\n display: none;\n }\n }\n }\n}\n\n/* Sidebar is visible */\n@media all and (min-width: 831px) {\n #post-list {\n margin-top: 3rem;\n\n .post-preview {\n padding: 0.5rem;\n\n .post-meta {\n .pin {\n background: var(--pin-bg);\n border-radius: 5px;\n line-height: 1.4rem;\n height: 1.3rem;\n margin-top: 3px;\n padding-left: 1px;\n padding-right: 6px;\n\n > span {\n display: inline;\n }\n }\n }\n }\n }\n\n .pagination {\n font-size: 0.85rem;\n\n .page-item {\n &:not(:last-child) {\n margin-right: 0.7rem;\n }\n\n .page-link {\n width: 2rem;\n height: 2rem;\n }\n }\n\n .page-index {\n display: none;\n }\n } /* .pagination */\n}\n\n/* Panel hidden */\n@media all and (max-width: 1200px) {\n #post-list {\n padding-right: 0;\n }\n}\n","/*\n Post-specific style\n*/\n\n@mixin btn-sharing-color($light-color, $important: false) {\n @if $important {\n color: var(--btn-share-color, $light-color) !important;\n } @else {\n color: var(--btn-share-color, $light-color);\n }\n}\n\n@mixin btn-post-nav {\n width: 50%;\n position: relative;\n border-color: var(--btn-border-color);\n}\n\n@mixin dot($pl: 0.25rem, $pr: 0.25rem) {\n content: '\\2022';\n padding-left: $pl;\n padding-right: $pr;\n}\n\n%text-color {\n color: var(--text-color);\n}\n\n.preview-img {\n @extend %rounded;\n\n &:not(.no-bg) {\n img.lazyloaded {\n background: var(--img-bg);\n }\n }\n\n img {\n aspect-ratio: 40 / 21;\n -o-object-fit: cover;\n object-fit: cover;\n\n @extend %rounded;\n }\n}\n\nh1 + .post-meta {\n span + span::before {\n @include dot;\n }\n\n em {\n @extend %text-color;\n\n a {\n @extend %text-color;\n }\n }\n}\n\n.post-tail-wrapper {\n margin-top: 6rem;\n border-bottom: 1px double var(--main-border-color);\n font-size: 0.85rem;\n\n .post-tail-bottom a {\n color: inherit;\n }\n\n .license-wrapper {\n line-height: 1.2rem;\n\n > a {\n color: var(--text-color);\n\n &:hover {\n @extend %link-hover;\n }\n }\n\n span:last-child {\n font-size: 0.85rem;\n }\n } /* .license-wrapper */\n\n .post-meta a:not(:hover) {\n @extend %link-underline;\n }\n\n .share-wrapper {\n vertical-align: middle;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n\n .share-icons {\n font-size: 1.2rem;\n\n > i {\n position: relative;\n bottom: 1px;\n\n @extend %cursor-pointer;\n\n &:hover {\n @extend %btn-share-hovor;\n }\n }\n\n a {\n &:not(:last-child) {\n margin-right: 0.25rem;\n }\n\n &:hover {\n text-decoration: none;\n\n > i {\n @extend %btn-share-hovor;\n }\n }\n }\n\n .fab {\n &.fa-twitter {\n @include btn-sharing-color(rgba(29, 161, 242, 1));\n }\n\n &.fa-facebook-square {\n @include btn-sharing-color(rgb(66, 95, 156));\n }\n\n &.fa-telegram {\n @include btn-sharing-color(rgb(39, 159, 217));\n }\n\n &.fa-linkedin {\n @include btn-sharing-color(rgb(0, 119, 181));\n }\n\n &.fa-weibo {\n @include btn-sharing-color(rgb(229, 20, 43));\n }\n }\n } /* .share-icons */\n\n .fas.fa-link {\n @include btn-sharing-color(rgb(171, 171, 171));\n }\n } /* .share-wrapper */\n}\n\n.post-tags {\n line-height: 2rem;\n}\n\n.post-navigation {\n padding-top: 3rem;\n padding-bottom: 4rem;\n\n .btn {\n @include btn-post-nav;\n\n color: var(--link-color);\n\n &:hover {\n background: #2a408e;\n color: #ffffff;\n border-color: #2a408e;\n }\n\n &.disabled {\n @include btn-post-nav;\n\n pointer-events: auto;\n cursor: not-allowed;\n background: none;\n color: gray;\n\n &:hover {\n border-color: none;\n }\n }\n\n &.btn-outline-primary.disabled:focus {\n box-shadow: none;\n }\n\n &::before {\n color: var(--text-muted-color);\n font-size: 0.65rem;\n text-transform: uppercase;\n content: attr(prompt);\n }\n\n &:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n left: 0.5px;\n }\n\n &:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n right: 0.5px;\n }\n }\n\n p {\n font-size: 1.1rem;\n line-height: 1.5rem;\n margin-top: 0.3rem;\n white-space: normal;\n }\n} /* .post-navigation */\n\n@-webkit-keyframes fade-up {\n from {\n opacity: 0;\n position: relative;\n top: 2rem;\n }\n\n to {\n opacity: 1;\n position: relative;\n top: 0;\n }\n}\n\n@keyframes fade-up {\n from {\n opacity: 0;\n position: relative;\n top: 2rem;\n }\n\n to {\n opacity: 1;\n position: relative;\n top: 0;\n }\n}\n\n#toc-wrapper {\n border-left: 1px solid rgba(158, 158, 158, 0.17);\n position: -webkit-sticky;\n position: sticky;\n top: 4rem;\n transition: top 0.2s ease-in-out;\n -webkit-animation: fade-up 0.8s;\n animation: fade-up 0.8s;\n\n ul {\n list-style: none;\n font-size: 0.85rem;\n line-height: 1.25;\n padding-left: 0;\n\n li {\n &:not(:last-child) {\n margin: 0.4rem 0;\n }\n\n a {\n padding: 0.2rem 0 0.2rem 1.25rem;\n }\n }\n\n /* Overwrite TOC plugin style */\n\n .toc-link {\n display: block;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n\n &:hover {\n color: var(--toc-highlight);\n text-decoration: none;\n }\n\n &::before {\n display: none;\n }\n }\n\n .is-active-link {\n color: var(--toc-highlight) !important;\n font-weight: 600;\n\n &::before {\n display: inline-block;\n width: 1px;\n left: -1px;\n height: 1.25rem;\n background-color: var(--toc-highlight) !important;\n }\n }\n\n ul {\n a {\n padding-left: 2rem;\n }\n }\n }\n}\n\n/* --- Related Posts --- */\n\n#related-posts {\n > h3 {\n @include label(1.1rem, 600);\n }\n\n em {\n @extend %normal-font-style;\n\n color: var(--relate-post-date);\n }\n\n p {\n font-size: 0.9rem;\n margin-bottom: 0.5rem;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n\n a:hover {\n text-decoration: none;\n }\n\n .card {\n border-color: var(--card-border-color);\n background-color: var(--card-bg);\n box-shadow: 0 0 5px 0 var(--card-box-shadow);\n transition: all 0.3s ease-in-out;\n\n h3 {\n @extend %text-color;\n }\n\n &:hover {\n -webkit-transform: translate3d(0, -3px, 0);\n transform: translate3d(0, -3px, 0);\n box-shadow: 0 10px 15px -4px rgba(0, 0, 0, 0.15);\n }\n }\n}\n\n#tail-wrapper {\n min-height: 2rem;\n\n > div:last-of-type {\n margin-bottom: 2rem;\n }\n\n /* stylelint-disable-next-line selector-id-pattern */\n #disqus_thread {\n min-height: 8.5rem;\n }\n}\n\n%btn-share-hovor {\n color: var(--btn-share-hover-color) !important;\n}\n\n.share-label {\n @include label(inherit, 400, inherit);\n\n &::after {\n content: ':';\n }\n}\n\n@media all and (max-width: 576px) {\n .preview-img[data-src] {\n margin-top: 2.2rem;\n }\n\n .post-tail-bottom {\n flex-wrap: wrap-reverse !important;\n\n > div:first-child {\n width: 100%;\n margin-top: 1rem;\n }\n }\n}\n\n@media all and (max-width: 768px) {\n .post-content > p > img {\n max-width: calc(100% + 1rem);\n }\n}\n\n/* Hide SideBar and TOC */\n@media all and (max-width: 849px) {\n .post-navigation {\n padding-left: 0;\n padding-right: 0;\n margin-left: -0.5rem;\n margin-right: -0.5rem;\n }\n\n .preview-img[data-src] {\n max-width: 100vw;\n border-radius: 0;\n }\n}\n","/*\n Styles for Tab Tags\n*/\n\n.tag {\n border-radius: 0.7em;\n padding: 6px 8px 7px;\n margin-right: 0.8rem;\n line-height: 3rem;\n letter-spacing: 0;\n border: 1px solid var(--tag-border) !important;\n box-shadow: 0 0 3px 0 var(--tag-shadow);\n\n span {\n margin-left: 0.6em;\n font-size: 0.7em;\n font-family: Oswald, sans-serif;\n }\n}\n","/*\n Style for Archives\n*/\n\n#archives {\n letter-spacing: 0.03rem;\n\n $timeline-width: 4px;\n\n %timeline {\n content: '';\n width: $timeline-width;\n position: relative;\n float: left;\n background-color: var(--timeline-color);\n }\n\n .year {\n height: 3.5rem;\n font-size: 1.5rem;\n position: relative;\n left: 2px;\n margin-left: -$timeline-width;\n\n &::before {\n @extend %timeline;\n\n height: 72px;\n left: 79px;\n bottom: 16px;\n }\n\n &:first-child::before {\n @extend %timeline;\n\n height: 32px;\n top: 24px;\n }\n\n /* Year dot */\n &::after {\n content: '';\n display: inline-block;\n position: relative;\n border-radius: 50%;\n width: 12px;\n height: 12px;\n left: 21.5px;\n border: 3px solid;\n background-color: var(--timeline-year-dot-color);\n border-color: var(--timeline-node-bg);\n box-shadow: 0 0 2px 0 #c2c6cc;\n z-index: 1;\n }\n }\n\n ul {\n li {\n font-size: 1.1rem;\n line-height: 3rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n\n &:nth-child(odd) {\n background-color: var(--main-bg, #ffffff);\n background-image: linear-gradient(\n to left,\n #ffffff,\n #fbfbfb,\n #fbfbfb,\n #fbfbfb,\n #ffffff\n );\n }\n\n &::before {\n @extend %timeline;\n\n top: 0;\n left: 77px;\n height: 3.1rem;\n }\n }\n\n &:last-child li:last-child::before {\n height: 1.5rem;\n }\n } /* #archives ul */\n\n .date {\n white-space: nowrap;\n display: inline-block;\n position: relative;\n right: 0.5rem;\n\n &.month {\n width: 1.4rem;\n text-align: center;\n }\n\n &.day {\n font-size: 85%;\n font-family: Lato, sans-serif;\n }\n }\n\n a {\n /* post title in Archvies */\n margin-left: 2.5rem;\n position: relative;\n top: 0.1rem;\n\n &:hover {\n border-bottom: none;\n }\n\n &::before {\n /* the dot before post title */\n content: '';\n display: inline-block;\n position: relative;\n border-radius: 50%;\n width: 8px;\n height: 8px;\n float: left;\n top: 1.35rem;\n left: 71px;\n background-color: var(--timeline-node-bg);\n box-shadow: 0 0 3px 0 #c2c6cc;\n z-index: 1;\n }\n }\n} /* #archives */\n\n@media all and (max-width: 576px) {\n #archives {\n margin-top: -1rem;\n\n ul {\n letter-spacing: 0;\n }\n }\n}\n","/*\n Style for Tab Categories\n*/\n\n%category-icon-color {\n color: gray;\n}\n\n.categories {\n margin-bottom: 2rem;\n\n .card-header {\n padding-right: 12px;\n }\n\n i {\n @extend %category-icon-color;\n\n font-size: 86%; /* fontawesome icons */\n }\n\n .list-group-item {\n border-left: none;\n border-right: none;\n padding-left: 2rem;\n\n &:first-child {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n} /* .categories */\n\n.category-trigger {\n width: 1.7rem;\n height: 1.7rem;\n border-radius: 50%;\n text-align: center;\n color: #6c757d !important;\n\n i {\n position: relative;\n height: 0.7rem;\n width: 1rem;\n transition: -webkit-transform 300ms ease;\n transition: transform 300ms ease;\n transition: transform 300ms ease, -webkit-transform 300ms ease;\n }\n\n &:hover {\n i {\n color: var(--categories-icon-hover-color);\n }\n }\n}\n\n/* only works on desktop */\n@media (hover: hover) {\n .category-trigger:hover {\n background-color: var(--categories-hover-bg);\n }\n}\n\n.rotate {\n -webkit-transform: rotate(-90deg);\n transform: rotate(-90deg);\n}\n","/*\n Style for page Category and Tag\n*/\n\n.dash {\n margin: 0 0.5rem 0.6rem 0.5rem;\n border-bottom: 2px dotted var(--dash-color);\n}\n\n#page-category,\n#page-tag {\n ul > li {\n line-height: 1.5rem;\n padding: 0.6rem 0;\n\n /* dot */\n &::before {\n background: #999999;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n display: block;\n content: '';\n position: relative;\n top: 0.6rem;\n margin-right: 0.5rem;\n }\n\n /* post's title */\n > a {\n @extend %no-bottom-border;\n\n font-size: 1.1rem;\n }\n\n /* post's date */\n > span:last-child {\n white-space: nowrap;\n }\n }\n}\n\n/* tag icon */\n#page-tag h1 > i {\n font-size: 1.2rem;\n}\n\n#page-category h1 > i {\n font-size: 1.25rem;\n}\n\n#page-category,\n#page-tag,\n#access-lastmod {\n a:hover {\n @extend %link-hover;\n\n margin-bottom: -1px; /* Avoid jumping */\n }\n}\n\n@media all and (max-width: 576px) {\n #page-category,\n #page-tag {\n ul > li {\n &::before {\n margin: 0 0.5rem;\n }\n\n > a {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n }\n }\n}\n"],"file":"style.css"} \ No newline at end of file diff --git a/assets/draw.io/2022-09-27-15-29-00.drawio b/assets/draw.io/2022-09-27-15-29-00.drawio new file mode 100644 index 000000000..50d3962c9 --- /dev/null +++ b/assets/draw.io/2022-09-27-15-29-00.drawio @@ -0,0 +1 @@ +7VlLc9sqFP41WtbDQw9YJnaau+lMZ7pos1QsbGsqCxfjxO6vvyAhCZAd2Ul0ffvwIoFPcDhwznc+ZAd4ut7fi3Sz+sQzVgQIZPsAzwKEcEzVXw0cagDBsAaWIs9qCHbAl/wnMyAw6C7P2NYZKDkvZL5xwTkvSzaXDpYKwZ/dYQteuKtu0iXrAV/madFHv+aZXNUoiUCH/8Py5apZGQLzZJ02gw2wXaUZf7YgfBfgqeBc1q31fsoKfXbNudTzPp542jomWCnPmfBVPNx9Y8l9MgXJJ/b4RPD96oOx8pQWO7NhHNx9DG6nAZnStoXaVty2SNuK2lZidioPzfFJtlfO3a7kulAAVM20yJelas+V20wo4IkJmasDvzEP1nmW6em3gm3zn+ljZQqo/obnpazCGd0G0Uzb2km+rVNGm95Kwb+zKS+4sjsreamtLPKi8CCzZ7Uu2588TNiGSKU242smxUENaSZQE1WT1onpPnc5AkODraz8wAZLTVouW8td5FTDBO+CQOJeIHuxYGV2oxmhD79It9t87kZG8F2ZscycNtvn8ptuT8KEmv6DHjmBpjfbW0NnB6vzmYlcbUuHt8JqV1jWo5p33MpdvhNzNpSw/bBY5x4dOfYGE6xIZf7kunEsFmaFzzrluqhHxI16iCLXRO2+mWVz0TOEosQxBH1DMhVLJnuGqtRot/36bAmPZEtcaKYueOVnlzbxjx1vHnyoyXajBkC02VeRbZ6r1lL/R0cqBDxaK5pWeKS6eO7MDYHt5fBigdWn7wG01jVm1CHVluoRJ+E/tHrBMJpQ2q9g5L+sYPBYUr6phOnYmhsFRLo0FY/V1CZmGiqV63WZQzRpgIcKoKQFulJX9Q52zy92XeEECXEKJwDxNUunOd+6tAyNu1aJRSB0UzMBbWpeXmXdNEegb+tEoVVZlh6sYYa957tN26VOeheCgSmqUfvxrrUfRtflGSSxyzOA6Nt4hmho8UwZVKrwl2cDPMPEzb5IFzzafcgrOac4hjzLuvJZHzwOA0PsLouHGYjBwJSRGBhfg4EtXSCFriypinApXSw+U+jwWQf7VXz+4xiIvOxL4hd5ci4Dsa8rSTSa6iFf9aIhzuHIVz1/ykicI9dVvV/7dnk1jsRggoGlSj3GRPErdYrSSeIphiaKLYHnvZNfTBq1J+S9/lM4MXp82t94eNZI1KF/L4y/IHVUgodeeQ77SnA2Xwhw1SnumSbJOHxRG4m8KyUGA87GA1PGYUqj6Fe62L2HIli8IxB6Fzs07r3uf35f0wzAljx4DHiDGPWubMT7ku29qER6X4i87Bd+cfxIJELXlZuke30x6gDJ2+QmgdiVGxSOJje/y9sRRnRCUSc38N2UDEMwCUnP3Ejq5VNoSLowfHH8pZRT3e4X5np49zM9vvsX \ No newline at end of file diff --git a/assets/draw.io/2022-09-30-15-06-17.drawio b/assets/draw.io/2022-09-30-15-06-17.drawio new file mode 100644 index 000000000..550ccfe7b --- /dev/null +++ b/assets/draw.io/2022-09-30-15-06-17.drawio @@ -0,0 +1 @@ +7Zxdb6M4FIZ/TS53xUcI5LJJ21lpd6XZ6Uhz7QEnWONgZJwm6a9fAwZCTmhDJpQWU1VVfEIcOO9j4/dAmdjLzf4LR3H4LwswnVhGsJ/Y9xPLcqau/JsGDnnANlVgzUmQh8wq8EResAoaKrolAU5qGwrGqCBxPeizKMK+qMUQ52xX32zFaP1bY7TGIPDkIwqjP0ggwjzqOUYV/wuTdVh8s2modzao2FgFkhAFbHcUsh8m9pIzJvJXm/0S0zR3RV7yzz02vFvuGMeRuOQDPoqdFVrv//vxK1h8/5uhO4H+mE3zbp4R3aojVnsrDkUK5I7H6UuBfqahRSIQF0opQ7Zl6gUiEeaybWZtSlGckGzrfIuQ0OAfdGBbUfRTtGRvnP3CS0ZZ+vmIRelXwGNTh/uMucD7o5A61i+YbbDgB7mJetcu8q7AszzV3lUy2oVY4ZGE1lQFkUJnXfZdZVe+UAlukeySjabkfkv5WISMk5c0p1Sl7zThyY5sKIokeCg4CS1YNtDSkGCxekXxSqiXP5kQbKMaXB20cVa0gLP4O+JrXGyyIpQWOk0s+2F2b9zLg1/EjEQiy5WzkL/yGJfGn87EkRstZdus2vI33ZyLJYuk7hKatG+MErHDSSMMnG2jABdHGqAkzBqmanxFQmAeZRHLMI+S8Xi0w41gvT4s3sbtLZw6pKkYE9XYNRv5kocsCKLf5ByJonU2jkOxoSqPu5AI/BQjP910JyfyfFinMyqqiGAyHSuazWEhCQIcnUChctwOvIwezB+ecQ6RCTAI8AptqbhcvXKctZNv1lI91VmV0ta9IZqSi4QcthLxBCBR7ufvUGIBSppnoU9FSQc07OvaDR8OG8Bhj3C8DodpaUMHXBw6Ix1v0HFmmTlQOpzxxNKWjlKoodPhwlVHIxufzPoozG7oei7krYUpOdSF782juHB94fU6Rxz71lv7lHYWsxwho0dx4TpjOowzSZORLWuBRifAaGZjXLgScYfBT2doaGNi3BlgYyBFsu7Y0MXCuC5go3mVOrKhl4GZAzYa0RgNTFcG5tJrdp0ZmGIHPkyB9LYG5tJ5Yn6xcEOfFzx40U2vmmgLFjSzIR6seel1MaU9GtrYEE/3C21XsKGLDfEGW/7qjA1tbIgHyxeNaIw2pCsbcu409c4+BJYqZr3OEb34kHI0jD7E8wAQ/V59l0A8Ptry54MCoZsZgfUrvXxqezS0MSNzWNQa547RkRwBAotcernV9mxo40jmsJLRiMboSDpzJGfWMe/rSOawaqHhlZFyNIyOZA7vwmmeGYZ40mjBgmZmZA6rWP1WLz4+GvqYEVjZ6vcW4U/AhjY+ZNhFrvrdwcd9d4aONjbFNJpvAR5tSVe2xDL6tiWmAeeMfpehvfiSEv/RmMhcwFq4Xs6kDQ2aWROzeNCQtv8gcAUc2pgT04SVcL3+tegaOnSxJ6ZpATr0uk5yBR36OBDzgmer4Si4S59SJ1s+RUlC/DoW9WdfQVXU9ISD2lPsXl3jOWfSU8Q4pkiQ5/p58BUFvqboVNpOi5Pp4UTroouEbbmP1aeqlL/dkXvSkcjMEujodtrBYvbAtXPsG2kHOnp37S64Z3Jg2s1vpd1pR++uHawGD1y7MsXFMzusK7Ur6yxNHXWuHay86Kbd7FbanXbUuXawRAIr7XKFJ+pyIUrW6ZM3fZyuIGUgXQcSH9E79cZGrlKzpS7HCXk5WsnWq5lpwRJtBUvywmvjs1XOrHUbGWmzJHXq2S8ebVWrYkKKbKMZmN8rYlrQdmosx7R3OaDP00cOe16XwzJ7lwPegqaPHI754eSAPlcjOdx3k0M2q6fF52f+6pH79sP/ \ No newline at end of file diff --git a/assets/draw.io/2022-10-08-13-03-50.drawio b/assets/draw.io/2022-10-08-13-03-50.drawio new file mode 100644 index 000000000..28e4b1792 --- /dev/null +++ b/assets/draw.io/2022-10-08-13-03-50.drawio @@ -0,0 +1 @@ +7VpbT9swFP41fQQlTtPLIy2wizQJ0U3bHk3iJt4cO3JcmvLrZyd2c3ELLdABTasK2SeOY5/vOyffMe150yT/xGEaf2MhIj3ghHnPu+wBMByM5V9lWJWGft8vDRHHYWlyK8MMPyBtdLR1gUOUNQYKxojAadMYMEpRIBo2yDlbNofNGWk+NYURsgyzABLb+hOHIi6tI9+p7J8RjmLzZNfRVxJoBmtDFsOQLWsm76rnTTljomwl+RQR5Tvjl/K+6y1X1wvjiIpdbsh/RLP4akCGX87IIv+ag9nZrzNXo3EPyULvWK9WrIwL5MJT1RTwjqBbtYVJzDh+YFRAIu2ONGQCcqHRK/pLnBBIpW9g2DJNWMEFZRIs1S2C5kI375gQLNEdrl2r2gEjBKYZviPmKSFn6XfII2SGzDEhU0YYl33KqBw3SRmmoiCBP5Ff6aOpc+73fLmzqey7VV9+1XAupoxmgkNc+BXBTCxRJpsT7SrEBcq3guCuoZUhgViCBF/JIfmaH+UtOhy8vu4vK3IBY4trxDI2qPkcraeuIJcNjfo+DBhYDBhtpYDcqMCQ3MpIgzRSVyexSBQJXNlcxligWQoDNXQp00EBWhGXsAJtA0b78YBJBOakCKUYhyGiu0LzSATYgNUAGeyJh56s8tLes0EiEKdQyGBZ0DCzQF6v8wW4Dy3c3e2x3yHg8yZMR8+DkcUD0D/xoMrYoCtEGFtE8E4JoUaEUUeIYAh/0oT/RxN64/emCc0e6trA7VIqqBVonRKFJtLrwHsn4DsnCoFni0L/xIPuiUJTCnRWFD5FhM6Iwh0OChENL9SRq+wFBGYZDpoE4Gp1yGg/6Xe++qU656Ohbwy/61cv80ZvVe/dII7lphDXxnI1KLSOc1syDDjCqMNte93hSMjfgIqxcUSgwPfNdTwC/I1SozU5CFpyELTQztiCB0jfVT/wbU80bE00aE1UOsKa6NUoY58sYoszMiBFkyWQ4IgqCkncFLoTFbY4gORCX0hkVBe5haMMP9RSR1PWK+UOF4JlZQXiFhUJZ39RK7tsSDg7pYsnRL3jN5wPxr6VKLwNJPIOpunt876jDeAd5PsBA3jgvFIAWxO1yXHgAPbsMvBPZwLYH72zADa6q4YGXSSZXZcfKyKgFQ67VmOHQ8SukhQidl49VkT6o/eGiF2uvPAl1/RmD3jX15787PG+ei+vITDun/cHz3sT+eDpuQ79MrLV5MfUL2/FB7/ffKE9W5ZYE7XL0UMzwVayHZIlXjsS31qW2P82fCwutSd2z7dO8VH+ZFR8xDxsleGjZ8ZdeyLwavW87Fa/QyuHVz/m867+AQ== \ No newline at end of file diff --git a/assets/img/avator.jpg b/assets/img/avator.jpg new file mode 100644 index 000000000..fb279b2b9 Binary files /dev/null and b/assets/img/avator.jpg differ diff --git a/assets/img/favicons/android-chrome-192x192.png b/assets/img/favicons/android-chrome-192x192.png new file mode 100644 index 000000000..dbd2ef825 Binary files /dev/null and b/assets/img/favicons/android-chrome-192x192.png differ diff --git a/assets/img/favicons/android-chrome-384x384.png b/assets/img/favicons/android-chrome-384x384.png new file mode 100644 index 000000000..5a8252df9 Binary files /dev/null and b/assets/img/favicons/android-chrome-384x384.png differ diff --git a/assets/img/favicons/android-chrome-512x512.png b/assets/img/favicons/android-chrome-512x512.png new file mode 100644 index 000000000..aac4514aa Binary files /dev/null and b/assets/img/favicons/android-chrome-512x512.png differ diff --git a/assets/img/favicons/apple-touch-icon.png b/assets/img/favicons/apple-touch-icon.png new file mode 100644 index 000000000..028378bf9 Binary files /dev/null and b/assets/img/favicons/apple-touch-icon.png differ diff --git a/assets/img/favicons/browserconfig.xml b/assets/img/favicons/browserconfig.xml new file mode 100644 index 000000000..54217f7c0 --- /dev/null +++ b/assets/img/favicons/browserconfig.xml @@ -0,0 +1 @@ + #da532c diff --git a/assets/img/favicons/favicon-16x16.png b/assets/img/favicons/favicon-16x16.png new file mode 100644 index 000000000..3326ca4d8 Binary files /dev/null and b/assets/img/favicons/favicon-16x16.png differ diff --git a/assets/img/favicons/favicon-32x32.png b/assets/img/favicons/favicon-32x32.png new file mode 100644 index 000000000..f44360d0f Binary files /dev/null and b/assets/img/favicons/favicon-32x32.png differ diff --git a/assets/img/favicons/favicon.ico b/assets/img/favicons/favicon.ico new file mode 100644 index 000000000..bff131660 Binary files /dev/null and b/assets/img/favicons/favicon.ico differ diff --git a/assets/img/favicons/mstile-150x150.png b/assets/img/favicons/mstile-150x150.png new file mode 100644 index 000000000..3eca21b27 Binary files /dev/null and b/assets/img/favicons/mstile-150x150.png differ diff --git a/assets/img/favicons/site.webmanifest b/assets/img/favicons/site.webmanifest new file mode 100644 index 000000000..897d82ffd --- /dev/null +++ b/assets/img/favicons/site.webmanifest @@ -0,0 +1 @@ +{ "name": "Blogs", "short_name": "Blogs", "description": "A minimal, responsive, and powerful Jekyll theme for presenting professional writing.", "icons": [ { "src": "/assets/img/favicons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/assets/img/favicons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }], "start_url": "/index.html", "theme_color": "#2a1e6b", "background_color": "#ffffff", "display": "fullscreen" } diff --git a/assets/index.html b/assets/index.html new file mode 100644 index 000000000..380189923 --- /dev/null +++ b/assets/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/assets/js/data/search.json b/assets/js/data/search.json new file mode 100644 index 000000000..37d1cc57a --- /dev/null +++ b/assets/js/data/search.json @@ -0,0 +1 @@ +[ { "title": "Zookeeper 学习", "url": "/posts/zookeeper/", "categories": "Backend, Zookeeper", "tags": "zookeeper", "date": "2024-08-11 09:56:08 +0800", "snippet": "Zookeeper 是什么Zookeeper 是一个分布式的协调服务,可以实现 统一配置管理。比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置。将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。 统一命名服务。节点存储ip地址,只需要访问Znode节点就可以获取这些ip地址。 统一集群管理。Kafka 的集群管理基于Zookeeper。 ...", "content": "Zookeeper 是什么Zookeeper 是一个分布式的协调服务,可以实现 统一配置管理。比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置。将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。 统一命名服务。节点存储ip地址,只需要访问Znode节点就可以获取这些ip地址。 统一集群管理。Kafka 的集群管理基于Zookeeper。 统一服务管理。Dubbo 的服务发现基于Zookeeper。 分布式锁。通过在持久节点下建立临时顺序节点,可以保证锁的有序,监听机制保障锁传递的高效。原理文件系统数据结构文件系统类似的,整体上可以看成一棵树,每个节点称作一个 ZNode,每个 ZNode 都可以通过其路径得到唯一标识。默认只能最多存储 1MB 的数据但与文件系统不同,每一个 ZNode 节点都可以存储数据,但文件系统的目录不可以存储数据。ZNode 类型三大类: 持久节点,除非客户端主动执行删除操作,否则 ZooKeeper 不会删除持久的 znode 临时节点,客户端断开连接后,ZooKeeper会自动删除临时节点 顺序节点,每次创建顺序节点时,ZooKeeper都会在路径后面自动添加上10位的数字,从1开始,最大是2147483647 (2^32-1)四种形式: 持久节点 持久顺序节点 临时节点 临时顺序节点唯一事务id每次的变化都会产生一个集群全局的唯一的事务id, Zxid(ZooKeeper Transaction Id),由Zookeeper的 Leader 实例维护。变化包括 任何的客户端连接到Server 任何的客户端断开与Server连接 任何的Znode节点被创建create、修改set、删除delete通知机制(监听机制)具体是基于观察者模式实现的。在ZooKeeper中,客户端是观察者,服务端是被观察者。 客户端通过注册Watch来监听服务端节点的数据变化。ZooKeeper 的观察机制允许用户在指定节点上针对感兴趣的事件注册监听,当事件发生时,监听器会被触发,并将事件信息推送到客户端。监听两方面的内容: 监听 Znode 的数据变化 监听子节点的数量变化角色一主多从的结构 Leader,同一时间只会有一个Leader,负责发起和提交写请求。接收到写请求后同时发送给Follower,统计Follower写入成功的数量,超过一半则认为成功。 Follower,处理读请求,Leader宕机后使用 Paxos 一致性算法的 Zab 协议负责选举新的Leader。 Observer,处理读请求,但没有选举权会话(Session)client与ZooKeeper集群中的某一台server保持连接,发送读/写请求,读请求直接由当前连接的server处理,写请求由于是事务请求,由当前server转发给leader进行处理。同时,client还能接收来自server端的watcher通知。所有的这些交互,都是基于client和ZooKeeper的server之间的TCP长连接,也称之为Session会话。有了会话之后,后续的请求发送,回应,心跳检测等机制都是基于会话来实现的。ZAB 算法与 Raft 算法的选举过程ZAB 算法 所有节点第一票先选举自己当leader,将投票信息广播出去; 按照规则(epoch 是否比自己的 epoch 大,以及 counter 是否比自己的 counter 大,总之选择zxid最大的投票信息,说明该节点的数据最全)判断是否需要更改投票信息,将更改后的投票信息再次广播出去; 判断是否有超过一半的投票选举同一个节点,如果是选举结束根据投票结果设置自己的服务状态,选举结束,否则继续进入投票流程。Raft每个节点都有一个定时器,最先结束倒计时的节点最先发送选举的信号(基本也会是被选举为 leader)" }, { "title": "Go 语言学习", "url": "/posts/go/", "categories": "Backend, Go", "tags": "go", "date": "2024-02-27 14:25:08 +0800", "snippet": "基本语法变量初始化var s string = \"string\"var s = \"string\"s := \"string\"二维切片初始化slice1 := make([][]bool, m)for i := range slice1 { slice1[i] = make([]bool, n)}变量自增只有后缀自增或自减,并且必须单独一行(除了在range语句中)。条件判断语句中的初始变...", "content": "基本语法变量初始化var s string = \"string\"var s = \"string\"s := \"string\"二维切片初始化slice1 := make([][]bool, m)for i := range slice1 { slice1[i] = make([]bool, n)}变量自增只有后缀自增或自减,并且必须单独一行(除了在range语句中)。条件判断语句中的初始变量可以在布尔表达式里,并且不能使用0/1作为判断条件。if cond := true; cond { fmt.Println()}随机数生成 [0, 99] 的随机数rand.Seed(time.Now().UnixNano())r := rand.Intn(100)fmt.Println(r)栈/队列Go 语言中的栈和队列都是基于切片实现的。stack := make([]int, 0) //创建切片stack = append(stack, 10) //push压入栈value := stack[len(stack)-1] // 取栈顶的值stack = stack[:len(stack)-1] //pop弹出len(stack) == 0 //检查栈空queue := make([]int, 0) //创建切片queue = append(queue, 10) //enqueue入队v := queue[0] // 取队首的值queue = queue[1:] //dequeue出队len(queue) == 0 //检查队列为空合并切片slice1 := []string{\"Moe\", \"Larry\", \"Curly\"}slice2 := []string{\"php\", \"golang\", \"java\"}slice3 = append(slice1, slice2...)fmt.Println(slice3)//[Moe Larry Curly php golang java]整数和字符串转换// string to int, \"99\" -> 99int, err := strconv.Atoi(str)// asscii to string, 97 -> \"a\"str := string(asscii) // asscii to string// int to string, 10 -> \"10\"str := fmt.Sprintf(\"%d\", int)str := strconv.Itoa(int)str := strconv.FormatInt(int, 10)Range 用法// 数组var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}for i, v := range pow { fmt.Printf(\"2**%d = %d\\n\", i, v)}// Mapfor key, value := range oldMap { newMap[key] = value}for key := range oldMap { fmt.Println(oldMap[key])}for _, value := range oldMap { fmt.Println(value)}// 字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。for i, c := range \"go\" { fmt.Println(i, c, string(c)) //0 103 g \\n 1 111 o}最值fmt.Printf(\"int min - max: %d - %d\\n\", math.MinInt, math.MaxInt) // intfmt.Printf(\"int8 min - max: %d - %d\\n\", math.MinInt8, math.MaxInt8) // int8fmt.Printf(\"int16 min - max: %d - %d\\n\", math.MinInt16, math.MaxInt16) // int16fmt.Printf(\"int32 min - max: %d - %d\\n\", math.MinInt32, math.MaxInt32) // int32fmt.Printf(\"int64 min - max: %d - %d\\n\", math.MinInt64, math.MaxInt64) // int64// unsignedfmt.Printf(\"uint min - max: %d - %d\\n\", uint(0), uint(math.MaxUint)) // uintfmt.Printf(\"uint8 min - max: %d - %d\\n\", 0, math.MaxUint8) // uint8fmt.Printf(\"uint16 min - max: %d - %d\\n\", 0, math.MaxUint16) // uint16fmt.Printf(\"uint32 min - max: %d - %d\\n\", 0, math.MaxUint32) // uint32fmt.Printf(\"uint64 min - max: %d - %d\\n\", 0, uint64(math.MaxUint64)) // uint64排序// intarray := []int{3, 1, 34, 25, 14, 5}sort.Ints(array) // 升序fmt.Println(array)sort.Sort(sort.Reverse(sort.IntSlice(array))) // 降序fmt.Println(array)// stringarray1 := []string{\"ss\", \"sa\", \"avs\", \"vs\"}sort.Strings(array1)fmt.Println(array1)sort.Sort(sort.Reverse(sort.StringSlice(array1)))fmt.Println(array1)自定义排序array := []int{3, 17, 1, 34, 25, 14, 5}sort.Slice(array, func(i, j int) bool { if abs(array[i]-9) != abs(array[j]-9) { return abs(array[i]-9) < abs(array[j]-9) } return array[i] < array[j]})fmt.Printf(\"%+v\", array)双向列表doubleLinkedList := list.New()doubleLinkedList.PushBack(3)var doubleLinkedListElement *list.ElementdoubleLinkedListElement = doubleLinkedList.Back()Map 用法m := make(map[string]int) // 初始化m := map[string]int{ // 使用组合字面量初始化 \"apple\": 1, \"banana\": 2, \"orange\": 3,}v1 := m[\"apple\"] // 获取键值对v2, ok := m[\"pear\"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值for k, v := range m { // 遍历Map,无序的 fmt.Printf(\"key=%s, value=%d\\n\", k, v)}delete(m, \"banana\") // 删除元素m := make(map[string]int) // 清空元素的方法就是重新make一个map// 基于 Map 实现 Setset := make(map[string]bool)set[\"Foo\"] = truefor k := range set { fmt.Println(k)}delete(set, \"Foo\")size := len(set)exists := set[\"Foo\"]堆的用法package mainimport (\t\"container/heap\"\t\"fmt\"\t\"sort\")type heap2 struct {\tsort.IntSlice}func (h heap2) Less(i, j int) bool { // 最小堆:h.IntSlice[i] < h.IntSlice[j];最大堆:h.IntSlice[i] > h.IntSlice[j];\treturn h.IntSlice[i] > h.IntSlice[j]}func (h *heap2) Push(v interface{}) {\th.IntSlice = append(h.IntSlice, v.(int))}func (h *heap2) Pop() interface{} {\ttemp := h.IntSlice\tv := temp[len(temp)-1]\th.IntSlice = temp[:len(temp)-1]\treturn v}func (h *heap2) push(v int) {\theap.Push(h, v)}func (h *heap2) pop() int {\treturn heap.Pop(h).(int)}func main() {\tq := &heap2{[]int{3, 4, 1, 2, 4, 3}}\theap.Init(q)\tq.pop()\tfmt.Println(len(q.IntSlice))\tfmt.Println(q)}知识点数组和切片 数组在内存中是一段连续的内存空间,元素的类型和长度都是固定的; 切片在内存中由一个指向底层数组的指针、长度和容量组成的,长度表示当前包含的元素个数,容量表示切片可以拓展的最大元素个数。切片 s[x: y] 表示 s 中第 x 位到第 y - 1 位元素截取。协程/线程/进程的区别 进程:是具有一定独立功能的程序,是系统资源分配和调度的最小单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。 线程:是进程的一个实体,是内核态,而且是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。 协程:是一种用户态的轻量级线程,调度完全是由用户来控制的拥有自己的寄存器上下文和栈。调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。一个线程可以有多个协程,线程、进程都是同步机制,但协程是异步 Go 和 Java 的区别 语言特性: Java 面向对象,静态语言,有完整的继承体系,不支持多继承,值传递 Go 虽然是静态类型语言,但具有动态类型语言的鸭子类型(基于 interface 实现),支持多继承,值传递 并发模型: Java 的并发基于线程和锁 Go 的并发基于协程和通道,可以实现轻量级的并发 性能 Java 需要通过虚拟机,可跨平台,从字节码编译成机器码 Go 的效率高,因为没有虚拟机,直接编译成机器码 Byte 和 Rune 类型 byte是uint8的别称,一个值就是一个ASICII码的值,占 1 个字节的英文类字符,使用 byte rune是int32的别称,一个值就是一个Unicode字符,占 1 ~ 4 个字节的其他字符,可以使用rune(或者int32),如中文、特殊符号等。" }, { "title": "Note from Work", "url": "/posts/note-from-work/", "categories": "", "tags": "backend", "date": "2023-09-07 10:41:22 +0800", "snippet": " Grafana 的数据显示会五分钟自动补全。当向 Prometheus 中插入某个时间戳的值时,其值会延续五分钟。 K8S 中的 Sidecar 模式:通常情况下一个 Pod 只包含一个容器,但是 Sidecar 模式是指为主容器提供额外功能(例如监控) 从而将其他容器加入到同一个 Pod 中。再例如 Istio 实现 Sidecar 自动注入。 Fe...", "content": " Grafana 的数据显示会五分钟自动补全。当向 Prometheus 中插入某个时间戳的值时,其值会延续五分钟。 K8S 中的 Sidecar 模式:通常情况下一个 Pod 只包含一个容器,但是 Sidecar 模式是指为主容器提供额外功能(例如监控) 从而将其他容器加入到同一个 Pod 中。再例如 Istio 实现 Sidecar 自动注入。 Federated cluster,联邦集群 限流算法:漏桶和令牌桶算法,漏桶算法处理请求的速度固定,突发请求过多时会丢弃;令牌桶算法除了限制数据的平均传输速率外,还要求允许某种程度的突发传输。 常见 HTTP 状态码:2XX,成功响应;3XX,重定向消息;4XX,客户端错误响应;5XX,服务端错误响应。 请求分为四部分:请求行,请求头,空行,请求正文(不一定有)。 .gitignore 是在文件从工作区被 add 到暂存区时判断是否要被忽略,对于已经在暂存区的文件,不作判断。 数据结构红黑树特性 节点非红即黑 根节点是黑色 叶子节点是黑色的空节点 红节点的子节点是黑节点 从根节点到空节点的每条路径,包含相同数量的黑色节点常见NoSQL数据库类型 键值数据库:Redis 列式数据库:HBase, ClickHouse,适用于联机分析处理(OLAP)场景,数仓等 文档数据库:MongoDB, ElasticSearch 图数据库Clickhouse: 不支持事务:因为面向列。不存在隔离级别。ClickHouse的定位是分析性数据库,而不是严格的关系型数据库 不支持高并发 可处理大量读请求,数据压缩的特性分布式理论两阶段协议https://juejin.cn/post/6844903621495095309三阶段协议https://juejin.cn/post/6844903621495111688FLP 不可能性原理在网络可靠,存在节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性算法。对于允许节点失效情况下,纯粹异步系统无法确保一致性在有限时间内完成。举例:三个人在不同房间,进行投票(投票结果是 0 或者 1)。三个人彼此可以通过电话进行沟通,但经常会有人时不时地睡着。比如某个时候,A 投票 0,B 投票 1,C 收到了两人的投票,然后 C 睡着了。A 和 B 则永远无法在有限时间内获知最终的结果。CAP 定理分布式系统只能交付以下三个所需特性中的两个特性:一致性、可用性和分区容错性。 C(Consistency):一致性,指的是所有客户端可以同时看到相同的数据。每当数据写入一个节点时,就必须立即将其转发或复制到系统中的所有其他节点 A(Availability):可用性,指的是可用性表示发出数据请求的任何客户端都会得到响应,即使一个或多个节点宕机。 P(Partition tolerance):分区容错性,指分区之间的通信可能失败。一般来说,在分布式系统中不可避免会出现分区,因此认为 P 总是成立。因此若要实现一致性(CP),那么当出现分区问题时,就需要舍弃不一致的节点,即牺牲可用性。同理,若要实现可用性(AP),那么当出现分区问题时,就无法保证一致性。Nacos集群默认支持 AP 原则(即不支持数据一致性,支持服务注册的临时实例),但也可切换至基于 Raft的 CP 原则(支持服务注册的永久实例)。临时实例和持久化实例的区别: 持久化实例健康检查后会被标记为不健康,而临时实例会直接从列表中删除。MongoDB 是一种 CP 数据存储——它通过牺牲可用性、保持一致性来解决网络分区问题。BASE 理论 Basically Available(基本可用),假设系统出现了不可预知的故障,但还是能用。 Soft State(软状态),允许系统在多个不同节点的数据副本存在数据延时。 Eventually Consistent(最终一致性),在软状态的一定期限过后,应达到数据的最终一致性。核心思想是:即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。Raft 算法可视化链接三种角色: Leader,发送心跳包以维护自己的Leader状态 Follower,响应Leader发送的心跳包 Candidate,如果没有接收到Leader的心跳信号后,Follower会变成Candidate并向其他节点发送拉票信息记时器: 选举超时时间(Follower拥有):若超时前没有收到心跳包,认为Leader下线,此时Follower会变成Candidate;但反之如果接收到心跳包,则重置计时器。 投票超时时间(Candidate拥有):若超时前没有得到半数以上的票数,则竞选失败;反之成功。 竞选等待超时时间(竞选失败的Follower拥有):竞选失败的Follower需要等待一段时间后才能重新竞选。脑裂由于某种原因(网络不稳定)使得主从集群一分为二,导致master或者leader节点的数量不为1(0或2)。 redis脑裂master 机器突然脱离网络,使得 sentinel 集群无法感知到 master 的存在,会重新选举一个 master 节点。网络恢复后则会存在两个 master 节点。 zookeeper脑裂脑裂可能出现平票的情况,从而无法选举出 leader。因此,zk 中建议节点数是奇数。K8sKubernetes主要由以下几个核心组件组成: etcd保存了整个集群的状态; apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制; controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等; scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上; kubelet负责维持容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理; Container runtime负责镜像管理以及Pod和容器的真正运行(CRI); kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;除了核心组件,还有一些推荐的add-ons(扩展): kube-dns负责为整个集群提供DNS服务 Ingress Controller为服务提供外网入口 Heapster提供资源监控 Dashboard提供GUI Federation提供跨可用区的集群 Fluentd-elasticsearch提供集群日志采集、存储与查询双重校验锁实现单例模式public class Singleton { private static volatile Singleton singleton = null; // volatile 必不可少,防止jvm指令重排优化 private Singleton() { } public static Singleton getInstance(){ //第一次校验singleton是否为空,为了提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例即可。 if(singleton==null){ synchronized (Singleton.class){ //第二次校验singleton是否为空,防止二次创建实例 if(singleton==null){ singleton = new Singleton(); } } } return singleton; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+\" : \"+Singleton.getInstance().hashCode()); } }).start(); } }}23种设计模式与六大原则23种设计模式 创建型 单例模式(Singleton) 饿汉模式:类加载的时候就创建实例,整个程序周期都存在 懒汉模式:只有当第一次使用的时候才创建实例,线程不安全 双重校验锁: 静态内部类: 原型模式(Prototype),能够复制已有对象,而又无需使代码依赖它们所属的类。例如 Cloneable 接口是立即可用的原型模式。 工厂方法模式(Factory Method),在父类中提供一个创建对象的方法, 允许子类决定实例化对象 抽象工厂模式(Abstract Factory),定义了用于创建不同产品的接口, 但将实际的创建工作留给了具体工厂类。 每个工厂类型都对应一个特定的产品变体。 建造者模式(Builder),分步骤创建复杂对象。 行为型 模板方法模式(Template Method),它在基类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。 责任链模式(Chain of Responsibility),它让多个处理器(对象节点)按顺序处理该请求,直到其中某个处理成功为止,例如检查商品模块,需要先检查商品合法性,再检查商品可见性等等。 命令模式(Command) 迭代器模式(Iterator),能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。 中介者模式(Mediator) 备忘录模式(Memeoto) 观察者模式(Observer) 状态模式(State) 策略模式(Strategy),规定每个策略的不同方法,只需选择不同的策略即可执行不同的方法,例如数字人项目中 Provider 的选择(1:AiLab,2:Creatify,3:IC,4:IC-NEW) 访问者模式(Visitor) 解释器模式(Interpreter) 结构型 适配器模式(Adaptor) 桥接模式(Bridge) 组合模式(Composite) 装饰模式(Decorator) 外观模式(Facade),为复杂系统、程序库或框架提供一个简单的接口。通常作用于整个对象子系统上。 享元模式(Flyweight),共享多个对象的部分状态将内存消耗最小化。 代理模式(Proxy) 六大原则 开闭原则,对扩展开放,对修改关闭。 单一职责原则,顾名思义,一个类的职责只有有一个。 里氏替换原则,子类可以扩展父类的功能,但不能改变父类原有的功能。 依赖倒转原则,依赖于抽象,不能依赖于具体实现(面向接口编程)。 接口隔离原则,类之间的依赖关系应该建立在最小的接口上。 迪米特原则,一个软件实体应当尽可能少的与其他实体发生相互作用。分布式事务 Seata分两阶段提交 TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。 RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。执行流程: TM 向 TC 请求发起(Begin)、提交(Commit)、回滚(Rollback)等全局事务。 TM 把代表全局事务的XID绑定到分支事务上。 RM 向 TC 注册,把分支事务关联到XID代表的全局事务中。 RM 把分支事务的执行结果上报给 TC。 TC 发送分支提交(Branch Commit)或分支回滚(Branch Rollback)命令给RM。四大模式: AT模式,能适用于大部分事务情况。 XA模式 SAGA模式,核心思想是将长事务拆分成多个本地短事务。 TCC模式,核心思想是针对每个操作都要注册一个与其对应的确认和补偿操作。其他分布式事务解决方案: 基于 RocketMQ 的消息事务 二阶段提交 三阶段提交,CanCommit、PreCommit、DoCommit三阶段ZooKeeper(CP)使用 ZAB 协议,包括两种运行模式: 消息广播(Leader 正常运行),所有事务请求由单一主进程处理,Leader 转换为事务 Proposal 并广播分发给 Follower,Leader 等待 Follower 的反馈,超过半数的 Follower 反馈消息后,Leader 再发送 Commit 消息提交事务 Proposal。 崩溃恢复(Leader 不可用时),新选举产生的 Leader 与过半的 Follower 进行同步,使数据一致,同步后进入消息广播模式。 主要服务于分布式系统,可以用ZooKeeper来做:统一配置管理、统一命名服务、分布式锁、集群管理,用来解决分布式集群中应用系统的一致性问题,构建 ZooKeeper 集群时使用的服务器最好是奇数台。其设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。数据结构:Znode节点,与Unix文件系统类似,通过路径来标识,例如 /home/app,Znode节点又分为两种类型:临时节点、持久节点、临时顺序节点、持久顺序节点。客户端和服务端断开连接后,临时节点会自动删除但持久节点不会。分布式锁使用的是临时节点。Watcher 监听器:节点的数据发生变化后会通知到节点的监听器。基本命令: create : 在树中的某个位置创建一个节点 delete : 删除一个节点存在:测试节点是否存在于某个位置 get data : 从节点读取数据 set data: 将数据写入节点 get children : 检索节点的子节点列表 sync : 等待数据被传播实现分布式锁原理:判断能否创建临时节点,如果不能则监听父节点(非公平锁)或上一个节点(公平锁),任务执行完成后释放该节点并通知所有监听的节点。实现注册中心原理:初始化时 Provider 先向目录写入 URL 地址,Consumer 订阅相同目录的 URL 地址和自己的 URL 地址,监控中心订阅 Provider 和 Consumer 的 URL 地址;Consumer 在第一次调用服务时,会通过注册中心找到相应的服务的IP地址列表,并缓存到本地,以供后续使用;当 Provider 下线时,会在列表中移除 URL 并将新的 URL 地址发送给 Consumer 并缓存至本地,服务上线也是一样的。与 Nacos 的区别:Nacos 是 AP 的,保证可用性,每个节点都是平等的,若干个节点 crash 后不会影响正常节点,但查到的信息不一定是最新的。ZK 在 crash 后进行 leader 选举,期间是不可用的。微服务框架:Dubbo底层基于 Netty 的 NIO 框架,基于 TCP 协议传输四种负载均衡策略支持服务端服务级别、服务端方法级别、客户端服务级别、客户端方法级别的负载均衡配置。还可以拓展负载均衡算法。 随机负载均衡,默认策略 轮询负载均衡 最少活跃调用数,每个 Provider 都有一个计数器,开始调用则计数器加一,结束调用计数器减一。原则是将请求分配给处理速度快的 Provider。 一致性哈希负载均衡HTTPS 如何保证可靠传输TLS 协议: 对称加密/非对称加密 数字证书 三次握手/四次挥手TLS 的 rsa 握手过程,存在什么安全隐患,怎么解决的? 安全隐患:不支持前向保密。如果私钥泄露了,所有密文都会被破解 解决方案:密钥协商算法HTTPS 绝对安全吗SSL 加密是绝对安全的,但是 HTTPS 并不是绝对安全的,可以通过中间人攻击的方式,即所有请求先发送给第三方(中间人),返回中间人的证书,后续的请求/响应都通过中间人进行转发。HTTP版本的演变HTTP/1.0 无状态:服务器不追踪不记录请求过的状态 无连接:每次请求要建立连接,无法复用连接;在前一个请求响应到达之后下一个请求才能发送,如果前一个阻塞,后面的请求也被阻塞HTTP/1.1默认浏览器对同一域名的并发请求限制为 6 - 8 个。 长连接:默认保持长连接,数据传输完成后只要不断开连接,就可以继续传输数据 管道化:基于上面长连接的基础,可以不等第一个请求响应继续发送后面的请求,但响应的顺序还是按照请求的顺序返回 缓存处理:新增字段cache-control 断点传输:在上传/下载资源时,如果资源过大,将其分割为多个部分,分别上传/下载HTTP/2 二进制分帧传输:更方便头部,只传输差异部分 多路复用:同一服务下只需要用一个连接,节省了连接 头部压缩:合并同时发出请求的相同部分 服务器推送:一次客户端请求服务端可以多次响应HTTP/3基于谷歌的QUIC,底层使用udp代码tcp协议,解决了队头阻塞问题,同样无需握手,性能大大地提升,默认使用tls加密。进程和线程区别: 进程是系统资源分配的最小单位,实现了操作系统的并发;线程是CPU调度的最小单位,实现了进程内部的并发。 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。 进程切换的开销远大于线程切换的开销。 进程之间不会相互影响,但线程挂掉会影响整个进程。进程间通信方式: 管道 信号量 消息队列 共享内存 套接字,也可以用于不同主机之间进程的通信线程间通信方式: 共享内存,比如 volatile 保证内存的可见性。 消息传递,比如 wait/notify/notifyAll 等待通知方式和 join 方式。 管道流用户态和内核态为什么要有这两个状态?保护机制,防止用户误操作或恶意破坏系统用户态 是用户进程/线程所在的区域,主要用于执行用户程序 运行的代码会受到CPU的很多检查,不能直接访问内核数据 只拥有受限的权限 只能响应部分中断请求 只能访问受限的地址空间内核态 是内核进程/线程所在的区域,主要用于执行操作系统程序,硬件交互 运行的代码不受任何限制,可以执行任意指令 拥有最高权限 可以响应所有中断请求 可以访问所有内存空间用户态切换到内核态 系统调用(主动)。操作系统在执行用户程序的时候主要工作在用户态,当执行没有权限的任务时,才切换到内核态。 异常(被动)。执行用户程序出现异常时,会从用户态切换到内核态处理异常 外围设备中断(被动)。中断发生时,如果中断之前在运行用户态的程序,那么会切换至内核态处理中断。服务注册与发现如果自己实现服务注册与发现,需要考虑以下三点: Register, 服务启动时候进行注册 Query, 查询已注册服务信息 Healthy Check, 确认服务状态是否健康实现方案: Eureka,AP ZooKeeper,CP,Paxos 算法 Consul,CP,Raft 算法 Etcd,CP,Raft 算法Paxos 和 Raft 算法都属于一致性算法,所以是保证 CP实际上,作为一个注册中心来说,保证 AP 更加重要,即可用性。两种模式 客户端发现模式,首先要进行的是到服务注册中心获取服务列表,然后再根据调用端本地的负载均衡策略,进行服务调用。 服务端发现模式,调用方直接向服务注册中心进行请求,服务注册中心再通过自身负载均衡策略,对微服务进行调用。这个模式下,调用方不需要在自身节点维护服务发现逻辑以及服务注册信息。常见的负载均衡 轮询,按照请求的顺序轮流分配到不同的服务器,循环往复。 加权轮询,给不同的服务器分配不同的权重,根据权重比例来决定分配请求的数量。 最小连接数 最短响应时间 IP哈希RPC/HTTP与 HTTP 的对比,RPC 使用二进制传输,传输效率高(HTTP额外空间开销大,包含大量元数据,头字段等),但通用性不如 HTTP 协议核心 消息协议:以何种方式打包编码和拆包解码 传输控制:主要有HTTP传输和TCP传输,鉴于TCP传输的可靠性,RPC的传输一般使用TCP作为传输协议工作流程 客户端(Client)以本地方法调用服务 客户端存根(Client Stub)收到调用后把方法、参数等内容打包成特定格式、能进行网络传输的消息体(Marshalling) 客户端存根找到服务地址,发送给服务端(这个过程可以基于TCP也可以基于HTTP) 服务端存根(Server Stub)收到消息后拆包解码(Unmarshalling) 服务端存根根据方法名和参数进行本地调用服务 服务端(Server)本地执行后把结果返回给服务端存根 服务端存根把结果打包发送给客户端存根 客户端存根接收到消息进行解码 客户端得到最终结果实现方式 基于 http 基于 tcp(常见)计算机网络TCP/UDP 可以使用同一个端口吗可以。传输层有两个传输协议分别是 TCP 和 UDP,在内核中是两个完全独立的软件模块。可以在 IP 包头的协议号字段判断出 TCP 还是 UDPTCP 三次握手为什么三次握手因为三次握手才能保证双方具有接收和发送的能力,并且防止重复建立历史连接。为什么四次挥手本质原因是 TCP 是全双工通信,客户端确认没有数据发送后,发出结束报文,此时服务端返回确认后,服务端也不会接收客户端数据。但是此时服务端可能还有数据没有传输完,客户端还是可以接收数据。如果挥手过程中「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。DNS使用TCP还是UDPDNS 在进行区域传输的时候使用 TCP,其他情况使用 UDP。区域传输:是指DNS主从服务器之间的数据同步,保证数据的一致性,传送会利用DNS域,所以就称为DNS区域传送。TCP 保证可靠传输 序列号 连接建立:三次握手四次挥手 头部检验和 确认应答 超时重传 拥塞控制:四种算法 慢启动 拥塞避免 拥塞发生 快速恢复 流量控制:滑动窗口实现OSI 七层模型和 TCP/IP 四层模型 应用层:HTTP,DNS,FTP;网关 应用层 表示层 会话层 传输层:TCP,UDP;网关 网络层:IP(在TCP/IP模型中,ARP属于网络层);路由器 网络接口层 数据链路层:ARP;网桥,交换机 物理层:网卡;中继器,集线器 架构在高并发环境下,服务之间的依赖关系导致调用失败,解决的方式通常是: 限流->熔断->隔离->降级, 其目的是防止雪崩效应。死锁四个条件: 互斥 请求和保持 不可剥夺 循环等待一致性哈希一致性哈希是指将「存储节点」和「数据」都映射到一个首尾相连的哈希环(固定大小)上。数据存储在哈希值通过顺时针找到的第一个节点。优势一致性哈希算法是对 2^32 取模,是一个固定的值;而普通哈希表的长度是由节点数决定的。 普通哈希函数,如果节点数量发生了变化(对系统进行扩容缩容的时候),大部分改变了映射关系,因此需要迁移大量的数据。 一致性哈希算法,如果节点数量发生了变化,只影响该节点顺时针相邻的后继节点。虚拟节点的引入一致性哈希算法并不保证节点能够在哈希环上分布均匀,因此引入均匀分布的虚拟节点,建立真实节点和虚拟节点的映射关系。IO 同步 同步阻塞IO 同步非阻塞IO IO多路复用 信号驱动IO 异步 异步IO 虚拟内存虚拟内存是一种计算机技术,它允许系统将一部分硬盘空间当作RAM(随机存取存储器)使用。当物理内存不足以支持正在运行的应用程序时,系统会将不常用的数据移动至磁盘中。虚拟内存为每个进程提供了一个一致的、私有的地址空间32位/64位操作系统的区别32位/64位表示CPU可以处理最大位数,一次性的运算量不一样,寻址能力也不同。域名解析流程 检查缓存 浏览器缓存 操作系统缓存 本地 hosts 文件 使用递归查询向本地域名服务器查询 使用迭代查询向跟服务器查询 根域名服务器(.) 顶级域名服务器(.com) 二级域名服务器(google.com) 排查 CPU 占用过高和内存溢出的问题JavaCPU 占用排查:使用 top 和 top -Hp xxx 命令定位占用率最高的进程和该进程的线程内存占用排查:jstack, jmap 打印出堆栈信息, jstat 查看垃圾回收的情况Go使用 pprof 工具,可以查看 CPU 占用、排查内存泄漏、协程泄漏等。" }, { "title": "Kafka vs RocketMQ", "url": "/posts/kafka-vs-rocketmq/", "categories": "", "tags": "backend", "date": "2023-08-25 17:10:22 +0800", "snippet": "基本概念 RocketMQ 由 Producer, Brocker, Consumer 组成 Producer 负责生产消息 Consumer 负责消费消息 Broker 负责存储消息,每一个 Broker 对应一台服务器但可以存储多个 Topic 的消息,每个 Topic 的消息也分片存储在不同的 Broker 里。 Topic...", "content": "基本概念 RocketMQ 由 Producer, Brocker, Consumer 组成 Producer 负责生产消息 Consumer 负责消费消息 Broker 负责存储消息,每一个 Broker 对应一台服务器但可以存储多个 Topic 的消息,每个 Topic 的消息也分片存储在不同的 Broker 里。 Topic 是逻辑概念,队列(Kafka 中叫分区)是物理概念。每个主题包含多个消息,每条消息只属于一个主题。一个 Producer 可以同时发送多种 Topic 的消息,而一个 Consumer 只能订阅一个 Topic 的消息。Tag 类似于子主题。 MessageQueue用于存储消息的物理地址,每个Topic中的消息地址存储于多个MessageQueue,是消息的最小存储单元。 消费方式,Pull 拉取式消费,Consumer 需要主动拉取 Broker 中的消息;Push 推送式消费,Broker 一接收到消息马上发送给 Consumer,具有实时性。RocketMQ是基于 Pull 模式的长轮询策略实现消息消费的。即 Consumer 发送拉取请求到 Broker 端,如果 Broker 有数据则返回并继续发起长轮询,如果没有则 hold 请求(指的服务端暂时不回复结果,保存相关请求,不关闭请求连接),不立即返回,直到超时(默认5s)并继续发起长轮询。为什么需要设置超时?1. 无法避免服务器假死等情况以确保可用性;2. 可能修改监听的配置消息队列的三大作用 解耦 异步 削峰模式 点对点模式:基于队列,每条消息只能被一个消费者消费,RabbitMQ。 发布/订阅模式:一条消息能被多个消费者消费,RocketMQ 和 Kafka。消息发送方式 RocketMQ 同步发送(Sync) 异步发送(Async) 单向发送(One-way)其中同步发送和异步发送需要 Broker 返回确认消息,而单向发送不需要。 Kafka 发后即忘(fire-and-forget) 同步(sync) 异步(async) 消息消费方式 Kafka:从用户角度分为手动提交和自动提交;从 Consumer 的角度分为同步提交和异步提交 自动提交偏移量 手动同步提交偏移量 手动异步提交偏移量 同步异步结合提交偏移量 区别整体区别是 Kafka 的设计初衷是用于日志传输,而 RocketMQ 是用于解决各类应用可靠的消息传输,适用于业务需求。存储形式 Kafka 采用partition,每个topic的每个partition对应一个文件。顺序写入,定时刷盘。但一旦单个broker的partition过多,则顺序写将退化为随机写,Page Cache脏页过多,频繁触发缺页中断,性能大幅下降。 RocketMQ 采用CommitLog + ConsumeQueue,物理存储文件是CommitLog,ConsumeQueue是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。每个Topic下的每个MessageQueue都有一个对应的ConsumeQueue文件,单个broker所有topic在CommitLog中顺序写。每个CommitLog大小固定为1G。 生产消息:Producer 先向 CommitLog 顺序写,持久化后将数据 Dispatch 到 ConsumeQueue 中。消费消息:Consumer 从 ConsumeQueue 中拉取数据,但拉取到数据是指向 CommitLog 的地址,此时是随机读,但又因为 PageCache 的存在,还是整体有序的。Page Cache(页面缓存)从内存中划出一块区域缓存文件页,如果要访问外部磁盘上的文件页,首先将这些页面拷贝到内存中,再进行读写。吞吐量 Kafka 单机吞吐量 TPS 可上百万,远高于 RocketMQ的 TPS 十万级。数据可靠性RocketMQ新增同步刷盘和同步复制机制,保证可靠性。而 Kafka 倾向于牺牲部分可靠性换取更高的性能(因为 Kafka 中的 producer 将信息堆起来一起发送,以减少网络IO,但是这个时候如果 producer 宕机了,会导致信息丢失的)。 Kafka 支持异步刷盘,异步复制。 RocketMq 支持异步刷盘和同步刷盘,同步复制和异步复制。异步刷盘:返回写成功状态时,消息可能只是被写进内存,吞吐量大,当内存大消息积累到一定程度时,统一触发写磁盘操作,快速写入。同步刷盘:返回写成功状态时,消息已经被写入磁盘。流程是消息写入内存后,立刻通知刷盘线程刷盘,等待刷盘完成后再唤醒等待的线程返回消息写成功的状态。异步复制:只要写就返回写成功状态。较低的延迟和较高的吞吐量。同步复制:写成功后返回写成功状态。容易恢复故障的数据。如何保证消息不丢失两者类似,都是从 Producer -> Broker -> Consumer 三个阶段逐一判断,只不过设置的参数不同。 Producer: 同步发送消息;超时重试发送;消息补偿机制(Kafka,超时仍失败的情况下,会继续投递到本地消息表,定时轮询并推送到Kafka);ACKs(Kafka中,该参数表示多少个副本收到消息,认为消息写入成功) Broker: 同步刷盘;设置主从模式,配置副本 Consumer: At least Once 的消费机制;消费重试;ACK机制;手动提交位移(Kafka) 零拷贝传统的数据传输过程通常需要经历多次内存拷贝。首先,从磁盘读取数据,然后将数据从内核空间拷贝到用户空间,再从用户空间拷贝到应用程序的内存中。这些额外的拷贝会消耗大量的CPU资源和内存带宽,降低数据传输的效率。零拷贝就是为了避免这些不必要的数据拷贝,能够将数据直接传输到目标内存区域,以提高数据传输的效率。实现方式 mmap(Memory Mapped Files) + write,作用:将磁盘文件映射到内存, 用户通过修改内存就能修改磁盘文件。Java NIO里对应的是MappedByteBuffer类,可以用来实现内存映射。它的底层是调用了Linux内核的mmap的API。 sendfile,实现:将读到内核空间的数据,直接拷贝到socket buffer,进行网络发送。避免了数据在内核和用户空间之间的额外拷贝。FileChannel的transferTo()/transferFrom(),底层就是sendfile() 系统调用函数。零拷贝技术减少了用户进程地址空间和内核地址空间之间由于上下文切换而带来的开销。DMA (Direct Memory Access) 是零拷贝技术的基石。并不是不需要拷贝,而是减少冗余不必要的拷贝。 Kafka: Producer生产的数据持久化到 broker 采用 mmap 文件映射(在读写稀疏索引文件用到),实现顺序的快速写入;而 Customer 从 broker 读取数据采用 sendfile 进行网络发送。 RocketMQ:采用 mmap 的方法。正因为使用内存映射机制,RocketMQ的文件存储都使用定长结构来存储,方便一次将整个文件映射至内存。为什么 Kafka 这么快?答:六个要点,顺序读写、零拷贝、消息压缩、分批读写/发送、基于操作系统内存PageCache的读写、分区分段 + 索引。为什么 RocketMQ 这么快?答:顺序写,零拷贝,异步刷盘(先写入操作系统的PageCache再异步刷盘到磁盘)消息失败重试 Kafka 不支持重试。 RocketMQ 支持定时重试,每次重试间隔逐渐增加。RocketMQ 的消费重试是基于延迟消息实现的,在消息消费失败的情况下,重新当作延迟消息投递到 Broker 中,并且延迟等级逐渐增加,消息重试会有 16 个级别,恰好是延迟消息的 18 个级别的后 16 个级别。Rocket延迟消息主要分为以下几步,类似于流转: 修改消息的Topic名称和队列信息(因为发送到普通的Topic会马上被消费) 转发消息到延迟Topic为SCHDULE_TOPIC_XXX的ConsumeQueue中 延迟服务(本质是Java自带的延迟队列)消费SCHDULE_TOPIC_XXX的消息 将信息重新存储到CommitLog中 将消息投递到目标Topic中 消费Topic的消息Kafka 延迟消息基于 时间轮 算法(类似于钟表):存储定时任务的环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表。每隔一个时间跨度,下标移动一次。事务 Kafka 支持事务(更像是原子性),可以实现对多个 Topic 、多个 Partition 的原子性的写入,即处于同一个事务内的所有消息,最终结果是要么全部写成功,要么全部写失败。这种事务机制单独使用的场景不多,更多的是配合其幂等机制来实现 Exactly Once 语义的。通常用于解决从一个 Kafka 数据源消费进行计算等操作,再输入到另一个 Kafka 数据源中的场景。 RocketMQ 支持事务,采用二阶段提交+broker定时回查。但也只能保证生产者与broker的一致性,broker与消费者之间只能单向重试。即保证的是最终一致性。Kafka 事务RocketMQ 事务对于一个大事务来说,可以划分成多个小事务异步执行。二阶段:第一阶段发送 prepared 消息,接着执行本地事务,第二阶段发送 commit 或 rollback 的消息。定时回查:定时遍历 commitlog 中的半事务消息如果事务正常执行,则 commit 该消息,如果抛出异常,则 rollback。对于消费消息失败,RocketMQ 会尝试重新消费,直到被加入死信队列中为止。在重试的过程中有可能产生重复的消息,所以对于消费端来说要确保消费幂等!消息堆积原因可能是以下三种: 生产远超预期 消息接收和持久化出现故障 消费能力下降 程序问题处理堆积的消息:建立临时的 topic(扩容),转发堆积的消息服务发现 Kafka 使用 ZooKeeper,但新版本使用内嵌的KRaft替代了ZooKeeper RocketMQ 使用自己实现的 nameserverTopic 数量对吞吐量的影响RocketMQ:topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topicKafka:topic 从几十到几百个时候,吞吐量会大幅度下降。原理:Kafka 利用操作系统的 PageCache 先将消息持久化到内存中,并不是直接写入磁盘。Topic 增加,也就是 Partition 数量会增加,使用的 PageCache 也会大量增加,大量增加后需要使用 LRU 淘汰算法对 Page 内容刷新到磁盘中,导致性能会下降。消息顺序性分为两步:生产者有序存储,消费者有序消费。Kafka 如何保证消息的顺序性针对消息有序的业务需求,还分为全局有序和局部有序。已知,每个partition的消费是顺序性的,但每个topic可以有若干个partition。全局有序:一个Topic下的所有消息都需要按照生产顺序消费。解决方法:1个Topic只能对应1个Partition。局部有序:一个Topic下的消息,只需要满足同一业务字段的要按照生产顺序消费。例如:Topic消息是订单的流水表,包含订单orderId,业务要求同一个orderId的消息需要按照生产顺序进行消费。解决方法:要满足局部有序,只需要在发消息的时候指定Partition Key,Partition Key相同的消息会放在同一个Partition。RocketMQ 如何保证消息的顺序性全局有序:对于指定的一个 Topic,设置读写队列的数量为一。(与Kafka设置一个partition类似)局部有序:对于指定的一个 Topic,生产者根据 hashKey 将消息发送到同一个MessageQueue。 同一个分区内的消息按照严格的 FIFO 顺序进行发布和消费。实现消息有序性从三个方面: 生产者生产顺序消息:生产者将消息路由到特定分区,单线程发送消息 Broker 保存顺序消息:保证生产者顺序生产即可,保存到指定的Partition或MessageQueue中 消费者顺序消费消息:设置 consumeMode 为 ORDERLY,单线程消费消息Kafka 负载策略主写主读,不支持读写分离Producer 负载均衡当 key 不存在时,会从当前存活的分区中轮询;当 key 存在时,发送给哈希后的指定分区。Consumer 负载均衡Kafka 中主题订阅者的基本单位是消费者组,每个分区只能由消费者组中的一个消费者进行消费,多个消费者组之间对于分区的消费互不影响。共有三个分区分配器: RangeAssignor(默认) RoundRobinAssignor StickyAssignorRocketMQ 负载策略Producer 负载均衡Producer 默认采用轮询的方法,按顺序将消息发送到 MessageQueue 里。Consumer 负载均衡 平均负载策略(默认):AllocateMessageQueueAveragely 循环分配策略:循环顺序遍历消费者:AllocateMessageQueueAveragelyByCircle 指定机房分配策略:AllocateMessageQueueByMachineRoom 机房就近分配策略:AllocateMachineRoomNearby 一致性哈希算法策略:AllocateMessageQueueConsistentHash 按照指定配置的策略:AllocateMessageQueueByConfig适用场景Kafka:适用于日志收集与分析、实时流处理、大数据集成(例如 Apache Storm 或 Flink)、用户行为追踪等场景。RocketMQ:更适合金融交易、订单处理、秒杀活动、库存同步、跨系统间的服务解耦和异步调用等场景,尤其是那些对消息顺序、事务完整性和实时性要求极高的业务。消费者消费者群组(Consumer Group)消费者组是一组共享 group.id 的消费者实例,一个消费者组可以消费多个 Topic 的消息,组内的消费者只能订阅相同的 Topic 和相同的 Tag 且 Tag 顺序相同。详见订阅关系一致。消费方式(RocketMQ) 集群模式(默认):相同消费者群组的消费者平摊消息,便于负载均衡 广播模式:相同消费者群组的每个消费者接收全量的消息,适合并行处理的场景。在该模式下,消费者组的概念在消息划分方面并没有意义。 RocketMQ/Kafka 使用 Consumer Group 机制,实现了传统两大消息引擎。如果所有实例属于同一个Group,那么它实现的就是消息队列模型;如果所有实例分别属于不同的Group,且订阅了相同的主题,那么它就实现了发布/订阅模型;消费者和消费者组的关系 同一个消费者组内部的消费者均匀消费订阅的 Topic 的消息,负载均衡 不同消费者组全量消费订阅的 Topic 的消息,类似消费者组层面的广播模式。但 Kafka 和 RocketMQ 不同的地方在于,Kafka 所有 Partition 会均匀分配给 Consumer 消费(因此 Consumer 只消费 Topic 的部分数据),而不像 RocketMQ 那样,每个 Consumer 全量消费 Topic 里的消息。Kafka 知识点知识点 Partition 数量只能增加,不能减少。 偏移量:指Kafka主题中每个分区中消息的唯一标识符 ISR: In-Sync Replica; OSR: Out-Sync Replica索引机制一个Topic分为多个Partition,一个Partition分为多个Segment。每个Segment对应三个文件:偏移量索引文件、时间戳索引文件、消息存储文件Producer 生产消息的流程在消息发送的过程中,涉及到两个线程,main线程和sender线程,其中main线程是消息的生产线程,而sender线程是jvm单例的线程,专门用于消息的发送。在jvm的内存中开辟了一块缓存空间叫RecordAccumulator(消息累加器),用于将多条消息合并成一个批次,然后由sender线程发送给kafka集群。 创建消息以及指定 Topic 调用 Send() 方法 调用拦截器 调用序列化器,将消息序列化 使用分区器(三种分区策略)指定 Partition DefaultPartitioner 默认分区:指定分区则用该分区,没指定则使用对 key 哈希后值的分区,没有 key 则使用粘性分区策略 UniformStickyPartitioner 统一粘性分区:直接使用粘性分区策略,即逐个填满 Batch 里的消息 RoundRobinPartitioner 轮询分区:指定分区则用该分区,否则平均分配 将消息缓存到消息累加器中 压缩和批处理消息 找到相应的 Broker 并发送消息(Sender 线程触发的),可以同步发送也可以异步发送 确认(ACK)和重试 更新偏移量 错误处理,将重试仍失败的消息放入死信队列里Consumer 消费过程Kafka 采用 Pull 的方式,每个 Consumer 维护一个 HW 水位信息消费者线程模型Thread per consumer model:即每个线程都有自己的consumer实例,然后在一个线程里面完成数据的获取(pull)、处理(process)、offset提交。Leader Replica 选举策略 ISR 选举策略:在 ISR 副本集合中选举 首选副本选举策略:每个分区都有一个首选副本,通常是副本集合中的第一个副本 不干净副本选举策略:从所有副本中(包含 OSR 集合)选择一个副本选举Zookeeper 在 Kafka 的作用 Broker 注册:每个Broker服务器在启动时,都会到ZooKeeper上进行注册 Topic 注册:同一个 Topic 的消息会被分成多个分区并将其分布在多个 Broker 上,ZK 负责维护这些分区信息及与 Broker 的对应关系 负载均衡:Consumer 消费消息时,ZK 会根据当前 Partition 数量和 Consumer 数量进行动态负载均衡消息压缩/解压Producer 发送压缩消息到 Broker 后,Broker 会保存压缩数据,由Consumer 解压数据。ControllerController 用于在 ZK 的帮助下管理和协调整个 Kafka 集群。集群内任意一台 Broker 都能充当 Controller 的角色,但在运行过程中,只有一个 Controller。Controller 职责 管理 Topic Preferred Leader 选举 集群 Broker 管理 数据服务,保存最完整的元数据信息" }, { "title": "SpringBoot", "url": "/posts/springboot/", "categories": "", "tags": "backend", "date": "2023-08-10 15:08:32 +0800", "snippet": "Spring, SpringBoot, Spring MVC 区别: Spring框架(Framework)是最流行的Java应用程序开发框架。 Spring框架的主要功能是依赖项注入或控制反转(IoC)。 Spring MVC是Spring的一个MVC框架,包含前端视图,文件配置等。XML和config配置比较复杂。 Spring Boot 是为简化Spring配置的快速开发整合包,...", "content": "Spring, SpringBoot, Spring MVC 区别: Spring框架(Framework)是最流行的Java应用程序开发框架。 Spring框架的主要功能是依赖项注入或控制反转(IoC)。 Spring MVC是Spring的一个MVC框架,包含前端视图,文件配置等。XML和config配置比较复杂。 Spring Boot 是为简化Spring配置的快速开发整合包,允许构建具有最少配置或零配置的独立应用程序。SpringBoot项目启动流程: 总体来说分两部分,先初始化 SpringApplication,再运行 SpringApplication。运行 SpringApplication 又分为:1、获取并启动监听器2、根据监听器和参数来创建运行环境3、准备 Banner 打印器4、创建 Spring 容器5、Spring 容器前置处理(将前几步生成的监听器,Banner打印器和环境等配置到容器中)6、刷新容器7、Spring 容器后置处理(空方法)8、发出结束执行的事件通知9、返回容器Spring核心之控制反转(IOC)和依赖注入(DI): IOC是一种设计思想,将设计好的Bean对象交给容器控制,而不是在传统的在对象内部直接控制。用@Configutation + @Bean的方式。用通俗的话解释就是在容器里创建Bean对象,在需要的时候取出使用即可。 DI是一种实现方式,将应用程序依赖的对象注入到容器中。IoCrefresh() 的作用:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。Spring创建Bean的三步(由反射创建的): 实例化,AbstractAutowireCapableBeanFactory 的 createBeanInstance 方法 属性注入,AbstractAutowireCapableBeanFactory 的 populateBean 方法 初始化,AbstractAutowireCapableBeanFactory 的 initializeBean 方法 调用Aware接口(如果实现了Aware接口) postProcessBeforeInitialization afterPropertiesSet init-method postProcessAfterInitialization Spring启动时先扫描所有Bean信息,BeanDefinition存储日常给Spring Bean定义的元数据。然后存储BeanDefinition的BeanDefinitionMap,是根据字典序依次创建Bean对象。两种IOC容器IOC的实现原理是工厂模式加反射机制。 BeanFactory,只提供了实例化对象和获取对象的功能,使用懒加载机制,不支持国际化和基于依赖的注解 ApplicationContext,其拓展了BeanFactory接口,使用即时加载的机制,它是在Ioc启动时就一次性创建所有的Bean,支持国际化和基于依赖的注解,包括AnnotationConfigApplicationContext、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等三级缓存解决循环依赖: 前提,出现循环依赖的Bean必须是单例;依赖注入的方式不能全是构造器注入(通过setter方法进行依赖注入,全是构造器注入则无法解决循环依赖)。 getSingleton(beanName)方法三级缓存 singletonObjects,一级缓存,存储的是所有创建好了的单例Bean对象 earlySingletonObjects,二级缓存,完成实例化,但是还未进行属性注入及初始化的提前暴露的对象 singletonFactories,三级缓存,存放生产对象的工厂,并且每次从这个工厂中拿到的对象都是不一样的,二级缓存中存储的就是从这个工厂中获取到的对象,如果Bean存在AOP的话,返回的是AOP的代理对象,提前进行了代理,避免对后面重复创建代理对象。 为什么需要二级缓存:如果出现循环依赖+aop时,多个地方注入这个动态代理对象需要保证都是同一个对象。如果只使用这两层缓存,在使用三级缓存中的工厂对象生成的动态代理对象都是新创建的,循环依赖的时候,注入到别的bean里面去的那个动态代理对象和最终这个bean在初始化后自己创建的bean地址值不一样。如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,而 Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。所以,Spring 选择了三级缓存。但是因为循环依赖的出现,导致了 Spring 不得不提前去创建代理,因为如果不提前创建代理对象,那么注入的就是原始对象,这样就会产生错误。Prototype(原型)对象和单例对象的区别: 单例对象在Spring加载的时候就创建,创建完毕后放入一级缓存中 而原型对象在每次在需要的时候都会创建,并且也不会存入缓存中 单例有状态(有状态指有成员变量并会进行修改操作)Bean对象的劣势:线程不安全。解决方案:考虑ThreadLocal(使用完后记得调用 ThreadLocal 的 remove 方法)或者锁机制。为什么Springboot默认创建单例Bean: 不需要多次创建实例 减少垃圾回收 缓存中可以快速获得容器注册组件的四种方式: @Configuration + @Bean,注意,使用这种注册方法 Spring 会通过 CGLIB 来增强这个类,会判断是否实例已经存在,多次调用只会生成一个实例,意味着在@Configuration中的@Bean生成的是单例;相比之下,在@Component类中使用@Bean注解时,并不会有这种增强的行为。也就是说,当你在@Component类中调用一个@Bean方法时,实际上就是直接调用这个方法,并且每次调用都会创建一个新的 Bean,也就是说是生成多实例。 @ComponentScan + @Component @Import 新建一个实现FactoryBean接口的类面向切面编程(AOP)也是一种设计思想,本质是为了解耦。其实现方式是动态织入,在运行时动态将要增强的代码织入到目标类中,借助动态代理技术完成的。并且接口与非接口的动态代理机制并不相同,如下所示: 接口使用JDK代理,通过反射类Proxy以及拦截器的InvocationHandler回调接口实现的,使用反射,效率会低,不提供子类代理。 非接口使用CGlib代理,没有接口,只有实现类,采用底层字节码增强技术ASM在运行时使用Enhancer类创建目标的子类,提供子类代理。执行顺序: doAround(ProceedingJoinPoint pjp)的 pjp.proceed()前的内容 doBefore() doAfterReturning(String result) / doAfterThrowing(Exception e) doAfter() doAround(ProceedingJoinPoint pjp)的 pjp.proceed()后的内容(当且仅当非异常情况下才会执行)注解: @Component修饰的类为组件类并为这个对象创建Bean,@Bean修饰的方法会生成一个对象Bean。 @SpringBootApplication 包含三个注解: @SpringBootConfiguration, 继承@Configuration,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例注册到spring容器中,并且实例名就是方法名。 @EnableAutoConfiguration,主要由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)这两个注解组成的。 @AutoConfigurationPackage 内部是 @({Registrar.class}),用于将启动类所在的包里面的所有组件注册到spring容器。扫描 @Entity, @Mapper 等第三方依赖注解。 @Import(EnableAutoConfigurationImportSelector.class) 是将特定路径(META-INF/spring.factories)中所有符合自动配置条件(@ConditionalOnClass)的类加载到Ioc容器,例如mybatis-spring-boot-starter。AutoConfigurationImportSelector.java 中可以看到所有自动配置类的名称。自动配置类原理 @ComponentScan,自动扫描并加载被@Component或@Repository修饰的组件,最终将这些组件加载到容器中,默认路径是该注解所在类的package。 事务事务的几个参数:rollbackFor,propagation,isolationPROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的区别: PROPAGATION_REQUIRES_NEW:内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚,两个事务互不影响。 PROPAGATION_NESTED:外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚。事务失效 数据库引擎不支持(MyIsam) 没有注册成Bean被Spring管理(没有使用@Service等注解) 方法不是public或者被final修饰(无法生成代理类) 方法内部调用(解决方法是引入自身Bean) 异常被捕捉或抛出其他类型的异常(回滚的默认异常为RuntimeException) 未开启事务方法调用的事务回滚情况前提:a方法调用b方法 a b 报错情况 同类调用的现象 不同类调用的现象 transactional注解 无注解 a方法报错,b方法不报错 均回滚 均回滚 transactional注解 无注解 a方法不报错,b方法报错 均回滚 均回滚 无注解 transactional注解 a方法报错,b方法不报错 均不回滚 均不回滚 无注解 transactional注解 a方法不报错,b方法报错 均不回滚 a不回滚,b回滚 结论: transactional注解修饰的方法会创建一个代理增强类,其他方法调用该注解修饰的方法也只是调用原类中的方法。 transactional修饰的方法内报错就一定会回滚。Spring 用到了哪些设计模式 工厂模式:通过 BeanFactory 和 ApplicationContext 容器创建 Bean 对象 代理模式:AOP 的实现 单例模式:Bean 默认是单例 模板方法:jdbcTemplate 等用到了模板方法 观察者模式:Springboot 事件驱动,监听器等" }, { "title": "ElasticSearch", "url": "/posts/elasticsearch/", "categories": "", "tags": "", "date": "2023-08-10 15:08:32 +0800", "snippet": "Elasticsearch是基于 Lucene 架构实现的分布式、海量数据的存储分析引擎,其中 Lucene 最主要的倒排索引结构,赋予了ES全文检索、模糊匹配、联合索引查询等等快速检索文档数据的能力,使得ES在这些查询的应用场景下优于数据库。适合用于搜索,不适合复杂的关系查询倒排索引:被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。写数据的过程 客户端选择一个 n...", "content": "Elasticsearch是基于 Lucene 架构实现的分布式、海量数据的存储分析引擎,其中 Lucene 最主要的倒排索引结构,赋予了ES全文检索、模糊匹配、联合索引查询等等快速检索文档数据的能力,使得ES在这些查询的应用场景下优于数据库。适合用于搜索,不适合复杂的关系查询倒排索引:被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。写数据的过程 客户端选择一个 node 发送请求,这个 node 就成为了 coordinate node (协调节点) 该协调节点将输入的 document 做哈希路由得到 shard id,将请求转发给 shard id 对应的 node 该 node 在 primary shard 上处理请求,并将数据同步到 replica 上 协调节点在写入完成后返回响应结果读数据的过程 客户端选择一个 node 发送请求,这个 node 就成为了 coordinate node (协调节点) 该协调节点对输入的 document id 做哈希得到 shard id,将请求转发给 shard id 对应的 node 该 node 使用 round-robin 轮询算法在 primary 和所有 replica 中选择一个,让读请求负载均衡 协调节点返回查询结果更新、删除数据的过程本质上都是写操作!磁盘上的每个段都有一个相应的 .del 文件。 删除操作(不是删除索引)是逻辑删除,document 被标记为 deleted 状态。当段合并时,在 .del 文件中被标记为删除的文档将不会被写入新段。 更新操作是把原 document 标记为删除,再写入新的 document 数据为什么近实时数据写入原理大概分为三个步骤:write -> refresh -> flush1、write:文档数据到内存缓存,并存到 translog2、refresh:内存缓存中的文档数据,到文件缓存中的 segment 。此时可以被搜到。3、flush: 缓存中的 segment 文档数据写入到磁盘当数据添加到索引后并不能马上被查询到,等到索引刷新后才会被查询到。refresh_interval 参数设置为正数之后,需要等一段时间后才可以在es索引中搜索到,因为已经从内存缓存刷新到文件缓存中了。详见数据写入与查询存在时间差问题 indexing Buffer 属于 ES 内存的一部分,OS 系统文件缓存属于操作系统的,不属于 ES 内存 refresh 操作:定时将 ES 缓冲区转换成 segment 并写入系统文件的过程。(近实时搜索的根本原因) translog 日志文件,不管是 ES 缓冲区还是系统缓冲区的内容,只要没写入到磁盘,就会在日志文件里记录,当 flush 到磁盘后,内存和磁盘中的文件就会清空。日志文件也是先写入 os cache,默认每隔 5 秒刷一次到磁盘,所以可能存在5秒的数据丢失 flush 操作:将 segment 持久化到磁盘,同时清理 translog。主要分为以下几步: 内存中的数据写入新的segment并放入缓存(清空内存区) 将commit point 写入磁盘,表示哪些 segment 已经写入磁盘 将 segment 写入磁盘,使用 fsync 命令 清空 translog 日志内容 为什么这么快 分布式储存:采用分布式储存技术,将数据存储于多节点,分散负载,优化整体执行效能。 索引分片:将每索引分裂为多片段,实现并行查询,提升搜索速度。 倒排索引:支持倒排索引数据结构,映射文档中每个词汇至文档出现位置,当搜索请求发生时,能快速检索包含所有搜索词的文档,迅速返回结果。 索引压缩:使用不同的压缩算法对倒排索引进行压缩,以减少存储空间占用和提高读写效率。 预存储结果:插入数据时,预处理数据,将结果预存储于索引中,查询时无需重新计算,提升查询速度。 内存存储:应用内存映射(Memory Mapped)技术来提高磁盘I/O性能。它将倒排索引缓存在内存中,同时使用内存映射技术将文件映射到虚拟地址空间中。减少磁盘访问次数,提高数据存储与查询效率。Term Index: 倒排的树状结构,存在内存里,是Term dictionary的索引。基本概念: Cluster,集群。一个集群包含多个节点,对外提供服务。 Node,节点。一个节点运行在一个独立的环境或虚拟机上,一个节点可以包含多个分片,一个索引由多个分片组成 Shard,分片。(Primary Shard 和 Replica Shard),主分片数量在创建索引的时候就需要固定,并且无法做修改。 Index,索引,相当于mysql中的库。 Type,类型,一个索引可以对应一个或者多个类型。(ES6.0后被废弃,ES7完全删除) Mapping,映射,相当于表结构。 Document,文档,相当于行。 Field,字段,相当于列。分片与副本的区别在于: 当分片设置为5,数据量为30G时,ES会将数据平均分配到5个分片上,每个分片6G数据。进行数据查询时,ES会把查询发送给每个分片,最后将结果组合在一起。目的是保障查询的高效性。 副本是对分片的数据进行复制,目的是保障数据的高可靠性,防止丢失。Filter VS Query尽可能使用过滤器上下文(Filter)替代查询上下文(Query) Query:此文档与此查询子句的匹配程度如何? Filter:此文档和查询子句匹配吗?Elasticsearch 针对 Filter 查询只需要回答「是」或者「否」,不需要像 Query 查询一样计算相关性分数,同时Filter结果可以缓存。Reindex 重建索引的原理:Scroll Query + BulkIngest pipeline 可以在数据存入ES之前对数据进行转换,例如转小写,增加字段等。性能优化:背景:每五分钟就有6.5M条数据,直接reindex需要1000s(15分钟) 创建索引前设置主分片数量为二,副本比例为一,700s batch size = 2000(原来为默认值1000,使用堆缓存索引数据,默认最大值为100 MB), 副本比例为零,480s,平均每个 document 30-40 kb 左右 slice = 2(其实是三个并行任务,一个父任务二个子任务),420s(甚至有一次350s)text 和 keyword 的区别 text类型: text类型是指可分词的文本,适用于长文本或短语查询。当文本被索引时,会被分成一些个别单词或词组,并且会去除停用词(如“a”、“the”、“and”等)和标点符号。这些单词或词组将被标准化并存储在倒排索引中,使得搜索时可以更快地匹配文档。适合全文搜索。 keyword类型: keyword类型是指未经过分词处理的文本,适用于精确匹配和排序。当文本被索引时,会被作为一个整体进行索引。它们通常用于搜索和排序非文本字段,例如数字或日期。适合过滤、排序、聚合。 ElasticSearch 默认为text类型,但是text类型总会有keyword的类型的字段,等价于 .keywordES 调优硬件配置优化 CPU 配置 内存配置 禁止 swap 配置 GC 磁盘索引优化 批量提交(Bulk),但不能一次性提交过多内容。 增加 refresh 时间间隔 减少副本数量查询优化 尽可能使用过滤器上下文(Filter)替代查询上下文(Query) 拆分索引 减少模糊匹配数据结构优化 减少不需要的字段 text 和 keyword 类型字段的设置集群架构设计 设置分片数量,但不宜过大。扩容 垂直扩容(纵向扩容):替换旧的设备 水平扩容(横向扩容):直接新增设备到集群中,会触发relocation 写入和更新的并发针对写入和更新时可能出现的并发问题,ES是通过文档版本号来解决的。当用户对文档进行操作时,并不需要对文档加锁和解锁操作,只需要带着版本号。当版本号冲突的时候,ES会提示冲突并抛出异常,并进行重试存储结构 一个集群包含1个或多个节点; 一个节点包含1个或多个索引; 一个索引,类似 Mysql 中的数据库; 每个索引又由一个或多个分片组成; 每个分片都是一个 Lucene 索引实例,您可以将其视作一个独立的搜索引擎,它能够对 Elasticsearch 集群中的数据子集进行索引并处理相关查询; 每个分片包含多个segment(段),每一个segment都是一个倒排索引。查询时,会把所有的segment查询结果汇总归并为最终的分片查询结果返回。段段是不可变的为了实现高索引速度,故使用了segment 分段架构存储。一批写入数据保存在一个段中,其中每个段是磁盘中的单个文件。由于两次写入之间的文件操作非常繁重,因此将一个段设为不可变的,以便所有后续写入都转到New段。段合并可以是内存里的段,也可以是在磁盘里的段进行段合并流程 合并候选阶段:es会选择一些相似大小的段作为合并候选,减少合并过程中的IO开销。 合并阶段:ES将合并时候选中的多个段合并成为一个新的段,同时删除旧的段(在 .del 文件里标记为删除)。在合并过程中ES会进行数据的排序和去重,并重新生成倒排索引为什么要段合并由于自动刷新流程每秒会创建一个新的段(由动态配置参数:refresh_interval 决定),这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦,导致: 消耗资源:每一个段都会消耗文件句柄、内存和cpu运行周期; 搜索变慢:每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。触发条件后台进程定期检查,可能进行 merge 操作 手动触发 段 flush 触发 由前一个成功的 merge 触发分词器 ik-analyzer 分词器,支持中文分词 pinyin 分词器,支持输入拼音查到相关关键词 pattern 分词器,支持正则表达式 whitespace 分词器,用于去除空格" }, { "title": "Redis 知识体系", "url": "/posts/redis/", "categories": "Backend, Redis", "tags": "redis", "date": "2023-07-06 19:24:40 +0800", "snippet": "单机 QPS单机 QPS 能力参考范围为 8 - 10 万。为什么 Redis 这么快 用 C 语言编写的,执行效率高 基于内存的数据库,避免磁盘IO操作 采用高效的数据结构 合理的数据编码,同样的数据结构在不同数据量的情况下采用不同的编码方式 采用单线程,避免上下文切换 多路IO复用,一个线程处理多个大量Socket请求。 虚拟内存虚拟内存Redis 的数据并不是完全在内存中...", "content": "单机 QPS单机 QPS 能力参考范围为 8 - 10 万。为什么 Redis 这么快 用 C 语言编写的,执行效率高 基于内存的数据库,避免磁盘IO操作 采用高效的数据结构 合理的数据编码,同样的数据结构在不同数据量的情况下采用不同的编码方式 采用单线程,避免上下文切换 多路IO复用,一个线程处理多个大量Socket请求。 虚拟内存虚拟内存Redis 的数据并不是完全在内存中,当 Redis 的数据量大于物理内存容量时,会通过虚拟内存的手段,将不经常使用的数据(冷数据)存储到硬盘上,以释放物理内存空间。在虚拟内存中,数据被分为多个页面,每个页面大小默认 32 字节。显然,虚拟内存可以节省内存,但也带来一定的性能损失。持久化AOF采用写后日志的方式,先执行命令,再将操作日志以文本形式追加到文件中。为什么采用写后日志?(Mysql是采用写前日志) 避免出现记录错误命令的情况,并且 AOF 写日志也是在主线程中进行的。优缺点 优点 故障不丢失,写回策略默认是每秒一次,保证故障最多丢失一秒的数据 缺点 文件体积较大 恢复速度更慢 写回(硬盘)策略 Always(总是),每次写操作命令执行完成后,同步将 AOF 日志数据写回硬盘。本质是每次写操作都执行 fsync() 函数。 EverySec(每秒),每次写操作命令执行完成后,将命令写入到 AOF 的内核缓冲区,每秒将缓冲区的内容写回到硬盘。本质是每秒创建一个异步任务执行 fsync() 函数。 No,意味着交给操作系统控制写回的时机。本质是不执行 fsync() 函数。重写(BGREWRITE)当 AOF文件太大时,Redis fork出一个子进程调用 bgrewriteaof 方法重写一个新的文件,比如increase(1)和increase(1),会被合并成set(2)。也用到了 写时复制。RDB将某一时刻的内存数据以二进制的形式写入磁盘。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。save 和 bgsave 两条命令触发 RDB 持久化操作。save 命令在主线程中执行,会导致阻塞。而 bgsave 创建一个子线程,避免阻塞,是默认的 RDB 的默认配置。并且 bgsave 允许执行快照命令期间修改数据,因为子线程会创建一个修改后的副本再写入(写时复制)。优缺点 优点 体积更小,二进制文件 恢复更快,适合备份 性能更高,只需要fork子进程 缺点 故障丢失,每隔一段时间(最少五分钟)持久化一次,如果发生故障,意味着这段时间内的所有数据都丢失 耐久性差,数据量大的时候,fork会很耗时 Copy On Write(写时复制) 原理:fork() 后的子进程与父进程共享内存空间,如果是读取数据,相安无事;如果是写数据,会检测到内存页是只读然后触发中断最后复制一份数据再做修改。 好处:减少不必要的资源分配;减少分配和复制大量资源时带来的瞬间延时。 缺点:如果父子进程都在进行大量的写操作,会产生大量的分页错误。 混合持久化结合 AOF 和 RDB 两种持久化方式。混合持久化只发生于 AOF 重写过程。 重写后的新 AOF 文件前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据。缺点:不再是 AOF 格式的文件,可读性差。数据结构Redis 7之后使用 Listpack(紧凑列表) 数据结构代替 Ziplist。 String: Simple Dynamic String(SDS) List: Quicklist(3.2之后);LinkedList、Ziplist(3.2之前) Hash: HashTable(哈希表)、ZipList。当同时满足以下两个条件的时候,Hash 对象使用 ZipList 编码否则使用 Hashtable:a、每个Field-Value的最大长度小于等于64字节;b、Field-Value最大数量小于512个。 Set: HashTable、Intset。当同时满足以下两个条件的时候,Set 对象使用 Intset 编码否则使用 Hashtable:a、结合对象保存的所有元素都是整数值;b、集合对象保存的元素数量不超过512个。 Sorted set: ZipList(压缩列表)、SkipList(跳表)。当同时满足以下两个条件的时候,Sorted set 对象使用 ZipList 编码否则使用 SkipList:a、每个元素空间小于64字节;b、集合中元素数量小于128个。 Stream: 借鉴 Kafka 的设计,是一个支持多播且可持久化的消息队列。与 PUB/SUB 模式相比,无法持久化。与基于 List LPUSH 和 BRPOP 或 Sorted set 相比,不支持多播分组消费。 Bitmap: 位图 Hyperloglog: 基数(不重复的元素)统计,但是会存在一定误差。可用于比如注册 IP 数、每日访问 IP 数、页面实时UV、在线用户数,共同好友数等允许一定容错的业务场景。 Geospatial index: 实现两个位置距离的计算、获取指定位置附近的元素等功能。消息通信模式:pub/sub,不支持消息持久化 基于频道(channel) 基于模式(pattern)注意事项: 客户端需要及时消费,否则会自动断开连接或丢数据 连接断开后需要重新连接,否则无法收到消息 该消息模式不是一种可靠的消息系统。当出现客户端连接退出,或者极端情况下服务端发生主备切换时,未消费的消息会被丢弃。Hash 数据结构下的 rehash 过程: 为 ht[1] 分配空间,作为 rehash 后的哈希表。 构造一个索引技术器变量 rehashidx 并初始化为零,表示 rehash 正在执行,否则 rehashidx 为 -1 表示没在进行。 在 rehash 期间,删改查操作都是先在旧表上操作,并把旧表的数据迁移到新表;新增的操作直接在新表上新增,并且 rehashidx 自增。 除此之外,没有增删改查的操作时,还会有一个定时任务周期性(每100ms触发一次)的迁移数据好处:将一次性的大批量拷贝分摊到多个请求中。缓冲区共有三个缓冲区: 客户端输入/输出缓冲区为了解决客户端和服务器端的请求发送和处理速度不匹配所设置的。输入缓冲区会先暂存客户端发送过来的命令,Redis 主线程从输入缓冲区中读取命令,进行处理。当 Redis 主线程处理完数据后,会把结果写入到输出缓冲区,再从输出缓冲区返回给客户端。缓冲区溢出的可能情况:BigKey,缓冲区大小不合理。 复制缓冲区,复制积压缓冲区用于Redis主从节点之间复制时使用的。由于主从节点间的数据复制包括全量复制和增量复制两种。因此也分为复制缓冲区和复制积压缓冲区两种。 复制缓冲区是 Redis 在全量复制过程中,主节点在向从节点传输 RDB 文件的同时,会继续接收客户端发送的写命令请求。这些写命令就会先保存在复制缓冲区中,等 RDB 文件传输完成后,再发送给从节点去执行。主节点上会为每个从节点都维护一个复制缓冲区,来保证主从节点间的数据同步。 复制积压缓冲区是一个大小有限的环形缓冲区。当服务器断线重连后,复制积压缓冲区的内容会被发送到从节点,当主节点把复制积压缓冲区写满后,会覆盖缓冲区中的旧命令数据,会造成主从节点的数据不一致。在命令传播阶段出现断网的情况,或者网络抖动时会导致连接断开,此时主节点会向复制积压缓冲区写数据。 AOF 缓冲区,AOF 重写缓冲区 AOF 缓冲区:Redis在AOF持久化的时候,会先把命令写入到AOF缓冲区,然后通过回写策略(always、everysec、no)来写入硬盘AOF文件。 AOF 重写缓冲区:当需要进行AOF重写时,主进程会fork一个子进程进行AOF重写,此时主进程还要继续接收客户端传来的指令,此时这些指令就会存放在AOF重写缓冲区中;当AOF重写完成后,将AOF重写缓冲区中的指令追加到aof文件中,并将原来的aof文件替换,来保证数据的一致性。(与主从复制中的复制缓冲区类似) 单线程与多线程redis 4.0 后部分命令开始使用多线程。为什么使用单线程? Redis 的瓶颈是内存和带宽,而不是 CPU。什么是多路IO复用:一个服务端进程可以同时处理多个客户端连接。用于AOF持久化任务和处理客户端请求。其实现函数有: select:数据结构是bitmap,采用轮询,限制连接数为1024个(最大文件描述符数量) poll:数据结构是数组,解决了select的个数限制,但依旧是轮询 epoll:数据结构是红黑树,使用回调的方式,解决了个数限制,也解决了轮询方式 边缘触发(Edge Trigger,ET),更适合高流量或发送大文件的场景,例如 nginx 水平触发(Level Trigger,LT),默认,能保证事件一定被处理。 为什么 Redis 又支持多线程了? 使用多线程仅在部分非阻塞的删除操作上面,通过多线程非阻塞释放内存,减少对主线程的阻塞,提高执行效率。高可用(主从,哨兵,集群)主从模式主节点可以读、写,从节点只能读 slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照,并使用复制缓冲区记录保存快照这段时间内执行的写命令 master将保存的快照文件发送给slave,并继续在复制缓冲区记录执行的写命令 slave接收到快照文件后,加载快照文件,载入数据 master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化 此后master每次执行一个写命令都会同步发送给slave,同时也会写入复制积压缓冲区,保持master与slave之间数据的一致性,如果主从之间断开连接,那么重连后会根据该缓冲区中的偏移量将断开连接这段时间的数据修改写入从节点。请求转发主从模式下,服务端并不做转发处理。而要实现读写分离的功能,需要客户端自行处理了。比如要自行定位master节点,然后将写请求发送过去,读请求则可以做负载均衡处理。哨兵模式哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。哨兵每2秒向pubsub频道接收和发送hello信息,实现哨兵之间的通信。哨兵每1秒向所有主从节点和其他哨兵发送PING命令来判断其是否正常运行。哨兵每10秒向主从节点发送INFO命令来获取节点信息(被标记为客观下线后每1秒一次)。如果主节点或者从节点没有在规定的时间内响应哨兵的 PING 命令,哨兵就会将它们标记为「主观下线」。其他哨兵会尝试与节点建立连接,如果大于配置文件中设定的值,则认定节点「客观下线」。集群模式Redis 集群无法保证强一致性,选择了高可用和分区容忍,即AP。将不同的数据分布在不同的节点中,分布的规则采用一致性哈希算法。指将数据映射到一个首尾相连的哈希环上,如果增加或者移除一个节点,仅影响该节点在哈希环上顺时针相邻的后继节点,其它数据也不会受到影响。共有16384个哈希槽。一致性哈希算法存在节点分布不均匀的问题。解决方法:引入虚拟节点,将数据映射至虚拟节点而不是真实节点,虚拟节点和真实节点又存在一层映射关系。请求转发在集群模式下,对于读/写请求,并非是服务端转发请求,而是服务端返回转移指令,通知客户端数据所在节点,并让客户端重新发起请求事务Redis 有事务但没有回滚,因为Redis认为事务的失败都是使用者造成的。缓存雪崩、击穿、穿透 缓存雪崩:大量缓存失效或 Redis 宕机时,大量请求访问到数据库,导致数据库压力骤增。 解决方法: 大量失效:均匀设置过期时间或缓存预热 宕机:设置 Redis 高可用集群,熔断或限流 缓存击穿:热点数据过期,大量请求访问到数据库 解决方法:热点数据不设置过期时间 缓存穿透:访问的数据既不在缓存中也不在数据库中,被黑客攻击。 解决方法:使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在。 缓存策略 Cache-Aside Pattern 旁路缓存模式;读,先从缓存读,未命中从数据库读;写,先写数据库,再写缓存 Read/Write-Through Pattern 读/写穿透模式;在旁路缓存模式的基础上封装一层缓存服务,读写操作都调用该缓存服务。读。先从缓存读,未命中数据库读并写到缓存中再返回。 Write-Behind Pattern 异步回写模式;与读/写穿透模式类似,缓存系统只同步写缓存,异步批量写数据库旁路缓存下的一致性问题不存在实时一致性,只能保证最终一致性。首选 写策略:先更新数据库,再删除缓存。 读策略:先读缓存中数据,如果缓存中没有,则读数据库的数据并且写入缓存中。如果对缓存命中率有要求,可以采用先更新数据库再更新缓存 + 分布式锁的方案。分布式锁保证同一时间只运行一个请求更新缓存。最终一致性方案:主要考虑以下两点: 第一个操作成功,但第二个失败会有什么问题 高并发场景下数据会不会不一致 如果采用先删除缓存,再更新数据库如何避免出现脏数据?延迟双删:先删除缓存,更新数据库,睡眠,再次删除缓存。睡眠的时间一般是一次查询的耗时 + 几百毫秒。第二次的删除可以清除掉因并发导致的缓存脏数据。#删除缓存redis.delKey(X)#更新数据库db.update(X)#睡眠Thread.sleep(N)#再删除缓存redis.delKey(X)缓存删除重试:为了保证缓存删除成功,需要在缓存失败时增加重试机制。可以借助消息队列,将删除失败的数据进行异步重试。BinLog缓存删除方案:BinLog存储了对数据库的更改操作日志记录,通过订阅该日志(如:canal),获取需要更新缓存的key和数据。过期删除策略和内存淘汰策略过期删除策略 定时删除策略:在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。 优点:内存可以最快的被释放。 缺点:删除过期key会占用 CPU 时间,对 CPU 不友好。 惰性删除策略:不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。 优点:只有在访问的时候检查是否过期,对 CPU 友好。 缺点:过期的 key 没被删除,对内存不友好。 定期删除策略:每隔一段时间随机从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。 优点:减少删除操作对 CPU 的影响,也能删除一部分过期的数据。 缺点:难以确定定期删除的频率。Redis的删除策略是惰性删除+定期删除。定期删除如果检查到过期key的数据超过一定百分比,循环再次检查并删除。内存淘汰策略 不进行数据淘汰(默认的内存淘汰策略),写入时禁止写入并报错。 进行数据淘汰 在设置了过期时间的数据中进行淘汰 volatile-random: 随机淘汰设置了过期时间的任意键值; volatile-ttl:优先淘汰更早过期的键值。 volatile-lru:淘汰所有设置了过期时间的键值中,最久未使用的键值; volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值; 在所有数据范围内进行淘汰 allkeys-random:随机淘汰任意键值; allkeys-lru:淘汰整个键值中最久未使用的键值; allkeys-lfu:淘汰整个键值中最少使用的键值。 近似 LRU 算法出于节省内存的考虑,Redis 的 LRU 算法并非完整的实现,是一个基于哈希表的近似 LRU 算法,通过对少量键进行取样,然后回收其中的最久未被访问的键。通过调整每次回收时的采样数量 maxmemory-samples,可以实现调整算法的精度。RedLock 红锁红锁是一种分布式锁。在 Redis 单独节点的基础上,RedLock 使用了多个独立的 Redis 的 Master 实例(通常建议是奇数个,比如 5 个),共同协作来提供更强健的分布式锁服务。特点 互斥性:任意时刻,只有一个客户端能持有锁 防止死锁:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。 可重入性:一个线程如果获取了锁之后,可以再次对其请求加锁。实现思路RedLock 是对集群的每个节点进行加锁,如果大多数节点(N/2+1)加锁成功,则才会认为加锁成功。这样即使集群中有某个节点挂掉了,因为大部分集群节点都加锁成功了,所以分布式锁还是可以继续使用的。分布式锁超时了怎么办答:设置看门狗(定时器)大 Key/ 热 Key大 Key指 Redis 的 Key 对应的 Value 过大,并不是指 Key 过大。危害 性能问题:读取大 Key 消耗更多的网络带宽和处理时间 引发网络阻塞:如果一个 Key 的大小是 1MB,每秒访问量是 1000,那么每秒会产生 1000 MB 的流量 内存分配不均 持久化问题:持久化变得更耗时解决方案 拆分大 Key,将数据分散到多个小 Key 中 定期清理或设置过期时间 数据压缩 选取合适的数据结构如何找到大 Key 使用 redis-cli --bigkeys 命令,不建议 使用 scan 命令 使用 RdbTools 工具如何删除大 Key 系统低峰期使用 del 命令(会造成线程阻塞) 使用 scan 命令(会造成线程阻塞) 使用 unlink 异步删除热 Key指短时间内被频繁访问的数据危害 可能会缓存击穿,使存储层访问量激增 负载不均衡,热 Key 可能导致某些 Redis 节点负载过高 性能瓶颈,占用大量 CPU 资源,影响其他请求解决方案 读写分离(最重要) 对热点数据分片,分散到不同的 Redis 实例,提升吞吐量如何找到热 Key 使用 monitor 命令 使用 redis-cli --hotkeys 命令,不建议为什么100万QPS的热key请求可能会影响1000万QPS的Redis实例 100万个请求在单线程的模型下会串行处理,阻塞其他请求 热 key 的能承载的QPS是由单个 key 的处理速度决定的 1000万QPS可能是集群模式下能承载的请求量" }, { "title": "Windows修改注册表脚本", "url": "/posts/reg-scripts-in-windows/", "categories": "Tutorial, Windows 10", "tags": "skills, windows 10", "date": "2022-09-28 21:09:55 +0800", "snippet": " 鼠标右键新建菜单栏中增加 New Markdown File:Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\\.md]@=\"Typora.md\"\"Content Type\"=\"text/markdown\"\"PerceivedType\"=\"text\"[HKEY_CLASSES_ROOT\\.md\\ShellNew]\"NullFil...", "content": " 鼠标右键新建菜单栏中增加 New Markdown File:Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\\.md]@=\"Typora.md\"\"Content Type\"=\"text/markdown\"\"PerceivedType\"=\"text\"[HKEY_CLASSES_ROOT\\.md\\ShellNew]\"NullFile\"=\"\" 鼠标右键菜单栏新增 Open with Code,需要根据实际情况修改路径:Windows Registry Editor Version 5.00; Open files[HKEY_CLASSES_ROOT\\*\\shell\\Open with VS Code]@=\"Open with Code\"\"Icon\"=\"C:\\\\Program Files\\\\Microsoft VS Code\\\\Code.exe,0\"[HKEY_CLASSES_ROOT\\*\\shell\\Open with VS Code\\command]@=\"\\\"C:\\\\Program Files\\\\Microsoft VS Code\\\\Code.exe\\\" \\\"%1\\\"\"; This will make it appear when you right click ON a folder; The \"Icon\" line can be removed if you don't want the icon to appear[HKEY_CLASSES_ROOT\\Directory\\shell\\vscode]@=\"Open with Code\"\"Icon\"=\"\\\"C:\\\\Program Files\\\\Microsoft VS Code\\\\Code.exe\\\",0\"[HKEY_CLASSES_ROOT\\Directory\\shell\\vscode\\command]@=\"\\\"C:\\\\Program Files\\\\Microsoft VS Code\\\\Code.exe\\\" \\\"%1\\\"\"; This will make it appear when you right click INSIDE a folder; The \"Icon\" line can be removed if you don't want the icon to appear[HKEY_CLASSES_ROOT\\Directory\\Background\\shell\\vscode]@=\"Open with Code\"\"Icon\"=\"\\\"C:\\\\Program Files\\\\Microsoft VS Code\\\\Code.exe\\\",0\"[HKEY_CLASSES_ROOT\\Directory\\Background\\shell\\vscode\\command]@=\"\\\"C:\\\\Program Files\\\\Microsoft VS Code\\\\Code.exe\\\" \\\"%V\\\"\"" }, { "title": "线段树(Java实现)", "url": "/posts/segment-tree/", "categories": "Algorithm", "tags": "segment tree", "date": "2022-07-05 14:13:29 +0800", "snippet": "线段树class NumArray { int[] segmentTree; int n; public NumArray(int[] nums) { n = nums.length; segmentTree = new int [2 * n]; System.arraycopy(nums, 0, segmentTree, n, n...", "content": "线段树class NumArray { int[] segmentTree; int n; public NumArray(int[] nums) { n = nums.length; segmentTree = new int [2 * n]; System.arraycopy(nums, 0, segmentTree, n, n); for(int i = n - 1; i > 0; i--){ segmentTree[i] = segmentTree[2 * i] + segmentTree[2 * i + 1]; } } public void update(int index, int val) { index += n; segmentTree[index] = val; index /= 2; while(index != 0){ segmentTree[index] = segmentTree[2 * index] + segmentTree[2 * index + 1]; index /= 2; } } public int sumRange(int left, int right) { left += n; right += n; int sum = 0; while(left <= right){ if(left % 2 == 1){ sum += segmentTree[left]; left ++; } if(right % 2 == 0){ sum += segmentTree[right]; right--; } left /= 2; right /= 2; } return sum; }}" }, { "title": "MySQL知识点汇总", "url": "/posts/mysql/", "categories": "Backend, MySQL", "tags": "", "date": "2022-06-22 13:33:24 +0800", "snippet": "单机QPS单机 QPS 为 4k 左右。MySQL select语句执行 prepare 阶段,检查查询语句中的表活字段是否存在,将 * 拓展为表上的所有列。 optimize 阶段,优化器决定使用哪个索引。 execute 阶段,执行器,索引下推。表空间文件结构组成:段(默认256MB) -> 区(默认1MB) -> 页(默认16KB) -> 行页:InnoDB的数...", "content": "单机QPS单机 QPS 为 4k 左右。MySQL select语句执行 prepare 阶段,检查查询语句中的表活字段是否存在,将 * 拓展为表上的所有列。 optimize 阶段,优化器决定使用哪个索引。 execute 阶段,执行器,索引下推。表空间文件结构组成:段(默认256MB) -> 区(默认1MB) -> 页(默认16KB) -> 行页:InnoDB的数据按页为单位读写,默认每个页大小为16KB,意味着一次最少从磁盘读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。表中的记录存储在数据页中。B+树的每个节点都是一个数据页。区:数据量大的时候,索引分配空间则是按区为单位分配,每个区的大小为1MB,对于16KB的页来说,连续(物理连续)64个页会被划分到一个区,这样,B+树中节点所构成的链表中相邻页的物理位置也相邻,便能顺序IO。段:段是数据库中的分配单位,不同类型的数据库对象以不同的段形式存在。当创建数据表、索引的时候,就会相应创建对应的段,比如创建一张表时会创建一个表段,创建一个索引时会创建一个索引段。在段中不要求区与区之间是相邻的。 索引段:存放B+树非叶子结点的区的集合 数据段:存放B+树叶子结点的区的集合 回滚段:存放的是回滚数据的区的集合,MVCC利用回滚段实现了多版本查询数据区存在的意义数据页都是以双向链表的形式存在,如果以页分配存储空间,双向链表相邻的两个页之间的物理位置可能距离非常远,会产生随机 IO,影响性能段存在的意义对于范围查询,本质是对B+树叶子节点中的记录进行顺序扫描,但如果不区分叶子节点和非叶子节点,把节点代表的页都申请到区中,范围查找效率大打折扣。一个索引会产生两个段,一个叶子节点段(数据段)和一个非叶子节点段(索引段)。行格式 Redundant Compact,一条完整的记录分为“记录的额外信息”和“记录的真实数据”,额外信息中又分为变长字段长度列表、NULL值列表和记录头数据。变长字段长度列表和NULL值列表都是倒序保存,并且不是必须的,只要没有变长字段或NULL值字段即可。8个字段值可以为NULL,那么NULL值列表空间为1字节,9字段则2字节。 Dynamic CompressedVarchar(n) n最大取多少?MySQL 规定除了 TEXT、BLOBs 这种大对象类型之外,其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过 65535 个字节。注意,是一行数据的最大字节数 65535,其实是包含「变长字段长度列表」和 「NULL 值列表」所占用的字节数的。行格式溢出后,数据存放到溢出页。text 各个类型对比 TINYTEXT 最大长度是 255 (2^8 – 1) 个字符。 TEXT 最大长度是 65535 (2^16 – 1) 个字符。 MEDIUMTEXT 最大长度是 16777215 (2^24 – 1) 个字符。 LONGTEXT 最大长度是 4294967295 (2^32 – 1) 个字符 Varchar 对每个英文(ASCII)字符都占用2个字节,对一个汉字也只占用两个字节 Char 对英文(ASCII)字符占用1个字节,对一个汉字占用2个字节数据的检索效率是:char>varchar>textchar: 定长,会用空格补齐varchar:变长B树和B+树 B树(多路的平衡搜索树),节点可以存储多个数据,并且每个节点存放索引和数据,一个节点的关键字等于该节点子节点个数减一。查询的最好情况是O(1),一般高度为3。 B+树,非叶子节点关键字个数等于子节点个数 叶子节点保存数据(数据页),非叶子节点保存索引,查询固定为O(logn),并且由于这个特点更适合外部存储。 叶子节点有指向下一个叶子节点的指针,双向链表,可顺序访问,因此区间访问的性能较好。 存在重复元素,因为非叶子结点不保存数据。 与红黑树的比较,B+树的优势: 更少的查询次数,因为B+树的树高一般小 利用计算机的预读特性,因为B+树具有类似链表的特点,因此相邻的节点也能被预先载入为什么不使用 B 树:B+ 树的非叶子节点只存储索引,单个节点可以存储更多的索引,计算机一次性加载更多的索引数据到内存中。并且B树不适合范围查找。为什么不使用跳表:MySQL 一次数据页加载都需要一次磁盘IO。并且磁盘IO的次数和树高有关系,又因为跳表的高度一般比B+树高,所以查询速度会大大降低,因此不适合。为什么 Redis 使用了跳表:因为 Redis 不存在磁盘IO。重点在于磁盘IO次数。MySQL索引给表添加索引时,是会对表加锁,因此在生产环境中不能直接添加索引。InnoDB 存储引擎默认会创建一个主键索引,也就是聚簇索引,其它索引都属于二级索引。当没有显式定义主键索引时,MySQL 会选择第一个唯一索引,并自动设置非空约束,当作聚簇索引。数据结构维度: B+树索引,适合范围查询,复杂度为O(logn)。 哈希索引,能以O(1)时间进行查找,但失去有序性,无法用于排序和分组,只支持精确查找(等值查找)。 全文索引,使用倒排索引实现,记录关键词到所在文档的映射,一般在text, varchar上创建。 R-Trees索引,MyISAM支持的索引类型,和空间地理数据有关。物理存储维度(InnoDB): 聚簇索引,叶节点存放一整行记录。一个表只能拥有一个聚簇索引。并非完全等价于主键索引。 非聚簇索引(二级索引),叶节点只存放主键信息,所以一般需要回表查询。逻辑维度 主键索引,不允许空值 普通索引,没任何限制 联合索引,多个字段创建的索引,使用时遵循左前缀原则。 唯一索引,索引列中的值必须是唯一的,但可以为空值。 空间索引索引失效(触发全盘扫描)主要看是否回表或者是查询的数据量过大一定失效: 索引列参与运算 索引列使用函数 两列做比较 类型隐式转换可能失效: 不满足最左匹配(覆盖索引,索引下推,索引跳跃扫描) 尽可能明确查询列,而不是select *,即使不满足最左匹配(可以用于性能优化) 错误的like使用(当模糊匹配的占位符位于条件的首部,并且要看数据库中的字段) 错误的or使用(切记两个条件都要添加索引,否则会导致索引失效,or两边同时使用 < 和 >,也会失效) 错误的 <> 和 != 使用(查询结果集占比较大时索引会失效) is not null(is null 走索引) not in(条件列是主键时走索引) not exists orderby(部分会失效) 范围过大的 between and索引跳跃扫描最左缀原则可以通过跳跃扫描的方式打破,当第一列索引的唯一值较少时,即使where条件没有第一列索引,查询的时候也可以用到联合索引。覆盖索引指查询列被所建的索引覆盖,覆盖索引是 select 的数据列只需要从索引中就能取到,不必回表。例如对于联合索引(col1,col2,col3),查询语句SELECT col1,col2,col3 FROM test WHERE col2=2哪些情况下不适合建索引 数据量少 更新频繁 区分度低 where, groupby, orderby 后没有使用到的字段 联合索引中的索引索引下推指将部分上层(服务层)负责index filter的事情,交给了下层(引擎层)去处理。它能减少二级索引再查询时的回表查询次数,提高查询效率。只适用于二级索引。EXPLAIN 命令的extra一栏中有Using index condition,表明使用了索引下推举例:select * from table1 where b like '3%' and c = 35.6 之前: 先通过 联合索引 查询到 开头为 3 的数据 然后拿到主键 然后通过主键去主键索引里面去回表查询 二级索引里面查询出来几个 3 开头的就回表几次5.6 之后: 先通过 二级索引 查询到开头为 3 的数据 然后 再找到 c = 3 的数据进行过滤 之后拿到主键 通过主键进行回表查询,减少了回表次数建索引的原则 选择唯一性索引 为经常需要orderby,groupby操作的字段建立索引 经常作为查询条件的字段建立索引 限制索引数量 尽量使用数据量少的索引 尽量使用最左前缀匹配原则 使用区分度高的列作为索引 扩展索引而不是新建索引强制索引深度分页有分页需求时,一般会用limit实现,但是当偏移量特别大的时候,查询效率就变得低下。select * from table limit 10 和 select * from table limit 10000, 10 的查询时间是不一样的,后者是查询出 10010 条,再取最后 10 条记录。本质原因就是:偏移量(offset)越大,mysql就会扫描越多的行,然后再抛弃掉。这样就导致查询性能的下降。解决方案: 标签记录法,使用 select * from table where id > 10000 limit 10, 范围查询,使用 select * from table where id between 100000 and 100010 order by id desc; 使用子查询,把条件转移到主键索引树,使用 select * from table where id >= (select a.id from table a where a.update_time >= xxx limit 100000, 1) limit 10 INNER JOIN 延迟关联,与上述的子查询思路类似,将条件转移到主键索引树 尽量满足索引覆盖,效率低下是因为回表次数过多,如果可以满足索引覆盖,则就不需要回表ACID 原则 Atomicity(原子性),每次事务是原子的,事务包含的所有操作要么全部成功,要么全部不执行。一旦有操作失败,则需要回退状态到执行事务之前;通过 undo log 来保证。 Consistency(一致性),数据库的状态在事务执行前后的状态是一致的和完整的,无中间状态。即只能处于成功事务提交后的状态;例如转账前后,两用户的余额总和一定是不变的。通过原子性 + 隔离性 + 持久性保证的。 Isolation(隔离性),各种事务可以并发执行,但彼此之间互相不影响。按照标准 SQL 规范,从弱到强可以分为读未提交、读已提交、可重复读和串行化四种隔离等级;通过 MVCC 或锁机制保证的。 Durability(持久性),状态的改变是持久的,不会失效。一旦某个事务提交,则它造成的状态变更就是永久性的,即便系统故障也不会丢失。通过 redo log 来保证。事务的隔离MVCC 用于解决脏读和不可重复读的问题,在读已提交和可重复读两种隔离级别生效。读已提交是每次 select 都会重新生成一次 Read View,而可重复读是一次事务只会创建一次且在第一次查询时创建 Read View。MVCC: Read view + undo log + 两个隐藏字段,基于乐观锁的理论为了解决读写冲突,可以在读已提交和可重复读的隔离级别下使用。 Read View 是一个事务快照,保存当前事务开启时所有活跃的事务列表。通过与版本链的配合可以实现对数据的 “快照读”。其中也有四个字段,分别是creater_trx_id, m_ids, min_trx_id, max_trx_id。 undo log 是存放更新前的数据(快照),保存了历史快照 隐藏字段:row_trx_id 和 roll_pointer,前者表示更新行数据的事务id,后者是上一次修改之前保存在 undo log 中的记录位置(指针),使每行记录变化前后形成了一条版本链MVCC(Multiversion Concurrency Control) + 间隙锁在RR的隔离级别下能解决大部分幻读情况 仅快照读的情况下(普通 select 语句),通过 MVCC 的方式,在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,多次查询获取的是同一个快照数据。 仅当前读的情况下(select … for update 等语句),通过 Next-Lock Key(临键锁),其他事务的插入操作会被阻塞。 查询条件不走索引,退化成表锁。 查询条件走普通索引,锁住查询条件附近的间隙,等于间隙锁 查询条件走唯一索引,只锁一条数据,等价于记录锁 解决不了的幻读情况:快照读和当前读发生在同一个事务中(更新、删除等操作本质也需要进行当前读) 事务A快照读,事务B新增数据并提交,接着事务A修改事务B新增的数据并快照读,此时发生幻读。 事务A快照读,事务B新增数据并提交,接着事务A当前读,此时发生幻读。 若要尽可能避免幻读现象的发生,尽量在开启时候后马上执行当前读。三种问题: 脏读,指读取未提交数据,事务的数据可能回滚,导致数据不一致。 不可重复读,指在同一事务内读到的数据是不一致的 幻读,指同一事务在查询的过程中,有另外一个事务对范围内新增了记录,导致范围查询的结果条数不一致的现象。SQL的四种隔离级别: 读未提交,指一个事务还没提交时,它做的变更就能被其他事务看到。可能出现脏读 读已提交,指一个事务提交之后,它做的变更才能被其他事务看到。可能出现不可重复读 可重复读,指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的。可能出现幻读,但使用MVCC + 间隙锁能解决大部分幻读现象,事务开始读操作的时候,不允许其他事务对读的数据做修改 串行化,对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;WAL 机制WAL(Write-ahead logging)指 MySQL 在执行写操作时先记录在日志中,后更新到磁盘中。WAL 核心:将随机写(磁盘的写操作是随机IO,耗性能)转变成顺序写和组提交机制,降低客户端延迟,提高吞吐量。三大日志binlog, redolog, undolog: Buffer Pool是MySQL进程管理的一块内存空间,有减少磁盘IO次数的作用。 redo log重做日志是InnoDB存储引擎的一种物理格式的日志,用来实现事务持久性,主要有两部分文件组成,在内存中的重做日志缓冲(redo log buffer)以及磁盘中的重做日志文件(redo log),(循环写,数据会被覆盖)。使用场景:崩溃恢复,在发生故障的时间点,尚有脏页未写入磁盘,在重启 mysql 服务的时候,根据 redo log 进行重做,从而达到事务的持久性这一特性。 undo log回滚日志是InnoDB存储引擎的一种逻辑格式的日志,记录的是数据的逻辑变化,保证的是数据库的原子性,比如一条insert语句对应的是一条delete的undo log,在发生事务错误时,就能回滚到事务之前的数据状态;MVCC,事务未提交前,undo log 保存了未提交的版本数据,作为旧版本的快照数据,类似于做备份。 binlog是MySQL Server层的一种逻辑格式的日志,用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。使用场景:主从复制 :采用异步复制方式,在 Master 端开启 binlog ,然后将 binlog 发送到各个 Slave 端, Slave 端重放 binlog 从而达到主从数据一致;数据恢复,通过使用 mysqlbinlog 工具再结合 binlog 文件,可以将数据恢复到过去的某一时刻。主从复制如果对数据一致性和可靠性要求较高,可以考虑使用半同步复制;如果对延迟和主服务器性能要求较高,可以继续使用异步复制,根据实际需求调整复制模式。异步复制 master服务器将数据的改变记录二进制binlog日志,当master上的数据发生改变时,则将其改变写入二进制日志中; slave服务器会在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/OThread请求master二进制事件 主节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中, 从节点将启动SQL线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致,最后I/OThread和SQLThread将进入睡眠状态,等待下一次被唤醒。半同步复制主服务器将数据修改操作记录到二进制日志,并等待至少一个slave服务器确认已接收到并应用了这些日志后才继续执行后续操作。BinlogBinlog 有三种格式: Row,记录操作语句对具体行的操作和操作前的整行信息。缺点是占用空间大,优点是保证数据安全 Statement,记录修改的 sql 语句。缺点是在mysql集群时的一些操作会导致数据不一致(例如 Now() 的时间不同)。 MixedBinlog 写入到磁盘的操作: write:从binglog cache写到 page cache。 fsync:将数据持久化到磁盘。Binlog 的持久化: sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync; sync_binlog=1 的时候,表示每次提交事务都会执行 fsync; sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。Redolog刷盘时机:事务进行中 当innodb_flush_log_at_trx_commit=0时,InnoDB会每秒钟将log buffer中的数据更新到磁盘中。但并不能完全保证每秒将数据更新到磁盘一次。因此,在实例崩溃恢复场景中,可能会出现丢失1秒钟的事务。 当innodb_flush_log_at_trx_commit=1时,InnoDB将在每次事务提交时将log buffer的数据更新到文件系统os buffer中,并调用文件系统的flush操作将数据缓存更新至磁盘中。此种方式下,数据库完全遵守ACID特性,安全性较高。 当innodb_flush_log_at_trx_commit=2时,InnoDB将在每次事务提交时将log buffer中的数据更新到文件系统缓存中,每秒钟将文件系统缓存中的数据更新到磁盘一次,该操作由操作系统调度。不能完全保证每秒更新磁盘一次,没有被更新到磁盘中的事务可能会因宕机而丢失。Crash-Safe 能力,两阶段提交前提:innodb_flush_log_at_trx_commit 设置为1,sync_binlog 设置大于0 先将数据修改写入redo log,并将其标记为 prepare 状态 将相应的 sql 语句写入 binlog commit阶段:将 redo log 标记为 commit 状态,然后根据 sync_binlog 参数的设置,决定是否将 binlog 刷盘如果发生崩溃,先检查 redo log 记录的事务操作是否为 commit 状态。 如果是 commit 状态说明没有数据丢失,判断下一个。 如果是 prepare 状态,检查 binlog 记录的对应事务操作(redo log 与 binlog 记录的事务操作有一个共同字段 XID,redo log 就是通过这个字段找到 binlog 中对应的事务的)是否完整(这点在前面 binlog 三种格式分析过,每种格式记录的事务结尾都有特定的标识),如果完整就将 redo log 设为 commit 状态,然后结束;不完整就回滚 redo log 的事务,结束。为什么需要两阶段提交? 以基本的事务为单位,redo log 在事务执行过程中可以不断写入,而 binlog 只有在提交事务时才写入,所以redo log 与 binlog 的写入时机不一样。为了解决两份日志之间的逻辑一致问题,将redo log的写入拆成了两个步骤 prepare 和 commit,即两阶段提交。如果只有 redo log 或者只有 binlog,那么事务就不需要两阶段提交。执行顺序 from on join where group by having select distinct order by limit每一步执行时都会产生一个虚拟表,都会被用作下一个步骤的输入。锁乐观锁、悲观锁 乐观锁:不能解决脏读问题 悲观锁:select … for update全局锁、表级锁、行锁 全局锁:对整个数据库实例加锁,处于只读状态,其命令是Flush tables with read lock(FTWRL),用于全库逻辑备份。 表级锁:开销小,加锁快,不会出现死锁,发生锁冲突的概率最高,并发度最低。适合以查询为主。 表锁,可以添加表级别的共享锁或排他锁,锁的粒度较粗。 元数据锁(MDL),CRUD时,数据库自动给表加上MDL读锁;变更表结构时,自动加MDL写锁。事务提交后才会释放MDL锁。 意向锁,在执行插入、更新、删除需要加独占锁之前,需要对表先加上意向独占锁,其目的是快速判断表里是否有记录被加锁。 AUTO-INC锁,自增锁,插入数据时获取该锁,插入语句完成后,释放锁。 行锁:开销大,加锁慢,会出现死锁,发生锁冲突的概率最低,并发读也高。只有InnoDB引擎支持。行锁依赖于索引,如果加锁操作没有使用到索引,那么会退化成表锁。 记录锁:锁定一条记录,存在 S 型记录锁和 X 型记录锁。加了 S 型记录锁后可以继续加 S 型记录锁,但不能加 X 型记录锁;加了 X 型记录锁后不能加 S 型或 X 型记录锁。存在于包含主键索引的唯一索引中。 间隙锁:锁定一个范围,前开后开,不包含记录本身,存在于可重复读和串行化这两种隔离级别,为了解决可重复读隔离级别下幻读的现象。插入意向锁是一种表明插入意向的间隙锁。 临键锁(next-key lock):加锁的基本单位,锁定一个范围,前开后闭,存在于非唯一索引中。 共享锁(读锁)、排他锁(写锁) 共享锁:读锁,其他事务可以读,但不能写,select lock in share mode 排他锁:写锁,其他事务不能读,也不能写,select for updatemysql 数据实时同步到Es常见的数据同步方案主要有以下三种: 同步调用。在实现增删改的同时,通过调用ES所在服务提供的接口。 异步调用。增删改服务和搜索服务分别通过MQ进行发送和监听消息,有效降低业务的耦合度,但较为依赖MQ的性能; binlog监听。给MySQL开启binlog功能,搜索服务基于canal监听binlog变化,但开启binlog会增加数据库负担、同时实现复杂度较高。但也有异步调用和binlog监听结合在一起的例子,用canal作slave节点。分库/分表分为 水平切分(又称为 Sharding) 和 垂直切分。切分策略 范围切分,例如每一千万条数据一张表 哈希切分,对分表键进行哈希运算,主流方法 映射表,将分表键和数据库表对映射关系记录在表上分布式ID建议使用雪花算法,其中序列号一直保持递增,不会因为时间戳的不同而归零,防止被猜测上下文ID。注入攻击通过客户端向应用程序输入数据来插入或注入一个 SQL 查询/操作。防止注入攻击 参数化查询 使用正则化验证输入 最小权限原则 字符串过滤关键字MyBatis # 和 $ 的区别 # 将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。很大程度防止sql注入 $ 将传入的数据直接显示生成在sql中。无法防止Sql注入。" }, { "title": "Basics of Java", "url": "/posts/java/", "categories": "Backend, Java", "tags": "java, backend", "date": "2022-06-21 14:23:14 +0800", "snippet": "基本知识三大特点:封装继承多态。语法糖:switch支持String、泛型、自动拆装箱、变长参数、枚举、内部类、条件编译、断言、数值下划线、for-each、try-with-resources、Lambda表达式装箱:Integer i = Integer.valueOf(10), 拆箱:int n = i.intValue()内存结构运行时数据区域包含线程私有的程序计数器、虚拟机栈、本地...", "content": "基本知识三大特点:封装继承多态。语法糖:switch支持String、泛型、自动拆装箱、变长参数、枚举、内部类、条件编译、断言、数值下划线、for-each、try-with-resources、Lambda表达式装箱:Integer i = Integer.valueOf(10), 拆箱:int n = i.intValue()内存结构运行时数据区域包含线程私有的程序计数器、虚拟机栈、本地方法栈,线程共享的堆(包含字符串常量池)和非运行时数据区的元空间(包含类常量池和运行时常量池)、直接内存。 程序计数器:与操作系统中的程序计数器类似,为了线程切换后能恢复到正确的执行位置,是唯一一个不会出现 OutOfMemoryError 的内存区域。 虚拟机栈:以帧为单位,帧由局部变量表、操作数栈、动态链接、方法返回地址组成。每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。 局部变量表:主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置) 操作数栈:用于存放方法执行过程中产生的中间计算结果,也存放计算过程中产生的临时变量。 动态链接:将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。与类加载中的解析类似。 方法返回地址:顾名思义。 本地方法栈:和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法(机器码)服务。 堆:唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。 字符串常量池:简单理解为用C++实现的默认固定大小为1009的HashTable。在每个VM中只有一份,存放的是字符串常量的引用值 。关于常量池中的String类型的数据,String#intern 的用法。 类常量池:每个java文件被编译成class文件后会有一项常量池,用于存放编译器生成的字面量和符号引用。在编译阶段,存放的是常量的符号引用。 运行时常量池:是在类加载完成后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析阶段,将符号引用替换成直接引用,与字符串常量池中的引用值保持一致。class常量池、字符串常量池和运行时常量池的区别内存模型(JMM)JMM 旨在提供一个统一的可参考的规范,屏蔽平台内存访问差异性。这个规范为读写共享变量时如何与内存交互提供了规则和保证。并发编程中,程序会因为 CPU 多级缓存或指令重排序等出现问题,因此需要一些规范要保证并发编程的可靠性。关键概念包括: 主内存:表示所有线程都可以访问的共享内存。线程不能直接读写主内存中的变量。 工作内存:每个线程都有自己的工作内存,线程的工作内存保存了该线程用到的变量和主内存共享变量的副本拷贝,线程对变量的操作都在工作内存中进行。当一个线程修改了自己工作内存中的变量时,它必须把这个变量的最新值写回到主内存中,以便其他线程可以看到这个最新的值。 共享变量:这些变量可以被多个线程访问。它们可以是实例变量或静态变量。必须存储在主内存中。JMM 为处理共享变量定义了三个特征(多线程中的概念): 可见性:当一个线程修改共享变量的值,其他线程能够立即知道被修改了。当变量被 volatile 修饰时,这个变量被修改后会立刻刷新到主内存,当其它线程需要读取该变量时,会去主内存中读取新值。但普通变量读取的仍是旧值。 原子性:一个操作是不可分割,不可中断的,一个线程在执行时不会被其他线程干扰。Synchronized 块之间的操作具有原子性 顺序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。volatile通过内存屏障来保证可见性的 保证可见性,但不保证原子性!只是确保将变量的更新操作通知到其他线程。不能一定能保证线程安全。 禁止指令重排,背景:为了提高性能,编译器和处理器常常会对指令重排。禁止指令重排避免了多线程环境下程序出现乱序执行的现象。happens-beforehappens-before原则定义如下: 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。重排序之后的执行结果与按照happens-before关系来执行的结果一致即可。as-if-serialAs-if-serial的意思是所有的语句都可以为了优化而被重排序,但是必须保证它们重排序后的结果和程序代码本身的应有结果是一致的。为保证as-if-serial语义,Java异常处理机制也会为重排序做一些特殊处理。八种内存交互操作内存屏障 LoadLoad 屏障:对于这样的语句Load1,LoadLoad,Load2。在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。 StoreStore屏障:对于这样的语句Store1, StoreStore, Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。 LoadStore 屏障:对于这样的语句Load1, LoadStore,Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。 StoreLoad 屏障:对于这样的语句Store1, StoreLoad,Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。在每个volatile读操作后插入LoadLoad屏障,在读操作后插入LoadStore屏障。在每个volatile写操作的前面插入一个StoreStore屏障,后面插入一个SotreLoad屏障。     类的生命周期 加载:通过类的全限定名(包名 + 类名)获取class文件的二进制字节流(通过类加载器来完成,其加载过程使用双亲委派模型),将其转化为方法区运行时的数据结构,最后在堆中实例化一个java.lang.Class对象,作为方法区中这个类的信息的入口。 连接 验证:确保被加载的类的正确性 准备:为类的静态变量分配内存并设为jvm的默认值(不同于下文的初值,基本类型为零,引用类型为null,final修饰的常量为设定的值),对于非静态的变量,则不会为它们分配内存。 解析:虚拟机将常量池中的符号引用替换为直接引用,主要针对类或接口,字段,类方法,方法类型等。举例:使用内存地址(直接引用)指向方法名(符号引用)代替方法名。 初始化:按照顺序自上而下运行类中的变量赋值语句和静态语句,如果有父类,则首先按照顺序运行父类中的变量赋值语句和静态语句在类的初始化阶段,只会初始化与类相关的静态赋值语句和静态语句。类变量(静态变量)在方法区分配内存,并设置初值。 使用:包括主动引用和被动引用。直接引用就会触发类的初始化,其中包括以下四种情况: 通过new关键字实例化对象、读取或设置类的静态变量、调用类的静态方法。 初始化子类的时候,会触发父类的初始化。 作为程序入口直接运行时(也就是直接调用main方法)。 通过反射方式执行以上三种行为。 卸载:需要同时满足以下三个条件:该类所有的实例都已经被回收,即Java堆中不存在该类的任何实例;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。单例对象不会被JVM垃圾回收,因为无法满足卸载的第一个条件,Java堆中会始终存在该单例的实例。类加载器线程上下文类加载器:破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器,例如SPI (Service Provider Interface)。SPI接口中的代码经常需要加载具体的实现类。SPI接口是Java核心库的一部分,由 启动类加载器(Bootstrap Classloader) 来加载,而实现类由 系统类加载器(AppClassLoader) 来加载。双亲委派机制概念:双亲委派机制是指当一个类加载器收到某个类加载请求时,该类加载器首先会把请求委派给父类加载器。每个类加载器都是如此,它会先委托父类加载器在自己的搜索范围内找不到对应的类时,该类加载器才会尝试自己去加载。Tomcat中的类加载器: Tomcat自身所使用的类加载器,会加载jre的lib包及tomcat的lib包的类,遵循双亲委派机制。加载顺序:(1).先从缓存中加载;(2).如果没有,则从JVM的Bootstrap类加载器加载;(3).如果没有,则从父类加载器加载,加载顺序是AppClassLoader、Common、Shared。(4).如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序); 每个Web应用程序用的,每个web应用程序都有自己专用的WebappClassLoader,优先加载/web-inf/lib下的jar中的class文件,这样就隔离了每个web应用程序的影响,不遵循双亲委派机制。加载顺序:(1).先从缓存中加载;(2).如果没有,则从JVM的Bootstrap类加载器加载;(3).如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序);(4).如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模式,所以加载顺序是AppClassLoader、Common、Shared。Java的SPI:SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。1.6 -> 1.7 -> 1.8: 1.6 -> 1.7: 字符串常量池从方法区(永久代)中移到堆中。原因: GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC。Java 程序中通常会有大量的被创建的字符串等待回收。 1.7 -> 1.8: 将运行时数据区方法区(永久代)移动到直接内存中,字符串常量池仍然在堆中。对象的创建过程 类加载检查 分配内存:指针碰撞或空闲列表 当多个对象并发争抢空间时,有两种解决办法:CAS 和本地线程分配缓冲(TLAB,默认方式) 初始化零值 设置对象头 执行构造方法对象的内存布局 对象头,两部分组成:存储自身运行时数据如哈希码,GC分代年龄;指向类的类型指针 实例数据,真正存储有效信息的部分 对齐填充,起占位作用对象的定位访问(针对JVM虚拟机栈中的局部变量表): 句柄,Java 堆中将会划分出一块内存来作为句柄池,局部变量表 reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与对象类型数据各自的具体地址信息。 直接指针,局部变量表里 reference 中存储的直接就是对象的地址。垃圾回收JVM触发GC时,首先会让所有的用户线程到达安全点SafePoint时阻塞,也就是STW,然后枚举根节点,即找到所有的GC Roots,通过可达性算法向下搜寻活跃对象,可达的对象就保留,不可达的对象就回收。可达性算法: 引用计数算法 可达性分析哪些对象可以作为GC Roots 虚拟机栈(栈帧中的本地变量表)中引用的对象 本地方法栈(Native 方法)中引用的对象 方法区中类静态属性引用的对象 方法区中常量引用的对象 所有被同步锁持有的对象内存分配和回收原则 对象优先在Eden区分配 大对象直接进入老年代 长期存活的进入老年代GC 分类 Partial GC Minor GC:只对新生代进行垃圾收集 Major GC:只对老年代进行垃圾收集 Mixed GC:整个新生代和部分老年代,只有G1收集器有 Full GC:整个Java堆和方法区Full GC 触发条件 老年代空间不足 创建大对象,Eden 区域放不下大对象,直接进入老年代 Minor GC 后,存活对象进入老年代 调用 system.gc(),系统会建议执行 FGC 空间分配担保机制失败空间担保策略空间担保策略是 JVM 的一种机制,发生 Minor GC 之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果小于,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;如果小于或者HandlePromotionFailure=false,则改为进行一次Full GC。垃圾收集算法 标记——清除算法:顾名思义,标记可回收的对象并清除。 标记——复制算法:将内存分成大小相同的两份,需要垃圾收集时,将存活的对象复制到另一份内存中,缺点:内存缩小为原来的一半。 标记——整理算法:标记可回收的对象,将存活的对象向一端移动,适合老年代这种垃圾回收频率不高的场景。 分代收集算法:在新生代和老年代不同的代用不同的垃圾收集算法。垃圾收集器 Serial 收集器,单线程、复制算法的新生代收集器 ParNew 收集器,多线程、复制算法的新生代收集器,老年代采用Serial Old收集器 Parallel Scavenge 收集器,多线程、复制算法的新生代收集器,高吞吐量。 Serial Old 收集器,单线程、标记-整理算法的老年代收集器。 Parallel Old 收集器,多线程、标记-整理算法的老年代收集器。 CMS(Concurrent Mark Sweep) 收集器,标记-清除算法,以获取最短回收停顿时间为目标的收集器。JDK14正式移除。 G1(Garbage-First) 收集器,标记-整理 + 复制算法,内存碎片的产生率大大降低。JDK9-JDK17的默认垃圾收集器。G1收集器内存模型垃圾收集器发展历程: JDK8 默认 Parallel Scavenge + Parallel Old JDK9 默认 G1 JDK11 提出ZGC JDK14 CMS 被移除CMS(Concurrent Mark Sweep)收集器:基于标记-清除算法,在Minor GC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收。在Full GC时不再暂停应用线程,而是使用若干个后台线程定期的对老年代空间进行扫描。 步骤: 初始标记(CMS initial mark):有STW,但速度很快 并发标记(CMS concurrent mark):从GC Roots的直接关联对象开始遍历整个对象图 重新标记(CMS remark):STW,为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录;采用三色标记算法和增量更新避免漏标 并发清除(CMS concurrent sweep):清理删除掉标记阶段判断的已经死亡的对象 缺点: 工作时会占用一部分CPU资源而导致用户程序变慢,降低总吞吐量 CMS无法清除浮动垃圾 基于标记-清除算法会导致内存碎片不断增多,在分配大对象时有可能会提前触发一次Full GC。 Garbage First(G1)收集器: 特点:引入分区的思路,弱化了分代的概念,并合理利用垃圾收集各个周期的资源。 内存结构:堆内存被切分为多个固定大小的区域,最小为1M,最大为32M,默认2048份。 内存分配:每个区域被标记为E、S、O和H,分别表示Eden,Survivor,Old,Humongous。Humongous区域是为了那些存储超过50%标准region大小的对象而设计的,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC。 执行特点: 并行与并发:使用多个CPU核缩短Stop The World停顿时间。 空间整合:从整体来看是基于“标记-整理”算法实现的收集器;从局部上来看是基于“标记-复制”算法实现的。 可观测的停顿:能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒。 G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。 步骤: 初始标记(Initial Marking),STW,标记一下 GC Roots 能直接关联到的对象。 并发标记(Concurrent Marking),从 GC Root 开始对堆中对象进行可达性分析,找到存活对象。 最终标记(Final Marking),为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,STW,但是可并行执行。 筛选回收(Live Data Counting and Evacuation),对各个Region中的回收价值和成本进行排序并制定回收计划。 JVM调优 选择合适的垃圾收集器:CPU单核,只能选择Serial;CPU多核,关注吞吐量 ,那么选择Parallel Scavenge(标记复制) + Paralle Old(标记整理)组合;CPU多核,关注用户停顿时间,JDK版本1.6或者1.7,内存小,那么选择CMS。CPU多核,关注用户停顿时间,JDK1.8及以上,JVM可用内存6G以上,那么选择G1。 调整内存大小,现象:垃圾收集频率非常频繁。 设置符合预期的停顿时间,现象:程序间接性的卡顿。参数:-XX:MaxGCPauseMillis 调整内存区域大小比率,现象:某一个区域的GC频繁,其他都正常。参数:-XX:SurvivorRatio=6, -XX:NewRatio=4 提升老年代年龄标准,现象:老年代频繁GC,每次回收的对象很多。参数:-XX:InitialTenuringThreshol=7 调整大对象的标准,现象:老年代频繁GC,每次回收的对象很多,而且单个对象的体积都比较大。参数:-XX:PretenureSizeThreshold=1000000//新生代可容纳的最大对象,大于则直接会分配到老年代,0代表没有限制。 调整GC的触发时机,现象:CMS收集器的情况下,G1 经常 Full GC,程序卡顿严重。 调整JVM本地内存(直接内存)大小,现象:堆内存空间充足,但是报OOM调优的一条经验总结: 将新对象预留在新生代,由于 Full GC 的成本远高于 Minor GC,因此尽可能将对象分配在新生代是明智的做法,实际项目中根据 GC 日志分析新生代空间大小分配是否合理,适当通过“-Xmn”命令调节新生代大小,最大限度降低新对象直接进入老年代的情况。Java IO BIO:同步阻塞IO,使用方便,但并发处理能力低 NIO:同步非阻塞IO,适用于连接数目多且连接比较短(轻操作)的架构 AIO:异步非阻塞IO,适用于连接数目多且连接比较长(重操作)的架构 阻塞/非阻塞,是对同一个线程来说。关注的是程序在等待调用结果(消息,返回值)时的状态。阻塞调用是指调用结果返回之前,当前线程会被挂起。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 同步/异步,针对调用者与被调用者,它们是线程之间的关系。同步操作,调用者需要等待被调用者返回结果,才会进行下一步操作;异步操作,调用者不需要等待被调用者返回调用,即可进行下一步操作,被调用者通常依靠事件、回调等机制来通知调用者结果NIO vs IO: IO是面向字节流的,NIO是面向缓冲区的 IO流是阻塞的,NIO流是不阻塞的 选择器:Java NIO的选择器允许一个单独的线程来监视多个输入通道IO多路复用(事件驱动):一个线程不断轮询多个socket的状态,只有当socket真正有读写状态时,借用当前线程或者使用线程池额外启动线程,调用实际的IO读写操作。Java NIO: 实际上也是一种多路复用的IO。 三大核心部分:Channel(通道) ,Buffer(缓冲区), Selector(选择器),Channel 负责传输(类比成铁路), Buffer 负责存取数据(类比成载着货物的火车) Channel是双向的,数据总是从通道读到缓冲区或者从缓冲区中写入通道内。 额外一个Selector线程,用于监听多个通道(Channel)的事件(比如:连接打开,数据到达),如果由事件发生,则获取事件并对每个事件进行相应的响应处理。并发编程一个 Java 程序的运行是 main 线程和多个其他线程同时运行。多线程synchronized 修饰普通方法/静态方法:通过 monitorenter 和 monitorexit 指令实现同步 修饰代码块:通过 ACC_SYNCHRONIZED 标记符实现同步底层都是通过对象头里 Mark Word 指向的对象监听器(Monitor)实现的,再底层是操作系统的互斥量(mutex)实现的同一时刻只能有一个线程运行 synchronized(lock) 内的代码块,其他线程会否则阻塞。PS:获取锁(运行代码块),释放锁(阻塞代码块) wait(): 获取锁并使线程进入等待状态 notify(): 随机唤醒一个在等待锁释放(wait())的线程 notifyAll(): 唤醒所有正在等待锁释放(wait())的线程,注意:notify() 或 notifyAll() 必须等到退出 synchronized() 或 wait() 后才释放锁!synchronized (obj) { // 条件不满足 while (condition does not hold) { obj.wait(); } // 执行满足条件的代码 obj.notifyAll();}orsynchronized (obj) { while (true){ // 条件满足 if (condition holds){ // 执行满足条件的代码 obj.notifyAll(); } obj.wait(); }}synchronized 可以用来修饰非静态方法(普通方法)、静态方法、代码块,锁住的是 class 对象的对象头!run() 和 start() 的区别: run(),调用普通方法,并不开启新线程。 start(),启动新线程,由JVM调用线程的run()方法。Synchronized 和 Lock 的区别: Lock 是一个接口,Synchronized 是一个关键字 Lock 需要手动释放锁,Synchronized 会自动释放锁 Lock 可以是公平锁/非公平锁,Synchronized 只能是非公平锁 Lock 有多种获取锁的方式,例如一定时间内获取不到会返回,Synchronized 获取不到锁一直会阻塞 性能方面,在竞争激烈的情况下,Lock 的性能会比 Synchronized 好RenentantLock: Lock:拿不到锁会一直等待 tryLock:去尝试获取锁,获取不到返回 false守护线程(Daemon Thread)Java 中的线程分为两种: 用户线程。 守护线程,其主要作用是为用户线程服务,比如垃圾回收线程,就是最典型的守护线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。也就是守护线程拥有自动结束自己生命周期的特性,而非守护线程不具备这个特点。集合框架: Collection Set List Queue Map线程安全的list: vector CopyOnWriteArrayList 读多写少的情况 Collections.synchronizedList() 读少写多的情况HashMap 知识点 HashMap: 乱序,数组+链表+红黑树,链表长度大于8转红黑树,红黑树节点个数小于6转链表。 LinkedHashMap: 按插入顺序排序 TreeMap: 按字典序排序,因为是按字典序排序的,所以键肯定不能为null,值可以为null IdentityHashMap:利用哈希表实现Map接口,不同的是,其比较键(或值)时,使用引用相等性代替对象相等性。 ConcurrentSkipListMap:基于跳表的线程安全的,实现快速查找的链表结构。HashMap面试题为什么计算哈希值采用低十六位和高十六位异或操作:计算数组下标是与操作,只有低 n 位进行与操作,高位不参与任何操作 -> 为了增大散列程度减小哈希碰撞,因此将高十六位参与进哈希值的计算。put() 的流程: hashcode的高十六位和低十六位进行异或运算 (n - 1) & hash 计算数组下标,当 n 为二次幂时,等价于取余操作((n - 1)& hash = hash % n)。 判断当前下标是否有元素,若有元素,使用尾插法。再根据链表长度判断是否需要转换成红黑树。扩容的过程: 将数组扩容成原数组的两倍 重新计算下标,将原 hash 值与 新数组长度减一(类似于 put 操作的第二步) 进行“与”操作 如果高位结果是0,桶位置不变 如果高位结果是1,桶位置是原位置 + 扩容长度Set: HashSet: 乱序,基于HashMap实现 LinkedHashSet: 按插入的顺序排序,基于LinkedHashMap实现 TreeSet: 按字典序排序,基于红黑树NULL key AND NULL value: key HashMap、LinkedHashMap 能使用 null key ConcurrentHashMap、TreeMap、HashTable 不能使用 null key。 value HashMap、LinkedHashMap、TreeMap 能使用 null value Hashtable、ConcurrentHashMap 不能使用 null value。 为什么ConcureentHashMap的key和value都不能为null: value不能为null:多线程情况下需要杜绝二义性。二义性是指当返回null时,无法判断是存在value为null的key还是不存在key从而返回null。因为单线程中可以使用 containsKey() 解决,但是多线程下无法使用同样的方法,因为可能会有其他线程进行其他操作影响返回值。ConcurrentHashMap JDK7 vs JDK8 JDK7: 数组 + 链表。先定位 Segment,再定位桶。底层结构是继承了ReentrantLock的Segment数组。可以看成是由线程安全的HashMap组成的一个map数组,数组的长度决定了支持的最大的并发量。 JDK8: 数组 + 链表 + 红黑树。可以直接定位到桶。链表中的元素超过8后,将链表结构转换成红黑树。通过对Node数组以CAS方式实现扩容和对Node数组的每个元素的synchronized保证ConcurrentHashMap整体的线程安全。HashTable速度慢:使用synchronized对整个对象加锁。JDK7:对整个数组进行分段(每段都是由若干个 hashEntry 对象组成的链表),每个分段都有一个 Segment 分段锁(继承 ReentrantLock 分段锁)。与hashtable相比,加锁粒度更细,但是初始化Segment数组长度后就无法扩容。ConcurrentHashMap 是一个二级哈希表。在一个总的哈希表下面,有若干个子哈希表。JDK8:对table数组的头节点加锁(哈希桶为空时,使用CAS将新的Node写入哈希桶的首节点;哈希桶不为空时,使用synchronized对首节点加锁接着添加节点) put:分两步,计算哈希值和一个死循环,循环步骤, first节点还没有初始化,所以初始化first节点,然后进入下次循环; first节点初始化了,但是为空,采用CAS方式把当前要put的值设置进这处,设置失败则进入下次循环,成功则保存成功,退出循环; 如果判断有其他线程正在对ConcurrentHashMap扩容(hash==MOVED),获取要去获取新的tab,进入下次循环; 找到了对应哈希桶的首节点f,直接对f加synchronized同步,然后判断f节点是链表结构还是红黑树结构,链表结构则遍历链表进行设置,红黑树则采用红黑树设置进去。设置成功后判断是否需要把链表结构转红黑树; ThreadLocal: 提供线程内的局部变量,在多线程的环境中保证各个线程内的变量不同。将数据封闭在线程中而避免使用同步,即线程封闭。一个ThreadLocal对象即是一个线程局部变量。jdbc连接池就是用ThreadLocal,典型例子。以下使四种方法: Object get():获取该线程局部变量的值。 void set(Object value):给该线程局部变量赋值。 protected Object initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。 public void remove():将当前线程局部变量的值删除。底层是 ThreadLocalMap 内部静态类,由数组实现,解决 hash 冲突的方式采用的是线性探测法存在内存泄漏的原因:由于 ThreadLocalMap 的生命周期跟 Thread 一样长,如果没有手动删除对应 key 就会导致该 key 的value 永远无法被访问,造成内存泄漏正确使用方法: 每次使用完ThreadLocal都调用它的remove()方法清除数据,防止 ThreadLocalMap 中 Entry 一直保持对 value 的强引用,导致 value 不能被回收 将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉线程池线程池的七个参数: 核心线程数(corePoolSize):核心线程数是线程池中保持活动状态的线程数。即使没有任务需要执行,核心线程也不会被回收。当有新任务提交时,如果核心线程都在忙碌,则会创建新的线程来处理任务。 最大线程数(maximumPoolSize):最大线程数是线程池中允许的最大线程数。当工作队列满了并且活动线程数达到最大线程数时,如果还有新任务提交,线程池将创建新的线程来处理任务。但是,超过最大线程数的线程可能会导致资源消耗过大。 空闲线程存活时间(keepAliveTime):空闲线程存活时间指的是非核心线程在没有任务执行时的最长存活时间。当线程池中的线程数超过核心线程数且空闲时间达到设定值时,多余的线程将被终止,直到线程池中的线程数不超过核心线程数。 时间单位(unit):时间单位是用于表示核心线程数和空闲线程存活时间的单位。常见的时间单位包括秒、毫秒、分钟等。 工作队列(workQueue):工作队列用于存储待执行的任务。当线程池中的线程都在忙碌时,新提交的任务将被添加到工作队列中等待执行。常见的工作队列类型有有界队列(如 ArrayBlockingQueue)和无界队列(如 LinkedBlockingQueue)等。 线程工厂(threadFactory):线程工厂用于创建新线程。线程工厂提供了创建线程的方法,可以自定义线程的名称、优先级等属性。 拒绝策略(rejectedExecutionHandler): 拒绝策略定义了当线程池无法接受新任务时的处理策略。当工作队列已满且线程池中的线程数已达到最大线程数时,新任务将被拒绝执行。常见的拒绝策略有丢弃、丢弃最旧的任务、抛出异常等。 AbortPolicy 拒绝任务并抛出一个异常 RejectedExecutionException DiscardPolicy 拒绝任务,不抛出异常。 DiscardOldestPolicy 把老的任务丢掉,执行新任务。 CallerRunsPolicy 直接调用线程处理该任务。JDK四种线程池: newCachedThreadPool,可根据需要创建新线程的线程池 newSingleThreadExecutor,单线程池 newFixedThreadPool,创建固定大小的线程池 newScheduledThreadPool,创建一个大小无限的线程池线程池执行顺序: 首先判断 corePoolSize 是否已满,如果没有满,那么就去创建一个线程去执行该任务;否则请看下一步 如果线程池的核心线程数已满,那么就继续判断 BlockingQueue 是否已满,如果没满,那么就将任务放到任务队列中;否则请看下一步 如果任务队列已满,那么就判断线程池中的线程数量是否达到了maxumunPoolSize,如果没达到,那么就创建线程去执行该任务;否则请看下一步; 如果线程池已满,那么就根据拒绝策略来做出相应的处理;简而言之:corePool->workQueue->maxPool线程池被回收:线程池也是在堆中也是一个对象,一定要调用shutdown线程池何时回收线程:getTask()的返回值为null时 未调用shutdown(),并且当前工作线程数过多 调用shutdown(),缓冲队列中的线程为空核心线程数设置: CPU密集型任务:CPU核心数 + 1:这样设置线程池的大小能实现 CPU 的最优利用率。即使当计算密集型的线程偶尔由于页缺失故障或者其他原因暂停时,这个 “额外” 的线程也能确保CPU 的时装周期不会被浪费。 IO密集型任务:CPU核心数 * 2 混合型任务:CPU核心数 * (1 + IO耗时/CPU耗时)锁 乐观锁,悲观锁: 乐观锁,修改数据前比较数据是否被修改过。CAS,原子类的递增操作,适合频繁读 悲观锁,加锁使其他线程无法修改。synchronized和lock的实现类,适合频繁写 自旋锁,非自旋锁:获取同步资源的锁失败,资源被占用(上下文切换,也就是线程的唤醒和阻塞是十分耗时的) 自旋锁,不放弃CPU时间片,通过自旋等待锁的释放,但自旋超过一定次数(默认10次)仍没有获得锁,那么线程被挂起。线程竞争不激烈并且锁持有的时间不长时,可以使用自旋锁。 非自旋锁,线程会进入阻塞状态 无锁,偏向锁,轻量级锁,重量级锁:指针对synchronized同步锁的状态,锁可以升级但不能降级。 偏向锁,通过对比Mark Word中是否存储着指向当前线程的偏向锁以解决加锁问题,避免执行CAS操作来加锁和解锁,Java15放弃偏向锁。使用背景:锁不仅不存在多线程竞争,而且总是由同一个线程多次获取,那么在同一个线程反复获取所释放锁中,其中并还没有锁的竞争。 轻量级锁,通过用CAS修改Mark Word操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。 重量级锁,将除了拥有锁的线程以外的线程都阻塞。 锁升级的过程:当有线程访问同步块时,无锁升级为偏向锁;当有锁竞争时,升级为轻量级锁;当自旋十次失败,升级为重量级锁。 公平锁,非公平锁: 公平锁,每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的。 非公平锁,每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则,所有线程会竞争获取锁。 可重入锁(递归锁),非可重入锁:ReentrantLock和synchronized都是可重入锁,NonReentrantLock是非可重入锁 可重入锁,指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。好处是一定程度避免死锁。 非可重入锁,如果一个方法中获取锁并调用另外方法,那么在调用另外方法前需要释放锁。 独享锁(排它锁),共享锁 独享锁,ReentrantLock、synchronized、ReentrantReadWriteLock的写锁 共享锁,ReentrantReadWriteLock的读锁,可以再加共享锁但不可以加排他锁! Synchronized(同步锁):属于独占锁、悲观锁、可重入锁、非公平锁。ReentrantLock:继承了Lock类,两者都是可重入锁、悲观锁、独占锁、默认非公平锁。AbstractQueuedSynchronizer(AQS)该类是一个抽象类,采用模板方法的设计模式,规定了独占和共享模式需要实现的方法。简单解释:CAS修改volatile修饰的int值state(该值代表竞争资源标识) + 一个存放等待锁的线程队列。其定义了两种资源共享模式: 独占式。ReentrantLock 是独占式的锁资源。初始化 state = 0,表示资源未被锁定,调用 lock() 方法时state的值加一,并且当 state = 0 才表明其他线程有机会获取锁。 共享式。ReentrantWriteLock 和 CountDownLatch 是共享锁模式。CountDownLatch 会将任务分成 N 个子任务,初始化 state = N,每个子线程完成任务后会减一,直到为零。 Contract接口模式,结合feign实现 contract 用于暴露接口 service 用于实现接口接口和抽象类的区别: 相同点: 都不能被实例化 接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化 不同点: 接口是对行为的抽象(强调特定功能的实现),抽象类是对物体的抽象(强调所属关系)。 接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。 实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。 接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。 static修饰词java中静态属性和静态方法可以被继承,但是不能被重写,因此不能实现多态。静态常量/静态变量/静态方法是用static修饰的常量/变量/方法,其从属于类。另外,static是不允许用来修饰局部变量的。 静态方法可以调用静态变量,但不能调用非静态变量,因为静态方法在类加载时就分配了内存,而非静态变量是在对象实例化时才分配内存。 非静态方法可以调用静态变量,也可以调用非静态变量。 静态初始化块、初始化块和构造方法的区别执行顺序:静态初始化块 > 初始化块 > 构造方法非静态初始化块(构造代码块):作用:给对象进行初始化。对象一建立就运行,且优先于构造函数的运行。与构造函数的区别:非静态初始化块给所有对象进行统一初始化,构造函数只给对应对象初始化。应用:将所有构造函数共性的东西定义在构造代码块中。静态初始化块:作用:给类进行初始化。随着类的加载而执行,且只执行一次与构造代码块的区别: 构造代码块用于初始化对象,每创建一个对象就会被执行一次;静态代码块用于初始化类,随着类的加载而执行,不管创建几个对象,都只执行一次。 静态代码块优先于构造代码块的执行 都定义在类中,一个带static关键字,一个不带static泛型和泛型擦除泛型:参数化类型,指在定义一个类、接口或者方法时可以指定类型参数。泛型擦除:是指Java中的泛型只在编译期有效,在运行期间会被删除。也就是说所有泛型参数在编译后都会被清除掉。在编译器编译后,泛型的转换规则如下: List、List 擦除后的类型为 List; List[]、List[] 擦除后的类型为 List[]; List<? extends E>、List<? super E> 擦除后的类型为 List; List<T extends Serialzable & Cloneable> 擦除后类型为 List。JDK 设计模式 单例模式:Runtime 类使用饿汉式创建单例 工厂模式:线程池中所有线程,通过工厂模式创建 代理模式:java.lang.reflect.Proxy 中的动态代理 迭代器模式:java.util.Iterator 使用迭代器遍历集合容器 模板方法:AQS 中的 acquire 和 release 方法被独占式和共享式所重写" }, { "title": "Java知识点记录博客", "url": "/posts/java-syntax/", "categories": "Backend, Java", "tags": "java, syntax", "date": "2022-06-12 23:14:04 +0800", "snippet": "运算符优先级 优先级 运算符 1 ( ) [ ]  . 2 !  ~  ++  – 3 *  /  % 4 +  - 5 «  »  «<  »> ...", "content": "运算符优先级 优先级 运算符 1 ( ) [ ]  . 2 !  ~  ++  – 3 *  /  % 4 +  - 5 «  »  «<  »> 6 <  <=  >  >=  instanceof 7 ==  != 8 & 9 ^ 10 | 11 && 12 || 13 ? : 14 =  +=  -=  *=  /=  %=  &=  |=  ^=  ~=  «=  »=  »>= 15 , 总结:括号级别最高,逗号级别最低,单目 > 算术 > 位移 > 关系 > 逻辑 > 三目 > 赋值。容器(集合类)Deque<Integer> stack = new ArrayDeque<>(); // stackDeque<String> queue = new ArrayDeque<>(); // queuePriorityQueue<Integer> queue = new PriorityQueue<>(); // priority queue// SetHashSet<String> unordered_set = new HashSet<>(); // 乱序,底层使用散列函数LinkedHashSet<String> set = new LinkedHashSet<>(); // 以插入顺序排序TreeSet<String> set = new TreeSet<>(); // 以字典序排序,底层使用红黑树// MapHashMap<String, Object> unordered_map = new HashMap<>(); // 乱序LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>(); // 以插入顺序排序TreeMap<Integer, Integer> map = new TreeMap<>(); // 以字典序排序TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); // 手动加泛型,多一些约束少一些出错。在运行期没有任何区别, java的泛型只在编译期有效。ArrayList:public static void main(String[] args) { ArrayList<String> sites = new ArrayList<String>(); sites.add(\"Google\"); sites.add(\"Runoob\"); sites.add(\"Taobao\"); sites.add(\"Weibo\"); System.out.println(sites.get(1)); // 访问第二个元素 sites.set(2, \"Wiki\"); // 第一个参数为索引位置,第二个为要修改的值 sites.remove(3); // 删除第四个元素 Collections.sort(sites); // 字母排序 System.out.println(sites.isEmpty()); // 空判断 System.out.println(sites); String[] arr = new String[sites.size()]; // 创建一个新的 String 类型的数组 sites.toArray(arr); // 将ArrayList对象转换成数组}LinkedList:HashSet:public static void main(String[] args) { HashSet<Integer> set = new HashSet<Integer>(); set.add(1); set.add(2); set.add(3); set.remove(2); System.out.println(set.contains(4)); for(int item: set){ System.out.println(item); }}HashMap:public static void main(String[] args) { Map<String, Integer> numbers = new HashMap<>(); numbers.put(\"One\", 1); numbers.put(\"Two\", 2); numbers.put(\"Three\", 3); numbers.put(\"Four\", 4); System.out.println(numbers.getOrDefault(\"Five\", 0)); numbers.remove(\"Two\"); numbers.replace(\"One\",111); System.out.println(numbers.containsKey(\"Four\")); System.out.println(numbers.containsValue(111)); // Method A(Recommended!) numbers.forEach((k,v) -> System.out.println(\"Item: \" + k + \" Count: \" + v)); numbers.forEach((k,v) -> { System.out.println(\"Item: \" + k + \" Count: \" + v); if(\"Four\".equals(k)){ System.out.println(\"Hello Four\"); } }); // Method B(Recommended!) for (Map.Entry<String, Integer> entry : numbers.entrySet()) { System.out.println(\"key:\" + entry.getKey() + \".value:\" + entry.getValue()); } // Method C(NOT Recommended!) for (String string : numbers.keySet()) { System.out.println(\"key:\" + string + \".value:\" + numbers.get(string)); } // Method D(Recommended When Deleting) Iterator<String> iterators = numbers.keySet().iterator(); while (iterators.hasNext()) { String key = iterators.next(); System.out.println(\"key:\" + key + \".value:\" + numbers.get(key)); }}ArrayDeque:在堆栈中,元素从栈顶插入,从栈顶弹出在队列中,元素从队尾插入,从队首弹出public static void main(String[] args) { ArrayDeque<String> animals= new ArrayDeque<>(); /* *****Stack***** */ animals.push(\"Pig\"); System.out.println(\"返回栈顶元素: \" + animals.peek()); System.out.println(\"返回栈顶元素并弹出: \" + animals.pop()); /* *****Queue***** */ animals.offer(\"Horse\"); System.out.println(\"返回队首元素: \" + animals.peek()); System.out.println(\"返回队首元素并弹出: \" + animals.poll()); /* *****Deque***** */ animals.push(\"Bird\"); // 队首 animals.offer(\"Dog\"); // 队尾 System.out.println(\"返回队首元素: \" + animals.peek()); System.out.println(\"返回队首元素并弹出: \" + animals.poll()); System.out.println(\"返回队尾元素: \" + animals.peekLast()); System.out.println(\"返回队尾元素并弹出: \" + animals.pollLast()); /* *****Common***** */ System.out.println(\"判断是否包含Dog: \" + (animals.contains(\"Dog\") ? \"是\" : \"否\")); System.out.println(\"转换成数组输出: \" + Arrays.toString(animals.toArray()));}赋值和new的区别赋值是创建常量,在编译期时就被确定了;new创建的对象不是常量,无法在编译期中确定。String s0 = \"aaa\" + \"bbb\"; // 常量,同\"aaabbb\",放入常量池中。创建了1个对象String s1 = \"aaa\" + new String(\"bbb\"); // 非常量,因为new出来的字符串无法在编译期中确定。创建了4个对象String s2 = new String(\"aaabbb\"); // 同上。由于常量池中已经存在\"aaabbb\",因此只创建了1个对象System.out.println(s1.intern() == s0); // true,intern()函数会在常量池里找是否相同的字符串,有则返回常量池的引用System.out.println(s2.intern() == s0); // true,同上System.out.println(s0 == s1); // false,虽然内容一样,但是new出来的地址肯定不一样System.out.println(s1 == s2); // false,同上Java只有值传递,没有引用传递SEE HERE赋值是给变量绑定一个新对象,而不是改变对象。举例:public static void main(String[] args) { String x = new String(\"沉默王二\"); change(x); System.out.println(x); // \"沉默王二\"}public static void change(String x) { x = \"沉默王三\";}直接改变对象内容。举例:public static void change(A a) { a.name = \"bbb\";}public static void main(String[] args) { A a = new A(); a.name = \"aaa\"; change(a); System.out.println(a.name); // \"bbb\"}length为属性,length()为方法 数组属性:length 字符串方法:length() 集合方法:size()红黑树的时间复杂度为: O(logn)一棵含有n个节点的红黑树的高度至多为2log(n+1).See Here.同步容器同步容器主要包括2类: Vector、Stack、HashTable Collections类中提供的静态工厂方法创建的类 同步容器的所有操作并不都是线程安全的。HERE所谓“线程安全”,并不包括多个操作之间的“原子性”支持。100%的情况下不要用 StringBuffer99% 的情况下不要用 Vector那么那剩下的 1% 用 Vector 的情况在哪呢?熟悉Swing的都知道: 现有的一些 model 的类里面用了 Vector,假如你去定制它们,有时不可避免要用到 Vector。StringBuilder与StringBuffer相比,StringBuilder不是线程安全,所以单线程情况下效率更高。两者除线程安全方面之外无差别。StringBuilder sb = new StringBuilder();sb.append(\"a\".repeat(100)); // repeat方法用于构造重复String" }, { "title": "Ubuntu中关闭摄像头的自动曝光", "url": "/posts/ubuntu-camera-setting/", "categories": "Ubuntu, Camera", "tags": "camera, auto exposure", "date": "2022-05-21 13:31:32 +0800", "snippet": "目的通过Python代码关闭杰锐微通摄像头的自动曝光功能。尝试查阅相关资料,有网友提出使用以下代码:capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25)失败。解决方案先通过在终端输入v4l2-ctl --list-devices得到摄像头列表。接着输入v4l2-ctl -d /dev/video2 --all查看单个摄像头的参数。发现最后几行中的expo...", "content": "目的通过Python代码关闭杰锐微通摄像头的自动曝光功能。尝试查阅相关资料,有网友提出使用以下代码:capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25)失败。解决方案先通过在终端输入v4l2-ctl --list-devices得到摄像头列表。接着输入v4l2-ctl -d /dev/video2 --all查看单个摄像头的参数。发现最后几行中的exposure_auto的默认值为3,正好代表着光圈优先。将其设置为1,代表手动模式因此在代码修改为capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1)capture.set(cv2.CAP_PROP_EXPOSURE, 50)即可。曝光值参数可根据环境自主调节。" }, { "title": "【解决方案】NUC11锁屏后无法唤醒", "url": "/posts/nuc11-wakeup/", "categories": "NUC11", "tags": "bug, windows 10", "date": "2022-05-01 17:20:20 +0800", "snippet": "设备配置 型号:NUC11PAHi5 硬盘:Samsung 970Pro 1TB 内存:Samsung 3200MHz 8GB * 2 操作系统:Windows10 Pro 问题描述:锁屏黑屏后有一定几率无法唤醒,右侧蓝色的电源灯常亮(非呼吸灯),左侧橙色的硬盘灯不亮(正常情况下会闪),只能通过长按电源键十秒左右才能关机。解决方案:显卡驱...", "content": "设备配置 型号:NUC11PAHi5 硬盘:Samsung 970Pro 1TB 内存:Samsung 3200MHz 8GB * 2 操作系统:Windows10 Pro 问题描述:锁屏黑屏后有一定几率无法唤醒,右侧蓝色的电源灯常亮(非呼吸灯),左侧橙色的硬盘灯不亮(正常情况下会闪),只能通过长按电源键十秒左右才能关机。解决方案:显卡驱动回滚至27版本。27版本的驱动" }, { "title": "Windows任务栏时间显示秒", "url": "/posts/show-seconds-in-taskbar/", "categories": "Tutorial, Windows 10", "tags": "skills, windows 10", "date": "2022-04-22 18:17:37 +0800", "snippet": " 使用 win + r 打开运行,再输入 regedit 在打开的注册表中定位至HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced 新建 DWORD(32位)值,命名为ShowSecondsInSystemClock,将其数值置为 1 并保存。 ...", "content": " 使用 win + r 打开运行,再输入 regedit 在打开的注册表中定位至HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced 新建 DWORD(32位)值,命名为ShowSecondsInSystemClock,将其数值置为 1 并保存。 打开任务管理器重新启动Windows资源管理器即可。 " }, { "title": "Dev-C++支持C++11的操作", "url": "/posts/dev-c++11/", "categories": "IDE Configuration, Dev-C++", "tags": "dev", "date": "2022-04-17 13:51:08 +0800", "snippet": " Click Tools and Complier Options Add -std=c++11 ", "content": " Click Tools and Complier Options Add -std=c++11 " }, { "title": "MathJax 数学公式语法", "url": "/posts/mathjax/", "categories": "Tutorial, Mathjax", "tags": "getting started, syntax", "date": "2022-04-11 16:09:20 +0800", "snippet": "MathJax中的公式排版有两种方式,inline和displayed。 inline表示公式嵌入到文本段中,也称为行模式。$ f(x) = 3 \\times x $这是一个inline公式 displayed表示公式独自成为一个段落,也成为块模式。下面则是一个displayed公式。 \\[f(x) = 3 \\times x\\]符号(Operators) ...", "content": "MathJax中的公式排版有两种方式,inline和displayed。 inline表示公式嵌入到文本段中,也称为行模式。$ f(x) = 3 \\times x $这是一个inline公式 displayed表示公式独自成为一个段落,也成为块模式。下面则是一个displayed公式。 \\[f(x) = 3 \\times x\\]符号(Operators) Symbol Command Symbol Command Symbol Command $\\pm$ \\pm $\\mp$ \\mp $\\times$ \\times $\\div$ \\div $\\cdot$ \\cdot $\\ast$ \\ast $\\star$ \\star $\\dagger$ \\dagger $\\ddagger$ \\ddagger $\\amalg$ \\amalg $\\cap$ \\cap $\\cup$ \\cup $\\uplus$ \\uplus $\\sqcap$ \\sqcap $\\sqcup$ \\sqcup $\\vee$ \\vee $\\wedge$ \\wedge $\\oplus$ \\oplus $\\ominus$ \\ominus $\\otimes$ \\otimes $\\circ$ \\circ $\\bullet$ \\bullet $\\diamond$ \\diamond $\\lhd$ \\lhd $\\rhd$ \\rhd $\\unlhd$ \\unlhd $\\unrhd$ \\unrhd $\\oslash$ \\oslash $\\odot$ \\odot $\\bigcirc$ \\bigcirc $\\triangleleft$ \\triangleleft $\\Diamond$ \\Diamond $\\bigtriangleup$ \\bigtriangleup $\\bigtriangledown$ \\bigtriangledown $\\Box$ \\Box $\\triangleright$ \\triangleright $\\setminus$ \\setminus $\\wr$ \\wr $\\sqrt{x}$ \\sqrt{x} $x^{\\circ}$ x^{\\circ} $\\triangledown$ \\triangledown $\\sqrt[n]{x}$ \\sqrt[n]{x} $a^x$ a^x $a^{xyz}$ a^{xyz} $\\frac{x}{y}$ \\frac{x}{y} $\\sin{x}$ \\sin{x} $\\cos{x}$ \\cos{x} $\\tan{x}$ \\tan{x} $\\log_xy$ \\log_xy $\\ln{x}$ \\ln{x} $\\max(x,y,z)$ \\max(x,y,z) 求和:\\sum\\limits_{i=0}^n{a_i} 显示为$\\sum\\limits_{i=0}^n{a_i}$ 求积:\\prod\\limits_{i=0}^n{\\frac{1}{i^2}} 显示为$\\prod\\limits_{i=0}^n{\\frac{1}{i^2}}$ 积分:\\int_0^xf(x)dx显示为$\\int_0^xf(x)dx$ 极限:\\lim\\limits_{x\\to 0}{x} 显示为$\\lim\\limits_{x\\to 0}{x}$ 自定义符号:\\mathop{SUPER}\\limits_{i=0}^n{i^2} 显示为$\\mathop{SUPER}\\limits_{i=0}^n{i^2}$关系(Relations) Symbol Command Symbol Command Symbol Command $\\le $ \\le $\\ge $ \\ge $\\neq $ \\neq $\\sim $ \\sim $\\ll $ \\ll $\\gg $ \\gg $\\doteq $ \\doteq $\\simeq $ \\simeq $\\subset $ \\subset $\\supset $ \\supset $\\approx $ \\approx $\\asymp $ \\asymp $\\subseteq $ \\subseteq $\\supseteq $ \\supseteq $\\cong $ \\cong $\\smile $ \\smile $\\sqsubset $ \\sqsubset $\\sqsupset $ \\sqsupset $\\equiv $ \\equiv $\\frown $ \\frown $\\sqsubseteq $ \\sqsubseteq $\\sqsupseteq$ \\sqsupseteq $\\propto $ \\propto $\\bowtie $ \\bowtie $\\in $ \\in $\\ni $ \\ni $\\prec $ \\prec $\\succ $ \\succ $\\vdash $ \\vdash $\\dashv $ \\dashv $\\preceq $ \\preceq $\\succeq $ \\succeq $\\models $ \\models $\\perp $ \\perp $\\parallel $ \\parallel     $\\mid $ \\mid $\\bumpeq $ \\bumpeq     上面这些关系符号的否定(反义)形式可以通过在原符号前添加 \\not 来进行实现,或者在 \\ 和符号单词之间添加 n 来实现。下面列出几个常用的否定形式,其他符号的否定形式规则基本类似。 Symbol Command Symbol Command Symbol Command $\\nmid $ \\nmid $\\nleq $ \\nleq $\\ngeq $ \\ngeq $\\nsim $ \\nsim $\\ncong $ \\ncong $\\nparallel $ \\nparallel $\\not< $ \\not< $\\not> $ \\not> $\\not= $ \\not= $\\not\\le $ \\not\\le $\\not\\ge $ \\not\\ge $\\not\\sim $ \\not\\sim $\\not\\approx $ \\not\\approx $\\not\\cong $ \\not\\cong $\\not\\equiv $ \\not\\equiv $\\not\\parallel $ \\not\\parallel $\\nless $ \\nless $\\ngtr $ \\ngtr $\\lneq $ \\lneq $\\gneq $ \\gneq $\\lnsim $ \\lnsim $\\lneqq $ \\lneqq $\\gneqq $ \\gneqq     像 =, >, 和 < 并没有列在上面的符号,可以直接字面输入,并不需要命令进行触发。希腊字母(Greek Letters)小写: Symbol Command Symbol Command Symbol Command $\\alpha $ \\alpha $ \\beta $ \\beta $\\gamma $ \\gamma $\\epsilon $ \\epsilon $ \\varepsilon $ \\varepsilon $\\zeta $ \\zeta $\\theta $ \\theta $ \\vartheta $ \\vartheta $\\iota $ \\iota $\\lambda $ \\lambda $ \\mu $ \\mu $\\nu $ \\nu $\\pi $ \\pi $ \\varpi $ \\varpi $\\rho $ \\rho $\\sigma $ \\sigma $ \\varsigma $ \\varsigma $\\tau $ \\tau $\\phi $ \\phi $ \\varphi $ \\varphi $\\chi $ \\chi $\\omega $ \\omega $ \\varrho $ \\varrho $\\kappa $ \\kappa $\\delta $ \\delta $ \\upsilon $ \\upsilon $\\xi $ \\xi $\\eta $ \\eta $ \\psi $ \\psi     大写: Symbol Command Symbol Command Symbol Command $\\Gamma $ \\Gamma $\\Delta $ \\Delta $ \\Theta $ \\Theta $\\Xi $ \\Xi $\\Pi $ \\Pi $ \\Sigma $ \\Sigma $\\Phi $ \\Phi $\\Psi $ \\Psi $ \\Omega $ \\Omega $\\Lambda $ \\Lambda $\\Upsilon $ \\Upsilon     斜体大写: Symbol Command Symbol Command Symbol Command $\\varGamma $ \\varGamma $\\varDelta $ \\varDelta $ \\varTheta $ \\varTheta $\\varXi $ \\varXi $\\varPi $ \\varPi $ \\varSigma $ \\varSigma $\\varPhi $ \\varPhi $\\varPsi $ \\varPsi $ \\varOmega $ \\varOmega $\\varLambda $ \\varLambda $\\varUpsilon $ \\varUpsilon     箭头(Arrors) Symbol Command Symbol Command $\\gets $ \\gets $\\to $ \\to $\\leftarrow $ \\leftarrow $\\Leftarrow $ \\Leftarrow $\\rightarrow $ \\rightarrow $\\Rightarrow $ \\Rightarrow $\\leftrightarrow $ \\leftrightarrow $\\Leftrightarrow $ \\Leftrightarrow $\\mapsto $ \\mapsto $\\hookleftarrow $ \\hookleftarrow $\\leftharpoonup $ \\leftharpoonup $\\leftharpoondown $ \\leftharpoondown $\\rightleftharpoons $ \\rightleftharpoons $\\longleftarrow $ \\longleftarrow $\\Longleftarrow $ \\Longleftarrow $\\longrightarrow $ \\longrightarrow $\\Longrightarrow $ \\Longrightarrow $\\longleftrightarrow$ \\longleftrightarrow $\\Longleftrightarrow $ \\Longleftrightarrow $\\longmapsto $ \\longmapsto $\\hookrightarrow $ \\hookrightarrow $\\rightharpoonup $ \\rightharpoonup $\\rightharpoondown $ \\rightharpoondown $\\leadsto $ \\leadsto $\\uparrow $ \\uparrow $\\Uparrow $ \\Uparrow $\\downarrow $ \\downarrow $\\Downarrow $ \\Downarrow $\\updownarrow $ \\updownarrow $\\Updownarrow $ \\Updownarrow $\\nearrow $ \\nearrow $\\searrow $ \\searrow $\\swarrow $ \\swarrow $\\nwarrow $ \\nwarrow 有些箭头指令, mathjax 提供了缩写指令, $\\iff$(\\iff) 和 $\\implies$(\\implies) 可以分别表示为 $\\Longleftrightarrow$(\\Longleftrightarrow) 和 $\\Longrightarrow$(\\Longrightarrow)重音(Accents) Symbol Command Symbol Command Symbol Command $\\hat{x} $ \\hat{x} $\\check{x} $ \\check{x} $\\dot{x} $ \\dot{x} $\\breve{x}$ \\breve{x} $\\acute{x} $ \\acute{x} $\\ddot{x} $ \\ddot{x} $\\grave{x}$ \\grave{x} $\\tilde{x} $ \\tilde{x} $\\mathring{x} $ \\mathring{x} $\\bar{x} $ \\bar{x} $\\vec{x} $ \\vec{x} $\\overline{x} $ \\overline{x} $\\widehat{7+x}$ \\widehat{7+x} $\\widetilde{abc}$ \\widetilde{abc}     \\tilde 和 \\hat 两个指令有宽符号的版本。上述表格最后一行,\\widetilde 和 \\widehat,通过这两个指令可以生成长版本的表达式结构的符号。方程组(Equation Sets)$$f(n) = \\tag{1}\\begin{cases}\\frac{a_n^3}{2}, & \\text{if $n$ is even} \\\\ 3a_n^2+1, & \\text{if $n$ is odd}\\end{cases}$$\\[f(n) = \\tag{1}\\begin{cases}\\frac{a_n^3}{2}, & \\text{if $n$ is even} \\\\ 3a_n^2+1, & \\text{if $n$ is odd}\\end{cases}\\]字体(Fonts) 打印机字体Typewriter:\\mathtt{R} 显示为$\\mathtt{R}$ 黑板粗体字Blackboard Bold:\\mathbb{R} 显示为$\\mathbb{R}$。表示实数集的意思。 无衬线字体Sans Serif:\\mathsf{R} 显示为$\\mathsf{R}$ 手写体Script:\\mathscr{R} 显示为$\\mathscr{R}$ 罗马字体Roman:\\mathrm{R} 显示为$\\mathrm{R}$点(Dots) Symbol Command Symbol Command $\\cdot $ \\cdot $\\vdots $ \\vdots $\\dots $ \\dots $\\ddots $ \\ddots $\\cdots$ \\cdots $\\ldots $ \\ldots \\ldots 和 \\cdots 是低位置省略号和中心位置省略号的 latex 命令, \\dots 是 amsmath 命令用来试图帮你在 \\ldots 和 \\cdots 中自动做决断的。通常来讲中心省略 \\cdots 一般用在数学模式的中心线上的符号后面,例如加号 + 或者右箭头 -> , 而 \\ldots 一般用在标点符号的后面,例如句号“ . ” or逗号“ , ”。例如,$ a + b + \\cdots + z \\quad a_1, \\ldots, a_n $ 显示为$ a + b + \\cdots + z \\quad a_1, \\ldots, a_n $改为\\dots便可以根据实际情况自动地改变省略号的位置$ a + b + \\dots + z \\quad a_1, \\dots, a_n $ 显示为$ a + b + \\dots + z \\quad a_1, \\dots, a_n $使用\\dots基本可以满足要求。但是,\\dots 并不是每次都能正确自动改变省略号的位置,所以还是需要根据自己的实际情况选择不同的dots。矩阵(Matrices) 小括号边框:pmatrix 中括号边框:bmatrix 大括号边框:Bmatrix 单竖线边框:vmatrix 双竖线边框:Vmatrix$$\\begin{bmatrix}{a_{11}}&{a_{12}}&{\\cdots}&{a_{1n}}\\\\{a_{21}}&{a_{22}}&{\\cdots}&{a_{2n}}\\\\{\\vdots}&{\\vdots}&{\\ddots}&{\\vdots}\\\\{a_{m1}}&{a_{m2}}&{\\cdots}&{a_{mn}}\\\\\\end{bmatrix}$$\\[\\begin{bmatrix}{a_{11}}&{a_{12}}&{\\cdots}&{a_{1n}}\\\\{a_{21}}&{a_{22}}&{\\cdots}&{a_{2n}}\\\\{\\vdots}&{\\vdots}&{\\ddots}&{\\vdots}\\\\{a_{m1}}&{a_{m2}}&{\\cdots}&{a_{mn}}\\\\\\end{bmatrix}\\]界限符号(Bracketing Symbols)在数学公式里,有时我们会通过括号( (), [], {} )进行界线控制。这些符号有些是可以直接输入,比如 (), [], | 等,而有些符号是要经过转义的,下面列出了这些比较特殊的符号。 Symbol Command Symbol Command $\\lfloor $ \\lfloor $\\rfloor $ \\rfloor $\\lceil $ \\lceil $\\rceil $ \\rceil $\\langle $ \\langle $\\rangle $ \\rangle 上括号$ \\overbrace{a_0+a_1+a_2+\\cdots+a_n}^{x} $,显示为$ \\overbrace{a_0+a_1+a_2+\\cdots+a_n}^{x} $下括号$ \\underbrace{a_0+a_1+a_2+\\cdots+a_n}_{x} $,显示为$ \\underbrace{a_0+a_1+a_2+\\cdots+a_n}_{x} $但是有时候界限符号不够高,所以需要一些自适应括号,比如$ (\\frac{a}{x} )^2 $,显示为\\[(\\frac{a}{x} )^2\\]使用自适应括号的代码为$ \\left(\\frac{a}{x} \\right)^2 $,显示为\\[\\left(\\frac{a}{x} \\right)^2\\]但是也只有在块模式的时候会比较明显。其他(Others) Symbol Command Symbol Command Symbol Command $\\infty $ \\infty $ \\triangle $ \\triangle $\\angle $ \\angle $\\aleph $ \\aleph $ \\hbar $ \\hbar $\\imath $ \\imath $\\jmath $ \\jmath $ \\ell $ \\ell $\\wp $ \\wp $\\Re $ \\Re $ \\Im $ \\Im $\\mho $ \\mho $\\prime $ \\prime $ \\emptyset $ \\emptyset $\\nabla $ \\nabla $\\surd $ \\surd $ \\partial $ \\partial $\\top $ \\top $\\bot $ \\bot $ \\vdash $ \\vdash $\\dashv $ \\dashv $\\forall $ \\forall $ \\exists $ \\exists $\\neg $ \\neg $\\flat $ \\flat $ \\natural $ \\natural $\\sharp $ \\sharp $\\backslash$ \\backslash $ \\Box $ \\Box $\\Diamond $ \\Diamond $\\clubsuit $ \\clubsuit $ \\diamondsuit $ \\diamondsuit $\\heartsuit $ \\heartsuit $\\spadesuit$ \\spadesuit $ \\Join $ \\Join $\\blacksquare$ \\blacksquare $\\bigstar $ \\bigstar $ \\in $ \\in $\\cup $ \\cup $\\square $ \\square $ \\S $ \\S $\\checkmark $ \\checkmark $\\because $ \\because $ \\therefore $ \\therefore     " }, { "title": "Difference between NEW and MALLOC in C++", "url": "/posts/new-and-malloc/", "categories": "C++", "tags": "c++, new, malloc", "date": "2022-03-31 16:48:53 +0800", "snippet": "Difference1. 属性 new/delete是C++关键字,需要编译器支持。 malloc/free是库函数,需要头文件支持。 2. 参数 new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。 而malloc则需要显式地指出所需内存的尺寸。 3. 返回类型 new操作符内存分配成功时,返回的是对象...", "content": "Difference1. 属性 new/delete是C++关键字,需要编译器支持。 malloc/free是库函数,需要头文件支持。 2. 参数 new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。 而malloc则需要显式地指出所需内存的尺寸。 3. 返回类型 new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。 而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。 4. 分配失败 new内存分配失败时,会抛出bac_alloc异常。 malloc分配内存失败时返回NULL。 5. 自定义类型 new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。 malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。 6. 重载 C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。 而malloc不允许重载。 7. 内存区域 new操作符从自由存储区(free store)上为对象动态分配内存空间。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。 而malloc函数从堆上动态分配内存。堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。 8. 重新分配内存 new没有扩张内存的机制。 而malloc分配内存后,如果发现内存不够用,可以通过realloc函数来扩张内存大小,realloc会先判断当前申请的内存后面是否还有足够的内存空间进行扩张,如果有足够的空间,那么就会往后面继续申请空间,并返回原来的地址指针;否则realloc会在另外有足够大小的内存申请一块空间,并将当前内存空间里的内容拷贝到新的内存空间里,最后返回新的地址指针。 Code以下是 vector, vector with reserve, array, malloc, new 这几种不同创建并销毁数据的方法的运行时间对比代码。根据运行结果可以大致得到:$T_{array} \\approx T_{malloc} \\approx T_{new} < T_{vector-with-reserve} < T_{vector}$#include<bits/stdc++.h>#include <windows.h>using namespace std;int main(){\tcout<<\"=====Runing time of program=====\" <<endl;\t// vector\t\tDWORD start = GetTickCount();\tint t = 100;\tint n = 200000;\twhile (t)\t{\t\tvector<int> a;\t\tfor(int i=0;i<n;i++) a.push_back(i);\t\tt--;\t}\tcout<<\"Vector: \"<<GetTickCount() - start<<endl;\t// vector with reserve\tstart = GetTickCount();\tt = 100;\tn = 200000;\twhile (t)\t{\t\tvector<int> b;\t\tb.reserve(n);\t\tfor(int i=0;i<n;i++) b.push_back(i);\t\tt--;\t}\tcout<<\"Vector with reserve: \"<<GetTickCount() - start<<endl;\t// array\tstart = GetTickCount();\tt = 100;\tn = 200000;\twhile (t)\t{\t\tint a[200000];\t\tfor(int i=0;i<n;i++) a[i]=i;\t\tt--;\t}\tcout<<\"Array: \"<<GetTickCount() - start<<endl;\t// malloc\tstart = GetTickCount();\tt = 100;\tn = 200000;\twhile (t)\t{\t\tint *p = (int *)malloc((n+1) * sizeof(int));\t\tfor(int i=0;i<n;i++) p[i]=i;\t\tfree(p);\t\tt--;\t}\tcout<<\"Malloc: \"<<GetTickCount()-start<<endl;\t\t\t// new\tstart = GetTickCount();\tt = 100;\tn = 200000;\twhile (t)\t{\t\tint *p=new int[n+1];\t\tfor(int i=0;i<n;i++) p[i]=i;\t\tdelete []p;\t\tt--;\t}\tcout<<\"New: \"<<GetTickCount() - start<<endl;\t}ConclusionReference new与malloc的10点区别 C++ 自由存储区是否等价于堆?" }, { "title": "C++中静态成员及静态成员函数详解", "url": "/posts/static/", "categories": "C++", "tags": "c++, static", "date": "2022-03-18 11:40:41 +0800", "snippet": "1. 局部静态变量局部静态变量用于函数体内部修饰变量,这种变量的生存期长于该函数。用法:局部变量前加static,修饰局部变量为静态局部变量。作用:改变局部变量的销毁时期。作用域:局部作用域,当定义它的函数或语句块结束的时候,作用域结束。与局部变量的区别: 静态局部变量在全局数据区(静态存储区)分配内存(局部变量在栈区分配内存); 静态局部变量在程序执行到该对象的声明处时被首次初始化,即...", "content": "1. 局部静态变量局部静态变量用于函数体内部修饰变量,这种变量的生存期长于该函数。用法:局部变量前加static,修饰局部变量为静态局部变量。作用:改变局部变量的销毁时期。作用域:局部作用域,当定义它的函数或语句块结束的时候,作用域结束。与局部变量的区别: 静态局部变量在全局数据区(静态存储区)分配内存(局部变量在栈区分配内存); 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化(局部变量每次函数调用都会被初始化); 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0(局部变量不会被初始化); 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,也就是不能在函数体外面使用它(局部变量在栈区,在函数结束后立即释放内存); 当静态局部变量离开作用域后,并没有被销毁。当该函数再次被调用的时候,该变量的值为上次函数调用结束时的值(局部变量离开作用域便被销毁,再次调用函数时,其值为初始值)。与全局变量的区别:同样是初始化一次,连续调用fun()的结果是一样的,但是,使用全局变量的话,变量就不属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。Code:#include<stdio.h>int fun(void){\tstatic int count = 20; // 只能在该函数内修改静态局部变量的值\treturn count--;}int main() {\tprintf(\"global\\t\\tlocal static\\n\");\tint count = 1;\tfor(; count <= 10; ++count) printf(\"%d\\t\\t%d\\n\", count, fun());}运行结果图2. 全局静态变量全局静态变量定义在函数体外,该变量只在本文件可见。用法:全局变量前加static,修饰全局变量为静态全局变量。作用:改变全局变量的可见性。静态全局变量的存储位置在静态存储区,未被初始化的静态全局变量会被自动初始化为0。作用域:全局静态变量在声明他的文件之外是不可见的,仅在从定义该变量的开始位置到文件结尾可见。特点: 静态全局变量不能被其它文件所用(全局变量可以); 其它文件中可以定义相同名字的变量,不会发生冲突。Code:以下两个文件要放到项目工程里才有效果。//file a.cpp //static int n = 15; // ERROR!因为static隔离了文件。int n = 15;//file main.cpp#include <iostream>//#include\"a.cpp\" //不管有没有static,加include都不会报错using namespace std;extern int n;void fn(){\tn++;}int main() {\tcout<<\"Before:\" <<n<<endl;\tfn();\tcout<<\"After:\" <<n<<endl;\treturn 0;}运行结果图3. 静态函数静态函数的作用与2. 静态全局变量类似。特点也类似:不能被其他文件所用;其它文件中可以定义相同名字的变量,不会发生冲突。用法:函数返回类型前加static,修饰函数为静态函数。作用:改变函数的可见性。函数的定义和声明在默认情况下都是extern的,但静态函数只在声明它的文件中可见,不能被其他文件使用。4. 静态成员用法:类成员前加static,修饰类的成员为类的静态成员。作用:实现多个对象之间的数据共享,并且使用静态成员不会破坏封装性,也保证了安全性。Code:#include<iostream>using namespace std;class Rectangle{private: int m_w,m_h; static int s_sum;public: Rectangle(int w,int h) { this->m_w = w; this->m_h = h; s_sum += (this->m_w * this->m_h); } void GetSum() { cout<<\"sum = \"<<s_sum<<endl; }};int Rectangle::s_sum = 0; //静态成员类外初始化,因为其属于类,不属于类对象int main(){ cout<<\"sizeof(Rectangle)=\"<<sizeof(Rectangle)<<endl; Rectangle *rect1 = new Rectangle(3,4); rect1->GetSum(); cout<<\"sizeof(rect1)=\"<<sizeof(*rect1)<<endl; Rectangle rect2(2,3); rect2.GetSum(); cout<<\"sizeof(rect2)=\"<<sizeof(rect2)<<endl;}sizeof(Rectangle)=8sum = 12sizeof(rect1)=8sum = 12sizeof(rect1)=8由此可知:sizeof(Rectangle) = 8bytes = sizeof(m_w) + sizeof(m_h)。也就是说静态数据成员并不占用Rectangle的内存空间。因为静态数据成员在全局数据区(静态区)分配内存。再看看GetSum(),第一次12 = 3 * 4,第二次18 = 12 + 2 * 3。因此,静态数据成员只会被初始化一次,与对象无关。结论:静态数据成员被当作是类的成员,由该类型的所有对象共享访问,对该类的多个对象来说,静态数据成员只分配一次内存。静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。从某种程度上来说,静态数据成员与静态变量相类似。5. 静态成员函数用法:类函数前加static,修饰类的函数为静态函数。作用:减少资源消耗,不需要实例化就可以使用Code:#include<iostream>using namespace std;class Rectangle{private: int m_w,m_h; static int s_sum;public: Rectangle(int w,int h) { this->m_w = w; this->m_h = h; s_sum += (this->m_w * this->m_h); } static void GetSum() // 此处为静态成员函数 { cout<<\"sum = \"<<s_sum<<endl; }};int Rectangle::s_sum = 0; //静态成员类外初始化,因为其属于类,不属于类对象int main(){ cout<<\"sizeof(Rectangle)=\"<<sizeof(Rectangle)<<endl; Rectangle *rect1 = new Rectangle(3,4); rect1->GetSum(); cout<<\"sizeof(rect1)=\"<<sizeof(*rect1)<<endl; Rectangle rect2(2,3); rect2.GetSum(); //可以用对象名.函数名访问 cout<<\"sizeof(rect2)=\"<<sizeof(rect2)<<endl; Rectangle::GetSum(); //也可以可以用类名::函数名访问}结论: 非静态成员函数可访问静态成员函数/成员; 静态成员函数不能访问非静态成员函数/成员,只能访问静态成员函数/变量; 调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以用类名::函数名调用。 另外,既然是在类里操作,对类成员的访问还是要遵从public,protected,private访问规则。6. 静态变量内存分配和初始化全局变量、文件域的静态变量和类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化;局部静态变量(一般为函数内的静态变量)在第一次使用时分配内存并初始化。这里的变量包含内置数据类型和自定义类型的对象。7. static关键字的好处7.1 隐藏变量或函数、隔离错误,有利于模块化程序在编程中,难免会用到全局变量,全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,全局变量在所有的源文件中都是有效的。如果希望全局变量仅限于在本源文件中使用,在其他源文件中不能引用,也就是说限制其作用域只在定义该变量的源文件内有效,而在同一源程序的其他源文件中不能使用,这时,就可以通过在全局变量上加static来实现,使全局变量被定义成一个静态全局变量。这样就可以避免其他源文件使用该变量、避免其他源文件因为该变量引起的错误。起到了对其他源文件隐藏该变量和隔离错误的作用,有利于模块化程序。7.2 保持变量内容的持久性有时候,我们希望函数中局部变量的值在函数调用结束之后不会消失,仍然保留函数调用结束的值。即它所在的存储单元不释放。这时,应该将该局部变量用关关键字static声明为静态局部变量。当局部变量被声明为静态局部变量的时候,也就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区存放,全局变量也存放在静态存储区,静态局部变量与全局变量的主要区别就在于可见性,静态局部变量只在其被声明的代码块中是可见的。" }, { "title": "C++中vector的初始化及赋值方式", "url": "/posts/c++-vector-init/", "categories": "C++", "tags": "c++, vector", "date": "2022-03-17 19:39:44 +0800", "snippet": "一维向量1. 不带参数的构造函数初始化//初始化一个size为0的vectorvector<int> abc;2. 带参数的构造函数初始化//初始化size,但每个元素值为默认值vector<int> abc(10); //初始化了10个默认值为0的元素//初始化size,并且设置初始值vector<int> cde(10, 1); /...", "content": "一维向量1. 不带参数的构造函数初始化//初始化一个size为0的vectorvector<int> abc;2. 带参数的构造函数初始化//初始化size,但每个元素值为默认值vector<int> abc(10); //初始化了10个默认值为0的元素//初始化size,并且设置初始值vector<int> cde(10, 1); //初始化了10个值为1的元素3. 通过数组地址初始化int a[5] = {1, 2, 3, 4, 5};//通过数组a的地址初始化,注意地址是从0到5(左闭右开区间)vector<int> b(a, a + 5);4. 通过同类型的vector初始化(深拷贝,即不共享数据)vector<int> a(5, 1);//通过a初始化vector<int> b(a);5. 通过insert初始化//insert初始化方式将同类型的迭代器对应的始末区间(左闭右开区间)内的值插入到vector中vector<int> a(6, 6);vecot<int> b;//将a[0]~a[2]插入到b中,b.size()由0变为3b.insert(b.begin(), a.begin(), a.begin() + 3);insert也可通过数组地址区间实现插入int a[6] = {6, 6, 6, 6, 6, 6};vector<int> b;//将a的所有元素插入到b中b.insert(b.begin(), a, a + 6);此外,insert还可以插入m个值为n的元素//在b开始位置处插入3个6b.insert(b.begin(), 3, 6);6. 通过copy函数赋值vector<int> a(5, 1);int a1[5] = {2, 2, 2, 2, 2};vector<int> b(10);/*将a中元素全部拷贝到b开始的位置中,注意拷贝的区间为a.begin() ~ a.end()的左闭右开的区间*/copy(a.begin(), a.end(), b.begin());//拷贝区间也可以是数组地址构成的区间copy(a1, a1 + 5, b.begin() + 5);二维向量1. 向量 + 向量(常用方法)vector<vector<int> > myv(row, vector<int>(column,0));vector<vector<int> > myv(5, vector<int>(10,0)); // 两个维度都是向量cout<<myv.size()<<endl; // 正确,因为向量有size()函数vector<int> temp(2);myv.push_back(temp); // 正确,可以将向量插入到向量中或者:vector<vector<int> > myv(5); // 两个维度都是向量,只是第二个维度的向量长度为0cout<<myv.size()<<endl; // 5cout<<myv[0].size()<<endl; // 0vector<vector<int>> v(5) 大于号之间没有空格的初始化方式在C++11之后也是正确的,即C++11以后允许两个大于号之间没有空格。C++11是一种标准。2. 数组 + 向量(不建议)vector<int> myv[n]; // 此时第一维是数组,第二维才是向量。// cout<<myv.size()<<endl; // 错误,因为数组没有size()函数vector<int> temp(2);myv.push_back(temp); // 错误,无法将向量插入到数组中" }, { "title": "C++知识点整理及常见STL函数的使用", "url": "/posts/C++Syntax/", "categories": "C++", "tags": "stl, c++, syntax", "date": "2022-03-13 17:33:45 +0800", "snippet": "本篇博客不阐述原理,只是记录一些知识点以及常用的C++函数代码。知识点整理点运算符和箭头运算符这两个符号都是C++成员运算符1,主要用于确定类对象和成员之间的关系,用于引用类、结构和共用体的成员。箭头运算符->与一个指针对象的指针一起使用。如果是指针访问数据成员或成员函数,用->;点运算符.与实际的对象一起使用。如果是某个数据类型的对象,访问自己的数据成员和成员函数用.;举个例子...", "content": "本篇博客不阐述原理,只是记录一些知识点以及常用的C++函数代码。知识点整理点运算符和箭头运算符这两个符号都是C++成员运算符1,主要用于确定类对象和成员之间的关系,用于引用类、结构和共用体的成员。箭头运算符->与一个指针对象的指针一起使用。如果是指针访问数据成员或成员函数,用->;点运算符.与实际的对象一起使用。如果是某个数据类型的对象,访问自己的数据成员和成员函数用.;举个例子:string s1 = \"a string\",*p = &s1;int n = s1.size(); //运行string对象s1的size()成员n = (*p).size(); //运行p所指对象的size成员n = p->size(;) //等价于(*p).size左移右移运算符左移乘,右移除bitset.count()函数用于统计二进制数中1的数量。__builtin_popcount()函数也可以统计二进制中1的数量。#include<bits/stdc++.h>using namespace std;int main() { unsigned short short1 = 4; bitset<16> bitset1(short1); // the bitset representation of 4 cout << bitset1 << endl; // 0b00000000'00000100 unsigned short short2 = short1 << 1; // 4 left-shifted by 1 = 8 bitset<16> bitset2(short2); cout << bitset2 << endl; // 0b00000000'00001000 unsigned short short3 = short1 >> 2; // 4 right-shifted by 2 = 1 bitset<16> bitset3(short3); cout << bitset3 << endl; // 0b00000000'00000001 int int1 = 5; bitset<4> bitset4(int1);\t\t\t// 0b1001 cout << bitset4.count() << endl;\t// number of set bits in bitset4 = 2 cout << __builtin_popcount(5) << endl;\t// same as above}前中后序遍历前序(preorder): 根结点 -> 遍历左子树 -> 遍历右子树 (首先访问根结点)中序(inorder): 遍历左子树 -> 根结点 -> 遍历右子树后序(postorder): 遍历左子树 -> 遍历右子树 -> 根结点 (最后访问根结点)约瑟夫环简介:n 个人围成一个圈,每次数 k 个数,被数到的那个人出局。数学解法:int findTheWinner(int n, int k) { int p = 0; for (int i = 2; i <= n; i++) { p = (p + k) % i; } return p + 1;}队列:int findTheWinner(int n, int k) { queue<int> q; for(int i = 0; i < n; i++) q.push(i + 1); while(q.size() != 1){ for(int i = 0; i < k - 1; i++){ q.push(q.front()); q.pop(); } q.pop(); } return q.front();}常见STL函数的使用STL: Standard Template Librarylistemplace可以代替insert.emplace_back可以代替push_back.emplace_front可以代替push_front.mylist.front()mylist.back()mylist.begin()mylist.end()mylist.empty()mylist.erase()mylist.remove()mylist.insert()mylist.pop_back()mylist.pop_front()mylist.push_back()mylist.push_front()mylist.reverse()mylist.sort()mylist.unique()vectoremplace可以代替insert.emplace_back可以代替push_back.emplace_front可以代替push_front.myvector.front()myvector.back()myvector.size()myvector.begin()myvector.end()for(vector<int>::iterator it = myvector.begin();it!=myvector.end();it++){ cout<<*it<<endl;}myvector.clear()myvector.pop_back()myvector.push_back()myvector.empty()myvector.insert()queueemplace可以代替pushmyqueue.push()myqueue.pop()myqueue.empty()myqueue.size()myqueue.front()myqueue.back()stackemplace可以代替pushmystack.empty()mystack.pop()mystack.push()mystack.size()mystack.top() //栈顶,即出入口map & unordered_mapemplace可以代替insert// 不需要对key进行排列或数据量不小于10000的时候,用unordered_map.unordered_map<char,int> mymap;mymap['a'] = 100;mymap['b'] = 200;for(map<char,int>::iterator it = mymap.begin();it != mymap.end(); it++){ cout<<it->first<<\" => \" <<it->second<<endl;}mymap.size()mymap.clear()mymap.count(k) //return 1 if found or 0 otherwisemymap.erase(key)mymap.erase(iterator first,iterator last)it = mymap.find('b')mymap.insert()set & unordered_setemplace可以代替insertunordered_set<int> myset;unordered_set<pair<int, string>> myset;priority_queue优先队列与堆类似。默认大顶堆。 和队列基本操作相同: top() 访问队头元素 empty() 队列是否为空 size() 返回队列内元素个数 push() 插入元素到队尾 (并排序) emplace() 原地构造一个元素并插入队列 pop() 弹出队头元素 //大顶堆,即降序队列priority_queue<int> big_heap;priority_queue<int, vector<int>, less<int> > big_heap2; //小顶堆,即升序队列priority_queue<int, vector<int>, greater<int> > small_heap; pair的比较,先比较第一个元素,第一个相等比较第二个//大顶堆,即降序队列priority_queue<pair<int, int> > big_pair_heap;//小顶堆,即升序队列priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> small_pair_heap;lower_boundlower_bound()和upper_bound()是利用二分查找的方法在一个排好序的容器中进行查找,比如set,vector,map容器。具体用法如下所示: lower_bound(value): 第一个大于等于(可以是字典序,也可以是值)value的下标或者指针 upper_bound(value): 第一个大于(可以是字典序,也可以是值)value的下标或者指针map:map<char,int> mymap;mymap['a']=20;mymap['b']=40;mymap['c']=60;mymap['d']=80;mymap['f']=100;map<char,int>::iterator itlow=mymap.lower_bound ('b'); // itlow points to bmap<char,int>::iterator temp=mymap.upper_bound ('d'); // temp points to f (not d!)map<char,int>::iterator itup=mymap.upper_bound ('e'); // there's no e, so itup points to fmymap.erase(itlow,itup); // erases [itlow,itup)set:set<int> myset;for (int i=1; i<10; i++) myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90set<int>::iterator temp1=myset.lower_bound (25); // 30 >= 25,so temp1 points to 30set<int>::iterator temp2=myset.lower_bound (60); // 70 > 60,so temp2 points to 70set<int>::iterator itlow=myset.lower_bound (30); // itlow points to 30set<int>::iterator itup=myset.upper_bound (65); // itup points to 70myset.erase(itlow,itup); // erases [itlow,itup)string::findint index = mystring.find(s, pos);其中 s 可以是字符也可以是字符串,pos 是寻找字符或字符串的起始位置。string str = \"abcdefgabc\";int index = str.find(\"abc\"); // index = 0int index = str.find(\"abc\", 3); // index = 7string::substrstring str = mystring.substr(pos, len);2Example// string::substr#include <iostream>#include <string>int main (){ std::string str=\"We think in generalities, but we live in details.\"; // (quoting Alfred N. Whitehead) std::string str2 = str.substr (3,5); // \"think\" std::size_t pos = str.find(\"live\"); // position of \"live\" in str std::string str3 = str.substr (pos); // get from \"live\" to the end std::cout << str2 << ' ' << str3 << '\\n'; return 0;}Output:think live in details.string::to_stringstring str = to_string(a); 3include<bits/stdc++.h>struct person{ // structure double a; double b;}temp[100];getline(cin,s);int a; // int to stringstring str = to_string(a); string b; // string to intint int1 = atoi(b.c_str()); //遇到字母会自动停下,如果没有数字,则定义为0 。b为string类型的情况下还需要使用c_str()函数int int2 = stoi(b); //遇到字母会自动停下,如果没有数字,运行会出错string str; // length(string)int res = str.length();char *test; // length(char)int res = strlen(test);int num = 3;temp = string(num, 'a'); // \"aaa\"int i = 1, j = 4;int res = (i + j)/2; // 向下取整,res = 2;cout<<res<<endl;malloc动态分配与释放内存。与vector相比,使用malloc的效率更高。分配一维与二维数组代码如下所示:#include<bits/stdc++.h>using namespace std;int main(){\t// 1-dimensional\tint len = 10;\tint *p;\tp = (int *)malloc(len * sizeof(int));\tfree(p);\t// 2-dimensional \tint **a; int row = 3; int col = 3; a = (int **)malloc(row * sizeof(int*)); for(int i = 0; i < row; i++) { a[i] = (int *)malloc(col * sizeof(int)); } for(int i = 0; i < row; i++) { free(a[i]); } free(a);}代码备忘录素数判断bool is_prime(int s){ if(s <= 3) return s > 1; int sqt = sqrt(s); for(int i = 2; i <= sqt; i++) if(s % i == 0) return false; return true;}自定义排序bool cmp(string a, string b){ if(a.length() != b.length()) return a.length() < b.length(); // 按长度升序:a的长度小于b的长度,所以从左到右长度变大。 return a > b; // 按字典序降序:a的字典序大于b的字典序,所以从左到右字典序降低。}int main(){\tsort(temp1, temp1 + 4, cmp); // temp1为数组\tsort(temp2.begin(), temp2.end(), cmp); // temp2为向量} 最大最小值与sort()函数类似,可对数组、向量的任意区间求最大或最小值。#include<bits/stdc++.h>using namespace std;int main(){\tint temp_myv[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};\tvector<int> myv(temp_myv, temp_myv + 10);\tcout<<*min_element(temp_myv, temp_myv + 10)<<endl;\tcout<<*max_element(myv.begin(), myv.end())<<endl;}所有元素求和int a[5] = {1, 2, 3, 4, 5};vector<int> v(a);int sum = accumulate(a, a + 5, 0);int sum2 = accumulate(v.begin(), v.end(), 0);输出标准化输出标准化参考此博客45。cout<<fixed<<setprecision(1)<<a<<endl; // accurate to 1 decimal placecout<<setw(3)<<setfill('0')<<a<<endl; // filled with '0'printf(\"%03d\",a); // if a=1; 输出001cout<<hex<<12<<endl; // 十六进制输出cout<<oct<<12<<endl; // 八进制输出 cout<<left<<setw(5)<<10<<endl; // 左对齐 cout<<right<<setw(5)<<10<<endl; // 右对齐string a; int b,c;scanf(\"%s : %d :%d\", &a[0], &b, &c); //可以隔着冒号取值,记得引用符号&printf(\"%s\\n\",a);回文判断用于判断字符串s中的任意子串是否回文。动态规划法:judge[start][end]表示字符串s中[start, end]的子串是否为回文串。int n = s.length();vector<vector<bool> > judge(n, vector<bool>(n, true));// 从后往前去遍历for (int i = n - 1; i >= 0; --i){ for (int j = i + 1; j < n; ++j) { // 判断i和j是否相等,相等则扩展 judge[i][j] = (s[i] == s[j]) && judge[i + 1][j - 1]; }}双指针遍历法:描述同动态规划法,适用于单次简单判断。若要判断字符串s的每个子串是否为回文串,复杂度则为O(n^3)。bool judge(string s, int start, int end){ // 双指针遍历 for(int i = start; i <= (start + end) / 2; i++){ if(s[i] != s[end + start - i]) return false; } return true;}二分查找详细的二分查找分析见此。int nums[5] = {10, 20, 30, 40, 50};int flag = -1, target = 16;int i = 0, j = n - 1; // 前闭后闭while(i <= j){ // 因此i > j(即i == j + 1)时退出循环 int mid = i + (j - i)/2; if(target == nums[mid]){ flag = mid; break; } else if(target > nums[mid]) i = mid + 1; // mid已经遍历,所以新的区间为[i, mid - 1]或[mid + 1, j] else j = mid - 1;}排列组合排列(permutation):\\[A_{n}^{m} = \\frac{n!}{(n - m)!} = (n - m + 1) * \\cdots * (n - 1) * n\\]int A(int n, int m) { int res = 1; for(int i = n - m + 1; i <= n; i++){ res *= i; } return res;}组合(combination):\\[C_{n}^{m} = \\frac{ A_{n}^{m} }{m!} = \\frac{n!}{(n - m)!m!} = C_{n}^{n-m}\\]int C(int n, int m) { m = min(m, n - m); int numerator = A(n, m);\t//分子 int denominator = A(m, m);\t//分母 return numerator / denominator;}字符串大小写判断及转换大小写判断:for (int i = 0; i < str.size(); i++) if(islower(str[i])) return false; // bool islower(int c);for (int i = 0; i < str.size(); i++) if(isupper(str[i])) return false; // bool isupper(int c);大小写转换:for (int i = 0; i < str.size(); i++) str[i] = tolower(str[i]); // int tolower(int c);for (int i = 0; i < str.size(); i++) str[i] = toupper(str[i]); // int toupper(int c);三个及以上的数字取最大最小值max()、min()函数中只有两个参数,意味着只能在两个数中取最大或者最小值。当需要对三个及以上的数字取最大或最小值时,可用以下方法。int x = 6, y = 2, z = 10;int m1 = max({x,y,z});int m2 = max<int>({x,y,z});int m3 = max({(int)x,y,z});double a = 9.2, b = 2.4, c = 5.5555;double n1 = min({a,b,c});double n2 = min<double>({a,b,c});double n3 = min({(double)a,b,c});或者:int maxn(int x, int y, int z){ return max(max(x, y), z);}随机数生成伪随机数: rand() / double(RAND_MAX) 产生随机数的范围是 [0, 1] rand() / double(RAND_MAX) * 2 * r 产生随机数范围为 [0, 2r] rand() / double(RAND_MAX) * 2 * r - r + x 产生随机数范围为 [x - r, x + r] 真随机数:#include <random>#include <iostream>int main(){ std::random_device rd; // random_device 是一个“真随机数”发生器,它的缺点就是耗能太大,所以尽量别奢侈地一直用它 std::mt19937 gen(rd()); // 用 random_device产生一个真随机数,用作“伪随机数发生器”的种子,此后就雪藏之 std::uniform_int_distribution<> dis(1, 6); // 一个正态“分布器”,高斯分布器是 std::normal_distribution for (int n=0; n<10; ++n) // 用 dis 变换 gen 所生成的随机 unsigned int 到 [1, 6] 中的 int std::cout << dis(gen) << ' '; std::cout << '\\n';}Reference C++ 成员运算符 ↩ string::substr ↩ string::to_string ↩ C++ cout格式化输出(输出格式) ↩ C++ 基本的输入输出 ↩ " }, { "title": "申请软著的时间周期记录以及注意事项", "url": "/posts/software-copyright-timeline/", "categories": "Tutorial, Software Copyright", "tags": "copyright, timeline", "date": "2022-03-11 20:01:10 +0800", "snippet": "Introduction版权中心官网链接:https://www.ccopyright.com.cn/。在没有加急的情况下,申请一篇软著的总体周期大概需要三四个月左右。如果时间紧迫,可以去某宝找机构加急办理(不建议)。另外,申请软著可能用到的模板材料放到文末的附件中,有需要自取。Timeline2020-07-24:填写并提交两篇软著申请电子稿2020-07-28:EMS邮寄所需材料2020...", "content": "Introduction版权中心官网链接:https://www.ccopyright.com.cn/。在没有加急的情况下,申请一篇软著的总体周期大概需要三四个月左右。如果时间紧迫,可以去某宝找机构加急办理(不建议)。另外,申请软著可能用到的模板材料放到文末的附件中,有需要自取。Timeline2020-07-24:填写并提交两篇软著申请电子稿2020-07-28:EMS邮寄所需材料2020-07-29:北京登记部签收2020-09-03:一篇受理登记,邮箱收到受理通知书。同天,另一篇软著材料提交不规范,收到补正通知书。2020-09-04:再次EMS邮寄材料不规范的替代材料。2020-09-05:北京登记部签收2020-09-17:补正软著成功受理2020-10-29:收到第一件软著登记证书的挂号信(寄信日期为10月26日)2020-11-13:收到第二件软著登记证书的挂号信(寄信日期为11月9日)第一篇软著时间线第二篇软著时间线第三篇软件著作初次邮寄材料的时间点是2020年12月17日左右,其余时间点见下。从受理到邮寄发放间隔了一个半月,可见效率之低下!第三篇软著时间线Notice 身份证复印件复印到同一张A4纸上。 使用说明书里的图片要清晰。 在合作开发的情况下,合作开发合同的落款一定要早于开发完成时间。 副本不收费 Attachment 模板材料 外部链接 " }, { "title": "Github合并两个不同的仓库", "url": "/posts/merge-two-repos/", "categories": "Tutorial, Github", "tags": "github, branch, commits, git", "date": "2022-03-07 21:55:10 +0800", "snippet": "Description两个独立的仓库A、B,将仓库B合并至仓库A的分支,并保留A、B的所有commits例如:将dumped-CompetitiveLin.github.io中的所有提交内容合并至CompetitiveLin.github.io的another分支。Solution1. 克隆主仓库代码git clone git@github.com:CompetitiveLin/Compet...", "content": "Description两个独立的仓库A、B,将仓库B合并至仓库A的分支,并保留A、B的所有commits例如:将dumped-CompetitiveLin.github.io中的所有提交内容合并至CompetitiveLin.github.io的another分支。Solution1. 克隆主仓库代码git clone git@github.com:CompetitiveLin/CompetitiveLin.github.io2. 添加需要合并远程仓库git remote add base git@github.com:CompetitiveLin/dumped-CompetitiveLin.github.io此时查看remote: git remote -v,如下图所示:如果需要删除remote: git remote rm base3. 把base远程仓库中数据抓取到本仓库git fetch base4. 切换到base分支上,命名为dumpedgit checkout -b another base/dumped查看branch分支:git branch5. 切换到main分支git checkout main6. 合并两个分支git merge another此时可能出现类似下图fatal: refusing to merge unrelated histories的报错信息。解决方法:git merge another --allow-unrelated-histories在合并时有可能两个分支对同一个文件都做了修改,这时需要解决冲突1,在windows下可以使用Github Desktop解决冲突问题。7. 提交git push origin anotherProblemsA. Permission denied (publickey)原因:没有配置ssh-key,没有权限2解决方法: ssh-keygen -t rsa -b 4096 -C \"your_email@example.com\" clip < ~/.ssh/id_rsa.pubSee here.Reference 在 GitHub 上解决合并冲突 ↩ Permission denied (publickey) ↩ " }, { "title": "Python实现点击图片获取HSV或BGR的值", "url": "/posts/get-hsv-and-bgr/", "categories": "Script, Python", "tags": "python, hsv, bgr, opencv", "date": "2022-03-04 13:35:49 +0800", "snippet": "单击图片即可得到HSV或BGR的值,代码如下:import cv2def getposHsv(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: print(\"HSV is\", HSV[y, x])def getposBgr(event, x, y, flags, param): if eve...", "content": "单击图片即可得到HSV或BGR的值,代码如下:import cv2def getposHsv(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: print(\"HSV is\", HSV[y, x])def getposBgr(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: print(\"BGR is\", image[y, x])image = cv2.imread('frame_B.jpg')HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)cv2.imshow(\"imageHSV\", HSV)cv2.imshow('image', image)cv2.setMouseCallback(\"imageHSV\", getposHsv)cv2.setMouseCallback(\"image\", getposBgr)cv2.waitKey(0)" }, { "title": "Python脚本制作coco格式的实例分割数据集", "url": "/posts/make-coco-dataset/", "categories": "Script, Python", "tags": "mmdetection, labelme, coco, python", "date": "2022-03-04 01:42:07 +0800", "snippet": "Introdution利用labelme制作coco格式的实例分割数据集,该数据集适用于mmdetection2.0中的mask部分。在mmdetection2.0框架下,利用coco格式的数据集进行实例分割默认只需要train2017和val2017两部分(当然也可以将test中的目录修改成test2017,但没必要)。mmdetection2.0框架下coco格式数据集文件如下图放置:(...", "content": "Introdution利用labelme制作coco格式的实例分割数据集,该数据集适用于mmdetection2.0中的mask部分。在mmdetection2.0框架下,利用coco格式的数据集进行实例分割默认只需要train2017和val2017两部分(当然也可以将test中的目录修改成test2017,但没必要)。mmdetection2.0框架下coco格式数据集文件如下图放置:(运行完本文的.py文件后即可生成以下文件夹)数据集标注方式:当一张图片里没有多个同类别的物体,使用car,computer,bottle等标签直接进行标注;当一张图片里同个类别有多个物体时,标签采用sofa-1,sofa-2,desk-1,desk-2等标签-数字的格式进行标注;如果同一物体在遮挡情况下被分为多个部分,则不同部分都用同一个标签(具体如下图所示,牙膏的三个部分标签均为toothpaste)。最后得到的json文件里的segmentation是分为三部分,bbox只存在一个(在笔者找的其他资料里,都是分为三个独立的牙膏部分,得到三个segmentation以及三个相应的bbox,显然不符合实际情况)。建议在使用labelme进行对数据集标注时,将生成的.json文件放置在与数据集相同的路径下以避免一些不必要的麻烦。即下图:否则,生成的json文件里的imagePath可能出现下图所示的情况:如果实在是没办法,没有在同一路径下,imagePath会比较复杂(可能是windows系统用\\分隔号,ubuntu系统则会是/分隔号,这一点是笔者的猜测),根据不同的情况修改后文代码。Solution各文件夹布局如下所示:所有的.jpg文件放在images文件夹下,所有的.json文件(labelme标注完成后生成的文件)放在labelme/total2017文件夹下。creat_txt.py# !/usr/bin/python# -*- coding: utf-8 -*-import osimport randomtrainval_percent = 1 # No test sampletrain_percent = 0.9jsonfilepath = 'labelme/total2017'txtsavepath = './'total_xml = os.listdir(jsonfilepath)num = len(total_xml)list = range(num)tv = int(num * trainval_percent)tr = int(tv * train_percent)trainval = random.sample(list, tv)train = random.sample(trainval, tr)ftrainval = open('./trainval2017.txt', 'w')ftrain = open('./train2017.txt', 'w')fval = open('./val2017.txt', 'w')ftest = open('./test2017.txt', 'w') #Still create test2017.txtfor i in list: name = total_xml[i][:-5] + '\\n' if i in trainval: ftrainval.write(name) if i in train: ftrain.write(name) else: fval.write(name) else: ftest.write(name)ftrainval.close()ftrain.close()fval.close()ftest.close()print('Create_txt Done')classify.pyimport shutilimport osimport os.path as ospsets=['train2017', 'val2017', 'test2017']for image_set in sets: if osp.exists(image_set): shutil.rmtree(image_set) print('Deleted previous %s file and created a new one'%(image_set)) os.makedirs(image_set) json_path = 'labelme/%s'%(image_set) if osp.exists(json_path): shutil.rmtree(json_path) print('Deleted previous %s file and created a new one' % (json_path)) os.makedirs(json_path) image_ids = open('./%s.txt'%(image_set)).read().strip().split() for image_id in image_ids: img = 'images/%s.jpg' % (image_id) json = 'labelme/total2017/%s.json'% (image_id) shutil.copy(img,image_set) shutil.copy(json,'labelme/%s/'% (image_set))print(\"Done\")labelme2coco.py#!/usr/bin/env pythonimport collectionsimport datetimeimport globimport jsonimport osimport os.path as ospimport sysimport numpy as npimport PIL.Imageimport labelmeimport shutiltry: import pycocotools.maskexcept ImportError: print('Please install pycocotools:\\n\\n pip install pycocotools\\n') sys.exit(1)def main(): sets = ['train2017','val2017','test2017'] output_dir = './annotations' if osp.exists(output_dir): print('Output directory already exists:', output_dir) shutil.rmtree(output_dir) os.makedirs(output_dir) print('Creating dataset:', output_dir) for set in sets: input_dir = './labelme/%s'%(set) filename = 'instances_%s'%(set) now = datetime.datetime.now() data = dict( info=dict( description=None, version=None, contributor=None, date_created=now.strftime('%Y-%m-%d %H:%M:%S.%f'), ), licenses=[dict( id=0, name=None, )], images=[ # license, url, file_name, height, width, date_captured, id ], type='instances', annotations=[ # segmentation, area, iscrowd, image_id, bbox, category_id, id ], categories=[ # supercategory, id, name ], ) class_name_to_id = {} for i, line in enumerate(open('labels.txt').readlines()): class_id = i - 1 # starts with -1 class_name = line.strip() if class_id == -1: assert class_name == '__ignore__' continue class_name_to_id[class_name] = class_id data['categories'].append(dict( supercategory=None, id=class_id, name=class_name, )) out_ann_file = osp.join(output_dir, filename+'.json') label_files = glob.glob(osp.join(input_dir, '*.json')) for image_id, label_file in enumerate(label_files): with open(label_file) as f: label_data = json.load(f) path=label_data['imagePath'].split(\"\\\\\") # 可能因为windows或ubuntu不同的系统用\\\\或/划分,详见前言三 img_file = './%s/'%(set) + path[-1] img = np.asarray(PIL.Image.open(img_file)) data['images'].append(dict( license=0, url=None, file_name=label_file.split('/')[-1].split('.')[0] + '.jpg', height=img.shape[0], width=img.shape[1], date_captured=None, id=image_id, )) masks = {} # for area segmentations = collections.defaultdict(list) # for segmentation for shape in label_data['shapes']: points = shape['points'] label = shape['label'] shape_type = shape.get('shape_type', None) mask = labelme.utils.shape_to_mask( img.shape[:2], points, shape_type ) if label in masks: masks[label] = masks[label] | mask else: masks[label] = mask points = np.asarray(points).flatten().tolist() segmentations[label].append(points) for label, mask in masks.items(): cls_name = label.split('-')[0] if cls_name not in class_name_to_id: continue cls_id = class_name_to_id[cls_name] mask = np.asfortranarray(mask.astype(np.uint8)) mask = pycocotools.mask.encode(mask) area = float(pycocotools.mask.area(mask)) bbox = pycocotools.mask.toBbox(mask).flatten().tolist() data['annotations'].append(dict( id=len(data['annotations']), image_id=image_id, category_id=cls_id, segmentation=segmentations[label], area=area, bbox=bbox, iscrowd=0, )) with open(out_ann_file, 'w') as f: json.dump(data, f,indent=4) print(set + ' is done')if __name__ == '__main__': main()上述三个文件按顺序执行即可。最后所有的文件夹如下图所示,如前文所提到的,在mmdetection中被利用到的只有annotations,train2017,val2017三个文件夹。本文的代码可以反复运行,因为代码中包含一些旧文件夹的删除以及新建,不会报错。Reference prepare_detection_dataset" }, { "title": "Github删除Commits记录", "url": "/posts/use-branch-to-rollback/", "categories": "Tutorial, Github", "tags": "github, branch, commits", "date": "2022-03-02 13:00:12 +0800", "snippet": "众所周知,已经Push到Github的Commits是不能撤销的。但是,我们可以通过创建分支并修改默认分支的方法让Repo回退至某个版本并删除该版本后的所有Commits记录。1. Description删除已Push的Commits并回退至旧版本。如下图所示,删除红框中的Commits记录并回退至箭头所指的版本。Revert Commits也能回退版本。但是其不同之处在于,Revert C...", "content": "众所周知,已经Push到Github的Commits是不能撤销的。但是,我们可以通过创建分支并修改默认分支的方法让Repo回退至某个版本并删除该版本后的所有Commits记录。1. Description删除已Push的Commits并回退至旧版本。如下图所示,删除红框中的Commits记录并回退至箭头所指的版本。Revert Commits也能回退版本。但是其不同之处在于,Revert Commits会保留所有的Commits。2. Solution本方法是在Windows的Github Desktop中操作的。但不管在哪个操作系统,解决该问题的思路是类似的。首先选择要回退到的Commit并Create branch, Name branch, Publish branch. 如下三图所示:接着,在Github上设置默认分支1,删除原分支,修改新建的分支名即可,如下三图所示:但是这样有个缺点:因为在Github主页上修改分支名并不会实时同步到Github Desktop,因此GitHub Desktop中会出现“混乱”,此时建议删除本地代码并重新Clone至本地。Reference GitHub Docs:更改默认分支 ↩ " }, { "title": "Windows配置Jekyll相关环境", "url": "/posts/install-jekyll/", "categories": "Tutorial, Jekyll", "tags": "getting started, windows 10, installation", "date": "2022-03-01 21:40:53 +0800", "snippet": "1. Install Ruby在Windows上使用RubyInstaller安装比较方便,在Ruby官网下载最新版本的RubyInstaller WITH DEVKIT。注意32位和64位版本的区分。安装:使用默认路径即可,避免出错;勾选添加到PATH,就不用手动添加环境变量了安装完成如图:这里需要勾选Run 'ridk install',在弹出来的安装界面中选择3,安装MSYS2 and...", "content": "1. Install Ruby在Windows上使用RubyInstaller安装比较方便,在Ruby官网下载最新版本的RubyInstaller WITH DEVKIT。注意32位和64位版本的区分。安装:使用默认路径即可,避免出错;勾选添加到PATH,就不用手动添加环境变量了安装完成如图:这里需要勾选Run 'ridk install',在弹出来的安装界面中选择3,安装MSYS2 and MINGW development toolchain:2. Install RubyGems在RubyGems官网下载ZIP格式的安装包,下载后解压到任意路径。进入解压目录,在终端输入命令:ruby setup.rb 或者直接双击setup.rb文件即可。3. Install Jekyll打开cmd输入以下命令并等待安装完成即可。gem install jekyllgem install jekyll-paginategem install bundler4. Check installationjekyll -vbundle -v输出版本信息则代表安装没问题。5. Create a repo安装完成,我们可以用jekyll命令创建一个博客模板,进入一个目录,打开命令行执行:jekyll new testblogcd testblogbundle exec jekyll serve大功告成!PS:遇到的问题A. cannot load such file – webrick (LoadError)问题描述:执行 bundle exec jekyll serve 时出现 cannot load such file -- webrick (LoadError) 错误,如下图所示解决方法:终端输入bundle add webrick.See here.Reference windows安装jekyll 搭建个人博客:Jekyll + Github Pages + VSCode" }, { "title": "It's a Long Story", "url": "/posts/it's-a-long-story/", "categories": "Tutorial, Jekyll", "tags": "getting started, syntax", "date": "1999-05-25 10:00:00 +0800", "snippet": "This post is to show Markdown syntax rendering on Chirpy, you can also use it as an example of writing. Now, let’s start looking at text and typography.ParagraphI wandered lonely as a cloudThat flo...", "content": "This post is to show Markdown syntax rendering on Chirpy, you can also use it as an example of writing. Now, let’s start looking at text and typography.ParagraphI wandered lonely as a cloudThat floats on high o’er vales and hills,When all at once I saw a crowd,A host, of golden daffodils;Beside the lake, beneath the trees,Fluttering and dancing in the breeze.Nested and mixed listsNested and mixed lists are an interesting beast1. It’s a corner case to make sure that Lists within lists do not break the ordered list numbering order Your list styles go deep enough.Ordered list Firstly Firstly Secondly Thirdly Secondly Firstly Secondly Thirdly Thirdly Firstly Secondly Thirdly Unordered list Chapter Section Paragraph Task Lists Finish my changes Push my commits to GitHub Open a pull requestDescription list Sun the star around which the earth orbits Moon the natural satellite of the earth, visible by reflected light from the sunQuote Only one thing is impossible for God: To find any sense in any copyright law on the planet. Mark TwainPrompts An example showing the tip type prompt. An example showing the info type prompt. An example showing the warning type prompt. An example showing the danger type prompt.Twitter embedded🎨 Finally got around to adding all my @procreateapp creations with time lapse videos https://t.co/1nNbkefC3L pic.twitter.com/gcNLJoJ0Gn— Michael Rose (@mmistakes) November 6, 2015This post tests Twitter Embeds.Video embeddedYouTube video embedded below.ENGLISH SPEECH | MUNIBA MAZARI - We all are Perfectly Imperfect (English Subtitles)Here is the code:<iframe width=\"640\" height=\"360\" src=\"https://www.youtube.com/embed/fBnAMUkNM2k\" frameborder=\"0\" allowfullscreen></iframe>Tables Header1 Header2 Header3 left center right left center right left center right left center right Image alignmentWelcome to image alignment! The best way to demonstrate the ebb and flow of the various image positioning options is to nestle them snuggly among an ocean of words. Grab a paddle and let’s get started.]The image above happens to be centered. The rest of this paragraph is filler for the sake of seeing the text wrap around the 150×150 image, which is left aligned.As you can see there should be some space above, below, and to the right of the image. The text should not be creeping on the image. Creeping is just not right. Images need breathing room too. Let them speak like you words. Let them do their jobs without any hassle from the text. In about one more sentence here, we’ll see that the text moves from the right of the image down below the image in seamless transition. Again, letting the do it’s thing. Mission accomplished!And now for a massively large image. It also has no alignment.The image above, though 1200px wide, should not overflow the content area. It should remain contained with no visible disruption to the flow of content.And now we’re going to shift things to the right align. Again, there should be plenty of room above, below, and to the left of the image. Just look at him there — Hey guy! Way to rock that right side. I don’t care what the left aligned image says, you look great. Don’t let anyone else tell you differently.In just a bit here, you should see the text start to wrap below the right aligned image and settle in nicely. There should still be plenty of room and everything should be sitting pretty. Yeah — Just like that. It never felt so good to be right.And just when you thought we were done, we’re going to do them all over again with captions! Once the position is specified, the image caption should not be added.With caption Default (with caption)Full screen width and center alignment Shadowshadow effect (visible in light mode) Left aligned Float to left “A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space.” Float to right “A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space.” Mermaid SVG gantt title Adding GANTT diagram functionality to mermaid apple :a, 2017-07-20, 1w banana :crit, b, 2017-07-23, 1d cherry :active, c, after b a, 1dMathematicsThe mathematics powered by MathJax:\\[\\sum_{n=1}^\\infty 1/n^2 = \\frac{\\pi^2}{6}\\]When $a \\ne 0$, there are two solutions to $ax^2 + bx + c = 0$ and they are\\[x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}\\]FilepathHere is the /path/to/the/file.extend.Code blockCommonThis is a common code snippet, without syntax highlight and line number.Specific LanguagesUsing ```{language} you will get a code block with syntax highlight:```yamlkey: value```Console$ env |grep SHELLSHELL=/usr/local/bin/bashPYENV_SHELL=bashShellif [ $? -ne 0 ]; then echo \"The command was not successful.\"; #do the needful / exitfi;Specific filename@import \"colors/light-typography\", \"colors/dark-typography\"Line NumberBy default, all languages except plaintext, console, and terminal will display line numbers. When you want to hide the line number of a code block, add the class nolineno to it:```shellecho 'No more line numbers!'```{: .nolineno }Learn MoreFor more knowledge about Jekyll posts, visit the Jekyll Docs: Posts.Date modifiedThis post has been updated and show a modified date.Reference Texture image courtesty of Lovetextures ↩ " } ] diff --git a/assets/js/data/swcache.js b/assets/js/data/swcache.js new file mode 100644 index 000000000..03bc9944c --- /dev/null +++ b/assets/js/data/swcache.js @@ -0,0 +1 @@ +const resource = [ /* --- CSS --- */ '/assets/css/style.css', /* --- PWA --- */ '/app.js', '/sw.js', /* --- HTML --- */ '/index.html', '/404.html', '/categories/', '/tags/', '/archives/', '/about/', /* --- Favicons & compressed JS --- */ '/assets/img/favicons/android-chrome-192x192.png', '/assets/img/favicons/android-chrome-384x384.png', '/assets/img/favicons/android-chrome-512x512.png', '/assets/img/favicons/apple-touch-icon.png', '/assets/img/favicons/favicon-16x16.png', '/assets/img/favicons/favicon-32x32.png', '/assets/img/favicons/favicon.ico', '/assets/img/favicons/mstile-150x150.png', '/assets/js/dist/categories.min.js', '/assets/js/dist/commons.min.js', '/assets/js/dist/misc.min.js', '/assets/js/dist/page.min.js', '/assets/js/dist/post.min.js' ]; /* The request url with below domain will be cached */ const allowedDomains = [ '', 'fonts.gstatic.com', 'fonts.googleapis.com', 'cdn.jsdelivr.net', 'polyfill.io' ]; /* Requests that include the following path will be banned */ const denyUrls = [ ]; diff --git a/assets/js/dist/categories.min.js b/assets/js/dist/categories.min.js new file mode 100644 index 000000000..edb1eeca3 --- /dev/null +++ b/assets/js/dist/categories.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v5.6.1 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +!function(){"use strict";var o=$(".mode-toggle");function t(o,t){if(!(o instanceof t))throw new TypeError("Cannot call a class as a function")}function e(o,t){for(var e=0;e0}},{key:"topbarLocked",value:function(){return!0===o.topbarIsLocked}},{key:"unlockTopbar",value:function(){o.topbarIsLocked=!1}},{key:"getTopbarHeight",value:function(){return _.outerHeight()}},{key:"orientationLocked",value:function(){return!0===o.orientationIsLocked}},{key:"lockOrientation",value:function(){o.orientationIsLocked=!0}},{key:"unLockOrientation",value:function(){o.orientationIsLocked=!1}}]),o}();r(H,"scrollUpCount",0),r(H,"topbarIsLocked",!1),r(H,"orientationIsLocked",!1);var M,N=$("#search-input"),R=H.getTopbarHeight(),q=0;function A(){0!==$(window).scrollTop()&&(H.lockOrientation(),H.hideTopbar())}function Y(){var o=screen.orientation;o?o.onchange=function(){var t=o.type;"landscape-primary"!==t&&"landscape-secondary"!==t||A()}:$(window).on("orientationchange",(function(){$(window).width()<$(window).height()&&A()})),$(window).on("scroll",(function(){M||(M=!0)})),setInterval((function(){M&&(!function(){var o=$(window).scrollTop();if(!(Math.abs(q-o)<=R)){if(o>q)H.hideTopbar(),N.is(":focus")&&N.trigger("blur");else if(o+$(window).height()<$(document).height()){if(H.hasScrollUpTask())return;H.topbarLocked()?H.unlockTopbar():H.orientationLocked()?H.unLockOrientation():H.showTopbar()}q=o}}(),M=!1)}),250)}var z=$(".collapse");$(".code-header>button").children().attr("class"),$(window).on("scroll",(function(){$(window).scrollTop()>50&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()})),$("#back-to-top").on("click",(function(){return $("body,html").animate({scrollTop:0},800),!1})),$('[data-toggle="tooltip"]').tooltip(),0!==o.length&&o.off().on("click",(function(o){var t=$(o.target),e=t.prop("tagName")==="button".toUpperCase()?t:t.parent();modeToggle.flipMode(),e.trigger("blur")})),$("#sidebar-trigger").on("click",s.toggle),$("#mask").on("click",s.toggle),function(){if(0!==u.length&&!u.hasClass("dynamic-title")&&!f.is(":hidden")){var o=u.text().trim(),t=!1,e=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(o)&&(o=o.replace(/[0-9]/g,"").trim()),u.offset().top<$(window).scrollTop()&&f.text(o),new IntersectionObserver((function(n){if(t){var r=$(window).scrollTop(),a=e0}},{key:"topbarLocked",value:function(){return!0===o.topbarIsLocked}},{key:"unlockTopbar",value:function(){o.topbarIsLocked=!1}},{key:"getTopbarHeight",value:function(){return H.outerHeight()}},{key:"orientationLocked",value:function(){return!0===o.orientationIsLocked}},{key:"lockOrientation",value:function(){o.orientationIsLocked=!0}},{key:"unLockOrientation",value:function(){o.orientationIsLocked=!1}}]),o}();r(M,"scrollUpCount",0),r(M,"topbarIsLocked",!1),r(M,"orientationIsLocked",!1);var N,R=$("#search-input"),q=M.getTopbarHeight(),A=0;function Y(){0!==$(window).scrollTop()&&(M.lockOrientation(),M.hideTopbar())}function z(){var o=screen.orientation;o?o.onchange=function(){var e=o.type;"landscape-primary"!==e&&"landscape-secondary"!==e||Y()}:$(window).on("orientationchange",(function(){$(window).width()<$(window).height()&&Y()})),$(window).on("scroll",(function(){N||(N=!0)})),setInterval((function(){N&&(!function(){var o=$(window).scrollTop();if(!(Math.abs(A-o)<=q)){if(o>A)M.hideTopbar(),R.is(":focus")&&R.trigger("blur");else if(o+$(window).height()<$(document).height()){if(M.hasScrollUpTask())return;M.topbarLocked()?M.unlockTopbar():M.orientationLocked()?M.unLockOrientation():M.showTopbar()}A=o}}(),N=!1)}),250)}$(window).on("scroll",(function(){$(window).scrollTop()>50&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()})),$("#back-to-top").on("click",(function(){return $("body,html").animate({scrollTop:0},800),!1})),$('[data-toggle="tooltip"]').tooltip(),0!==o.length&&o.off().on("click",(function(o){var e=$(o.target),t=e.prop("tagName")==="button".toUpperCase()?e:e.parent();modeToggle.flipMode(),t.trigger("blur")})),$("#sidebar-trigger").on("click",s.toggle),$("#mask").on("click",s.toggle),function(){if(0!==u.length&&!u.hasClass("dynamic-title")&&!f.is(":hidden")){var o=u.text().trim(),e=!1,t=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(o)&&(o=o.replace(/[0-9]/g,"").trim()),u.offset().top<$(window).scrollTop()&&f.text(o),new IntersectionObserver((function(n){if(e){var r=$(window).scrollTop(),i=t0}},{key:"topbarLocked",value:function(){return!0===t.topbarIsLocked}},{key:"unlockTopbar",value:function(){t.topbarIsLocked=!1}},{key:"getTopbarHeight",value:function(){return P.outerHeight()}},{key:"orientationLocked",value:function(){return!0===t.orientationIsLocked}},{key:"lockOrientation",value:function(){t.orientationIsLocked=!0}},{key:"unLockOrientation",value:function(){t.orientationIsLocked=!1}}]),t}();r(V,"scrollUpCount",0),r(V,"topbarIsLocked",!1),r(V,"orientationIsLocked",!1);var A,H=$("#search-input"),M=V.getTopbarHeight(),N=0;function R(){0!==$(window).scrollTop()&&(V.lockOrientation(),V.hideTopbar())}function _(){var t=screen.orientation;t?t.onchange=function(){var e=t.type;"landscape-primary"!==e&&"landscape-secondary"!==e||R()}:$(window).on("orientationchange",(function(){$(window).width()<$(window).height()&&R()})),$(window).on("scroll",(function(){A||(A=!0)})),setInterval((function(){A&&(!function(){var t=$(window).scrollTop();if(!(Math.abs(N-t)<=M)){if(t>N)V.hideTopbar(),H.is(":focus")&&H.trigger("blur");else if(t+$(window).height()<$(document).height()){if(V.hasScrollUpTask())return;V.topbarLocked()?V.unlockTopbar():V.orientationLocked()?V.unLockOrientation():V.showTopbar()}N=t}}(),A=!1)}),250)}$(".collapse");$(".code-header>button").children().attr("class");var q=function(){function t(){e(this,t)}return n(t,null,[{key:"attrTimestamp",get:function(){return"data-ts"}},{key:"attrDateFormat",get:function(){return"data-df"}},{key:"locale",get:function(){return $("html").attr("lang").substring(0,2)}},{key:"getTimestamp",value:function(e){return Number(e.attr(t.attrTimestamp))}},{key:"getDateFormat",value:function(e){return e.attr(t.attrDateFormat)}}]),t}();$(window).on("scroll",(function(){$(window).scrollTop()>50&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()})),$("#back-to-top").on("click",(function(){return $("body,html").animate({scrollTop:0},800),!1})),$('[data-toggle="tooltip"]').tooltip(),0!==t.length&&t.off().on("click",(function(t){var e=$(t.target),o=e.prop("tagName")==="button".toUpperCase()?e:e.parent();modeToggle.flipMode(),o.trigger("blur")})),$("#sidebar-trigger").on("click",s.toggle),$("#mask").on("click",s.toggle),function(){if(0!==u.length&&!u.hasClass("dynamic-title")&&!f.is(":hidden")){var t=u.text().trim(),e=!1,o=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(t)&&(t=t.replace(/[0-9]/g,"").trim()),u.offset().top<$(window).scrollTop()&&f.text(t),new IntersectionObserver((function(n){if(e){var r=$(window).scrollTop(),a=o0}},{key:"topbarLocked",value:function(){return!0===t.topbarIsLocked}},{key:"unlockTopbar",value:function(){t.topbarIsLocked=!1}},{key:"getTopbarHeight",value:function(){return R.outerHeight()}},{key:"orientationLocked",value:function(){return!0===t.orientationIsLocked}},{key:"lockOrientation",value:function(){t.orientationIsLocked=!0}},{key:"unLockOrientation",value:function(){t.orientationIsLocked=!1}}]),t}();r(V,"scrollUpCount",0),r(V,"topbarIsLocked",!1),r(V,"orientationIsLocked",!1);var A,N=$("#search-input"),D=V.getTopbarHeight(),M=0;function q(){0!==$(window).scrollTop()&&(V.lockOrientation(),V.hideTopbar())}function z(){var t=screen.orientation;t?t.onchange=function(){var e=t.type;"landscape-primary"!==e&&"landscape-secondary"!==e||q()}:$(window).on("orientationchange",(function(){$(window).width()<$(window).height()&&q()})),$(window).on("scroll",(function(){A||(A=!0)})),setInterval((function(){A&&(!function(){var t=$(window).scrollTop();if(!(Math.abs(M-t)<=D)){if(t>M)V.hideTopbar(),N.is(":focus")&&N.trigger("blur");else if(t+$(window).height()<$(document).height()){if(V.hasScrollUpTask())return;V.topbarLocked()?V.unlockTopbar():V.orientationLocked()?V.unLockOrientation():V.showTopbar()}M=t}}(),A=!1)}),250)}$(".collapse");var B=".code-header>button",J="fas fa-check",Y="timeout",F="data-title-succeed",G="data-original-title",K=2e3;function Q(t){if($(t)[0].hasAttribute(Y)){var e=$(t).attr(Y);if(Number(e)>Date.now())return!0}return!1}function W(t){$(t).attr(Y,Date.now()+K)}function X(t){$(t).removeAttr(Y)}var Z,_=$(B).children().attr("class");$(window).on("scroll",(function(){$(window).scrollTop()>50&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()})),$("#back-to-top").on("click",(function(){return $("body,html").animate({scrollTop:0},800),!1})),$('[data-toggle="tooltip"]').tooltip(),0!==t.length&&t.off().on("click",(function(t){var e=$(t.target),o=e.prop("tagName")==="button".toUpperCase()?e:e.parent();modeToggle.flipMode(),o.trigger("blur")})),$("#sidebar-trigger").on("click",c.toggle),$("#mask").on("click",c.toggle),function(){if(0!==u.length&&!u.hasClass("dynamic-title")&&!f.is(":hidden")){var t=u.text().trim(),e=!1,o=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(t)&&(t=t.replace(/[0-9]/g,"").trim()),u.offset().top<$(window).scrollTop()&&f.text(t),new IntersectionObserver((function(n){if(e){var r=$(window).scrollTop(),a=o0}},{key:"topbarLocked",value:function(){return!0===t.topbarIsLocked}},{key:"unlockTopbar",value:function(){t.topbarIsLocked=!1}},{key:"getTopbarHeight",value:function(){return V.outerHeight()}},{key:"orientationLocked",value:function(){return!0===t.orientationIsLocked}},{key:"lockOrientation",value:function(){t.orientationIsLocked=!0}},{key:"unLockOrientation",value:function(){t.orientationIsLocked=!1}}]),t}();r(Y,"scrollUpCount",0),r(Y,"topbarIsLocked",!1),r(Y,"orientationIsLocked",!1);var F,N=$("#search-input"),U=Y.getTopbarHeight(),j=0;function A(){0!==$(window).scrollTop()&&(Y.lockOrientation(),Y.hideTopbar())}function M(){var t=screen.orientation;t?t.onchange=function(){var e=t.type;"landscape-primary"!==e&&"landscape-secondary"!==e||A()}:$(window).on("orientationchange",(function(){$(window).width()<$(window).height()&&A()})),$(window).on("scroll",(function(){F||(F=!0)})),setInterval((function(){F&&(!function(){var t=$(window).scrollTop();if(!(Math.abs(j-t)<=U)){if(t>j)Y.hideTopbar(),N.is(":focus")&&N.trigger("blur");else if(t+$(window).height()<$(document).height()){if(Y.hasScrollUpTask())return;Y.topbarLocked()?Y.unlockTopbar():Y.orientationLocked()?Y.unLockOrientation():Y.showTopbar()}j=t}}(),F=!1)}),250)}$(".collapse");var D=".code-header>button",H="fas fa-check",J="timeout",q="data-title-succeed",z="data-original-title",B=2e3;function G(t){if($(t)[0].hasAttribute(J)){var e=$(t).attr(J);if(Number(e)>Date.now())return!0}return!1}function Q(t){$(t).attr(J,Date.now()+B)}function W(t){$(t).removeAttr(J)}var X=$(D).children().attr("class");var Z=function(){function t(){e(this,t)}return n(t,null,[{key:"attrTimestamp",get:function(){return"data-ts"}},{key:"attrDateFormat",get:function(){return"data-df"}},{key:"locale",get:function(){return $("html").attr("lang").substring(0,2)}},{key:"getTimestamp",value:function(e){return Number(e.attr(t.attrTimestamp))}},{key:"getDateFormat",value:function(e){return e.attr(t.attrDateFormat)}}]),t}();var tt,et,ot=(tt=!1,function(){var t=tt;return tt||(tt=!0),t}),nt=function(){function t(t){return $(t).attr("content")}function e(e){var o=t(e);return void 0!==o&&!1!==o}return{getProxyMeta:function(){return t("meta[name=pv-proxy-endpoint]")},getLocalMeta:function(){return t("meta[name=pv-cache-path]")},hasProxyMeta:function(){return e("meta[name=pv-proxy-endpoint]")},hasLocalMeta:function(){return e("meta[name=pv-cache-path]")}}}(),rt=function(){var t={KEY_PV:"pv",KEY_PV_SRC:"pv_src",KEY_CREATION:"pv_created_date"},e="same-origin",o="cors";function n(t){return localStorage.getItem(t)}function r(t,e){localStorage.setItem(t,e)}function a(e,o){r(t.KEY_PV,e),r(t.KEY_PV_SRC,o),r(t.KEY_CREATION,(new Date).toJSON())}return{keysCount:function(){return Object.keys(t).length},hasCache:function(){return null!==localStorage.getItem(t.KEY_PV)},getCache:function(){return JSON.parse(localStorage.getItem(t.KEY_PV))},saveLocalCache:function(t){a(t,e)},saveProxyCache:function(t){a(t,o)},isExpired:function(){var e=new Date(n(t.KEY_CREATION));return e.setHours(e.getHours()+1),Date.now()>=e.getTime()},isFromLocal:function(){return n(t.KEY_PV_SRC)===e},isFromProxy:function(){return n(t.KEY_PV_SRC)===o},newerThan:function(t){return rt.getCache().totalsForAllResults["ga:pageviews"]>t.totalsForAllResults["ga:pageviews"]},inspectKeys:function(){if(localStorage.length===rt.keysCount())for(var e=0;ea&&function(t,e,o){if(t0)$(".post-preview").each((function(){var t=$(this).find("a").attr("href");at(o,t,$(this).find(".pageviews"),e)}));else if($(".post").length>0){var n=window.location.pathname;at(o,n,$("#pv"),e)}}}function ct(){nt.hasProxyMeta()&&$.ajax({type:"GET",url:nt.getProxyMeta(),dataType:"jsonp",success:function(t){it(t),rt.saveProxyCache(JSON.stringify(t))},error:function(t,e,o){console.log("Failed to load pageviews from proxy server: "+o)}})}function lt(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return fetch(nt.getLocalMeta()).then((function(t){return t.json()})).then((function(e){t&&rt.isFromProxy()&&rt.newerThan(e)||(it(e),rt.saveLocalCache(JSON.stringify(e)))}))}$(window).on("scroll",(function(){$(window).scrollTop()>50&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()})),$("#back-to-top").on("click",(function(){return $("body,html").animate({scrollTop:0},800),!1})),$('[data-toggle="tooltip"]').tooltip(),0!==t.length&&t.off().on("click",(function(t){var e=$(t.target),o=e.prop("tagName")==="button".toUpperCase()?e:e.parent();modeToggle.flipMode(),o.trigger("blur")})),$("#sidebar-trigger").on("click",l.toggle),$("#mask").on("click",l.toggle),function(){if(0!==u.length&&!u.hasClass("dynamic-title")&&!f.is(":hidden")){var t=u.text().trim(),e=!1,o=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(t)&&(t=t.replace(/[0-9]/g,"").trim()),u.offset().top<$(window).scrollTop()&&f.text(t),new IntersectionObserver((function(n){if(e){var r=$(window).scrollTop(),a=o Algorithm | Blogs
Home Categories Algorithm
Category
Cancel
diff --git a/categories/backend/index.html b/categories/backend/index.html new file mode 100644 index 000000000..fd072d1fb --- /dev/null +++ b/categories/backend/index.html @@ -0,0 +1 @@ + Backend | Blogs
Home Categories Backend
Category
Cancel
diff --git a/categories/c/index.html b/categories/c/index.html new file mode 100644 index 000000000..9beb0a593 --- /dev/null +++ b/categories/c/index.html @@ -0,0 +1 @@ + C++ | Blogs
Home Categories C++
Category
Cancel
diff --git a/categories/camera/index.html b/categories/camera/index.html new file mode 100644 index 000000000..be6019c0f --- /dev/null +++ b/categories/camera/index.html @@ -0,0 +1 @@ + Camera | Blogs
Home Categories Camera
Category
Cancel
diff --git a/categories/dev-c/index.html b/categories/dev-c/index.html new file mode 100644 index 000000000..78f772b8a --- /dev/null +++ b/categories/dev-c/index.html @@ -0,0 +1 @@ + Dev-C++ | Blogs
Home Categories Dev-C++
Category
Cancel
diff --git a/categories/github/index.html b/categories/github/index.html new file mode 100644 index 000000000..b7e99112b --- /dev/null +++ b/categories/github/index.html @@ -0,0 +1 @@ + Github | Blogs
Home Categories Github
Category
Cancel
diff --git a/categories/go/index.html b/categories/go/index.html new file mode 100644 index 000000000..f0f23826b --- /dev/null +++ b/categories/go/index.html @@ -0,0 +1 @@ + Go | Blogs
Home Categories Go
Category
Cancel
diff --git a/categories/ide-configuration/index.html b/categories/ide-configuration/index.html new file mode 100644 index 000000000..f4269022d --- /dev/null +++ b/categories/ide-configuration/index.html @@ -0,0 +1 @@ + IDE Configuration | Blogs
Home Categories IDE Configuration
Category
Cancel
diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 000000000..1c332ec52 --- /dev/null +++ b/categories/index.html @@ -0,0 +1 @@ + Categories | Blogs
Home Categories
Categories
Cancel

Categories

Algorithm 1 posts
Backend 5 categories , 6 posts
C++ 4 posts
IDE Configuration 1 categories , 1 posts
NUC11 1 posts
Script 1 categories , 2 posts
Tutorial 5 categories , 8 posts
Ubuntu 1 categories , 1 posts
diff --git a/categories/java/index.html b/categories/java/index.html new file mode 100644 index 000000000..d379e8468 --- /dev/null +++ b/categories/java/index.html @@ -0,0 +1 @@ + Java | Blogs
Home Categories Java
Category
Cancel
diff --git a/categories/jekyll/index.html b/categories/jekyll/index.html new file mode 100644 index 000000000..6d352a4bb --- /dev/null +++ b/categories/jekyll/index.html @@ -0,0 +1 @@ + Jekyll | Blogs
Home Categories Jekyll
Category
Cancel
diff --git a/categories/mathjax/index.html b/categories/mathjax/index.html new file mode 100644 index 000000000..7ffcc4c59 --- /dev/null +++ b/categories/mathjax/index.html @@ -0,0 +1 @@ + Mathjax | Blogs
Home Categories Mathjax
Category
Cancel
diff --git a/categories/mysql/index.html b/categories/mysql/index.html new file mode 100644 index 000000000..e21751ffe --- /dev/null +++ b/categories/mysql/index.html @@ -0,0 +1 @@ + MySQL | Blogs
Home Categories MySQL
Category
Cancel
diff --git a/categories/nuc11/index.html b/categories/nuc11/index.html new file mode 100644 index 000000000..5a3786fb9 --- /dev/null +++ b/categories/nuc11/index.html @@ -0,0 +1 @@ + NUC11 | Blogs
Home Categories NUC11
Category
Cancel
diff --git a/categories/python/index.html b/categories/python/index.html new file mode 100644 index 000000000..96c1b5c74 --- /dev/null +++ b/categories/python/index.html @@ -0,0 +1 @@ + Python | Blogs
Home Categories Python
Category
Cancel
diff --git a/categories/redis/index.html b/categories/redis/index.html new file mode 100644 index 000000000..63d340c9f --- /dev/null +++ b/categories/redis/index.html @@ -0,0 +1 @@ + Redis | Blogs
Home Categories Redis
Category
Cancel
diff --git a/categories/script/index.html b/categories/script/index.html new file mode 100644 index 000000000..491d489ba --- /dev/null +++ b/categories/script/index.html @@ -0,0 +1 @@ + Script | Blogs
Home Categories Script
Category
Cancel
diff --git a/categories/software-copyright/index.html b/categories/software-copyright/index.html new file mode 100644 index 000000000..606711bcf --- /dev/null +++ b/categories/software-copyright/index.html @@ -0,0 +1 @@ + Software Copyright | Blogs
Home Categories Software Copyright
Category
Cancel
diff --git a/categories/tutorial/index.html b/categories/tutorial/index.html new file mode 100644 index 000000000..4cd82d136 --- /dev/null +++ b/categories/tutorial/index.html @@ -0,0 +1 @@ + Tutorial | Blogs
Home Categories Tutorial
Category
Cancel
diff --git a/categories/ubuntu/index.html b/categories/ubuntu/index.html new file mode 100644 index 000000000..4a7dbf5db --- /dev/null +++ b/categories/ubuntu/index.html @@ -0,0 +1 @@ + Ubuntu | Blogs
Home Categories Ubuntu
Category
Cancel
diff --git a/categories/windows-10/index.html b/categories/windows-10/index.html new file mode 100644 index 000000000..d3671fe4e --- /dev/null +++ b/categories/windows-10/index.html @@ -0,0 +1 @@ + Windows 10 | Blogs
Home Categories Windows 10
Category
Cancel
diff --git a/categories/zookeeper/index.html b/categories/zookeeper/index.html new file mode 100644 index 000000000..5bbc62e79 --- /dev/null +++ b/categories/zookeeper/index.html @@ -0,0 +1 @@ + Zookeeper | Blogs
Home Categories Zookeeper
Category
Cancel
diff --git a/feed.xml b/feed.xml new file mode 100644 index 000000000..974ac90b0 --- /dev/null +++ b/feed.xml @@ -0,0 +1 @@ + /BlogsA minimal, responsive, and powerful Jekyll theme for presenting professional writing. 2024-09-24T09:23:33+08:00 CompetitiveLin / Jekyll © 2024 CompetitiveLin /assets/img/favicons/favicon.ico /assets/img/favicons/favicon-96x96.png Zookeeper 学习2024-08-11T09:56:08+08:00 2024-09-24T09:23:03+08:00 /posts/zookeeper/ CompetitiveLin Zookeeper 是什么 Zookeeper 是一个分布式的协调服务,可以实现 统一配置管理。比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置。将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。 统一命名服务。节点存储ip地址,只需要访问Znode节点就可以获取这些ip地址。 统一集群管理。Kafka 的集群管理基于Zookeeper。 统一服务管理。Dubbo 的服务发现基于Zookeeper。 分布式锁。通过在持久节点下建立临时顺序节点,可以保证锁的有序,监听机制保障锁传递的高效。 原理 文件系统 数据结构 文件系统类似的,整体上可以看成一棵树,每个节点称作一个 ZNode,每个 ZNode 都可以通过其路径得到唯一标识。默认只能最多存储 1MB 的数据 但与文件系统不同,每一个 ZNode... Go 语言学习2024-02-27T14:25:08+08:00 2024-09-03T10:05:07+08:00 /posts/go/ CompetitiveLin 基本语法 变量初始化 var s string = "string" var s = "string" s := "string" 二维切片初始化 slice1 := make([][]bool, m) for i := range slice1 { slice1[i] = make([]bool, n) } 变量自增 只有后缀自增或自减,并且必须单独一行(除了在range语句中)。 条件判断语句中的初始变量可以在布尔表达式里,并且不能使用0/1作为判断条件。 if cond := true; cond { fmt.Println() } 随机数 生成 [0, 99] 的随机数 rand.Seed(time.Now().UnixNano()) r := rand.Intn(100) fmt.Println(r) 栈/队列 Go 语言中的... Note from Work2023-09-07T10:41:22+08:00 2024-09-24T09:23:03+08:00 /posts/note-from-work/ CompetitiveLin Grafana 的数据显示会五分钟自动补全。当向 Prometheus 中插入某个时间戳的值时,其值会延续五分钟。 K8S 中的 Sidecar 模式:通常情况下一个 Pod 只包含一个容器,但是 Sidecar 模式是指为主容器提供额外功能(例如监控) 从而将其他容器加入到同一个 Pod 中。再例如 Istio 实现 Sidecar 自动注入。 Federated cluster,联邦集群 限流算法:漏桶和令牌桶算法,漏桶算法处理请求的速度固定,突发请求过多时会丢弃;令牌桶算法除了限制数据的平均传输速率外,还要求允许某种程度的突发传输。 常见 HTTP 状态码:2XX,成功响应;3XX,重定向消息;4XX,客户端错误响应;5XX,服务端错误响应。 请求分为四部分:请求... Kafka vs RocketMQ2023-08-25T17:10:22+08:00 2024-09-08T22:04:07+08:00 /posts/kafka-vs-rocketmq/ CompetitiveLin 基本概念 RocketMQ 由 Producer, Brocker, Consumer 组成 Producer 负责生产消息 Consumer 负责消费消息 Broker 负责存储消息,每一个 Broker 对应一台服务器但可以存储多个 Topic 的消息,每个 Topic 的消息也分片存储在不同的 Broker 里。 Topic 是逻辑概念,队列(Kafka 中叫分区)是物理概念。每个主题包含多个消息,每条消息只属于一个主题。一个 Producer 可以同时发送多种 Topic 的消息,而一个 Consumer 只能订阅一个 Topic 的消息。Tag 类似于子主题。 MessageQueue用于存储消息的物理地址,每个Topic中的消息地址存储于多个MessageQueue,是... SpringBoot2023-08-10T15:08:32+08:00 2024-09-21T23:09:06+08:00 /posts/springboot/ CompetitiveLin Spring, SpringBoot, Spring MVC 区别: Spring框架(Framework)是最流行的Java应用程序开发框架。 Spring框架的主要功能是依赖项注入或控制反转(IoC)。 Spring MVC是Spring的一个MVC框架,包含前端视图,文件配置等。XML和config配置比较复杂。 Spring Boot 是为简化Spring配置的快速开发整合包,允许构建具有最少配置或零配置的独立应用程序。 SpringBoot项目启动流程: 总体来说分两部分,先初始化 SpringApplication,再运行 SpringApplication。 运行 SpringApplication 又分为: 1、获取并启动监听器 2、根据监听器和参数来创建运行环境 3、准备 Banner 打印器 4、创建 Spring 容器 5、Sprin... diff --git a/images/header/devices-mockup.png b/images/header/devices-mockup.png new file mode 100644 index 000000000..c66fa4e5c Binary files /dev/null and b/images/header/devices-mockup.png differ diff --git a/images/header/unsplash-image-1.jpg b/images/header/unsplash-image-1.jpg new file mode 100644 index 000000000..86a9f3d6f Binary files /dev/null and b/images/header/unsplash-image-1.jpg differ diff --git a/images/posts/0-image-alignment-1200x4002.jpg b/images/posts/0-image-alignment-1200x4002.jpg new file mode 100644 index 000000000..8e3137cbe Binary files /dev/null and b/images/posts/0-image-alignment-1200x4002.jpg differ diff --git a/images/posts/0-image-alignment-150x150.jpg b/images/posts/0-image-alignment-150x150.jpg new file mode 100644 index 000000000..d3b0e4837 Binary files /dev/null and b/images/posts/0-image-alignment-150x150.jpg differ diff --git a/images/posts/0-image-alignment-300x200.jpg b/images/posts/0-image-alignment-300x200.jpg new file mode 100644 index 000000000..3921878b2 Binary files /dev/null and b/images/posts/0-image-alignment-300x200.jpg differ diff --git a/images/posts/0-image-alignment-580x300.jpg b/images/posts/0-image-alignment-580x300.jpg new file mode 100644 index 000000000..75bf08e90 Binary files /dev/null and b/images/posts/0-image-alignment-580x300.jpg differ diff --git a/images/posts/0-mockup.png b/images/posts/0-mockup.png new file mode 100644 index 000000000..530fbb0b0 Binary files /dev/null and b/images/posts/0-mockup.png differ diff --git a/images/posts/0-window.png b/images/posts/0-window.png new file mode 100644 index 000000000..e60b1d971 Binary files /dev/null and b/images/posts/0-window.png differ diff --git a/images/posts/1-1.png b/images/posts/1-1.png new file mode 100644 index 000000000..54112f92a Binary files /dev/null and b/images/posts/1-1.png differ diff --git a/images/posts/1-2.png b/images/posts/1-2.png new file mode 100644 index 000000000..7f7e83a23 Binary files /dev/null and b/images/posts/1-2.png differ diff --git a/images/posts/1-3.png b/images/posts/1-3.png new file mode 100644 index 000000000..5fe8a96a0 Binary files /dev/null and b/images/posts/1-3.png differ diff --git a/images/posts/1-4.png b/images/posts/1-4.png new file mode 100644 index 000000000..7cef4b2fd Binary files /dev/null and b/images/posts/1-4.png differ diff --git a/images/posts/1-5.png b/images/posts/1-5.png new file mode 100644 index 000000000..fe9d0e416 Binary files /dev/null and b/images/posts/1-5.png differ diff --git a/images/posts/1-6.png b/images/posts/1-6.png new file mode 100644 index 000000000..1011c9f0f Binary files /dev/null and b/images/posts/1-6.png differ diff --git a/images/posts/2-1.png b/images/posts/2-1.png new file mode 100644 index 000000000..f4102cd06 Binary files /dev/null and b/images/posts/2-1.png differ diff --git a/images/posts/2-2.png b/images/posts/2-2.png new file mode 100644 index 000000000..ef172f6d5 Binary files /dev/null and b/images/posts/2-2.png differ diff --git a/images/posts/2-3.png b/images/posts/2-3.png new file mode 100644 index 000000000..f3ee401a7 Binary files /dev/null and b/images/posts/2-3.png differ diff --git a/images/posts/2-4.png b/images/posts/2-4.png new file mode 100644 index 000000000..1e73839e5 Binary files /dev/null and b/images/posts/2-4.png differ diff --git a/images/posts/2-5.png b/images/posts/2-5.png new file mode 100644 index 000000000..7a85c244c Binary files /dev/null and b/images/posts/2-5.png differ diff --git a/images/posts/2-6.png b/images/posts/2-6.png new file mode 100644 index 000000000..2b211d9e5 Binary files /dev/null and b/images/posts/2-6.png differ diff --git a/images/posts/2-7.png b/images/posts/2-7.png new file mode 100644 index 000000000..acf29c74f Binary files /dev/null and b/images/posts/2-7.png differ diff --git a/images/posts/2022-03-31-19-02-51.png b/images/posts/2022-03-31-19-02-51.png new file mode 100644 index 000000000..23479b003 Binary files /dev/null and b/images/posts/2022-03-31-19-02-51.png differ diff --git a/images/posts/2022-04-17-14-00-49.png b/images/posts/2022-04-17-14-00-49.png new file mode 100644 index 000000000..ba30ae5ac Binary files /dev/null and b/images/posts/2022-04-17-14-00-49.png differ diff --git a/images/posts/2022-05-01-17-24-12.png b/images/posts/2022-05-01-17-24-12.png new file mode 100644 index 000000000..a67d52dc8 Binary files /dev/null and b/images/posts/2022-05-01-17-24-12.png differ diff --git a/images/posts/2022-05-21-13-20-16.png b/images/posts/2022-05-21-13-20-16.png new file mode 100644 index 000000000..e6b06d993 Binary files /dev/null and b/images/posts/2022-05-21-13-20-16.png differ diff --git a/images/posts/2022-05-21-14-15-21.png b/images/posts/2022-05-21-14-15-21.png new file mode 100644 index 000000000..d9ad6adb3 Binary files /dev/null and b/images/posts/2022-05-21-14-15-21.png differ diff --git a/images/posts/2022-06-19-22-51-30.png b/images/posts/2022-06-19-22-51-30.png new file mode 100644 index 000000000..a9f931dd4 Binary files /dev/null and b/images/posts/2022-06-19-22-51-30.png differ diff --git a/images/posts/2022-06-20-23-26-39.png b/images/posts/2022-06-20-23-26-39.png new file mode 100644 index 000000000..8b35fba07 Binary files /dev/null and b/images/posts/2022-06-20-23-26-39.png differ diff --git a/images/posts/3-1.png b/images/posts/3-1.png new file mode 100644 index 000000000..344546355 Binary files /dev/null and b/images/posts/3-1.png differ diff --git a/images/posts/3-2.png b/images/posts/3-2.png new file mode 100644 index 000000000..e578d1426 Binary files /dev/null and b/images/posts/3-2.png differ diff --git a/images/posts/3-3.png b/images/posts/3-3.png new file mode 100644 index 000000000..2503939b5 Binary files /dev/null and b/images/posts/3-3.png differ diff --git a/images/posts/3-4.png b/images/posts/3-4.png new file mode 100644 index 000000000..ec1ff7723 Binary files /dev/null and b/images/posts/3-4.png differ diff --git a/images/posts/3-5.png b/images/posts/3-5.png new file mode 100644 index 000000000..45d16c5ba Binary files /dev/null and b/images/posts/3-5.png differ diff --git a/images/posts/3-6.png b/images/posts/3-6.png new file mode 100644 index 000000000..93386a9b4 Binary files /dev/null and b/images/posts/3-6.png differ diff --git a/images/posts/3-7.png b/images/posts/3-7.png new file mode 100644 index 000000000..56a542002 Binary files /dev/null and b/images/posts/3-7.png differ diff --git a/images/posts/3-8.png b/images/posts/3-8.png new file mode 100644 index 000000000..db85051ba Binary files /dev/null and b/images/posts/3-8.png differ diff --git a/images/posts/4-1.png b/images/posts/4-1.png new file mode 100644 index 000000000..016a4f680 Binary files /dev/null and b/images/posts/4-1.png differ diff --git a/images/posts/4-2.png b/images/posts/4-2.png new file mode 100644 index 000000000..3db2f98b7 Binary files /dev/null and b/images/posts/4-2.png differ diff --git a/images/posts/5-1.png b/images/posts/5-1.png new file mode 100644 index 000000000..1039e4c00 Binary files /dev/null and b/images/posts/5-1.png differ diff --git a/images/posts/5-2.png b/images/posts/5-2.png new file mode 100644 index 000000000..88907ddf8 Binary files /dev/null and b/images/posts/5-2.png differ diff --git a/images/posts/5-3.png b/images/posts/5-3.png new file mode 100644 index 000000000..4b9ca5123 Binary files /dev/null and b/images/posts/5-3.png differ diff --git a/images/posts/7-2.png b/images/posts/7-2.png new file mode 100644 index 000000000..e5ef9c509 Binary files /dev/null and b/images/posts/7-2.png differ diff --git a/images/posts/8-1.png b/images/posts/8-1.png new file mode 100644 index 000000000..78b170fec Binary files /dev/null and b/images/posts/8-1.png differ diff --git a/images/posts/8-2.png b/images/posts/8-2.png new file mode 100644 index 000000000..107685dcd Binary files /dev/null and b/images/posts/8-2.png differ diff --git a/index.html b/index.html new file mode 100644 index 000000000..d19a2e787 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ + Blogs
Home
Blogs
Cancel

SpringBoot

Spring, SpringBoot, Spring MVC 区别: Spring框架(Framework)是最流行的Java应用程序开发框架。 Spring框架的主要功能是依赖项注入或控制反转(IoC)。 Spring MVC是Spring的一个MVC框架,包含前端视图,文件配置等。XML和config配置比较复杂。 Spring Boot 是为简化Spring配置的快速开发...

Redis 知识体系

单机 QPS 单机 QPS 能力参考范围为 8 - 10 万。 为什么 Redis 这么快 用 C 语言编写的,执行效率高 基于内存的数据库,避免磁盘IO操作 采用高效的数据结构 合理的数据编码,同样的数据结构在不同数据量的情况下采用不同的编码方式 采用单线程,避免上下文切换 多路IO复用,一个线程处理多个大量Socket请求。 虚拟内存 虚拟内存 R...

MySQL知识点汇总

单机QPS 单机 QPS 为 4k 左右。 MySQL select语句执行 prepare 阶段,检查查询语句中的表活字段是否存在,将 * 拓展为表上的所有列。 optimize 阶段,优化器决定使用哪个索引。 execute 阶段,执行器,索引下推。 表空间文件结构 组成:段(默认256MB) -&gt; 区(默认1MB) -&gt; 页(默认16KB) -&gt...

Basics of Java

基本知识 三大特点:封装继承多态。 语法糖:switch支持String、泛型、自动拆装箱、变长参数、枚举、内部类、条件编译、断言、数值下划线、for-each、try-with-resources、Lambda表达式 装箱:Integer i = Integer.valueOf(10), 拆箱:int n = i.intValue() 内存结构 运行时数据区域包含线程私有的程...

Java知识点记录博客

运算符优先级 优先级 运算符 1 ( ) [ ]  . 2 !  ~  ++  – 3 *  /  % 4 +  - 5 ...

It's a Long Story

This post is to show Markdown syntax rendering on Chirpy, you can also use it as an example of writing. Now, let’s start looking at text and typography. Paragraph I wandered lonely as a cloud Th...

Zookeeper 学习

Zookeeper 是什么 Zookeeper 是一个分布式的协调服务,可以实现 统一配置管理。比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置。将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。 统一命名服务。节点存储ip地址,只需要访问Znode节点就可以获取这些ip地址。 统一集群管理。Kafka 的集群管理基于Zooke...

Go 语言学习

基本语法 变量初始化 var s string = "string" var s = "string" s := "string" 二维切片初始化 slice1 := make([][]bool, m) for i := range slice1 { slice1[i] = make([]bool, n) } 变量自增 只有后缀自增或自减,并且必须单独一行(除了在ra...

Note from Work

Grafana 的数据显示会五分钟自动补全。当向 Prometheus 中插入某个时间戳的值时,其值会延续五分钟。 K8S 中的 Sidecar 模式:通常情况下一个 Pod 只包含一个容器,但是 Sidecar 模式是指为主容器提供额外功能(例如监控) 从而将其他容器加入到同一个 Pod 中。再例如 Istio 实现 Sidecar 自动注入。 ...

Kafka vs RocketMQ

基本概念 RocketMQ 由 Producer, Brocker, Consumer 组成 Producer 负责生产消息 Consumer 负责消费消息 Broker 负责存储消息,每一个 Broker 对应一台服务器但可以存储多个 Topic 的消息,每个 Topic 的消息也分片存储在不同的 Broker 里。 ...

diff --git a/norobots/index.html b/norobots/index.html new file mode 100644 index 000000000..380189923 --- /dev/null +++ b/norobots/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/page2/index.html b/page2/index.html new file mode 100644 index 000000000..d2b8e8139 --- /dev/null +++ b/page2/index.html @@ -0,0 +1 @@ + Blogs
Home
Blogs
Cancel

ElasticSearch

Elasticsearch是基于 Lucene 架构实现的分布式、海量数据的存储分析引擎,其中 Lucene 最主要的倒排索引结构,赋予了ES全文检索、模糊匹配、联合索引查询等等快速检索文档数据的能力,使得ES在这些查询的应用场景下优于数据库。适合用于搜索,不适合复杂的关系查询 倒排索引:被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。 写数据的过程 客户...

Windows修改注册表脚本

鼠标右键新建菜单栏中增加 New Markdown File: Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\.md] @="Typora.md" "Content Type"="text/markdown" "PerceivedType"="text" [HKEY_CLASSES_ROOT\.md\ShellNew...

线段树(Java实现)

线段树 class NumArray { int[] segmentTree; int n; public NumArray(int[] nums) { n = nums.length; segmentTree = new int [2 * n]; System.arraycopy(nums, 0, segment...

Ubuntu中关闭摄像头的自动曝光

目的 通过Python代码关闭杰锐微通摄像头的自动曝光功能。 尝试 查阅相关资料,有网友提出使用以下代码: capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25) 失败。 解决方案 先通过在终端输入v4l2-ctl --list-devices得到摄像头列表。 接着输入v4l2-ctl -d /dev/video2 --all查看单个摄像...

【解决方案】NUC11锁屏后无法唤醒

设备配置 型号:NUC11PAHi5 硬盘:Samsung 970Pro 1TB 内存:Samsung 3200MHz 8GB * 2 操作系统:Windows10 Pro 问题描述: 锁屏黑屏后有一定几率无法唤醒,右侧蓝色的电源灯常亮(非呼吸灯),左侧橙色的硬盘灯不亮(正常情况下会闪),只能通过长按电...

Windows任务栏时间显示秒

使用 win + r 打开运行,再输入 regedit 在打开的注册表中定位至HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced 新建 DWORD(32位)值,命名为ShowSecondsInSystemClock,将其数值置为 1 ...

Dev-C++支持C++11的操作

Click Tools and Complier Options Add -std=c++11

MathJax 数学公式语法

MathJax中的公式排版有两种方式,inline和displayed。 inline表示公式嵌入到文本段中,也称为行模式。$ f(x) = 3 \times x $这是一个inline公式 displayed表示公式独自成为一个段落,也成为块模式。下面则是一个displayed公式。 [f(x) = 3 \times x] 符号(Opera...

Difference between NEW and MALLOC in C++

Difference 1. 属性 new/delete是C++关键字,需要编译器支持。 malloc/free是库函数,需要头文件支持。 2. 参数 new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。 而malloc则需要显式地指出所需内存的尺寸。 3. 返回类型 ...

C++中静态成员及静态成员函数详解

1. 局部静态变量 局部静态变量用于函数体内部修饰变量,这种变量的生存期长于该函数。 用法:局部变量前加static,修饰局部变量为静态局部变量。 作用:改变局部变量的销毁时期。 作用域:局部作用域,当定义它的函数或语句块结束的时候,作用域结束。 与局部变量的区别: 静态局部变量在全局数据区(静态存储区)分配内存(局部变量在栈区分配内存); 静态局部变量在程序执行到该对...

diff --git a/page3/index.html b/page3/index.html new file mode 100644 index 000000000..45d8e039d --- /dev/null +++ b/page3/index.html @@ -0,0 +1 @@ + Blogs
Home
Blogs
Cancel

C++中vector的初始化及赋值方式

一维向量 1. 不带参数的构造函数初始化 //初始化一个size为0的vector vector&lt;int&gt; abc; 2. 带参数的构造函数初始化 //初始化size,但每个元素值为默认值 vector&lt;int&gt; abc(10); //初始化了10个默认值为0的元素 //初始化size,并且设置初始值 vector&lt;int&gt; cde...

C++知识点整理及常见STL函数的使用

本篇博客不阐述原理,只是记录一些知识点以及常用的C++函数代码。 知识点整理 点运算符和箭头运算符 这两个符号都是C++成员运算符1,主要用于确定类对象和成员之间的关系,用于引用类、结构和共用体的成员。 箭头运算符-&gt;与一个指针对象的指针一起使用。如果是指针访问数据成员或成员函数,用-&gt;; 点运算符.与实际的对象一起使用。如果是某个数据类型的对象,访问自己的数据成员和成...

申请软著的时间周期记录以及注意事项

Introduction 版权中心官网链接:https://www.ccopyright.com.cn/。 在没有加急的情况下,申请一篇软著的总体周期大概需要三四个月左右。如果时间紧迫,可以去某宝找机构加急办理(不建议)。 另外,申请软著可能用到的模板材料放到文末的附件中,有需要自取。 Timeline 2020-07-24:填写并提交两篇软著申请电子稿 2020-07-28:E...

Github合并两个不同的仓库

Description 两个独立的仓库A、B,将仓库B合并至仓库A的分支,并保留A、B的所有commits 例如:将dumped-CompetitiveLin.github.io中的所有提交内容合并至CompetitiveLin.github.io的another分支。 Solution 1. 克隆主仓库代码 git clone git@github.com:Competitive...

Python实现点击图片获取HSV或BGR的值

单击图片即可得到HSV或BGR的值,代码如下: import cv2 def getposHsv(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: print("HSV is", HSV[y, x]) def getposBgr(event, x, y, flags, param): ...

Python脚本制作coco格式的实例分割数据集

Introdution 利用labelme制作coco格式的实例分割数据集,该数据集适用于mmdetection2.0中的mask部分。 在mmdetection2.0框架下,利用coco格式的数据集进行实例分割默认只需要train2017和val2017两部分(当然也可以将test中的目录修改成test2017,但没必要)。 mmdetection2.0框架下coco格式数据集文...

Github删除Commits记录

众所周知,已经Push到Github的Commits是不能撤销的。但是,我们可以通过创建分支并修改默认分支的方法让Repo回退至某个版本并删除该版本后的所有Commits记录。 1. Description 删除已Push的Commits并回退至旧版本。如下图所示,删除红框中的Commits记录并回退至箭头所指的版本。 Revert Commits也能回退版本。但是其不同之处在于,...

Windows配置Jekyll相关环境

1. Install Ruby 在Windows上使用RubyInstaller安装比较方便,在Ruby官网下载最新版本的RubyInstaller WITH DEVKIT。注意32位和64位版本的区分。 安装:使用默认路径即可,避免出错;勾选添加到PATH,就不用手动添加环境变量了 安装完成如图: 这里需要勾选Run 'ridk install',在弹出来的安装界面中选择...

diff --git a/posts/C++Syntax/index.html b/posts/C++Syntax/index.html new file mode 100644 index 000000000..e2e8fd5a6 --- /dev/null +++ b/posts/C++Syntax/index.html @@ -0,0 +1,661 @@ + C++知识点整理及常见STL函数的使用 | Blogs
Home C++知识点整理及常见STL函数的使用
Post
Cancel

C++知识点整理及常见STL函数的使用

本篇博客不阐述原理,只是记录一些知识点以及常用的C++函数代码。

知识点整理

点运算符和箭头运算符

这两个符号都是C++成员运算符1,主要用于确定类对象和成员之间的关系,用于引用类、结构和共用体的成员。

箭头运算符->与一个指针对象的指针一起使用。如果是指针访问数据成员或成员函数,用->;

点运算符.与实际的对象一起使用。如果是某个数据类型的对象,访问自己的数据成员和成员函数用.;

举个例子:

1
+2
+3
+4
+
string s1 = "a string",*p = &s1;
+int n = s1.size();                  //运行string对象s1的size()成员
+n = (*p).size();                    //运行p所指对象的size成员
+n = p->size(;)                      //等价于(*p).size
+

左移右移运算符

左移乘,右移除

bitset.count()函数用于统计二进制数中1的数量。

__builtin_popcount()函数也可以统计二进制中1的数量。

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
#include<bits/stdc++.h>
+using namespace std;
+int main() {
+    unsigned short short1 = 4;
+    bitset<16> bitset1(short1);   // the bitset representation of 4
+    cout << bitset1 << endl;  // 0b00000000'00000100
+
+    unsigned short short2 = short1 << 1;     // 4 left-shifted by 1 = 8
+    bitset<16> bitset2(short2);
+    cout << bitset2 << endl;  // 0b00000000'00001000
+
+    unsigned short short3 = short1 >> 2;     // 4 right-shifted by 2 = 1
+    bitset<16> bitset3(short3);
+    cout << bitset3 << endl;  // 0b00000000'00000001
+    
+    int int1 = 5;
+    bitset<4> bitset4(int1);			// 0b1001 
+    cout << bitset4.count() << endl;	// number of set bits in bitset4 = 2
+    cout << __builtin_popcount(5) << endl;	// same as above
+}
+

前中后序遍历

前序(preorder): 根结点 -> 遍历左子树 -> 遍历右子树 (首先访问根结点)

中序(inorder): 遍历左子树 -> 根结点 -> 遍历右子树

后序(postorder): 遍历左子树 -> 遍历右子树 -> 根结点 (最后访问根结点)

约瑟夫环

简介:n 个人围成一个圈,每次数 k 个数,被数到的那个人出局。

数学解法:

1
+2
+3
+4
+5
+6
+7
+
int findTheWinner(int n, int k) {
+    int p = 0;
+    for (int i = 2; i <= n; i++) {
+        p = (p + k) % i;
+    }
+    return p + 1;
+}
+

队列:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
int findTheWinner(int n, int k) {
+    queue<int> q;
+    for(int i = 0; i < n; i++) q.push(i + 1);
+    while(q.size() != 1){
+        for(int i = 0; i < k - 1; i++){
+            q.push(q.front());
+            q.pop();
+        }
+        q.pop();
+    }
+    return q.front();
+}
+

常见STL函数的使用

STL: Standard Template Library

list

emplace可以代替insert. emplace_back可以代替push_back. emplace_front可以代替push_front.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
mylist.front()
+mylist.back()
+mylist.begin()
+mylist.end()
+mylist.empty()
+
+mylist.erase()
+mylist.remove()
+
+mylist.insert()
+
+mylist.pop_back()
+mylist.pop_front()
+mylist.push_back()
+mylist.push_front()
+
+mylist.reverse()
+mylist.sort()
+
+mylist.unique()
+

vector

emplace可以代替insert. emplace_back可以代替push_back. emplace_front可以代替push_front.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
myvector.front()
+myvector.back()
+myvector.size()
+
+myvector.begin()
+myvector.end()
+
+for(vector<int>::iterator it = myvector.begin();it!=myvector.end();it++){
+    cout<<*it<<endl;
+}
+
+myvector.clear()
+
+myvector.pop_back()
+myvector.push_back()
+
+myvector.empty()
+myvector.insert()
+

queue

emplace可以代替push

1
+2
+3
+4
+5
+6
+
myqueue.push()
+myqueue.pop()
+myqueue.empty()
+myqueue.size()
+myqueue.front()
+myqueue.back()
+

stack

emplace可以代替push

1
+2
+3
+4
+5
+
mystack.empty()
+mystack.pop()
+mystack.push()
+mystack.size()
+mystack.top()  //栈顶,即出入口
+

map & unordered_map

emplace可以代替insert

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
// 不需要对key进行排列或数据量不小于10000的时候,用unordered_map.
+
+unordered_map<char,int> mymap;
+mymap['a'] = 100;
+mymap['b'] = 200;
+
+for(map<char,int>::iterator it = mymap.begin();it != mymap.end(); it++){
+    cout<<it->first<<" => " <<it->second<<endl;
+}
+
+mymap.size()
+
+mymap.clear()
+
+mymap.count(k)  //return 1 if found or 0 otherwise
+
+mymap.erase(key)
+mymap.erase(iterator first,iterator last)
+
+it = mymap.find('b')
+mymap.insert()
+

set & unordered_set

emplace可以代替insert

1
+2
+3
+
unordered_set<int> myset;
+
+unordered_set<pair<int, string>> myset;
+

priority_queue

优先队列类似。默认大顶堆。

和队列基本操作相同:

  • top() 访问队头元素
  • empty() 队列是否为空
  • size() 返回队列内元素个数
  • push() 插入元素到队尾 (并排序)
  • emplace() 原地构造一个元素并插入队列
  • pop() 弹出队头元素
1
+2
+3
+4
+5
+
//大顶堆,即降序队列
+priority_queue<int> big_heap;
+priority_queue<int, vector<int>, less<int> > big_heap2; 
+//小顶堆,即升序队列
+priority_queue<int, vector<int>, greater<int> > small_heap;  
+

pair的比较,先比较第一个元素,第一个相等比较第二个

1
+2
+3
+4
+
//大顶堆,即降序队列
+priority_queue<pair<int, int> > big_pair_heap;
+//小顶堆,即升序队列
+priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> small_pair_heap;
+

lower_bound

lower_bound()upper_bound()是利用二分查找的方法在一个排好序的容器中进行查找,比如set,vector,map容器。具体用法如下所示:

  • lower_bound(value): 第一个大于等于(可以是字典序,也可以是值)value的下标或者指针
  • upper_bound(value): 第一个大于(可以是字典序,也可以是值)value的下标或者指针

map:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
map<char,int> mymap;
+mymap['a']=20;
+mymap['b']=40;
+mymap['c']=60;
+mymap['d']=80;
+mymap['f']=100;
+map<char,int>::iterator itlow=mymap.lower_bound ('b');  // itlow points to b
+map<char,int>::iterator temp=mymap.upper_bound ('d');   // temp points to f (not d!)
+map<char,int>::iterator itup=mymap.upper_bound ('e');   // there's no e, so itup points to f
+mymap.erase(itlow,itup);                                // erases [itlow,itup)
+

set:

1
+2
+3
+4
+5
+6
+7
+
set<int> myset;
+for (int i=1; i<10; i++) myset.insert(i * 10);        // 10 20 30 40 50 60 70 80 90
+set<int>::iterator  temp1=myset.lower_bound (25);     // 30 >= 25,so temp1 points to 30
+set<int>::iterator  temp2=myset.lower_bound (60);     // 70 > 60,so temp2 points to 70
+set<int>::iterator  itlow=myset.lower_bound (30);     // itlow points to 30
+set<int>::iterator  itup=myset.upper_bound (65);      // itup  points to 70
+myset.erase(itlow,itup);                              // erases [itlow,itup)
+

string::find

int index = mystring.find(s, pos);

其中 s 可以是字符也可以是字符串,pos 是寻找字符或字符串的起始位置。

1
+2
+3
+
string str = "abcdefgabc";
+int index = str.find("abc");    // index = 0
+int index = str.find("abc", 3); // index = 7
+

string::substr

string str = mystring.substr(pos, len);2

Example

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
// string::substr
+#include <iostream>
+#include <string>
+
+int main ()
+{
+  std::string str="We think in generalities, but we live in details.";  // (quoting Alfred N. Whitehead)
+
+  std::string str2 = str.substr (3,5);     // "think"
+
+  std::size_t pos = str.find("live");      // position of "live" in str
+
+  std::string str3 = str.substr (pos);     // get from "live" to the end
+
+  std::cout << str2 << ' ' << str3 << '\n';
+
+  return 0;
+}
+

Output:

think live in details.

string::to_string

string str = to_string(a); 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
+
include<bits/stdc++.h>
+
+struct person{                   // structure
+ double a;
+ double b;
+}temp[100];
+
+getline(cin,s);
+
+int a;                           // int to string
+string str = to_string(a);     
+
+string b;                        // string to int
+int int1 = atoi(b.c_str());      //遇到字母会自动停下,如果没有数字,则定义为0 。b为string类型的情况下还需要使用c_str()函数
+int int2 = stoi(b);              //遇到字母会自动停下,如果没有数字,运行会出错
+
+string str;                      // length(string)
+int res = str.length();
+
+char *test;                      // length(char)
+int res = strlen(test);
+
+int num = 3;
+temp = string(num, 'a');        // "aaa"
+
+int i = 1, j = 4;
+int res = (i + j)/2;    // 向下取整,res = 2;
+cout<<res<<endl;
+

malloc

动态分配与释放内存。与vector相比,使用malloc的效率更高。

分配一维与二维数组代码如下所示:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
#include<bits/stdc++.h>
+using namespace std;
+
+int main(){
+	// 1-dimensional
+	int len = 10;
+	int *p;
+	p  = (int *)malloc(len * sizeof(int));
+	free(p);
+	// 2-dimensional 
+	int **a;
+    int row = 3; int col = 3;
+    a = (int **)malloc(row * sizeof(int*));
+    for(int i = 0; i < row; i++)
+    {
+        a[i] = (int *)malloc(col * sizeof(int));
+    }
+    
+    for(int i = 0; i < row; i++)
+    {
+        free(a[i]);
+    }
+    free(a);
+}
+

代码备忘录

素数判断

1
+2
+3
+4
+5
+6
+
bool is_prime(int s){
+    if(s <= 3) return s > 1;
+    int sqt = sqrt(s);
+    for(int i = 2; i <= sqt; i++)  if(s % i == 0) return false;
+    return true;
+}
+

自定义排序

1
+2
+3
+4
+5
+6
+7
+8
+9
+
bool cmp(string a, string b){
+    if(a.length() != b.length()) return a.length() < b.length(); // 按长度升序:a的长度小于b的长度,所以从左到右长度变大。
+    return a > b;                   // 按字典序降序:a的字典序大于b的字典序,所以从左到右字典序降低。
+}
+
+int main(){
+	sort(temp1, temp1 + 4, cmp);            // temp1为数组
+	sort(temp2.begin(), temp2.end(), cmp);  // temp2为向量
+} 
+

最大最小值

sort()函数类似,可对数组、向量的任意区间求最大或最小值。

1
+2
+3
+4
+5
+6
+7
+8
+
#include<bits/stdc++.h>
+using namespace std;
+int main(){
+	int temp_myv[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+	vector<int> myv(temp_myv, temp_myv + 10);
+	cout<<*min_element(temp_myv, temp_myv + 10)<<endl;
+	cout<<*max_element(myv.begin(), myv.end())<<endl;
+}
+

所有元素求和

1
+2
+3
+4
+
int a[5] = {1, 2, 3, 4, 5};
+vector<int> v(a);
+int sum = accumulate(a, a + 5, 0);
+int sum2 = accumulate(v.begin(), v.end(), 0);
+

输出标准化

输出标准化参考此博客45

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
cout<<fixed<<setprecision(1)<<a<<endl;  // accurate to 1 decimal place
+
+cout<<setw(3)<<setfill('0')<<a<<endl;    // filled with '0'
+printf("%03d",a);   //  if a=1;   输出001
+
+
+cout<<hex<<12<<endl;                    // 十六进制输出
+cout<<oct<<12<<endl;                    // 八进制输出
+         
+cout<<left<<setw(5)<<10<<endl;        // 左对齐  
+cout<<right<<setw(5)<<10<<endl;       // 右对齐
+
+string a; 
+int b,c;
+scanf("%s : %d :%d", &a[0], &b, &c);     //可以隔着冒号取值,记得引用符号&
+printf("%s\n",a);
+

回文判断

用于判断字符串s中的任意子串是否回文。

动态规划法:

judge[start][end]表示字符串s[start, end]的子串是否为回文串。

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
int n = s.length();
+vector<vector<bool> > judge(n, vector<bool>(n, true));
+// 从后往前去遍历
+for (int i = n - 1; i >= 0; --i)
+{
+    for (int j = i + 1; j < n; ++j)
+    {
+        // 判断i和j是否相等,相等则扩展
+        judge[i][j] = (s[i] == s[j]) && judge[i + 1][j - 1];
+    }
+}
+

双指针遍历法:

描述同动态规划法,适用于单次简单判断。若要判断字符串s的每个子串是否为回文串,复杂度则为O(n^3)

1
+2
+3
+4
+5
+6
+
bool judge(string s, int start, int end){                               // 双指针遍历
+    for(int i = start; i <= (start + end) / 2; i++){
+        if(s[i] != s[end + start - i]) return false;
+    }
+    return true;
+}
+

二分查找

详细的二分查找分析见此

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
int nums[5] = {10, 20, 30, 40, 50};
+int flag = -1, target = 16;
+int i = 0, j = n - 1;           // 前闭后闭
+while(i <= j){                  // 因此i > j(即i == j + 1)时退出循环
+    int mid = i + (j - i)/2;
+    if(target == nums[mid]){
+        flag = mid;
+        break;
+    }
+    else if(target > nums[mid]) i = mid + 1;    // mid已经遍历,所以新的区间为[i, mid - 1]或[mid + 1, j]
+    else j = mid - 1;
+}
+

排列组合

排列(permutation):

\[A_{n}^{m} = \frac{n!}{(n - m)!} = (n - m + 1) * \cdots * (n - 1) * n\]
1
+2
+3
+4
+5
+6
+7
+
int A(int n, int m) {
+    int res = 1;
+    for(int i = n - m + 1; i <= n; i++){
+        res *= i;
+    }
+    return res;
+}
+

组合(combination):

\[C_{n}^{m} = \frac{ A_{n}^{m} }{m!} = \frac{n!}{(n - m)!m!} = C_{n}^{n-m}\]
1
+2
+3
+4
+5
+6
+
int C(int n, int m) {
+    m = min(m, n - m);
+    int numerator =   A(n, m);	//分子
+    int denominator = A(m, m);	//分母
+    return numerator / denominator;
+}
+

字符串大小写判断及转换

大小写判断:

1
+2
+
for (int i = 0; i < str.size(); i++) if(islower(str[i])) return false;      // bool islower(int c);
+for (int i = 0; i < str.size(); i++) if(isupper(str[i])) return false;      // bool isupper(int c);
+

大小写转换:

1
+2
+
for (int i = 0; i < str.size(); i++) str[i] = tolower(str[i]);      // int tolower(int c);
+for (int i = 0; i < str.size(); i++) str[i] = toupper(str[i]);      // int toupper(int c);
+

三个及以上的数字取最大最小值

max()min()函数中只有两个参数,意味着只能在两个数中取最大或者最小值。

当需要对三个及以上的数字取最大或最小值时,可用以下方法。

1
+2
+3
+4
+5
+6
+7
+8
+9
+
int x = 6, y = 2, z = 10;
+int m1 = max({x,y,z});
+int m2 = max<int>({x,y,z});
+int m3 = max({(int)x,y,z});
+
+double a = 9.2, b = 2.4, c = 5.5555;
+double n1 = min({a,b,c});
+double n2 = min<double>({a,b,c});
+double n3 = min({(double)a,b,c});
+

或者:

1
+2
+3
+
int maxn(int x, int y, int z){
+    return max(max(x, y), z);
+}
+

随机数生成

伪随机数:

  • rand() / double(RAND_MAX) 产生随机数的范围是 [0, 1]

  • rand() / double(RAND_MAX) * 2 * r 产生随机数范围为 [0, 2r]

  • rand() / double(RAND_MAX) * 2 * r - r + x 产生随机数范围为 [x - r, x + r]

真随机数:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
#include <random>
+#include <iostream>
+
+int main()
+{
+    std::random_device rd;  // random_device 是一个“真随机数”发生器,它的缺点就是耗能太大,所以尽量别奢侈地一直用它
+    std::mt19937 gen(rd()); // 用 random_device产生一个真随机数,用作“伪随机数发生器”的种子,此后就雪藏之
+    std::uniform_int_distribution<> dis(1, 6); // 一个正态“分布器”,高斯分布器是 std::normal_distribution
+ 
+    for (int n=0; n<10; ++n)
+        // 用 dis 变换 gen 所生成的随机 unsigned int 到 [1, 6] 中的 int
+        std::cout << dis(gen) << ' ';
+    std::cout << '\n';
+}
+

Reference

This post is licensed under CC BY 4.0 by the author.

申请软著的时间周期记录以及注意事项

C++中vector的初始化及赋值方式

diff --git a/posts/c++-vector-init/index.html b/posts/c++-vector-init/index.html new file mode 100644 index 000000000..9685d34f1 --- /dev/null +++ b/posts/c++-vector-init/index.html @@ -0,0 +1,91 @@ + C++中vector的初始化及赋值方式 | Blogs
Home C++中vector的初始化及赋值方式
Post
Cancel

C++中vector的初始化及赋值方式

一维向量

1. 不带参数的构造函数初始化

1
+2
+
//初始化一个size为0的vector
+vector<int> abc;
+

2. 带参数的构造函数初始化

1
+2
+3
+4
+
//初始化size,但每个元素值为默认值
+vector<int> abc(10);        //初始化了10个默认值为0的元素
+//初始化size,并且设置初始值
+vector<int> cde(10, 1);    //初始化了10个值为1的元素
+

3. 通过数组地址初始化

1
+2
+3
+
int a[5] = {1, 2, 3, 4, 5};
+//通过数组a的地址初始化,注意地址是从0到5(左闭右开区间)
+vector<int> b(a, a + 5);
+

4. 通过同类型的vector初始化(深拷贝,即不共享数据)

1
+2
+3
+
vector<int> a(5, 1);
+//通过a初始化
+vector<int> b(a);
+

5. 通过insert初始化

1
+2
+3
+4
+5
+
//insert初始化方式将同类型的迭代器对应的始末区间(左闭右开区间)内的值插入到vector中
+vector<int> a(6, 6);
+vecot<int> b;
+//将a[0]~a[2]插入到b中,b.size()由0变为3
+b.insert(b.begin(), a.begin(), a.begin() + 3);
+

insert也可通过数组地址区间实现插入

1
+2
+3
+4
+
int a[6] = {6, 6, 6, 6, 6, 6};
+vector<int> b;
+//将a的所有元素插入到b中
+b.insert(b.begin(), a, a + 6);
+

此外,insert还可以插入m个值为n的元素

1
+2
+
//在b开始位置处插入3个6
+b.insert(b.begin(), 3, 6);
+

6. 通过copy函数赋值

1
+2
+3
+4
+5
+6
+7
+8
+9
+
vector<int> a(5, 1);
+int a1[5] = {2, 2, 2, 2, 2};
+vector<int> b(10);
+
+/*将a中元素全部拷贝到b开始的位置中,注意拷贝的区间为a.begin() ~ a.end()的左闭右开的区间*/
+copy(a.begin(), a.end(), b.begin());
+
+//拷贝区间也可以是数组地址构成的区间
+copy(a1, a1 + 5, b.begin() + 5);
+

二维向量

1. 向量 + 向量(常用方法)

vector<vector<int> > myv(row, vector<int>(column,0));

1
+2
+3
+4
+5
+
vector<vector<int> > myv(5, vector<int>(10,0)); // 两个维度都是向量
+cout<<myv.size()<<endl; // 正确,因为向量有size()函数
+
+vector<int> temp(2);
+myv.push_back(temp);    // 正确,可以将向量插入到向量中
+

或者:

1
+2
+3
+
vector<vector<int> > myv(5);    // 两个维度都是向量,只是第二个维度的向量长度为0
+cout<<myv.size()<<endl;         // 5
+cout<<myv[0].size()<<endl;      // 0
+

vector<vector<int>> v(5) 大于号之间没有空格的初始化方式在C++11之后也是正确的,即C++11以后允许两个大于号之间没有空格。C++11是一种标准。

2. 数组 + 向量(不建议)

1
+2
+3
+4
+5
+
vector<int> myv[n]; // 此时第一维是数组,第二维才是向量。
+// cout<<myv.size()<<endl; // 错误,因为数组没有size()函数
+
+vector<int> temp(2);
+myv.push_back(temp);    // 错误,无法将向量插入到数组中
+
This post is licensed under CC BY 4.0 by the author.

C++知识点整理及常见STL函数的使用

C++中静态成员及静态成员函数详解

diff --git a/posts/dev-c++11/index.html b/posts/dev-c++11/index.html new file mode 100644 index 000000000..7dd975a03 --- /dev/null +++ b/posts/dev-c++11/index.html @@ -0,0 +1 @@ + Dev-C++支持C++11的操作 | Blogs
Home Dev-C++支持C++11的操作
Post
Cancel

Dev-C++支持C++11的操作

  1. Click Tools and Complier Options

  2. Add -std=c++11

This post is licensed under CC BY 4.0 by the author.

MathJax 数学公式语法

Windows任务栏时间显示秒

diff --git a/posts/elasticsearch/index.html b/posts/elasticsearch/index.html new file mode 100644 index 000000000..0ff6f55c4 --- /dev/null +++ b/posts/elasticsearch/index.html @@ -0,0 +1 @@ + ElasticSearch | Blogs
Home ElasticSearch
Post
Cancel

ElasticSearch

Elasticsearch是基于 Lucene 架构实现的分布式、海量数据的存储分析引擎,其中 Lucene 最主要的倒排索引结构,赋予了ES全文检索、模糊匹配、联合索引查询等等快速检索文档数据的能力,使得ES在这些查询的应用场景下优于数据库。适合用于搜索,不适合复杂的关系查询

倒排索引:被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。

写数据的过程

  1. 客户端选择一个 node 发送请求,这个 node 就成为了 coordinate node (协调节点)
  2. 该协调节点将输入的 document 做哈希路由得到 shard id,将请求转发给 shard id 对应的 node
  3. 该 node 在 primary shard 上处理请求,并将数据同步到 replica 上
  4. 协调节点在写入完成后返回响应结果

读数据的过程

  1. 客户端选择一个 node 发送请求,这个 node 就成为了 coordinate node (协调节点)
  2. 该协调节点对输入的 document id 做哈希得到 shard id,将请求转发给 shard id 对应的 node
  3. 该 node 使用 round-robin 轮询算法在 primary 和所有 replica 中选择一个,让读请求负载均衡
  4. 协调节点返回查询结果

更新、删除数据的过程

本质上都是写操作!磁盘上的每个段都有一个相应的 .del 文件。

  • 删除操作(不是删除索引)是逻辑删除,document 被标记为 deleted 状态。当段合并时,在 .del 文件中被标记为删除的文档将不会被写入新段。
  • 更新操作是把原 document 标记为删除,再写入新的 document 数据

为什么近实时

数据写入原理

大概分为三个步骤:write -> refresh -> flush

1、write:文档数据到内存缓存,并存到 translog

2、refresh:内存缓存中的文档数据,到文件缓存中的 segment 。此时可以被搜到。

3、flush: 缓存中的 segment 文档数据写入到磁盘

当数据添加到索引后并不能马上被查询到,等到索引刷新后才会被查询到。refresh_interval 参数设置为正数之后,需要等一段时间后才可以在es索引中搜索到,因为已经从内存缓存刷新到文件缓存中了。详见数据写入与查询存在时间差问题

  1. indexing Buffer 属于 ES 内存的一部分,OS 系统文件缓存属于操作系统的,不属于 ES 内存
  2. refresh 操作:定时将 ES 缓冲区转换成 segment 并写入系统文件的过程。(近实时搜索的根本原因)
  3. translog 日志文件,不管是 ES 缓冲区还是系统缓冲区的内容,只要没写入到磁盘,就会在日志文件里记录,当 flush 到磁盘后,内存和磁盘中的文件就会清空。日志文件也是先写入 os cache,默认每隔 5 秒刷一次到磁盘,所以可能存在5秒的数据丢失
  4. flush 操作:将 segment 持久化到磁盘,同时清理 translog。主要分为以下几步:
    1. 内存中的数据写入新的segment并放入缓存(清空内存区)
    2. 将commit point 写入磁盘,表示哪些 segment 已经写入磁盘
    3. 将 segment 写入磁盘,使用 fsync 命令
    4. 清空 translog 日志内容

为什么这么快

  1. 分布式储存:采用分布式储存技术,将数据存储于多节点,分散负载,优化整体执行效能。
  2. 索引分片:将每索引分裂为多片段,实现并行查询,提升搜索速度。
  3. 倒排索引:支持倒排索引数据结构,映射文档中每个词汇至文档出现位置,当搜索请求发生时,能快速检索包含所有搜索词的文档,迅速返回结果。
  4. 索引压缩:使用不同的压缩算法对倒排索引进行压缩,以减少存储空间占用和提高读写效率。
  5. 预存储结果:插入数据时,预处理数据,将结果预存储于索引中,查询时无需重新计算,提升查询速度。
  6. 内存存储:应用内存映射(Memory Mapped)技术来提高磁盘I/O性能。它将倒排索引缓存在内存中,同时使用内存映射技术将文件映射到虚拟地址空间中。减少磁盘访问次数,提高数据存储与查询效率。

Term Index: 倒排的树状结构,存在内存里,是Term dictionary的索引。

基本概念:

  1. Cluster,集群。一个集群包含多个节点,对外提供服务。
  2. Node,节点。一个节点运行在一个独立的环境或虚拟机上,一个节点可以包含多个分片,一个索引由多个分片组成
  3. Shard,分片。(Primary Shard 和 Replica Shard),主分片数量在创建索引的时候就需要固定,并且无法做修改。
  4. Index,索引,相当于mysql中的库。
  5. Type,类型,一个索引可以对应一个或者多个类型。(ES6.0后被废弃,ES7完全删除)
  6. Mapping,映射,相当于表结构。
  7. Document,文档,相当于行。
  8. Field,字段,相当于列。

分片与副本的区别在于:

  • 当分片设置为5,数据量为30G时,ES会将数据平均分配到5个分片上,每个分片6G数据。进行数据查询时,ES会把查询发送给每个分片,最后将结果组合在一起。目的是保障查询的高效性。
  • 副本是对分片的数据进行复制,目的是保障数据的高可靠性,防止丢失。

Filter VS Query

尽可能使用过滤器上下文(Filter)替代查询上下文(Query)

  • Query:此文档与此查询子句的匹配程度如何?
  • Filter:此文档和查询子句匹配吗?

Elasticsearch 针对 Filter 查询只需要回答「是」或者「否」,不需要像 Query 查询一样计算相关性分数,同时Filter结果可以缓存。

Reindex 重建索引的原理:

Scroll Query + Bulk

Ingest pipeline 可以在数据存入ES之前对数据进行转换,例如转小写,增加字段等。

性能优化:

背景:每五分钟就有6.5M条数据,直接reindex需要1000s(15分钟)

  1. 创建索引前设置主分片数量为二,副本比例为一,700s
  2. batch size = 2000(原来为默认值1000,使用堆缓存索引数据,默认最大值为100 MB), 副本比例为零,480s,平均每个 document 30-40 kb 左右
  3. slice = 2(其实是三个并行任务,一个父任务二个子任务),420s(甚至有一次350s)

text 和 keyword 的区别

  • text类型: text类型是指可分词的文本,适用于长文本或短语查询。当文本被索引时,会被分成一些个别单词或词组,并且会去除停用词(如“a”、“the”、“and”等)和标点符号。这些单词或词组将被标准化并存储在倒排索引中,使得搜索时可以更快地匹配文档。适合全文搜索。

  • keyword类型: keyword类型是指未经过分词处理的文本,适用于精确匹配和排序。当文本被索引时,会被作为一个整体进行索引。它们通常用于搜索和排序非文本字段,例如数字或日期。适合过滤、排序、聚合。

ElasticSearch 默认为text类型,但是text类型总会有keyword的类型的字段,等价于 .keyword

ES 调优

硬件配置优化

  1. CPU 配置
  2. 内存配置
    • 禁止 swap
    • 配置 GC
  3. 磁盘

索引优化

  1. 批量提交(Bulk),但不能一次性提交过多内容。
  2. 增加 refresh 时间间隔
  3. 减少副本数量

查询优化

  1. 尽可能使用过滤器上下文(Filter)替代查询上下文(Query)
  2. 拆分索引
  3. 减少模糊匹配

数据结构优化

  1. 减少不需要的字段
  2. text 和 keyword 类型字段的设置

集群架构设计

  1. 设置分片数量,但不宜过大。

扩容

  • 垂直扩容(纵向扩容):替换旧的设备

  • 水平扩容(横向扩容):直接新增设备到集群中,会触发relocation

写入和更新的并发

针对写入和更新时可能出现的并发问题,ES是通过文档版本号来解决的。当用户对文档进行操作时,并不需要对文档加锁和解锁操作,只需要带着版本号。当版本号冲突的时候,ES会提示冲突并抛出异常,并进行重试

存储结构

  1. 一个集群包含1个或多个节点;
  2. 一个节点包含1个或多个索引;
  3. 一个索引,类似 Mysql 中的数据库;
  4. 每个索引又由一个或多个分片组成;
  5. 每个分片都是一个 Lucene 索引实例,您可以将其视作一个独立的搜索引擎,它能够对 Elasticsearch 集群中的数据子集进行索引并处理相关查询;
  6. 每个分片包含多个segment(段),每一个segment都是一个倒排索引。

查询时,会把所有的segment查询结果汇总归并为最终的分片查询结果返回。

段是不可变的

为了实现高索引速度,故使用了segment 分段架构存储。

一批写入数据保存在一个段中,其中每个段是磁盘中的单个文件。

由于两次写入之间的文件操作非常繁重,因此将一个段设为不可变的,以便所有后续写入都转到New段。

段合并

可以是内存里的段,也可以是在磁盘里的段进行段合并

流程

  1. 合并候选阶段:es会选择一些相似大小的段作为合并候选,减少合并过程中的IO开销。
  2. 合并阶段:ES将合并时候选中的多个段合并成为一个新的段,同时删除旧的段(在 .del 文件里标记为删除)。在合并过程中ES会进行数据的排序和去重,并重新生成倒排索引

为什么要段合并

由于自动刷新流程每秒会创建一个新的段(由动态配置参数:refresh_interval 决定),这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦,导致:

  1. 消耗资源:每一个段都会消耗文件句柄、内存和cpu运行周期;
  2. 搜索变慢:每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。

触发条件

后台进程定期检查,可能进行 merge 操作

  1. 手动触发
  2. 段 flush 触发
  3. 由前一个成功的 merge 触发

分词器

  1. ik-analyzer 分词器,支持中文分词
  2. pinyin 分词器,支持输入拼音查到相关关键词
  3. pattern 分词器,支持正则表达式
  4. whitespace 分词器,用于去除空格
This post is licensed under CC BY 4.0 by the author.

Redis 知识体系

SpringBoot

diff --git a/posts/get-hsv-and-bgr/index.html b/posts/get-hsv-and-bgr/index.html new file mode 100644 index 000000000..dd1704a70 --- /dev/null +++ b/posts/get-hsv-and-bgr/index.html @@ -0,0 +1,35 @@ + Python实现点击图片获取HSV或BGR的值 | Blogs
Home Python实现点击图片获取HSV或BGR的值
Post
Cancel

Python实现点击图片获取HSV或BGR的值

单击图片即可得到HSV或BGR的值,代码如下:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
import cv2
+
+def getposHsv(event, x, y, flags, param):
+    if event == cv2.EVENT_LBUTTONDOWN:
+        print("HSV is", HSV[y, x])
+
+def getposBgr(event, x, y, flags, param):
+    if event == cv2.EVENT_LBUTTONDOWN:
+        print("BGR is", image[y, x])
+
+image = cv2.imread('frame_B.jpg')
+HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
+cv2.imshow("imageHSV", HSV)
+cv2.imshow('image', image)
+cv2.setMouseCallback("imageHSV", getposHsv)
+cv2.setMouseCallback("image", getposBgr)
+cv2.waitKey(0)
+
This post is licensed under CC BY 4.0 by the author.

Python脚本制作coco格式的实例分割数据集

Github合并两个不同的仓库

diff --git a/posts/go/index.html b/posts/go/index.html new file mode 100644 index 000000000..7751abf04 --- /dev/null +++ b/posts/go/index.html @@ -0,0 +1,327 @@ + Go 语言学习 | Blogs
Home Go 语言学习
Post
Cancel

Go 语言学习

基本语法

变量初始化

1
+2
+3
+
var s string = "string"
+var s = "string"
+s := "string"
+

二维切片初始化

1
+2
+3
+4
+
slice1 := make([][]bool, m)
+for i := range slice1 {
+    slice1[i] = make([]bool, n)
+}
+

变量自增

只有后缀自增或自减,并且必须单独一行(除了在range语句中)。

条件判断语句中的初始变量可以在布尔表达式里,并且不能使用0/1作为判断条件。

1
+2
+3
+
if cond := true; cond {
+    fmt.Println()
+}
+

随机数

生成 [0, 99] 的随机数

1
+2
+3
+
rand.Seed(time.Now().UnixNano())
+r := rand.Intn(100)
+fmt.Println(r)
+

栈/队列

Go 语言中的栈和队列都是基于切片实现的。

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
stack := make([]int, 0) //创建切片
+stack = append(stack, 10)   //push压入栈
+value := stack[len(stack)-1]    // 取栈顶的值
+stack = stack[:len(stack)-1]    //pop弹出
+len(stack) == 0 //检查栈空
+
+queue := make([]int, 0) //创建切片
+queue = append(queue, 10)   //enqueue入队
+v := queue[0]   // 取队首的值
+queue = queue[1:]   //dequeue出队
+len(queue) == 0 //检查队列为空
+

合并切片

1
+2
+3
+4
+
slice1 := []string{"Moe", "Larry", "Curly"}
+slice2 := []string{"php", "golang", "java"}
+slice3 = append(slice1, slice2...)
+fmt.Println(slice3)//[Moe Larry Curly php golang java]
+

整数和字符串转换

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
// string to int, "99" -> 99
+int, err := strconv.Atoi(str)
+
+// asscii to string, 97 -> "a"
+str := string(asscii)   // asscii to string
+
+// int to string, 10 -> "10"
+str := fmt.Sprintf("%d", int)
+str := strconv.Itoa(int)
+str := strconv.FormatInt(int, 10)
+
+

Range 用法

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
// 数组
+var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
+for i, v := range pow {
+    fmt.Printf("2**%d = %d\n", i, v)
+}
+// Map
+for key, value := range oldMap {
+    newMap[key] = value
+}
+for key := range oldMap {
+    fmt.Println(oldMap[key])
+}
+for _, value := range oldMap {
+    fmt.Println(value)
+}
+
+// 字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
+for i, c := range "go" {
+    fmt.Println(i, c, string(c))   //0 103 g \n 1 111 o
+}
+

最值

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
fmt.Printf("int min - max: %d - %d\n", math.MinInt, math.MaxInt)   // int
+fmt.Printf("int8 min - max: %d - %d\n", math.MinInt8, math.MaxInt8)    // int8
+fmt.Printf("int16 min - max: %d - %d\n", math.MinInt16, math.MaxInt16) // int16
+fmt.Printf("int32 min - max: %d - %d\n", math.MinInt32, math.MaxInt32) // int32
+fmt.Printf("int64 min - max: %d - %d\n", math.MinInt64, math.MaxInt64) // int64
+// unsigned
+fmt.Printf("uint min - max: %d - %d\n", uint(0), uint(math.MaxUint))   // uint
+fmt.Printf("uint8 min - max: %d - %d\n", 0, math.MaxUint8) // uint8
+fmt.Printf("uint16 min - max: %d - %d\n", 0, math.MaxUint16)   // uint16
+fmt.Printf("uint32 min - max: %d - %d\n", 0, math.MaxUint32)   // uint32
+fmt.Printf("uint64 min - max: %d - %d\n", 0, uint64(math.MaxUint64))   // uint64
+

排序

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
// int
+array := []int{3, 1, 34, 25, 14, 5}
+sort.Ints(array)    // 升序
+fmt.Println(array)
+sort.Sort(sort.Reverse(sort.IntSlice(array)))   // 降序
+fmt.Println(array)
+// string
+array1 := []string{"ss", "sa", "avs", "vs"}
+sort.Strings(array1)
+fmt.Println(array1)
+sort.Sort(sort.Reverse(sort.StringSlice(array1)))
+fmt.Println(array1)
+

自定义排序

1
+2
+3
+4
+5
+6
+7
+8
+
array := []int{3, 17, 1, 34, 25, 14, 5}
+sort.Slice(array, func(i, j int) bool {
+    if abs(array[i]-9) != abs(array[j]-9) {
+        return abs(array[i]-9) < abs(array[j]-9)
+    }
+    return array[i] < array[j]
+})
+fmt.Printf("%+v", array)
+

双向列表

1
+2
+3
+4
+
doubleLinkedList := list.New()
+doubleLinkedList.PushBack(3)
+var doubleLinkedListElement *list.Element
+doubleLinkedListElement = doubleLinkedList.Back()
+

Map 用法

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
+
m := make(map[string]int) // 初始化
+
+m := map[string]int{    //  使用组合字面量初始化
+    "apple": 1,
+    "banana": 2,
+    "orange": 3,
+}
+
+v1 := m["apple"]    //  获取键值对
+v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
+
+for k, v := range m {   //  遍历Map,无序的
+    fmt.Printf("key=%s, value=%d\n", k, v)
+}
+delete(m, "banana") //  删除元素
+m := make(map[string]int) //    清空元素的方法就是重新make一个map
+
+// 基于 Map 实现 Set
+set := make(map[string]bool)
+set["Foo"] = true
+for k := range set {
+    fmt.Println(k)
+}
+delete(set, "Foo")
+size := len(set)
+exists := set["Foo"]
+

堆的用法

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
+
package main
+
+import (
+	"container/heap"
+	"fmt"
+	"sort"
+)
+
+type heap2 struct {
+	sort.IntSlice
+}
+
+func (h heap2) Less(i, j int) bool { // 最小堆:h.IntSlice[i] < h.IntSlice[j];最大堆:h.IntSlice[i] > h.IntSlice[j];
+	return h.IntSlice[i] > h.IntSlice[j]
+}
+
+func (h *heap2) Push(v interface{}) {
+	h.IntSlice = append(h.IntSlice, v.(int))
+}
+
+func (h *heap2) Pop() interface{} {
+	temp := h.IntSlice
+	v := temp[len(temp)-1]
+	h.IntSlice = temp[:len(temp)-1]
+	return v
+}
+
+func (h *heap2) push(v int) {
+	heap.Push(h, v)
+}
+
+func (h *heap2) pop() int {
+	return heap.Pop(h).(int)
+}
+
+func main() {
+	q := &heap2{[]int{3, 4, 1, 2, 4, 3}}
+	heap.Init(q)
+	q.pop()
+	fmt.Println(len(q.IntSlice))
+	fmt.Println(q)
+}
+
+

知识点

数组和切片

  • 数组在内存中是一段连续的内存空间,元素的类型和长度都是固定的;
  • 切片在内存中由一个指向底层数组的指针、长度和容量组成的,长度表示当前包含的元素个数,容量表示切片可以拓展的最大元素个数。切片 s[x: y] 表示 s 中第 x 位到第 y - 1 位元素截取。

协程/线程/进程的区别

  • 进程:是具有一定独立功能的程序,是系统资源分配和调度的最小单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

  • 线程:是进程的一个实体,是内核态,而且是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

  • 协程:是一种用户态的轻量级线程,调度完全是由用户来控制的拥有自己的寄存器上下文和栈。调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。一个线程可以有多个协程,线程、进程都是同步机制,但协程是异步

Go 和 Java 的区别

  1. 语言特性:
    • Java 面向对象,静态语言,有完整的继承体系,不支持多继承,值传递
    • Go 虽然是静态类型语言,但具有动态类型语言的鸭子类型(基于 interface 实现),支持多继承,值传递
  2. 并发模型:
    • Java 的并发基于线程和锁
    • Go 的并发基于协程和通道,可以实现轻量级的并发
  3. 性能
    • Java 需要通过虚拟机,可跨平台,从字节码编译成机器码
    • Go 的效率高,因为没有虚拟机,直接编译成机器码

Byte 和 Rune 类型

  • byte是uint8的别称,一个值就是一个ASICII码的值,占 1 个字节的英文类字符,使用 byte
  • rune是int32的别称,一个值就是一个Unicode字符,占 1 ~ 4 个字节的其他字符,可以使用rune(或者int32),如中文、特殊符号等。
This post is licensed under CC BY 4.0 by the author.

Note from Work

Zookeeper 学习

diff --git a/posts/index.html b/posts/index.html new file mode 100644 index 000000000..380189923 --- /dev/null +++ b/posts/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/posts/install-jekyll/index.html b/posts/install-jekyll/index.html new file mode 100644 index 000000000..1e3905279 --- /dev/null +++ b/posts/install-jekyll/index.html @@ -0,0 +1,17 @@ + Windows配置Jekyll相关环境 | Blogs
Home Windows配置Jekyll相关环境
Post
Cancel

Windows配置Jekyll相关环境

1. Install Ruby

在Windows上使用RubyInstaller安装比较方便,在Ruby官网下载最新版本的RubyInstaller WITH DEVKIT。注意32位和64位版本的区分。

安装:使用默认路径即可,避免出错;勾选添加到PATH,就不用手动添加环境变量了

安装完成如图:

这里需要勾选Run 'ridk install',在弹出来的安装界面中选择3,安装MSYS2 and MINGW development toolchain

2. Install RubyGems

RubyGems官网下载ZIP格式的安装包,下载后解压到任意路径。进入解压目录,在终端输入命令:ruby setup.rb 或者直接双击setup.rb文件即可。

3. Install Jekyll

打开cmd输入以下命令并等待安装完成即可。

1
+2
+3
+
gem install jekyll
+gem install jekyll-paginate
+gem install bundler
+

4. Check installation

1
+2
+
jekyll -v
+bundle -v
+

输出版本信息则代表安装没问题。

5. Create a repo

安装完成,我们可以用jekyll命令创建一个博客模板,进入一个目录,打开命令行执行:

1
+2
+3
+
jekyll new testblog
+cd testblog
+bundle exec jekyll serve
+

大功告成!

PS:遇到的问题

A. cannot load such file – webrick (LoadError)

问题描述:执行 bundle exec jekyll serve 时出现 cannot load such file -- webrick (LoadError) 错误,如下图所示

解决方法:终端输入bundle add webrick.See here.

Reference

  1. windows安装jekyll
  2. 搭建个人博客:Jekyll + Github Pages + VSCode
This post is licensed under CC BY 4.0 by the author.

It's a Long Story

Github删除Commits记录

diff --git a/posts/it's-a-long-story/index.html b/posts/it's-a-long-story/index.html new file mode 100644 index 000000000..270a6559c --- /dev/null +++ b/posts/it's-a-long-story/index.html @@ -0,0 +1,44 @@ + It's a Long Story | Blogs
Home It's a Long Story
Post
Cancel

It's a Long Story

This post is to show Markdown syntax rendering on Chirpy, you can also use it as an example of writing. Now, let’s start looking at text and typography.

Paragraph

I wandered lonely as a cloud

That floats on high o’er vales and hills,

When all at once I saw a crowd,

A host, of golden daffodils;

Beside the lake, beneath the trees,

Fluttering and dancing in the breeze.

Nested and mixed lists

Nested and mixed lists are an interesting beast1. It’s a corner case to make sure that

  • Lists within lists do not break the ordered list numbering order
  • Your list styles go deep enough.

Ordered list

  1. Firstly
    1. Firstly
    2. Secondly
    3. Thirdly
  2. Secondly
    1. Firstly
    2. Secondly
    3. Thirdly
  3. Thirdly
    1. Firstly
    2. Secondly
    3. Thirdly

Unordered list

  • Chapter
    • Section
      • Paragraph

Task Lists

  • Finish my changes
  • Push my commits to GitHub
  • Open a pull request

Description list

Sun
the star around which the earth orbits
Moon
the natural satellite of the earth, visible by reflected light from the sun

Quote

Only one thing is impossible for God: To find any sense in any copyright law on the planet.

Mark Twain

Prompts

An example showing the tip type prompt.

An example showing the info type prompt.

An example showing the warning type prompt.

An example showing the danger type prompt.

Twitter embedded

This post tests Twitter Embeds.

Video embedded

YouTube video embedded below.

ENGLISH SPEECH | MUNIBA MAZARI - We all are Perfectly Imperfect (English Subtitles)

Here is the code:

1
+
<iframe width="640" height="360" src="https://www.youtube.com/embed/fBnAMUkNM2k" frameborder="0" allowfullscreen></iframe>
+

Tables

Header1Header2Header3
leftcenterright
leftcenterright
leftcenterright
leftcenterright

Image alignment

Welcome to image alignment! The best way to demonstrate the ebb and flow of the various image positioning options is to nestle them snuggly among an ocean of words. Grab a paddle and let’s get started.

image-center]

The image above happens to be centered.

image-left The rest of this paragraph is filler for the sake of seeing the text wrap around the 150×150 image, which is left aligned.

As you can see there should be some space above, below, and to the right of the image. The text should not be creeping on the image. Creeping is just not right. Images need breathing room too. Let them speak like you words. Let them do their jobs without any hassle from the text. In about one more sentence here, we’ll see that the text moves from the right of the image down below the image in seamless transition. Again, letting the do it’s thing. Mission accomplished!

And now for a massively large image. It also has no alignment.

no-alignment

The image above, though 1200px wide, should not overflow the content area. It should remain contained with no visible disruption to the flow of content.

image-right

And now we’re going to shift things to the right align. Again, there should be plenty of room above, below, and to the left of the image. Just look at him there — Hey guy! Way to rock that right side. I don’t care what the left aligned image says, you look great. Don’t let anyone else tell you differently.

In just a bit here, you should see the text start to wrap below the right aligned image and settle in nicely. There should still be plenty of room and everything should be sitting pretty. Yeah — Just like that. It never felt so good to be right.

And just when you thought we were done, we’re going to do them all over again with captions!

Once the position is specified, the image caption should not be added.

With caption

  • Default (with caption)

Desktop View Full screen width and center alignment


  • Shadow

Window shadow shadow effect (visible in light mode)


  • Left aligned

Desktop View


  • Float to left

    Desktop View “A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space.”


  • Float to right

    Desktop View “A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space. A repetitive and meaningless text is used to fill the space.”


Mermaid SVG

 gantt
+  title  Adding GANTT diagram functionality to mermaid
+  apple :a, 2017-07-20, 1w
+  banana :crit, b, 2017-07-23, 1d
+  cherry :active, c, after b a, 1d
+

Mathematics

The mathematics powered by MathJax:

\[\sum_{n=1}^\infty 1/n^2 = \frac{\pi^2}{6}\]

When $a \ne 0$, there are two solutions to $ax^2 + bx + c = 0$ and they are

\[x = {-b \pm \sqrt{b^2-4ac} \over 2a}\]

Filepath

Here is the /path/to/the/file.extend.

Code block

Common

1
+
This is a common code snippet, without syntax highlight and line number.
+

Specific Languages

Using ```{language} you will get a code block with syntax highlight:

1
+2
+3
+
```yaml
+key: value
+```
+

Console

1
+2
+3
+
$ env |grep SHELL
+SHELL=/usr/local/bin/bash
+PYENV_SHELL=bash
+

Shell

1
+2
+3
+4
+
if [ $? -ne 0 ]; then
+    echo "The command was not successful.";
+    #do the needful / exit
+fi;
+

Specific filename

1
+2
+3
+
@import
+  "colors/light-typography",
+  "colors/dark-typography"
+

Line Number

By default, all languages except plaintext, console, and terminal will display line numbers. When you want to hide the line number of a code block, add the class nolineno to it:

1
+2
+3
+4
+
```shell
+echo 'No more line numbers!'
+```
+{: .nolineno }
+

Learn More

For more knowledge about Jekyll posts, visit the Jekyll Docs: Posts.

Date modified

This post has been updated and show a modified date.

Reference

This post is licensed under CC BY 4.0 by the author.

-

Windows配置Jekyll相关环境

diff --git a/posts/java-syntax/index.html b/posts/java-syntax/index.html new file mode 100644 index 000000000..5b1cf5b71 --- /dev/null +++ b/posts/java-syntax/index.html @@ -0,0 +1,295 @@ + Java知识点记录博客 | Blogs
Home Java知识点记录博客
Post
Cancel

Java知识点记录博客

运算符优先级

优先级运算符
1( ) [ ]  .
2!  ~  ++  –
3*  /  %
4+  -
5«  »  «<  »>
6<  <=  >  >=  instanceof
7==  !=
8&
9^
10|
11&&
12||
13? :
14=  +=  -=  *=  /=  %=  &=  |=  ^=  ~=  «=  »=  »>=
15

总结:括号级别最高,逗号级别最低,单目 > 算术 > 位移 > 关系 > 逻辑 > 三目 > 赋值。

容器(集合类)

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
+
+Deque<Integer> stack = new ArrayDeque<>();  // stack
+
+Deque<String> queue = new ArrayDeque<>();   // queue
+
+PriorityQueue<Integer> queue = new PriorityQueue<>();   // priority queue
+
+// Set
+
+HashSet<String> unordered_set = new HashSet<>();  // 乱序,底层使用散列函数
+
+LinkedHashSet<String> set = new LinkedHashSet<>(); // 以插入顺序排序
+
+TreeSet<String> set = new TreeSet<>();  // 以字典序排序,底层使用红黑树
+
+// Map
+
+HashMap<String, Object> unordered_map = new HashMap<>();  // 乱序
+
+LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>();  // 以插入顺序排序
+
+TreeMap<Integer, Integer> map = new TreeMap<>();  // 以字典序排序
+
+TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();  // 手动加泛型,多一些约束少一些出错。在运行期没有任何区别, java的泛型只在编译期有效。
+
+

ArrayList:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
public static void main(String[] args) {
+    ArrayList<String> sites = new ArrayList<String>();
+    sites.add("Google");
+    sites.add("Runoob");
+    sites.add("Taobao");
+    sites.add("Weibo");
+    System.out.println(sites.get(1));  // 访问第二个元素
+    sites.set(2, "Wiki"); // 第一个参数为索引位置,第二个为要修改的值
+    sites.remove(3); // 删除第四个元素
+    Collections.sort(sites);  // 字母排序
+    System.out.println(sites.isEmpty());    // 空判断
+    System.out.println(sites);
+    String[] arr = new String[sites.size()];    // 创建一个新的 String 类型的数组
+    sites.toArray(arr); // 将ArrayList对象转换成数组
+}
+

LinkedList:

1
+
+

HashSet:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
public static void main(String[] args) {
+    HashSet<Integer> set = new HashSet<Integer>();
+    set.add(1);
+    set.add(2);
+    set.add(3);
+    set.remove(2);
+    System.out.println(set.contains(4));
+    for(int item: set){
+        System.out.println(item);
+    }
+}
+

HashMap:

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
+
public static void main(String[] args) {
+    Map<String, Integer> numbers = new HashMap<>();
+    numbers.put("One", 1);
+    numbers.put("Two", 2);
+    numbers.put("Three", 3);
+    numbers.put("Four", 4);
+    System.out.println(numbers.getOrDefault("Five", 0));
+    numbers.remove("Two");
+    numbers.replace("One",111);
+    System.out.println(numbers.containsKey("Four"));
+    System.out.println(numbers.containsValue(111));
+    
+    // Method A(Recommended!)
+    numbers.forEach((k,v) -> System.out.println("Item: " + k + " Count: " + v));
+    numbers.forEach((k,v) -> {
+        System.out.println("Item: " + k + " Count: " + v);
+        if("Four".equals(k)){
+            System.out.println("Hello Four");
+        }
+    });
+    // Method B(Recommended!)
+    for (Map.Entry<String, Integer> entry : numbers.entrySet()) {
+        System.out.println("key:" + entry.getKey() + ".value:" + entry.getValue());
+    }
+    // Method C(NOT Recommended!)
+    for (String string : numbers.keySet()) {
+        System.out.println("key:" + string + ".value:" + numbers.get(string));
+    }
+    // Method D(Recommended When Deleting)
+    Iterator<String> iterators = numbers.keySet().iterator();
+    while (iterators.hasNext()) {
+        String key = iterators.next();
+        System.out.println("key:" + key + ".value:" + numbers.get(key));
+    }
+}
+

ArrayDeque:

在堆栈中,元素从栈顶插入,从栈顶弹出

在队列中,元素从队尾插入,从队首弹出

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
+
public static void main(String[] args) {
+    ArrayDeque<String> animals= new ArrayDeque<>();
+    /*
+        *****Stack*****
+    */
+    animals.push("Pig");
+    System.out.println("返回栈顶元素: " + animals.peek());
+    System.out.println("返回栈顶元素并弹出: " + animals.pop()); 
+
+    /*
+        *****Queue*****
+    */
+    animals.offer("Horse");
+    System.out.println("返回队首元素: " + animals.peek()); 
+    System.out.println("返回队首元素并弹出: " + animals.poll());
+
+    /*
+        *****Deque*****
+    */
+    animals.push("Bird");    // 队首
+    animals.offer("Dog");    // 队尾
+    System.out.println("返回队首元素: " + animals.peek()); 
+    System.out.println("返回队首元素并弹出: " + animals.poll());
+    System.out.println("返回队尾元素: " + animals.peekLast()); 
+    System.out.println("返回队尾元素并弹出: " + animals.pollLast());
+    
+    /*
+        *****Common*****
+    */
+    System.out.println("判断是否包含Dog: " + (animals.contains("Dog") ? "是" : "否"));
+    System.out.println("转换成数组输出: " + Arrays.toString(animals.toArray()));
+}
+

赋值和new的区别

赋值是创建常量,在编译期时就被确定了;new创建的对象不是常量,无法在编译期中确定。

1
+2
+3
+4
+5
+6
+7
+8
+
+String s0 = "aaa" + "bbb";   // 常量,同"aaabbb",放入常量池中。创建了1个对象
+String s1 = "aaa" + new String("bbb");  //  非常量,因为new出来的字符串无法在编译期中确定。创建了4个对象
+String s2 = new String("aaabbb");   // 同上。由于常量池中已经存在"aaabbb",因此只创建了1个对象
+System.out.println(s1.intern() == s0);  // true,intern()函数会在常量池里找是否相同的字符串,有则返回常量池的引用
+System.out.println(s2.intern() == s0);  // true,同上
+System.out.println(s0 == s1);   // false,虽然内容一样,但是new出来的地址肯定不一样
+System.out.println(s1 == s2);   // false,同上
+

Java只有值传递,没有引用传递

SEE HERE

赋值是给变量绑定一个新对象,而不是改变对象。

举例:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
public static void main(String[] args) {
+    String x = new String("沉默王二");
+    change(x);
+    System.out.println(x);  // "沉默王二"
+}
+
+public static void change(String x) {
+    x = "沉默王三";
+}
+

直接改变对象内容。

举例:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
public static void change(A a) {
+    a.name = "bbb";
+}
+public static void main(String[] args) {
+    A a = new A();
+    a.name = "aaa";
+    change(a);
+    System.out.println(a.name); // "bbb"
+}
+

length为属性,length()为方法

  • 数组属性:length
  • 字符串方法:length()
  • 集合方法:size()

红黑树的时间复杂度为: O(logn)

一棵含有n个节点的红黑树的高度至多为2log(n+1).See Here.

同步容器

同步容器主要包括2类:

  • Vector、Stack、HashTable

  • Collections类中提供的静态工厂方法创建的类

同步容器的所有操作并不都是线程安全的。HERE

所谓“线程安全”,并不包括多个操作之间的“原子性”支持。

100%的情况下不要用 StringBuffer

99% 的情况下不要用 Vector

那么那剩下的 1% 用 Vector 的情况在哪呢?

熟悉Swing的都知道: 现有的一些 model 的类里面用了 Vector,假如你去定制它们,有时不可避免要用到 Vector。

StringBuilder

与StringBuffer相比,StringBuilder不是线程安全,所以单线程情况下效率更高。两者除线程安全方面之外无差别。

1
+2
+
StringBuilder sb = new StringBuilder();
+sb.append("a".repeat(100)); // repeat方法用于构造重复String
+
This post is licensed under CC BY 4.0 by the author.

Ubuntu中关闭摄像头的自动曝光

Basics of Java

diff --git a/posts/java/index.html b/posts/java/index.html new file mode 100644 index 000000000..f9b893596 --- /dev/null +++ b/posts/java/index.html @@ -0,0 +1,39 @@ + Basics of Java | Blogs
Home Basics of Java
Post
Cancel

Basics of Java

基本知识

三大特点:封装继承多态。

语法糖:switch支持String、泛型、自动拆装箱、变长参数、枚举、内部类、条件编译、断言、数值下划线、for-each、try-with-resources、Lambda表达式

装箱:Integer i = Integer.valueOf(10), 拆箱:int n = i.intValue()

内存结构

运行时数据区域包含线程私有的程序计数器、虚拟机栈、本地方法栈,线程共享的堆(包含字符串常量池)和非运行时数据区的元空间(包含类常量池和运行时常量池)、直接内存。

  • 程序计数器:与操作系统中的程序计数器类似,为了线程切换后能恢复到正确的执行位置,是唯一一个不会出现 OutOfMemoryError 的内存区域。
  • 虚拟机栈:以帧为单位,帧由局部变量表、操作数栈、动态链接、方法返回地址组成。每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。
    • 局部变量表:主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)
    • 操作数栈:用于存放方法执行过程中产生的中间计算结果,也存放计算过程中产生的临时变量。
    • 动态链接:将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。与类加载中的解析类似。
    • 方法返回地址:顾名思义。
  • 本地方法栈:和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法(机器码)服务
  • 堆:唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
  • 字符串常量池:简单理解为用C++实现的默认固定大小为1009的HashTable。在每个VM中只有一份,存放的是字符串常量的引用值 。关于常量池中的String类型的数据,String#intern 的用法
  • 类常量池:每个java文件被编译成class文件后会有一项常量池,用于存放编译器生成的字面量符号引用。在编译阶段,存放的是常量的符号引用
  • 运行时常量池:是在类加载完成后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析阶段,将符号引用替换成直接引用,与字符串常量池中的引用值保持一致。

class常量池、字符串常量池和运行时常量池的区别

内存模型(JMM)

JMM 旨在提供一个统一的可参考的规范,屏蔽平台内存访问差异性。这个规范为读写共享变量时如何与内存交互提供了规则和保证。并发编程中,程序会因为 CPU 多级缓存或指令重排序等出现问题,因此需要一些规范要保证并发编程的可靠性。

关键概念包括:

  • 主内存:表示所有线程都可以访问的共享内存。线程不能直接读写主内存中的变量。
  • 工作内存:每个线程都有自己的工作内存,线程的工作内存保存了该线程用到的变量和主内存共享变量的副本拷贝,线程对变量的操作都在工作内存中进行。当一个线程修改了自己工作内存中的变量时,它必须把这个变量的最新值写回到主内存中,以便其他线程可以看到这个最新的值。
  • 共享变量:这些变量可以被多个线程访问。它们可以是实例变量或静态变量。必须存储在主内存中。

JMM 为处理共享变量定义了三个特征(多线程中的概念):

  • 可见性:当一个线程修改共享变量的值,其他线程能够立即知道被修改了。当变量被 volatile 修饰时,这个变量被修改后会立刻刷新到主内存,当其它线程需要读取该变量时,会去主内存中读取新值。但普通变量读取的仍是旧值。
  • 原子性:一个操作是不可分割,不可中断的,一个线程在执行时不会被其他线程干扰。Synchronized 块之间的操作具有原子性
  • 顺序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

volatile

通过内存屏障来保证可见性的

  • 保证可见性,但不保证原子性!只是确保将变量的更新操作通知到其他线程。不能一定能保证线程安全。
  • 禁止指令重排,背景:为了提高性能,编译器和处理器常常会对指令重排。禁止指令重排避免了多线程环境下程序出现乱序执行的现象。

happens-before

happens-before原则定义如下:

  1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
  2. 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。重排序之后的执行结果与按照happens-before关系来执行的结果一致即可。

as-if-serial

As-if-serial的意思是所有的语句都可以为了优化而被重排序,但是必须保证它们重排序后的结果和程序代码本身的应有结果是一致的。为保证as-if-serial语义,Java异常处理机制也会为重排序做一些特殊处理。

八种内存交互操作

内存屏障

  1. LoadLoad 屏障:对于这样的语句Load1,LoadLoad,Load2。在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
  2. StoreStore屏障:对于这样的语句Store1, StoreStore, Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
  3. LoadStore 屏障:对于这样的语句Load1, LoadStore,Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
  4. StoreLoad 屏障:对于这样的语句Store1, StoreLoad,Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

在每个volatile读操作后插入LoadLoad屏障,在读操作后插入LoadStore屏障。

在每个volatile写操作的前面插入一个StoreStore屏障,后面插入一个SotreLoad屏障。

  

类的生命周期

  • 加载:通过类的全限定名(包名 + 类名)获取class文件的二进制字节流(通过类加载器来完成,其加载过程使用双亲委派模型),将其转化为方法区运行时的数据结构,最后在堆中实例化一个java.lang.Class对象,作为方法区中这个类的信息的入口。
  • 连接
    • 验证:确保被加载的类的正确性
    • 准备:为类的静态变量分配内存并设为jvm的默认值(不同于下文的初值,基本类型为零,引用类型为null,final修饰的常量为设定的值),对于非静态的变量,则不会为它们分配内存。
    • 解析:虚拟机将常量池中的符号引用替换为直接引用,主要针对类或接口,字段,类方法,方法类型等。举例:使用内存地址(直接引用)指向方法名(符号引用)代替方法名。
  • 初始化:按照顺序自上而下运行类中的变量赋值语句和静态语句,如果有父类,则首先按照顺序运行父类中的变量赋值语句和静态语句在类的初始化阶段,只会初始化与类相关的静态赋值语句和静态语句。类变量(静态变量)在方法区分配内存,并设置初值
  • 使用:包括主动引用和被动引用。直接引用就会触发类的初始化,其中包括以下四种情况:
    1. 通过new关键字实例化对象、读取或设置类的静态变量、调用类的静态方法。
    2. 初始化子类的时候,会触发父类的初始化。
    3. 作为程序入口直接运行时(也就是直接调用main方法)。
    4. 通过反射方式执行以上三种行为。
  • 卸载:需要同时满足以下三个条件:该类所有的实例都已经被回收,即Java堆中不存在该类的任何实例;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。单例对象不会被JVM垃圾回收,因为无法满足卸载的第一个条件,Java堆中会始终存在该单例的实例。

类加载器

线程上下文类加载器:破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器,例如SPI (Service Provider Interface)。SPI接口中的代码经常需要加载具体的实现类。SPI接口是Java核心库的一部分,由 启动类加载器(Bootstrap Classloader) 来加载,而实现类由 系统类加载器(AppClassLoader) 来加载。

双亲委派机制概念:双亲委派机制是指当一个类加载器收到某个类加载请求时,该类加载器首先会把请求委派给父类加载器。每个类加载器都是如此,它会先委托父类加载器在自己的搜索范围内找不到对应的类时,该类加载器才会尝试自己去加载。

Tomcat中的类加载器:

  • Tomcat自身所使用的类加载器,会加载jre的lib包及tomcat的lib包的类,遵循双亲委派机制。加载顺序:(1).先从缓存中加载;(2).如果没有,则从JVM的Bootstrap类加载器加载;(3).如果没有,则从父类加载器加载,加载顺序是AppClassLoader、Common、Shared。(4).如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序);
  • 每个Web应用程序用的,每个web应用程序都有自己专用的WebappClassLoader,优先加载/web-inf/lib下的jar中的class文件,这样就隔离了每个web应用程序的影响,不遵循双亲委派机制。加载顺序:(1).先从缓存中加载;(2).如果没有,则从JVM的Bootstrap类加载器加载;(3).如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序);(4).如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模式,所以加载顺序是AppClassLoader、Common、Shared。

Java的SPI:SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

1.6 -> 1.7 -> 1.8:

  • 1.6 -> 1.7: 字符串常量池从方法区(永久代)中移到堆中。原因: GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC。Java 程序中通常会有大量的被创建的字符串等待回收。
  • 1.7 -> 1.8: 将运行时数据区方法区(永久代)移动到直接内存中,字符串常量池仍然在堆中。

对象的创建过程

  1. 类加载检查
  2. 分配内存:指针碰撞或空闲列表
    • 当多个对象并发争抢空间时,有两种解决办法:CAS 和本地线程分配缓冲(TLAB,默认方式)
  3. 初始化零值
  4. 设置对象头
  5. 执行构造方法

对象的内存布局

  1. 对象头,两部分组成:存储自身运行时数据如哈希码,GC分代年龄;指向类的类型指针
  2. 实例数据,真正存储有效信息的部分
  3. 对齐填充,起占位作用

对象的定位访问(针对JVM虚拟机栈中的局部变量表)

  1. 句柄,Java 堆中将会划分出一块内存来作为句柄池,局部变量表 reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与对象类型数据各自的具体地址信息。
  2. 直接指针,局部变量表里 reference 中存储的直接就是对象的地址。

垃圾回收

JVM触发GC时,首先会让所有的用户线程到达安全点SafePoint时阻塞,也就是STW,然后枚举根节点,即找到所有的GC Roots,通过可达性算法向下搜寻活跃对象,可达的对象就保留,不可达的对象就回收

可达性算法

  • 引用计数算法
  • 可达性分析

哪些对象可以作为GC Roots

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 本地方法栈(Native 方法)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有被同步锁持有的对象

内存分配和回收原则

  • 对象优先在Eden区分配
  • 大对象直接进入老年代
  • 长期存活的进入老年代

GC 分类

  • Partial GC
    • Minor GC:只对新生代进行垃圾收集
    • Major GC:只对老年代进行垃圾收集
    • Mixed GC:整个新生代和部分老年代,只有G1收集器有
  • Full GC:整个Java堆和方法区

Full GC 触发条件

  1. 老年代空间不足
  2. 创建大对象,Eden 区域放不下大对象,直接进入老年代
  3. Minor GC 后,存活对象进入老年代
  4. 调用 system.gc(),系统会建议执行 FGC
  5. 空间分配担保机制失败

空间担保策略

空间担保策略是 JVM 的一种机制,发生 Minor GC 之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果小于,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;如果小于或者HandlePromotionFailure=false,则改为进行一次Full GC。

垃圾收集算法

  • 标记——清除算法:顾名思义,标记可回收的对象并清除。
  • 标记——复制算法:将内存分成大小相同的两份,需要垃圾收集时,将存活的对象复制到另一份内存中,缺点:内存缩小为原来的一半。
  • 标记——整理算法:标记可回收的对象,将存活的对象向一端移动,适合老年代这种垃圾回收频率不高的场景。
  • 分代收集算法:在新生代和老年代不同的代用不同的垃圾收集算法。

垃圾收集器

  1. Serial 收集器,单线程、复制算法的新生代收集器
  2. ParNew 收集器,多线程、复制算法的新生代收集器,老年代采用Serial Old收集器
  3. Parallel Scavenge 收集器,多线程、复制算法的新生代收集器,高吞吐量。
  4. Serial Old 收集器,单线程、标记-整理算法的老年代收集器。
  5. Parallel Old 收集器,多线程、标记-整理算法的老年代收集器。
  6. CMS(Concurrent Mark Sweep) 收集器,标记-清除算法,以获取最短回收停顿时间为目标的收集器。JDK14正式移除。
  7. G1(Garbage-First) 收集器,标记-整理 + 复制算法,内存碎片的产生率大大降低。JDK9-JDK17的默认垃圾收集器。

G1收集器内存模型

垃圾收集器发展历程:

  • JDK8 默认 Parallel Scavenge + Parallel Old
  • JDK9 默认 G1
  • JDK11 提出ZGC
  • JDK14 CMS 被移除

CMS(Concurrent Mark Sweep)收集器: 基于标记-清除算法,在Minor GC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收。在Full GC时不再暂停应用线程,而是使用若干个后台线程定期的对老年代空间进行扫描。

  • 步骤:
    1. 初始标记(CMS initial mark):有STW,但速度很快
    2. 并发标记(CMS concurrent mark):从GC Roots的直接关联对象开始遍历整个对象图
    3. 重新标记(CMS remark):STW,为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录;采用三色标记算法和增量更新避免漏标
    4. 并发清除(CMS concurrent sweep):清理删除掉标记阶段判断的已经死亡的对象
  • 缺点:
    1. 工作时会占用一部分CPU资源而导致用户程序变慢,降低总吞吐量
    2. CMS无法清除浮动垃圾
    3. 基于标记-清除算法会导致内存碎片不断增多,在分配大对象时有可能会提前触发一次Full GC。

Garbage First(G1)收集器:

  • 特点:引入分区的思路,弱化了分代的概念,并合理利用垃圾收集各个周期的资源。
  • 内存结构:堆内存被切分为多个固定大小的区域,最小为1M,最大为32M,默认2048份。
  • 内存分配:每个区域被标记为E、S、O和H,分别表示Eden,Survivor,Old,Humongous。Humongous区域是为了那些存储超过50%标准region大小的对象而设计的,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC。
  • 执行特点:
    • 并行与并发:使用多个CPU核缩短Stop The World停顿时间。
    • 空间整合:从整体来看是基于“标记-整理”算法实现的收集器;从局部上来看是基于“标记-复制”算法实现的。
    • 可观测的停顿:能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒。
    • G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。
  • 步骤:
    1. 初始标记(Initial Marking),STW,标记一下 GC Roots 能直接关联到的对象。
    2. 并发标记(Concurrent Marking),从 GC Root 开始对堆中对象进行可达性分析,找到存活对象。
    3. 最终标记(Final Marking),为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,STW,但是可并行执行。
    4. 筛选回收(Live Data Counting and Evacuation),对各个Region中的回收价值和成本进行排序并制定回收计划。

JVM调优

  1. 选择合适的垃圾收集器:CPU单核,只能选择Serial;CPU多核,关注吞吐量 ,那么选择Parallel Scavenge(标记复制) + Paralle Old(标记整理)组合;CPU多核,关注用户停顿时间,JDK版本1.6或者1.7,内存小,那么选择CMS。CPU多核,关注用户停顿时间,JDK1.8及以上,JVM可用内存6G以上,那么选择G1。
  2. 调整内存大小,现象:垃圾收集频率非常频繁。
  3. 设置符合预期的停顿时间,现象:程序间接性的卡顿。参数:-XX:MaxGCPauseMillis
  4. 调整内存区域大小比率,现象:某一个区域的GC频繁,其他都正常。参数:-XX:SurvivorRatio=6, -XX:NewRatio=4
  5. 提升老年代年龄标准,现象:老年代频繁GC,每次回收的对象很多。参数:-XX:InitialTenuringThreshol=7
  6. 调整大对象的标准,现象:老年代频繁GC,每次回收的对象很多,而且单个对象的体积都比较大。参数:-XX:PretenureSizeThreshold=1000000//新生代可容纳的最大对象,大于则直接会分配到老年代,0代表没有限制。
  7. 调整GC的触发时机,现象:CMS收集器的情况下,G1 经常 Full GC,程序卡顿严重。
  8. 调整JVM本地内存(直接内存)大小,现象:堆内存空间充足,但是报OOM

调优的一条经验总结:

将新对象预留在新生代,由于 Full GC 的成本远高于 Minor GC,因此尽可能将对象分配在新生代是明智的做法,实际项目中根据 GC 日志分析新生代空间大小分配是否合理,适当通过“-Xmn”命令调节新生代大小,最大限度降低新对象直接进入老年代的情况。

Java IO

  1. BIO:同步阻塞IO,使用方便,但并发处理能力低
  2. NIO:同步非阻塞IO,适用于连接数目多且连接比较短(轻操作)的架构
  3. AIO:异步非阻塞IO,适用于连接数目多且连接比较长(重操作)的架构
  • 阻塞/非阻塞,是对同一个线程来说。关注的是程序在等待调用结果(消息,返回值)时的状态。阻塞调用是指调用结果返回之前,当前线程会被挂起。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
  • 同步/异步,针对调用者与被调用者,它们是线程之间的关系。同步操作,调用者需要等待被调用者返回结果,才会进行下一步操作;异步操作,调用者不需要等待被调用者返回调用,即可进行下一步操作,被调用者通常依靠事件、回调等机制来通知调用者结果

NIO vs IO:

  • IO是面向字节流的,NIO是面向缓冲区的
  • IO流是阻塞的,NIO流是不阻塞的
  • 选择器:Java NIO的选择器允许一个单独的线程来监视多个输入通道

IO多路复用(事件驱动):一个线程不断轮询多个socket的状态,只有当socket真正有读写状态时,借用当前线程或者使用线程池额外启动线程,调用实际的IO读写操作。

Java NIO:

  • 实际上也是一种多路复用的IO。
  • 三大核心部分:Channel(通道) ,Buffer(缓冲区), Selector(选择器),Channel 负责传输(类比成铁路), Buffer 负责存取数据(类比成载着货物的火车)
    • Channel是双向的,数据总是从通道读到缓冲区或者从缓冲区中写入通道内。
  • 额外一个Selector线程,用于监听多个通道(Channel)的事件(比如:连接打开,数据到达),如果由事件发生,则获取事件并对每个事件进行相应的响应处理。

并发编程

一个 Java 程序的运行是 main 线程和多个其他线程同时运行。

多线程

synchronized

  1. 修饰普通方法/静态方法:通过 monitorenter 和 monitorexit 指令实现同步
  2. 修饰代码块:通过 ACC_SYNCHRONIZED 标记符实现同步

底层都是通过对象头里 Mark Word 指向的对象监听器(Monitor)实现的,再底层是操作系统的互斥量(mutex)实现的

同一时刻只能有一个线程运行 synchronized(lock) 内的代码块,其他线程会否则阻塞。PS:获取锁(运行代码块),释放锁(阻塞代码块)

  1. wait(): 获取锁并使线程进入等待状态
  2. notify(): 随机唤醒一个在等待锁释放(wait())的线程
  3. notifyAll(): 唤醒所有正在等待锁释放(wait())的线程,

注意:notify() 或 notifyAll() 必须等到退出 synchronized() 或 wait() 后才释放锁!

1
+2
+3
+4
+5
+6
+7
+8
+
synchronized (obj) {
+    // 条件不满足
+    while (condition does not hold) {
+        obj.wait();
+    }
+    // 执行满足条件的代码
+    obj.notifyAll();
+}
+

or

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
synchronized (obj) {
+  while (true){
+    // 条件满足
+    if (condition holds){
+      // 执行满足条件的代码
+      obj.notifyAll();
+    }
+    obj.wait();
+  }
+}
+
+

synchronized 可以用来修饰非静态方法(普通方法)、静态方法、代码块,锁住的是 class 对象的对象头!

run() 和 start() 的区别:

  • run(),调用普通方法,并不开启新线程。
  • start(),启动新线程,由JVM调用线程的run()方法。

Synchronized 和 Lock 的区别:

  1. Lock 是一个接口,Synchronized 是一个关键字
  2. Lock 需要手动释放锁,Synchronized 会自动释放锁
  3. Lock 可以是公平锁/非公平锁,Synchronized 只能是非公平锁
  4. Lock 有多种获取锁的方式,例如一定时间内获取不到会返回,Synchronized 获取不到锁一直会阻塞
  5. 性能方面,在竞争激烈的情况下,Lock 的性能会比 Synchronized 好

RenentantLock:

  • Lock:拿不到锁会一直等待
  • tryLock:去尝试获取锁,获取不到返回 false

守护线程(Daemon Thread)

Java 中的线程分为两种:

  1. 用户线程。
  2. 守护线程,其主要作用是为用户线程服务,比如垃圾回收线程,就是最典型的守护线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。也就是守护线程拥有自动结束自己生命周期的特性,而非守护线程不具备这个特点。

集合框架:

  • Collection
    • Set
    • List
    • Queue
  • Map

线程安全的list:

  • vector
  • CopyOnWriteArrayList 读多写少的情况
  • Collections.synchronizedList() 读少写多的情况

HashMap 知识点

  • HashMap: 乱序,数组+链表+红黑树,链表长度大于8转红黑树,红黑树节点个数小于6转链表。
  • LinkedHashMap: 按插入顺序排序
  • TreeMap: 按字典序排序,因为是按字典序排序的,所以键肯定不能为null,值可以为null
  • IdentityHashMap:利用哈希表实现Map接口,不同的是,其比较键(或值)时,使用引用相等性代替对象相等性。
  • ConcurrentSkipListMap:基于跳表的线程安全的,实现快速查找的链表结构。

HashMap面试题

为什么计算哈希值采用低十六位和高十六位异或操作:

计算数组下标是与操作,只有低 n 位进行与操作,高位不参与任何操作 -> 为了增大散列程度减小哈希碰撞,因此将高十六位参与进哈希值的计算。

put() 的流程:

  1. hashcode的高十六位和低十六位进行异或运算
  2. (n - 1) & hash 计算数组下标,当 n 为二次幂时,等价于取余操作((n - 1)& hash = hash % n)。
  3. 判断当前下标是否有元素,若有元素,使用尾插法。再根据链表长度判断是否需要转换成红黑树。

扩容的过程:

  1. 将数组扩容成原数组的两倍
  2. 重新计算下标,将原 hash 值与 新数组长度减一(类似于 put 操作的第二步) 进行“与”操作
  3. 如果高位结果是0,桶位置不变
  4. 如果高位结果是1,桶位置是原位置 + 扩容长度

Set:

  • HashSet: 乱序,基于HashMap实现
  • LinkedHashSet: 按插入的顺序排序,基于LinkedHashMap实现
  • TreeSet: 按字典序排序,基于红黑树

NULL key AND NULL value:

  • key
    • HashMap、LinkedHashMap 能使用 null key
    • ConcurrentHashMap、TreeMap、HashTable 不能使用 null key。
  • value
    • HashMap、LinkedHashMap、TreeMap 能使用 null value
    • Hashtable、ConcurrentHashMap 不能使用 null value。

为什么ConcureentHashMap的key和value都不能为null:

  • value不能为null:多线程情况下需要杜绝二义性。二义性是指当返回null时,无法判断是存在value为null的key还是不存在key从而返回null。因为单线程中可以使用 containsKey() 解决,但是多线程下无法使用同样的方法,因为可能会有其他线程进行其他操作影响返回值

ConcurrentHashMap JDK7 vs JDK8

  • JDK7: 数组 + 链表。先定位 Segment,再定位桶。底层结构是继承了ReentrantLock的Segment数组。可以看成是由线程安全的HashMap组成的一个map数组,数组的长度决定了支持的最大的并发量。
  • JDK8: 数组 + 链表 + 红黑树。可以直接定位到桶。链表中的元素超过8后,将链表结构转换成红黑树。通过对Node数组以CAS方式实现扩容和对Node数组的每个元素的synchronized保证ConcurrentHashMap整体的线程安全。

HashTable速度慢:使用synchronized对整个对象加锁。

JDK7:对整个数组进行分段(每段都是由若干个 hashEntry 对象组成的链表),每个分段都有一个 Segment 分段锁(继承 ReentrantLock 分段锁)。与hashtable相比,加锁粒度更细,但是初始化Segment数组长度后就无法扩容。ConcurrentHashMap 是一个二级哈希表。在一个总的哈希表下面,有若干个子哈希表。

JDK8:对table数组的头节点加锁(哈希桶为空时,使用CAS将新的Node写入哈希桶的首节点;哈希桶不为空时,使用synchronized对首节点加锁接着添加节点)

  • put:分两步,计算哈希值和一个死循环,循环步骤,
    1. first节点还没有初始化,所以初始化first节点,然后进入下次循环;
    2. first节点初始化了,但是为空,采用CAS方式把当前要put的值设置进这处,设置失败则进入下次循环,成功则保存成功,退出循环;
    3. 如果判断有其他线程正在对ConcurrentHashMap扩容(hash==MOVED),获取要去获取新的tab,进入下次循环;
    4. 找到了对应哈希桶的首节点f,直接对f加synchronized同步,然后判断f节点是链表结构还是红黑树结构,链表结构则遍历链表进行设置,红黑树则采用红黑树设置进去。设置成功后判断是否需要把链表结构转红黑树;

ThreadLocal: 提供线程内的局部变量,在多线程的环境中保证各个线程内的变量不同。将数据封闭在线程中而避免使用同步,即线程封闭。一个ThreadLocal对象即是一个线程局部变量。jdbc连接池就是用ThreadLocal,典型例子。以下使四种方法:

  • Object get():获取该线程局部变量的值。
  • void set(Object value):给该线程局部变量赋值。
  • protected Object initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。
  • public void remove():将当前线程局部变量的值删除。

底层是 ThreadLocalMap 内部静态类,由数组实现,解决 hash 冲突的方式采用的是线性探测法

存在内存泄漏的原因:由于 ThreadLocalMap 的生命周期跟 Thread 一样长,如果没有手动删除对应 key 就会导致该 key 的value 永远无法被访问,造成内存泄漏

正确使用方法:

  1. 每次使用完ThreadLocal都调用它的remove()方法清除数据,防止 ThreadLocalMap 中 Entry 一直保持对 value 的强引用,导致 value 不能被回收
  2. 将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉

线程池

线程池的七个参数:

  1. 核心线程数(corePoolSize): 核心线程数是线程池中保持活动状态的线程数。即使没有任务需要执行,核心线程也不会被回收。当有新任务提交时,如果核心线程都在忙碌,则会创建新的线程来处理任务。

  2. 最大线程数(maximumPoolSize): 最大线程数是线程池中允许的最大线程数。当工作队列满了并且活动线程数达到最大线程数时,如果还有新任务提交,线程池将创建新的线程来处理任务。但是,超过最大线程数的线程可能会导致资源消耗过大。

  3. 空闲线程存活时间(keepAliveTime): 空闲线程存活时间指的是非核心线程在没有任务执行时的最长存活时间。当线程池中的线程数超过核心线程数且空闲时间达到设定值时,多余的线程将被终止,直到线程池中的线程数不超过核心线程数。

  4. 时间单位(unit): 时间单位是用于表示核心线程数和空闲线程存活时间的单位。常见的时间单位包括秒、毫秒、分钟等。

  5. 工作队列(workQueue): 工作队列用于存储待执行的任务。当线程池中的线程都在忙碌时,新提交的任务将被添加到工作队列中等待执行。常见的工作队列类型有有界队列(如 ArrayBlockingQueue)和无界队列(如 LinkedBlockingQueue)等。

  6. 线程工厂(threadFactory): 线程工厂用于创建新线程。线程工厂提供了创建线程的方法,可以自定义线程的名称、优先级等属性。

  7. 拒绝策略(rejectedExecutionHandler): 拒绝策略定义了当线程池无法接受新任务时的处理策略。当工作队列已满且线程池中的线程数已达到最大线程数时,新任务将被拒绝执行。常见的拒绝策略有丢弃、丢弃最旧的任务、抛出异常等。
  8. AbortPolicy 拒绝任务并抛出一个异常 RejectedExecutionException
  9. DiscardPolicy 拒绝任务,不抛出异常。
  10. DiscardOldestPolicy 把老的任务丢掉,执行新任务。
  11. CallerRunsPolicy 直接调用线程处理该任务。

JDK四种线程池:

  • newCachedThreadPool,可根据需要创建新线程的线程池
  • newSingleThreadExecutor,单线程池
  • newFixedThreadPool,创建固定大小的线程池
  • newScheduledThreadPool,创建一个大小无限的线程池

线程池执行顺序:

  1. 首先判断 corePoolSize 是否已满,如果没有满,那么就去创建一个线程去执行该任务;否则请看下一步
  2. 如果线程池的核心线程数已满,那么就继续判断 BlockingQueue 是否已满,如果没满,那么就将任务放到任务队列中;否则请看下一步
  3. 如果任务队列已满,那么就判断线程池中的线程数量是否达到了maxumunPoolSize,如果没达到,那么就创建线程去执行该任务;否则请看下一步;
  4. 如果线程池已满,那么就根据拒绝策略来做出相应的处理;

简而言之:corePool->workQueue->maxPool

线程池被回收:线程池也是在堆中也是一个对象,一定要调用shutdown

线程池何时回收线程:getTask()的返回值为null时

  1. 未调用shutdown(),并且当前工作线程数过多
  2. 调用shutdown(),缓冲队列中的线程为空

核心线程数设置:

  1. CPU密集型任务:CPU核心数 + 1:这样设置线程池的大小能实现 CPU 的最优利用率。即使当计算密集型的线程偶尔由于页缺失故障或者其他原因暂停时,这个 “额外” 的线程也能确保CPU 的时装周期不会被浪费。
  2. IO密集型任务:CPU核心数 * 2
  3. 混合型任务:CPU核心数 * (1 + IO耗时/CPU耗时)

  1. 乐观锁,悲观锁:
    • 乐观锁,修改数据前比较数据是否被修改过。CAS,原子类的递增操作,适合频繁读
    • 悲观锁,加锁使其他线程无法修改。synchronized和lock的实现类,适合频繁写
  2. 自旋锁,非自旋锁:获取同步资源的锁失败,资源被占用(上下文切换,也就是线程的唤醒和阻塞是十分耗时的)
    • 自旋锁,不放弃CPU时间片,通过自旋等待锁的释放,但自旋超过一定次数(默认10次)仍没有获得锁,那么线程被挂起。线程竞争不激烈并且锁持有的时间不长时,可以使用自旋锁。
    • 非自旋锁,线程会进入阻塞状态
  3. 无锁,偏向锁,轻量级锁,重量级锁:指针对synchronized同步锁的状态,锁可以升级但不能降级。
    • 偏向锁,通过对比Mark Word中是否存储着指向当前线程的偏向锁以解决加锁问题,避免执行CAS操作来加锁和解锁,Java15放弃偏向锁。使用背景:锁不仅不存在多线程竞争,而且总是由同一个线程多次获取,那么在同一个线程反复获取所释放锁中,其中并还没有锁的竞争。
    • 轻量级锁,通过用CAS修改Mark Word操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。
    • 重量级锁,将除了拥有锁的线程以外的线程都阻塞。

锁升级的过程:当有线程访问同步块时,无锁升级为偏向锁;当有锁竞争时,升级为轻量级锁;当自旋十次失败,升级为重量级锁。

  1. 公平锁,非公平锁:
    • 公平锁,每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的。
    • 非公平锁,每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则,所有线程会竞争获取锁。
  2. 可重入锁(递归锁),非可重入锁:ReentrantLock和synchronized都是可重入锁,NonReentrantLock是非可重入锁
    • 可重入锁,指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。好处是一定程度避免死锁。
    • 非可重入锁,如果一个方法中获取锁并调用另外方法,那么在调用另外方法前需要释放锁。
  3. 独享锁(排它锁),共享锁
    • 独享锁,ReentrantLock、synchronized、ReentrantReadWriteLock的写锁
    • 共享锁,ReentrantReadWriteLock的读锁,可以再加共享锁但不可以加排他锁!

Synchronized(同步锁):属于独占锁、悲观锁、可重入锁、非公平锁。

ReentrantLock:继承了Lock类,两者都是可重入锁、悲观锁、独占锁、默认非公平锁。

AbstractQueuedSynchronizer(AQS)

该类是一个抽象类,采用模板方法的设计模式,规定了独占共享模式需要实现的方法。

简单解释:CAS修改volatile修饰的int值state(该值代表竞争资源标识) + 一个存放等待锁的线程队列。其定义了两种资源共享模式:

  1. 独占式。ReentrantLock 是独占式的锁资源。初始化 state = 0,表示资源未被锁定,调用 lock() 方法时state的值加一,并且当 state = 0 才表明其他线程有机会获取锁。

  2. 共享式。ReentrantWriteLock 和 CountDownLatch 是共享锁模式。CountDownLatch 会将任务分成 N 个子任务,初始化 state = N,每个子线程完成任务后会减一,直到为零。

Contract接口模式,结合feign实现

  • contract 用于暴露接口
  • service 用于实现接口

接口和抽象类的区别:

  • 相同点:
    • 都不能被实例化
    • 接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化
  • 不同点:
    • 接口是对行为的抽象(强调特定功能的实现),抽象类是对物体的抽象(强调所属关系)。
    • 接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
    • 实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
    • 接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

static修饰词

java中静态属性和静态方法可以被继承,但是不能被重写,因此不能实现多态。

静态常量/静态变量/静态方法是用static修饰的常量/变量/方法,其从属于类。另外,static是不允许用来修饰局部变量的。

  • 静态方法可以调用静态变量,但不能调用非静态变量,因为静态方法在类加载时就分配了内存,而非静态变量是在对象实例化时才分配内存。

  • 非静态方法可以调用静态变量,也可以调用非静态变量。

静态初始化块、初始化块和构造方法的区别

执行顺序:静态初始化块 > 初始化块 > 构造方法

非静态初始化块(构造代码块):

作用:给对象进行初始化。对象一建立就运行,且优先于构造函数的运行。

与构造函数的区别:

非静态初始化块给所有对象进行统一初始化,构造函数只给对应对象初始化。

应用:将所有构造函数共性的东西定义在构造代码块中。

静态初始化块:

作用:给类进行初始化。随着类的加载而执行,且只执行一次

与构造代码块的区别:

  • 构造代码块用于初始化对象,每创建一个对象就会被执行一次;静态代码块用于初始化类,随着类的加载而执行,不管创建几个对象,都只执行一次。
  • 静态代码块优先于构造代码块的执行
  • 都定义在类中,一个带static关键字,一个不带static

泛型和泛型擦除

泛型:参数化类型,指在定义一个类、接口或者方法时可以指定类型参数。

泛型擦除:是指Java中的泛型只在编译期有效,在运行期间会被删除。也就是说所有泛型参数在编译后都会被清除掉。

在编译器编译后,泛型的转换规则如下:

  • List、List 擦除后的类型为 List;
  • List[]、List[] 擦除后的类型为 List[];
  • List<? extends E>、List<? super E> 擦除后的类型为 List;
  • List<T extends Serialzable & Cloneable> 擦除后类型为 List。

JDK 设计模式

  1. 单例模式:Runtime 类使用饿汉式创建单例
  2. 工厂模式:线程池中所有线程,通过工厂模式创建
  3. 代理模式:java.lang.reflect.Proxy 中的动态代理
  4. 迭代器模式:java.util.Iterator 使用迭代器遍历集合容器
  5. 模板方法:AQS 中的 acquirerelease 方法被独占式和共享式所重写
This post is licensed under CC BY 4.0 by the author.

Java知识点记录博客

MySQL知识点汇总

diff --git a/posts/kafka-vs-rocketmq/index.html b/posts/kafka-vs-rocketmq/index.html new file mode 100644 index 000000000..26857b599 --- /dev/null +++ b/posts/kafka-vs-rocketmq/index.html @@ -0,0 +1 @@ + Kafka vs RocketMQ | Blogs
Home Kafka vs RocketMQ
Post
Cancel

Kafka vs RocketMQ

基本概念

  1. RocketMQ 由 Producer, Brocker, Consumer 组成
    • Producer 负责生产消息
    • Consumer 负责消费消息
    • Broker 负责存储消息,每一个 Broker 对应一台服务器但可以存储多个 Topic 的消息,每个 Topic 的消息也分片存储在不同的 Broker 里。
  2. Topic 是逻辑概念,队列(Kafka 中叫分区)是物理概念。每个主题包含多个消息,每条消息只属于一个主题。一个 Producer 可以同时发送多种 Topic 的消息,而一个 Consumer 只能订阅一个 Topic 的消息。Tag 类似于子主题。

  3. MessageQueue用于存储消息的物理地址,每个Topic中的消息地址存储于多个MessageQueue,是消息的最小存储单元。

  4. 消费方式,Pull 拉取式消费,Consumer 需要主动拉取 Broker 中的消息;Push 推送式消费,Broker 一接收到消息马上发送给 Consumer,具有实时性。RocketMQ是基于 Pull 模式的长轮询策略实现消息消费的。即 Consumer 发送拉取请求到 Broker 端,如果 Broker 有数据则返回并继续发起长轮询,如果没有则 hold 请求(指的服务端暂时不回复结果,保存相关请求,不关闭请求连接),不立即返回,直到超时(默认5s)并继续发起长轮询。为什么需要设置超时?1. 无法避免服务器假死等情况以确保可用性;2. 可能修改监听的配置

消息队列的三大作用

  • 解耦
  • 异步
  • 削峰

模式

  • 点对点模式:基于队列,每条消息只能被一个消费者消费,RabbitMQ。
  • 发布/订阅模式:一条消息能被多个消费者消费,RocketMQ 和 Kafka。

消息发送方式

  • RocketMQ
    1. 同步发送(Sync)
    2. 异步发送(Async)
    3. 单向发送(One-way) 其中同步发送和异步发送需要 Broker 返回确认消息,而单向发送不需要。
  • Kafka
    1. 发后即忘(fire-and-forget)
    2. 同步(sync)
    3. 异步(async)

消息消费方式

  • Kafka:从用户角度分为手动提交和自动提交;从 Consumer 的角度分为同步提交和异步提交
    1. 自动提交偏移量
    2. 手动同步提交偏移量
    3. 手动异步提交偏移量
    4. 同步异步结合提交偏移量

区别

整体区别是 Kafka 的设计初衷是用于日志传输,而 RocketMQ 是用于解决各类应用可靠的消息传输,适用于业务需求。

存储形式

  • Kafka 采用partition,每个topic的每个partition对应一个文件。顺序写入,定时刷盘。但一旦单个broker的partition过多,则顺序写将退化为随机写,Page Cache脏页过多,频繁触发缺页中断,性能大幅下降。

  • RocketMQ 采用CommitLog + ConsumeQueue,物理存储文件是CommitLog,ConsumeQueue是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。每个Topic下的每个MessageQueue都有一个对应的ConsumeQueue文件,单个broker所有topic在CommitLog中顺序写。每个CommitLog大小固定为1G。

生产消息:Producer 先向 CommitLog 顺序写,持久化后将数据 Dispatch 到 ConsumeQueue 中。 消费消息:Consumer 从 ConsumeQueue 中拉取数据,但拉取到数据是指向 CommitLog 的地址,此时是随机读,但又因为 PageCache 的存在,还是整体有序的。

Page Cache(页面缓存)从内存中划出一块区域缓存文件页,如果要访问外部磁盘上的文件页,首先将这些页面拷贝到内存中,再进行读写。

吞吐量

  • Kafka 单机吞吐量 TPS 可上百万,远高于 RocketMQ的 TPS 十万级。

数据可靠性

RocketMQ新增同步刷盘和同步复制机制,保证可靠性。而 Kafka 倾向于牺牲部分可靠性换取更高的性能(因为 Kafka 中的 producer 将信息堆起来一起发送,以减少网络IO,但是这个时候如果 producer 宕机了,会导致信息丢失的)。

  • Kafka 支持异步刷盘,异步复制。
  • RocketMq 支持异步刷盘和同步刷盘,同步复制和异步复制。

异步刷盘:返回写成功状态时,消息可能只是被写进内存,吞吐量大,当内存大消息积累到一定程度时,统一触发写磁盘操作,快速写入。

同步刷盘:返回写成功状态时,消息已经被写入磁盘。流程是消息写入内存后,立刻通知刷盘线程刷盘,等待刷盘完成后再唤醒等待的线程返回消息写成功的状态。

异步复制:只要写就返回写成功状态。较低的延迟和较高的吞吐量。

同步复制:写成功后返回写成功状态。容易恢复故障的数据。

如何保证消息不丢失

两者类似,都是从 Producer -> Broker -> Consumer 三个阶段逐一判断,只不过设置的参数不同。

  • Producer: 同步发送消息;超时重试发送;消息补偿机制(Kafka,超时仍失败的情况下,会继续投递到本地消息表,定时轮询并推送到Kafka);ACKs(Kafka中,该参数表示多少个副本收到消息,认为消息写入成功)

  • Broker: 同步刷盘;设置主从模式,配置副本

  • Consumer: At least Once 的消费机制;消费重试;ACK机制;手动提交位移(Kafka)

零拷贝

传统的数据传输过程通常需要经历多次内存拷贝。

首先,从磁盘读取数据,然后将数据从内核空间拷贝到用户空间,再从用户空间拷贝到应用程序的内存中。这些额外的拷贝会消耗大量的CPU资源和内存带宽,降低数据传输的效率。零拷贝就是为了避免这些不必要的数据拷贝,能够将数据直接传输到目标内存区域,以提高数据传输的效率。

实现方式

  1. mmap(Memory Mapped Files) + write,作用:将磁盘文件映射到内存, 用户通过修改内存就能修改磁盘文件。Java NIO里对应的是MappedByteBuffer类,可以用来实现内存映射。它的底层是调用了Linux内核的mmap的API。
  2. sendfile,实现:将读到内核空间的数据,直接拷贝到socket buffer,进行网络发送。避免了数据在内核和用户空间之间的额外拷贝。FileChannel的transferTo()/transferFrom(),底层就是sendfile() 系统调用函数。

零拷贝技术减少了用户进程地址空间和内核地址空间之间由于上下文切换而带来的开销。DMA (Direct Memory Access) 是零拷贝技术的基石。并不是不需要拷贝,而是减少冗余不必要的拷贝。

  • Kafka: Producer生产的数据持久化到 broker 采用 mmap 文件映射(在读写稀疏索引文件用到),实现顺序的快速写入;而 Customer 从 broker 读取数据采用 sendfile 进行网络发送。
  • RocketMQ:采用 mmap 的方法。正因为使用内存映射机制,RocketMQ的文件存储都使用定长结构来存储,方便一次将整个文件映射至内存。

为什么 Kafka 这么快?

答:六个要点,顺序读写、零拷贝、消息压缩、分批读写/发送、基于操作系统内存PageCache的读写、分区分段 + 索引。

为什么 RocketMQ 这么快?

答:顺序写,零拷贝,异步刷盘(先写入操作系统的PageCache再异步刷盘到磁盘)

消息失败重试

  • Kafka 不支持重试。
  • RocketMQ 支持定时重试,每次重试间隔逐渐增加。

RocketMQ 的消费重试是基于延迟消息实现的,在消息消费失败的情况下,重新当作延迟消息投递到 Broker 中,并且延迟等级逐渐增加,消息重试会有 16 个级别,恰好是延迟消息的 18 个级别的后 16 个级别。

Rocket延迟消息

主要分为以下几步,类似于流转:

  1. 修改消息的Topic名称和队列信息(因为发送到普通的Topic会马上被消费)
  2. 转发消息到延迟Topic为SCHDULE_TOPIC_XXX的ConsumeQueue中
  3. 延迟服务(本质是Java自带的延迟队列)消费SCHDULE_TOPIC_XXX的消息
  4. 将信息重新存储到CommitLog中
  5. 将消息投递到目标Topic中
  6. 消费Topic的消息

Kafka 延迟消息

基于 时间轮 算法(类似于钟表):存储定时任务的环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表。每隔一个时间跨度,下标移动一次。

事务

  • Kafka 支持事务(更像是原子性),可以实现对多个 Topic 、多个 Partition 的原子性的写入,即处于同一个事务内的所有消息,最终结果是要么全部写成功,要么全部写失败。这种事务机制单独使用的场景不多,更多的是配合其幂等机制来实现 Exactly Once 语义的。通常用于解决从一个 Kafka 数据源消费进行计算等操作,再输入到另一个 Kafka 数据源中的场景。
  • RocketMQ 支持事务,采用二阶段提交+broker定时回查。但也只能保证生产者与broker的一致性,broker与消费者之间只能单向重试。即保证的是最终一致性。

Kafka 事务

RocketMQ 事务

对于一个大事务来说,可以划分成多个小事务异步执行。

二阶段:第一阶段发送 prepared 消息,接着执行本地事务,第二阶段发送 commit 或 rollback 的消息。

定时回查:定时遍历 commitlog 中的半事务消息

如果事务正常执行,则 commit 该消息,如果抛出异常,则 rollback。对于消费消息失败,RocketMQ 会尝试重新消费,直到被加入死信队列中为止。在重试的过程中有可能产生重复的消息,所以对于消费端来说要确保消费幂等

消息堆积

原因可能是以下三种:

  1. 生产远超预期
  2. 消息接收和持久化出现故障
  3. 消费能力下降
  4. 程序问题

处理堆积的消息:建立临时的 topic(扩容),转发堆积的消息

服务发现

  • Kafka 使用 ZooKeeper,但新版本使用内嵌的KRaft替代了ZooKeeper
  • RocketMQ 使用自己实现的 nameserver

Topic 数量对吞吐量的影响

RocketMQ:topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic

Kafka:topic 从几十到几百个时候,吞吐量会大幅度下降。原理:Kafka 利用操作系统的 PageCache 先将消息持久化到内存中,并不是直接写入磁盘。Topic 增加,也就是 Partition 数量会增加,使用的 PageCache 也会大量增加,大量增加后需要使用 LRU 淘汰算法对 Page 内容刷新到磁盘中,导致性能会下降。

消息顺序性

分为两步:生产者有序存储,消费者有序消费。

Kafka 如何保证消息的顺序性

针对消息有序的业务需求,还分为全局有序和局部有序。已知,每个partition的消费是顺序性的,但每个topic可以有若干个partition。

全局有序:一个Topic下的所有消息都需要按照生产顺序消费。

解决方法:1个Topic只能对应1个Partition。

局部有序:一个Topic下的消息,只需要满足同一业务字段的要按照生产顺序消费。例如:Topic消息是订单的流水表,包含订单orderId,业务要求同一个orderId的消息需要按照生产顺序进行消费。

解决方法:要满足局部有序,只需要在发消息的时候指定Partition Key,Partition Key相同的消息会放在同一个Partition。

RocketMQ 如何保证消息的顺序性

全局有序:对于指定的一个 Topic,设置读写队列的数量为一。(与Kafka设置一个partition类似) 局部有序:对于指定的一个 Topic,生产者根据 hashKey 将消息发送到同一个MessageQueue。 同一个分区内的消息按照严格的 FIFO 顺序进行发布和消费。

实现消息有序性从三个方面:

  1. 生产者生产顺序消息:生产者将消息路由到特定分区,单线程发送消息
  2. Broker 保存顺序消息:保证生产者顺序生产即可,保存到指定的Partition或MessageQueue中
  3. 消费者顺序消费消息:设置 consumeMode 为 ORDERLY,单线程消费消息

Kafka 负载策略

主写主读,不支持读写分离

Producer 负载均衡

当 key 不存在时,会从当前存活的分区中轮询;当 key 存在时,发送给哈希后的指定分区。

Consumer 负载均衡

Kafka 中主题订阅者的基本单位是消费者组,每个分区只能由消费者组中的一个消费者进行消费,多个消费者组之间对于分区的消费互不影响。共有三个分区分配器

  1. RangeAssignor(默认)
  2. RoundRobinAssignor
  3. StickyAssignor

RocketMQ 负载策略

Producer 负载均衡

Producer 默认采用轮询的方法,按顺序将消息发送到 MessageQueue 里。

Consumer 负载均衡

  1. 平均负载策略(默认):AllocateMessageQueueAveragely

  1. 循环分配策略:循环顺序遍历消费者:AllocateMessageQueueAveragelyByCircle

  1. 指定机房分配策略:AllocateMessageQueueByMachineRoom

  1. 机房就近分配策略:AllocateMachineRoomNearby

  1. 一致性哈希算法策略:AllocateMessageQueueConsistentHash

  1. 按照指定配置的策略:AllocateMessageQueueByConfig

适用场景

Kafka:适用于日志收集与分析、实时流处理、大数据集成(例如 Apache Storm 或 Flink)、用户行为追踪等场景。 RocketMQ:更适合金融交易、订单处理、秒杀活动、库存同步、跨系统间的服务解耦和异步调用等场景,尤其是那些对消息顺序、事务完整性和实时性要求极高的业务。

消费者

消费者群组(Consumer Group)

消费者组是一组共享 group.id 的消费者实例,一个消费者组可以消费多个 Topic 的消息,组内的消费者只能订阅相同的 Topic 和相同的 Tag 且 Tag 顺序相同。详见订阅关系一致

消费方式(RocketMQ)

  1. 集群模式(默认):相同消费者群组的消费者平摊消息,便于负载均衡

  2. 广播模式:相同消费者群组的每个消费者接收全量的消息,适合并行处理的场景。在该模式下,消费者组的概念在消息划分方面并没有意义。

RocketMQ/Kafka 使用 Consumer Group 机制,实现了传统两大消息引擎。如果所有实例属于同一个Group,那么它实现的就是消息队列模型;如果所有实例分别属于不同的Group,且订阅了相同的主题,那么它就实现了发布/订阅模型;

消费者和消费者组的关系

  1. 同一个消费者组内部的消费者均匀消费订阅的 Topic 的消息,负载均衡
  2. 不同消费者组全量消费订阅的 Topic 的消息,类似消费者组层面的广播模式。但 Kafka 和 RocketMQ 不同的地方在于,Kafka 所有 Partition 会均匀分配给 Consumer 消费(因此 Consumer 只消费 Topic 的部分数据),而不像 RocketMQ 那样,每个 Consumer 全量消费 Topic 里的消息。

Kafka 知识点

知识点

  1. Partition 数量只能增加,不能减少。
  2. 偏移量:指Kafka主题中每个分区中消息的唯一标识符
  3. ISR: In-Sync Replica; OSR: Out-Sync Replica

索引机制

一个Topic分为多个Partition,一个Partition分为多个Segment。每个Segment对应三个文件:偏移量索引文件、时间戳索引文件、消息存储文件

Producer 生产消息的流程

在消息发送的过程中,涉及到两个线程,main线程和sender线程,其中main线程是消息的生产线程,而sender线程是jvm单例的线程,专门用于消息的发送。在jvm的内存中开辟了一块缓存空间叫RecordAccumulator(消息累加器),用于将多条消息合并成一个批次,然后由sender线程发送给kafka集群。

  1. 创建消息以及指定 Topic 调用 Send() 方法
  2. 调用拦截器
  3. 调用序列化器,将消息序列化
  4. 使用分区器(三种分区策略)指定 Partition
    1. DefaultPartitioner 默认分区:指定分区则用该分区,没指定则使用对 key 哈希后值的分区,没有 key 则使用粘性分区策略
    2. UniformStickyPartitioner 统一粘性分区:直接使用粘性分区策略,即逐个填满 Batch 里的消息
    3. RoundRobinPartitioner 轮询分区:指定分区则用该分区,否则平均分配
  5. 将消息缓存到消息累加器中
  6. 压缩和批处理消息
  7. 找到相应的 Broker 并发送消息(Sender 线程触发的),可以同步发送也可以异步发送
  8. 确认(ACK)和重试
  9. 更新偏移量
  10. 错误处理,将重试仍失败的消息放入死信队列里

Consumer 消费过程

Kafka 采用 Pull 的方式,每个 Consumer 维护一个 HW 水位信息

消费者线程模型

Thread per consumer model:即每个线程都有自己的consumer实例,然后在一个线程里面完成数据的获取(pull)、处理(process)、offset提交。

Leader Replica 选举策略

  1. ISR 选举策略:在 ISR 副本集合中选举
  2. 首选副本选举策略:每个分区都有一个首选副本,通常是副本集合中的第一个副本
  3. 不干净副本选举策略:从所有副本中(包含 OSR 集合)选择一个副本选举

Zookeeper 在 Kafka 的作用

  1. Broker 注册:每个Broker服务器在启动时,都会到ZooKeeper上进行注册
  2. Topic 注册:同一个 Topic 的消息会被分成多个分区并将其分布在多个 Broker 上,ZK 负责维护这些分区信息及与 Broker 的对应关系
  3. 负载均衡:Consumer 消费消息时,ZK 会根据当前 Partition 数量和 Consumer 数量进行动态负载均衡

消息压缩/解压

Producer 发送压缩消息到 Broker 后,Broker 会保存压缩数据,由Consumer 解压数据。

Controller

Controller 用于在 ZK 的帮助下管理和协调整个 Kafka 集群。集群内任意一台 Broker 都能充当 Controller 的角色,但在运行过程中,只有一个 Controller。

Controller 职责

  1. 管理 Topic
  2. Preferred Leader 选举
  3. 集群 Broker 管理
  4. 数据服务,保存最完整的元数据信息
This post is licensed under CC BY 4.0 by the author.

SpringBoot

Note from Work

diff --git a/posts/make-coco-dataset/index.html b/posts/make-coco-dataset/index.html new file mode 100644 index 000000000..37dd10855 --- /dev/null +++ b/posts/make-coco-dataset/index.html @@ -0,0 +1,381 @@ + Python脚本制作coco格式的实例分割数据集 | Blogs
Home Python脚本制作coco格式的实例分割数据集
Post
Cancel

Python脚本制作coco格式的实例分割数据集

Introdution

利用labelme制作coco格式的实例分割数据集,该数据集适用于mmdetection2.0中的mask部分。

在mmdetection2.0框架下,利用coco格式的数据集进行实例分割默认只需要train2017和val2017两部分(当然也可以将test中的目录修改成test2017,但没必要)。

mmdetection2.0框架下coco格式数据集文件如下图放置:(运行完本文的.py文件后即可生成以下文件夹)

数据集标注方式:

当一张图片里没有多个同类别的物体,使用car,computer,bottle等标签直接进行标注;当一张图片里同个类别有多个物体时,标签采用sofa-1,sofa-2,desk-1,desk-2等标签-数字的格式进行标注;如果同一物体在遮挡情况下被分为多个部分,则不同部分都用同一个标签(具体如下图所示,牙膏的三个部分标签均为toothpaste)。

最后得到的json文件里的segmentation是分为三部分,bbox只存在一个(在笔者找的其他资料里,都是分为三个独立的牙膏部分,得到三个segmentation以及三个相应的bbox,显然不符合实际情况)。

建议在使用labelme进行对数据集标注时,将生成的.json文件放置在与数据集相同的路径下以避免一些不必要的麻烦。即下图:

否则,生成的json文件里的imagePath可能出现下图所示的情况:

如果实在是没办法,没有在同一路径下,imagePath会比较复杂(可能是windows系统用\分隔号,ubuntu系统则会是/分隔号,这一点是笔者的猜测),根据不同的情况修改后文代码。

Solution

各文件夹布局如下所示:

所有的.jpg文件放在images文件夹下,所有的.json文件(labelme标注完成后生成的文件)放在labelme/total2017文件夹下。

creat_txt.py

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
+
# !/usr/bin/python
+# -*- coding: utf-8 -*-
+import os
+import random
+
+trainval_percent = 1 # No test sample
+train_percent = 0.9
+jsonfilepath = 'labelme/total2017'
+txtsavepath = './'
+total_xml = os.listdir(jsonfilepath)
+
+num = len(total_xml)
+list = range(num)
+tv = int(num * trainval_percent)
+tr = int(tv * train_percent)
+trainval = random.sample(list, tv)
+train = random.sample(trainval, tr)
+
+ftrainval = open('./trainval2017.txt', 'w')
+ftrain = open('./train2017.txt', 'w')
+fval = open('./val2017.txt', 'w')
+ftest = open('./test2017.txt', 'w')  #Still create test2017.txt
+
+for i in list:
+    name = total_xml[i][:-5] + '\n'
+    if i in trainval:
+        ftrainval.write(name)
+        if i in train:
+            ftrain.write(name)
+        else:
+            fval.write(name)
+    else:
+        ftest.write(name)
+ftrainval.close()
+ftrain.close()
+fval.close()
+ftest.close()
+print('Create_txt Done')
+

classify.py

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
import shutil
+import os
+import os.path as osp
+
+sets=['train2017',  'val2017', 'test2017']
+for image_set in sets:
+    if osp.exists(image_set):
+        shutil.rmtree(image_set)
+        print('Deleted previous %s file and created a new one'%(image_set))
+    os.makedirs(image_set)
+    json_path = 'labelme/%s'%(image_set)
+    if osp.exists(json_path):
+        shutil.rmtree(json_path)
+        print('Deleted previous %s file and created a new one' % (json_path))
+    os.makedirs(json_path)
+    image_ids = open('./%s.txt'%(image_set)).read().strip().split()
+    for image_id in image_ids:
+        img = 'images/%s.jpg' % (image_id)
+        json = 'labelme/total2017/%s.json'% (image_id)
+        shutil.copy(img,image_set)
+        shutil.copy(json,'labelme/%s/'% (image_set))
+print("Done")
+

labelme2coco.py

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
+
#!/usr/bin/env python
+import collections
+import datetime
+import glob
+import json
+import os
+import os.path as osp
+import sys
+import numpy as np
+import PIL.Image
+import labelme
+import shutil
+
+try:
+    import pycocotools.mask
+except ImportError:
+    print('Please install pycocotools:\n\n    pip install pycocotools\n')
+    sys.exit(1)
+
+
+def main():
+    sets = ['train2017','val2017','test2017']
+    output_dir = './annotations'
+    if osp.exists(output_dir):
+        print('Output directory already exists:', output_dir)
+        shutil.rmtree(output_dir)
+    os.makedirs(output_dir)
+    print('Creating dataset:', output_dir)
+    for set in sets:
+        input_dir = './labelme/%s'%(set)
+        filename = 'instances_%s'%(set)
+        now = datetime.datetime.now()
+        data = dict(
+            info=dict(
+                description=None,
+                version=None,
+                contributor=None,
+                date_created=now.strftime('%Y-%m-%d %H:%M:%S.%f'),
+            ),
+            licenses=[dict(
+                id=0,
+                name=None,
+            )],
+            images=[
+                # license, url, file_name, height, width, date_captured, id
+            ],
+            type='instances',
+            annotations=[
+                # segmentation, area, iscrowd, image_id, bbox, category_id, id
+            ],
+            categories=[
+                # supercategory, id, name
+            ],
+        )
+
+        class_name_to_id = {}
+        for i, line in enumerate(open('labels.txt').readlines()):
+            class_id = i - 1  # starts with -1
+            class_name = line.strip()
+            if class_id == -1:
+                assert class_name == '__ignore__'
+                continue
+            class_name_to_id[class_name] = class_id
+            data['categories'].append(dict(
+                supercategory=None,
+                id=class_id,
+                name=class_name,
+            ))
+        out_ann_file = osp.join(output_dir,  filename+'.json')
+        label_files = glob.glob(osp.join(input_dir, '*.json'))
+        for image_id, label_file in enumerate(label_files):
+            with open(label_file) as f:
+                label_data = json.load(f)
+            path=label_data['imagePath'].split("\\") # 可能因为windows或ubuntu不同的系统用\\或/划分,详见前言三
+            img_file = './%s/'%(set) + path[-1]
+            img = np.asarray(PIL.Image.open(img_file))
+            data['images'].append(dict(
+                license=0,
+                url=None,
+                file_name=label_file.split('/')[-1].split('.')[0] + '.jpg',
+                height=img.shape[0],
+                width=img.shape[1],
+                date_captured=None,
+                id=image_id,
+            ))
+            masks = {}                                     # for area
+            segmentations = collections.defaultdict(list)  # for segmentation
+            for shape in label_data['shapes']:
+                points = shape['points']
+                label = shape['label']
+                shape_type = shape.get('shape_type', None)
+                mask = labelme.utils.shape_to_mask(
+                    img.shape[:2], points, shape_type
+                )
+
+                if label in masks:
+                    masks[label] = masks[label] | mask
+                else:
+                    masks[label] = mask
+
+                points = np.asarray(points).flatten().tolist()
+                segmentations[label].append(points)
+
+            for label, mask in masks.items():
+                cls_name = label.split('-')[0]
+                if cls_name not in class_name_to_id:
+                    continue
+                cls_id = class_name_to_id[cls_name]
+
+                mask = np.asfortranarray(mask.astype(np.uint8))
+                mask = pycocotools.mask.encode(mask)
+                area = float(pycocotools.mask.area(mask))
+                bbox = pycocotools.mask.toBbox(mask).flatten().tolist()
+
+                data['annotations'].append(dict(
+                    id=len(data['annotations']),
+                    image_id=image_id,
+                    category_id=cls_id,
+                    segmentation=segmentations[label],
+                    area=area,
+                    bbox=bbox,
+                    iscrowd=0,
+                ))
+
+        with open(out_ann_file, 'w') as f:
+            json.dump(data, f,indent=4)
+        print(set + ' is done')
+
+if __name__ == '__main__':
+    main()
+

上述三个文件按顺序执行即可。

最后所有的文件夹如下图所示,如前文所提到的,在mmdetection中被利用到的只有annotations,train2017,val2017三个文件夹。

本文的代码可以反复运行,因为代码中包含一些旧文件夹的删除以及新建,不会报错。

Reference

  1. prepare_detection_dataset
This post is licensed under CC BY 4.0 by the author.

Github删除Commits记录

Python实现点击图片获取HSV或BGR的值

diff --git a/posts/mathjax/index.html b/posts/mathjax/index.html new file mode 100644 index 000000000..a530e926a --- /dev/null +++ b/posts/mathjax/index.html @@ -0,0 +1,31 @@ + MathJax 数学公式语法 | Blogs
Home MathJax 数学公式语法
Post
Cancel

MathJax 数学公式语法

MathJax中的公式排版有两种方式,inline和displayed。

  • inline表示公式嵌入到文本段中,也称为行模式。$ f(x) = 3 \times x $这是一个inline公式

  • displayed表示公式独自成为一个段落,也成为块模式。下面则是一个displayed公式。

\[f(x) = 3 \times x\]

符号(Operators)

SymbolCommandSymbolCommandSymbolCommand
$\pm$\pm$\mp$\mp$\times$\times
$\div$\div$\cdot$\cdot$\ast$\ast
$\star$\star$\dagger$\dagger$\ddagger$\ddagger
$\amalg$\amalg$\cap$\cap$\cup$\cup
$\uplus$\uplus$\sqcap$\sqcap$\sqcup$\sqcup
$\vee$\vee$\wedge$\wedge$\oplus$\oplus
$\ominus$\ominus$\otimes$\otimes$\circ$\circ
$\bullet$\bullet$\diamond$\diamond$\lhd$\lhd
$\rhd$\rhd$\unlhd$\unlhd$\unrhd$\unrhd
$\oslash$\oslash$\odot$\odot$\bigcirc$\bigcirc
$\triangleleft$\triangleleft$\Diamond$\Diamond$\bigtriangleup$\bigtriangleup
$\bigtriangledown$\bigtriangledown$\Box$\Box$\triangleright$\triangleright
$\setminus$\setminus$\wr$\wr$\sqrt{x}$\sqrt{x}
$x^{\circ}$x^{\circ}$\triangledown$\triangledown$\sqrt[n]{x}$\sqrt[n]{x}
$a^x$a^x$a^{xyz}$a^{xyz}$\frac{x}{y}$\frac{x}{y}
$\sin{x}$\sin{x}$\cos{x}$\cos{x}$\tan{x}$\tan{x}
$\log_xy$\log_xy$\ln{x}$\ln{x}$\max(x,y,z)$\max(x,y,z)
  • 求和:\sum\limits_{i=0}^n{a_i} 显示为$\sum\limits_{i=0}^n{a_i}$
  • 求积:\prod\limits_{i=0}^n{\frac{1}{i^2}} 显示为$\prod\limits_{i=0}^n{\frac{1}{i^2}}$
  • 积分:\int_0^xf(x)dx显示为$\int_0^xf(x)dx$
  • 极限:\lim\limits_{x\to 0}{x} 显示为$\lim\limits_{x\to 0}{x}$
  • 自定义符号:\mathop{SUPER}\limits_{i=0}^n{i^2} 显示为$\mathop{SUPER}\limits_{i=0}^n{i^2}$

关系(Relations)

SymbolCommandSymbolCommandSymbolCommand
$\le $\le$\ge $\ge$\neq $\neq
$\sim $\sim$\ll $\ll$\gg $\gg
$\doteq $\doteq$\simeq $\simeq$\subset $\subset
$\supset $\supset$\approx $\approx$\asymp $\asymp
$\subseteq $\subseteq$\supseteq $\supseteq$\cong $\cong
$\smile $\smile$\sqsubset $\sqsubset$\sqsupset $\sqsupset
$\equiv $\equiv$\frown $\frown$\sqsubseteq $\sqsubseteq
$\sqsupseteq$\sqsupseteq$\propto $\propto$\bowtie $\bowtie
$\in $\in$\ni $\ni$\prec $\prec
$\succ $\succ$\vdash $\vdash$\dashv $\dashv
$\preceq $\preceq$\succeq $\succeq$\models $\models
$\perp $\perp$\parallel $\parallel  
$\mid $\mid$\bumpeq $\bumpeq  

上面这些关系符号的否定(反义)形式可以通过在原符号前添加 \not 来进行实现,或者在 \ 和符号单词之间添加 n 来实现。

下面列出几个常用的否定形式,其他符号的否定形式规则基本类似。

SymbolCommandSymbolCommandSymbolCommand
$\nmid $\nmid$\nleq $\nleq$\ngeq $\ngeq
$\nsim $\nsim$\ncong $\ncong$\nparallel $\nparallel
$\not< $\not<$\not> $\not>$\not= $\not=
$\not\le $\not\le$\not\ge $\not\ge$\not\sim $\not\sim
$\not\approx $\not\approx$\not\cong $\not\cong$\not\equiv $\not\equiv
$\not\parallel $\not\parallel$\nless $\nless$\ngtr $\ngtr
$\lneq $\lneq$\gneq $\gneq$\lnsim $\lnsim
$\lneqq $\lneqq$\gneqq $\gneqq  

=, >, 和 < 并没有列在上面的符号,可以直接字面输入,并不需要命令进行触发。

希腊字母(Greek Letters)

小写:

SymbolCommandSymbolCommandSymbolCommand
$\alpha $\alpha$ \beta $\beta$\gamma $\gamma
$\epsilon $\epsilon$ \varepsilon $\varepsilon$\zeta $\zeta
$\theta $\theta$ \vartheta $\vartheta$\iota $\iota
$\lambda $\lambda$ \mu $\mu$\nu $\nu
$\pi $\pi$ \varpi $\varpi$\rho $\rho
$\sigma $\sigma$ \varsigma $\varsigma$\tau $\tau
$\phi $\phi$ \varphi $\varphi$\chi $\chi
$\omega $\omega$ \varrho $\varrho$\kappa $\kappa
$\delta $\delta$ \upsilon $\upsilon$\xi $\xi
$\eta $\eta$ \psi $\psi  

大写:

SymbolCommandSymbolCommandSymbolCommand
$\Gamma $\Gamma$\Delta $\Delta$ \Theta $\Theta
$\Xi $\Xi$\Pi $\Pi$ \Sigma $\Sigma
$\Phi $\Phi$\Psi $\Psi$ \Omega $\Omega
$\Lambda $\Lambda$\Upsilon $\Upsilon  

斜体大写:

SymbolCommandSymbolCommandSymbolCommand
$\varGamma $\varGamma$\varDelta $\varDelta$ \varTheta $\varTheta
$\varXi $\varXi$\varPi $\varPi$ \varSigma $\varSigma
$\varPhi $\varPhi$\varPsi $\varPsi$ \varOmega $\varOmega
$\varLambda $\varLambda$\varUpsilon $\varUpsilon  

箭头(Arrors)

SymbolCommandSymbolCommand
$\gets $\gets$\to $\to
$\leftarrow $\leftarrow$\Leftarrow $\Leftarrow
$\rightarrow $\rightarrow$\Rightarrow $\Rightarrow
$\leftrightarrow $\leftrightarrow$\Leftrightarrow $\Leftrightarrow
$\mapsto $\mapsto$\hookleftarrow $\hookleftarrow
$\leftharpoonup $\leftharpoonup$\leftharpoondown $\leftharpoondown
$\rightleftharpoons $\rightleftharpoons$\longleftarrow $\longleftarrow
$\Longleftarrow $\Longleftarrow$\longrightarrow $\longrightarrow
$\Longrightarrow $\Longrightarrow$\longleftrightarrow$\longleftrightarrow
$\Longleftrightarrow $\Longleftrightarrow$\longmapsto $\longmapsto
$\hookrightarrow $\hookrightarrow$\rightharpoonup $\rightharpoonup
$\rightharpoondown $\rightharpoondown$\leadsto $\leadsto
$\uparrow $\uparrow$\Uparrow $\Uparrow
$\downarrow $\downarrow$\Downarrow $\Downarrow
$\updownarrow $\updownarrow$\Updownarrow $\Updownarrow
$\nearrow $\nearrow$\searrow $\searrow
$\swarrow $\swarrow$\nwarrow $\nwarrow

有些箭头指令, mathjax 提供了缩写指令, $\iff$(\iff) 和 $\implies$(\implies) 可以分别表示为 $\Longleftrightarrow$(\Longleftrightarrow) 和 $\Longrightarrow$(\Longrightarrow)

重音(Accents)

SymbolCommandSymbolCommandSymbolCommand
$\hat{x} $\hat{x}$\check{x} $\check{x}$\dot{x} $\dot{x}
$\breve{x}$\breve{x}$\acute{x} $\acute{x}$\ddot{x} $\ddot{x}
$\grave{x}$\grave{x}$\tilde{x} $\tilde{x}$\mathring{x} $\mathring{x}
$\bar{x} $\bar{x}$\vec{x} $\vec{x}$\overline{x} $\overline{x}
$\widehat{7+x}$\widehat{7+x}$\widetilde{abc}$\widetilde{abc}  

\tilde\hat 两个指令有宽符号的版本。

上述表格最后一行,\widetilde\widehat,通过这两个指令可以生成长版本的表达式结构的符号。

方程组(Equation Sets)

1
+2
+3
+4
+5
+6
+7
+
$$
+f(n) =  \tag{1}
+\begin{cases}
+\frac{a_n^3}{2},  & \text{if $n$ is even} \\ 
+3a_n^2+1, & \text{if $n$ is odd}
+\end{cases}
+$$
+
\[f(n) = \tag{1} \begin{cases} \frac{a_n^3}{2}, & \text{if $n$ is even} \\ 3a_n^2+1, & \text{if $n$ is odd} \end{cases}\]

字体(Fonts)

  • 打印机字体Typewriter:\mathtt{R} 显示为$\mathtt{R}$
  • 黑板粗体字Blackboard Bold:\mathbb{R} 显示为$\mathbb{R}$。表示实数集的意思。
  • 无衬线字体Sans Serif:\mathsf{R} 显示为$\mathsf{R}$
  • 手写体Script:\mathscr{R} 显示为$\mathscr{R}$
  • 罗马字体Roman:\mathrm{R} 显示为$\mathrm{R}$

点(Dots)

SymbolCommandSymbolCommand
$\cdot $\cdot$\vdots $\vdots
$\dots $\dots$\ddots $\ddots
$\cdots$\cdots$\ldots $\ldots

\ldots\cdots 是低位置省略号和中心位置省略号的 latex 命令, \dotsamsmath 命令用来试图帮你在 \ldots\cdots 中自动做决断的。

通常来讲中心省略 \cdots 一般用在数学模式的中心线上的符号后面,例如加号 + 或者右箭头 -> , 而 \ldots 一般用在标点符号的后面,例如句号“ . ” or逗号“ , ”。

例如,

$ a + b + \cdots + z \quad a_1, \ldots, a_n $ 显示为$ a + b + \cdots + z \quad a_1, \ldots, a_n $

改为\dots便可以根据实际情况自动地改变省略号的位置

$ a + b + \dots + z \quad a_1, \dots, a_n $ 显示为$ a + b + \dots + z \quad a_1, \dots, a_n $

使用\dots基本可以满足要求。但是,\dots 并不是每次都能正确自动改变省略号的位置,所以还是需要根据自己的实际情况选择不同的dots。

矩阵(Matrices)

  • 小括号边框:pmatrix
  • 中括号边框:bmatrix
  • 大括号边框:Bmatrix
  • 单竖线边框:vmatrix
  • 双竖线边框:Vmatrix
1
+2
+3
+4
+5
+6
+7
+8
+
$$
+\begin{bmatrix}
+{a_{11}}&{a_{12}}&{\cdots}&{a_{1n}}\\
+{a_{21}}&{a_{22}}&{\cdots}&{a_{2n}}\\
+{\vdots}&{\vdots}&{\ddots}&{\vdots}\\
+{a_{m1}}&{a_{m2}}&{\cdots}&{a_{mn}}\\
+\end{bmatrix}
+$$
+
\[\begin{bmatrix} {a_{11}}&{a_{12}}&{\cdots}&{a_{1n}}\\ {a_{21}}&{a_{22}}&{\cdots}&{a_{2n}}\\ {\vdots}&{\vdots}&{\ddots}&{\vdots}\\ {a_{m1}}&{a_{m2}}&{\cdots}&{a_{mn}}\\ \end{bmatrix}\]

界限符号(Bracketing Symbols)

在数学公式里,有时我们会通过括号( (), [], {} )进行界线控制。这些符号有些是可以直接输入,比如 (), [], | 等,而有些符号是要经过转义的,下面列出了这些比较特殊的符号。

SymbolCommandSymbolCommand
$\lfloor $\lfloor$\rfloor $\rfloor
$\lceil $\lceil$\rceil $\rceil
$\langle $\langle$\rangle $\rangle

上括号$ \overbrace{a_0+a_1+a_2+\cdots+a_n}^{x} $,显示为$ \overbrace{a_0+a_1+a_2+\cdots+a_n}^{x} $

下括号$ \underbrace{a_0+a_1+a_2+\cdots+a_n}_{x} $,显示为$ \underbrace{a_0+a_1+a_2+\cdots+a_n}_{x} $

但是有时候界限符号不够高,所以需要一些自适应括号,比如$ (\frac{a}{x} )^2 $,显示为

\[(\frac{a}{x} )^2\]

使用自适应括号的代码为$ \left(\frac{a}{x} \right)^2 $,显示为

\[\left(\frac{a}{x} \right)^2\]

但是也只有在块模式的时候会比较明显。

其他(Others)

SymbolCommandSymbolCommandSymbolCommand
$\infty $\infty$ \triangle $\triangle$\angle $\angle
$\aleph $\aleph$ \hbar $\hbar$\imath $\imath
$\jmath $\jmath$ \ell $\ell$\wp $\wp
$\Re $\Re$ \Im $\Im$\mho $\mho
$\prime $\prime$ \emptyset $\emptyset$\nabla $\nabla
$\surd $\surd$ \partial $\partial$\top $\top
$\bot $\bot$ \vdash $\vdash$\dashv $\dashv
$\forall $\forall$ \exists $\exists$\neg $\neg
$\flat $\flat$ \natural $\natural$\sharp $\sharp
$\backslash$\backslash$ \Box $\Box$\Diamond $\Diamond
$\clubsuit $\clubsuit$ \diamondsuit $\diamondsuit$\heartsuit $\heartsuit
$\spadesuit$\spadesuit$ \Join $\Join$\blacksquare$\blacksquare
$\bigstar $\bigstar$ \in $\in$\cup $\cup
$\square $\square$ \S $\S$\checkmark $\checkmark
$\because $\because$ \therefore $\therefore  
This post is licensed under CC BY 4.0 by the author.

Difference between NEW and MALLOC in C++

Dev-C++支持C++11的操作

diff --git a/posts/merge-two-repos/index.html b/posts/merge-two-repos/index.html new file mode 100644 index 000000000..67df552d2 --- /dev/null +++ b/posts/merge-two-repos/index.html @@ -0,0 +1,21 @@ + Github合并两个不同的仓库 | Blogs
Home Github合并两个不同的仓库
Post
Cancel

Github合并两个不同的仓库

Description

两个独立的仓库A、B,将仓库B合并至仓库A的分支,并保留A、B的所有commits

例如:将dumped-CompetitiveLin.github.io中的所有提交内容合并至CompetitiveLin.github.io的another分支。

Solution

1. 克隆主仓库代码

1
+
git clone git@github.com:CompetitiveLin/CompetitiveLin.github.io
+

2. 添加需要合并远程仓库

1
+
git remote add base git@github.com:CompetitiveLin/dumped-CompetitiveLin.github.io
+

此时查看remote: git remote -v,如下图所示:

如果需要删除remote: git remote rm base

3. 把base远程仓库中数据抓取到本仓库

1
+
git fetch base
+

4. 切换到base分支上,命名为dumped

1
+
git checkout -b another base/dumped
+

查看branch分支:git branch

5. 切换到main分支

1
+
git checkout main
+

6. 合并两个分支

1
+
git merge another
+

此时可能出现类似下图fatal: refusing to merge unrelated histories的报错信息。

解决方法:

1
+
git merge another --allow-unrelated-histories
+

在合并时有可能两个分支对同一个文件都做了修改,这时需要解决冲突1,在windows下可以使用Github Desktop解决冲突问题。

7. 提交

1
+
git push origin another
+

Problems

A. Permission denied (publickey)

原因:没有配置ssh-key,没有权限2

解决方法:

1
+2
+
  ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
+  clip < ~/.ssh/id_rsa.pub
+

See here.

Reference

This post is licensed under CC BY 4.0 by the author.

Python实现点击图片获取HSV或BGR的值

申请软著的时间周期记录以及注意事项

diff --git a/posts/mysql/index.html b/posts/mysql/index.html new file mode 100644 index 000000000..08ea6df6e --- /dev/null +++ b/posts/mysql/index.html @@ -0,0 +1 @@ + MySQL知识点汇总 | Blogs
Home MySQL知识点汇总
Post
Cancel

MySQL知识点汇总

单机QPS

单机 QPS 为 4k 左右。

MySQL select语句执行

  • prepare 阶段,检查查询语句中的表活字段是否存在,将 * 拓展为表上的所有列。
  • optimize 阶段,优化器决定使用哪个索引。
  • execute 阶段,执行器,索引下推。

表空间文件结构

组成:段(默认256MB) -> 区(默认1MB) -> 页(默认16KB) -> 行

页:InnoDB的数据按为单位读写,默认每个页大小为16KB,意味着一次最少从磁盘读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。表中的记录存储在数据页中。B+树的每个节点都是一个数据页。

区:数据量大的时候,索引分配空间则是按为单位分配,每个区的大小为1MB,对于16KB的页来说,连续(物理连续)64个页会被划分到一个区,这样,B+树中节点所构成的链表中相邻页的物理位置也相邻,便能顺序IO。

段:段是数据库中的分配单位,不同类型的数据库对象以不同的段形式存在。当创建数据表、索引的时候,就会相应创建对应的段,比如创建一张表时会创建一个表段,创建一个索引时会创建一个索引段。在段中不要求区与区之间是相邻的。

  • 索引段:存放B+树非叶子结点的区的集合
  • 数据段:存放B+树叶子结点的区的集合
  • 回滚段:存放的是回滚数据的区的集合,MVCC利用回滚段实现了多版本查询数据

区存在的意义

数据页都是以双向链表的形式存在,如果以页分配存储空间,双向链表相邻的两个页之间的物理位置可能距离非常远,会产生随机 IO,影响性能

段存在的意义

对于范围查询,本质是对B+树叶子节点中的记录进行顺序扫描,但如果不区分叶子节点和非叶子节点,把节点代表的页都申请到区中,范围查找效率大打折扣。一个索引会产生两个段,一个叶子节点段(数据段)和一个非叶子节点段(索引段)。

行格式

  • Redundant
  • Compact,一条完整的记录分为“记录的额外信息”和“记录的真实数据”,额外信息中又分为变长字段长度列表、NULL值列表和记录头数据。变长字段长度列表和NULL值列表都是倒序保存,并且不是必须的,只要没有变长字段或NULL值字段即可。8个字段值可以为NULL,那么NULL值列表空间为1字节,9字段则2字节。
  • Dynamic
  • Compressed

Varchar(n) n最大取多少?

MySQL 规定除了 TEXT、BLOBs 这种大对象类型之外,其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过 65535 个字节。注意,是一行数据的最大字节数 65535,其实是包含「变长字段长度列表」和 「NULL 值列表」所占用的字节数的。

行格式溢出后,数据存放到溢出页

text 各个类型对比

  • TINYTEXT 最大长度是 255 (2^8 – 1) 个字符。
  • TEXT 最大长度是 65535 (2^16 – 1) 个字符。
  • MEDIUMTEXT 最大长度是 16777215 (2^24 – 1) 个字符。
  • LONGTEXT 最大长度是 4294967295 (2^32 – 1) 个字符
  • Varchar 对每个英文(ASCII)字符都占用2个字节,对一个汉字也只占用两个字节
  • Char 对英文(ASCII)字符占用1个字节,对一个汉字占用2个字节

数据的检索效率是:char>varchar>text

char: 定长,会用空格补齐 varchar:变长

B树和B+树

  • B树(多路的平衡搜索树),节点可以存储多个数据,并且每个节点存放索引和数据,一个节点的关键字等于该节点子节点个数减一。查询的最好情况是O(1),一般高度为3。
  • B+树,非叶子节点关键字个数等于子节点个数
    1. 叶子节点保存数据(数据页),非叶子节点保存索引,查询固定为O(logn),并且由于这个特点更适合外部存储。
    2. 叶子节点有指向下一个叶子节点的指针,双向链表,可顺序访问,因此区间访问的性能较好。
    3. 存在重复元素,因为非叶子结点不保存数据。

与红黑树的比较,B+树的优势:

  1. 更少的查询次数,因为B+树的树高一般小
  2. 利用计算机的预读特性,因为B+树具有类似链表的特点,因此相邻的节点也能被预先载入

为什么不使用 B 树:B+ 树的非叶子节点只存储索引,单个节点可以存储更多的索引,计算机一次性加载更多的索引数据到内存中。并且B树不适合范围查找。

为什么不使用跳表:MySQL 一次数据页加载都需要一次磁盘IO。并且磁盘IO的次数和树高有关系,又因为跳表的高度一般比B+树高,所以查询速度会大大降低,因此不适合。

为什么 Redis 使用了跳表:因为 Redis 不存在磁盘IO。重点在于磁盘IO次数。

MySQL索引

给表添加索引时,是会对表加锁,因此在生产环境中不能直接添加索引。InnoDB 存储引擎默认会创建一个主键索引,也就是聚簇索引,其它索引都属于二级索引。当没有显式定义主键索引时,MySQL 会选择第一个唯一索引,并自动设置非空约束,当作聚簇索引。

数据结构维度:

  1. B+树索引,适合范围查询,复杂度为O(logn)。
  2. 哈希索引,能以O(1)时间进行查找,但失去有序性,无法用于排序和分组,只支持精确查找(等值查找)。
  3. 全文索引,使用倒排索引实现,记录关键词到所在文档的映射,一般在text, varchar上创建。
  4. R-Trees索引,MyISAM支持的索引类型,和空间地理数据有关。

物理存储维度(InnoDB):

  1. 聚簇索引,叶节点存放一整行记录。一个表只能拥有一个聚簇索引。并非完全等价于主键索引。
  2. 非聚簇索引(二级索引),叶节点只存放主键信息,所以一般需要回表查询。

逻辑维度

  1. 主键索引,不允许空值
  2. 普通索引,没任何限制
  3. 联合索引,多个字段创建的索引,使用时遵循左前缀原则。
  4. 唯一索引,索引列中的值必须是唯一的,但可以为空值。
  5. 空间索引

索引失效(触发全盘扫描)主要看是否回表或者是查询的数据量过大

一定失效:

  1. 索引列参与运算
  2. 索引列使用函数
  3. 两列做比较
  4. 类型隐式转换

可能失效:

  1. 不满足最左匹配(覆盖索引,索引下推,索引跳跃扫描)
  2. 尽可能明确查询列,而不是select *,即使不满足最左匹配(可以用于性能优化)
  3. 错误的like使用(当模糊匹配的占位符位于条件的首部,并且要看数据库中的字段)
  4. 错误的or使用(切记两个条件都要添加索引,否则会导致索引失效,or两边同时使用 < 和 >,也会失效)
  5. 错误的 <> 和 != 使用(查询结果集占比较大时索引会失效)
  6. is not null(is null 走索引)
  7. not in(条件列是主键时走索引)
  8. not exists
  9. orderby(部分会失效)
  10. 范围过大的 between and

索引跳跃扫描

最左缀原则可以通过跳跃扫描的方式打破,当第一列索引的唯一值较少时,即使where条件没有第一列索引,查询的时候也可以用到联合索引。

覆盖索引

指查询列被所建的索引覆盖,覆盖索引是 select 的数据列只需要从索引中就能取到,不必回表。例如对于联合索引(col1,col2,col3),查询语句SELECT col1,col2,col3 FROM test WHERE col2=2

哪些情况下不适合建索引

  • 数据量少
  • 更新频繁
  • 区分度低
  • where, groupby, orderby 后没有使用到的字段
  • 联合索引中的索引

索引下推

指将部分上层(服务层)负责index filter的事情,交给了下层(引擎层)去处理。它能减少二级索引再查询时的回表查询次数,提高查询效率。只适用于二级索引。

EXPLAIN 命令的extra一栏中有Using index condition,表明使用了索引下推

举例:select * from table1 where b like '3%' and c = 3

5.6 之前:

  • 先通过 联合索引 查询到 开头为 3 的数据 然后拿到主键
  • 然后通过主键去主键索引里面去回表查询 二级索引里面查询出来几个 3 开头的就回表几次

5.6 之后:

  • 先通过 二级索引 查询到开头为 3 的数据 然后 再找到 c = 3 的数据进行过滤 之后拿到主键
  • 通过主键进行回表查询,减少了回表次数

建索引的原则

  1. 选择唯一性索引
  2. 为经常需要orderby,groupby操作的字段建立索引
  3. 经常作为查询条件的字段建立索引
  4. 限制索引数量
  5. 尽量使用数据量少的索引
  6. 尽量使用最左前缀匹配原则
  7. 使用区分度高的列作为索引
  8. 扩展索引而不是新建索引

强制索引

深度分页

有分页需求时,一般会用limit实现,但是当偏移量特别大的时候,查询效率就变得低下。

select * from table limit 10select * from table limit 10000, 10 的查询时间是不一样的,后者是查询出 10010 条,再取最后 10 条记录。

本质原因就是:偏移量(offset)越大,mysql就会扫描越多的行,然后再抛弃掉。这样就导致查询性能的下降。

解决方案:

  1. 标签记录法,使用 select * from table where id > 10000 limit 10
  2. 范围查询,使用 select * from table where id between 100000 and 100010 order by id desc;
  3. 使用子查询,把条件转移到主键索引树,使用 select * from table where id >= (select a.id from table a where a.update_time >= xxx limit 100000, 1) limit 10
  4. INNER JOIN 延迟关联,与上述的子查询思路类似,将条件转移到主键索引树
  5. 尽量满足索引覆盖,效率低下是因为回表次数过多,如果可以满足索引覆盖,则就不需要回表

ACID 原则

  • Atomicity(原子性),每次事务是原子的,事务包含的所有操作要么全部成功,要么全部不执行。一旦有操作失败,则需要回退状态到执行事务之前;通过 undo log 来保证。
  • Consistency(一致性),数据库的状态在事务执行前后的状态是一致的和完整的,无中间状态。即只能处于成功事务提交后的状态;例如转账前后,两用户的余额总和一定是不变的。通过原子性 + 隔离性 + 持久性保证的。
  • Isolation(隔离性),各种事务可以并发执行,但彼此之间互相不影响。按照标准 SQL 规范,从弱到强可以分为读未提交、读已提交、可重复读和串行化四种隔离等级;通过 MVCC 或锁机制保证的。
  • Durability(持久性),状态的改变是持久的,不会失效。一旦某个事务提交,则它造成的状态变更就是永久性的,即便系统故障也不会丢失。通过 redo log 来保证。

事务的隔离

MVCC 用于解决脏读和不可重复读的问题,在读已提交和可重复读两种隔离级别生效。读已提交是每次 select 都会重新生成一次 Read View,而可重复读是一次事务只会创建一次且在第一次查询时创建 Read View。

MVCC: Read view + undo log + 两个隐藏字段,基于乐观锁的理论为了解决读写冲突,可以在读已提交和可重复读的隔离级别下使用。

  • Read View 是一个事务快照,保存当前事务开启时所有活跃的事务列表。通过与版本链的配合可以实现对数据的 “快照读”。其中也有四个字段,分别是creater_trx_id, m_ids, min_trx_id, max_trx_id。
  • undo log 是存放更新前的数据(快照),保存了历史快照
  • 隐藏字段:row_trx_id 和 roll_pointer,前者表示更新行数据的事务id,后者是上一次修改之前保存在 undo log 中的记录位置(指针),使每行记录变化前后形成了一条版本链

MVCC(Multiversion Concurrency Control) + 间隙锁在RR的隔离级别下能解决大部分幻读情况

  • 仅快照读的情况下(普通 select 语句),通过 MVCC 的方式,在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,多次查询获取的是同一个快照数据。
  • 仅当前读的情况下(select … for update 等语句),通过 Next-Lock Key(临键锁),其他事务的插入操作会被阻塞。
    • 查询条件不走索引,退化成表锁
    • 查询条件走普通索引,锁住查询条件附近的间隙,等于间隙锁
    • 查询条件走唯一索引,只锁一条数据,等价于记录锁
  • 解决不了的幻读情况:快照读和当前读发生在同一个事务中(更新、删除等操作本质也需要进行当前读)
    1. 事务A快照读,事务B新增数据并提交,接着事务A修改事务B新增的数据并快照读,此时发生幻读。
    2. 事务A快照读,事务B新增数据并提交,接着事务A当前读,此时发生幻读。

若要尽可能避免幻读现象的发生,尽量在开启时候后马上执行当前读。

三种问题:

  1. 脏读,指读取未提交数据,事务的数据可能回滚,导致数据不一致。
  2. 不可重复读,指在同一事务内读到的数据是不一致的
  3. 幻读,指同一事务在查询的过程中,有另外一个事务对范围内新增了记录,导致范围查询的结果条数不一致的现象。

SQL的四种隔离级别:

  1. 读未提交,指一个事务还没提交时,它做的变更就能被其他事务看到。可能出现脏读
  2. 读已提交,指一个事务提交之后,它做的变更才能被其他事务看到。可能出现不可重复读
  3. 可重复读,指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的。可能出现幻读,但使用MVCC + 间隙锁能解决大部分幻读现象,事务开始读操作的时候,不允许其他事务对读的数据做修改
  4. 串行化,对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

WAL 机制

WAL(Write-ahead logging)指 MySQL 在执行写操作时先记录在日志中,后更新到磁盘中。

WAL 核心:将随机写(磁盘的写操作是随机IO,耗性能)转变成顺序写和组提交机制,降低客户端延迟,提高吞吐量。

三大日志binlog, redolog, undolog:

  1. Buffer Pool是MySQL进程管理的一块内存空间,有减少磁盘IO次数的作用。
  2. redo log重做日志是InnoDB存储引擎的一种物理格式的日志,用来实现事务持久性,主要有两部分文件组成,在内存中的重做日志缓冲(redo log buffer)以及磁盘中的重做日志文件(redo log),(循环写,数据会被覆盖)。使用场景:崩溃恢复,在发生故障的时间点,尚有脏页未写入磁盘,在重启 mysql 服务的时候,根据 redo log 进行重做,从而达到事务的持久性这一特性。
  3. undo log回滚日志是InnoDB存储引擎的一种逻辑格式的日志,记录的是数据的逻辑变化,保证的是数据库的原子性,比如一条insert语句对应的是一条delete的undo log,在发生事务错误时,就能回滚到事务之前的数据状态;MVCC,事务未提交前,undo log 保存了未提交的版本数据,作为旧版本的快照数据,类似于做备份。
  4. binlog是MySQL Server层的一种逻辑格式的日志,用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。使用场景:主从复制 :采用异步复制方式,在 Master 端开启 binlog ,然后将 binlog 发送到各个 Slave 端, Slave 端重放 binlog 从而达到主从数据一致;数据恢复,通过使用 mysqlbinlog 工具再结合 binlog 文件,可以将数据恢复到过去的某一时刻。

主从复制

如果对数据一致性和可靠性要求较高,可以考虑使用半同步复制;如果对延迟和主服务器性能要求较高,可以继续使用异步复制,根据实际需求调整复制模式。

异步复制
  1. master服务器将数据的改变记录二进制binlog日志,当master上的数据发生改变时,则将其改变写入二进制日志中;
  2. slave服务器会在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/OThread请求master二进制事件
  3. 主节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中,
  4. 从节点将启动SQL线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致,最后I/OThread和SQLThread将进入睡眠状态,等待下一次被唤醒。
半同步复制

主服务器将数据修改操作记录到二进制日志,并等待至少一个slave服务器确认已接收到并应用了这些日志后才继续执行后续操作。

Binlog

Binlog 有三种格式:

  1. Row,记录操作语句对具体行的操作和操作前的整行信息。缺点是占用空间大,优点是保证数据安全
  2. Statement,记录修改的 sql 语句。缺点是在mysql集群时的一些操作会导致数据不一致(例如 Now() 的时间不同)。
  3. Mixed

Binlog 写入到磁盘的操作:

  1. write:从binglog cache写到 page cache。
  2. fsync:将数据持久化到磁盘。

Binlog 的持久化:

  1. sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
  2. sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;
  3. sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。

Redolog

刷盘时机:事务进行中

  1. 当innodb_flush_log_at_trx_commit=0时,InnoDB会每秒钟将log buffer中的数据更新到磁盘中。但并不能完全保证每秒将数据更新到磁盘一次。因此,在实例崩溃恢复场景中,可能会出现丢失1秒钟的事务。
  2. 当innodb_flush_log_at_trx_commit=1时,InnoDB将在每次事务提交时将log buffer的数据更新到文件系统os buffer中,并调用文件系统的flush操作将数据缓存更新至磁盘中。此种方式下,数据库完全遵守ACID特性,安全性较高。
  3. 当innodb_flush_log_at_trx_commit=2时,InnoDB将在每次事务提交时将log buffer中的数据更新到文件系统缓存中,每秒钟将文件系统缓存中的数据更新到磁盘一次,该操作由操作系统调度。不能完全保证每秒更新磁盘一次,没有被更新到磁盘中的事务可能会因宕机而丢失。

Crash-Safe 能力,两阶段提交

前提:innodb_flush_log_at_trx_commit 设置为1,sync_binlog 设置大于0

  1. 先将数据修改写入redo log,并将其标记为 prepare 状态
  2. 将相应的 sql 语句写入 binlog
  3. commit阶段:将 redo log 标记为 commit 状态,然后根据 sync_binlog 参数的设置,决定是否将 binlog 刷盘

如果发生崩溃,先检查 redo log 记录的事务操作是否为 commit 状态。

  1. 如果是 commit 状态说明没有数据丢失,判断下一个。
  2. 如果是 prepare 状态,检查 binlog 记录的对应事务操作(redo log 与 binlog 记录的事务操作有一个共同字段 XID,redo log 就是通过这个字段找到 binlog 中对应的事务的)是否完整(这点在前面 binlog 三种格式分析过,每种格式记录的事务结尾都有特定的标识),如果完整就将 redo log 设为 commit 状态,然后结束;不完整就回滚 redo log 的事务,结束。

为什么需要两阶段提交?

  • 以基本的事务为单位,redo log 在事务执行过程中可以不断写入,而 binlog 只有在提交事务时才写入,所以redo log 与 binlog 的写入时机不一样。为了解决两份日志之间的逻辑一致问题,将redo log的写入拆成了两个步骤 prepare 和 commit,即两阶段提交。如果只有 redo log 或者只有 binlog,那么事务就不需要两阶段提交。

执行顺序

  1. from
  2. on
  3. join
  4. where
  5. group by
  6. having
  7. select
  8. distinct
  9. order by
  10. limit

每一步执行时都会产生一个虚拟表,都会被用作下一个步骤的输入。

乐观锁、悲观锁

  • 乐观锁:不能解决脏读问题
  • 悲观锁:select … for update

全局锁、表级锁、行锁

  • 全局锁:对整个数据库实例加锁,处于只读状态,其命令是Flush tables with read lock(FTWRL),用于全库逻辑备份。
  • 表级锁:开销小,加锁快,不会出现死锁,发生锁冲突的概率最高,并发度最低。适合以查询为主。
    • 表锁,可以添加表级别的共享锁或排他锁,锁的粒度较粗。
    • 元数据锁(MDL),CRUD时,数据库自动给表加上MDL读锁;变更表结构时,自动加MDL写锁。事务提交后才会释放MDL锁。
    • 意向锁,在执行插入、更新、删除需要加独占锁之前,需要对表先加上意向独占锁,其目的是快速判断表里是否有记录被加锁
    • AUTO-INC锁,自增锁,插入数据时获取该锁,插入语句完成后,释放锁。
  • 行锁:开销大,加锁慢,会出现死锁,发生锁冲突的概率最低,并发读也高。只有InnoDB引擎支持。行锁依赖于索引,如果加锁操作没有使用到索引,那么会退化成表锁。
    • 记录锁:锁定一条记录,存在 S 型记录锁和 X 型记录锁。加了 S 型记录锁后可以继续加 S 型记录锁,但不能加 X 型记录锁;加了 X 型记录锁后不能加 S 型或 X 型记录锁。存在于包含主键索引的唯一索引中。
    • 间隙锁:锁定一个范围,前开后开,不包含记录本身,存在于可重复读和串行化这两种隔离级别,为了解决可重复读隔离级别下幻读的现象。插入意向锁是一种表明插入意向的间隙锁。
    • 临键锁(next-key lock):加锁的基本单位,锁定一个范围,前开后闭,存在于非唯一索引中。

共享锁(读锁)、排他锁(写锁)

  • 共享锁:读锁,其他事务可以读,但不能写,select lock in share mode
  • 排他锁:写锁,其他事务不能读,也不能写,select for update

mysql 数据实时同步到Es

常见的数据同步方案主要有以下三种:

  1. 同步调用。在实现增删改的同时,通过调用ES所在服务提供的接口。
  2. 异步调用。增删改服务和搜索服务分别通过MQ进行发送和监听消息,有效降低业务的耦合度,但较为依赖MQ的性能;
  3. binlog监听。给MySQL开启binlog功能,搜索服务基于canal监听binlog变化,但开启binlog会增加数据库负担、同时实现复杂度较高。

但也有异步调用和binlog监听结合在一起的例子,用canal作slave节点。

分库/分表

分为 水平切分(又称为 Sharding)垂直切分

切分策略

  • 范围切分,例如每一千万条数据一张表
  • 哈希切分,对分表键进行哈希运算,主流方法
  • 映射表,将分表键和数据库表对映射关系记录在表上

分布式ID

建议使用雪花算法,其中序列号一直保持递增,不会因为时间戳的不同而归零,防止被猜测上下文ID。

注入攻击

通过客户端向应用程序输入数据来插入或注入一个 SQL 查询/操作。

防止注入攻击

  1. 参数化查询
  2. 使用正则化验证输入
  3. 最小权限原则
  4. 字符串过滤关键字

MyBatis #$ 的区别

  • # 将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。很大程度防止sql注入
  • $ 将传入的数据直接显示生成在sql中。无法防止Sql注入。
This post is licensed under CC BY 4.0 by the author.

Basics of Java

线段树(Java实现)

diff --git a/posts/new-and-malloc/index.html b/posts/new-and-malloc/index.html new file mode 100644 index 000000000..189451d95 --- /dev/null +++ b/posts/new-and-malloc/index.html @@ -0,0 +1,145 @@ + Difference between NEW and MALLOC in C++ | Blogs
Home Difference between NEW and MALLOC in C++
Post
Cancel

Difference between NEW and MALLOC in C++

Difference

1. 属性

  • new/delete是C++关键字,需要编译器支持。

  • malloc/free是库函数,需要头文件支持。

2. 参数

  • new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。

  • 而malloc则需要显式地指出所需内存的尺寸。

3. 返回类型

  • new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。

  • 而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

4. 分配失败

  • new内存分配失败时,会抛出bac_alloc异常。

  • malloc分配内存失败时返回NULL。

5. 自定义类型

  • new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。

  • malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

6. 重载

  • C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。

  • 而malloc不允许重载。

7. 内存区域

  • new操作符从自由存储区(free store)上为对象动态分配内存空间。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。

  • 而malloc函数从堆上动态分配内存。堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。

8. 重新分配内存

  • new没有扩张内存的机制。

  • 而malloc分配内存后,如果发现内存不够用,可以通过realloc函数来扩张内存大小,realloc会先判断当前申请的内存后面是否还有足够的内存空间进行扩张,如果有足够的空间,那么就会往后面继续申请空间,并返回原来的地址指针;否则realloc会在另外有足够大小的内存申请一块空间,并将当前内存空间里的内容拷贝到新的内存空间里,最后返回新的地址指针。

Code

以下是 vector, vector with reserve, array, malloc, new 这几种不同创建并销毁数据的方法的运行时间对比代码。

根据运行结果可以大致得到:$T_{array} \approx T_{malloc} \approx T_{new} < T_{vector-with-reserve} < T_{vector}$

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
+
#include<bits/stdc++.h>
+#include <windows.h>
+using namespace std;
+
+int main(){
+	cout<<"=====Runing time of program=====" <<endl;
+	// vector	
+	DWORD start = GetTickCount();
+	int t = 100;
+	int n = 200000;
+	while (t)
+	{
+		vector<int> a;
+		for(int i=0;i<n;i++) a.push_back(i);
+		t--;
+	}
+	cout<<"Vector: "<<GetTickCount() - start<<endl;
+
+	// vector with reserve
+	start = GetTickCount();
+	t = 100;
+	n = 200000;
+	while (t)
+	{
+		vector<int> b;
+		b.reserve(n);
+		for(int i=0;i<n;i++) b.push_back(i);
+		t--;
+	}
+	cout<<"Vector with reserve: "<<GetTickCount() - start<<endl;
+
+	// array
+	start = GetTickCount();
+	t = 100;
+	n = 200000;
+	while (t)
+	{
+		int a[200000];
+		for(int i=0;i<n;i++) a[i]=i;
+		t--;
+	}
+	cout<<"Array: "<<GetTickCount() - start<<endl;
+
+
+	// malloc
+	start = GetTickCount();
+	t = 100;
+	n = 200000;
+	while (t)
+	{
+		int *p = (int *)malloc((n+1) * sizeof(int));
+		for(int i=0;i<n;i++) p[i]=i;
+		free(p);
+		t--;
+	}
+	cout<<"Malloc: "<<GetTickCount()-start<<endl;
+	
+	
+	// new
+	start = GetTickCount();
+	t = 100;
+	n = 200000;
+	while (t)
+	{
+		int *p=new int[n+1];
+		for(int i=0;i<n;i++) p[i]=i;
+		delete []p;
+		t--;
+	}
+	cout<<"New: "<<GetTickCount() - start<<endl;
+	
+}
+

Conclusion

Reference

  1. new与malloc的10点区别
  2. C++ 自由存储区是否等价于堆?
This post is licensed under CC BY 4.0 by the author.

C++中静态成员及静态成员函数详解

MathJax 数学公式语法

diff --git a/posts/note-from-work/index.html b/posts/note-from-work/index.html new file mode 100644 index 000000000..0c416612d --- /dev/null +++ b/posts/note-from-work/index.html @@ -0,0 +1,61 @@ + Note from Work | Blogs
Home Note from Work
Post
Cancel

Note from Work

  1. Grafana 的数据显示会五分钟自动补全。当向 Prometheus 中插入某个时间戳的值时,其值会延续五分钟。

  2. K8S 中的 Sidecar 模式:通常情况下一个 Pod 只包含一个容器,但是 Sidecar 模式是指为主容器提供额外功能(例如监控) 从而将其他容器加入到同一个 Pod 中。再例如 Istio 实现 Sidecar 自动注入

  3. Federated cluster,联邦集群

  4. 限流算法:漏桶和令牌桶算法,漏桶算法处理请求的速度固定,突发请求过多时会丢弃;令牌桶算法除了限制数据的平均传输速率外,还要求允许某种程度的突发传输。

  5. 常见 HTTP 状态码:2XX,成功响应;3XX,重定向消息;4XX,客户端错误响应;5XX,服务端错误响应。

  6. 请求分为四部分:请求行,请求头,空行,请求正文(不一定有)。

  7. .gitignore 是在文件从工作区被 add 到暂存区时判断是否要被忽略,对于已经在暂存区的文件,不作判断。

数据结构

红黑树特性

  1. 节点非红即黑
  2. 根节点是黑色
  3. 叶子节点是黑色的空节点
  4. 红节点的子节点是黑节点
  5. 从根节点到空节点的每条路径,包含相同数量的黑色节点

常见NoSQL数据库类型

  1. 键值数据库:Redis
  2. 列式数据库:HBase, ClickHouse,适用于联机分析处理(OLAP)场景,数仓等
  3. 文档数据库:MongoDB, ElasticSearch
  4. 图数据库

Clickhouse:

  1. 不支持事务:因为面向列。不存在隔离级别。ClickHouse的定位是分析性数据库,而不是严格的关系型数据库
  2. 不支持高并发
  3. 可处理大量读请求,数据压缩的特性

分布式理论

两阶段协议

https://juejin.cn/post/6844903621495095309

三阶段协议

https://juejin.cn/post/6844903621495111688

FLP 不可能性原理

在网络可靠,存在节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性算法。对于允许节点失效情况下,纯粹异步系统无法确保一致性在有限时间内完成。

举例:三个人在不同房间,进行投票(投票结果是 0 或者 1)。三个人彼此可以通过电话进行沟通,但经常会有人时不时地睡着。比如某个时候,A 投票 0,B 投票 1,C 收到了两人的投票,然后 C 睡着了。A 和 B 则永远无法在有限时间内获知最终的结果。

CAP 定理

分布式系统只能交付以下三个所需特性中的两个特性:一致性、可用性和分区容错性。

  • C(Consistency):一致性,指的是所有客户端可以同时看到相同的数据。每当数据写入一个节点时,就必须立即将其转发或复制到系统中的所有其他节点
  • A(Availability):可用性,指的是可用性表示发出数据请求的任何客户端都会得到响应,即使一个或多个节点宕机。
  • P(Partition tolerance):分区容错性,指分区之间的通信可能失败。

一般来说,在分布式系统中不可避免会出现分区,因此认为 P 总是成立。因此若要实现一致性(CP),那么当出现分区问题时,就需要舍弃不一致的节点,即牺牲可用性。同理,若要实现可用性(AP),那么当出现分区问题时,就无法保证一致性。

Nacos集群默认支持 AP 原则(即不支持数据一致性,支持服务注册的临时实例),但也可切换至基于 Raft的 CP 原则(支持服务注册的永久实例)。

临时实例和持久化实例的区别:

  • 持久化实例健康检查后会被标记为不健康,而临时实例会直接从列表中删除。

MongoDB 是一种 CP 数据存储——它通过牺牲可用性、保持一致性来解决网络分区问题。

BASE 理论

  • Basically Available(基本可用),假设系统出现了不可预知的故障,但还是能用。
  • Soft State(软状态),允许系统在多个不同节点的数据副本存在数据延时。
  • Eventually Consistent(最终一致性),在软状态的一定期限过后,应达到数据的最终一致性。

核心思想是:即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

Raft 算法

可视化链接

三种角色:

  1. Leader,发送心跳包以维护自己的Leader状态
  2. Follower,响应Leader发送的心跳包
  3. Candidate,如果没有接收到Leader的心跳信号后,Follower会变成Candidate并向其他节点发送拉票信息

记时器:

  1. 选举超时时间(Follower拥有):若超时前没有收到心跳包,认为Leader下线,此时Follower会变成Candidate;但反之如果接收到心跳包,则重置计时器。
  2. 投票超时时间(Candidate拥有):若超时前没有得到半数以上的票数,则竞选失败;反之成功。
  3. 竞选等待超时时间(竞选失败的Follower拥有):竞选失败的Follower需要等待一段时间后才能重新竞选。

脑裂

由于某种原因(网络不稳定)使得主从集群一分为二,导致master或者leader节点的数量不为1(0或2)。

  • redis脑裂

master 机器突然脱离网络,使得 sentinel 集群无法感知到 master 的存在,会重新选举一个 master 节点。网络恢复后则会存在两个 master 节点。

  • zookeeper脑裂

脑裂可能出现平票的情况,从而无法选举出 leader。因此,zk 中建议节点数是奇数。

K8s

Kubernetes主要由以下几个核心组件组成:

  • etcd保存了整个集群的状态;
  • apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
  • controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
  • scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
  • kubelet负责维持容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
  • Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
  • kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;

除了核心组件,还有一些推荐的add-ons(扩展):

  • kube-dns负责为整个集群提供DNS服务
  • Ingress Controller为服务提供外网入口
  • Heapster提供资源监控
  • Dashboard提供GUI
  • Federation提供跨可用区的集群
  • Fluentd-elasticsearch提供集群日志采集、存储与查询

双重校验锁实现单例模式

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 class Singleton {
+
+    private static volatile Singleton singleton = null; // volatile 必不可少,防止jvm指令重排优化
+
+    private Singleton() {
+    }
+
+    public static Singleton getInstance(){
+        //第一次校验singleton是否为空,为了提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例即可。
+        if(singleton==null){
+            synchronized (Singleton.class){
+                //第二次校验singleton是否为空,防止二次创建实例
+                if(singleton==null){
+                    singleton = new Singleton();
+                }
+            }
+        }
+        return singleton;
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 100; i++) {
+            new Thread(new Runnable() {
+                public void run() {
+                    System.out.println(Thread.currentThread().getName()+" : "+Singleton.getInstance().hashCode());
+                }
+            }).start();
+        }
+    }
+}
+

23种设计模式与六大原则

23种设计模式

  • 创建型
    1. 单例模式(Singleton)
      • 饿汉模式:类加载的时候就创建实例,整个程序周期都存在
      • 懒汉模式:只有当第一次使用的时候才创建实例,线程不安全
      • 双重校验锁:
      • 静态内部类:
    2. 原型模式(Prototype),能够复制已有对象,而又无需使代码依赖它们所属的类。例如 Cloneable 接口是立即可用的原型模式。
    3. 工厂方法模式(Factory Method),在父类中提供一个创建对象的方法, 允许子类决定实例化对象
    4. 抽象工厂模式(Abstract Factory),定义了用于创建不同产品的接口, 但将实际的创建工作留给了具体工厂类。 每个工厂类型都对应一个特定的产品变体。
    5. 建造者模式(Builder),分步骤创建复杂对象。
  • 行为型
    1. 模板方法模式(Template Method),它在基类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。
    2. 责任链模式(Chain of Responsibility),它让多个处理器(对象节点)按顺序处理该请求,直到其中某个处理成功为止,例如检查商品模块,需要先检查商品合法性,再检查商品可见性等等。
    3. 命令模式(Command)
    4. 迭代器模式(Iterator),能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。
    5. 中介者模式(Mediator)
    6. 备忘录模式(Memeoto)
    7. 观察者模式(Observer)
    8. 状态模式(State)
    9. 策略模式(Strategy),规定每个策略的不同方法,只需选择不同的策略即可执行不同的方法,例如数字人项目中 Provider 的选择(1:AiLab,2:Creatify,3:IC,4:IC-NEW)
    10. 访问者模式(Visitor)
    11. 解释器模式(Interpreter)
  • 结构型
    1. 适配器模式(Adaptor)
    2. 桥接模式(Bridge)
    3. 组合模式(Composite)
    4. 装饰模式(Decorator)
    5. 外观模式(Facade),为复杂系统、程序库或框架提供一个简单的接口。通常作用于整个对象子系统上。
    6. 享元模式(Flyweight),共享多个对象的部分状态将内存消耗最小化。
    7. 代理模式(Proxy)

六大原则

  1. 开闭原则,对扩展开放,对修改关闭。
  2. 单一职责原则,顾名思义,一个类的职责只有有一个。
  3. 里氏替换原则,子类可以扩展父类的功能,但不能改变父类原有的功能。
  4. 依赖倒转原则,依赖于抽象,不能依赖于具体实现(面向接口编程)。
  5. 接口隔离原则,类之间的依赖关系应该建立在最小的接口上。
  6. 迪米特原则,一个软件实体应当尽可能少的与其他实体发生相互作用。

分布式事务 Seata

分两阶段提交

  1. TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。
  2. TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  3. RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

执行流程:

  1. TM 向 TC 请求发起(Begin)、提交(Commit)、回滚(Rollback)等全局事务。
  2. TM 把代表全局事务的XID绑定到分支事务上。
  3. RM 向 TC 注册,把分支事务关联到XID代表的全局事务中。
  4. RM 把分支事务的执行结果上报给 TC。
  5. TC 发送分支提交(Branch Commit)或分支回滚(Branch Rollback)命令给RM。

四大模式

  1. AT模式,能适用于大部分事务情况。
  2. XA模式
  3. SAGA模式,核心思想是将长事务拆分成多个本地短事务。
  4. TCC模式,核心思想是针对每个操作都要注册一个与其对应的确认和补偿操作。

其他分布式事务解决方案:

  1. 基于 RocketMQ 的消息事务
  2. 二阶段提交
  3. 三阶段提交,CanCommit、PreCommit、DoCommit三阶段

ZooKeeper(CP)

使用 ZAB 协议,包括两种运行模式:

  1. 消息广播(Leader 正常运行),所有事务请求由单一主进程处理,Leader 转换为事务 Proposal 并广播分发给 Follower,Leader 等待 Follower 的反馈,超过半数的 Follower 反馈消息后,Leader 再发送 Commit 消息提交事务 Proposal。

  2. 崩溃恢复(Leader 不可用时),新选举产生的 Leader 与过半的 Follower 进行同步,使数据一致,同步后进入消息广播模式。

主要服务于分布式系统,可以用ZooKeeper来做:统一配置管理、统一命名服务、分布式锁、集群管理,用来解决分布式集群中应用系统的一致性问题,构建 ZooKeeper 集群时使用的服务器最好是奇数台。其设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。

数据结构:Znode节点,与Unix文件系统类似,通过路径来标识,例如 /home/app,Znode节点又分为两种类型:临时节点、持久节点、临时顺序节点、持久顺序节点。客户端和服务端断开连接后,临时节点会自动删除但持久节点不会。分布式锁使用的是临时节点

Watcher 监听器:节点的数据发生变化后会通知到节点的监听器。

基本命令:

  • create : 在树中的某个位置创建一个节点
  • delete : 删除一个节点存在:测试节点是否存在于某个位置
  • get data : 从节点读取数据
  • set data: 将数据写入节点
  • get children : 检索节点的子节点列表
  • sync : 等待数据被传播

实现分布式锁原理:判断能否创建临时节点,如果不能则监听父节点(非公平锁)或上一个节点(公平锁),任务执行完成后释放该节点并通知所有监听的节点。

实现注册中心原理:初始化时 Provider 先向目录写入 URL 地址,Consumer 订阅相同目录的 URL 地址和自己的 URL 地址,监控中心订阅 Provider 和 Consumer 的 URL 地址;Consumer 在第一次调用服务时,会通过注册中心找到相应的服务的IP地址列表,并缓存到本地,以供后续使用;当 Provider 下线时,会在列表中移除 URL 并将新的 URL 地址发送给 Consumer 并缓存至本地,服务上线也是一样的。

与 Nacos 的区别:

Nacos 是 AP 的,保证可用性,每个节点都是平等的,若干个节点 crash 后不会影响正常节点,但查到的信息不一定是最新的。ZK 在 crash 后进行 leader 选举,期间是不可用的。

微服务框架:Dubbo

底层基于 Netty 的 NIO 框架,基于 TCP 协议传输

四种负载均衡策略

支持服务端服务级别、服务端方法级别、客户端服务级别、客户端方法级别的负载均衡配置。还可以拓展负载均衡算法。

  1. 随机负载均衡,默认策略
  2. 轮询负载均衡
  3. 最少活跃调用数,每个 Provider 都有一个计数器,开始调用则计数器加一,结束调用计数器减一。原则是将请求分配给处理速度快的 Provider。
  4. 一致性哈希负载均衡

HTTPS 如何保证可靠传输

TLS 协议:

  1. 对称加密/非对称加密
  2. 数字证书
  3. 三次握手/四次挥手

TLS 的 rsa 握手过程,存在什么安全隐患,怎么解决的?

  • 安全隐患:不支持前向保密。如果私钥泄露了,所有密文都会被破解
  • 解决方案:密钥协商算法

HTTPS 绝对安全吗

SSL 加密是绝对安全的,但是 HTTPS 并不是绝对安全的,可以通过中间人攻击的方式,即所有请求先发送给第三方(中间人),返回中间人的证书,后续的请求/响应都通过中间人进行转发。

HTTP版本的演变

HTTP/1.0

  • 无状态:服务器不追踪不记录请求过的状态
  • 无连接:每次请求要建立连接,无法复用连接;在前一个请求响应到达之后下一个请求才能发送,如果前一个阻塞,后面的请求也被阻塞

HTTP/1.1

默认浏览器对同一域名的并发请求限制为 6 - 8 个。

  • 长连接:默认保持长连接,数据传输完成后只要不断开连接,就可以继续传输数据
  • 管道化:基于上面长连接的基础,可以不等第一个请求响应继续发送后面的请求,但响应的顺序还是按照请求的顺序返回
  • 缓存处理:新增字段cache-control
  • 断点传输:在上传/下载资源时,如果资源过大,将其分割为多个部分,分别上传/下载

HTTP/2

  • 二进制分帧传输:更方便头部,只传输差异部分
  • 多路复用:同一服务下只需要用一个连接,节省了连接
  • 头部压缩:合并同时发出请求的相同部分
  • 服务器推送:一次客户端请求服务端可以多次响应

HTTP/3

基于谷歌的QUIC,底层使用udp代码tcp协议,解决了队头阻塞问题,同样无需握手,性能大大地提升,默认使用tls加密。

进程和线程

区别:

  1. 进程是系统资源分配的最小单位,实现了操作系统的并发;线程是CPU调度的最小单位,实现了进程内部的并发。
  2. 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
  3. 进程切换的开销远大于线程切换的开销。
  4. 进程之间不会相互影响,但线程挂掉会影响整个进程。

进程间通信方式:

  1. 管道
  2. 信号量
  3. 消息队列
  4. 共享内存
  5. 套接字,也可以用于不同主机之间进程的通信

线程间通信方式:

  1. 共享内存,比如 volatile 保证内存的可见性。
  2. 消息传递,比如 wait/notify/notifyAll 等待通知方式和 join 方式。
  3. 管道流

用户态和内核态

为什么要有这两个状态?保护机制,防止用户误操作或恶意破坏系统

用户态

  1. 是用户进程/线程所在的区域,主要用于执行用户程序
  2. 运行的代码会受到CPU的很多检查,不能直接访问内核数据
  3. 只拥有受限的权限
  4. 只能响应部分中断请求
  5. 只能访问受限的地址空间

内核态

  1. 是内核进程/线程所在的区域,主要用于执行操作系统程序,硬件交互
  2. 运行的代码不受任何限制,可以执行任意指令
  3. 拥有最高权限
  4. 可以响应所有中断请求
  5. 可以访问所有内存空间

用户态切换到内核态

  1. 系统调用(主动)。操作系统在执行用户程序的时候主要工作在用户态,当执行没有权限的任务时,才切换到内核态。
  2. 异常(被动)。执行用户程序出现异常时,会从用户态切换到内核态处理异常
  3. 外围设备中断(被动)。中断发生时,如果中断之前在运行用户态的程序,那么会切换至内核态处理中断。

服务注册与发现

如果自己实现服务注册与发现,需要考虑以下三点:

  1. Register, 服务启动时候进行注册
  2. Query, 查询已注册服务信息
  3. Healthy Check, 确认服务状态是否健康

实现方案:

  1. Eureka,AP
  2. ZooKeeper,CP,Paxos 算法
  3. Consul,CP,Raft 算法
  4. Etcd,CP,Raft 算法

Paxos 和 Raft 算法都属于一致性算法,所以是保证 CP

实际上,作为一个注册中心来说,保证 AP 更加重要,即可用性。

两种模式

  1. 客户端发现模式,首先要进行的是到服务注册中心获取服务列表,然后再根据调用端本地的负载均衡策略,进行服务调用。
  2. 服务端发现模式,调用方直接向服务注册中心进行请求,服务注册中心再通过自身负载均衡策略,对微服务进行调用。这个模式下,调用方不需要在自身节点维护服务发现逻辑以及服务注册信息。

常见的负载均衡

  1. 轮询,按照请求的顺序轮流分配到不同的服务器,循环往复。
  2. 加权轮询,给不同的服务器分配不同的权重,根据权重比例来决定分配请求的数量。
  3. 最小连接数
  4. 最短响应时间
  5. IP哈希

RPC/HTTP

与 HTTP 的对比,RPC 使用二进制传输,传输效率高(HTTP额外空间开销大,包含大量元数据,头字段等),但通用性不如 HTTP 协议

核心

  1. 消息协议:以何种方式打包编码和拆包解码
  2. 传输控制:主要有HTTP传输和TCP传输,鉴于TCP传输的可靠性,RPC的传输一般使用TCP作为传输协议

工作流程

  1. 客户端(Client)以本地方法调用服务
  2. 客户端存根(Client Stub)收到调用后把方法、参数等内容打包成特定格式、能进行网络传输的消息体(Marshalling)
  3. 客户端存根找到服务地址,发送给服务端(这个过程可以基于TCP也可以基于HTTP)
  4. 服务端存根(Server Stub)收到消息后拆包解码(Unmarshalling)
  5. 服务端存根根据方法名和参数进行本地调用服务
  6. 服务端(Server)本地执行后把结果返回给服务端存根
  7. 服务端存根把结果打包发送给客户端存根
  8. 客户端存根接收到消息进行解码
  9. 客户端得到最终结果

实现方式

  1. 基于 http
  2. 基于 tcp(常见)

计算机网络

TCP/UDP 可以使用同一个端口吗

可以。传输层有两个传输协议分别是 TCP 和 UDP,在内核中是两个完全独立的软件模块。可以在 IP 包头的协议号字段判断出 TCP 还是 UDP

TCP 三次握手

为什么三次握手

因为三次握手才能保证双方具有接收和发送的能力,并且防止重复建立历史连接。

为什么四次挥手

本质原因是 TCP 是全双工通信,客户端确认没有数据发送后,发出结束报文,此时服务端返回确认后,服务端也不会接收客户端数据。但是此时服务端可能还有数据没有传输完,客户端还是可以接收数据。

如果挥手过程中「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手

DNS使用TCP还是UDP

DNS 在进行区域传输的时候使用 TCP,其他情况使用 UDP。

区域传输:是指DNS主从服务器之间的数据同步,保证数据的一致性,传送会利用DNS域,所以就称为DNS区域传送。

TCP 保证可靠传输

  1. 序列号
  2. 连接建立:三次握手四次挥手
  3. 头部检验和
  4. 确认应答
  5. 超时重传
  6. 拥塞控制:四种算法
    1. 慢启动
    2. 拥塞避免
    3. 拥塞发生
    4. 快速恢复
  7. 流量控制:滑动窗口实现

OSI 七层模型和 TCP/IP 四层模型

  1. 应用层:HTTP,DNS,FTP;网关
    1. 应用层
    2. 表示层
    3. 会话层
  2. 传输层:TCP,UDP;网关
  3. 网络层:IP(在TCP/IP模型中,ARP属于网络层);路由器
  4. 网络接口层
    1. 数据链路层:ARP;网桥,交换机
    2. 物理层:网卡;中继器,集线器

架构

在高并发环境下,服务之间的依赖关系导致调用失败,解决的方式通常是: 限流->熔断->隔离->降级, 其目的是防止雪崩效应。

死锁

四个条件:

  1. 互斥
  2. 请求和保持
  3. 不可剥夺
  4. 循环等待

一致性哈希

一致性哈希是指将「存储节点」和「数据」都映射到一个首尾相连的哈希环(固定大小)上。数据存储在哈希值通过顺时针找到的第一个节点。

优势

一致性哈希算法是对 2^32 取模,是一个固定的值;而普通哈希表的长度是由节点数决定的。

  • 普通哈希函数,如果节点数量发生了变化(对系统进行扩容缩容的时候),大部分改变了映射关系,因此需要迁移大量的数据。
  • 一致性哈希算法,如果节点数量发生了变化,只影响该节点顺时针相邻的后继节点。

虚拟节点的引入

一致性哈希算法并不保证节点能够在哈希环上分布均匀,因此引入均匀分布的虚拟节点,建立真实节点和虚拟节点的映射关系。

IO

  1. 同步
    1. 同步阻塞IO
    2. 同步非阻塞IO
    3. IO多路复用
    4. 信号驱动IO
  2. 异步
    1. 异步IO

虚拟内存

虚拟内存是一种计算机技术,它允许系统将一部分硬盘空间当作RAM(随机存取存储器)使用。当物理内存不足以支持正在运行的应用程序时,系统会将不常用的数据移动至磁盘中。虚拟内存为每个进程提供了一个一致的、私有的地址空间

32位/64位操作系统的区别

32位/64位表示CPU可以处理最大位数,一次性的运算量不一样,寻址能力也不同。

域名解析流程

  1. 检查缓存
    1. 浏览器缓存
    2. 操作系统缓存
    3. 本地 hosts 文件
  2. 使用递归查询向本地域名服务器查询
  3. 使用迭代查询向跟服务器查询
    1. 根域名服务器(.)
    2. 顶级域名服务器(.com)
    3. 二级域名服务器(google.com)

排查 CPU 占用过高和内存溢出的问题

Java

CPU 占用排查:使用 toptop -Hp xxx 命令定位占用率最高的进程和该进程的线程 内存占用排查:jstackjmap 打印出堆栈信息, jstat 查看垃圾回收的情况

Go

使用 pprof 工具,可以查看 CPU 占用、排查内存泄漏、协程泄漏等。

This post is licensed under CC BY 4.0 by the author.

Kafka vs RocketMQ

Go 语言学习

diff --git a/posts/nuc11-wakeup/index.html b/posts/nuc11-wakeup/index.html new file mode 100644 index 000000000..2e5585653 --- /dev/null +++ b/posts/nuc11-wakeup/index.html @@ -0,0 +1 @@ + 【解决方案】NUC11锁屏后无法唤醒 | Blogs
Home 【解决方案】NUC11锁屏后无法唤醒
Post
Cancel

【解决方案】NUC11锁屏后无法唤醒

设备配置

  • 型号:NUC11PAHi5

  • 硬盘:Samsung 970Pro 1TB

  • 内存:Samsung 3200MHz 8GB * 2

  • 操作系统:Windows10 Pro

问题描述:

锁屏黑屏后有一定几率无法唤醒,右侧蓝色的电源灯常亮(非呼吸灯),左侧橙色的硬盘灯不亮(正常情况下会闪),只能通过长按电源键十秒左右才能关机。

解决方案:

显卡驱动回滚至27版本。

27版本的驱动

This post is licensed under CC BY 4.0 by the author.

Windows任务栏时间显示秒

Ubuntu中关闭摄像头的自动曝光

diff --git a/posts/redis/index.html b/posts/redis/index.html new file mode 100644 index 000000000..fd2a508bf --- /dev/null +++ b/posts/redis/index.html @@ -0,0 +1,17 @@ + Redis 知识体系 | Blogs
Home Redis 知识体系
Post
Cancel

Redis 知识体系

单机 QPS

单机 QPS 能力参考范围为 8 - 10 万。

为什么 Redis 这么快

  1. 用 C 语言编写的,执行效率高
  2. 基于内存的数据库,避免磁盘IO操作
  3. 采用高效的数据结构
  4. 合理的数据编码,同样的数据结构在不同数据量的情况下采用不同的编码方式
  5. 采用单线程,避免上下文切换
  6. 多路IO复用,一个线程处理多个大量Socket请求。
  7. 虚拟内存

虚拟内存

Redis 的数据并不是完全在内存中,当 Redis 的数据量大于物理内存容量时,会通过虚拟内存的手段,将不经常使用的数据(冷数据)存储到硬盘上,以释放物理内存空间。

在虚拟内存中,数据被分为多个页面,每个页面大小默认 32 字节。显然,虚拟内存可以节省内存,但也带来一定的性能损失。

持久化

AOF

采用写后日志的方式,先执行命令,再将操作日志以文本形式追加到文件中。

为什么采用写后日志?(Mysql是采用写前日志

  • 避免出现记录错误命令的情况,并且 AOF 写日志也是在主线程中进行的。

优缺点

  • 优点
    • 故障不丢失,写回策略默认是每秒一次,保证故障最多丢失一秒的数据
  • 缺点
    • 文件体积较大
    • 恢复速度更慢

写回(硬盘)策略

  1. Always(总是),每次写操作命令执行完成后,同步将 AOF 日志数据写回硬盘。本质是每次写操作都执行 fsync() 函数。
  2. EverySec(每秒),每次写操作命令执行完成后,将命令写入到 AOF 的内核缓冲区,每秒将缓冲区的内容写回到硬盘。本质是每秒创建一个异步任务执行 fsync() 函数。
  3. No,意味着交给操作系统控制写回的时机。本质是不执行 fsync() 函数。

重写(BGREWRITE)

当 AOF文件太大时,Redis fork出一个子进程调用 bgrewriteaof 方法重写一个新的文件,比如increase(1)和increase(1),会被合并成set(2)。也用到了 写时复制

RDB

将某一时刻的内存数据以二进制的形式写入磁盘。

RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

save 和 bgsave 两条命令触发 RDB 持久化操作。save 命令在主线程中执行,会导致阻塞。而 bgsave 创建一个子线程,避免阻塞,是默认的 RDB 的默认配置。并且 bgsave 允许执行快照命令期间修改数据,因为子线程会创建一个修改后的副本再写入(写时复制)。

优缺点

  • 优点
    • 体积更小,二进制文件
    • 恢复更快,适合备份
    • 性能更高,只需要fork子进程
  • 缺点
    • 故障丢失,每隔一段时间(最少五分钟)持久化一次,如果发生故障,意味着这段时间内的所有数据都丢失
    • 耐久性差,数据量大的时候,fork会很耗时

Copy On Write(写时复制)

  • 原理:fork() 后的子进程与父进程共享内存空间,如果是读取数据,相安无事;如果是写数据,会检测到内存页是只读然后触发中断最后复制一份数据再做修改

  • 好处:减少不必要的资源分配;减少分配和复制大量资源时带来的瞬间延时。

  • 缺点:如果父子进程都在进行大量的写操作,会产生大量的分页错误。

混合持久化

结合 AOF 和 RDB 两种持久化方式。混合持久化只发生于 AOF 重写过程。 重写后的新 AOF 文件前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据。

缺点:不再是 AOF 格式的文件,可读性差。

数据结构

Redis 7之后使用 Listpack(紧凑列表) 数据结构代替 Ziplist。

  1. String: Simple Dynamic String(SDS)
  2. List: Quicklist(3.2之后);LinkedList、Ziplist(3.2之前)
  3. Hash: HashTable(哈希表)、ZipList。当同时满足以下两个条件的时候,Hash 对象使用 ZipList 编码否则使用 Hashtable:a、每个Field-Value的最大长度小于等于64字节;b、Field-Value最大数量小于512个。
  4. Set: HashTable、Intset。当同时满足以下两个条件的时候,Set 对象使用 Intset 编码否则使用 Hashtable:a、结合对象保存的所有元素都是整数值;b、集合对象保存的元素数量不超过512个。
  5. Sorted set: ZipList(压缩列表)、SkipList(跳表)。当同时满足以下两个条件的时候,Sorted set 对象使用 ZipList 编码否则使用 SkipList:a、每个元素空间小于64字节;b、集合中元素数量小于128个。
  6. Stream: 借鉴 Kafka 的设计,是一个支持多播且可持久化的消息队列。与 PUB/SUB 模式相比,无法持久化。与基于 List LPUSH 和 BRPOP 或 Sorted set 相比,不支持多播分组消费。
  7. Bitmap: 位图
  8. Hyperloglog: 基数(不重复的元素)统计,但是会存在一定误差。可用于比如注册 IP 数、每日访问 IP 数、页面实时UV、在线用户数,共同好友数等允许一定容错的业务场景。
  9. Geospatial index: 实现两个位置距离的计算、获取指定位置附近的元素等功能。

消息通信模式:pub/sub,不支持消息持久化

  • 基于频道(channel)
  • 基于模式(pattern)

注意事项:

  1. 客户端需要及时消费,否则会自动断开连接或丢数据
  2. 连接断开后需要重新连接,否则无法收到消息
  3. 该消息模式不是一种可靠的消息系统。当出现客户端连接退出,或者极端情况下服务端发生主备切换时,未消费的消息会被丢弃。

Hash 数据结构下的 rehash 过程

  1. 为 ht[1] 分配空间,作为 rehash 后的哈希表。
  2. 构造一个索引技术器变量 rehashidx 并初始化为零,表示 rehash 正在执行,否则 rehashidx 为 -1 表示没在进行。
  3. 在 rehash 期间,删改查操作都是先在旧表上操作,并把旧表的数据迁移到新表;新增的操作直接在新表上新增,并且 rehashidx 自增。
  4. 除此之外,没有增删改查的操作时,还会有一个定时任务周期性(每100ms触发一次)的迁移数据

好处:将一次性的大批量拷贝分摊到多个请求中。

缓冲区

共有三个缓冲区:

  1. 客户端输入/输出缓冲区

为了解决客户端和服务器端的请求发送和处理速度不匹配所设置的。

输入缓冲区会先暂存客户端发送过来的命令,Redis 主线程从输入缓冲区中读取命令,进行处理。当 Redis 主线程处理完数据后,会把结果写入到输出缓冲区,再从输出缓冲区返回给客户端。

缓冲区溢出的可能情况:BigKey,缓冲区大小不合理。

  1. 复制缓冲区,复制积压缓冲区

用于Redis主从节点之间复制时使用的。由于主从节点间的数据复制包括全量复制增量复制两种。因此也分为复制缓冲区复制积压缓冲区两种。

  • 复制缓冲区是 Redis 在全量复制过程中,主节点在向从节点传输 RDB 文件的同时,会继续接收客户端发送的写命令请求。这些写命令就会先保存在复制缓冲区中,等 RDB 文件传输完成后,再发送给从节点去执行。主节点上会为每个从节点都维护一个复制缓冲区,来保证主从节点间的数据同步。

  • 复制积压缓冲区是一个大小有限的环形缓冲区。当服务器断线重连后,复制积压缓冲区的内容会被发送到从节点,当主节点把复制积压缓冲区写满后,会覆盖缓冲区中的旧命令数据,会造成主从节点的数据不一致。在命令传播阶段出现断网的情况,或者网络抖动时会导致连接断开,此时主节点会向复制积压缓冲区写数据。

  1. AOF 缓冲区,AOF 重写缓冲区
  • AOF 缓冲区:Redis在AOF持久化的时候,会先把命令写入到AOF缓冲区,然后通过回写策略(always、everysec、no)来写入硬盘AOF文件。

  • AOF 重写缓冲区:当需要进行AOF重写时,主进程会fork一个子进程进行AOF重写,此时主进程还要继续接收客户端传来的指令,此时这些指令就会存放在AOF重写缓冲区中;当AOF重写完成后,将AOF重写缓冲区中的指令追加到aof文件中,并将原来的aof文件替换,来保证数据的一致性。(与主从复制中的复制缓冲区类似)

单线程与多线程

redis 4.0 后部分命令开始使用多线程。

为什么使用单线程?

  • Redis 的瓶颈是内存和带宽,而不是 CPU。

什么是多路IO复用:一个服务端进程可以同时处理多个客户端连接。用于AOF持久化任务和处理客户端请求。其实现函数有:

  • select:数据结构是bitmap,采用轮询,限制连接数为1024个(最大文件描述符数量)
  • poll:数据结构是数组,解决了select的个数限制,但依旧是轮询
  • epoll:数据结构是红黑树,使用回调的方式,解决了个数限制,也解决了轮询方式
    1. 边缘触发(Edge Trigger,ET),更适合高流量或发送大文件的场景,例如 nginx
    2. 水平触发(Level Trigger,LT),默认,能保证事件一定被处理。

为什么 Redis 又支持多线程了?

  • 使用多线程仅在部分非阻塞的删除操作上面,通过多线程非阻塞释放内存,减少对主线程的阻塞,提高执行效率。

高可用(主从,哨兵,集群)

主从模式

主节点可以读、写,从节点只能读

  1. slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照,并使用复制缓冲区记录保存快照这段时间内执行的写命令
  2. master将保存的快照文件发送给slave,并继续在复制缓冲区记录执行的写命令
  3. slave接收到快照文件后,加载快照文件,载入数据
  4. master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化
  5. 此后master每次执行一个写命令都会同步发送给slave,同时也会写入复制积压缓冲区,保持master与slave之间数据的一致性,如果主从之间断开连接,那么重连后会根据该缓冲区中的偏移量将断开连接这段时间的数据修改写入从节点。

请求转发

主从模式下,服务端并不做转发处理。而要实现读写分离的功能,需要客户端自行处理了。比如要自行定位master节点,然后将写请求发送过去,读请求则可以做负载均衡处理。

哨兵模式

哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。

哨兵每2秒向pubsub频道接收和发送hello信息,实现哨兵之间的通信。

哨兵每1秒向所有主从节点和其他哨兵发送PING命令来判断其是否正常运行。

哨兵每10秒向主从节点发送INFO命令来获取节点信息(被标记为客观下线后每1秒一次)。

如果主节点或者从节点没有在规定的时间内响应哨兵的 PING 命令,哨兵就会将它们标记为「主观下线」。其他哨兵会尝试与节点建立连接,如果大于配置文件中设定的值,则认定节点「客观下线」。

集群模式

Redis 集群无法保证强一致性,选择了高可用和分区容忍,即AP。

将不同的数据分布在不同的节点中,分布的规则采用一致性哈希算法。指将数据映射到一个首尾相连的哈希环上,如果增加或者移除一个节点,仅影响该节点在哈希环上顺时针相邻的后继节点,其它数据也不会受到影响。共有16384个哈希槽。

一致性哈希算法存在节点分布不均匀的问题。解决方法:引入虚拟节点,将数据映射至虚拟节点而不是真实节点,虚拟节点和真实节点又存在一层映射关系。

请求转发

在集群模式下,对于读/写请求,并非是服务端转发请求,而是服务端返回转移指令,通知客户端数据所在节点,并让客户端重新发起请求

事务

Redis 有事务但没有回滚,因为Redis认为事务的失败都是使用者造成的。

缓存雪崩、击穿、穿透

  • 缓存雪崩:大量缓存失效或 Redis 宕机时,大量请求访问到数据库,导致数据库压力骤增。
    • 解决方法:
      1. 大量失效:均匀设置过期时间或缓存预热
      2. 宕机:设置 Redis 高可用集群,熔断或限流
  • 缓存击穿:热点数据过期,大量请求访问到数据库
    • 解决方法:热点数据不设置过期时间
  • 缓存穿透:访问的数据既不在缓存中也不在数据库中,被黑客攻击。
    • 解决方法:使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在。

缓存策略

  1. Cache-Aside Pattern 旁路缓存模式;读,先从缓存读,未命中从数据库读;写,先写数据库,再写缓存
  2. Read/Write-Through Pattern 读/写穿透模式;在旁路缓存模式的基础上封装一层缓存服务,读写操作都调用该缓存服务。读。先从缓存读,未命中数据库读并写到缓存中再返回。
  3. Write-Behind Pattern 异步回写模式;与读/写穿透模式类似,缓存系统只同步写缓存,异步批量写数据库

旁路缓存下的一致性问题

不存在实时一致性,只能保证最终一致性。

首选

  • 写策略:先更新数据库,再删除缓存
  • 读策略:先读缓存中数据,如果缓存中没有,则读数据库的数据并且写入缓存中。

如果对缓存命中率有要求,可以采用先更新数据库再更新缓存 + 分布式锁的方案。分布式锁保证同一时间只运行一个请求更新缓存。

最终一致性方案:

主要考虑以下两点:

  1. 第一个操作成功,但第二个失败会有什么问题
  2. 高并发场景下数据会不会不一致

如果采用先删除缓存,再更新数据库如何避免出现脏数据?

延迟双删:先删除缓存,更新数据库,睡眠,再次删除缓存。睡眠的时间一般是一次查询的耗时 + 几百毫秒。第二次的删除可以清除掉因并发导致的缓存脏数据。

1
+2
+3
+4
+5
+6
+7
+8
+
#删除缓存
+redis.delKey(X)
+#更新数据库
+db.update(X)
+#睡眠
+Thread.sleep(N)
+#再删除缓存
+redis.delKey(X)
+

缓存删除重试:为了保证缓存删除成功,需要在缓存失败时增加重试机制。可以借助消息队列,将删除失败的数据进行异步重试。

BinLog缓存删除方案:BinLog存储了对数据库的更改操作日志记录,通过订阅该日志(如:canal),获取需要更新缓存的key和数据。

过期删除策略和内存淘汰策略

过期删除策略

  1. 定时删除策略:在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。
  • 优点:内存可以最快的被释放。
  • 缺点:删除过期key会占用 CPU 时间,对 CPU 不友好。
  1. 惰性删除策略:不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。
  • 优点:只有在访问的时候检查是否过期,对 CPU 友好。
  • 缺点:过期的 key 没被删除,对内存不友好。
  1. 定期删除策略:每隔一段时间随机从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。
  • 优点:减少删除操作对 CPU 的影响,也能删除一部分过期的数据。
  • 缺点:难以确定定期删除的频率。

Redis的删除策略是惰性删除+定期删除。定期删除如果检查到过期key的数据超过一定百分比,循环再次检查并删除。

内存淘汰策略

  1. 不进行数据淘汰(默认的内存淘汰策略),写入时禁止写入并报错。

  2. 进行数据淘汰

    1. 在设置了过期时间的数据中进行淘汰
      • volatile-random: 随机淘汰设置了过期时间的任意键值;
      • volatile-ttl:优先淘汰更早过期的键值。
      • volatile-lru:淘汰所有设置了过期时间的键值中,最久未使用的键值;
      • volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值;
    2. 在所有数据范围内进行淘汰
      • allkeys-random:随机淘汰任意键值;
      • allkeys-lru:淘汰整个键值中最久未使用的键值;
      • allkeys-lfu:淘汰整个键值中最少使用的键值。

近似 LRU 算法

出于节省内存的考虑,Redis 的 LRU 算法并非完整的实现,是一个基于哈希表的近似 LRU 算法,通过对少量键进行取样,然后回收其中的最久未被访问的键。通过调整每次回收时的采样数量 maxmemory-samples,可以实现调整算法的精度。

RedLock 红锁

红锁是一种分布式锁。在 Redis 单独节点的基础上,RedLock 使用了多个独立的 Redis 的 Master 实例(通常建议是奇数个,比如 5 个),共同协作来提供更强健的分布式锁服务。

特点

  1. 互斥性:任意时刻,只有一个客户端能持有锁
  2. 防止死锁:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。
  3. 可重入性:一个线程如果获取了锁之后,可以再次对其请求加锁。

实现思路

RedLock 是对集群的每个节点进行加锁,如果大多数节点(N/2+1)加锁成功,则才会认为加锁成功。

这样即使集群中有某个节点挂掉了,因为大部分集群节点都加锁成功了,所以分布式锁还是可以继续使用的。

分布式锁超时了怎么办

答:设置看门狗(定时器)

大 Key/ 热 Key

大 Key

指 Redis 的 Key 对应的 Value 过大,并不是指 Key 过大。

危害

  1. 性能问题:读取大 Key 消耗更多的网络带宽和处理时间
  2. 引发网络阻塞:如果一个 Key 的大小是 1MB,每秒访问量是 1000,那么每秒会产生 1000 MB 的流量
  3. 内存分配不均
  4. 持久化问题:持久化变得更耗时

解决方案

  1. 拆分大 Key,将数据分散到多个小 Key 中
  2. 定期清理或设置过期时间
  3. 数据压缩
  4. 选取合适的数据结构

如何找到大 Key

  1. 使用 redis-cli --bigkeys 命令,不建议
  2. 使用 scan 命令
  3. 使用 RdbTools 工具

如何删除大 Key

  1. 系统低峰期使用 del 命令(会造成线程阻塞)
  2. 使用 scan 命令(会造成线程阻塞)
  3. 使用 unlink 异步删除

热 Key

指短时间内被频繁访问的数据

危害

  1. 可能会缓存击穿,使存储层访问量激增
  2. 负载不均衡,热 Key 可能导致某些 Redis 节点负载过高
  3. 性能瓶颈,占用大量 CPU 资源,影响其他请求

解决方案

  1. 读写分离(最重要)
  2. 对热点数据分片,分散到不同的 Redis 实例,提升吞吐量

如何找到热 Key

  1. 使用 monitor 命令
  2. 使用 redis-cli --hotkeys 命令,不建议

为什么100万QPS的热key请求可能会影响1000万QPS的Redis实例

  1. 100万个请求在单线程的模型下会串行处理,阻塞其他请求
  2. 热 key 的能承载的QPS是由单个 key 的处理速度决定的
  3. 1000万QPS可能是集群模式下能承载的请求量
This post is licensed under CC BY 4.0 by the author.

Windows修改注册表脚本

ElasticSearch

diff --git a/posts/reg-scripts-in-windows/index.html b/posts/reg-scripts-in-windows/index.html new file mode 100644 index 000000000..e46850138 --- /dev/null +++ b/posts/reg-scripts-in-windows/index.html @@ -0,0 +1,57 @@ + Windows修改注册表脚本 | Blogs
Home Windows修改注册表脚本
Post
Cancel

Windows修改注册表脚本

  • 鼠标右键新建菜单栏中增加 New Markdown File
1
+2
+3
+4
+5
+6
+7
+
Windows Registry Editor Version 5.00
+[HKEY_CLASSES_ROOT\.md]
+@="Typora.md"
+"Content Type"="text/markdown"
+"PerceivedType"="text"
+[HKEY_CLASSES_ROOT\.md\ShellNew]
+"NullFile"=""
+
  • 鼠标右键菜单栏新增 Open with Code,需要根据实际情况修改路径:
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
Windows Registry Editor Version 5.00
+; Open files
+[HKEY_CLASSES_ROOT\*\shell\Open with VS Code]
+@="Open with Code"
+"Icon"="C:\\Program Files\\Microsoft VS Code\\Code.exe,0"
+[HKEY_CLASSES_ROOT\*\shell\Open with VS Code\command]
+@="\"C:\\Program Files\\Microsoft VS Code\\Code.exe\" \"%1\""
+; This will make it appear when you right click ON a folder
+; The "Icon" line can be removed if you don't want the icon to appear
+[HKEY_CLASSES_ROOT\Directory\shell\vscode]
+@="Open with Code"
+"Icon"="\"C:\\Program Files\\Microsoft VS Code\\Code.exe\",0"
+[HKEY_CLASSES_ROOT\Directory\shell\vscode\command]
+@="\"C:\\Program Files\\Microsoft VS Code\\Code.exe\" \"%1\""
+; This will make it appear when you right click INSIDE a folder
+; The "Icon" line can be removed if you don't want the icon to appear
+[HKEY_CLASSES_ROOT\Directory\Background\shell\vscode]
+@="Open with Code"
+"Icon"="\"C:\\Program Files\\Microsoft VS Code\\Code.exe\",0"
+[HKEY_CLASSES_ROOT\Directory\Background\shell\vscode\command]
+@="\"C:\\Program Files\\Microsoft VS Code\\Code.exe\" \"%V\""
+
This post is licensed under CC BY 4.0 by the author.

线段树(Java实现)

Redis 知识体系

diff --git a/posts/segment-tree/index.html b/posts/segment-tree/index.html new file mode 100644 index 000000000..9c00fae8b --- /dev/null +++ b/posts/segment-tree/index.html @@ -0,0 +1,87 @@ + 线段树(Java实现) | Blogs
Home 线段树(Java实现)
Post
Cancel

线段树(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
+
class NumArray {
+    int[] segmentTree;
+    int n;
+
+
+    public NumArray(int[] nums) {
+        n = nums.length;
+        segmentTree = new int [2 * n];
+        System.arraycopy(nums, 0, segmentTree, n, n);
+        for(int i = n - 1; i > 0; i--){
+            segmentTree[i] = segmentTree[2 * i] + segmentTree[2 * i + 1];
+        }
+    }
+    
+    public void update(int index, int val) {
+        index += n;
+        segmentTree[index] = val;
+        index /= 2;
+        while(index != 0){
+            segmentTree[index] = segmentTree[2 * index] + segmentTree[2 * index + 1];
+            index /= 2;
+        }
+    }
+    
+    public int sumRange(int left, int right) {
+        left += n;
+        right += n;
+        int sum = 0;
+        while(left <= right){
+            if(left % 2 == 1){
+                sum += segmentTree[left];
+                left ++;
+            }
+            if(right % 2 == 0){
+                sum += segmentTree[right];
+                right--;
+            }
+            left /= 2;
+            right /= 2;
+        }
+        return sum;
+    }
+}
+
This post is licensed under CC BY 4.0 by the author.

MySQL知识点汇总

Windows修改注册表脚本

diff --git a/posts/show-seconds-in-taskbar/index.html b/posts/show-seconds-in-taskbar/index.html new file mode 100644 index 000000000..a3df3457b --- /dev/null +++ b/posts/show-seconds-in-taskbar/index.html @@ -0,0 +1 @@ + Windows任务栏时间显示秒 | Blogs
Home Windows任务栏时间显示秒
Post
Cancel

Windows任务栏时间显示秒

  1. 使用 win + r 打开运行,再输入 regedit

  2. 在打开的注册表中定位至HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced

  3. 新建 DWORD(32位)值,命名为ShowSecondsInSystemClock,将其数值置为 1 并保存。

  4. 打开任务管理器重新启动Windows资源管理器即可。

This post is licensed under CC BY 4.0 by the author.

Dev-C++支持C++11的操作

【解决方案】NUC11锁屏后无法唤醒

diff --git a/posts/software-copyright-timeline/index.html b/posts/software-copyright-timeline/index.html new file mode 100644 index 000000000..fc7a5f145 --- /dev/null +++ b/posts/software-copyright-timeline/index.html @@ -0,0 +1 @@ + 申请软著的时间周期记录以及注意事项 | Blogs
Home 申请软著的时间周期记录以及注意事项
Post
Cancel

申请软著的时间周期记录以及注意事项

Introduction

版权中心官网链接:https://www.ccopyright.com.cn/

在没有加急的情况下,申请一篇软著的总体周期大概需要三四个月左右。如果时间紧迫,可以去某宝找机构加急办理(不建议)。

另外,申请软著可能用到的模板材料放到文末的附件中,有需要自取。

Timeline

2020-07-24:填写并提交两篇软著申请电子稿

2020-07-28:EMS邮寄所需材料

2020-07-29:北京登记部签收

2020-09-03:一篇受理登记,邮箱收到受理通知书。同天,另一篇软著材料提交不规范,收到补正通知书。

2020-09-04:再次EMS邮寄材料不规范的替代材料。

2020-09-05:北京登记部签收

2020-09-17:补正软著成功受理

2020-10-29:收到第一件软著登记证书的挂号信(寄信日期为10月26日)

2020-11-13:收到第二件软著登记证书的挂号信(寄信日期为11月9日)

第一篇软著时间线

第二篇软著时间线

第三篇软件著作初次邮寄材料的时间点是2020年12月17日左右,其余时间点见下。从受理到邮寄发放间隔了一个半月,可见效率之低下!

第三篇软著时间线

Notice

  1. 身份证复印件复印到同一张A4纸上。

  2. 使用说明书里的图片要清晰。

  3. 在合作开发的情况下,合作开发合同的落款一定要早于开发完成时间。

  4. 副本不收费

Attachment

  1. 模板材料

  2. 外部链接

This post is licensed under CC BY 4.0 by the author.

Github合并两个不同的仓库

C++知识点整理及常见STL函数的使用

diff --git a/posts/springboot/index.html b/posts/springboot/index.html new file mode 100644 index 000000000..b51daaec4 --- /dev/null +++ b/posts/springboot/index.html @@ -0,0 +1 @@ + SpringBoot | Blogs
Home SpringBoot
Post
Cancel

SpringBoot

Spring, SpringBoot, Spring MVC 区别:

  • Spring框架(Framework)是最流行的Java应用程序开发框架。 Spring框架的主要功能是依赖项注入或控制反转(IoC)。
  • Spring MVC是Spring的一个MVC框架,包含前端视图,文件配置等。XML和config配置比较复杂。
  • Spring Boot 是为简化Spring配置的快速开发整合包,允许构建具有最少配置或零配置的独立应用程序。

SpringBoot项目启动流程:

  • 总体来说分两部分,先初始化 SpringApplication,再运行 SpringApplication。

运行 SpringApplication 又分为: 1、获取并启动监听器 2、根据监听器和参数来创建运行环境 3、准备 Banner 打印器 4、创建 Spring 容器 5、Spring 容器前置处理(将前几步生成的监听器,Banner打印器和环境等配置到容器中) 6、刷新容器 7、Spring 容器后置处理(空方法) 8、发出结束执行的事件通知 9、返回容器

Spring核心之控制反转(IOC)和依赖注入(DI):

  • IOC是一种设计思想,将设计好的Bean对象交给容器控制,而不是在传统的在对象内部直接控制。用@Configutation + @Bean的方式。用通俗的话解释就是在容器里创建Bean对象,在需要的时候取出使用即可。
  • DI是一种实现方式,将应用程序依赖的对象注入到容器中。

IoC

refresh() 的作用:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。

Spring创建Bean的三步(由反射创建的)

  • 实例化,AbstractAutowireCapableBeanFactorycreateBeanInstance 方法
  • 属性注入,AbstractAutowireCapableBeanFactorypopulateBean 方法
  • 初始化,AbstractAutowireCapableBeanFactoryinitializeBean 方法
    • 调用Aware接口(如果实现了Aware接口)
    • postProcessBeforeInitialization
    • afterPropertiesSet
    • init-method
    • postProcessAfterInitialization

Spring启动时先扫描所有Bean信息,BeanDefinition存储日常给Spring Bean定义的元数据。然后存储BeanDefinition的BeanDefinitionMap,是根据字典序依次创建Bean对象。

两种IOC容器

IOC的实现原理是工厂模式加反射机制。

  • BeanFactory,只提供了实例化对象和获取对象的功能,使用懒加载机制,不支持国际化和基于依赖的注解
  • ApplicationContext,其拓展了BeanFactory接口,使用即时加载的机制,它是在Ioc启动时就一次性创建所有的Bean,支持国际化和基于依赖的注解,包括AnnotationConfigApplicationContext、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等

三级缓存解决循环依赖

  • 前提,出现循环依赖的Bean必须是单例;依赖注入的方式不能全是构造器注入(通过setter方法进行依赖注入,全是构造器注入则无法解决循环依赖)。
  • getSingleton(beanName)方法三级缓存
    1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean对象
    2. earlySingletonObjects,二级缓存,完成实例化,但是还未进行属性注入及初始化的提前暴露的对象
    3. singletonFactories,三级缓存,存放生产对象的工厂,并且每次从这个工厂中拿到的对象都是不一样的,二级缓存中存储的就是从这个工厂中获取到的对象,如果Bean存在AOP的话,返回的是AOP的代理对象,提前进行了代理,避免对后面重复创建代理对象。

为什么需要二级缓存: 如果出现循环依赖+aop时,多个地方注入这个动态代理对象需要保证都是同一个对象。如果只使用这两层缓存,在使用三级缓存中的工厂对象生成的动态代理对象都是新创建的,循环依赖的时候,注入到别的bean里面去的那个动态代理对象和最终这个bean在初始化后自己创建的bean地址值不一样。如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,而 Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。所以,Spring 选择了三级缓存。但是因为循环依赖的出现,导致了 Spring 不得不提前去创建代理,因为如果不提前创建代理对象,那么注入的就是原始对象,这样就会产生错误。

Prototype(原型)对象和单例对象的区别:

  • 单例对象在Spring加载的时候就创建,创建完毕后放入一级缓存中
  • 而原型对象在每次在需要的时候都会创建,并且也不会存入缓存中
  • 单例有状态(有状态指有成员变量并会进行修改操作)Bean对象的劣势:线程不安全。解决方案:考虑ThreadLocal(使用完后记得调用 ThreadLocal 的 remove 方法)或者锁机制。

为什么Springboot默认创建单例Bean:

  • 不需要多次创建实例
  • 减少垃圾回收
  • 缓存中可以快速获得

容器注册组件的四种方式

  1. @Configuration + @Bean,注意,使用这种注册方法 Spring 会通过 CGLIB 来增强这个类,会判断是否实例已经存在,多次调用只会生成一个实例,意味着在@Configuration中的@Bean生成的是单例;相比之下,在@Component类中使用@Bean注解时,并不会有这种增强的行为。也就是说,当你在@Component类中调用一个@Bean方法时,实际上就是直接调用这个方法,并且每次调用都会创建一个新的 Bean,也就是说是生成多实例。
  2. @ComponentScan + @Component
  3. @Import
  4. 新建一个实现FactoryBean接口的类

面向切面编程(AOP)也是一种设计思想,本质是为了解耦。其实现方式是动态织入,在运行时动态将要增强的代码织入到目标类中,借助动态代理技术完成的。并且接口与非接口的动态代理机制并不相同,如下所示:

  • 接口使用JDK代理,通过反射类Proxy以及拦截器的InvocationHandler回调接口实现的,使用反射,效率会低,不提供子类代理。
  • 非接口使用CGlib代理,没有接口,只有实现类,采用底层字节码增强技术ASM在运行时使用Enhancer类创建目标的子类,提供子类代理。

执行顺序:

  1. doAround(ProceedingJoinPoint pjp)的 pjp.proceed()的内容
  2. doBefore()
  3. doAfterReturning(String result) / doAfterThrowing(Exception e)
  4. doAfter()
  5. doAround(ProceedingJoinPoint pjp)的 pjp.proceed()的内容(当且仅当非异常情况下才会执行)

注解:

  • @Component修饰的类为组件类并为这个对象创建Bean,@Bean修饰的方法会生成一个对象Bean。

  • @SpringBootApplication 包含三个注解:

    1. @SpringBootConfiguration, 继承@Configuration,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例注册到spring容器中,并且实例名就是方法名。
    2. @EnableAutoConfiguration,主要由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)这两个注解组成的。
      • @AutoConfigurationPackage 内部是 @({Registrar.class}),用于将启动类所在的包里面的所有组件注册到spring容器。扫描 @Entity, @Mapper 等第三方依赖注解。
      • @Import(EnableAutoConfigurationImportSelector.class) 是将特定路径(META-INF/spring.factories)中所有符合自动配置条件(@ConditionalOnClass)的类加载到Ioc容器,例如mybatis-spring-boot-starter。AutoConfigurationImportSelector.java 中可以看到所有自动配置类的名称。自动配置类原理
    3. @ComponentScan,自动扫描并加载被@Component或@Repository修饰的组件,最终将这些组件加载到容器中,默认路径是该注解所在类的package。

事务

事务的几个参数:rollbackFor,propagation,isolation

PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的区别:

  • PROPAGATION_REQUIRES_NEW:内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚,两个事务互不影响。
  • PROPAGATION_NESTED:外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚。

事务失效

  1. 数据库引擎不支持(MyIsam)
  2. 没有注册成Bean被Spring管理(没有使用@Service等注解)
  3. 方法不是public或者被final修饰(无法生成代理类)
  4. 方法内部调用(解决方法是引入自身Bean)
  5. 异常被捕捉或抛出其他类型的异常(回滚的默认异常为RuntimeException)
  6. 未开启事务

方法调用的事务回滚情况

前提:a方法调用b方法

ab报错情况同类调用的现象不同类调用的现象
transactional注解无注解a方法报错,b方法不报错均回滚均回滚
transactional注解无注解a方法不报错,b方法报错均回滚均回滚
无注解transactional注解a方法报错,b方法不报错均不回滚均不回滚
无注解transactional注解a方法不报错,b方法报错均不回滚a不回滚,b回滚

结论:

  1. transactional注解修饰的方法会创建一个代理增强类,其他方法调用该注解修饰的方法也只是调用原类中的方法。
  2. transactional修饰的方法内报错就一定会回滚。

Spring 用到了哪些设计模式

  1. 工厂模式:通过 BeanFactory 和 ApplicationContext 容器创建 Bean 对象
  2. 代理模式:AOP 的实现
  3. 单例模式:Bean 默认是单例
  4. 模板方法:jdbcTemplate 等用到了模板方法
  5. 观察者模式:Springboot 事件驱动,监听器等
This post is licensed under CC BY 4.0 by the author.

ElasticSearch

Kafka vs RocketMQ

diff --git a/posts/static/index.html b/posts/static/index.html new file mode 100644 index 000000000..e5b85d328 --- /dev/null +++ b/posts/static/index.html @@ -0,0 +1,219 @@ + C++中静态成员及静态成员函数详解 | Blogs
Home C++中静态成员及静态成员函数详解
Post
Cancel

C++中静态成员及静态成员函数详解

1. 局部静态变量

局部静态变量用于函数体内部修饰变量,这种变量的生存期长于该函数。

用法:局部变量前加static,修饰局部变量为静态局部变量。

作用:改变局部变量的销毁时期。

作用域:局部作用域,当定义它的函数或语句块结束的时候,作用域结束。

与局部变量的区别:

  1. 静态局部变量在全局数据区(静态存储区)分配内存(局部变量在栈区分配内存);
  2. 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化(局部变量每次函数调用都会被初始化);
  3. 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0(局部变量不会被初始化);
  4. 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,也就是不能在函数体外面使用它(局部变量在栈区,在函数结束后立即释放内存);
  5. 当静态局部变量离开作用域后,并没有被销毁。当该函数再次被调用的时候,该变量的值为上次函数调用结束时的值(局部变量离开作用域便被销毁,再次调用函数时,其值为初始值)。

与全局变量的区别:

同样是初始化一次,连续调用fun()的结果是一样的,但是,使用全局变量的话,变量就不属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。

静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。

Code:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
#include<stdio.h>
+int fun(void){
+	static int count = 20;      // 只能在该函数内修改静态局部变量的值
+	return count--;
+}
+
+int main() {
+	printf("global\t\tlocal static\n");
+	int count = 1;
+	for(; count <= 10; ++count) printf("%d\t\t%d\n", count, fun());
+}
+

运行结果图

2. 全局静态变量

全局静态变量定义在函数体外,该变量只在本文件可见。

用法:全局变量前加static,修饰全局变量为静态全局变量。

作用:改变全局变量的可见性。静态全局变量的存储位置在静态存储区,未被初始化的静态全局变量会被自动初始化为0。

作用域:全局静态变量在声明他的文件之外是不可见的,仅在从定义该变量的开始位置到文件结尾可见。

特点:

  • 静态全局变量不能被其它文件所用(全局变量可以);
  • 其它文件中可以定义相同名字的变量,不会发生冲突。

Code:

以下两个文件要放到项目工程里才有效果。

1
+2
+3
+4
+
//file a.cpp
+ 
+//static int n = 15; // ERROR!因为static隔离了文件。
+int n = 15;
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
//file main.cpp
+
+#include <iostream>
+//#include"a.cpp"       //不管有没有static,加include都不会报错
+using namespace std;
+
+extern int n;
+
+void fn(){
+	n++;
+}
+
+int main() {
+	cout<<"Before:" <<n<<endl;
+	fn();
+	cout<<"After:" <<n<<endl;
+	return 0;
+}
+

运行结果图

3. 静态函数

静态函数的作用与2. 静态全局变量类似。特点也类似:不能被其他文件所用;其它文件中可以定义相同名字的变量,不会发生冲突。

用法:函数返回类型前加static,修饰函数为静态函数。

作用:改变函数的可见性。函数的定义和声明在默认情况下都是extern的,但静态函数只在声明它的文件中可见,不能被其他文件使用。

4. 静态成员

用法:类成员前加static,修饰类的成员为类的静态成员。

作用:实现多个对象之间的数据共享,并且使用静态成员不会破坏封装性,也保证了安全性。

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
+
#include<iostream>
+using namespace std;
+
+class Rectangle
+{
+private:
+    int m_w,m_h;
+    static int s_sum;
+
+public:
+    Rectangle(int w,int h)
+    {
+        this->m_w = w;
+        this->m_h = h;
+        s_sum += (this->m_w * this->m_h);
+    }
+
+    void GetSum()
+    {
+        cout<<"sum = "<<s_sum<<endl;
+    }
+};
+
+int Rectangle::s_sum = 0;  //静态成员类外初始化,因为其属于类,不属于类对象
+
+int main()
+{
+    cout<<"sizeof(Rectangle)="<<sizeof(Rectangle)<<endl;
+    Rectangle *rect1 = new Rectangle(3,4);
+    rect1->GetSum();
+    cout<<"sizeof(rect1)="<<sizeof(*rect1)<<endl;
+    Rectangle rect2(2,3);
+    rect2.GetSum();
+    cout<<"sizeof(rect2)="<<sizeof(rect2)<<endl;
+}
+
1
+2
+3
+4
+5
+
sizeof(Rectangle)=8
+sum = 12
+sizeof(rect1)=8
+sum = 12
+sizeof(rect1)=8
+

由此可知:sizeof(Rectangle) = 8bytes = sizeof(m_w) + sizeof(m_h)。也就是说静态数据成员并不占用Rectangle的内存空间。因为静态数据成员在全局数据区(静态区)分配内存。再看看GetSum(),第一次12 = 3 * 4,第二次18 = 12 + 2 * 3。因此,静态数据成员只会被初始化一次,与对象无关。

结论:

静态数据成员被当作是类的成员,由该类型的所有对象共享访问,对该类的多个对象来说,静态数据成员只分配一次内存。静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。从某种程度上来说,静态数据成员与静态变量相类似。

5. 静态成员函数

用法:类函数前加static,修饰类的函数为静态函数。

作用:减少资源消耗,不需要实例化就可以使用

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
+
#include<iostream>
+using namespace std;
+
+class Rectangle
+{
+private:
+    int m_w,m_h;
+    static int s_sum;
+
+public:
+    Rectangle(int w,int h)
+    {
+        this->m_w = w;
+        this->m_h = h;
+        s_sum += (this->m_w * this->m_h);
+    }
+
+    static void GetSum()                // 此处为静态成员函数
+    {
+        cout<<"sum = "<<s_sum<<endl;
+    }
+};
+
+int Rectangle::s_sum = 0;  //静态成员类外初始化,因为其属于类,不属于类对象
+
+int main()
+{
+    cout<<"sizeof(Rectangle)="<<sizeof(Rectangle)<<endl;
+    Rectangle *rect1 = new Rectangle(3,4);
+    rect1->GetSum();
+    cout<<"sizeof(rect1)="<<sizeof(*rect1)<<endl;
+    Rectangle rect2(2,3);
+    rect2.GetSum();          //可以用对象名.函数名访问
+    cout<<"sizeof(rect2)="<<sizeof(rect2)<<endl;
+    Rectangle::GetSum();     //也可以可以用类名::函数名访问
+}
+

结论:

  • 非静态成员函数可访问静态成员函数/成员;

  • 静态成员函数不能访问非静态成员函数/成员,只能访问静态成员函数/变量;

  • 调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以用类名::函数名调用。

另外,既然是在类里操作,对类成员的访问还是要遵从publicprotectedprivate访问规则。

6. 静态变量内存分配和初始化

全局变量文件域的静态变量类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化;局部静态变量(一般为函数内的静态变量)在第一次使用时分配内存并初始化。这里的变量包含内置数据类型和自定义类型的对象。

7. static关键字的好处

7.1 隐藏变量或函数、隔离错误,有利于模块化程序

在编程中,难免会用到全局变量,全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,全局变量在所有的源文件中都是有效的。如果希望全局变量仅限于在本源文件中使用,在其他源文件中不能引用,也就是说限制其作用域只在定义该变量的源文件内有效,而在同一源程序的其他源文件中不能使用,这时,就可以通过在全局变量上加static来实现,使全局变量被定义成一个静态全局变量。这样就可以避免其他源文件使用该变量、避免其他源文件因为该变量引起的错误。起到了对其他源文件隐藏该变量和隔离错误的作用,有利于模块化程序。

7.2 保持变量内容的持久性

有时候,我们希望函数中局部变量的值在函数调用结束之后不会消失,仍然保留函数调用结束的值。即它所在的存储单元不释放。这时,应该将该局部变量用关关键字static声明为静态局部变量。当局部变量被声明为静态局部变量的时候,也就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区存放,全局变量也存放在静态存储区,静态局部变量与全局变量的主要区别就在于可见性,静态局部变量只在其被声明的代码块中是可见的。

This post is licensed under CC BY 4.0 by the author.

C++中vector的初始化及赋值方式

Difference between NEW and MALLOC in C++

diff --git a/posts/ubuntu-camera-setting/index.html b/posts/ubuntu-camera-setting/index.html new file mode 100644 index 000000000..eda7800e9 --- /dev/null +++ b/posts/ubuntu-camera-setting/index.html @@ -0,0 +1,7 @@ + Ubuntu中关闭摄像头的自动曝光 | Blogs
Home Ubuntu中关闭摄像头的自动曝光
Post
Cancel

Ubuntu中关闭摄像头的自动曝光

目的

通过Python代码关闭杰锐微通摄像头的自动曝光功能。

尝试

查阅相关资料,有网友提出使用以下代码:

1
+
capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25)
+

失败

解决方案

先通过在终端输入v4l2-ctl --list-devices得到摄像头列表。

接着输入v4l2-ctl -d /dev/video2 --all查看单个摄像头的参数。

发现最后几行中的exposure_auto的默认值为3,正好代表着光圈优先。将其设置为1,代表手动模式

因此在代码修改为

1
+2
+
capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1)
+capture.set(cv2.CAP_PROP_EXPOSURE, 50)
+

即可。

曝光值参数可根据环境自主调节。

This post is licensed under CC BY 4.0 by the author.

【解决方案】NUC11锁屏后无法唤醒

Java知识点记录博客

diff --git a/posts/use-branch-to-rollback/index.html b/posts/use-branch-to-rollback/index.html new file mode 100644 index 000000000..0b91cd486 --- /dev/null +++ b/posts/use-branch-to-rollback/index.html @@ -0,0 +1 @@ + Github删除Commits记录 | Blogs
Home Github删除Commits记录
Post
Cancel

Github删除Commits记录

众所周知,已经Push到Github的Commits是不能撤销的。但是,我们可以通过创建分支并修改默认分支的方法让Repo回退至某个版本并删除该版本后的所有Commits记录。

1. Description

删除已Push的Commits并回退至旧版本。如下图所示,删除红框中的Commits记录并回退至箭头所指的版本。

Revert Commits也能回退版本。但是其不同之处在于,Revert Commits会保留所有的Commits。

2. Solution

本方法是在Windows的Github Desktop中操作的。但不管在哪个操作系统,解决该问题的思路是类似的。

首先选择要回退到的Commit并Create branch, Name branch, Publish branch. 如下三图所示:

接着,在Github上设置默认分支1,删除原分支,修改新建的分支名即可,如下三图所示:

但是这样有个缺点:因为在Github主页上修改分支名并不会实时同步到Github Desktop,因此GitHub Desktop中会出现“混乱”,此时建议删除本地代码并重新Clone至本地。

Reference

This post is licensed under CC BY 4.0 by the author.

Windows配置Jekyll相关环境

Python脚本制作coco格式的实例分割数据集

diff --git a/posts/zookeeper/index.html b/posts/zookeeper/index.html new file mode 100644 index 000000000..948eddf83 --- /dev/null +++ b/posts/zookeeper/index.html @@ -0,0 +1 @@ + Zookeeper 学习 | Blogs
Home Zookeeper 学习
Post
Cancel

Zookeeper 学习

Zookeeper 是什么

Zookeeper 是一个分布式的协调服务,可以实现

  1. 统一配置管理。比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置。将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。
  2. 统一命名服务。节点存储ip地址,只需要访问Znode节点就可以获取这些ip地址。
  3. 统一集群管理。Kafka 的集群管理基于Zookeeper。
  4. 统一服务管理。Dubbo 的服务发现基于Zookeeper。
  5. 分布式锁。通过在持久节点下建立临时顺序节点,可以保证锁的有序,监听机制保障锁传递的高效。

原理

文件系统

数据结构

文件系统类似的,整体上可以看成一棵树,每个节点称作一个 ZNode,每个 ZNode 都可以通过其路径得到唯一标识。默认只能最多存储 1MB 的数据

但与文件系统不同,每一个 ZNode 节点都可以存储数据,但文件系统的目录不可以存储数据。

ZNode 类型

三大类:

  1. 持久节点,除非客户端主动执行删除操作,否则 ZooKeeper 不会删除持久的 znode
  2. 临时节点,客户端断开连接后,ZooKeeper会自动删除临时节点
  3. 顺序节点,每次创建顺序节点时,ZooKeeper都会在路径后面自动添加上10位的数字,从1开始,最大是2147483647 (2^32-1)

四种形式:

  1. 持久节点
  2. 持久顺序节点
  3. 临时节点
  4. 临时顺序节点

唯一事务id

每次的变化都会产生一个集群全局的唯一的事务id, Zxid(ZooKeeper Transaction Id),由Zookeeper的 Leader 实例维护。变化包括

  1. 任何的客户端连接到Server
  2. 任何的客户端断开与Server连接
  3. 任何的Znode节点被创建create、修改set、删除delete

通知机制(监听机制)

具体是基于观察者模式实现的。在ZooKeeper中,客户端是观察者,服务端是被观察者。 客户端通过注册Watch来监听服务端节点的数据变化。

ZooKeeper 的观察机制允许用户在指定节点上针对感兴趣的事件注册监听,当事件发生时,监听器会被触发,并将事件信息推送到客户端。

监听两方面的内容:

  1. 监听 Znode 的数据变化
  2. 监听子节点的数量变化

角色

一主多从的结构

  1. Leader,同一时间只会有一个Leader,负责发起和提交写请求。接收到写请求后同时发送给Follower,统计Follower写入成功的数量,超过一半则认为成功。
  2. Follower,处理读请求,Leader宕机后使用 Paxos 一致性算法的 Zab 协议负责选举新的Leader。
  3. Observer,处理读请求,但没有选举权

会话(Session)

client与ZooKeeper集群中的某一台server保持连接,发送读/写请求,读请求直接由当前连接的server处理,写请求由于是事务请求,由当前server转发给leader进行处理。同时,client还能接收来自server端的watcher通知。所有的这些交互,都是基于client和ZooKeeper的server之间的TCP长连接,也称之为Session会话。有了会话之后,后续的请求发送,回应,心跳检测等机制都是基于会话来实现的。

ZAB 算法与 Raft 算法的选举过程

ZAB 算法

  1. 所有节点第一票先选举自己当leader,将投票信息广播出去;
  2. 按照规则(epoch 是否比自己的 epoch 大,以及 counter 是否比自己的 counter 大,总之选择zxid最大的投票信息,说明该节点的数据最全)判断是否需要更改投票信息,将更改后的投票信息再次广播出去;
  3. 判断是否有超过一半的投票选举同一个节点,如果是选举结束根据投票结果设置自己的服务状态,选举结束,否则继续进入投票流程。

Raft

每个节点都有一个定时器,最先结束倒计时的节点最先发送选举的信号(基本也会是被选举为 leader)

This post is licensed under CC BY 4.0 by the author.

Go 语言学习

-

diff --git a/redirects.json b/redirects.json new file mode 100644 index 000000000..8a5ca801f --- /dev/null +++ b/redirects.json @@ -0,0 +1 @@ +{"/norobots/":"/404.html","/assets/":"/404.html","/posts/":"/404.html"} \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 000000000..cf0e05946 --- /dev/null +++ b/robots.txt @@ -0,0 +1,5 @@ +User-agent: * + +Disallow: /norobots/ + +Sitemap: /sitemap.xml diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 000000000..a63456089 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,305 @@ + + + +/posts/it's-a-long-story/ +2022-05-21T14:32:43+08:00 + + +/posts/install-jekyll/ +2022-03-07T20:55:16+08:00 + + +/posts/use-branch-to-rollback/ +2022-03-07T21:04:27+08:00 + + +/posts/make-coco-dataset/ +2022-03-08T00:47:45+08:00 + + +/posts/get-hsv-and-bgr/ +2022-03-08T00:47:45+08:00 + + +/posts/merge-two-repos/ +2022-03-08T00:47:45+08:00 + + +/posts/software-copyright-timeline/ +2022-11-06T18:22:39+08:00 + + +/posts/C++Syntax/ +2022-03-30T14:53:09+08:00 + + +/posts/c++-vector-init/ +2022-04-08T13:13:16+08:00 + + +/posts/static/ +2022-03-31T20:06:41+08:00 + + +/posts/new-and-malloc/ +2022-11-08T11:50:26+08:00 + + +/posts/mathjax/ +2023-05-07T23:56:13+08:00 + + +/posts/dev-c++11/ +2022-04-17T13:51:08+08:00 + + +/posts/show-seconds-in-taskbar/ +2022-09-28T21:30:19+08:00 + + +/posts/nuc11-wakeup/ +2022-05-01T17:20:20+08:00 + + +/posts/ubuntu-camera-setting/ +2022-05-21T13:31:32+08:00 + + +/posts/java-syntax/ +2023-11-22T21:13:24+08:00 + + +/posts/java/ +2023-05-07T23:54:58+08:00 + + +/posts/mysql/ +2023-06-06T15:40:38+08:00 + + +/posts/segment-tree/ +2023-05-07T23:56:13+08:00 + + +/posts/reg-scripts-in-windows/ +2022-12-08T00:33:09+08:00 + + +/posts/redis/ +2024-09-24T09:23:03+08:00 + + +/posts/elasticsearch/ +2024-09-06T10:13:47+08:00 + + +/posts/springboot/ +2024-09-21T23:09:06+08:00 + + +/posts/kafka-vs-rocketmq/ +2024-09-08T22:04:07+08:00 + + +/posts/note-from-work/ +2024-09-24T09:23:03+08:00 + + +/posts/go/ +2024-09-03T10:05:07+08:00 + + +/posts/zookeeper/ +2024-09-24T09:23:03+08:00 + + +/categories/ +2024-09-24T09:23:33+08:00 + + +/tags/ +2024-09-24T09:23:33+08:00 + + +/archives/ +2024-09-24T09:23:33+08:00 + + +/about/ +2024-09-24T09:23:33+08:00 + + +/ + + +/tags/getting-started/ + + +/tags/syntax/ + + +/tags/windows-10/ + + +/tags/installation/ + + +/tags/github/ + + +/tags/branch/ + + +/tags/commits/ + + +/tags/mmdetection/ + + +/tags/labelme/ + + +/tags/coco/ + + +/tags/python/ + + +/tags/hsv/ + + +/tags/bgr/ + + +/tags/opencv/ + + +/tags/git/ + + +/tags/copyright/ + + +/tags/timeline/ + + +/tags/stl/ + + +/tags/c/ + + +/tags/vector/ + + +/tags/static/ + + +/tags/new/ + + +/tags/malloc/ + + +/tags/dev/ + + +/tags/skills/ + + +/tags/bug/ + + +/tags/camera/ + + +/tags/auto-exposure/ + + +/tags/java/ + + +/tags/backend/ + + +/tags/segment-tree/ + + +/tags/redis/ + + +/tags/go/ + + +/tags/zookeeper/ + + +/categories/tutorial/ + + +/categories/jekyll/ + + +/categories/github/ + + +/categories/script/ + + +/categories/python/ + + +/categories/software-copyright/ + + +/categories/c/ + + +/categories/mathjax/ + + +/categories/ide-configuration/ + + +/categories/dev-c/ + + +/categories/windows-10/ + + +/categories/nuc11/ + + +/categories/ubuntu/ + + +/categories/camera/ + + +/categories/backend/ + + +/categories/java/ + + +/categories/mysql/ + + +/categories/algorithm/ + + +/categories/redis/ + + +/categories/go/ + + +/categories/zookeeper/ + + +/page2/ + + +/page3/ + + diff --git a/sw.js b/sw.js new file mode 100644 index 000000000..84b14717d --- /dev/null +++ b/sw.js @@ -0,0 +1 @@ +self.importScripts('/assets/js/data/swcache.js'); const cacheName = 'chirpy-20240924.092336'; function verifyDomain(url) { for (const domain of allowedDomains) { const regex = RegExp(`^http(s)?:\/\/${domain}\/`); if (regex.test(url)) { return true; } } return false; } function isExcluded(url) { for (const item of denyUrls) { if (url === item) { return true; } } return false; } self.addEventListener('install', event => { event.waitUntil( caches.open(cacheName).then(cache => { return cache.addAll(resource); }) ); }); self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(keyList => { return Promise.all( keyList.map(key => { if (key !== cacheName) { return caches.delete(key); } }) ); }) ); }); self.addEventListener('message', (event) => { if (event.data === 'SKIP_WAITING') { self.skipWaiting(); } }); self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request).then(response => { if (response) { return response; } return fetch(event.request).then(response => { const url = event.request.url; if (event.request.method !== 'GET' || !verifyDomain(url) || isExcluded(url)) { return response; } /* see: */ let responseToCache = response.clone(); caches.open(cacheName).then(cache => { /* console.log('[sw] Caching new resource: ' + event.request.url); */ cache.put(event.request, responseToCache); }); return response; }); }) ); }); diff --git a/tags/auto-exposure/index.html b/tags/auto-exposure/index.html new file mode 100644 index 000000000..37d8938e2 --- /dev/null +++ b/tags/auto-exposure/index.html @@ -0,0 +1 @@ + auto exposure | Blogs
Home Tags auto exposure
Tag
Cancel
diff --git a/tags/backend/index.html b/tags/backend/index.html new file mode 100644 index 000000000..122442df0 --- /dev/null +++ b/tags/backend/index.html @@ -0,0 +1 @@ + backend | Blogs
Home Tags backend
Tag
Cancel
diff --git a/tags/bgr/index.html b/tags/bgr/index.html new file mode 100644 index 000000000..6d154812c --- /dev/null +++ b/tags/bgr/index.html @@ -0,0 +1 @@ + bgr | Blogs
Home Tags bgr
Tag
Cancel
diff --git a/tags/branch/index.html b/tags/branch/index.html new file mode 100644 index 000000000..041b09a7d --- /dev/null +++ b/tags/branch/index.html @@ -0,0 +1 @@ + branch | Blogs
Home Tags branch
Tag
Cancel
diff --git a/tags/bug/index.html b/tags/bug/index.html new file mode 100644 index 000000000..be5d392f5 --- /dev/null +++ b/tags/bug/index.html @@ -0,0 +1 @@ + bug | Blogs
Home Tags bug
Tag
Cancel
diff --git a/tags/c/index.html b/tags/c/index.html new file mode 100644 index 000000000..c37a299c4 --- /dev/null +++ b/tags/c/index.html @@ -0,0 +1 @@ + c++ | Blogs
Home Tags c++
Tag
Cancel
diff --git a/tags/camera/index.html b/tags/camera/index.html new file mode 100644 index 000000000..7368fb4e5 --- /dev/null +++ b/tags/camera/index.html @@ -0,0 +1 @@ + camera | Blogs
Home Tags camera
Tag
Cancel
diff --git a/tags/coco/index.html b/tags/coco/index.html new file mode 100644 index 000000000..ab231053c --- /dev/null +++ b/tags/coco/index.html @@ -0,0 +1 @@ + coco | Blogs
Home Tags coco
Tag
Cancel
diff --git a/tags/commits/index.html b/tags/commits/index.html new file mode 100644 index 000000000..af69da0ec --- /dev/null +++ b/tags/commits/index.html @@ -0,0 +1 @@ + commits | Blogs
Home Tags commits
Tag
Cancel
diff --git a/tags/copyright/index.html b/tags/copyright/index.html new file mode 100644 index 000000000..25adc60cd --- /dev/null +++ b/tags/copyright/index.html @@ -0,0 +1 @@ + copyright | Blogs
Home Tags copyright
Tag
Cancel
diff --git a/tags/dev/index.html b/tags/dev/index.html new file mode 100644 index 000000000..42aaf9194 --- /dev/null +++ b/tags/dev/index.html @@ -0,0 +1 @@ + dev | Blogs
Home Tags dev
Tag
Cancel
diff --git a/tags/getting-started/index.html b/tags/getting-started/index.html new file mode 100644 index 000000000..9c2a936df --- /dev/null +++ b/tags/getting-started/index.html @@ -0,0 +1 @@ + getting started | Blogs
Home Tags getting started
Tag
Cancel
diff --git a/tags/git/index.html b/tags/git/index.html new file mode 100644 index 000000000..4e2f17750 --- /dev/null +++ b/tags/git/index.html @@ -0,0 +1 @@ + git | Blogs
Home Tags git
Tag
Cancel
diff --git a/tags/github/index.html b/tags/github/index.html new file mode 100644 index 000000000..e4980821f --- /dev/null +++ b/tags/github/index.html @@ -0,0 +1 @@ + github | Blogs
Home Tags github
Tag
Cancel
diff --git a/tags/go/index.html b/tags/go/index.html new file mode 100644 index 000000000..c0764bdcc --- /dev/null +++ b/tags/go/index.html @@ -0,0 +1 @@ + go | Blogs
Home Tags go
Tag
Cancel
diff --git a/tags/hsv/index.html b/tags/hsv/index.html new file mode 100644 index 000000000..8dbe3cc71 --- /dev/null +++ b/tags/hsv/index.html @@ -0,0 +1 @@ + hsv | Blogs
Home Tags hsv
Tag
Cancel
diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 000000000..024e08fd4 --- /dev/null +++ b/tags/index.html @@ -0,0 +1 @@ + Tags | Blogs
Home Tags
Tags
Cancel
diff --git a/tags/installation/index.html b/tags/installation/index.html new file mode 100644 index 000000000..510f0925a --- /dev/null +++ b/tags/installation/index.html @@ -0,0 +1 @@ + installation | Blogs
Home Tags installation
Tag
Cancel
diff --git a/tags/java/index.html b/tags/java/index.html new file mode 100644 index 000000000..a72cb50ea --- /dev/null +++ b/tags/java/index.html @@ -0,0 +1 @@ + java | Blogs
Home Tags java
Tag
Cancel
diff --git a/tags/labelme/index.html b/tags/labelme/index.html new file mode 100644 index 000000000..6a30c3785 --- /dev/null +++ b/tags/labelme/index.html @@ -0,0 +1 @@ + labelme | Blogs
Home Tags labelme
Tag
Cancel
diff --git a/tags/malloc/index.html b/tags/malloc/index.html new file mode 100644 index 000000000..f02270175 --- /dev/null +++ b/tags/malloc/index.html @@ -0,0 +1 @@ + malloc | Blogs
Home Tags malloc
Tag
Cancel
diff --git a/tags/mmdetection/index.html b/tags/mmdetection/index.html new file mode 100644 index 000000000..4dc36e862 --- /dev/null +++ b/tags/mmdetection/index.html @@ -0,0 +1 @@ + mmdetection | Blogs
Home Tags mmdetection
Tag
Cancel
diff --git a/tags/new/index.html b/tags/new/index.html new file mode 100644 index 000000000..999a8dc49 --- /dev/null +++ b/tags/new/index.html @@ -0,0 +1 @@ + new | Blogs
Home Tags new
Tag
Cancel
diff --git a/tags/opencv/index.html b/tags/opencv/index.html new file mode 100644 index 000000000..04a0bb6fc --- /dev/null +++ b/tags/opencv/index.html @@ -0,0 +1 @@ + opencv | Blogs
Home Tags opencv
Tag
Cancel
diff --git a/tags/python/index.html b/tags/python/index.html new file mode 100644 index 000000000..c9fc8b724 --- /dev/null +++ b/tags/python/index.html @@ -0,0 +1 @@ + python | Blogs
Home Tags python
Tag
Cancel
diff --git a/tags/redis/index.html b/tags/redis/index.html new file mode 100644 index 000000000..7ed210f7b --- /dev/null +++ b/tags/redis/index.html @@ -0,0 +1 @@ + redis | Blogs
Home Tags redis
Tag
Cancel
diff --git a/tags/segment-tree/index.html b/tags/segment-tree/index.html new file mode 100644 index 000000000..406cfd386 --- /dev/null +++ b/tags/segment-tree/index.html @@ -0,0 +1 @@ + segment tree | Blogs
Home Tags segment tree
Tag
Cancel
diff --git a/tags/skills/index.html b/tags/skills/index.html new file mode 100644 index 000000000..d3dc6702d --- /dev/null +++ b/tags/skills/index.html @@ -0,0 +1 @@ + skills | Blogs
Home Tags skills
Tag
Cancel
diff --git a/tags/static/index.html b/tags/static/index.html new file mode 100644 index 000000000..e0ce145a8 --- /dev/null +++ b/tags/static/index.html @@ -0,0 +1 @@ + static | Blogs
Home Tags static
Tag
Cancel
diff --git a/tags/stl/index.html b/tags/stl/index.html new file mode 100644 index 000000000..a1330e24f --- /dev/null +++ b/tags/stl/index.html @@ -0,0 +1 @@ + stl | Blogs
Home Tags stl
Tag
Cancel
diff --git a/tags/syntax/index.html b/tags/syntax/index.html new file mode 100644 index 000000000..4ff30d126 --- /dev/null +++ b/tags/syntax/index.html @@ -0,0 +1 @@ + syntax | Blogs
Home Tags syntax
Tag
Cancel
diff --git a/tags/timeline/index.html b/tags/timeline/index.html new file mode 100644 index 000000000..ccae9fc18 --- /dev/null +++ b/tags/timeline/index.html @@ -0,0 +1 @@ + timeline | Blogs
Home Tags timeline
Tag
Cancel
diff --git a/tags/vector/index.html b/tags/vector/index.html new file mode 100644 index 000000000..28dd19bdc --- /dev/null +++ b/tags/vector/index.html @@ -0,0 +1 @@ + vector | Blogs
Home Tags vector
Tag
Cancel
diff --git a/tags/windows-10/index.html b/tags/windows-10/index.html new file mode 100644 index 000000000..99c2eade3 --- /dev/null +++ b/tags/windows-10/index.html @@ -0,0 +1 @@ + windows 10 | Blogs
Home Tags windows 10
Tag
Cancel
diff --git a/tags/zookeeper/index.html b/tags/zookeeper/index.html new file mode 100644 index 000000000..664160381 --- /dev/null +++ b/tags/zookeeper/index.html @@ -0,0 +1 @@ + zookeeper | Blogs
Home Tags zookeeper
Tag
Cancel
diff --git a/unregister.js b/unregister.js new file mode 100644 index 000000000..20cef0de8 --- /dev/null +++ b/unregister.js @@ -0,0 +1 @@ +if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistrations().then((registrations) => { for (let reg of registrations) { reg.unregister(); } }); }