diff --git a/CNAME b/CNAME index 61d7318..a938258 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -himring.top \ No newline at end of file +#填上自己的域名(如果有申请) \ No newline at end of file diff --git a/README.md b/README.md index fe4987d..3d0f7ce 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ -lemonchann的个人博客仓库。 +这是一个可 fork 的博客模板仓库,帮助你快速搭建自己的博客,可以参考[这篇文章](https://github.com/lemonchann/lemonchann.github.io/blob/master/_posts/2019-11-22-create_blog_with_github_pages.md)详细指导搭建步骤。 ### 文章版权 -**[_posts](https://github.com/lemonchann/lemonchann.github.io/tree/master/_posts)** 文件夹内所有文章若无特别声明均使用[CC BY-SA 4.0 International License(知识共享署名-相同方式共享 4.0 国际许可协议)](http://creativecommons.org/licenses/by-sa/4.0/)授权。 +`_posts` 文件夹内所有文章版权归我所有,转载需联系我获得授权。 ### 致谢 -本站原始主题来自Jekyll主题[LOFFER](https://fromendworld.github.io/LOFFER/) +感谢Jekyll主题[LOFFER](https://fromendworld.github.io/LOFFER/)模提供了原始主题模板,我在其上进行的二次开发。 + +如果搭建过程中有什么问题,也可以在我的个人技术公众号「后端技术学堂」讨论交流,扫码添加。 + +![公众号二维码](https://upload-images.jianshu.io/upload_images/7842464-15f939ec039690f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + diff --git a/_config.yml b/_config.yml index 67c75ad..3b7fc4c 100644 --- a/_config.yml +++ b/_config.yml @@ -3,16 +3,16 @@ # # Name of your site (displayed in the header) -name: "lemonchann's blog" +name: "lemon's blog" # Short bio or description (displayed in the header) -description: "learning and practicing make skillfull" +description: "个人技术公众号「后端技术学堂」分享、记录、成长" # # Flags below are optional # # URL of your avatar or profile pic (you could use your GitHub profile pic) -avatar: https://github.com/lemonchann/lemonchann.github.io/raw/master/images/blog.jpg +avatar: https://upload-images.jianshu.io/upload_images/7842464-15f939ec039690f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 # 你的favicon(出现在浏览器tab上)图片URL,建议使用较小(64px × 64px)的图片 favicon: https://github.com/lemonchann/lemonchann.github.io/raw/master/images/favicon.ico @@ -21,13 +21,13 @@ favicon: https://github.com/lemonchann/lemonchann.github.io/raw/master/images/fa # and URL Link(e.g. 'Theme' tab below) tabs. If you don't need one, just delete # it from the list(Delete '- name: ' and 'url: ', too!) navigation: - - name: Blog + - name: 首页 url: / - - name: About + - name: 关于 url: /about - - name: Archive + - name: 归档 url: /archive - - name: Tags + - name: 标签 url: /tags # Pagination @@ -40,7 +40,7 @@ footer-links: #weibo: frommidworld #请输入你的微博个性域名 https://www.weibo.com/ behance: # https://www.behance.net/ dribbble: - zhihu: ll-chen-2 + zhihu: ning-meng-cheng-31-94 email: lemonchann@foxmail.com facebook: flickr: @@ -56,7 +56,7 @@ footer-links: youtube: # Text under the icons in footer -footer-text: Copyright (c) 2019 lemonchann +footer-text: Copyright (c) 2019 lemon # Enter your Disqus shortname (not your username) to enable commenting on posts # You can find your shortname on the Settings page of your Disqus account @@ -70,7 +70,7 @@ gitalk: owner: lemonchann # Enter your Google Analytics web tracking code (e.g. UA-2110908-2) to activate tracking -google_analytics: UA-152888548-1 +#google_analytics: # Your website URL (e.g. http://barryclark.github.io or http://www.barryclark.co) # Used for Sitemap.xml and your RSS feed diff --git a/_posts/2018-10-12-Play_with_vscode_PlantUML_preview.md b/_posts/2018-10-12-Play_with_vscode_PlantUML_preview.md deleted file mode 100644 index 84b5b04..0000000 --- a/_posts/2018-10-12-Play_with_vscode_PlantUML_preview.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -layout: post -title: "玩转vscode支持PlantUML绘制预览流程图" -date: 2018-10-12 -tags: [vscode] -comments: true -author: lemonchann ---- - -软件设计过程中,有好几种图需要画,比如流程图、类图、组件图等,我知道大部分人画流程图一般都会用微软的viso绘制,我之前也是这个习惯,viso画图有个不好的地方是需要时刻去调整线条和边框已达到简洁美观,今天我给大家介绍一款程序员画图神器PlantUML,一款你用了就爱上的画图软件! - -VsCode以插件的形式支持了这款画图神器,还不知道VsCode? - -> VsCode 强大地自定义功能,已经成为程序员最爱编辑器。 -> Microsoft在2015年4月30日Build 开发者大会上正式宣布了 Visual Studio Code 项目:一个运行于 Mac OS X、Windows和Linux之上的,针对于编写现代 Web 和云应用的跨平台源代码编辑器。 - -> 该编辑器也集成了所有一款现代编辑器所应该具备的特性,包括语法高亮(syntax high lighting),可定制的热键绑定(customizable keyboard bindings),括号匹配(bracket matching)以及代码片段收集(snippets)。Somasegar 也告诉笔者这款编辑器也拥有对 Git 的开箱即用的支持。引用[360百科](https://baike.so.com/doc/24428308-25261478.html) - - - -## 主角出场 - -### PlantUML - -**PlantUML是一个开源项目,支持快速绘制:** - ->时序图 -用例图 -类图 -活动图 (旧版语法在此处) -组件图 -状态图 -对象图 -部署图 -定时图 - -**同时还支持以下非UML图:** ->线框图形界面 -架构图 -规范和描述语言 (SDL) -Ditaa diagram -甘特图 -MindMap diagram -以 AsciiMath 或 JLaTeXMath 符号的数学公式 - -通过简单直观的语言来定义这些示意图,与MarkDown有相似的作用,这两种语言一个主要面向文本渲染一个主要用于图形绘制。 -#### 语法 -语法简单明了,查看以下[官方教程](http://plantuml.com/zh/sequence-diagram) -**我截取几个官网的事例图片在这里:** - -- 活动图 -![活动图](https://i.loli.net/2019/11/21/GIBKdwfv2zCLH5y.png) -- 类图 -![类图](https://i.loli.net/2019/11/21/KZz4F82AugpNckW.png) -- 时序图 -![时序图](https://i.loli.net/2019/11/21/XIKTPifd6GmMB5Y.png) -- 用例图 -![用例图](https://i.loli.net/2019/11/21/8nMHGwyV1lobCLz.png) -- 状态图 -![状态图](https://i.loli.net/2019/11/21/6b9q2BXLyeZSIpD.png) - -#### 图中的图片都是用源代码' '写'' 出来的哦!是不是很cool - - -### PlantUML遇上VsCode -#### 安装 -- 安装graphviz-2.38.msi -- 安装2个vscode插件: -> PlantUML、Graphviz Preview - -#### 例子 -```plantUML -@startuml -Alice -> Bob: Authentication Request -Bob --> Alice: Authentication Response - -Alice -> Bob: Another authentication Request -Alice <-- Bob: another authentication Response -@enduml -``` -#### 预览 -> Alt+D - -#### 文件格式 -> .wsd, .pu, .puml, .plantuml, .iuml - -#### 如何导出 -> F1/ctrl+shift+p; PlantUML:导出当前图表;选择导出格式png;导出即可。 - - - -#### 好了,这么好用工具赶紧用起来吧! \ No newline at end of file diff --git a/_posts/2018-11-1-Performance_ulimit_TCP_link.md b/_posts/2018-11-1-Performance_ulimit_TCP_link.md deleted file mode 100644 index 6409442..0000000 --- a/_posts/2018-11-1-Performance_ulimit_TCP_link.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: post -title: "性能调优ulimit增加TCP连接最大限制" -date: 2018-11-1 -tags: [linux] -comments: true -author: lemonchann ---- - -Linux系统中tcp连接数是有最大限制的,即是进程可打开的最大文件描述个数,通过命令`ulimit -n`查看 - - - -## TCP连接数限制 - -高负载的服务器通过修改ulimit参数达到合理规划用户使用资源和系统资源的目的。 - -## 用户级别 - -#### 修改最大连接数 -- 修改系统参数实现 - -> ulimit -SHn 65535 - -> -H硬限制是实际的限制,-S软限制是warnning限制,只会做出warning. -如果运行ulimit命令修改的时候没有加上SH,就是两个参数一起改变. - -- 修改pam模块配置实现 - -1. session required /lib/security/pam_limits.so - -2. 修改/etc/security/limits.conf如下举例 - - `* soft nofile 65536` - `* hard nofile 65536` - `*代表所有用户,当然也可以指定用户如root` - -#### 确认修改是否生效 -项目中遇到修改后虽然命令查看已经是修改后的值,但是进程连接的tcp个数还是系统默认的1024导致接入拒绝 - -##### 查看进程实际的最大连接数 -`cat /proc/进程pid/limits` -`Max open files 就是当前进程的实际值` - -## 系统级别 -查看总的系统打开文件限制 -> cat /proc/sys/fs/file-max - -若要修改可以在rc.local加 -> echo 你要的 > /proc/sys/fs/file-max - - - - diff --git a/_posts/2018-6-21-Markdown_brief_syntactic.md b/_posts/2018-6-21-Markdown_brief_syntactic.md deleted file mode 100644 index 78c598e..0000000 --- a/_posts/2018-6-21-Markdown_brief_syntactic.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -layout: post -title: "Markdown 语法简明笔记" -date: 2018-6-21 -tags: [markdown] -comments: true -author: lemonchann -toc: true ---- -**Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。markdown是为那些需要经常码字或者进行文字排版的、对码字手速和排版顺畅度有要求的人群设计的,他们希望用键盘把文字内容打出来后就已经排版好了,最好从头到尾都不要使用鼠标。这些人包括经常需要写文档的码农、博客写手、网站小编、出版业人士等等** 参考[**百度百科**](https://baike.baidu.com/item/markdown/3245829?fr=aladdin) - - - -### markdown语法 -开始学习markdown就有必要了解一下基本的语法,这里罗列一些基本的语法,这些语法是非常简单且常用的,能够帮助你快速掌握这门轻量的标记语言并且能够动手写自己的小博客,动手写起博客之后一些高级的用法可以边用边学。 - -- **标题样式** -在Markdown中,若一段文字被定义为标题,只要在这段文字前加 # 号即可。注意这里#号后面是有空格的。 ->'# '一级标题, '## '二级标题, '### '三级标题 -- **列表** -1. 无序列表使用`*`、+和-来做为列表的项目标记,这些符号是都可以使用的,注意符号与字符间**必须有一个空格**。 ->* Candy. ->* Gum. ->* Booze. ->- Candy. ->- Gum. ->- Booze. ->+ Candy. ->+ Gum. ->+ Booze. - -2. 有序的列表则是使用一般的数字接着一个英文句点作为项目标记: ->1. Red ->2. Green ->3. Blue -- **目录** ->用`[TOC]`生成目录 -- **加粗** 用双*号 ->`**xxx**` **xxx** -- **引用** 由'>'开头 ->`>` -- **斜体**单*号 ->`*x*` *x* -- **删除线** 双波浪线 ->`~~xx~~` ~~xx~~ -- **分割线** 另起一行输入三个连续* ->`***` - -- **下划线** ++ 开头 ++结尾 ->`++下划线++` ++下划线++ - -- **高亮标记** ==开头 ==结尾 ->`==高亮标记==` ==高亮标记== - -- **换行** 在末尾敲击两个以上空白,然后回车 - -- **插入链接** ->语法:`[链接说明](uri)` - -- **插入图片** ->语法: `![image](uri)` 语法上和插入链接只是多了个! 插入图片的方法有很多种,csdn markdown提供插入图片功能,也可以注册各种图床(网上搜索说是七牛云最好用,没用过不发表)我这里说一种脑回路清奇的用GitHub当图床插入图片的方法。原链接参考[知乎](https://www.zhihu.com/question/21065229/answer/61070700?utm_medium=social&utm_source=wechat_session) - -- **插入表情** - -> :smile: :smile_cat: - -### 以我的实践举个图片插入的栗子: - -1. 将markdown需要用的图片放到git仓库中,发布到github上 -2. 访问我的github仓库https://github.com/lemonchann/cloud_image -3. 访问图片cloud_image/Markdown语法简明笔记1.png -4. 点 download 按钮,在地址栏可以复制图片地址,或者在Download按钮上直接右键 "复制链接地址" -5. 拷贝链接地址https://raw.githubusercontent.com/lemonchann/cloud_image/master/Markdown%E8%AF%AD%E6%B3%95%E7%AE%80%E6%98%8E%E7%AC%94%E8%AE%B01.png -6. 在Markdown中引用图片 -7. 这是这篇博客我在markdown编辑器里的编辑的内容![Markdown语法简明笔记1](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2018-6-21-Markdown_brief_syntactic/Markdown%E8%AF%AD%E6%B3%95%E7%AE%80%E6%98%8E%E7%AC%94%E8%AE%B01.png) - - ![Markdown语法简明笔记2](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2018-6-21-Markdown_brief_syntactic/Markdown%E8%AF%AD%E6%B3%95%E7%AE%80%E6%98%8E%E7%AC%94%E8%AE%B02.png) -- **插入图片2** -> 图片还可以用相对路径的方法插入,必须和markdown文件相同目录下的文件或文件夹,但这种方法不适合写单篇的csdn或知乎文章,可以用于写书写个人博客。 -> 语法示例: -> `![pic](image/test.png) 或 ![pic](test.png)` - -- **程序员必备代码段** 以三个 ` 开头带程序类型和 ``` 结尾,中间包含代码段 -```c++ -#include -using namespace std; -class test -{ - int a; - string str; -}; -``` -- **代码框** 用两个 ` 把代码框在中间就是代码段,也可以用于防止markdown语法生效(类似转义符) - ->`it is code` - -- **表格** - -header 1 | header 2 ----|--- -row 1 col 1 | row 1 col 2 -row 2 col 1 | row 2 col 2 - - - -### 编辑器推荐 - -推荐编辑器 - -- typora,谁用谁知道。 - -- vscode+markdown - - - -### 参考文章 -- [Markdown: Basics (快速入门)](http://wowubuntu.com/markdown/basic.html) -- [Markdown中插入图片有什么技巧?](https://www.zhihu.com/question/21065229/answer/61070700?utm_medium=social&utm_source=wechat_session) - -- [【进阶版】有道云笔记Markdown指南](http://note.youdao.com/iyoudao/?p=2445) -- [【简明版】有道云笔记Markdown指南](http://note.youdao.com/iyoudao/?p=2411)it \ No newline at end of file diff --git a/_posts/2019-11-18-new_gitbook_tutors.md b/_posts/2019-11-18-new_gitbook_tutors.md deleted file mode 100644 index cfc0e90..0000000 --- a/_posts/2019-11-18-new_gitbook_tutors.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -layout: post -title: "新版本gitbook配置图书预览和生成" -date: 2019-11-18 -tags: [tool] -comments: true -author: lemonchann - ---- - -什么是Gitbook? 简单说就是可以把用md写的多个文档组织成**书**发布,md你可以放在github管理,配置gitbook关联github可以实现实时commit的预览生成。也可本地预览,甚至生成各种格式文档输出的强大工具。 - - - -## 安装Gitbook -* 安装nodejs可以去[官网](http://nodejs.cn/download/)下载对应版本 , Gitbook 只支持 node 6.x.x版本 。参考: https://www.jianshu.com/p/57b46db0564e -* 安装gitbook,打开win cmd输入npm install gitbook-cli -g -> 常用命令: -> **gitbook -V 查看版本** -> **gitbook serve 生成网页localhost:4000预览** - 命令输入要进到SUMMARY.md所在目录![gitbook_serve](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-11-18-new_gitbook_tutors/gitbook_serve.png) -> gitbook init //初始化目录文件 -> gitbook help //列出gitbook所有的命令 -> gitbook --help //输出gitbook-cli的帮助信息 -> gitbook build //生成静态网页 -> gitbook serve //生成静态网页并运行服务器 -> gitbook build --gitbook=2.0.1 //生成时指定gitbook的版本, 本地没有会先下载 -> gitbook ls //列出本地所有的gitbook版本 -> gitbook ls-remote //列出远程可用的gitbook版本 -> gitbook fetch 标签/版本号 //安装对应的gitbook版本 -> gitbook update //更新到gitbook的最新版本 -> gitbook uninstall 2.0.1 //卸载对应的gitbook版本 -> gitbook build --log=debug //指定log的级别 -> gitbook builid --debug //输出错误信息 - -## 用Gitbook转换markdown文件生成PDF - -### 安装calibre -* 电子书生成下载依赖calibre否则会报错,建议先安装,[下载地址](https://calibre-ebook.com/download) -* 配置calibre环境变量,我的目录是C:\Program Files\Calibre2 - - ![环境变量设置1](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-11-18-new_gitbook_tutors/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E8%AE%BE%E7%BD%AE1.PNG) - - ![环境变量设置2](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-11-18-new_gitbook_tutors/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E8%AE%BE%E7%BD%AE2.PNG) - -### 生成PDF - -打开win cmd命令行,到SUMMARY.md所在目录执行 **gitbook pdf 生成pdf** -> 转换PDF失败原因: -1. 没有安装calibre -2. 安装calibre之后需要设置环境变量C:\Program Files\Calibre2 -3. [报错1](http://xcoding.tech/2018/08/08/hexo/%E5%A6%82%E4%BD%95%E4%BB%8E%E6%A0%B9%E6%9C%AC%E8%A7%A3%E5%86%B3hexo%E4%B8%8D%E5%85%BC%E5%AE%B9%7B%7B%7D%7D%E6%A0%87%E7%AD%BE%E9%97%AE%E9%A2%98/) - -## Gitbook关联github - -**Gitbook上同步github的配置界面已经发生了变化,新界面操作如下:** - -![gitbook空间](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-11-18-new_gitbook_tutors/gitbook%E7%A9%BA%E9%97%B4.png) - -![关联github](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-11-18-new_gitbook_tutors/%E5%85%B3%E8%81%94github.png) - -### 发布到github pages -#### 关于 GitHub Pages ->GitHub Pages 是一项静态站点托管服务,它直接从 GitHub 上的仓库获取 HTML、CSS 和 JavaScript 文件,(可选)通过构建过程运行文件,然后发布网站。 您可以在 GitHub Pages 示例集合中查看 GitHub Pages 站点的示例。 - -您可以在 GitHub 的 github.io 域或自己的自定义域上托管站点。 更多信息请参阅“对 GitHub Pages 使用自定义域”。 - -要开始使用,请参阅“创建 GitHub Pages 站点”。 - -#### GitHub Pages 站点的类型 -有三种类型的 GitHub Pages 站点:项目、用户和组织。 项目站点连接到 GitHub 上托管的特定项目,例如 JavaScript 库或配方集合。 用户和组织站点连接到特定的 GitHub 帐户。 - -用户和组织站点始终从名为 .github.io 或 .github.io 的仓库发布。 除非您使用自定义域,否则用户和组织站点位于 http(s)://.github.io 或 http(s)://.github.io。 - -项目站点的源文件与其项目存储在同一个仓库中。 除非您使用自定义域,否则项目站点位于 http(s)://.github.io/ 或 http(s)://.github.io/。 -[更多](https://help.github.com/cn/github/working-with-github-pages/about-github-pages) - -#### [这里](http://www.chengweiyang.cn/gitbook/github-pages/README.html)也包含推送到**github.page**的方法 -- master, 保存书籍的源码 -- gh-pages, 保存书籍编译后的 HTML 文件 - -**步骤:** - -- `gitbook build` 将书籍内容输出到默认目录,也就是当前目录下的 _book 目录 - - ![gitbook_pdf](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-11-18-new_gitbook_tutors/gitbook_pdf.png) - -- 创建gh-pages分支,并且删除不需要的文件,仅保留git目录和 _book目录 -> $ git checkout --orphan gh-pages -$ git rm --cached -r . -$ git clean -df -$ rm -rf *~` -- 然后,加入 _book 下的内容到分支中: -> $ cp -r _book/* . -$ git add . -$ git commit -m "Publish book" -- 将编译好的书籍内容上传到 GitHub 项目的 远程gh-pages 分支了 -> $git push -u origin gh-pages - - -### 参考 -[详细教程](https://jackchan1999.github.io/2017/05/01/gitbook/GitBook%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/) -[官方指引-integrations-Github](https://docs.gitbook.com/integrations/github) -[Github的GitBook项目](https://github.com/GitbookIO/gitbook/blob/master/docs/setup.md) -[github pages中文帮助](https://help.github.com/cn/github/working-with-github-pages/about-github-pages) \ No newline at end of file diff --git a/_posts/2019-11-22-create_blog_with_github_pages.md b/_posts/2019-11-22-create_blog_with_github_pages.md index 08cf07a..8eaa692 100644 --- a/_posts/2019-11-22-create_blog_with_github_pages.md +++ b/_posts/2019-11-22-create_blog_with_github_pages.md @@ -7,16 +7,22 @@ comments: true author: lemonchann --- -作为一个程序员怎么能没有自己的个人博客呢,这里详细记录和分享我的博客搭建经验,让你轻轻松松拥有自己的博客网站。 傻瓜式一站式教你用github pages 来搭建博客,详细记录全过程。 +作为一个程序员怎么能没有自己的个人博客呢,这里详细记录和分享我的博客搭建经验,让你轻轻松松拥有自己的博客网站。傻瓜式一站式教你用 github pages 来搭建博客,详细记录全过程,保证你能学会。 + +如果你是非程序员或者不关系技术细节,只需花 3 分钟阅读前面 5 个章节内容,就能轻松拥有自己的博客。 ## 开始 -先看下博客整体效果。[**点击在线预览我的博客**]( https://lemonchann.github.io/ ) +话不多说,直接上图先来看下我的博客整体效果。[**点击在线预览我的博客**]( https://lemonchann.github.io/blog/),个人比较喜欢这种简约的博客风格,不要花里胡哨但该有的功也都有。 ![blogPage](https://raw.githubusercontent.com/lemonchann/lemonchann.github.io/master/images/2019-11-22-create_blog_with_github_pages/blogPage.png) + + +下面列举这个博客具有的功能特性,其中我比较看重归档和搜索能力。 + ### 支持特性 - 简约风格博客 @@ -39,25 +45,31 @@ author: lemonchann - 支持归档与标签 +- 支持改变主题颜色 + +- 支持添加文章目录 + ## 建立博客Git仓库 首先你要在[github](https://github.com/)上有自己博客仓库,用来生成和存放博客文章。你可以直接fork我的博客仓库。这样你马上有了自己的博客仓库。 -[点这里我的博客地址](https://github.com/lemonchann/lemonchann.github.io)进去fork,之后在你自己的仓库下会看到刚复制的仓库。以后的操作都在你自己的仓库进行。 +[点这里我的博客地址](https://github.com/lemonchann/lemonchann.github.io)进去点击 fork,之后在你自己的仓库下会看到刚复制的仓库,以后的操作都在你自己的仓库进行,当然想感谢我写这个教程就帮我点个 start 吧! ![fork博客](https://raw.githubusercontent.com/lemonchann/lemonchann.github.io/master/images/2019-11-22-create_blog_with_github_pages/fork%E5%8D%9A%E5%AE%A2.png) -**版权声明: fork之后_posts文件夹内容是我的博客文章,版权归我所有。你可以选择删除里面的文章替换上自己的或者转载附上链接注明出处。 ** +**版权声明: fork之后_posts文件夹内容是我的博客文章,版权归我所有。你可以选择删除里面的文章替换上自己的博客文章,如需转载需要与我联系授权 **。 + + ## 修改博客仓库名称 -进到你自己的博客仓库,修改博客仓库名称成你自己的用户名。github page解析的时候找的是这个 username.github.io的仓库名。 +进到你自己的博客仓库,**修改博客仓库名称成你自己的用户名**。因为 github page 解析的时候找的是这个 username.github.io的仓库名,**这一步非常重要**。 ![修改仓库名称](https://raw.githubusercontent.com/lemonchann/lemonchann.github.io/master/images/2019-11-22-create_blog_with_github_pages/%E4%BF%AE%E6%94%B9%E4%BB%93%E5%BA%93%E5%90%8D%E7%A7%B0.png) -此时,不出意外的话,打开域名https://username.github.io 就能看到你刚搭建的博客了。*注意替换username成你自己的github用户名*。 +此时,不出意外的话,打开域名 https://username.github.io 就能看到你刚搭建的博客了。*注意替换 username成你自己的github 用户名*。 ## 博客配置 @@ -134,6 +146,8 @@ url: https://yourname.github.io **done! 现在输入上面提到的博客地址,回车,你拥有了自己的博客。** + + ## 如何写博客 好了,博客有了。如何更新文章呢? @@ -142,7 +156,13 @@ url: https://yourname.github.io 关于文章的**命名格式**:博客文章必须按照统一的命名格式 `yyyy-mm-dd-blogName.md` 比如我这篇博客的名字是`2019-11-22-create_blog_with_github_pages.md` -**看到这里,如果只是简单的想写博客,后面的不看也可以了,后面章节是记录一些DIY的过程。** +**看到这里,如果只是简单的想写博客,后面的不看也可以了,你已经拥有了自己的博客!后面章节是记录一些DIY的过程。** + +另外,发现最近用我这个模板的同学越来越多,如果搭建过程中有什么问题,可以在我的公众号「后端技术学堂」讨论交流。 + +![公众号二维码](https://upload-images.jianshu.io/upload_images/7842464-15f939ec039690f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + ## 本地博客预览 @@ -343,6 +363,20 @@ footer-links: eg. `style="font-family:arial;color:Gainsboro;font-size:10px; text-align:right;width:200px;background-color:gray;` +## 修改博客主题颜色 + +博客使用开源的颜色表[Open Color](https://yeun.github.io/open-color/),博客主题的可选颜色有: + +`red, pink, grape, violet, indigo, blue, cyan, teal, green, lime, yellow` + +修改文件`_sass/_variables.scss`,将文件中当前颜色,比如当前是 `grape` 全部替换成你想要的颜色即可。 + +## 显示文章目录 + +在文章开头信息中心增加 `toc: true` 描述即可打开文章目录显示。效果如下: + +![文章带目录](https://i.loli.net/2020/07/12/TFlRj3kBdpocY9K.png) + ## 如何传图片 @@ -359,6 +393,8 @@ PicGo支持图片上传github、SM.MS图床、阿里云、腾讯云等主流图 [好用的github插件](https://blog.csdn.net/u012702547/article/details/100533763) + + ## 网站结构 根目录的index.html生成blog首页 @@ -367,8 +403,14 @@ _include/footer.html生成侧边栏 _include/svg-icons.html生成社交头像的链接 + + ## 致谢 -感谢Jekyll提供的技术支持才能有这个博客。 +感谢 [Jekyll](https://www.jekyll.com.cn/) 提供的技术支持才能有这个博客。 -感谢[LOFFER](https://fromendworld.github.io/LOFFER/document/)提供的原始模板,我在其上进行的二次开发。 \ No newline at end of file +感谢 [LOFFER ](https://fromendworld.github.io/LOFFER/document/)提供的原始模板,我在其上进行的二次开发。 + +**我的个人技术公众号「后端技术学堂」分享、记录、成长,扫码添加,一起学习,共同成长。** + +![公众号二维码](https://upload-images.jianshu.io/upload_images/7842464-15f939ec039690f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) \ No newline at end of file diff --git a/_posts/2019-12-27-cpp_reference.md b/_posts/2019-12-27-cpp_reference.md deleted file mode 100644 index 4bbf611..0000000 --- a/_posts/2019-12-27-cpp_reference.md +++ /dev/null @@ -1,984 +0,0 @@ ---- -layout: post -title: "腾讯后台开发面试笔试C++知识点参考笔记" -date: 2019-12-29 -tags: [c++] -comments: true -author: lemonchann ---- - -**文章是由我笔试面试腾讯笔记整理而来,主要是针对面试的C++后台开发岗位,涵盖了大部分C++后台开发相关的,可能会考察和被问到的技术点。** - -**自认为这篇笔记比较全面的涵盖了,后台开发C++笔试面试大部分知识点,不管你是已经工作准备参加社招,还是在校学生准备参加校招,笔记都可以作为技术面试准备阶段参考查阅,查缺补漏。** - -笔记是基础C++知识点总结,没有过多的阐述后台开发的系统架构,和分布式后台服务设计相关内容,以及C++11新特性,这些在笔试面试也会被问到但不在这篇讨论范围,可以关注我后面有机会补上。 - -### 阅读提示 - -文章约12839字,阅读时长预计33分钟。建议关注收藏方便回头查阅。 - -### gdb调试命令 - -#### step和next的区别? - -当前line有函数调用的时候,next会直接执行到下一句 ,step会进入函数. - -#### 查看内存 - -> (gdb)p &a //打印变量地址 - -> (gdb)x 0xbffff543 //查看内存单元内变量 - -> 0xbffff543: 0x12345678 - -> (gdb) x /4xb 0xbffff543 //单字节查看4个内存单元变量的值 - -> 0xbffff543: 0x78 0x56 0x34 0x12 - -#### 多线程调试 - -> (gdb) info threads:查看GDB当前调试的程序的各个线程的相关信息 - -> (gdb) thread threadno:切换当前线程到由threadno指定的线程 - -> break filename:linenum thread all 在所有线程相应行设置断点,注意如果主线程不会执行到该行,并且启动all-stop模式,主线程执行n或s会切换过去 - -> set scheduler-locking off|on\step 默认off,执行s或c其它线程也同步执行。on,只有当前相称执行。step,只有当前线程执行 - -> show scheduler-locking 显示当前模式 - -> thread apply all command 每个线程执行同意命令,如bt。或者thread apply 1 3 bt,即线程1,3执行bt。 - -#### 查看调用堆栈 - -> (gdb)bt - -> (gdb)f 1 //帧简略信息 - -> (gdb)info f 1 //帧详细信息 - -#### 断点 - -> b test.cpp:11 - -> b test.cpp:main - -gdb attach 调试方法: - -> gdb->file xxxx->attach pid->**这时候进程是停止的**->c 继续运行 - -#### 带参数调试 - -输入参数命令set args 后面加上程序所要用的参数,注意,不再带有程序名,直接加参数,如: - -> (gdb)set args -l a -C abc - -#### list命令 - -> list linenum  //显示程序第linenum行的周围的程序 - -> list function  //显示程序名为function的函数的源程序 - - - -### static关键字的作用 - - - -### 软硬链接 - -ln -s 源文件 目标文件, ln -s / /home/good/linkname链接根目录/到/home/good/linkname - -1. 软链接就是:“ln –s 源文件 目标文件”,只会在选定的位置上生成一个文件的镜像,不会占用磁盘空间,类似与windows的快捷方式。 - -2. 硬链接ln源文件目标文件,没有参数-s, 会在选定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化。 - - - -### 函数指针 - - int (*func)(int, int) //函数指针 - - int (*funcArry[10])(int, int) //函数指针数组 - -const int* p; //指向const int的指针 - -int const* p; //同上 - -int* const p; //const指针 - - - -### 设计模式 - -[单例模式](http://blog.csdn.net/wuzhekai1985/article/details/6665869) - -[观察者模式(也叫发布订阅模式](http://blog.csdn.net/wuzhekai1985/article/details/6674984)) - -[工厂模式](http://blog.csdn.net/wuzhekai1985/article/details/6660462) 三种:简单工厂模式、工厂方法模式、抽象工厂模式 - -为什么要用工厂模式?原因就是对上层的使用者隔离对象创建的过程;或者是对象创建的过程复杂, - -使用者不容易掌握;或者是对象创建要满足某种条件,这些条件是业务的需求也好,是系统约束也好 - -,没有必要让上层使用者掌握,增加别人开发的难度。所以,到这时我们应该清楚了,无论是工厂模式, - -还是上面的战友说的开闭原则,都是为了隔离一些复杂的过程,使得这些复杂的过程不向外暴露, - -如果暴露了这些过程,会对使用者增加麻烦,这也就是所谓的团队合作。 - -### 数据结构 - -#### [各种排序算法](http://blog.csdn.net/daguairen/article/details/52611874) - -#### [堆排序](https://www.cnblogs.com/0zcl/p/6737944.html) - -关键:1.初始建堆从最后一个非叶节点开始调整 2.筛选从顶点开始往下调整 - -#### [通俗易懂的快排]( http://blog.csdn.net/vayne_xiao/article/details/53508973) - -#### 二叉树定理 - -度为2节点数 = 叶子节点数 - 1 - -证明:树枝数=节点数-1, n0*0 +n1*1 +n2*2 = n0+n1+n2-1 (n0代表度为0的节点数,以此类推) - - - -### 互斥锁 - -```c -pthread_mutex_t m_mutex; -pthread_mutex_init(&m_mutex, NULL)等效于pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER -pthread_mutex_lock(&m_mutex); -pthread_mutex_unlock(&m_mutex) -pthread_mutex_destroy(&m_mutex) -int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); -bool g_flag = false; -void* t1(void* arg) -{ - cout << "create t1 thread success" << endl; - pthread_mutex_lock(&m_mutex); - g_flag = true; - pthread_mutex_unlock(&m_mutex); -} - -void* t2(void* arg) -{ - cout << "create t2 thread success" << endl; - pthread_mutex_lock(&m_mutex); - g_flag = false; - pthread_mutex_unlock(&m_mutex); -} - -int main(int argc, char* argv[]) -{ - pthread_t tid1, tid2; - pthread_create(&tid1, NULL, t1, NULL); - sleep(2); - pthread_create(&tid2, NULL, t2, NULL); - pthread_join(tid1, NULL); - pthread_join(tid2, NULL); -} -``` - - - -### 大小端转换 - -```c -#define BigLittleSwap32(A) ((((uint32)(A) & 0xff000000) >> 24) | \ - (((uint32)(A) & 0x00ff0000) >> 8) | \ - (((uint32)(A) & 0x0000ff00) << 8) | \ - (((uint32)(A) & 0x000000ff) << 24)) -``` - - - -### io多路复用 - -[为什么 IO 多路复用要搭配非阻塞IO]( https://www.zhihu.com/question/37271342) - -设置非阻塞 `io fcntl(sockfd, F_SETFL, O_NONBLOCK); ` - -#### select - -```c -int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); - -void FD_CLR(int fd, fd_set *set); - -int FD_ISSET(int fd, fd_set *set); - -void FD_SET(int fd, fd_set *set); - -void FD_ZERO(fd_set *set); - -fd_set rdfds; -struct timeval tv; -int ret; -FD_ZERO(&rdfds); -FD_SET(socket, &rdfds); -tv.tv_sec = 1; -tv.tv_uses = 500; -ret = select (socket + 1, %rdfds, NULL, NULL, &tv); -if(ret < 0) perror (“select”); -else if (ret = = 0) printf(“time out”); -else -{ - printf(“ret = %d/n”,ret); - if(FD_ISSET(socket, &rdfds)){ - /* 读取socket句柄里的数据 */ -}注意select函数的第一个参数,是所有加入集合的句柄值的最大那个那个值还要加1.比如我们创建了3个句柄; -``` - - - -#### poll实现 - -poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。 - -#### epoll原理 - -https://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html - -```c -#include -int epoll_create(int size); - -int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); - -int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); -``` - -**epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:** - -LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。 - -ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。 - -ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候, - -必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。 - -Epoll ET模型下,为什么每次EPOLLIN事件都会带一次EPOLLOUT事件: https://bbs.csdn.net/topics/390630226 - - - -#### udp套接字 - - [ref1](http://blog.csdn.net/chenhanzhun/article/details/41914029) - - [ref1](https://www.cnblogs.com/bleopard/p/4004916.html) - -```c -#include - -ssize_t sendto(int sockfd, void *buff, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t addrlen); - -ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *addr, socklen_t *addrlen); -``` - - - -### 网络套接字 - -#### udp原理与套接字 - -udp服务端: - -```c -sockListener=socket(AF_INET,SOCK_DGRAM,0) - -bind(sockListener,(struct sockaddr*)&addrListener,sizeof(addrListener)) - -nMsgLen=recvfrom(sockListener,szBuf,1024,0,(struct sockaddr*)&addrClient,&addrLen) -``` - -udp客户端 - -```c -sockClient=socket(AF_INET,SOCK_DGRAM,0); -bind(sockClient,(struct sockaddr*)&addrLocal,sizeof(addrLocal)) -FD_ZERO(&setHold); -FD_SET(STDIN_FILENO,&setHold); -FD_SET(sockClient,&setHold); -cout<<"you can type in sentences any time"< 4字节单位- 首部长度单位 1字节单位-总长度单位 8字节单位-片偏移单位 - -### STL容器 - -#### vector与list - -1.vector数据结构 - -vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。 - -因此能高效的进行随机存取,时间复杂度为o(1); - -但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。 - -另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝。 - - 2.list数据结构 - -list是由双向链表实现的,因此内存空间是不连续的。 - -只能通过指针访问数据,所以list的随机存取非常没有效率,时间复杂度为o(n); - -但由于链表的特点,能高效地进行插入和删除。 - -#### [Vector动态内存分配]( https://blog.csdn.net/xnmc2014/article/details/86748138) - -这个问题其实很简单,在调用push_back时,若当前容量已经不能够放入心得元素(capacity=size),那么vector会重新申请一块内存,把之前的内存里的元素拷贝到新的内存当中,然后把push_back的元素拷贝到新的内存中,最后要析构原有的vector并释放原有的内存。所以说这个过程的效率是极低的,为了避免频繁的分配内存,C++每次申请内存都会成倍的增长,例如之前是4,那么重新申请后就是8,以此类推。当然不一定是成倍增长,比如在我的编译器环境下实测是0.5倍增长,之前是4,重新申请后就是6 - -[TinySTL]( https://github.com/zouxiaohang/TinySTL/tree/master/TinySTL) - - - -### 预处理指令 - -\#pragma once 防止头文件重复引用 - -一字节对齐 - -\#pragma pack(push, 1) - -\#pragma pack(pop) - - - -### class面向对象 - -#### 类继承 - -class LayerManager : public ILayerManager{}; - - - -#### 为什么析构函数要是虚函数? - -基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。所以,将析构函数声明为虚函数是十分必要的。 - - - -#### 覆盖虚函数机制 - -在某些情况下,希望覆盖虚函数机制并强制函数调用使用虚函数的特定版 - -本,这里可以使用作用域操作符: - -```c++ -Item_base *baseP = &derived; - -// calls version from the base class regardless of the dynamic type of baseP -double d = baseP->Item_base::net_price(42); -``` - -这段代码强制将 net_price 调用确定为 Item_base 中定义的版本,该调用 - -将在编译时确定。**只有成员函数中的代码才应该使用作用域操作符覆盖虚函数机制。** - -**为什么会希望覆盖虚函数机制?最常见的理由是为了派生类虚函数调用基类中的版本。**在这种情况下,基类版本可以完成继承层次中所有类型的公共任务,而每个派生类型只添加自己的特殊工作。 - -例如,可以定义一个具有虚操作的 Camera 类层次。Camera 类中的 display函数可以显示所有的公共信息,派生类(如 PerspectiveCamera)可能既需要显示公共信息又需要显示自己的独特信息。可以显式调用 Camera 版本以显示公共信息,而不是在 PerspectiveCamera 的 display 实现中复制 Camera 的操作。 - -在这种情况下,已经确切知道调用哪个实例,因此,不需要通过虚函数机制。派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。 - - - -#### 名字冲突与继承 - -虽然可以直接访问基类成员,就像它是派生类成员一样,但是成员保留了它 - -的基类成员资格。一般我们并不关心是哪个实际类包含成员,通常只在基类和派 - -生类共享同一名字时才需要注意。 - -与基类成员同名的派生类成员将屏蔽对基类成员的直接访问。 - -```c -struct Base -{ - Base(): mem(0) { } - protected: - int mem; -}; - -struct Derived : Base -{ - Derived(int i): mem(i) { } // initializes Derived::mem - int get_mem() { return mem; } // returns Derived::mem - protected: - int mem; // hides mem in the base -}; - -get_mem 中对 mem 的引用被确定为使用 Derived 中的名字。如果编写如下代码: -Derived d(42); -cout << d.get_mem() << endl; // prints 42 -``` - -则输出将是 42。 - -使用作用域操作符访问被屏蔽成员 - -可以使用作用域操作符访问被屏蔽的基类成员: - -```c -struct Derived : Base -{ - int get_base_mem() { return Base::mem; } -}; -``` - -作用域操作符指示编译器在 Base 中查找 mem。 - -设计派生类时,只要可能,最好避免与基类数据成员的名字相同 - - - -#### 类成员函数的重载、覆盖和隐藏区别? - -a.成员函数被重载的特征: - -(1)相同的范围(在同一个类中); - -(2)函数名字相同; - -(3)参数不同; - -(4)virtual 关键字可有可无。 - -b.覆盖是指派生类函数覆盖基类函数,特征是: - -(1)不同的范围(分别位于派生类与基类); - -(2)函数名字相同; - -(3)参数相同; - -(4)基类函数必须有virtual 关键字。 - -c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下: - -(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆,仅同名就可以)。 - -(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) - - - -#### 纯虚函数 - -```c -class Disc_item : public Item_base - -{ - public: - double net_price(std::size_t) const = 0; -}; -``` - -含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作 - -为抽象基类的派生类的对象的组成部分,甚至不能创建抽象类型Disc_item的对象。 - - - -### 模板编程 - -#### 函数模板 - -```c -template -int compare(const T &v1, const T &v2) -{ - if (v1 < v2) return -1; - if (v2 < v1) return 1; - return 0; -} - -``` - -使用compare(1, 2) - -#### 类模板 - -```c -template class Queue - -{ - -public: - - Queue (); // default constructor - Type &front (); // return element from head of Queue - const Type &front () const; - void push (const Type &); // add element to back of Queue - void pop(); // remove element from head of Queue - bool empty() const; // true if no elements in the Queue - private: - // ... -}; -``` - -使用Queue qi; - - - -### 操作符重载 - -#### 输出操作符 - -输出操作符通常是非成员函数,定义成类的友元 - -```c -friend ostream& operator<<(ostream& out, const Sales_item& s) -{ - out << s.isbn << "\t" << s.units_sold << "\t" - << s.revenue << "\t" << s.avg_price(); - return out; -} -``` - -#### 算术和关系操作 - -算术和关系操作符定义为非成员函数 - - 为了与内置操作符保持一致,加法返回一个右值,而不是一个引用。 - -```c -Sales_item operator+(const Sales_item& lhs, const Sales_item& rhs) - -{ - - Sales_item ret(lhs); // copy lhs into a local object that we'll - ret += rhs; // add in the contents of rhs - return ret; // return ret by value -} - -int operator<(const TableIndex2D& right) const; - -friend bool operator== (const UEContext& info1,const UEContext& info2) const -{ - if(info1.ContextID != info2.ContextID) return false; - return true; -} - -friend bool operator!= (const UEContext& info1,const UEContext& info2) const -{ - return !(info1 == info2); -} -``` - -### 复制控制 - -**包括,一个拷贝构造函数,一个赋值运算符,一个析构函数,一对取址运算符** - -如果你这么写:`class Empty{};` - -和你这么写是一样的: - -```c -class Empty -{ - public: - Empty(); // 缺省构造函数 - Empty(const Empty& rhs); // 拷贝构造函数 - ~Empty(); // 析构函数 ---- 是否 - // 为虚函数看下文说明 - Empty& operator=(const Empty& rhs); // 赋值运算符 - Empty* operator&(); // 取址运算符 - const Empty* operator&() const; -}; - -Empty(const Empty& rhs) -{ - a = rhs.a -} -``` - - - -类赋值操作符必须是类的成员,以便编译器可以知道是否需要合成一个, 赋值必须返回对 *this 的引用。 - -一般而言,赋值操作符与复合赋值操作符应返回操作符的引用 - -```c -Guti& Guti::operator=( const Guti& rhs ) -{ - mtmsi_m = rhs.mtmsi_m; - mmeCode_m = rhs.mmeCode_m; - mmeGid_m = rhs.mmeGid_m; - plmnId_m = rhs.plmnId_m; - return *this; -}; - -注意,检查对自己赋值的情况 -c& c::operator=(const c& rhs) -{ - // 检查对自己赋值的情况 - if (this == &rhs) return *this; - ... -} -``` - - - -### 构造函数初始化式 - -初始化const对象和引用对象的唯一机会。P389 C++ Primer 5th - - - -### 协议 - -#### RTP/RTSP/RTCP - -RTP协议RFC1889和RFC3550 G711 PCMU - -#### HTTP - - - -### Linux基础 - -Linux shell之数组:https://www.cnblogs.com/Joke-Shi/p/5705856.html - -Linux expr命令:http://www.runoob.com/linux/linux-comm-expr.html - -shell中变量类型:local,global,export关键字: https://www.cnblogs.com/kaishirenshi/p/10274179.html - -Linux let 命令:http://www.runoob.com/linux/linux-comm-let.html - -vim修改tab成4个空格写python: http://www.cnblogs.com/wi100sh/p/4938996.html - -python判断文件是否存在的几种方法: https://www.cnblogs.com/jhao/p/7243043.html - -python--文件操作删除某行: https://www.cnblogs.com/nopnog/p/7026390.html - -pytho3字典遍历的几种操作: https://www.jb51.net/article/138414.htm - - - -chmod - -命令名称: chmod - -执行权限: 所有用户 - -功能描述: 改变文件或目录权限 - -语法: 第一种方法 chmod [{ugoa}{+-=}{rwx}] [文件或目录] - -​ 备注: u:所有者 g:所属组 o:其他人 a:所有人 - -​ +:为用户增加权限 -:为用户减少权限 =:为用户赋予权限 - -​ r:读权限 w:写权限 x:执行权限 - - - -​ 第二种方法 chmod -R [mode=421] [文件或目录] ←(这种方法用的比较多) - -​ 备注: r:4 w:2 x:1 - -​ r为读权限,可以用4来表示, - -​ w为写权限,可以用2来表示, - -​ x为执行权限,可以用1来表示。 - - - -### new操作 - -动态分配数组int *pia = new int[10]; // array of 10 uninitialized ints - -释放分配的数组 delete [] pia; - -#### new数组 - -```c -int *arr = new int[1024] -delte [] a -# 堆上new 对象 -class MyClass -{ - MyClass(int a) {}; - int empty() {return 0;}; -}; - -MyClass *p = new MyClass(1); -delete p; - -# 栈上分配 对象 -MyClass test(1); -``` - - - -#### 放置式new - -区分以下几种操作符号: - -new operator-普通的new关键字 - -operator new-仅仅申请内存返回void* - -placement new-在指定内存调用构造函数初始化类 - -new [] operator-如果是类对象,会在首部多申请4字节内存用于保存对象个数 - - - -深入探究 new 和 delete https://blog.csdn.net/codedoctor/article/details/76187567 - -当我们使用关键字new在堆上动态创建一个对象A时,比如 A* p = new A(),它实际上做了三件事: - -向堆上申请一块内存空间(做够容纳对象A大小的数据)(operator new) - -调用构造函数 (调用A的构造函数(如果A有的话))(placement new) - -返回正确的指针 - -当然,如果我们创建的是简单类型的变量,那么第二步会被省略。 - - - -当我们delete的时候也是如此,比如我们delete p 的时候,其行为如下: - -定位到指针p所指向的内存空间,然后根据其类型,调用其自带的析构函数(内置类型不用) - -然后释放其内存空间(将这块内存空间标志为可用,然后还给操作系统) - -将指针标记为无效(指向NULL) - - - -https://blog.csdn.net/rain_qingtian/article/details/14225211 - -```c - - -void* p=::operator new (sizeof(Buffer)); //创建一块内存;冒号表示全局的new -Buffer* bp= start_cast(p); //指针进行装换 -Buffer* buf3=new(bp) Buffer(128); //把bp指针指向的内存租借buf3, -buf3->put('c'); -buf3->~Buffer(); //这里析够函数要显示调用 -::operator delete(p); -``` - - - -[放置new构造对象数组](https://bbs.csdn.net/topics/392271390) - -![放置new对象数组](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E6%94%BE%E7%BD%AEnew%E5%AF%B9%E8%B1%A1.png) - - - - - -在栈上分配类内存: https://www.cnblogs.com/weekbo/p/8533368.html - -new与malloc区别 - -b. new和malloc最大区别: new会调用类的构造函数,malloc不会; - -c. delete和free同理;new/delete是运算符,malloc/free函数。所以new/delete效率应该会高点。 - - - -### [Linux IPC机制汇总](https://www.cnblogs.com/Jimmy1988/p/7744659.html) - -#### 管道 - -```c - #include - -无名管道: int pipe(int pipedes[2]) - -有名管道:int mkfifo(const char *pathname, mode_t mode) -``` - - - -#### 消息队列 - -```c -#include - -int msgget(key_t key, int msgflg) //创建 - -int msgctl(int msqid, int cmd, struct msqid_ds *buf) //设置/获取消息队列的属性值 - -int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) //发送消息到消息队列(添加到尾端) - -ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) //接收消息 -``` - - - -#### 共享内存 - -```c -#include - -int shmget(key_t key, size_t size, int shmflg) //创建一个共享内存空间 - -int shmctl(int shmid, int cmd, struct shmid_ds *buf) //对共享内存进程操作,包括:读取/设置状态,删除操作 - -void *shmat(int shmid, const void *shmaddr, int shmflg) //将共享内存空间挂载到进程中 - -int shmdt(const void *shmaddr) //将进程与共享内存空间分离 **(****只是与共享内存不再有联系,并没有删除共享内存****)** -``` - - - -#### 信号 - -` #include` - - - -### 手动实现strcpy - -```c -char *strcpy(char *strDest, const char *strSrc) -{ - if ( strDest == NULL || strSrc == NULL) - return NULL ; - if ( strDest == strSrc) - return strDest ; - char *tempptr = strDest ; - while( (*strDest++ = *strSrc++) != ‘/0’) - return tempptr ; -} -``` - - - -### C++对象内存布局 - -这部分详细内容可以参考[深度探索C++对象模型](https://book.douban.com/subject/10427315/) - -#### 虚函数多态机制 - -通过虚表指针访问虚成员函数,对普通成员函数的访问区别于虚成员函数。具体如下: - -virtual member function虚成员函数normalize()的调用实际上转换成: - -(*ptr->vpter[1])(ptr) - -![成员虚函数指针](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E6%88%90%E5%91%98%E8%99%9A%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88.png) - -函数指针也有差别,下面第一个是普通函数指针或者static member function。第二个是non-static member function成员函数指针。 - -![成员函数指针](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88.png) - - - -#### 不同继承层次的对象内存布局 - -##### 单一继承 - -![单一继承](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E5%8D%95%E4%B8%80%E7%BB%A7%E6%89%BF.png) - -![单一继承1](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E5%8D%95%E4%B8%80%E7%BB%A7%E6%89%BF1.png) - - - -##### 多重继承 - -![多重继承](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E5%A4%9A%E9%87%8D%E7%BB%A7%E6%89%BF.png) - -![多重继承1](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E5%A4%9A%E9%87%8D%E7%BB%A7%E6%89%BF1.png) - -![多重继承2](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2019-12-27-cpp_reference/%E5%A4%9A%E9%87%8D%E7%BB%A7%E6%89%BF2.png) - - - -### 参考 - -[常见C++笔试题](http://blog.csdn.net/dongfengsun/article/details/1541926) \ No newline at end of file diff --git a/_posts/2019-6-12-Play_with_VsCode_MarkDown_preview.md b/_posts/2019-6-12-Play_with_VsCode_MarkDown_preview.md deleted file mode 100644 index 7b8b26a..0000000 --- a/_posts/2019-6-12-Play_with_VsCode_MarkDown_preview.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: post -title: "玩转vscode支持Markdown预览" -date: 2019-6-12 -tags: [markdown,vscode] -comments: true -author: lemonchann ---- - -Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。 - - - -### Markdown - -Markdown具有一系列衍生版本,用于扩展Markdown的功能(如表格、脚注、内嵌HTML等等),这些功能原初的Markdown尚不具备,它们能让Markdown转换成更多的格式,例如LaTeX,Docbook。Markdown增强版中比较有名的有Markdown Extra、MultiMarkdown、 Maruku等。这些衍生版本要么基于工具,如Pandoc;要么基于网站,如GitHub和Wikipedia,在语法上基本兼容,但在一些语法和渲染效果上有改动。来自[360百科](https://baike.so.com/doc/6949586-7171987.html) - -### VsCode -强大地自定义功能,已经成为程序员最爱编辑器。 -Microsoft在2015年4月30日Build 开发者大会上正式宣布了 Visual Studio Code 项目:一个运行于 Mac OS X、Windows和Linux之上的,针对于编写现代 Web 和云应用的跨平台源代码编辑器。 -该编辑器也集成了所有一款现代编辑器所应该具备的特性,包括语法高亮(syntax high lighting),可定制的热键绑定(customizable keyboard bindings),括号匹配(bracket matching)以及代码片段收集(snippets)。Somasegar 也告诉笔者这款编辑器也拥有对 Git 的开箱即用的支持。引用[360百科](https://baike.so.com/doc/24428308-25261478.html) - -### MarkDown遇上VsCode -#### 有两种方法预览markdown渲染效果 -* Ctrl+Shift+P 打开命令框输入Markdown即可匹配到一系列的MarkDown命令,选择其中的打开预览或打开侧边预览 -* 直接使用快捷键。打开侧边预览 -> Ctrl+K 松开接 V -* 打开预览 ->Ctrl+shift V - diff --git a/_posts/2020-1-28-redis_distributed_locks.md b/_posts/2020-1-28-redis_distributed_locks.md deleted file mode 100644 index 6031cc2..0000000 --- a/_posts/2020-1-28-redis_distributed_locks.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -layout: post -title: "redis分布式锁3种实现方式对比分析总结" -date: 2020-1-28 -tags: [后台开发] -comments: true -author: lemonchann ---- - -大家春节在家抢红包玩的不亦乐乎,抢红包服务看起来非常简单,实际上要做好这个服务,特别是money相关服务是不允许出错的,想想看每个红包的数字都是真金白银,要求服务的鲁棒性非常高,背后包含着很多后台服务技术细节。 - - - -## 什么是锁 - -后台开发中锁的概念是「实现多个进程或线程互斥的访问共享资源的一种机制」,这里的计算机术语我举个栗子你就能理解: - -> 小王家只有卧室一台电视机。小王他爸喜欢看篮球NBA,小王他妈喜欢追综艺,如果小王他爸妈一起抢着看就会打架谁都看不好,这就是「死锁」。 -> -> 怎么办?小王他爸每次进入房间看电视第一件事就是把房门锁上,同样的小王他妈每次进房间看综艺第一件事也是把房门锁上,这就是「加锁」。 - - - -在计算机中公共资源可以是一块公共的内存,或者是一个公共的文件,对于这类共享资源的访问都是需要「加锁」保证各个进程或线程的资源访问互相不干扰。 - - - -## 什么是分布式锁 - -分布式锁是在分布式系统中提出的概念,所谓分布式是指由很多功能对等的节点,提供相同的服务,各个节点如果需要访问「共享资源」,为了保证数据一致性也需要「加锁」,这个锁可以放在「公共存储数据库」,访问共享资源之前先去公共存储数据库拿锁,拿到锁才能访问共享资源。 - -还是拿上面的小王来举例子: - -> 现在小王的村里只有一个电视(小王村真穷),现在这个电视不是属于小王家,整个村的人都看这一个电视,并且要求一家在看的时候其他家不能看(这是看的啥电视),以前小王家的锁不能锁村里的电视,那怎么办呢? -> -> 村里每个家庭就是一个「分布式节点」,一个解决方案是把电视放在村长家「公共存储数据库」,各家轮流去村长家看电视,并且在进去看的时候让村长关门「加锁」,这就是分布式锁。 - - - -## 分布式锁实现 - -今天就来说说其中一个技术细节,也是在我另一篇文章[Linux后台开发C++学习路线技能加点](https://zhuanlan.zhihu.com/p/102048769)中提到但没展开讲的,高并发服务编程中的**redis分布式锁**。 - -这里罗列出**3种redis实现的分布式锁**,并分别对比说明各自特点。 - - - -## Redis单实例分布式锁 - -### 实现一: SETNX实现的分布式锁 - -setnx用法参考redis[官方文档](https://redis.io/commands/setnx) - -#### 语法 - -`SETNX key value` - -将`key`设置值为`value`,如果`key`不存在,这种情况下等同SET命令。 当`key`存在时,什么也不做。`SETNX`是”**SET** if **N**ot e**X**ists”的简写。 - -返回值: - -- 1 设置key成功 -- 0 设置key失败 - -#### 加锁步骤 - -1. ```SETNX lock.foo ``` - - 如果客户端获得锁,`SETNX`返回`1`,加锁成功。 - - 如果`SETNX`返回`0`,那么该键已经被其他的客户端锁定。 - -2. 接上一步,`SETNX`返回`0`加锁失败,此时,调用`GET lock.foo`获取时间戳检查该锁是否已经过期: - - - 如果没有过期,则休眠一会重试。 - - - 如果已经过期,则可以获取该锁。具体的:调用`GETSET lock.foo `基于当前时间设置新的过期时间。 - - **注意**: 这里设置的时候因为在`SETNX`与`GETSET`之间有个窗口期,在这期间锁可能已被其他客户端抢去,所以这里需要判断`GETSET`的返回值,他的返回值是SET之前旧的时间戳: - - - 若旧的时间戳已过期,则表示加锁成功。 - - 若旧的时间戳还未过期(说明被其他客户端抢去并设置了时间戳),代表加锁失败,需要等待重试。 - -#### 解锁步骤 - -解锁相对简单,只需`GET lock.foo`时间戳,判断是否过期,过期就调用删除`DEL lock.foo` - - - -### 实现二:SET实现的分布式锁 - -set用法参考[官方文档](https://redis.io/commands/set) - -#### 语法 - -`SET key value [EX seconds|PX milliseconds] [NX|XX]` - -将键`key`设定为指定的“字符串”值。如果 `key` 已经保存了一个值,那么这个操作会直接覆盖原来的值,并且忽略原始类型。当`set`命令执行成功之后,之前设置的过期时间都将失效。 - -从2.6.12版本开始,redis为`SET`命令增加了一系列选项: - -- `EX` *seconds* – Set the specified expire time, in seconds. -- `PX` *milliseconds* – Set the specified expire time, in milliseconds. -- `NX` – Only set the key if it does not already exist. -- `XX` – Only set the key if it already exist. -- `EX` *seconds* – 设置键key的过期时间,单位时秒 -- `PX` *milliseconds* – 设置键key的过期时间,单位是毫秒 -- `NX` – 只有键key不存在的时候才会设置key的值 -- `XX` – 只有键key存在的时候才会设置key的值 - -版本\>= 6.0 - -- `KEEPTTL` -- 保持 key 之前的有效时间TTL - -#### 加锁步骤 - -一条命令即可加锁: `SET resource_name my_random_value NX PX 30000` - -The command will set the key only if it does not already exist (NX option), with an expire of 30000 milliseconds (PX option). The key is set to a value “my*random*value”. This value must be unique across all clients and all lock requests. - -这个命令只有当`key` 对应的键不存在resource_name时(NX选项的作用)才生效,同时设置30000毫秒的超时,成功设置其值为my_random_value,这是个在所有redis客户端加锁请求中全局唯一的随机值。 - -#### 解锁步骤 - -解锁时需要确保my_random_value和加锁的时候一致。下面的Lua脚本可以完成 - -```lau -if redis.call("get",KEYS[1]) == ARGV[1] then - return redis.call("del",KEYS[1]) -else - return 0 -end -``` - -这段Lua脚本在执行的时候要把前面的`my_random_value`作为`ARGV[1]`的值传进去,把`resource_name`作为`KEYS[1]`的值传进去。释放锁其实包含三步操作:’GET’、判断和’DEL’,用Lua脚本来实现能保证这三步的原子性。 - - - -## Redis集群分布式锁 - - - -### 实现三:Redlock - -前面两种分布式锁的实现都是针对单redis master实例,既不是有互为备份的slave节点也不是多master集群,如果是redis集群,每个redis master节点都是独立存储,这种场景用前面两种加锁策略有锁的安全性问题。 - -比如下面这种场景: - -> 1. 客户端1从Master获取了锁。 -> 2. Master宕机了,存储锁的key还没有来得及同步到Slave上。 -> 3. Slave升级为Master。 -> 4. 客户端2从新的Master获取到了对应同一个资源的锁。 -> -> 于是,客户端1和客户端2同时持有了同一个资源的锁。锁的安全性被打破。 - -针对这种多redis服务实例的场景,redis作者antirez设计了**Redlock** (Distributed locks with Redis)算法,就是我们接下来介绍的。 - -### 加锁步骤 - -**集群加锁的总体思想是尝试锁住所有节点,当有一半以上节点被锁住就代表加锁成功。集群部署你的数据可能保存在任何一个redis服务节点上,一旦加锁必须确保集群内任意节点被锁住,否则也就失去了加锁的意义。** - -具体的: - -1. 获取当前时间(毫秒数)。 -2. 按顺序依次向N个Redis节点执行**获取锁**的操作。这个获取操作跟前面基于单Redis节点的**获取锁**的过程相同,包含随机字符串`my_random_value`,也包含过期时间(比如`PX 30000`,即锁的有效时间)。为了保证在某个Redis节点不可用的时候算法能够继续运行,这个**获取锁**的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个Redis节点获取锁失败以后,应该立即尝试下一个Redis节点。这里的失败,应该包含任何类型的失败,比如该Redis节点不可用,或者该Redis节点上的锁已经被其它客户端持有(注:Redlock原文中这里只提到了Redis节点不可用的情况,但也应该包含其它的失败情况)。 -3. 计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。 -4. 如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。 -5. 如果最终获取锁失败了(可能由于获取到锁的Redis节点个数少于N/2+1,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有Redis节点发起**释放锁**的操作(即前面介绍的Redis Lua脚本)。 - -### 解锁步骤 - -客户端向所有Redis节点发起释放锁的操作,不管这些节点当时在获取锁的时候成功与否。 - -### 算法实现 - -上面描述的算法已经有现成的实现,各种语言版本。 - -- [Redlock-rb](https://github.com/antirez/redlock-rb) (Ruby implementation). There is also a [fork of Redlock-rb](https://github.com/leandromoreira/redlock-rb) that adds a gem for easy distribution and perhaps more. -- [Redlock-py](https://github.com/SPSCommerce/redlock-py) (Python implementation). -- [Aioredlock](https://github.com/joanvila/aioredlock) (Asyncio Python implementation). -- [Redlock-php](https://github.com/ronnylt/redlock-php) (PHP implementation). -- [PHPRedisMutex](https://github.com/malkusch/lock#phpredismutex) (further PHP implementation) -- [cheprasov/php-redis-lock](https://github.com/cheprasov/php-redis-lock) (PHP library for locks) -- [Redsync](https://github.com/go-redsync/redsync) (Go implementation). -- [Redisson](https://github.com/mrniko/redisson) (Java implementation). -- [Redis::DistLock](https://github.com/sbertrang/redis-distlock) (Perl implementation). -- [Redlock-cpp](https://github.com/jacket-code/redlock-cpp) (C++ implementation). -- [Redlock-cs](https://github.com/kidfashion/redlock-cs) (C#/.NET implementation). -- [RedLock.net](https://github.com/samcook/RedLock.net) (C#/.NET implementation). Includes async and lock extension support. -- [ScarletLock](https://github.com/psibernetic/scarletlock) (C# .NET implementation with configurable datastore) -- [Redlock4Net](https://github.com/LiZhenNet/Redlock4Net) (C# .NET implementation) -- [node-redlock](https://github.com/mike-marcacci/node-redlock) (NodeJS implementation). Includes support for lock extension. - -### 比如我用的C++实现 - -[源码在这](https://github.com/jacket-code/redlock-cpp) - -#### 创建分布式锁管理类CRedLock - -```c++ -CRedLock * dlm = new CRedLock(); -dlm->AddServerUrl("127.0.0.1", 5005); -dlm->AddServerUrl("127.0.0.1", 5006); -dlm->AddServerUrl("127.0.0.1", 5007); -``` - -#### 加锁并设置超时时间 - -```c++ -CLock my_lock; -bool flag = dlm->Lock("my_resource_name", 1000, my_lock); -``` - -#### 加锁并保持直到释放 - -```c++ -CLock my_lock; -bool flag = dlm->ContinueLock("my_resource_name", 1000, my_lock); -``` - -`my_resource_name`是加锁标识;`1000`是锁的有效期,单位毫秒。 - -#### 加锁失败返回false, 加锁成功返回`Lock`结构如下 - -```c++ -class CLock { -public: - int m_validityTime; => 9897.3020019531 // 当前锁可以存活的时间, 毫秒 - sds m_resource; => my_resource_name // 要锁住的资源名称 - sds m_val; => 53771bfa1e775 // 锁住资源的进程随机名字 -}; -``` - -#### 解锁 - -```c++ -dlm->Unlock(my_lock); -``` - - - -## 总结 - -综上所述,三种实现方式。 - -- 单redis实例场景,分布式锁实现一和实现二都可以,实现二更简洁推荐用实现二,用实现三也可以,但是实现三有点复杂略显笨重。 -- 多redis实例场景推荐用实现三最安全,不过实现三也不是完美无瑕,也有针对这种算法缺陷的讨论(节点宕机同步时延、时间同步假设),大家还需要根据自身业务场景灵活选择或定制自己的分布式锁。 - - - -## 参考 - -[Distributed locks with Redis](https://redis.io/topics/distlock) - -[How to do distributed locking](https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html) - -[基于Redis的分布式锁到底安全吗](http://zhangtielei.com/posts/blog-redlock-reasoning.html) \ No newline at end of file diff --git a/_posts/2020-1-29-red_packet_thinking_lock.md b/_posts/2020-1-29-red_packet_thinking_lock.md deleted file mode 100644 index 8ea4d81..0000000 --- a/_posts/2020-1-29-red_packet_thinking_lock.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -layout: post -title: "后台服务高并发编程-抢红包" -date: 2020-1-27 -tags: [后台开发] -comments: true -author: lemonchann ---- - -今年春节响应国家号召在家宅着抵抗疫情,拜年也改用微信红包,春节发了很多也抢了很多微信红包,也算支持了公司业务,想到WXG的小伙伴丰厚的年终奖我柠檬了,微信支付融入生活,抢红包已经是非常平常的事情。 - -抢红包这一简单的动作,每一次都是对红包服务后台的一次请求,在春节期间海量的服务请求下,其实是一个很典型的高并发编程模型。后台开发程序员都有一个共识:**实现一个功能很容易,难的是大量请求下提高服务性能**。 - -在程序员眼里,大家抢的不是红包,是红包后台服务的**锁** !这里的**锁**不是我们日常生活中的锁,后台服务编程中锁的概念: - -> 实现多个进程或线程互斥的访问共享资源的一种机制 - -### 今天和大家聊聊后台服务编程中的锁。 - -## 业务模型 - -为便于说明,我们简化模型,约定抢红包服务是多线程服务,抢红包操作包含以下3个步骤: - -1. 查询数据库内红包余额 -2. 扣除抢到的红包金额 -3. 更新红包余额到数据库 - -假设发了100块钱红包,1000个人1秒内同时来抢(高并发),如果不加锁是这样的情况: - -- 第一个人查余额得到100元,他在此基础上扣除抢到的假设2元,准备步骤3更新到数据库。 -- 在第一个人更新进去之前,此时剩下的人查到的余额也是100,他们各自扣除抢到的金额,准备按步骤3更新。 -- 导致最后的红包余额只记录了最后一次更新的数据。 -- 很明显,这就可能出现1000个人都抢到红包,但是红包余额还没分完的情况,这就乱了。 - -怎么解决这个问题呢? 就用到我们上面说的**加锁**来解决。 - - - -## 有哪些锁 - -实现锁的方式有很多,这里列举几种常见的分类 - -### 悲观锁 - -> 顾名思义就是悲观的做最坏打算的锁机制,占有锁期间独占资源。 - -悲观锁把抢红包这三个步骤打包成一个整体做成互斥操作,**“在我抢了没更新数据之前你别来查余额,查到也不准确”**。也可以类比数据库的**事务**来理解。 - -> **事务必须具备以下四个属性,简称ACID 属性:** -> `原子性(Atomicity)`:事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执 行,要么都不执行 -> `一致性(Consistency)`:当事务完成时,数据必须处于一致状态 -> `隔离性(Isolation)`:对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务 -> `永久性(Durability)`:事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性 - -它悲观的认为你每次去抢红包必然有其他人也同时在抢,所以你这条线程在抢的时候要独占资源,其他线程需要阻塞挂起等待你抢完才能进来抢,挂起的线程就干不了其他事了。 - -> 鲁迅先生说过,浪费CPU资源就是浪费生命! - -而一旦你抢完红包释放了锁,其他在等待中的线程又要抢占资源、抢到了还要恢复线程上下文。 - -CPU不断的切换线程上下文非常浪费服务器资源,严重的会导致不能及时处理后续抢红包请求,需要想办法提高效率,于是有了**乐观锁** - -### 乐观锁 - -> 乐观锁是对悲观锁的改进,乐观的认为加锁的时候没有竞争,乐观锁不阻塞线程。 - -一种实现乐观锁的方法是**数据库内红包余额增加版本号**,初始版本号是0,每次抢完红包版本号加1后再去更新余额,**只有更新的版本号大于数据库内的版本号才认为是合法的,予以更新;否则不予更新,线程不阻塞可以稍后重试,**避免频繁切换线程上下文。 - -乐观锁在抢红包的步骤1、2不做加锁判断,在步骤3的时候才做加锁判断版本号。 - -- 第一个人抢到版本号是0的红包,第二个人也抢到版本号是0的红包 -- 第一个人更新红包余额并设置版本号为1 -- 第二个人更新红包余额设置版本号为1的时候发现余额版本号已经为1,更新失败 -- 第二个人更新失败后,**线程不阻塞,继续处理其他抢红包抢请求**,按**一定策略重试**(超时重试、有限次数重试)第二个人的更新操作 -- 其他请求以此类推 - -可以看到,乐观锁在加锁失败的时候不挂起线程等待,避免了线程上下文频繁的切换,提高红包服务处理性能。 - -### 分布式锁 - -上面两种锁的形式都是基于对数据库的更新来做的,在大请求高并发的时候,频繁的存取数据库,尤其是乐观锁重试会对数据库产生很大的冲击,在实际生产环境要尽量减少对数据库的访问。 - -Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。也可以用redis实现**分布式锁**,与数据库交互两次:第一次获取红包余额,第二次抢完更新红包状态。抢红包和中间过程更新操作都在内存中进行,这可比数据库操作快了几个数量级,显著改善服务并发性能。 - -redis分布式锁: - -> 利用Redis的SET操作在内存中保存key-value键值对,加锁就是获取这个键值对的值,解锁就是删除这个键值对。 - -分布式锁也不阻塞线程,关于这种分布式锁的实现不在这里展开说明,参考我另一篇公众号文章,[redis分布式锁3种实现方式分析](1) - -#### 更多原创技术干货分享在我的公众号:柠檬橙学编程 欢迎关注。 - diff --git a/_posts/2020-1-4-learn_cpp.md b/_posts/2020-1-4-learn_cpp.md deleted file mode 100644 index 8e58c5c..0000000 --- a/_posts/2020-1-4-learn_cpp.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -layout: post -title: "Linux后台开发C++学习路线技能加点" -date: 2020-1-1 -tags: [c++] -comments: true -author: lemonchann ---- - -最近在知乎经常被邀请回答类似如何学习C++和C++后台开发应该具体储备哪些基础技能的问题。 - -本身我从事的的C++后台开发的工作,目前在腾讯负责社交产品相关后台开发,所以写这篇文章,分享自己的C++后台开发学习路径和点过的技能树,希望能给想从事后台开发的同学一点参考,若能帮你少走些弯路就更好。 - -工欲善其事必先利其器,好的书籍能让学习事半功倍,所以每个技能点之后我会推荐一些书,都是我读过且口碑不错的书,供参考。 - -**分享的是我的学习路径,如果你也能顺着这个学习路径认真学一遍,我想在后台开发技术上你已经有一个很不错的技术积累,加上项目练习通过大部分大厂面试是没有问题的。后续还会继续分享关于C++编程和后台开发技术,感兴趣的同学可以关注我和专栏。** - -## 计算机基础综合 - -考过CS或者软件工程研究生的同学可能对这个标题不陌生,是的,我说的就是专业课代号408的**计算机基础综合**。这门专业课包含:数据结构、计算机组成原理、计算机网路、操作系统。 - -为什么提起这门课程呢,因为基础知识太重要了!这是科班区别于培训班的最大不同,理论知识不一定马上能用于项目上,但当与人讨论起某个技术问题时你能够知道它深层次的原因,看问题的角度会更加全面和系统。 - -打个比方,你可能听过堆栈的名词,但知道它的具体结构和不同吗?学完数据结构就明白了;你知道计算机会算加减乘除,但具体是如何实现的呢?组成原理会告诉你;知道程序执行的时候怎么区分指令地址和数据地址的吗?操作系统会告诉你答案。 - -所以如果你大学不是计算机相关专业,或者是本专业但是没有完全吃透基础的话,强烈建议你务必抽时间好好学习这几门课程。 - -#### 推荐书: - -**计算机基础综合**推荐看大学的计算机专业教材就可以:数据结构、计算机组成原理、计算机网路、操作系统。 - -- 数据结构  - -> 1.教材:[《数据结构》](https://www.baidu.com/s?wd=《数据结构》&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)[严蔚敏](https://www.baidu.com/s?wd=严蔚敏&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao) 清华大学出版社  -> -> 2.辅导书:《算法与数据结构考研试题精析(第二版)》[机械工业出版社](https://www.baidu.com/s?wd=机械工业出版社&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)  - -- 计算机组成原理   - -> 教材:[《计算机组成原理》](https://www.baidu.com/s?wd=《计算机组成原理》&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)[唐朔飞](https://www.baidu.com/s?wd=唐朔飞&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao) [高等教育出版社](https://www.baidu.com/s?wd=高等教育出版社&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)  -> -> 辅导书: -> -> [《计算机组成原理考研指导》](https://www.baidu.com/s?wd=《计算机组成原理考研指导》&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)徐爱萍 清华大学出版社  -> -> 《计算机组成原理--学习指导与习题解答》[唐朔飞](https://www.baidu.com/s?wd=唐朔飞&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao) [高等教育出版社](https://www.baidu.com/s?wd=高等教育出版社&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)   - -- 操作系统  - -> 教材:[《计算机操作系统(修订版)》](https://www.baidu.com/s?wd=《计算机操作系统(修订版)》&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)汤子瀛 [西安电子科技大学出版社](https://www.baidu.com/s?wd=西安电子科技大学出版社&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao)   -> -> 辅导书:《操作系统考研辅导教程(计算机专业研究生入学考试全真题解) 》电子科技大学出版社  -> -> 《操作系统考研指导》清华大学出版社  - -- 计算机网络  - -> 教材:《计算机网络(第五版)》谢希仁 电子工业出版社   -> -> 辅导书:《计算机网络知识要点与习题解析》哈尔滨工程大学出版社  - -#### 视频教材 - -看上面的课本教程估计非常枯燥,下面是我觉得讲的不错的国内大学公开课我听过一部分,讲的都是计算机专业的基础内容,如果你没有系统的学过或者学的不好,都是非常建议刷一遍视频课的。 - -[武汉大学-数据结构 MOOC网络课程 ](https://www.icourse163.org/course/WHU-1001539003) - -[华中科技大学-计算机组成原理](https://www.icourse163.org/course/HUST-1003159001) - -[电子科技大学-计算机组成原理](https://www.icourse163.org/course/UESTC-1001543002) - -[华中科技大学-操作系统原理](https://www.icourse163.org/course/HUST-1003405007) - -[哈尔滨工业大学-计算机网络](https://www.icourse163.org/course/HIT-154005) - -**这一小节写的有点多,因为基础实在是太重要了!科班和非科班的差距不是谁学的编程语言多,也不是谁框架用的溜,本质区别是理论知识储备差别和用CS思维独立思考分析解决问题的能力。** - - - -## C++和C语法基础 - -**语法是一门语言的基础。**C++的基础语句和语法和C是很像的,最大的不同在class和异常处理机制,还有模板的应用,所以有C基础语法学起来是很快,没有C基础也没关系,啃完下面推荐的书也差不多,光说不练假把式,看完之后趁热把课后习题敲一遍并且自己编译通过才算看完。 - -#### 推荐书: - -[《C++ Primer 中文版(第 5 版)》](https://book.douban.com/subject/25708312/) 经典的入门书籍,不要拿大学教材XX强来对比,不是一个等级。 - - - -## 标准库STL学习 - -**STL提供了丰富的算法库支持和各种容器。**C++标准库提供了包括最基础的标准输入输出`iostrem`、各种容器`vector、set、string` ,熟练掌握标准库,不用重复造轮子(练手学习目的的造轮子除外)写出更C++的代码。 - -#### 推荐书: - -[《C++ Primer 中文版(第 5 版)》](https://book.douban.com/subject/25708312/) - -[《STL源码剖析》]( https://book.douban.com/subject/1110934/ ) - - - -## C++进阶 - -**学完了上面的C++基础只是会用,要用好还需要不断学习进阶。**站在巨人的肩膀上写出更健壮高效的代码,你没踩过的坑前人已经踩过一遍,关于一些语言细节和更好的编码习惯,有很多优秀的书籍可以学习。 - -#### 推荐书 - -[《Effective C++》](https://book.douban.com/subject/1842426/) 改善程序与设计的55个具体做法,非常值得一看,老手和新手的差别由此产生! - -[《More Effective C++(中文版》](https://book.douban.com/subject/5908727/) - -> 同一个作者,继Effective C++之后,Scott Meyers于1996推出这本《More Effective C++(35个改善编程与设计的有效方法)》“续集”。条款变得比较少,页数倒是多了一些,原因是这次选材比“一集”更高阶,尤其是第5章。Meyers将此章命名为技术。 - -[《Inside the C++ Object Model》](https://book.douban.com/subject/1484262/) 这本书还有中文版本,翻译质量也很高[《深度探索C++对象模型》](https://book.douban.com/subject/1091086/) - - - -## C++11新标准 - -**新标准提供了解决现有问题更优雅、更C++的实现**。现行的大部分C++软件还是C++98的标准,C++98是C++的第一个标准,经历这么多年的发展,从前你需要从Boost库(一个在C++98年代的准C++标准)获得的对C++的扩充支持的大部分功能已经纳入了C++11和甚至C++2X更新的标准当中,与时俱进拿起更先进的生产工具,工具就是效率。 - -#### 推荐书: - -[《深入理解C++11》](https://book.douban.com/subject/24738301/) - - - -## Linux系统基础和shell script - -**如今几乎所有的互联网服务都是跑在linux系统上面的。**对Linux系统一无所知那更加谈不上后台开发了,所以要先学习linux系统操作,不如文件管理,系统命令,文件系统,权限管理,系统服务等。 - -至于shell script 就类似win的批处理脚本,相信我,你在linux下干活早晚会需要它,所以趁早系统学起来。 - -#### 推荐书: - -[《鸟哥的Linux私房菜基础学习篇》](https://book.douban.com/subject/4889838/) 这个系列还有一个服务器架设篇,前期学习个人感觉没必要看 - -[《Linux Shell脚本攻略》](https://book.douban.com/subject/6889456/) - -[《Shell脚本学习指南》](https://read.douban.com/ebook/124173616/?dcs=subject-rec&dcm=douban&dct=6889456) - - - -## Linux环境高级编程 - -**普通用户只需懂系统操作,软件开发人员还要懂编程接口。**上一阶段你已经能够完成熟练操作Linux系统,知道一些常规的系统命令和服务,并且能够利用shell script写一些小工具提高日常开发效率。 - -我们的目标是星辰大海,作为软件工程师,还需要更加深入的掌握linux系统编程技巧,**学习系统编程接口、系统调用API、内存管理、进程间通信(IPC)**,这是本阶段的学习目的。 - -#### 推荐书: - -[《UNIX环境高级编程》](https://book.douban.com/subject/1788421/) 这本是linux编程必看的APUE,强烈推荐通读一遍,后续值得反复翻阅。 - -[《Linux/UNIX系统编程手册》](https://book.douban.com/subject/25809330/) 这本书和APUE有点重复,我看完APUE这本就跳着看了,平常可以看目录查阅。 - - - -## TCP/IP协议 - -目前网络通信中应用最广泛的协议就是IP TCP协议,后面Unix提供的TCP套接字也是基于协议实现,所以很有必要系统的学习 TCP/IP 协议。 - -#### 推荐书: - -大学的计算机网络教程 - -[《TCP/IP详解 卷1:协议》](https://book.douban.com/subject/1088054/) - -[《TCP/IP详解 卷2:实现》](https://book.douban.com/subject/1087767/) - -[《TCP/IP详解 卷3:TCP事务协议、HTTP、NNTP和UNIX域协议》](https://book.douban.com/subject/1058634/) - -这几本书很厚,可以先看卷1、卷3 - - - -## Linux网络编程套接字 - -在同一台机器上进程间的通信(IPC)有多种方式,可以是通过**消息队列、FIFO、共享内存**等方式。网络编程套接字是指:分布在不同机器上的程序通过系统提供的网络通信接口,跨越网络将不同机器上的进程连接起来,实现跨机器的网络通信。一般有**UDP套接字、TCP套接字、Unix Domain,当然,如果你是通信从业者对SCTP套接字肯定也不会陌生。** - -#### 推荐书: - -[《UNIX网络编程 卷1:套接字联网API(第3版)》](https://book.douban.com/subject/4859464/) - -[《UNIX网络编程 卷2:进程间通信(第2版)》](https://book.douban.com/subject/26434599/) - - - -## 数据库和存储 - -**程序运行数据都在易失性的内存中,需要持久化存储时就需要数据库。**一个后台服务系统一般来说都需要考虑数据落地和持久性存储的问题,这时就会涉及到数据库选型和应用,数据库分为关系型数据库和非关系型数据库。 - -**关系型数据库:**指采用了关系模型来组织数据的数据库,代表是MySql。 -关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织。 - -**非关系型数据库:**以键值对存储,且结构不固定,每一个元组可以有不一样的字段,每个元组可以根据需要增加一些自己的键值对,不局限于固定的结构,可以减少一些时间和空间的开销。代表有redis、memcached,腾讯内部组件ckv也是非关系型数据库。 - -#### 推荐书: - -[《SQL必知必会》](https://book.douban.com/subject/24250054/) - -[《高性能MySQL》](https://book.douban.com/subject/23008813/) - -[redis官方文档](https://redis.io/documentation) [redis中文网](http://redis.cn/) - -> 关于redis还有很多应用,比如基于redis的分布式锁的应用,高并发抢红包模型等,这个后面可以写一篇关于分布式锁的原理和应用文章。 - - - -## 算法基础 - -**计算机算法就是利用编程语言编写出计算机能理解的解决问题的方法。** - -好的算法能更简洁高效的解决问题,如今不论是校招还是社招,大厂笔试都会考察算法,即使不是为了笔试作为软件从业者也应该经常练习算法,保持手感。学习算法是学习解决问题的通用性方法有助于提高逻辑思维能力。 - -#### 学习方法 - -**就我个人经验来说,不推荐直接啃书的方式学习算法,建议看书的同时结合刷在线编程算法题的方式。** - -具体的:边看数据结构或算法导论,同时在[牛客](https://www.nowcoder.com/activity/oj)或者 [leetcode](https://leetcode-cn.com/ )上刷题,因为看书太枯燥很容易失去耐心,在线刷题的好处是你可以每天定目标,享受每个题目通过的快感,有正向反馈更容易坚持下来。 - - - -## 架构能力 - -**架构能力是利用已有知识来设计整个后台服务系统的能力。**不仅要求掌握技能的维度还要深度,需要能根据不同需求和系统约束,制定不同的设计方案。 - -这时候考虑的东西会更多,包括服务模型的设计:是多进程还是多线程甚至协程微线程,分布式还是集中式; - -存储的选型:考虑数据库选型用哪个?需要根据存储的数据特征和应用场景来区分,如果是社交应用的数据用非关系型数据库来存储可能更好,如果是电商订单类型的数据,那么用关系型数据库来存储可能更好; - -当然,还有后台系统的其他方方面面需要考虑,不一一举例了。 - -## 更多的练习 - -**说了这么多,最最重要的还是练习练习练习。**理论知识储备是必要条件,移动互联网时代大家接触到的碎片化信息太杂太乱,我个人经验,高浓度的知识精华还是需要在大师的书本中汲取,所以看书是最正确和快速的学习路径,没有捷径可走。 - -不过光看书也是不行,编程能力和技术是也是一门现代手艺活,还需要日常不断的打磨手艺,正如**一万小时定律**: - -> 人们眼中的天才之所以卓越非凡,并非天资超人一等,而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成世界级大师的必要条件。要成为某个领域的专家,需要10000小时,按比例计算就是:如果每天工作八个小时,一周工作五天,那么成为一个领域的专家至少需要五年。这就是一万小时定律。 - -怎么打磨提高编程技术能力呢?找项目,**找感兴趣的**东西用代码去实现它,兴趣是最好的老师,这点在编程和技术学习上也完全适用。 - -人们总倾向于去做快速获得的愉悦感的事情,比如打一盘游戏30分钟就能获得快感。相反,技术碎片的提高是一个长期的过程,三分钟热度肯定是难以成功的。 - -所以要用技术做自己感兴趣的东西和带趣味性的编程,比如写个爬虫小程序抓取网站数据或者写个小游戏,再或者自己造轮子给自己用,并乐此不疲的优化轮子。这样每走一步都能获得一点成就感,激励自己继续走下去,慢慢的一定会有质的飞跃。 - -## 一个网站 - -这个网站一定要告诉大家,网站就是个C++百科全书,类似Linux的man手册,平常开发查忘记了函数名或者容器用法直接搜索非常方便。 - -网址:C++参考: [cppreference](https://en.cppreference.com/w/cpp) - - - -## 待续 - -一口气写下来肯定还不够完善,文章会保持更新和修改,想到了再补充吧。感兴趣可以关注我的微信公众号 **后端技术学堂** 更多干货和有趣的技术分享。 - -**我整理了文中提到和推荐的电子书与视频教材**,都是好几年学习过程中收集的,关注微信公众号 **后端技术学堂** 回复 【**1024**】 免费分享给大家。 - - diff --git a/_posts/2020-2-12-linux_date.md b/_posts/2020-2-12-linux_date.md deleted file mode 100644 index f9b1130..0000000 --- a/_posts/2020-2-12-linux_date.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -layout: post -title: "多面手linux date命令" -date: 2020-1-27 -tags: [后台开发] -comments: true -author: lemonchann ---- - -今天给项目写了个脚本需要获取前一天的时间,本来先获取今天的然后减一下,如果是1号的话还要考虑大小月份挺复杂的,于是去查了一下手册`date`命令原生支持,喜出望外,今天就详细说说这个看起来不起眼的`date`命令。 - -使用Linux的同学应该对linux的`date`命令不会陌生,经常需要在命令行敲一下这个命令获取当前时间。然而这只是他的能力冰山一角。 - -```bash -[lemon@localhost ~]$ date -2020年 02月 12日 星期三 19:51:46 CST -``` - - - -## 常规操作 - -#### 获取时间戳,1970年1月1日0点0分0秒到现在历经的秒数 - -```bash -[lemon@localhost ~]$ date +%s -1581508426 -``` - - - -#### 时间戳还原,把刚才的秒数还原成时间字符串 - -```bash -[lemon@localhost ~]$ date -d "@1581508426" -2020年 02月 12日 星期三 19:53:46 CST -``` - - - -#### 指定的时间字符串转换成时间戳 - -```bash -[lemon@localhost ~]$ date -d '02/22/2222 07:21:22' +%s -7956832882 -#或者 -[lemon@localhost ~]$ date -d '2222-02-22 07:21:22' +"%s" -7956832882 -``` - - - -#### 格式化输出时间格式 - -```bash -[lemon@localhost ~]$ date "+%Y-%m-%d" -2020-02-12 -[lemon@localhost ~]$ date "+%H:%M:%S" -20:01:53 -[lemon@localhost ~]$ date "+%Y-%m-%d %H:%M:%S" -2020-02-12 20:02:06 -``` - -具体的格式参考man手册: - -```bash - 格式 FORMAT 控制着输出格式. 仅当选项指定为全球时间时本格式才有效。 分别解释如下: - - %% 文本的 % - - %a 当前区域的星期几的简写 (Sun..Sat) - - %A 当前区域的星期几的全称 (不同长度) (Sunday..Saturday) - - %b 当前区域的月份的简写 (Jan..Dec) - - %B 当前区域的月份的全称(变长) (January..December) - - %c 当前区域的日期和时间 (Sat Nov 04 12:02:33 EST 1989) - - %d (月份中的)几号(用两位表示) (01..31) - - %D 日期(按照 月/日期/年 格式显示) (mm/dd/yy) - - %e (月份中的)几号(去零表示) ( 1..31) - - %h 同 %b - - %H 小时(按 24 小时制显示,用两位表示) (00..23) - - %I 小时(按 12 小时制显示,用两位表示) (01..12) - - %j (一年中的)第几天(用三位表示) (001..366) - - %k 小时(按 24 小时制显示,去零显示) ( 0..23) - - %l 小时(按 12 小时制显示,去零表示) ( 1..12) - - %m 月份(用两位表示) (01..12) - - %M 分钟数(用两位表示) (00..59) - - %n 换行 - - %p 当前时间是上午 AM 还是下午 PM - - %r 时间,按 12 小时制显示 (hh:mm:ss [A/P]M) - - %s 从 1970年1月1日0点0分0秒到现在历经的秒数 (GNU扩充) - - %S 秒数(用两位表示)(00..60) - - %t 水平方向的 tab 制表符 - - %T 时间,按 24 小时制显示(hh:mm:ss) - - %U (一年中的)第几个星期,以星期天作为一周的开始(用两位表示) (00..53) - - %V (一年中的)第几个星期,以星期一作为一周的开始(用两位表示) (01..52) - - %w 用数字表示星期几 (0..6); 0 代表星期天 - - %W (一年中的)第几个星期,以星期一作为一周的开始(用两位表示) (00..53) - - %x 按照 (mm/dd/yy) 格式显示当前日期 - - %X 按照 (%H:%M:%S) 格式显示当前时间 - - %y 年的后两位数字 (00..99) - - %Y 年(用 4 位表示) (1970...) - - %z 按照 RFC-822 中指定的数字时区显示(如, -0500) (为非标准扩充) - - %Z 时区(例如, EDT (美国东部时区)), 如果不能决定是哪个时区则为空 -``` - - - -## 下面就是比较骚的操作,我今天用到了。 - - - -#### 获取相对当前时间的明天的时间 - -```bash -[lemon@localhost ~]$ date -d next-day -2020年 02月 13日 星期四 20:08:35 CST - -#你可以指定输出格式,比如 -[lemon@localhost ~]$ date -d next-day +%Y%m%d -20200213 -``` - - - -#### 获取相对于当前时间的昨天的时间 - -```bash -[lemon@localhost ~]$ date -d last-day -2020年 02月 11日 星期二 20:11:35 CST - -#你也可以指定输出格式,比如 -[lemon@localhost ~]$ date -d last-day +%Y%m%d -20200211 -``` - - - -#### 获取相对当前时间的上个月的时间 - -```bash -[lemon@localhost ~]$ date -d last-month -2020年 01月 12日 星期日 20:13:20 CST - -#同样的你也可以指定输出格式,比如 -[lemon@localhost ~]$ date -d last-month +%Y-%m-%d -2020-01-12 -``` - - - -#### 获取相对当前时间的下个月的时间 - -```bash -[lemon@localhost ~]$ date -d next-month -2020年 03月 12日 星期四 20:15:44 CST - -[lemon@localhost ~]$ date -d next-month "+%Y-%m-%d %H:%M:%S" -2020-03-12 20:15:38 -``` - - - -#### 获取相对当前时间的明年的时间 - -```bash -[lemon@localhost ~]$ date -d next-year -2021年 02月 12日 星期五 20:17:21 CST -``` - - - -#### 获取相对当前时间的上一年的时间 - -```bash -[lemon@localhost ~]$ date -d last-year -2019年 02月 12日 星期二 20:17:29 CST -``` - diff --git a/_posts/2020-2-21-sourcegraph.md b/_posts/2020-2-21-sourcegraph.md deleted file mode 100644 index 7780f97..0000000 --- a/_posts/2020-2-21-sourcegraph.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: post -title: "推荐一款github代码在线浏览神器sourcegraph" -date: 2020-1-27 -tags: [后台开发] -comments: true -author: lemonchann ---- - -程序员逛github已经是每日必须项目,看到感兴趣的项目都会点进去看一下,github全球最大的同性交友平台,这里有海量的开源代码库,作为开源代码管理平台github是非常专业的。 - -但是,你要在上面看代码就不是那么舒服了,特别是点进去每个文件夹浏览文件非常的不方便,大工程文件之间的切换有时候网页加载特别慢非常不方便。 - -推荐这款我用的这款Google浏览器插件,安装之后让在线浏览github项目源码,查找引用和定义如同在IDE看代码一样,体验如丝滑般舒爽。 - -## 安装 - -进入[Google应用商店](https://chrome.google.com/webstore/category/extensions?utm_source=chrome-ntp-icon) 搜索sourcegraph下载安装插件,如下图: - -![应用商店.png](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2020-2-21-sourcegraph/%E5%BA%94%E7%94%A8%E5%95%86%E5%BA%97.png) - -点击,**添加至Chrome**,即可在项目中使用。 - - - -## 使用 - -打开github上任意一个项目,点击项目上方的Sourcegraph图标,即可进入代码浏览界面。 - -![启动插件.png](https://i.loli.net/2020/02/21/LpWeAxXIC4hDr73.png) - -代码浏览界面的左侧是代码目录结构,就跟一般的IDE工程视图一样,你可以很轻松的在各个文件夹中查看文件,不用像在github那样来回前进后退,望着网页加载进度发呆。 - -![工程文件浏览](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2020-2-21-sourcegraph/%E5%B7%A5%E7%A8%8B%E6%96%87%E4%BB%B6%E6%B5%8F%E8%A7%88.png) - -鼠标单击相应的函数,出现的选项框可以选择跳转到定义 - -![查找定义](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2020-2-21-sourcegraph/%E6%9F%A5%E6%89%BE%E5%AE%9A%E4%B9%89.png) - -也可选择查找所有引用 - -![查找引用](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2020-2-21-sourcegraph/%E6%9F%A5%E6%89%BE%E5%BC%95%E7%94%A8.png) - - - -## 离线安装 - -鉴于有些同学由于众所周知的原因,不方便去Google应用商店下载,这里再说说离线安装的方法 - -- 进入Chrome插件中心,浏览器输入 [chrome://extensions/](chrome://extensions/) -- 打开开发者模式开关 - -![插件中心](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2020-2-21-sourcegraph/%E6%8F%92%E4%BB%B6%E4%B8%AD%E5%BF%83.png) - -- 下载我提供的插件安装包 `20.2.5.1810_0.rar` ,安装包在公众号【柠檬的编程学堂】回复【插件】获取,解压放到插件文件夹路径,比如我的路径: - - `C:\Users\替换成你的电脑用户名\AppData\Local\Google\Chrome\User Data\Default\Extensions` - -- 打开浏览器插件中心,打开 **开发者模式**,选择 **加载已解压的扩展程序**,即可完成安装。 - - ![加载扩展程序](https://github.com/lemonchann/lemonchann.github.io/raw/master/images/2020-2-21-sourcegraph/%E5%8A%A0%E8%BD%BD%E6%89%A9%E5%B1%95%E7%A8%8B%E5%BA%8F.png) - - - -以上,这款好用的插件分享给大家,愉快的在github玩耍吧! - - - diff --git a/_posts/2020-3-3-job_analyzes.md b/_posts/2020-3-3-job_analyzes.md deleted file mode 100644 index 66d0f10..0000000 --- a/_posts/2020-3-3-job_analyzes.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -layout: post -title: "我分析几个一线城市的近千份岗位招聘需求,得出应该这么准备找工作" -date: 2020-1-27 -tags: [后台开发] -comments: true -author: lemonchann ---- - -每年的三四月份是招聘高峰,也常被大家称为金三银四黄金求职期,这时候上一年的总结做完了,奖金拿到了,职场人开始谋划着年初的找工作大戏。 - -作为IT人要发挥自己的专业特长,如何让伯乐和千里马更快相遇?我利用大数据分析了北京、广州、深圳三个一线城市的C++招聘岗位信息,篇幅限制文中只拿出北京和深圳的数据展示,让我们来看看岗位的招聘现状,以及如何科学提高应聘成功率。 - -文末可以获取本次分析的高清图表,需要的同学自取。同时分享完整源码用于学习交流,若对其他岗位感兴趣也可以自行运行源码分析。 - -### 需求分析 - -通过大数据分析招聘网站发布的招聘数据,得出岗位分布区域、薪资水平、岗位关键技能需求、匹配的人才具有哪些特点、学历要求。从而帮助应聘者提高自身能力,补齐短板,有的放矢的应对校招社招,达成终极目标获得心仪的offer。 - -### 软件设计 - -数据分析是Python的强项,项目用Python实现。软件分为两大模块:数据获取 和 数据分析 - -![](https://upload-images.jianshu.io/upload_images/7842464-b9d276d4329a7762.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -### 详细实现 - -#### 数据获取 - -request库构造请求获取数据 - -```py -cookie = s.cookies -req = requests.post(self.baseurl, headers=self.header, data={'first': True, 'pn': i, 'kd':self.keyword}, params={'px': 'default', 'city': self.city, 'needAddtionalResult': 'false'}, cookies=cookie, timeout=3) -text = req.json() -``` - -数据csv格式存储 - -```py -with open(os.path.join(self.path, '招聘_关键词_{}_城市_{}.csv'.format(self.keyword, self.city)), 'w',newline='', encoding='utf-8-sig') as f: - f_csv = csv.DictWriter(f, self.csv_header) - f_csv.writeheader() - f_csv.writerows(data_list) -``` - -#### 数据分析 - -字段预处理 - -```py -df_all.rename({'职位名称': 'position'}, axis=1, inplace=True) #axis=1代表index; axis=0代表column -df_all.rename({'详细链接': 'url'}, axis=1, inplace=True) -df_all.rename({'工作地点': 'region'}, axis=1, inplace=True) -df_all.rename({'薪资': 'salary'}, axis=1, inplace=True) -df_all.rename({'公司名称': 'company'}, axis=1, inplace=True) -df_all.rename({'经验要求': 'experience'}, axis=1, inplace=True) -df_all.rename({'学历': 'edu'}, axis=1, inplace=True) -df_all.rename({'福利': 'welfare'}, axis=1, inplace=True) -df_all.rename({'职位信息': 'detail'}, axis=1, inplace=True) -df_all.drop_duplicates(inplace=True) -df_all.index = range(df_all.shape[0]) -``` - -数据图表展示 - -```py -from pyecharts.charts import Bar -regBar = Bar(init_opts=opts.InitOpts(width='1350px', height='750px')) -regBar.add_xaxis(region.index.tolist()) -regBar.add_yaxis("区域", region.values.tolist()) -regBar.set_global_opts(title_opts=opts.TitleOpts(title="工作区域分布"), - toolbox_opts=opts.ToolboxOpts(), - visualmap_opts=opts.VisualMapOpts()) - -from pyecharts.commons.utils import JsCode -shBar = Bar(init_opts=opts.InitOpts(width='1350px', height='750px')) -shBar.add_xaxis(sala_high.index.tolist()) -shBar.add_yaxis("区域", sala_high.values.tolist()) -shBar.set_series_opts(itemstyle_opts={ - "normal": { - "color": JsCode("""new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ - offset: 0, - color: 'rgba(0, 244, 255, 1)' - }, { - offset: 1, - color: 'rgba(0, 77, 167, 1)' - }], false)"""), - "barBorderRadius": [30, 30, 30, 30], - "shadowColor": 'rgb(0, 160, 221)', - }}) -shBar.set_global_opts(title_opts=opts.TitleOpts(title="最高薪资范围分布"), toolbox_opts=opts.ToolboxOpts()) - -word.add("", [*zip(key_words.words, key_words.num)], - word_size_range=[20, 200], shape='diamond') -word.set_global_opts(title_opts=opts.TitleOpts(title="岗位技能关键词云图"), - toolbox_opts=opts.ToolboxOpts()) -``` - - - -### 数据分析 - -#### 区域分布 - -C++岗位区域分布,北京 VS 深圳 -![区域对比北京-深圳](https://upload-images.jianshu.io/upload_images/7842464-16da08548475ef0b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -北京的C++岗位数量比深圳更多,首都buff加持,并且集中分布在海淀区和朝阳区这两个区域,中关村位于海淀区,还有位于海淀区西北旺镇的后厂村,腾讯、滴滴、百度、新浪、网易这些互联网巨头扎堆,自然能提供更多的岗位。 - -深圳的岗位则集中在南山区,猜测鹅厂C++大厂在南山区贡献了重大份额,第二竟然在宝安区。 - -#### 学历分布 - -C++岗位学历分布,北京 VS 深圳 -![学历对比-北京-深圳](https://upload-images.jianshu.io/upload_images/7842464-d72d4abe4d8f3f37.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -学历上两个城市的本科学历占比都是85%以上,北京岗位需求研究生占比和大专相当。可见大部分岗位本科学历即可胜任,或许能给即将毕业纠结考不考研的你一些参考。 - -如果你的学历是专科,那么需要加倍的努力,因为留给你的职位并不是很多。同时,从图表数据来看,深圳的岗位对大专生需求10%而对硕士仅占2%,或许专科生去深圳比去北京更加友好,emmm...仅供参考。 - -#### 薪资分布 - -C++岗位薪资分布,薪资单位K。 - -北京最高薪资 VS 最低薪资 -![](https://upload-images.jianshu.io/upload_images/7842464-5021c0134674ce9b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -深圳最高薪资 VS 最低薪资 -![](https://upload-images.jianshu.io/upload_images/7842464-f9432cc08fda0d2a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -薪资对比没啥好说的,大家看图说话,只想说帝都果然财大气粗。 - -#### 技能储备 - -C++岗位关键技能词云,北京 VS 深圳 -![](https://upload-images.jianshu.io/upload_images/7842464-2a5da16d61f7222a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -首先在脱离开发走上管理岗位之前,编程解决问题能力是最重要,可以看到「编程」能力在技能词云中占比最大。 - -大部分岗位要求较高的「算法、数据结构、Linux、数据库(存储)、多线程(操作系统)」这些计算机基础素养,所以不管你是在校学生准备校招或者职场老人准备跳槽,都需要储备好这些计算机基础能力,无论哪种个方向,硬实力的储备都很重要。 - -值得一提的是除去硬核技术要求外,岗位对候选人的软实力也有要求,比如更加偏爱具备「团队、协作、学习、沟通」这些能力的候选人,大家在提高技术能力的同时,也要注重这些软实力的培养。 - -一个彩蛋。Linux和window下都有C++开发岗位需求,相对而言Linux下C++开发占比更多,词云更大,如果你对这两个平台没有特殊偏爱,那么学Linux下开发大概能加大应聘成功率,毕竟岗位需求更大。 - -关注公众号「柠檬的编程学堂」回复 「分析」获取本文程序完整源码以及高清分析图表。 - -![](https://upload-images.jianshu.io/upload_images/7842464-76c150cb84224878.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - - - - - - - \ No newline at end of file diff --git a/_posts/c++/Linux C++学习路线.xmind b/_posts/c++/Linux C++学习路线.xmind deleted file mode 100644 index afb1592..0000000 Binary files a/_posts/c++/Linux C++学习路线.xmind and /dev/null differ diff --git a/_posts/c++/stl系列/readme.md b/_posts/c++/stl系列/readme.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/c++/内存布局系列/readme.md b/_posts/c++/内存布局系列/readme.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/go/go素材.png b/_posts/go/go素材.png deleted file mode 100644 index 14e512a..0000000 Binary files a/_posts/go/go素材.png and /dev/null differ diff --git a/_posts/go/readme.md b/_posts/go/readme.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/go/tour_go/01环境配置/Go插件.png b/_posts/go/tour_go/01环境配置/Go插件.png deleted file mode 100644 index 04aab2f..0000000 Binary files a/_posts/go/tour_go/01环境配置/Go插件.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/Vscode调试.png b/_posts/go/tour_go/01环境配置/Vscode调试.png deleted file mode 100644 index fd36e52..0000000 Binary files a/_posts/go/tour_go/01环境配置/Vscode调试.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/golang环境搭建.md b/_posts/go/tour_go/01环境配置/golang环境搭建.md deleted file mode 100644 index 39ba2fd..0000000 --- a/_posts/go/tour_go/01环境配置/golang环境搭建.md +++ /dev/null @@ -1,281 +0,0 @@ -前面几周陆陆续续写了一些后端技术的文章,包括数据库、微服务、内存管理等等,我比较倾向于成体系的学习,所以数据库和微服务还有后续系列文章补充。 - -最近工作上比较多的 Golang 编程,现在很多互联网公司都在转向 Golang 开发,所以打算写一写有关 Go 语言学习的系列文章,目标是从 Go 基础到进阶输出一系列文章,沉淀下这些知识同时也给大家做参考,力求做到通俗易懂,即使你是 `Golang` 小白也能看懂,如果你是老手也能温故知新。 - -本文将要和你分享 linux 下安装 Golang 环境,并且讲解如何通过配置 VSCode 远程开发调试 Golang 程序。 - -## 下载源码 - -你可以用系统自带的包管理工具比如 `yum` 或 `apt-get` 来安装Golang开发环境。不过,为了通用性,我选择通过源码的方式来安装和讲解,在官网下载源码,下载地址 https://golang.org/dl/ - -![官方下载界面](F:\github\lemonchann.github.io\_posts\go\tour_go\环境配置\官方下载界面.png) - - - -## 手动安装 - -### 解压安装 - -我这里下载下来的源码包 `go1.14.2.linux-amd64.tar.gz` 放到远程 Linux 服务器目录下。执行以下命令安装到 /usr/local 目录。 - -``` -tar -zxvf -C /usr/local/ `go1.14.2.linux-amd64.tar.gz` -``` - - - -### 创建工作空间 - -工作空间是你Go项目的「工作目录」,挑选一个合适目录,执行下面操作: - -```shell -mkdir GoPath -mkdir -p GoPath/src -mkdir -p GoPath/bin -mkdir -p GoPath/pkg -``` - -三个目录含义: - -```css - src: 源码路径(例如:.go、.c、.h、.s 等) - pkg: 编译包时,生成的.a文件存放路径 - bin: 编译生成的可执行文件路径 -``` - - - -### 配置环境变量 - -安装过程中有这么几个环境变量需要配置,先来了解一下: - -GOROOT:Go的安装路径,也就是前面我们解压到的目录 `/usr/local/go`。 - -GOBIN:Go项目的二进制文件存放目录。 - -GOPATH:Go的工作空间。前面有介绍的工作空间目录。 - -在 `/etc/profile` 文件追加以下内容完成设置。 - -```shell -export GOROOT=/usr/local/go -export GOPATH=/yourpath/GoPath # 设置你自己的GoPath路径 -export GOBIN=$GOPATH/bin -export PATH=$PATH:$GOROOT/bin # 加入到PATH环境变量 -export PATH=$PATH:$GOPATH/bin -``` - -```shell -# source /etc/profile #立即生效 -``` - -### 验证安装 - -```shell -# go version #检查版本 -# go version go1.14.2 linux/amd64 # 输出版本号 -``` - -如果看到版本信息就代表安装成功了! - -## 远程开发 - -上面我们在 Linux 环境下安装好了 Golang 开发环境,但我不想每次打开终端登录服务器编写调试程序,怎么才能在本地PC开发调试Golang程序呢? - -看过我上一篇Vscode远程开发的小伙伴应该能想到方法,我们就要用Vscode搭建Golang远程开发环境。具体的远程开发配置可以查看我的另一篇文章。 - -### Golang开发插件 - -首先安装官方推荐的 Go 开发插件,如下,点他安装。 - -![Go插件](F:\github\lemonchann.github.io\_posts\go\tour_go\环境配置\Go插件.png) - -接着还会出现如下的提示,是因为缺少其他 Go 开发相关插件,点 `install all` 全都装上就行。 - -![安装所有插件](F:\github\lemonchann.github.io\_posts\go\tour_go\环境配置\安装所有插件.png) - - - -## Hello World - -编程界有个惯例,什么语言开始学习都是从 Hello World 开始。现在,我们就用 Golang 编写第一个 `HelloWorld` 程序吧。 - -上代码: - -```go -package main // 所有Go程序从main包开始运行 - -import "fmt" // 导入fmt包 - -func main() { - fmt.Print("hello world", " i am ready to go :)\n") - fmt.Println("hello world", "i am ready to go :)") -} -``` -### 格式化 包 - -`fmt` 实现了类似 C++/C 语言的格式IO库功能。 - -`Print` 和 `Println` 都可用于打印输出,但是功能略有不同。可以看到我在`Print` 函数中,对后一个字符串加了空格和换行符,这样两个打印出来的结果是相同的。 - -### Print - -``` -func Print(a ...interface{}) (n int, err error) -``` - -Print采用默认格式将其参数格式化并写入标准输出。如果两个相邻的参数都不是字符串,会在它们的输出之间添加空格。返回写入的字节数和遇到的任何错误。 - -### Println - -``` -func Println(a ...interface{}) (n int, err error) -``` - -Println采用默认格式将其参数格式化并写入标准输出。总是会在相邻参数的输出之间添加空格并在输出结束后添加换行符。返回写入的字节数和遇到的任何错误。 - -## 调试 - -### 终端调试 - -在终端命令行源码所在目录输入`go run` 运行程序。 - -```shell - -# go run HelloWorld.go -//输出 -hello world i am ready to go :) -hello world i am ready to go :) -``` - -也可以先编译`go build` 得到可执行文件后再运行。 - -```shell -# go build HelloWorld.go -# ls -HelloWorld HelloWorld.go -# ./HelloWorld -hello world i am ready to go :) -hello world i am ready to go :) -``` - - - -### Vscode调试 - -按`F5`启动调试,编辑与调试控制台输出如下: - -![Vscode调试](F:\github\lemonchann.github.io\_posts\go\tour_go\环境配置\Vscode调试.png) - - - -## 命令行参数获取 - -命令行参数可以通过`os` 包的 `Args` 函数获取,代码如下: - -```go -package main - -import ( - "fmt" - "os" - "strconv" -) - -func main() { - // 命令行参数获取,os.Args第一个参数是程序自身 - fmt.Println(os.Args) - for idx, args := range os.Args { - fmt.Println("参数"+strconv.Itoa(idx)+":", args) - } -} -``` - -### 终端设置 - - -以下是带参数argv1 argv2 运行golang程序和输出。 -```shell -# go run basic.go argv1 argv2 - -# 输出 -[/tmp/go-build441686724/b001/exe/basic argv1 argv2] -参数0: /tmp/go-build441686724/b001/exe/basic -参数1: argv1 -参数2: argv2 -``` - - - -### VSCode设置 - -launch.json文件的 `args` 属性配置可以设置程序启动调试的参数。 - -![vscode命令行参数设置](F:\github\lemonchann.github.io\_posts\go\tour_go\环境配置\vscode命令行参数设置.png) - -设置之后,按`F5` 启动调试,就会在调试控制台输出配置的参数。 - -![vscode带参数调试输出](F:\github\lemonchann.github.io\_posts\go\tour_go\环境配置\vscode带参数调试输出.png) - - - -## 环境变量获取 - -命令行参数可以通过`os` 包的 `Getenv` 函数获取,代码如下: - -```go -package main - -import ( - "fmt" - "os" -) - -func main() { - // 获取环境变量 - fmt.Println(os.Getenv("type"), os.Getenv("name"), os.Getenv("GOROOT")) -} - -``` - - - -### VSCode设置环境变量 - -`launch.json` 文件的 `args` 属性配置可以设置 VSCode 调试的 Golang 程序环境变量。 - -设置的格式是:name:vaule 形式,注意都是字符串。 - -![vscode环境变量设置](F:\github\lemonchann.github.io\_posts\go\tour_go\环境配置\vscode环境变量设置.png) - - - -### 终端设置环境变量 - -终端的环境变量设置就是可以用 Linux 的 `export` 命令设置,之后就可以用 `os.Getenv` 函数读取。 - -比如我们最初设置 `GOROOT` 环境变量的命令 - -```export GOROOT=/usr/local/go``` - -就可以用 `os.Getenv("GOROOT") ` 读取。比较简单,这里就不多说了。 - -## 总结 - -现在,你有了一个可以远程开发调试 Golang 的环境,赶紧去写个 `hello world` 体验一下吧!今天的分享就到这,下一篇文章讲解基础语法。 - -老规矩,感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。今天的技术分享就到这里,我们下期再见。 - -**原创不易,看到这里,如果在我这有一点点收获,就动动手指「转发」和「在看」是对我持续创作的最大支持。** - - - -## Reference - - [设置GOPATH]( https://studygolang.com/articles/17598 ) - -[Visual Studio Code变量参考](https://blog.csdn.net/acktomas/article/details/102851702) - -[Golang 获取系统环境变量](https://studygolang.com/articles/3387) - -[os库获取命令行参数](https://studygolang.com/articles/21438) \ No newline at end of file diff --git a/_posts/go/tour_go/01环境配置/vscode命令行参数设置.png b/_posts/go/tour_go/01环境配置/vscode命令行参数设置.png deleted file mode 100644 index 1066157..0000000 Binary files a/_posts/go/tour_go/01环境配置/vscode命令行参数设置.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/vscode带参数调试输出.png b/_posts/go/tour_go/01环境配置/vscode带参数调试输出.png deleted file mode 100644 index 9cf1d53..0000000 Binary files a/_posts/go/tour_go/01环境配置/vscode带参数调试输出.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/vscode环境变量设置.png b/_posts/go/tour_go/01环境配置/vscode环境变量设置.png deleted file mode 100644 index 10662dc..0000000 Binary files a/_posts/go/tour_go/01环境配置/vscode环境变量设置.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/安装所有插件.png b/_posts/go/tour_go/01环境配置/安装所有插件.png deleted file mode 100644 index a8b6a93..0000000 Binary files a/_posts/go/tour_go/01环境配置/安装所有插件.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/安装目录.png b/_posts/go/tour_go/01环境配置/安装目录.png deleted file mode 100644 index b59fa92..0000000 Binary files a/_posts/go/tour_go/01环境配置/安装目录.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/官方下载界面.png b/_posts/go/tour_go/01环境配置/官方下载界面.png deleted file mode 100644 index a532dde..0000000 Binary files a/_posts/go/tour_go/01环境配置/官方下载界面.png and /dev/null differ diff --git a/_posts/go/tour_go/01环境配置/插件安装.png b/_posts/go/tour_go/01环境配置/插件安装.png deleted file mode 100644 index 23ab327..0000000 Binary files a/_posts/go/tour_go/01环境配置/插件安装.png and /dev/null differ diff --git a/_posts/go/tour_go/02基础语法/02基础语法.png b/_posts/go/tour_go/02基础语法/02基础语法.png deleted file mode 100644 index c5aea13..0000000 Binary files a/_posts/go/tour_go/02基础语法/02基础语法.png and /dev/null differ diff --git a/_posts/go/tour_go/02基础语法/包-变量-函数.md b/_posts/go/tour_go/02基础语法/包-变量-函数.md deleted file mode 100644 index 13260ac..0000000 --- a/_posts/go/tour_go/02基础语法/包-变量-函数.md +++ /dev/null @@ -1,337 +0,0 @@ -对于一般的语言使用者来说 ,20% 的语言特性就能够满足 80% 的使用需求,剩下在使用中掌握。 - -基于这一理论,Go 基础系列的文章不会刻意追求面面俱到,但该有知识点都会覆盖,目的是带你快跑赶上 Golang 这趟新车。 - -**Hurry up , Let's go !** - -## 一个好消息一个坏消息一个潜规则 - -Go 的语法类似 C 语言,你是从 C/C++ 语言过来的话学习成本很低,其他语言过来甚至没有编程基础也没关系,这门语言入门很轻松。 - -**好消息**是你的键盘得救了,在 Go 的世界里不用在每个语句后面加分号了,C 和C++ 程序员听了喜大普奔,键盘不那么容易磨损了。 - -**坏消息**带给习惯花括号换行的朋友,在 Go 中第一个花括号 `{` 不能换行写,必须写在同一行,否则编译不过! - -**潜规则**是任何在 Go 中定义的变量必须使用,如果定义了变量不使用,编译不过! - -怎么样?是不是感觉到满满的霸道总裁味道? - -其实约束多了,程序员自由发挥的空间变少,出错的概率也会大大降低,Google 的大佬们怕你犯错,操碎了心。 - -![](https://imgkr.cn-bj.ufileos.com/70b0412d-147a-4e03-827e-c5c0bccae2e4.png) - - - - -## 包 - -### 概念 - -Go 语言程序都由包构成,类似其他语言中的模块概念,主程序都从 main 包开始运行。 - -所以一个程序开头是下面的语句: - -```go -package main -``` - -在程序中也可以导入其他包,这样就可以使用其他包定义的函数或变量。 - -### 导入 - -导入包语法有多种姿势。 - -#### 导入姿势一:单独导入 - -```go -import os // 导入 os 包 -import fmt /* 导入 fmt 包*/ -``` - -**fmt 包**:包内有格式化 IO 函数,类似 C 中的 `stdio.h` 和 C++ 中的 `iostream` ,初学者必备,导它! - -**os 包** :中实现了一些 操作系统函数,不依赖平台的接口 - -**另外,关于注释,如你所见,完全就是 C 语言里的注释形式,**`//` 或 `/**/ ` 都是允许的。 - -#### 导入姿势二:分组导入 - -```go -import ( - "fmt" - "os" -) -``` - -可以把需要的包,用括号放在一起导入。 - -#### 导入姿势三:指定别名导入 - -可以在导入的时候指定导入包的别名,这样在调用包函数的时候,可以直接使用包别名。 - -```go -import f "fmt" // 用别名f 代替 fmt -f.Println("go go go") // 用 f 代替了 fmt 调用 Println 函数 -``` - - - - - -### 包导出名称约定 - -包中定义的函数或变量,如果是大写字母开头,那么它就是可以导出的,外部使用包的用户可以访问到,类似 C++ 中的 `public` 标识。相反,小写字母开头的名字外部无法使用,使用会报错。 - -```go -// 如下,Println 和 Getenv 都是大写的名字 -fmt.Println(os.Getenv("GOPATH")) -``` - - - -## 基本类型 - -#### 内置类型 - -Go 语言内建的数据类型有下面这些,其实基本上看类型名字,就差不多能知道是什么类型了。 - -```go -int int8 int16 int32 int64 -uint uint8 uint16 uint32 uint64 uintptr -float32 float64 complex128 complex64 -bool byte rune string error -``` - -平常用的最多的类型: - -`int` 代表整型,在 32 位系统上通常为 32 位,在 64 位系统上则为 64 位。 - -`string` 字符串类型 - -`bool` 布尔类型,分 `true` 和 `false` 两种值。 - - - -#### 创建类型 - -下面的语法创建一个新的类型,类似C++中的typedef语法。 - -`type 新类型名字 底层类型` - -```go -type ProgramType string // 定义新类型 ProgramType -var t1 ProgramType = "Golang" -var t2 ProgramType = "C++" -``` - -不过Go中创建的新类型即使底层类型是一致的也不能相互操作,**这样起到很好的类型隔离作用**。 - -比如下面的代码,虽然`ProgramType` 和 `CompanyType` 都是` string` 类型,但是不能相互操作,下面举例说明: - -```go -type ProgramType string // 定义新类型 ProgramType -type CompanyType string // 定义新类型 ProgramType -var t2, t2 ProgramType = "Golang", "C++" -var c1, c2 CompanyType = "Google", "Tencent" -fmt.Println(t1+t2) // 同类型相加合法 -fmt.Println(t1+c1) // 不同类型相加非法 -``` - - - -#### 类型转换 - -不像 C 中有隐式类型转换,**在 Go 中 不同类型的项之间赋值时需要显式转换,否则编译会报错**!语法上,相对于 C 语言的强制转换语法换了下括号的位置,Go 语法如下。 - -``` -T(v) // 把值 v 转换为类型 T -``` - -举例: - -```go -var varint int = 66 -var varf float32 = float32(varint) // int 转换 float32 -fmt.Printf("%T %v %T %v \n", varint, varint, varf, varf) // %T输出值的类型 %v输出值 -``` - - - -## 变量 - -### 声明 - -Go 里面的变量声明其实和 C 语言差不多,唯一的区别是把变量类型放在在变量名字后面,另外多了一个 `var` 关键字标识。 - -```go -var imVar int // 声明了一个 int 类型的 imVar 变量 -``` - -当然也可以多个同类型变量一起声明 - -```go -var imVar1, imVar2, imVar3 int // 一口气声明了三个 int 类型的变量 -``` - -或者,多个不同类型的变量声明分组一起声明 - -```go -var ( - i int - b bool - s string -) -``` - - - - - -### 初始化 - -#### 未初始化 - -未初始化的对象会被赋予**零值**,也就是默认值。 - -- 数值类型初始值 `0` -- 布尔类型初始值 `false` -- 字符串为初始值 `""`(空字符串) - - - -#### 普通初始化 - -可以声明之后单个初始化 - -```go -var imVar int -imVar = 6 -``` - -也可以声明和初始化一步到位 - -```go -var imVar0 int = 7 -``` - -还可以批量声明加初始化一步到位 - -```go -var imVar4, imVar5 int = 4, 5 -``` - -多个不同类型的变量声明和初始化可以分组同时进行,像下面这样。 - -```go -var ( - i int = 1 - b bool = false - s string = "golang" -) -``` - - - -#### 偷懒初始化 - -##### 类型自动推导 - -如果初始化式右边的值是已存在确定类型的值,可以偷懒省略变量类型,聪明的 Go 会自动推导类型。 - -```go -var imVar4, imVar5 = 4, 5 // 省略了左边的 int 类型,自动推导imVar4, imVar5是int类型 -``` - -#### - -##### 简短初始化 - -**在函数内部**,可以使用简短赋值语句` := `来代替 `var` 关键字**声明并初始化**变量。 - -```go -imVar6, imVar7 := 8, 9 // 声明并初始化了 imVar6, imVar7 -``` - - - -## 常量 - -常量用 `const` 关键字声明,并且声明之后必须紧接着赋值,常量可以是字符、字符串、布尔值或数值 ,注意:**常量不能用 `:=` 语法声明** 。 - -```go -const imCnt int = 1 // 带类型的常量定义 -const imCnt1 = 1 // 省略类型的常量定义,自动推导类型 -``` - -Go 语言内建下面几种常量 - -```go -true false iota nil // 内建常量 -``` - - - -## 函数 - -### 声明 - -函数用关键字 `func` 来声明,带参数列表,把返回值类型放在最后,下面定义了一个简单的乘法函数,带两个整型参数,返回值也是整型。 - -```go -func mult(i int, j int) int { - return i * j -} -``` - -当函数参数类型相同时,可以只写最后一个参数的类型,下面这样简写也是可以的。 - -```go -func mult(i, j int) int { - return i * j -} -``` - - - -### 返回值 - -#### 多返回值 - -函数返回值可以是一个,也可以是多个,下面的函数就返回了 `i` 的平凡和 `j` 的平方两个返回值。 - -```go -func more(i, j int) (int, int) { - return i * i, j * j -} -``` - -#### 返回值命名 - -前面例子只指定了返回值类型,可以指定返回值名称,这样更加便于理解,同时,指定的名称可在函数内使用。 - -```go -func retName(i, j int) (x, y int) { - x = i * i - y = j * j - return x, y // 可用 return 代替,表示返回所有已命名的返回值。 -} -``` - - - -## 总结 - -通过本文的学习,我们掌握了 Golang 中的几个基础概念和用法:包、基本数据类型、变量、常量、函数。这些语法基础是Golang 的下层建筑,万丈高楼平地起,本节为后续学习打下了基础。 - -感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习. - -今天的技术分享就到这里,我们下期再见。 - ------ - - - -**原创不易,不想被白票,如果在我这有收获,就动动手指点个「在看」或给个「转发」是对我持续创作的最大支持。** - - - diff --git a/_posts/go/tour_go/03控制流/Golang基础-控制流.png b/_posts/go/tour_go/03控制流/Golang基础-控制流.png deleted file mode 100644 index a7f0a73..0000000 Binary files a/_posts/go/tour_go/03控制流/Golang基础-控制流.png and /dev/null differ diff --git a/_posts/go/tour_go/03控制流/ctrlflow.md b/_posts/go/tour_go/03控制流/ctrlflow.md deleted file mode 100644 index 3cd35ce..0000000 --- a/_posts/go/tour_go/03控制流/ctrlflow.md +++ /dev/null @@ -1,275 +0,0 @@ -对于一般的语言使用者来说 ,20% 的语言特性就能够满足 80% 的使用需求,剩下在使用中掌握。基于这一理论,Go 基础系列的文章不会刻意追求面面俱到,但该有知识点都会覆盖,目的是带你快跑赶上 Golang 这趟新车。 - -控制语句是程序的灵魂,有了它们程序才能完成各种逻辑,今天我们就来学习 Go 中的各种控制语句。 - -通过本文的学习你将掌握以下知识: - -- if 条件语句 -- for 循环语句 -- switch 语句 -- defer 延迟调用 - - - -## if 条件语句 - -与大多数编程语言一样,`if` 用于条件判断,当条件表达式 `expr` 为 `true` 执行 `{}` 包裹的消息体语句,否则不执行。 - -语法是这样的: - -```go -if expr { - // some code -} -``` - -**注意:**语法上和 `c` 语言不同的是不用在条件表达式 `expr` 外带括号,和 `python` 的语法类似。 - -当然,如果想在条件不满足的时候做点啥,就可以 `if` 后带 `else` 语句。语法: - -```go -if expr { - // some code -} else { - // another code -} -``` - - - -### 不仅仅是 if - - 除了可以在 `if` 中做条件判断之外,在 Golang 中你甚至可以在 `if` 的条件表达式前执行一个简单的语句。 - -举个例子: - -```go -if x2 := 1; x2 > 10 { - fmt.Println("x2 great than 10") -} else { - fmt.Println("x2 less than 10", x2) -} -``` - -上面的例子在 `if` 语句中先声明并赋值了 `x2`,之后对 `x2` 做条件判断。 - -**注意:**此处在 `if` 内声明的变量 `x2` 作用域仅限于 if 和else 语句。 - - - -## for循环语句 - -当需要重复执行的时候需要用到循环语句,**Go 中只有 `for` 这一种循环语句。** - -标准的for循环语法: - -```go -for 初始化语句; 条件表达式; 后置语句 { - // some code -} -``` - -这种语法形式和 C 语言中 `for` 循环写法还是很像的,不同的是不用把这三个部分用 `()` 括起来。循环执行逻辑: - -- 初始化语句:初始循环时执行一次,做一些初始化工作,一般是循环变量的声明和赋值。 -- 条件表达式:在每次循环前对条件表达式求值操作,若求值结果是 `true` 则执行循环体内语句,否则不执行。 -- 后置语句:在每次循环的结尾执行,一般是做循环变量的自增操作。 - -举个例子: - -```go -sum := 0 -for i := 0; i < 10; i++ { - sum += i // i作用域只在for语句内 - fmt.Println(i, sum) -} -``` - -注意:循环变量` i` 的作用域只在 `for` 语句内,超出这个范围就不能使用了。 - - - -### while循环怎么写? - -前面说了,Golang 中只有 `for` 这一种循环语法,那有没有类似 C 语言中 `while` 循环的写法呢?答案是有的:把 `for` 语句的前后两部分省略,只留中间的「条件表达式」的 `for` 语句等价于 `while` 循环。 - -像下面这样: - -```go -sum1 := 0 -for ;sum1 < 10; { // 可以省略初始化语句和后置语句 - sum1++ - fmt.Println(sum1) -} -``` - -上面的示例没有初始化语句和后置语句,会循环执行 10 次后退出。 - -当然你要是觉得前后的分号也不想写了,也可以省略不写,上面的代码和下面是等效的: - -```go -sum1 := 0 -for sum1 < 10 { // 可以省略初始化语句和后置语句,分号也能省略 - sum1++ - fmt.Println(sum1) -} -``` - -在 Golang 中死循环可以这样写,相当于 C 语言中的 `while(true)` - -```go - for { // 死循环 - // your code - } -``` - - - -## switch 语句 - -`switch` 语句可以简化多个 `if-else` 条件判断写法,避免代码看起来杂乱。 - -可以先定义变量,然后在 `switch` 中使用这个变量。 - -```go - a := 1 - switch a { - case 1: - fmt.Println("case 1") // 不用写break 执行到这自动跳出 - case 2: - fmt.Println("case 2") - default: - fmt.Printf("unexpect case") - } -``` - -```shell -输出:case 1 -``` - - - -从 C 语言过来的朋友一定有这样的经历:经常会在 case 语句中漏掉 break 导致程序继续往下执行,从而产生奇奇怪怪的 `bug` ,这种问题在 Golang 中不复存在了。Golang 在每个 case 后面隐式提供 `break` 语句。 除非以 `fallthrough` 语句结束,否则分支会自动终止。 - -```go - switch a := 1; a { //这里有分号 - case 1: // case 无需为常量,且取值不必为整数。 - fmt.Println("case 1") // 不用写break 执行到自动跳出 除非以 fallthrough 语句结束 - fallthrough - case 2: - fmt.Println("case 2") - default: - fmt.Printf("unexpect case") - } -``` - -``` -输出: -case 1 -case 2 -``` - -还可以直接在 `switch` 中定义变量后使用,**但是要注意变量定义之后又分号**,比如下面这样: - -```go - switch b :=1; b { //注意这里有分号 - case 1: - fmt.Println("case 1") - case 2: - fmt.Println("case 2") - default: - fmt.Printf("unexpect case") - } -``` - - - -### 没有条件的switch - -没有条件的 switch 同 `switch true` 一样,只有当 `case` 中的表达式值为「真」时才执行,这种形式能简化复杂的 `if-else-if else ` 语法。 - -下面是用 `if` 来写多重条件判断,这里写的比较简单若是再多几个 `else if` 代码结构看起来会更糟糕。 - -```go - a := 1 - if a > 0 { - fmt.Println("case 1") - } else if a < 0 { - fmt.Println("case 2") - } else { - fmt.Printf("unexpect case") - } -``` - -如果用上不带条件的 `switch` 语句,写出来就会简洁很多,像下面这样。 - -```go - a := 1 - switch { // 相当于switch true - case a > 0: // 若表达式为「真」则执行 - fmt.Println("case 1") - case a < 0: - fmt.Println("case 2") - default: - fmt.Printf("unexpect case") - } -``` - - - -## defer 语句 - -`defer` 语句有延迟调用的效果。具体来说`defer`后面的函数调用会被**压入堆栈**,当外层函数返回才会对压栈的函数按后进先出顺序调用。说起来有点抽象,举个例子: - -```go -package main - -import "fmt" - -func main() { - fmt.Println("entry main") - for i := 0; i < 6; i++ { - defer fmt.Println(i) - } - fmt.Println("exit main") -} -``` - -`fmt.Println(i)` 不会每次立即执行,而是在 `main` 函数返回之后才依次调用,编译运行上述程序的输出: - -```shell -entry main -exit main //外层函数返回 -5 -4 -3 -2 -1 -0 -``` - -上面是简单的使用示例,实际使用中**`defer` 通常用来释放函数内部变量,因为它可以在外层函数 `return` 之后继续执行一些清理动作。**这在文件类操作异常处理中非常实用,比如用于释放文件描述符,我们以后会讲解这块应用,总之先记住 `defer` 延迟调用的特点。 - - - -## 总结 - -通过本文的学习,我们掌握了 Golang 中基本的控制流语句,利用这些控制语句加上一节介绍的变量等基础知识,可以构成丰富的程序逻辑,你就能用 Golang 来做一些有意思的事情了。 - -感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习. - -今天的技术分享就到这里,我们下期再见。 - ------ - - - -**创作不易,白票不是好习惯,如果有收获,动动手指点个「在看」或给个「转发」是对我持续创作的最大支持** - - - -## reference - -[golang中defer的使用规则](https://studygolang.com/articles/10167) - -[GO 匿名函数和闭包](https://segmentfault.com/a/1190000018689134) \ No newline at end of file diff --git a/_posts/go/tour_go/04复合类型/Golang基础.png b/_posts/go/tour_go/04复合类型/Golang基础.png deleted file mode 100644 index bf850bd..0000000 Binary files a/_posts/go/tour_go/04复合类型/Golang基础.png and /dev/null differ diff --git a/_posts/go/tour_go/04复合类型/moretype指针和复合类型.md b/_posts/go/tour_go/04复合类型/moretype指针和复合类型.md deleted file mode 100644 index da7bf73..0000000 --- a/_posts/go/tour_go/04复合类型/moretype指针和复合类型.md +++ /dev/null @@ -1,374 +0,0 @@ -> 对于一般的语言使用者来说 ,20% 的语言特性就能够满足 80% 的使用需求,剩下在使用中掌握。基于这一理论,Go 基础系列的文章不会刻意追求面面俱到,但该有知识点都会覆盖,目的是带你快跑赶上 Golang 这趟新车。 - -前面我们学习了 Golang 中基础数据类型,比如内置类型 `int` `string` `bool` 等,还有一些复杂一点点,但很好用的复合类型,类似 C 中的数组和 `struct`、C++ 中的 `map` ,今天我们就来学习 Go 中的复合类型。 - -通过本文的学习你将掌握以下知识: - -- 结构体 -- 指针类型 -- 数组和切片 -- 映射类型 -- 遍历切片和映射 - - - -## 结构体 - -结构体是一种聚合的数据类型,与 C 中的结构体类似,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员,看例子: - -```go -type Test struct { - a int - b int - } -``` - -语法上的不同看到了吗? 每个结构体字段之后没有分号(还记得前面文章说过 Go 会自动加分号吧)没有分号写起来还是很舒服的。 - - - -### 初始化 - -可以在定义的时候初始化 - -```go -test := Test{1, 2} // 定义结构体变量并初始化 -``` - -初始化部分结构体字段 - -```go -t2 = Test{a: 3} //指定赋值Test.a为3 Test.b隐式赋值0 -``` - -隐式初始化 - -```go -t3 = Test{} // .a .b都隐式赋值0 -``` - -多个变量可以分组一起赋值 - -```go -var ( - t1 = Test{8, 6} - t2 = Test{a: 3} //指定赋值Test.a Test.b隐式赋值0 - t3 = Test{} // .a .b都隐式赋值0 -) -``` - - - -### 访问成员 - -通过 `.` 运算来访问结构体成员,「不区分结构体类型或是结构体指针类型」都可以用 `.` 号来访问。 - -```go -fmt.Println("struct", st0.a, st0.b) // 通过 . 运算来访问结构体成员 -``` - - - -对于只声明没赋值的结构体,其内部变量被赋予零值。下面我们声明了 `st0` 但没有对其赋值,成员 a 和 b 自动赋零值。 - -```go -var st0 Test -fmt.Println("struct", st0.a, st0.b) //输出:struct 0 0 -``` - - - -## 指针 - -指针不保存实际数据的内容,而是保存了指向值的内存地址 。用 `&` 对变量取内存地址,用 `*` 来访问指向的内存,这点和 C 中的指针是一样,唯一不同的是 Go 中的指针不能运算。 - -```go - a := 3 - pa := &a // 用 `&` 对变量取内存地址 - fmt.Println("point", a, *pa) // 用 `*` 来访问指向的内存 -``` - -只声明没赋值的指针值是 `nil` ,代表空指针。 - -```go - var a0 *int // 只声明没赋值的指针是nil - if a0 == nil { - fmt.Println("point", "it is nil point") - } -``` - - - -## 数组 - - 数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。 数组可以用下标访问元素,下标从 0 开始。 - -数组声明后赋值 - -```go - var strarr [2]string // 数组声明语法 - strarr[0] = "ready" - strarr[1] = "go" -``` - -声明赋值同时完成 - -```go - intarr := [5]int{6, 8, 9, 10, 7} // 声明赋值同时完成 -``` - -对于确定初始值个数的数组,可以省略数组长度用 `...` 代替。 - -```go - intarr := [...]int{6, 8, 9, 10, 7} // 声明赋值同时完成 -``` - - - -## Slice 切片 - -切片是变长的序列,序列中每个元素都有相同的类型。`slice` 语法和数组很像,只是没有固定长度而已,「切片底层引用一个数组对象」修改切片会修改原数组。 - -通过切片可以访问数组的部分或全部元素,正因为切片长度不是固定的,因此切片比数组更加的常用。 - - - -### 声明与初始化 - -#### 常规初始化 - -简短声明并初始化切片 - -```go -s0 := []int{1, 2, 3, 4, 5, 6} // 简短声明加赋值 -``` - -声明后再初始化 - -```go -var s []int // 声明切片s -s = s0 // 用切片s0初始化切片s -``` - -声明并初始化切片 - -```go -var s00 []int = s0 // 用切片s0初始化切片s -``` - -切片的零值是 `nil` - -```go -// 切片的零值是nil 空切片长度和容量都是0 -var nilslice []int -if nilslice == nil { - fmt.Println("slice", "nilslice is nil ", len(nilslice), cap(nilslice)) -} - -``` - -#### make初始化 - -除了上述的常规初始化方法,还可以用 `make` 内置函数来创建切片 - -```go -// 内建函数make创建切片,指定切片长度和容量 -// make 函数会分配一个元素为零值的数组并返回一个引用了它的切片 -s2 := make([]int, 4, 6) //创建元素都是0的切片s2, 长度为4,容量为6 第三个参数可以省略 -fmt.Println("slice", len(s2), cap(s2), s2) -``` - - - -#### 切片长度和容量 - - 切片长度表示切片中元素的数目,可用内置函数 `len` 函数得到。 - - 容量表示切片中第一个元素到引用的底层数组结尾所包含元素个数,可用内置函数 `cap` 求得。 - -```go -s0 := []int{1, 2, 3, 4, 5, 6} // 简短声明加赋值 -len1, cap1 := len(s0), cap(s0) -len2, cap2 := len(s0[:4]), cap(s0[:4]) -len3, cap3 := len(s0[2:]), cap(s0[2:]) -fmt.Println("slice", len1, cap1, len2, cap2, len3, cap3) // 6 6 4 6 4 4 -``` - - - -#### 切片区间 - -切片区间遵循「左闭右开」原则, - -```go -s0 := [5]int{6, 8, 9, 10, 7} // 数组定义 -var slice []int = intarr[1:4] // 创建切片slice 包含数组子序列 -``` - - 默认上下界。切片下界的默认值为 0,上界默认是该切片的长度。 - -```go -fmt.Println("slice", s0[:], s0[0:], s0[:5], s0[0:5]) // 这四个切片相同 -``` - - - -### 切片append操作 - -append 函数用于在切片末尾追加新元素。 - -添加元素也分两种情况。 - -#### 添加之后长度还在原切片容量范围内 - -```go -s2 := make([]int, 4, 6) // 创建元素都是0的切片s2, 长度为4,容量为6 第三个参数可以省略 -s21 := append(s2, 1) -s22 := append(s2, 2) // append每次都是在最后添加,此时,s21 s22指向同一个底层数组;但s2不改变! -fmt.Println(s2, s21, s22) // [0 0 0 0] [0 0 0 0 2] [0 0 0 0 2] -``` - - -#### 添加元素之后长度超出原切片容量 - -此时会分配新的数组空间,并返回指向这个新分配的数组的切片。 - -下面例子中 s24 切片已经指向新分配的数组,s22 依然指向的是原来的数组空间,而 s24 已经指向了新的底层数组。 - -```go - s24 := append(s2, 1, 2, 3) - fmt.Println(s24, s22) // s24 [0 0 0 0 1 2 3] [0 0 0 0 2] -``` - -### 二维切片 - -可以定义切片的切片,类似其他语言中的二维数组用法。参考代码: - -```go - s3 := [][]int{ - {1, 1, 1}, - {2, 2, 2}, - } - fmt.Println(s3, s3[0], len(s3), cap(s3)) // 输出: [[1 1 1] [2 2 2]] [1 1 1] 2 2 -``` - - - -## map 映射类型 - -在 Go 中 `map` 是键值对类型,代表 `key` 和` value` 的映射关系,一个 map 就是一个哈希表的引用 。 - -### 定义和初始化 - -下面这样定义并初始化一个 map 变量 - -```go - m0 := map[int]string{ - 0: "0", - 1: "1", - } -``` - -也可以用内置 make 函数来初始化一个 map 变量,后续再向其中添加键值对。像下面这样: - -```go - m1 := make(map[int]string) // make 函数会返回给定类型的映射,并将其初始化备用 - if m1 != nil { - fmt.Println("map", "m1 is not nil", m1) // m1 不是nil - } - m1[0] = "1" - m1[1] = "2" -``` - -注意:只声明不初始化的map变量是 `nil` 映射,不能直接拿来用! - -```go - var m map[int]string // 未初始化的m零值是nil映射 - if m == nil { - fmt.Println("map", "m is nil", m) - } - //m[0] = "1" // 这句引发panic异常, 映射的零值为 nil 。nil映射既没有键,也不能添加键。 -``` - -### 元素读取 - -使用语法:`vaule= m[key]` 获取键 key 对应的元素 vaule 。 - -上面我们只用了一个变量来获取元素,其实这个操作会返回两个值,第一个返回值代表读书的元素,第二个返回值是代表键是否存在的 bool 类型,举例说明: - -```go - v, st := m1[0] // v是元素值,下标对应的元素存在st=true 否则st=false - _, st1 := m1[0] // _ 符号表示忽略第一个元素 - v1, _ := m1[0] // _ 符号表示忽略第二个元素 - fmt.Println(v, st, v1, st1, m1[2]) // m1[2]不存在,返回元素string的零值「空字符」 -``` - -### 删除元素 - -内置函数 `delete` 可以删除 map 元素,举例: - -``` -delete(m1, 1) // 删除键是 1 的元素 -``` - - - -## range 遍历 - -range 用于遍历 切片 或 映射。 - -### 数组或切片遍历 - -当使用` for` 循环和 `range` 遍历数组或切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。 - -```go -s1 := []int{1, 2, 3, 4, 5, 6} -for key, vaule := range s1 { - fmt.Println("range", key, vaule) -} - -for key := range s1 { // 只需要索引,忽略第二个变量即可 - fmt.Println("range", key) -} - -for _, vaule := range s1 { // 只需要元素值,用'_'忽略索引 - fmt.Println("range", vaule) -} -``` - - - -### map 遍历 - -当使用` for` 循环和 `range` 遍历` map` 时,每次迭代都会返回两个值。第一个值为当前元素 `key` , 第二个值是 `value`。 - -```go -m0 := map[int]string{ - 0: "0", - 1: "1", -} -fmt.Println("map", m0) - -for k, v := range m0 { // range遍历映射,返回key 和 vaule - fmt.Println("map", "m0 key:", k, "vaule:", v) -} -``` - - - -## 总结 - -通过本文的学习,我们掌握了 Golang 中复合类型的学习,这些复合类型代表的数据结构都比较常见,比如切片和数组可以用于模仿队列或堆栈,`map` 的底层实现是 `hash` 表,当然初学者可以不必在意这些底层实现,先用起来已经领先大部分观望者。 - -感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习. - -今天的技术分享就到这里,我们下期再见。 - ------ - - - -**创作不易,白票不是好习惯,如果有收获,动动手指点个「在看」或给个「转发」是对我持续创作的最大支持** - -## - diff --git a/_posts/go/tour_go/04复合类型/带你学够浪:Go语言基础系列 - 8分钟学复合类型.md b/_posts/go/tour_go/04复合类型/带你学够浪:Go语言基础系列 - 8分钟学复合类型.md deleted file mode 100644 index 62b251d..0000000 --- a/_posts/go/tour_go/04复合类型/带你学够浪:Go语言基础系列 - 8分钟学复合类型.md +++ /dev/null @@ -1,368 +0,0 @@ -> 文章每周持续更新,原创不易,「三连」让更多人看到是对我最大的肯定。可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) - -对于一般的语言使用者来说 ,20% 的语言特性就能够满足 80% 的使用需求,剩下在使用中掌握。基于这一理论,Go 基础系列的文章不会刻意追求面面俱到,但该有知识点都会覆盖,目的是带你快跑赶上 Golang 这趟新车。 - -**Hurry up , Let's go !** - -前面我们学习过 Golang 中基础数据类型,比如内置类型 `int` `string` `bool` 等,其实还有一些复杂一点点,但很好用的复合类型,类似 C 中的数组和 `struct`、C++ 中的 `map` ,今天我们就来学习 Go 中的复合类型。 - -通过本文的学习你将掌握以下知识: - -- 结构体 -- 指针类型 -- 数组和切片 -- 映射类型 - - - -## 指针 - -指针不保存实际数据的内容,而是保存了指向值的内存地址 。用 `&` 对变量取内存地址,用 `*` 来访问指向的内存。这点和 C 中的指针是一样,唯一不同的是 Go 中的指针不能运算。 - -```go - a := 3 - pa := &a // 用 `&` 对变量取内存地址 - fmt.Println("point", a, *pa) // 用 `*` 来访问指向的内存 -``` - -只声明没赋值的指针值是 `nil` ,代表空指针。 - -```go - var a0 *int // 只声明没赋值的指针是nil - if a0 == nil { - fmt.Println("point", "it is nil point") - } -``` - - - -## 结构体 - -与C中的结构体类似, 结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员,看例子: - -```go -type Test struct { - a int - b int - } -``` - -语法上的不同看到了吗? 每个结构体字段之后没有分号,没有分号写起来还是很舒服的。 - -### 初始化 - -可以在定义的时候初始化 - -```go -test := Test{1, 2} // 定义结构体变量并初始化 -``` - -初始化部分结构体字段 - -```go -t2 = Test{a: 3} //指定赋值Test.a为3 Test.b隐式赋值0 -``` - -隐式初始化 - -```go -t3 = Test{} // .a .b都隐式赋值0 -``` - -多个变量可以分组一起赋值 - -```go -var ( - t1 = Test{8, 6} - t2 = Test{a: 3} //指定赋值Test.a Test.b隐式赋值0 - t3 = Test{} // .a .b都隐式赋值0 - pt4 = &Test{8, 6} // 指针 -) -``` - - - -### 访问成员 - -通过 `.` 运算来访问结构体成员,不区分结构体类型或是结构体指针类型。 - -```go -fmt.Println("struct", st0.a, st0.b) // 通过 . 运算来访问结构体成员 -``` - - - -对于只声明没赋值的结构体,其内部变量被赋予零值,下面我们声明了 `st0` 但没有对其赋值。 - -```go -var st0 Test -fmt.Println("struct", st0.a, st0.b) //输出:struct 0 0 -``` - - - -## 数组 - - 数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。 数组可以用下标访问元素,下标从 0 开始。 - -数组声明后赋值 - -```go - var strarr [2]string // 数组声明语法 - strarr[0] = "ready" - strarr[1] = "go" -``` - -声明赋值同时完成 - -```go - intarr := [5]int{6, 8, 9, 10, 7} // 声明赋值同时完成 -``` - -对于确定初始值个数的数组,可以省略数组长度 - -```go - intarr := [...]int{6, 8, 9, 10, 7} // 声明赋值同时完成 -``` - - - -## Slice 切片 - -切片是变长的序列,序列中每个元素都有相同的类型。`slice` 语法和数组很像,只是没有固定长度而已,切片底层引用一个数组对象,修改切片会修改原数组。 - -通过切片可以访问数组的部分或全部元素,正因为切片长度不是固定的,因此切片比数组更加的常用。 - -### 声明与初始化 - -#### 常规初始化 - -简短声明并初始化切片 - -```go -s0 := []int{1, 2, 3, 4, 5, 6} // 简短声明加赋值 -``` - -声明后再初始化 - -```go -var s []int // 声明切片s -s = s0 // 用切片s0初始化切片s -``` - -声明并初始化切片 - -```go -var s00 []int = s0 // 用切片s0初始化切片s -``` - -切片的零值是 `nil` - -```go -// 切片的零值是nil 空切片长度和容量都是0 -var nilslice []int -if nilslice == nil { - fmt.Println("slice", "nilslice is nil ", len(nilslice), cap(nilslice)) -} - -``` - -#### make初始化 - -除了上述的常规初始化方法,还可以用 `make` 内置函数来创建切片 - -```go -// 内建函数make创建切片,指定切片长度和容量 -// make 函数会分配一个元素为零值的数组并返回一个引用了它的切片 -s2 := make([]int, 4, 6) //创建元素都是0的切片s2, 长度为4,容量为6 第三个参数可以省略 -fmt.Println("slice", len(s2), cap(s2), s2) -``` - - - -#### 切片长度 - - 长度表示切片中元素的数目,可用内置函数 `len` 函数得到。 - -#### 切片容量 - - 容量表示切片中第一个元素到引用的底层数组结尾所包含元素个数,可用内置函数 `cap` 求得。 - - - -#### 切片区间 - -切片区间遵循「左闭右开」原则, - -```go -s0 := [5]int{6, 8, 9, 10, 7} // 数组定义 -var slice []int = intarr[1:4] // 创建切片slice 包含数组子序列 -``` - - 默认上下界。切片下界的默认值为 0,上界默认是该切片的长度。 - -```go -fmt.Println("slice", s0[:], s0[0:], s0[:5], s0[0:5]) // 这四个切片相同 -``` - - - -### 切片append操作 - -append 函数用于在切片末尾追加新元素。 - -添加元素也分两种情况。 - -#### 添加之后长度还在原切片容量范围内 - -```go -s2 := make([]int, 4, 6) //创建元素都是0的切片s2, 长度为4,容量为6 第三个参数可以省略 -s22 := append(s2, 2) // append每次都是在最后添加,所以此时,s21 s22指向同一个底层数组 -fmt.Println(s21, s22) // [0 0 0 0 2] [0 0 0 0 2] -``` -#### 添加元素之后长度超出原切片容量 - -此时会分配新的数组空间,并返回指向这个新分配的数组的切片。 - -下面例子中 s24 切片已经指向新分配的数组,s22 依然指向的是原来的数组空间,而 s24 已经指向了新的底层数组。 - -```go - s24 := append(s2, 1, 2, 3) - fmt.Println(s24, s22) // s24 [0 0 0 0 1 2 3] [0 0 0 0 2] -``` - -### 二维切片 - -可以定义切片的切片,类似其他语言中的二维数组用法。参考代码: - -```go - s3 := [][]int{ - {1, 1, 1}, - {2, 2, 2}, - } - fmt.Println(s3, s3[0], len(s3), cap(s3)) // 输出: [[1 1 1] [2 2 2]] [1 1 1] 2 2 -``` - - - -## map 映射类型 - -在 Go 中 `map` 是键值对类型,代表 `key` 和` value` 的映射关系,一个map就是一个哈希表的引用 。 - -### 定义和初始化 - -下面这样定义并初始化一个 map 变量 - -```go - m0 := map[int]string{ - 0: "0", - 1: "1", - } -``` - -也可以用内置 make 函数来初始化一个 map 变量,后续再向其中添加键值对。像下面这样: - -```go - m1 := make(map[int]string) // make 函数会返回给定类型的映射,并将其初始化备用 - if m1 != nil { - fmt.Println("map", "m1 is not nil", m1) // m1 不是nil - } - m1[0] = "1" - m1[1] = "2" -``` - -注意:只声明不初始化的map变量是 `nil` 映射,不能直接拿来用! - -```go - var m map[int]string // 未初始化的m零值是nil映射 - if m == nil { - fmt.Println("map", "m is nil", m) - } - //m[0] = "1" // 这句引发panic异常, 映射的零值为 nil 。nil映射既没有键,也不能添加键。 -``` - -### 元素读取 - -使用语法:`vaule= m[key]` 获取键 key 对应的元素 vaule 。 - -上面我们只用了一个变量来获取元素,其实这个操作会返回两个值,第一个返回值代表读书的元素,第二个返回值是代表键是否存在的 bool 类型,举例说明: - -```go - v, st := m1[0] // v是元素值,下标对应的元素存在st=true 否则st=false - _, st1 := m1[0] // _ 符号表示忽略第一个元素 - v1, _ := m1[0] // _ 符号表示忽略第二个元素 - fmt.Println(v, st, v1, st1, m1[2]) // m1[2]不存在,返回元素string的零值「空字符」 -``` - -### 删除元素 - -内置函数 `delete` 可以删除 map 元素,举例: - -``` -delete(m1, 1) // 删除键是 1 的元素 -``` - - - -## range 遍历 - -range 用于遍历 切片 或 映射。 - -### 数组或切片遍历 - -当使用` for` 循环和 `range` 遍历数组或切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。 - -```go -s1 := []int{1, 2, 3, 4, 5, 6} -for key, vaule := range s1 { - fmt.Println("range", key, vaule) -} - -for key := range s1 { // 只需要索引,忽略第二个变量即可 - fmt.Println("range", key) -} - -for _, vaule := range s1 { // 只需要元素值,用'_'忽略索引 - fmt.Println("range", vaule) -} -``` - - - -### map 遍历 - -当使用` for` 循环和 `range` 遍历` map` 时,每次迭代都会返回两个值。第一个值为当前元素 `key` , 第二个值是 `value`。 - -```go -m0 := map[int]string{ - 0: "0", - 1: "1", -} -fmt.Println("map", m0) - -for k, v := range m0 { // range遍历映射,返回key 和 vaule - fmt.Println("map", "m0 key:", k, "vaule:", v) -} -``` - - - - - - - -## 总结 - -通过本文的学习,我们掌握了 Golang 中基本的控制流语句,利用这些控制语句加上一节介绍的变量等基础知识,可以构成丰富的程序逻辑,你就能用 Golang 来做一些有意思的事情了。 - -感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习. - -今天的技术分享就到这里,我们下期再见。 - ------ - - - -**创作不易,白票不是好习惯,如果在我这有收获,动动手指「点赞」「关注」是对我持续创作的最大支持。** - -> 可以微信搜索公众号「 后端技术学堂 」回复「资料」「1024」有我给你准备的各种编程学习资料。文章每周持续更新,我们下期见! \ No newline at end of file diff --git a/_posts/go/tour_go/05方法和接口/Golang基础-方法和接口.png b/_posts/go/tour_go/05方法和接口/Golang基础-方法和接口.png deleted file mode 100644 index 0f977e4..0000000 Binary files a/_posts/go/tour_go/05方法和接口/Golang基础-方法和接口.png and /dev/null differ diff --git a/_posts/go/tour_go/05方法和接口/method_and_if.md b/_posts/go/tour_go/05方法和接口/method_and_if.md deleted file mode 100644 index a10d263..0000000 --- a/_posts/go/tour_go/05方法和接口/method_and_if.md +++ /dev/null @@ -1,326 +0,0 @@ -> 对于一般的语言使用者来说 ,20% 的语言特性就能够满足 80% 的使用需求,剩下在使用中掌握。基于这一理论,Go 基础系列的文章不会刻意追求面面俱到,但该有知识点都会覆盖,目的是带你快跑赶上 Golang 这趟新车。 - -最近工作上和生活上的事情都很多,这篇文章计划是周末发的,但是周末太忙时间不够,同时为了保证文章质量,反复修改到现在才算完成。 - -有时候还是很想回到学校,一心只用读书睡觉打游戏的日子,成年人的世界总是被各种中断,有各种各样的事情要处理。 - - ![img](https://i04piccdn.sogoucdn.com/a4eabef05f7da82e) - -**答应大家要写完的 Go 基础系列可能会迟到,但不会缺席。今天我们来继续学习,Go 中的面向对象编程思想,包括 方法 和 接口 两大部分学习内容。** - -通过学习本文,你将了解: - -- Go 的方法定义 -- 方法和函数的区别 -- 方法传值和传指针差异 -- 什么是接口类型 -- 如何判断接口底层值类型 -- 什么是空接口 -- nil 接口 和nil 底层值 - - - -如果你使用 C++ 或 Java 这类面向对象的语言,肯定知道类 `class` 和方法 `method` 的概念,Golang 中没有` class `关键字,但有上节介绍的 `struct` 结构体提供类似功能,配合方法和接口的支持,完成面向对象的程序设计完全没有问题,下面我们就来学习下方法和接口。 - -## 方法 - -### 定义 - -方法就是一类带特殊的接收者参数的函数 ,这些特殊的参数可以是结构体也可以是结构体指针,但不能是内置类型。 - -为了便于说明,先来定义一个结构体 `Person` 包含` name `和 `age` 属性。 - -```go -type Person struct { - name string - age int -} -``` - -下面给 `Person` 定义两个方法,分别用于获取` name `和` age ` ,重点看下代码中方法的定义语法。 - -```go -func (p Person) GetName() string { - return p.name + "'s age is" -} - -func (p Person) GetAge() int { - return p.age -} -``` - - - -### 和函数定义的区别 - -看了上面的方法定义是不是觉得和函数定义有点类似,还记得函数的定义吗?为了唤起你的记忆,下面分别定义两个相同功能的函数,大家可以对比一下。 - -```go -func GetNameF(p Person) string { - return p.name + "'s age is" -} - -func GetNameF(p Person) int { - return p.age -} -``` - -除了定义上的区别,还有调用上的区别。下面示例代码演示了两种调用方式的不同,在`fmt.Println` 中前面 2 个是正常函数调用,后面 2 个是方法调用,就是用点号`.` 和括号`()` 的区别。 - -```go -p := Person{"lemon", 18} -fmt.Println(GetNameF(p), GetNameF(p), p.GetName(), p.GetAge()) -//输出 lemon's age is 18 lemon's age is 18 -``` - - - -### 修改接收者的值 - -上面我演示的方法 `GetName` 和`GetAge` 的接收者是` Person `值,这种值传递方式是没办法修改接收者内部状态的,比如你没法通过方法调用修改 `Person `的` name` 或`age `。 - -假设有个需求要修改用户年龄,我们像下面这样定义方法 `ageWriteable` ,调用该方法之后 `p` 的 `name` 属性并不会变化。 - -```go -func (p *Person) ageWriteable() int { - p.age += 10 - return p.age -} -``` - -那要怎么才能实现对 `p` 的修改呢? 没错用 `*Person` 指针类型即可实现修改。类比 `C++` 中用指针或引用来理解。 - -```go -func (p *Person) ageWriteable() int { - p.age += 10 - return p.age -} -``` - - - -### 隐式值与指针转换 - -Golang 非常的聪明,为了不让你麻烦,它能自动识别方法的实际接收者类型(指针或值),并默默的帮你做转换,以便「方法」能正确的工作。 - -还是用我们上面定义的方法举例,先来看以「值」作为接收者的方法调用。方便阅读,我把前面的定义再写一遍。 - -```go -func (p Person) GetName() string { - return p.name + "'s age is" -} -``` - -对于这个定义的方法,按下面的调用方式 `p `和 `pp` 都能调用 `GetName` 方法。 - -怎么做到的呢?原来 `pp` 在调用方法时 Go 默默的做了隐式的转换,其实是按照 `(*pp).GetName*()` 去调用方法,怎么实现转换的这点我们不用关心,先用起来就可以。 - -```go - p := Person{"lemon", 18} - pp := &Person{"lemon", 18} - fmt.Println(p.GetName(), pp.GetName()) // p 和 pp都能调用 GetName 方法 -``` - - - -**同理,对接收者是指针的方法,也可以按给它传递值的方式来调用,这里不再赘述。** - - - -对方法的说明,就简单介绍到这里,更多细节不去深究,留给大家在使用中学习。 - - - -## 接口 - -接口我想不到准确的描述语句来说明他,通俗来讲接口类型就是一类预先约定好的方法声明集合。 - -接口定义就是把一系列可能实现的方法先声明出来,后面只要哪个类型完全实现了某个接口声明的方法,就可用这个「接口变量」来保存这些方法的值,其实是抽象设计的概念。 - -**可以类比 `C++` 中的纯虚函数。** - -### 定义 - -为了说明接口如何定义,我们要做一些准备工作。 - -1. 先来定义两个类型,代表男人女人,他们都有属性 `name` 和 `age` - -```go -type man struct { - name string - age int -} - -type woman struct { - name string - age int -} -``` - -2. 再来分别定义两个类型的方法,`getName` 和 `getAge` 用于获取各自的姓名和年龄。 - -```go -func (m *man) getName() string { - return m.name -} - -func (m *woman) getName() string { - return m.name -} - -func (m *man) getAge() int { - return m.age -} - -func (m *woman) getAge() int { - return m.age -} -``` - -好了, 下面我们的主角「接口」登场, 我们来实现一个通用的 `humanIf` 接口类型,这个接口包含了 `getName()` 方法声明,注意接口包含的这个方法的声明样式,和前面我们定义的 `man` 与 `women` 的 `getName` 方法一致。同理 `getAge()`样式也一致。 - -```go -type humanIf interface { - getName() string - getAge() int -} -``` - -**现在可以使用这个接口了!不管男人女人反正都是人,是人就可以用我的 `humanIf` 接口获取姓名。** - -```go -var m humanIf = &man{"lemon", 18} -var w humanIf = &woman{"hanmeimei", 19} -fmt.Println(m.getName(), w.getName()) -``` - - - -### 接口类型 - -当给定一个接口值,我们如何知道他代表的底层值的具体类型呢?还是上面的例子,我们拿到了 `humanIf` 类型的变量 `m ` 和 `w`, 怎么才能知道它们到底是 `man` 还是 `women `类型呢? - -有两种方法可以确定变量 `m ` 和 `w` 的底层值类型。 - -- 类型断言 - -断言如果不是预期的类型,就会抛出 `panic `异常,程序终止。 - -如果断言是符合预期的类型,会把调用者实际的底层值返回。 - -```go -v0 := w.(man) // w保存的不是 man 类型,程序终止 - -v1 := m.(man) // m保存的符合 man 类型,v1被赋值 m 的底层值 - -v, right := a.(man) // 两个返回值,第一个是值,第二代表是否断言正确的布尔值 -fmt.Println(v, right) -``` - - - -- 类型选择 - -相比类型断言直接粗暴的让程序终止,「类型选择」语法更加的温和,即使类型不符合也不会让程序挂掉。 - -下面示例,`v3` 获得 `w` 的底层类型,在后面 `case` 通过类型比较打印出匹配的类型。注意:`type` 也是关键字。 - -```go - - switch v3 := w.(type) { - case man: - fmt.Println("it is type:man", v3) - case women: - fmt.Println("it is type:women", v3) - default: - fmt.Printf("unknow type:%T value:%v", v3, v3) - } -``` - - - -### 空接口 - -空接口 `interface{}` 代表包含了 0 个方法的接口,试想一下每个类型都至少实现了零个方法,所以任何类型都可以给空接口类型赋值。 - -下面示例,用 `man` 值给空接口赋值。 - -```go - type nilIf interface{} - var ap nilIf = &man{"lemon", 18} - - //等价定义 - var ap interface{} = &man{"lemon", 18} //等价于上面一句 -``` - -空接口可以接收任何类型的值,包括指针、值甚至是`nil` 值。 - -```go - // 接收指针 - var ap nilIf = &man{"lemon", 18} - fmt.Println("interface", ap) - // 接收值 - var a nilIf = man{"lemon", 18} - fmt.Println("interface", a) - // 接收nil值 - var b nilIf - fmt.Println("interface", b) -``` - - - -### 处理nil接口调用 - -#### nil底层值不会引发异常 - -对 C 或 C++ 程序员来说空指针是噩梦,如果对空指针做操作,结果是不可预知的,很大概率会导致程序崩溃,程序莫名其妙挂掉,想想就令人头秃。 - - ![img](https://i04piccdn.sogoucdn.com/c4ead94ec095d457) - -`Golang` 中处理空指针这种情况要优雅的多,**允许用空底层值调用接口**,但是要修改方法定义,正确处理 `nil` 值避免程序崩溃。 - -```go -func (m *man) getName() string { - if m == nil { - return "nil" - } - - return m.name -} -``` - -下面演示了使用处理了 `nil` 值的方法,虽然 `nilMan` 是空指针,但仍然可以调用 `getName` 方法。 - -```go - var nilMan *man // 定义了一个空指针 nilMan - var w humanIf = nilMan - fmt.Println(w.getName()) -``` - - - -#### nil接口引发程序异常 - -但是,如果接口本身是 `nil` 去调用方法,仍然会引发异常。 - -```go - manIf = nil - fmt.Println("interface", manIf.getName()) -``` - - - -## 总结 - -本节学习的接口和方法是 `Golang` 对面向对象程序设计的支持,可以看到实现的非常简洁,并没常用的面向对象语言那么复杂的语法和关键字,简单不代表不够好,实际上也基本够用,一句话概括就是简洁并不简单。 - -感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。 - -今天的技术分享就到这里,我们下期再见。 - ------ - - - -**创作不易,白票不是好习惯,如果有收获,动动手指点个「在看」或给个「转发」是对我持续创作的最大支持** - diff --git a/_posts/go/tour_go/Golang基础创客贴封面.png b/_posts/go/tour_go/Golang基础创客贴封面.png deleted file mode 100644 index 64dbc4b..0000000 Binary files a/_posts/go/tour_go/Golang基础创客贴封面.png and /dev/null differ diff --git a/_posts/linux/gdb系列/readme.md b/_posts/linux/gdb系列/readme.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/linux/linux工具系列/01/drawproj.drawio b/_posts/linux/linux工具系列/01/drawproj.drawio deleted file mode 100644 index ac94cd2..0000000 --- a/_posts/linux/linux工具系列/01/drawproj.drawio +++ /dev/null @@ -1 +0,0 @@ -7VrLkps4FP0alu0CBBgvjdtJFkmqq3pqZjmFQTaqCESE6HbP148EEgaBH3SwnUq1N0ZXb52je48EBlil+880zJNvJIbYsM14b4BHw7Yt33P5n7C81RbX8WvDjqJYFjoYntF/UBpNaS1RDItOQUYIZijvGiOSZTBiHVtIKXntFtsS3O01D3ewZ3iOQty3/oNiltRW354f7F8g2iWqZ8tb1DlpqArLmRRJGJPXlgmsDbCihLD6Kd2vIBaLp9alrvfpSG4zMAozdkmFr/HT6/O/8x0GPwH4O/nrm0m+P8hWXkJcygnLwbI3tQIFo+RHM3nHAEEzE5Mn4rBIYCwTIUa7jD9HfEyQckPCUszTVl0rF02m+52gyiwl0Y8yn1FawRZQib0l2tkijFcEE1oNAWyrn2ijGksrxzQXC+eR5/RXQ00NUgb3LZNcnc+QpJDRN15E5SqkJFWBSr8egHdNaUvaoDvSGEqy7Zq2D3jwBwnJCHjsAXg8LJZrS/g8Be3VUng/S8GkoFoU04yitsnbyf8wzflDtinyKm1OalrPjWBpBJ/Ew9I3/MBSo+WTrwesBjLIMYUryhJIEdN4cLBy/sSI49zP6XLVujZXTQmEou5cplsE9aqfKidnLLqgsOC1Gt9hDXL45J49T+wWcS3rFHEpxCFDL12fN8Rm2ccTQRX9mp3jdquQ7baArEf/Zmjv3xFg2h3Ro6z9QdnfhrKmez3K2sC6FWXdPmXXvuDcImhodybqTk4LDLcsKBkjWS/0niZHE4p1cmgh2zT96FjIljm3ppN9DTpdnz7KcZ/SaFFJXyo6iEWEWbwUylcwAodFgSKNEn1EPG+16vkiezQgMO6I6ZNwuAPxyJ1ob7tdIdcIO9VEQUoaQVmrLZn1hrxuQ3o7LKQ7yHrt8NUP31rFclGgODHeuTZe0zw9LKdb3u0U5w/1ACZlofdBwpGayJuIhNb8JiS0/HEktNzbkxBc4ArvqctEVP0jVZnMBQqA1hZajD0Q312PgaFLj84Z4kAndWAQGQ9Fhc6SF7Dm+b59mhh9Dq962xw/hoDWMWSj1zt3NOFoajTsKUSBOYpCvJQZKYpjUb1mU7jBipXSbfB23cBwxU1LWDJSSJ5ek2ua//T61PMGqKd7x+muYvyPCDhuS4OpZJhOhStFQDBShoE7yDB7MTYCti9Jfy0G2pPfTQjKvesU+mvBcLQvcjWkh26G/cUkcfChkXs3CIRD18v3c2m/l/dyNafj6Fhe6r1cza0AvaGJ3JerCXhHfx+kl9fcs3J/11Xwx+9vN+8RXl1hNM0VsHNUZw1JsrPaS1dUmn66XJr1pVjbn2ckg70tKI2UlFnceOopHKLOaf/O6gw4F8RFLQpFqIjIrIDcwdFixtcS1s9d+KpVh3T9AuvFt/TAp198As9fzIecofYWs+0MFeZfww3ET5weDImY+LghPDimA6RgJB/iDikZRhnvVL0MnwjvBx3vATU+H8B7cS28lQ89hXdXgFwqW45uP50+9dcF9kz0iAqoImNQfelgzoQ6qT6DcEQ3GWFRdY/gCsbAkCWiM17MGWIK4ALHNMeFzRHvuc/vXWvoNbfrjgaTJw8fONQx5PCZCFj/Dw== \ No newline at end of file diff --git a/_posts/linux/linux工具系列/01/gdb.png b/_posts/linux/linux工具系列/01/gdb.png deleted file mode 100644 index 088ceb5..0000000 Binary files a/_posts/linux/linux工具系列/01/gdb.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/linux_tools_cmd.md b/_posts/linux/linux工具系列/01/linux_tools_cmd.md deleted file mode 100644 index f1fa93c..0000000 --- a/_posts/linux/linux工具系列/01/linux_tools_cmd.md +++ /dev/null @@ -1,154 +0,0 @@ -工作5年,资深程序员总结:分析Linux进程的6个方法,我全都告诉你 - -> 创作不易,点赞关注支持一下吧,我的更多原创技术分享,关注公众号「**后端技术学堂**」第一时间看! - -操作系统「进程」是学计算机都要接触的基本概念,抛开那些纯理论的操作系统底层实现,在Linux下做软件开发这么多年,每次程序运行出现问题,都要一步一步分析进程各种状态,去排查问题出在哪里,这次lemon带你在Linux环境下实操,一步步探究揭开「Linux进程」的那些秘密。 - -## 何为进程 - -首先我们说下「程序」的概念,程序是一些保存在磁盘上的指令的有序集合,是静态的。进程是程序执行的过程,包括了动态创建、调度和消亡的整个过程,它是程序资源管理的最小单位。 - -线程是操作操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位,一个进程内可以包含多个线程,是资源调度的最小单位。[引用维基百科] - -![多线程程序模型](https://imgkr.cn-bj.ufileos.com/73d34b23-bb9d-4193-a496-b00eb036e05e.png) - -> 探究进程第一步,你在吗?还好吗? - -## ps - -`report a snapshot of the current processes.` 列出当前系统进程的快照。 - -找到进程PID ( Process IDentity ),pid唯一标识一个进程。用`ps`这个命令,这个命令大家应该都知道吧,对于小白用户,首先他不是Photoshop。 - -![ps](http://ww1.sinaimg.cn/large/9150e4e5ly1frjcour2u8g203c02owej.gif) - -给大家简单介绍一下,一般用法是`ps -ef`列出系统内经常信息,通常都会带管道`grep`出自己感兴趣的进程,像这样`ps -ef|grep intresting`第一列PID代表进程号,PPID(parent process ID)代表父进程号。 -![ps输出实例](https://upload-images.jianshu.io/upload_images/7842464-be1e324fde371320.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -> 探究进程第二步,让我看看你都交了哪些朋友(系统调用 & 信号) - -## strace - -`trace system calls and signals` 跟踪进程内部的系统调用和信号 - -> 什么是「系统调用」?系统调用(system call),指运行在「用户态」的程序向操作系统「内核态」请求需要更高权限运行的服务,系统调用提供用户程序与操作系统之间的接口。 - -`strace`后面跟着启动一个进程,可以跟踪启动后进程的系统调用和信号,这个命令可以看到进程执行时候都调用了哪些系统调用,通过指定不同的选项可以输出系统调用发生的时间,精度可以精确到微秒,甚至还可以统计分析系统「调用的耗时」,这在排查进程假死问题的时候很有用,能帮你发现进程卡在哪个系统调用上。已经在运行的进程也可以指定`-p`参数加`pid`像`gdb attach`那样附着上去跟踪。 -![strace](https://upload-images.jianshu.io/upload_images/7842464-3c9d2df36326b3bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -![strace1](https://upload-images.jianshu.io/upload_images/7842464-45579c458ef59d88.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -> 探究进程第三步,让我看看你带的小弟们(线程)。 - -## pstack - -`print a stack trace of a running process` 打印出运行中程序的堆栈信息。 - -执行命令`pstack pid` 你能看到当前线程运行中的堆栈信息,其中的pid可用之前的`ps`命令获得,`pstack`可以看到进程内启动的线程号,每个进程内线程的「堆栈」内容也能看到。 -![pstack](https://upload-images.jianshu.io/upload_images/7842464-4f5108a7bd0631e3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -看到上面打印出的LWP了吗,这里是个知识点, LPW是指`Light-weight process` 轻量级线程。引申知识: - -> 1. Linux中没有真正的线程 -> 2. Linux中没有的线程`Thread`是由进程来模拟实现的所以称作:轻量级进程 -> 3. 进程是「资源管理」的最小单元,线程是「资源调度」的最小单元(这里不考虑协程) - - -> 探究进程第四步,让小弟们(线程)出来排个队吧。 - -## pstree - -`display a tree of processes` pstree按树形结构打印运行中进程结构信息 - -可以直观的查看进程和它启动的线程的关系,并能显示进程标识。 - -![pstree](https://upload-images.jianshu.io/upload_images/7842464-779ee63317590602.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - -> 探究进程第五步,是死(进程崩溃)是活(进程运行中)我都要知道你的秘密(堆栈帧 & 上下文)。 -## gdb - -gdb是GNU开发的gcc套件中Linux下程序调试工具,你可以查看程序的堆栈、设置断点、打印程序运行时信息,甚至还能调试多线程程序,功能十分强大。 - -在这里把gdb当成一个命令来讲有点大材小用,要详细说gdb的话,完全可以撑起一篇文章的篇幅,这里长话短说,有机会再开一篇文章详细介绍下它。 - -### 使用 - -要用gdb调试C/C++程序首先编译的时候要加`-g`选项,`g++ -g test.cpp -o test`这样生成的程序就可以用gdb来调试啦。 - -1. 可以直接用gdb启动程序调试,命令:`gdb prog` -2. 用gdb附着到一个已经启动的进程上调试也可以。命令:`gdb prog pid` -3. 程序崩溃之后参数corefile也可以用gdb调试,看看程序死掉之前留了什么遗言(堆栈信息)给你。命令:`gdb prog corefile`,这里有一点需要注意,有些Linux系统默认程序崩溃不生成`corefile`,这时你需要`ulimit -c unlimited`这样就能生成`corefile`了。 -![gdb调试](https://upload-images.jianshu.io/upload_images/7842464-ddc4bfc651297b49.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -> 探究进程第六步,关于你的所有,我都想知道。 - -## 更近一步 - -通过`/proc/pid`文件了解进程的运行时信息和统计信息。`/proc`系统是一个伪文件系统,它只存在内存当中,而不占用外存空间,以文件系统的方式为内核与进程提供通信的接口。进入系统`/proc`目录: - -![proc目录](https://upload-images.jianshu.io/upload_images/7842464-afef80294f10aa0d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -/proc目录下有很多以数字命名的目录,每个数字代表进程号PID它们是进程目录。系统中当前运行的每一个进程在/proc下都对应一个以进程号为目录名的目录`/proc/pid `,它们是读取进程信息的接口,我们可以进到这个文件里面,了解进程的运行时信息和统计信息。 - -### 高频使用 - -`/proc/pid`目录下的有一些重要文件,挑几个使用频率高的讲一讲。 -`/proc/pid/environ` 包含了进程的可用环境变量的列表 。程序出问题了如果不确定环境变量是否设置生效,可以`cat`这个文件出来查看确认一下。 - -`/proc/pid/fd/` 这个目录包含了进程打开的每一个文件的链接。从这里可以查看进程打开的文件描述符信息,包括标准输入、输出、错误流,进程打开的`socket`连接文件描述符也能看到,`lsof`命令也有类似的作用。 - -`/proc/pid/stat`包含了进程的所有状态信息,进程号、父进程号、 线程组号、 该任务在用户态运行的时间 、 该任务在用内核态运行的时间、 虚拟地址空间的代码段、 阻塞信号的位图等等信息应有尽有。 - -### 其他统计 - -`/proc/pid/cmdline` 包含了用于开始进程的命令 -`/proc/pid/cwd`包含了当前进程工作目录的一个链接 -`/proc/pid/exe `包含了正在进程中运行的程序链接 -`/proc/pid/mem `包含了进程在内存中的内容 -`/proc/pid/statm `包含了进程的内存使用信息 - -## 总结一下 -好了,一顿操作下来,你对进程和它背后的秘密你已经非常了解了,下次我们的好朋友「进程」如果遇到了什么问题(崩溃`coredump`、假死、阻塞、系统调用超时、文件描述符异常),你应该知道如何帮它处理了吧!我们来总结一下: - -- ps查看进程id,看看进程还在不在以及进程状态 -- 如果在的话`strace`、`psstack`看下进程当前信息,是不卡死在哪个位置,对比各帧最后调用信息找到异常点 -- 如果进程不再了,如果有`corefile`文件,直接上`gdb`查看`corefile`信息 -- 其他疑难杂症怀疑进程状态信息的时候,看看`/proc/pid`下面的进程状态信息,可能会给你启发。 -- 最后,如果以上都不行,闭目祈祷吧! - -![](http://wx1.sinaimg.cn/large/cf652d2bgy1fgchzezbpdj20k00k0dhe.jpg) - -## 写在最后 - -今天的分享希望对你有帮助,祝大家写的服务永不宕机,从不coredump,让上面教你的操作吃灰去吧。 -![永不宕机](https://upload-images.jianshu.io/upload_images/7842464-f7a94cc986f4ebb9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -
图片来源网络|侵删
- - -最后,感谢各位的阅读。文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。 - - - - -## reference - - https://man.linuxde.net/gdb - - https://blog.csdn.net/dan15188387481/article/details/49450491 - - https://blog.csdn.net/m0_37925202/article/details/78759408 - -https://blog.csdn.net/enweitech/article/details/53391567 - - - -### 创作不易,点赞关注支持一下吧 -我会持续分享软件编程和程序员那些事,欢迎关注。若你对编程感兴趣,我整理了这些年学习编程大约3G的资源汇总,关注公众号「**后端技术学堂**」后发送「**资料**」免费获取。 \ No newline at end of file diff --git a/_posts/linux/linux工具系列/01/proc目录.png b/_posts/linux/linux工具系列/01/proc目录.png deleted file mode 100644 index cd9b018..0000000 Binary files a/_posts/linux/linux工具系列/01/proc目录.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/ps.png b/_posts/linux/linux工具系列/01/ps.png deleted file mode 100644 index 2f408d0..0000000 Binary files a/_posts/linux/linux工具系列/01/ps.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/pstack.png b/_posts/linux/linux工具系列/01/pstack.png deleted file mode 100644 index bec7fcc..0000000 Binary files a/_posts/linux/linux工具系列/01/pstack.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/pstree.png b/_posts/linux/linux工具系列/01/pstree.png deleted file mode 100644 index c559f0b..0000000 Binary files a/_posts/linux/linux工具系列/01/pstree.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/strace.png b/_posts/linux/linux工具系列/01/strace.png deleted file mode 100644 index 826d9f2..0000000 Binary files a/_posts/linux/linux工具系列/01/strace.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/strace1.png b/_posts/linux/linux工具系列/01/strace1.png deleted file mode 100644 index 52d6643..0000000 Binary files a/_posts/linux/linux工具系列/01/strace1.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/佛祖.png b/_posts/linux/linux工具系列/01/佛祖.png deleted file mode 100644 index dd34e4d..0000000 Binary files a/_posts/linux/linux工具系列/01/佛祖.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/多线程模型.png b/_posts/linux/linux工具系列/01/多线程模型.png deleted file mode 100644 index f77e181..0000000 Binary files a/_posts/linux/linux工具系列/01/多线程模型.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/永不宕机.png b/_posts/linux/linux工具系列/01/永不宕机.png deleted file mode 100644 index a864daa..0000000 Binary files a/_posts/linux/linux工具系列/01/永不宕机.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/01/这种操作.png b/_posts/linux/linux工具系列/01/这种操作.png deleted file mode 100644 index 03ca261..0000000 Binary files a/_posts/linux/linux工具系列/01/这种操作.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/Linux_memory_mgr.drawio b/_posts/linux/linux工具系列/02/Linux_memory_mgr.drawio deleted file mode 100644 index 31fc7dd..0000000 --- a/_posts/linux/linux工具系列/02/Linux_memory_mgr.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/_posts/linux/linux工具系列/02/wm_arem_struct.png b/_posts/linux/linux工具系列/02/wm_arem_struct.png deleted file mode 100644 index fa1fbd8..0000000 Binary files a/_posts/linux/linux工具系列/02/wm_arem_struct.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/内核空间物理内存映射.png b/_posts/linux/linux工具系列/02/内核空间物理内存映射.png deleted file mode 100644 index cb01c82..0000000 Binary files a/_posts/linux/linux工具系列/02/内核空间物理内存映射.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/内核空间用户空间全图.png b/_posts/linux/linux工具系列/02/内核空间用户空间全图.png deleted file mode 100644 index 4234261..0000000 Binary files a/_posts/linux/linux工具系列/02/内核空间用户空间全图.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/内核空间细分区域.png b/_posts/linux/linux工具系列/02/内核空间细分区域.png deleted file mode 100644 index 2d9e3e3..0000000 Binary files a/_posts/linux/linux工具系列/02/内核空间细分区域.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/别再说你不懂Linux内存管理了,10张图给你安排的明明白白.md b/_posts/linux/linux工具系列/02/别再说你不懂Linux内存管理了,10张图给你安排的明明白白.md deleted file mode 100644 index aa3b732..0000000 --- a/_posts/linux/linux工具系列/02/别再说你不懂Linux内存管理了,10张图给你安排的明明白白.md +++ /dev/null @@ -1,209 +0,0 @@ -今天来带大家研究一下` Linux `内存管理。对于精通 `CURD` 的业务同学,内存管理好像离我们很远,但这个知识点虽然冷门(估计很多人学完根本就没机会用上)但绝对是基础中的基础,这就像武侠中的内功修炼,学完之后看不到立竿见影的效果,但对你日后的开发工作是大有裨益的,因为你站的更高了。 - -再功利点的说,面试的时候不经意间透露你懂这方面知识,并且能说出个一二三来,也许能让面试官对你更有兴趣,离升职加薪,走上人生巅峰又近了一步。 - - ![image](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTIzMmZkNThiMGUwYzg4ZDE?x-oss-process=image/format,png) - -前提约定:本文讨论技术内容前提,操作系统环境都是 `x86`架构的 32 位 ` Linux `系统。 - -## 虚拟地址 - -即使是现代操作系统中,内存依然是计算机中很宝贵的资源,看看你电脑几个T固态硬盘,再看看内存大小就知道了。为了充分利用和管理系统内存资源,Linux采用虚拟内存管理技术,利用虚拟内存技术让每个进程都有`4GB` 互不干涉的虚拟地址空间。 - -进程初始化分配和操作的都是基于这个「虚拟地址」,只有当进程需要实际访问内存资源的时候才会建立虚拟地址和物理地址的映射,调入物理内存页。 - -打个不是很恰当的比方。这个原理其实和现在的某某网盘一样,假如你的网盘空间是` 1TB `,真以为就一口气给了你这么大空间吗?那还是太年轻,都是在你往里面放东西的时候才给你分配空间,你放多少就分多少实际空间给你,但你和你朋友看起来就像大家都拥有` 1TB `空间一样。 - -![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTMwN2QzODQ1NzQyZmEwODM?x-oss-process=image/format,png) - -### 虚拟地址的好处 - -- 避免用户直接访问物理内存地址,防止一些破坏性操作,保护操作系统 -- 每个进程都被分配了4GB的虚拟内存,用户程序可使用比实际物理内存更大的地址空间 - -`4GB` 的进程虚拟地址空间被分成两部分:「用户空间」和「内核空间」 - -![用户空间内核空间](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTRmMzgyNjMxZmQxMzM1MjMucG5n?x-oss-process=image/format,png) - - -## 物理地址 - -上面章节我们已经知道不管是用户空间还是内核空间,使用的地址都是虚拟地址,当需进程要实际访问内存的时候,会由内核的「请求分页机制」产生「缺页异常」调入物理内存页。 - -把虚拟地址转换成内存的物理地址,这中间涉及利用`MMU` 内存管理单元(Memory Management Unit ) 对虚拟地址分段和分页(段页式)地址转换,关于分段和分页的具体流程,这里不再赘述,可以参考任何一本计算机组成原理教材描述。 - -![段页式内存管理地址转换](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTIyNzBlOTNmMjBlOGZkYjcucG5n?x-oss-process=image/format,png) - - -`Linux` 内核会将物理内存分为3个管理区,分别是: - -### ZONE_DMA - -`DMA`内存区域。包含0MB~16MB之间的内存页框,可以由老式基于` ISA `的设备通过` DMA `使用,直接映射到内核的地址空间。 - -### ZONE_NORMAL - -普通内存区域。包含16MB~896MB之间的内存页框,常规页框,直接映射到内核的地址空间。 - -### ZONE_HIGHMEM - -高端内存区域。包含896MB以上的内存页框,不进行直接映射,可以通过永久映射和临时映射进行这部分内存页框的访问。 - -![物理内存区划分](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTE2MzVmNGMwNzZjZWFlMTUucG5n?x-oss-process=image/format,png) - - -## 用户空间 - -用户进程能访问的是「用户空间」,每个进程都有自己独立的用户空间,虚拟地址范围从从 `0x00000000` 至 `0xBFFFFFFF` 总容量3G 。 - -用户进程通常只能访问用户空间的虚拟地址,只有在执行内陷操作或系统调用时才能访问内核空间。 - -### 进程与内存 - -进程(执行的程序)占用的用户空间按照「 访问属性一致的地址空间存放在一起 」的原则,划分成 `5`个不同的内存区域。 访问属性指的是“可读、可写、可执行等 。 - -- 代码段 - - 代码段是用来存放可执行文件的操作指令,可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,它是不可写的。 - -- 数据段 - - 数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。 - -- BSS段 - - ` BSS `段包含了程序中未初始化的全局变量,在内存中 `bss` 段全部置零。 - -- 堆 ` heap` - - 堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减) - -- 栈 `stack` - - 栈是用户存放程序临时创建的局部变量,也就是函数中定义的变量(但不包括 `static` 声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。 - -上述几种内存区域中数据段、`BSS` 段、堆通常是被连续存储在内存中,在位置上是连续的,而代码段和栈往往会被独立存放。堆和栈两个区域在 `i386` 体系结构中栈向下扩展、堆向上扩展,相对而生。 -![程序内存区域分段](https://img-blog.csdnimg.cn/20200419230245702.png#pic_center) - -你也可以再linux下用`size` 命令查看编译后程序的各个内存区域大小: - -```shell -[lemon ~]# size /usr/local/sbin/sshd - text data bss dec hex filename -1924532 12412 426896 2363840 2411c0 /usr/local/sbin/sshd -``` - - - -## 内核空间 - -在 `x86 32` 位系统里,Linux 内核地址空间是指虚拟地址从 `0xC0000000` 开始到 `0xFFFFFFFF` 为止的高端内存地址空间,总计 `1G` 的容量, 包括了内核镜像、物理页面表、驱动程序等运行在内核空间 。 - -![内核空间细分区域.](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWZmZTNlNGQ4ZDc5ZjNmNGMucG5n?x-oss-process=image/format,png) - - -#### 直接映射区 - -直接映射区 `Direct Memory Region`:从内核空间起始地址开始,最大`896M`的内核空间地址区间,为直接内存映射区。 - -直接映射区的896MB的「线性地址」直接与「物理地址」的前`896MB`进行映射,也就是说线性地址和分配的物理地址都是连续的。内核地址空间的线性地址`0xC0000001`所对应的物理地址为`0x00000001`,它们之间相差一个偏移量`PAGE_OFFSET = 0xC0000000` - -该区域的线性地址和物理地址存在线性转换关系「线性地址 = `PAGE_OFFSET` + 物理地址」也可以用 `virt_to_phys() `函数将内核虚拟空间中的线性地址转化为物理地址。 - - - -#### 高端内存线性地址空间 - -内核空间线性地址从 896M 到 1G 的区间,容量 128MB 的地址区间是高端内存线性地址空间,为什么叫高端内存线性地址空间?下面给你解释一下: - -前面已经说过,内核空间的总大小 1GB,从内核空间起始地址开始的 896MB 的线性地址可以直接映射到物理地址大小为 896MB 的地址区间。退一万步,即使内核空间的1GB线性地址都映射到物理地址,那也最多只能寻址 1GB 大小的物理内存地址范围。 - -请问你现在你家的内存条多大?快醒醒都 0202 年了,一般 PC 的内存都大于 1GB 了吧! -![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTc0N2U1ZmYxNzQ1NDhlNjAucG5n?x-oss-process=image/format,png) - - -所以,内核空间拿出了最后的 128M 地址区间,划分成下面三个高端内存映射区,以达到对整个物理地址范围的寻址。而 在 64 位的系统上就不存在这样的问题了,因为可用的线性地址空间远大于可安装的内存。 - -##### 动态内存映射区 - - `vmalloc Region` 该区域由内核函数`vmalloc`来分配,特点是:线性空间连续,但是对应的物理地址空间不一定连续。 `vmalloc` 分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存。 - -##### 永久内存映射区 - -`Persistent Kernel Mapping Region` 该区域可访问高端内存。访问方法是使用 `alloc_page (_GFP_HIGHMEM)` 分配高端内存页或者使用` kmap `函数将分配到的高端内存映射到该区域。 - -##### 固定映射区 - -`Fixing kernel Mapping Region` 该区域和 4G 的顶端只有 4k 的隔离带,其每个地址项都服务于特定的用途,如 `ACPI_BASE` 等。 - -![内核空间物理内存映射](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTJkNzA2MDBkMjc3YWRjNWMucG5n?x-oss-process=image/format,png) - - -## 回顾一下 - -上面讲的有点多,先别着急进入下一节,在这之前我们再来回顾一下上面所讲的内容。如果认真看完上面的章节,我这里再画了一张图,现在你的脑海中应该有这样一个内存管理的全局图。 - -![内核空间用户空间全图](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWQwYzA2YjA2ZjQ4NGQ0OGEucG5n?x-oss-process=image/format,png) - - - -## 内存数据结构 - -要让内核管理系统中的虚拟内存,必然要从中抽象出内存管理数据结构,内存管理操作如「分配、释放等」都基于这些数据结构操作,这里列举两个管理虚拟内存区域的数据结构。 - -### 用户空间内存数据结构 - -在前面「进程与内存」章节我们提到,Linux进程可以划分为 5 个不同的内存区域,分别是:代码段、数据段、` BSS `、堆、栈,内核管理这些区域的方式是,将这些内存区域抽象成` vm_area_struct `的内存管理对象。 - -` vm_area_struct `是描述进程地址空间的基本管理单元,一个进程往往需要多个` vm_area_struct `来描述它的用户空间虚拟地址,需要使用「链表」和「红黑树」来组织各个` vm_area_struct `。 - -链表用于需要遍历全部节点的时候用,而红黑树适用于在地址空间中定位特定内存区域。内核为了内存区域上的各种不同操作都能获得高性能,所以同时使用了这两种数据结构。 - -用户空间进程的地址管理模型: - -![wm_arem_struct](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWVmMDE0ODUyMWVlM2FkMTQucG5n?x-oss-process=image/format,png) - - - - -### 内核空间动态分配内存数据结构 - -在内核空间章节我们提到过「动态内存映射区」,该区域由内核函数`vmalloc`来分配,特点是:线性空间连续,但是对应的物理地址空间不一定连续。 `vmalloc` 分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存。 - -`vmalloc` 分配的地址则限于` vmalloc_start `与` vmalloc_end `之间。每一块` vmalloc `分配的内核虚拟内存都对应一个` vm_struct `结构体,不同的内核空间虚拟地址之间有` 4k `大小的防越界空闲区间隔区。与用户空间的虚拟地址特性一样,这些虚拟地址与物理内存没有简单的映射关系,必须通过内核页表才可转换为物理地址或物理页,它们有可能尚未被映射,当发生缺页时才真正分配物理页面。 - -![动态内存映射](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWU2YjEyZTcxMzE2NDRlODAucG5n?x-oss-process=image/format,png) - - - - -## 总结一下 - -`Linux `内存管理是一个非常复杂的系统,本文所述只是冰山一角,从宏观角度给你展现内存管理的全貌,但一般来说,这些知识在你和面试官聊天的时候还是够用的,当然我也希望大家能够通过读书了解更深层次的原理。 - -希望这篇文章可以作为一个索引一样的学习指南,当你想深入某一点学习的时候可以在这些章节里找到切入点,以及这个知识点在内存管理宏观上的位置。 - -**本文创作过程我也画了大量的示例图解,可以作为知识索引,个人感觉看图还是比看文字更清晰明了,你可以在我公众号「后端技术学堂」后台回复「内存管理」获取这些图片的高清原图。** - -老规矩,感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。今天的技术分享就到这里,我们下期再见。 - -**我是 lemon 一线互联网大厂程序员,热爱技术,乐于分享。欢迎扫码关注公众号「后端技术学堂」带你一起学编程,回复「资源」送你 3GB 的编程学习大礼包,包括Linux、数据库、C++、Python、数据结构与算法、设计模式、程序员面试指南等资源,欢迎关注,交流学习。** - -![扫码关注.png](https://upload-images.jianshu.io/upload_images/7842464-146a203080f94c9a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - -## Reference - -Linux内存管理(最透彻的一篇) https://cloud.tencent.com/developer/article/1515762 - -linux 内存管理初探 https://cloud.tencent.com/developer/article/1005671 - -linux内存管理源码分析 - 页框分配器 https://www.cnblogs.com/tolimit/p/4551428.html - -Linux内核--内核地址空间分布和进程地址空间 https://my.oschina.net/wuqingyi/blog/854382 - -Linux内存管理 http://gityuan.com/2015/10/30/kernel-memory/ - -Linux Used内存到底哪里去了? http://blog.yufeng.info/archives/2456 - diff --git a/_posts/linux/linux工具系列/02/动态内存映射.png b/_posts/linux/linux工具系列/02/动态内存映射.png deleted file mode 100644 index 179221d..0000000 Binary files a/_posts/linux/linux工具系列/02/动态内存映射.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/段页式内存管理地址转换.png b/_posts/linux/linux工具系列/02/段页式内存管理地址转换.png deleted file mode 100644 index 8738ff4..0000000 Binary files a/_posts/linux/linux工具系列/02/段页式内存管理地址转换.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/物理内存区划分.png b/_posts/linux/linux工具系列/02/物理内存区划分.png deleted file mode 100644 index 57f66f6..0000000 Binary files a/_posts/linux/linux工具系列/02/物理内存区划分.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/用户空间内核空间.png b/_posts/linux/linux工具系列/02/用户空间内核空间.png deleted file mode 100644 index c1abffa..0000000 Binary files a/_posts/linux/linux工具系列/02/用户空间内核空间.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/02/程序内存区域分段.png b/_posts/linux/linux工具系列/02/程序内存区域分段.png deleted file mode 100644 index 49a60a9..0000000 Binary files a/_posts/linux/linux工具系列/02/程序内存区域分段.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/kmalloc_vmalloc图解.png b/_posts/linux/linux工具系列/03/kmalloc_vmalloc图解.png deleted file mode 100644 index 2de25b8..0000000 Binary files a/_posts/linux/linux工具系列/03/kmalloc_vmalloc图解.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/kmem_list3.png b/_posts/linux/linux工具系列/03/kmem_list3.png deleted file mode 100644 index 509d43e..0000000 Binary files a/_posts/linux/linux工具系列/03/kmem_list3.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/linux物理页管理算法.md b/_posts/linux/linux工具系列/03/linux物理页管理算法.md deleted file mode 100644 index e4dfe33..0000000 --- a/_posts/linux/linux工具系列/03/linux物理页管理算法.md +++ /dev/null @@ -1,260 +0,0 @@ -今天继续来学习Linux内存管理,什么?你更想学时间管理,我不配,抱个西瓜去微博学吧。 - - ![img](https://i0.hdslb.com/bfs/article/666d2ea83d750ee420d79b3308f6cb164b9fc5a9.gif) - -言归正传,上一篇文章 [别再说你不懂Linux内存管理了,10张图给你安排的明明白白!](https://mp.weixin.qq.com/s?__biz=MzIwMjM4NDE1Nw==&mid=2247483865&idx=1&sn=dfa63a467b620b6131acaef9ea6874a3&chksm=96de37aba1a9bebdcc097314f40ae633bd393253759ecd970ac84cdb41f18994da9405dbf356&token=1178579599&lang=zh_CN#rd) 分析了 Linux 内存管理机制,如果已经忘了的同学还可以回头看下,并且也强烈建议先阅读那一篇再来看这一篇。限于篇幅,上一篇没有深入学习物理内存管理和虚拟内存分配,今天就来学习一下。 - -通过前面的学习我们知道,程序可没这么好骗,任你内存管理把虚拟地址空间玩出花来,到最后还是要给程序实实在在的物理内存,不然程序就要罢工了,所以物理内存这么重要的资源一定要好好管理起来使用(物理内存,就是你实实在在的内存条),那么内核是如何管理物理内存的呢? - -## 物理内存管理 - -在`Linux `系统中通过分段和分页机制,把物理内存划分 4K 大小的内存页 `Page`(也称作页框`Page Frame`),物理内存的分配和回收都是基于内存页进行,把物理内存分页管理的好处大大的。 - -假如系统请求小块内存,可以预先分配一页给它,避免了反复的申请和释放小块内存带来频繁的系统开销。 - -假如系统需要大块内存,则可以用多页内存拼凑,而不必要求大块连续内存。你看不管内存大小都能收放自如,分页机制多么完美的解决方案! - - ![](https://i04piccdn.sogoucdn.com/615d4aeb28926430) - -But,理想很丰满,现实很骨感。如果就直接这样把内存分页使用,不再加额外的管理还是存在一些问题,下面我们来看下,系统在多次分配和释放物理页的时候会遇到哪些问题。 - -### 物理页管理面临问题 - -物理内存页分配会出现外部碎片和内部碎片问题,所谓的「内部」和「外部」是针对「页框内外」而言,一个页框内的内存碎片是内部碎片,多个页框间的碎片是外部碎片。 - -#### 外部碎片 - -当需要分配大块内存的时候,要用好几页组合起来才够,而系统分配物理内存页的时候会尽量分配连续的内存页面,频繁的分配与回收物理页导致大量的小块内存夹杂在已分配页面中间,形成外部碎片,举个例子: - -![外部碎片](https://upload-images.jianshu.io/upload_images/7842464-39b54d3f1acf2f86.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -#### 内部碎片 - -物理内存是按页来分配的,这样当实际只需要很小内存的时候,也会分配至少是 4K 大小的页面,而内核中有很多需要以字节为单位分配内存的场景,这样本来只想要几个字节而已却不得不分配一页内存,除去用掉的字节剩下的就形成了内部碎片。 - -![内部碎片](https://upload-images.jianshu.io/upload_images/7842464-496b0cfd4d779633.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -### 页面管理算法 - -方法总比困难多,因为存在上面的这些问题,聪明的程序员灵机一动,引入了页面管理算法来解决上述的碎片问题。 - -#### Buddy(伙伴)分配算法 - - `Linux` 内核引入了伙伴系统算法(Buddy system),什么意思呢?就是把相同大小的页框块用链表串起来,页框块就像手拉手的好伙伴,也是这个算法名字的由来。 - - ![](https://i04piccdn.sogoucdn.com/3182f7a569acc1c1) - -具体的,所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。 - -![伙伴系统](https://upload-images.jianshu.io/upload_images/7842464-25a3e9bd900cb55c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -因为任何正整数都可以由 `2^n` 的和组成,所以总能找到合适大小的内存块分配出去,减少了外部碎片产生 。 - -##### 分配实例 - -比如:我需要申请4个页框,但是长度为4个连续页框块链表没有空闲的页框块,伙伴系统会从连续8个页框块的链表获取一个,并将其拆分为两个连续4个页框块,取其中一个,另外一个放入连续4个页框块的空闲链表中。释放的时候会检查,释放的这几个页框前后的页框是否空闲,能否组成下一级长度的块。 - -##### 命令查看 - -``` -[lemon]]# cat /proc/buddyinfo -Node 0, zone DMA 1 0 0 0 2 1 1 0 1 1 3 -Node 0, zone DMA32 3198 4108 4940 4773 4030 2184 891 180 67 32 330 -Node 0, zone Normal 42438 37404 16035 4386 610 121 22 3 0 0 1 - -``` - - - -#### slab分配器 - -看到这里你可能会想,有了伙伴系统这下总可以管理好物理内存了吧?不,还不够,否则就没有slab分配器什么事了。 - - ![](https://i02piccdn.sogoucdn.com/980b9721e70eedae) - -那什么是slab分配器呢? - -一般来说,内核对象的生命周期是这样的:分配内存-初始化-释放内存,内核中有大量的小对象,比如文件描述结构对象、任务描述结构对象,如果按照伙伴系统按页分配和释放内存,对小对象频繁的执行「分配内存-初始化-释放内存」会非常消耗性能。 - -伙伴系统分配出去的内存还是以页框为单位,而对于内核的很多场景都是分配小片内存,远用不到一页内存大小的空间。` slab `分配器,**通过将内存按使用对象不同再划分成不同大小的空间**,应用于内核对象的缓存。 - -伙伴系统和slab不是二选一的关系,`slab` 内存分配器是对伙伴分配算法的补充。 - -##### 大白话说原理 - -对于每个内核中的相同类型的对象,如:`task_struct、file_struct` 等需要重复使用的小型内核数据对象,都会有个 slab 缓存池,缓存住大量常用的「已经初始化」的对象,每当要申请这种类型的对象时,就从缓存池的`slab` 列表中分配一个出去;而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免内部碎片,同时也大大提高了内存分配性能。 - -##### 主要优点 - -- `slab` 内存管理基于内核小对象,不用每次都分配一页内存,充分利用内存空间,避免内部碎片。 -- `slab` 对内核中频繁创建和释放的小对象做缓存,重复利用一些相同的对象,减少内存分配次数。 - -##### 数据结构 - -![slab分配器](https://upload-images.jianshu.io/upload_images/7842464-d3d1a3ed84ac2960.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -`kmem_cache` 是一个`cache_chain` 的链表组成节点,代表的是一个内核中的相同类型的「对象高速缓存」,每个`kmem_cache` 通常是一段连续的内存块,包含了三种类型的 `slabs` 链表: - -- `slabs_full` (完全分配的 `slab` 链表) -- ` slabs_partial` (部分分配的`slab` 链表) -- `slabs_empty` ( 没有被分配对象的`slab` 链表) - -`kmem_cache` 中有个重要的结构体 `kmem_list3` 包含了以上三个数据结构的声明。 - -![kmem_list3 内核源码](https://upload-images.jianshu.io/upload_images/7842464-ff27149b346c7f1b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -`slab` 是` slab` 分配器的最小单位,在实现上一个 `slab` 有一个或多个连续的物理页组成(通常只有一页)。单个slab可以在 `slab` 链表之间移动,例如如果一个「半满` slabs_partial`链表」被分配了对象后变满了,就要从 `slabs_partial` 中删除,同时插入到「全满`slabs_full`链表」中去。内核` slab `对象的分配过程是这样的: - -1. 如果` slabs_partial`链表还有未分配的空间,分配对象,若分配之后变满,移动 `slab` 到`slabs_full` 链表 -2. 如果` slabs_partial`链表没有未分配的空间,进入下一步 -3. 如果`slabs_empty` 链表还有未分配的空间,分配对象,同时移动` slab `进入` slabs_partial`链表 -4. 如果`slabs_empty`为空,请求伙伴系统分页,创建一个新的空闲`slab`, 按步骤 3 分配对象 - -![slab分配图解](https://upload-images.jianshu.io/upload_images/7842464-e830d87e9bd2cba0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -##### 命令查看 - -上面说的都是理论,比较抽象,动动手来康康系统中的 slab 吧!你可以通过 `cat /proc/slabinfo` 命令,实际查看系统中` slab` 信息。 - -![slabinfo查询](https://upload-images.jianshu.io/upload_images/7842464-1a310d5b729d0a25.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -`slabtop` 实时显示内核 slab 内存缓存信息。 - -![slabtop查询](https://upload-images.jianshu.io/upload_images/7842464-69926181eda4d902.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -#### slab高速缓存的分类 - -slab高速缓存分为两大类,「通用高速缓存」和「专用高速缓存」。 - -##### 通用高速缓存 - -slab分配器中用 `kmem_cache` 来描述高速缓存的结构,它本身也需要 slab 分配器对其进行高速缓存。cache_cache 保存着对「高速缓存描述符的高速缓存」,是一种通用高速缓存,保存在`cache_chain` 链表中的第一个元素。 - - -另外,slab 分配器所提供的小块连续内存的分配,也是通用高速缓存实现的。通用高速缓存所提供的对象具有几何分布的大小,范围为32到131072字节。内核中提供了 `kmalloc()` 和 `kfree()` 两个接口分别进行内存的申请和释放。 - -##### 专用高速缓存 - -内核为专用高速缓存的申请和释放提供了一套完整的接口,根据所传入的参数为制定的对象分配slab缓存。 - -###### 专用高速缓存的申请和释放 - -kmem_cache_create() 用于对一个指定的对象创建高速缓存。它从 cache_cache 普通高速缓存中为新的专有缓存分配一个高速缓存描述符,并把这个描述符插入到高速缓存描述符形成的 cache_chain 链表中。kmem_cache_destory() 用于撤消和从 cache_chain 链表上删除高速缓存。 - -##### slab的申请和释放 - -`slab` 数据结构在内核中的定义,如下: - -![slab结构体内核代码](https://upload-images.jianshu.io/upload_images/7842464-8f1e770cb728e37e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -kmem_cache_alloc() 在其参数所指定的高速缓存中分配一个slab,对应的 kmem_cache_free() 在其参数所指定的高速缓存中释放一个slab。 - - - -## 虚拟内存分配 - -前面讨论的都是对物理内存的管理,Linux 通过虚拟内存管理,欺骗了用户程序假装每个程序都有 4G 的虚拟内存寻址空间(如果这里不懂我说啥,建议回头看下 [别再说你不懂Linux内存管理了,10张图给你安排的明明白白!](https://mp.weixin.qq.com/s?__biz=MzIwMjM4NDE1Nw==&mid=2247483865&idx=1&sn=dfa63a467b620b6131acaef9ea6874a3&chksm=96de37aba1a9bebdcc097314f40ae633bd393253759ecd970ac84cdb41f18994da9405dbf356&token=1178579599&lang=zh_CN#rd))。 - -所以我们来研究下虚拟内存的分配,这里包括用户空间虚拟内存和内核空间虚拟内存。 - -**注意,分配的虚拟内存还没有映射到物理内存,只有当访问申请的虚拟内存时,才会发生缺页异常,再通过上面介绍的伙伴系统和 slab 分配器申请物理内存。** - -### 用户空间内存分配 - -#### malloc - -`malloc` 用于申请用户空间的虚拟内存,当申请小于 `128KB` 小内存的时,` malloc `使用 `sbrk或brk` 分配内存;当申请大于 `128KB` 的内存时,使用 `mmap` 函数申请内存; - -##### 存在问题 - -由于 `brk/sbrk/mmap` 属于系统调用,如果每次申请内存都要产生系统调用开销,`cpu` 在用户态和内核态之间频繁切换,非常影响性能。 - -而且,堆是从低地址往高地址增长,如果低地址的内存没有被释放,高地址的内存就不能被回收,容易产生内存碎片。 - -##### 解决 - -因此,` malloc `采用的是内存池的实现方式,先申请一大块内存,然后将内存分成不同大小的内存块,然后用户申请内存时,直接从内存池中选择一块相近的内存块分配出去。 -![malloc原理](https://upload-images.jianshu.io/upload_images/7842464-26e7d450551a4631.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -### 内核空间内存分配 - -在讲内核空间内存分配之前,先来回顾一下内核地址空间。`kmalloc` 和 `vmalloc` 分别用于分配不同映射区的虚拟内存。 - -![内核空间细分区域.](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWZmZTNlNGQ4ZDc5ZjNmNGMucG5n?x-oss-process=image/format,png) - -#### kmalloc - -`kmalloc()` 分配的虚拟地址范围在内核空间的「直接内存映射区」。 - -按字节为单位虚拟内存,一般用于分配小块内存,释放内存对应于 `kfree` ,可以分配连续的物理内存。函数原型在 `` 中声明,一般情况下在驱动程序中都是调用 `kmalloc()` 来给数据结构分配内存 。 - -还记得前面说的 slab 吗?`kmalloc` 是基于slab 分配器的 ,同样可以用`cat /proc/slabinfo` 命令,查看 `kmalloc` 相关 `slab` 对象信息,下面的 kmalloc-8、kmalloc-16 等等就是基于slab分配的 kmalloc 高速缓存。 - -![slabinfo-kmalloc](https://upload-images.jianshu.io/upload_images/7842464-159cafcb9fca21cd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -#### vmalloc - -`vmalloc` 分配的虚拟地址区间,位于 `vmalloc_start` 与 ` vmalloc_end` 之间的「动态内存映射区」。 - -一般用分配大块内存,释放内存对应于 `vfree`,分配的虚拟内存地址连续,物理地址上不一定连续。函数原型在 `` 中声明。一般用在为活动的交换区分配数据结构,为某些 `I/O` 驱动程序分配缓冲区,或为内核模块分配空间。 - -下面的图总结了上述两种内核空间虚拟内存分配方式。 -![kmalloc_vmalloc图解](https://upload-images.jianshu.io/upload_images/7842464-0479ee1ebb47e96f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -## 总结一下 - -这是`Linux `内存管理系列文章的下篇,强烈建议阅读过程中有不清楚的同学,先去看看我之前写的 [别再说你不懂Linux内存管理了,10张图给你安排的明明白白!](https://mp.weixin.qq.com/s?__biz=MzIwMjM4NDE1Nw==&mid=2247483865&idx=1&sn=dfa63a467b620b6131acaef9ea6874a3&chksm=96de37aba1a9bebdcc097314f40ae633bd393253759ecd970ac84cdb41f18994da9405dbf356&token=1178579599&lang=zh_CN#rd),写到这里Linux 内存管理专题告一段落,我分享的这些知识很基础,基础到日常开发工作几乎用不上,但我认为每个在Linux下开发人员都应该了解。 - -我知道有些面试官喜欢在面试的时候考察一下,或多或少反应候选人基础素养,这两篇文章的内容也足够应付面试。还是那句话,Linxu 内存管理太复杂,不是一两篇文章能讲的清楚,但至少要有宏观意识,不至于一问三不知,如果你想深入了解原理,强烈建议从书中并结合内核源码学习,每天进步一点点,我们的目标是星辰大海。 - -**本文创作过程我也画了大量的示例图解,可以作为知识索引,个人感觉看图还是比看文字更清晰明了,你可以在我公众号「后端技术学堂」后台回复「内存管理」获取这些图片的高清原图。** - -老规矩,感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。今天的技术分享就到这里,我们下期再见。 - -**原创不易,看到这里,如果在我这有一点点收获,就动动手指「转发」和「在看」是对我持续创作的最大支持。** - - - -## Reference - -《Linux内核设计与实现(原书第3版)》 - -linux内核slab机制分析 https://www.jianshu.com/p/95d68389fbd1 - - Linux内存管理中的slab分配器 http://edsionte.com/techblog/archives/4019 - -Linux slab 分配器剖析 https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/index.html#table2 - -Linux内核内存管理算法Buddy和Slab https://zhuanlan.zhihu.com/p/36140017 - -Linux内存之Slab https://fivezh.github.io/2017/06/25/Linux-slab-info/ - -malloc 的实现原理 内存池 mmap sbrk 链表 https://zhuanlan.zhihu.com/p/57863097 - -malloc实现原理 http://luodw.cc/2016/02/17/malloc/ - -glibc内存管理那些事儿 https://www.jianshu.com/p/2fedeacfa797 - - - - \ No newline at end of file diff --git a/_posts/linux/linux工具系列/03/malloc原理.png b/_posts/linux/linux工具系列/03/malloc原理.png deleted file mode 100644 index 3f33059..0000000 Binary files a/_posts/linux/linux工具系列/03/malloc原理.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/slabinfo-kmalloc.png b/_posts/linux/linux工具系列/03/slabinfo-kmalloc.png deleted file mode 100644 index 4246362..0000000 Binary files a/_posts/linux/linux工具系列/03/slabinfo-kmalloc.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/slabinfo.png b/_posts/linux/linux工具系列/03/slabinfo.png deleted file mode 100644 index 980d9c4..0000000 Binary files a/_posts/linux/linux工具系列/03/slabinfo.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/slabtop.png b/_posts/linux/linux工具系列/03/slabtop.png deleted file mode 100644 index f09aa89..0000000 Binary files a/_posts/linux/linux工具系列/03/slabtop.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/slab分配器.png b/_posts/linux/linux工具系列/03/slab分配器.png deleted file mode 100644 index 582ee78..0000000 Binary files a/_posts/linux/linux工具系列/03/slab分配器.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/slab分配图解.png b/_posts/linux/linux工具系列/03/slab分配图解.png deleted file mode 100644 index eb0c797..0000000 Binary files a/_posts/linux/linux工具系列/03/slab分配图解.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/slab结构.png b/_posts/linux/linux工具系列/03/slab结构.png deleted file mode 100644 index a391672..0000000 Binary files a/_posts/linux/linux工具系列/03/slab结构.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/slab结构体.png b/_posts/linux/linux工具系列/03/slab结构体.png deleted file mode 100644 index 84191d3..0000000 Binary files a/_posts/linux/linux工具系列/03/slab结构体.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/伙伴系统.png b/_posts/linux/linux工具系列/03/伙伴系统.png deleted file mode 100644 index cadb39e..0000000 Binary files a/_posts/linux/linux工具系列/03/伙伴系统.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/内存管理slab.drawio b/_posts/linux/linux工具系列/03/内存管理slab.drawio deleted file mode 100644 index 22c1c49..0000000 --- a/_posts/linux/linux工具系列/03/内存管理slab.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/_posts/linux/linux工具系列/03/内部碎片.png b/_posts/linux/linux工具系列/03/内部碎片.png deleted file mode 100644 index 87be819..0000000 Binary files a/_posts/linux/linux工具系列/03/内部碎片.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/03/外部碎片.png b/_posts/linux/linux工具系列/03/外部碎片.png deleted file mode 100644 index 7b2d0f2..0000000 Binary files a/_posts/linux/linux工具系列/03/外部碎片.png and /dev/null differ diff --git a/_posts/linux/linux工具系列/linux_tool_awk.md b/_posts/linux/linux工具系列/linux_tool_awk.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/linux/linux工具系列/linux_tool_lsof.md b/_posts/linux/linux工具系列/linux_tool_lsof.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/linux/linux工具系列/linux_tool_nc.md b/_posts/linux/linux工具系列/linux_tool_nc.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/linux/linux工具系列/linux_tool_netstat.md b/_posts/linux/linux工具系列/linux_tool_netstat.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/linux/linux工具系列/linux_tool_sed.md b/_posts/linux/linux工具系列/linux_tool_sed.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/linux/linux工具系列/linux_tool_xdd.md b/_posts/linux/linux工具系列/linux_tool_xdd.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/linux/linux工具系列/linux性能分析工具.md b/_posts/linux/linux工具系列/linux性能分析工具.md deleted file mode 100644 index 79ac4f9..0000000 --- a/_posts/linux/linux工具系列/linux性能分析工具.md +++ /dev/null @@ -1,29 +0,0 @@ -## top - -`top`命令这个大家都非常的熟悉,他也是很多`LInux`发行版自带的系统工具,几乎每一个Linux程序员都会用过这个命令。`top` 可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过top命令所提供的互动式界面,用热键可以管理。 - -vmstat - -iostat - -ifstat - -dstat - -netstat - -perf - -tcpdump - - - - - - - -## Reference - - https://www.open-open.com/lib/view/open1418970861542.html#articleHeader1 - - https://www.open-open.com/news/view/62d559 \ No newline at end of file diff --git a/_posts/linux/进程线程协程/pstack查看lwp.png b/_posts/linux/进程线程协程/pstack查看lwp.png deleted file mode 100644 index de9bb44..0000000 Binary files a/_posts/linux/进程线程协程/pstack查看lwp.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/内核空间地址映射.png b/_posts/linux/进程线程协程/内核空间地址映射.png deleted file mode 100644 index aedb969..0000000 Binary files a/_posts/linux/进程线程协程/内核空间地址映射.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/内核线程图解.png b/_posts/linux/进程线程协程/内核线程图解.png deleted file mode 100644 index bbc3d71..0000000 Binary files a/_posts/linux/进程线程协程/内核线程图解.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/加星标操作.png b/_posts/linux/进程线程协程/加星标操作.png deleted file mode 100644 index 10a2532..0000000 Binary files a/_posts/linux/进程线程协程/加星标操作.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/协程切换.png b/_posts/linux/进程线程协程/协程切换.png deleted file mode 100644 index 5682f95..0000000 Binary files a/_posts/linux/进程线程协程/协程切换.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/协程图解.png b/_posts/linux/进程线程协程/协程图解.png deleted file mode 100644 index 6deff64..0000000 Binary files a/_posts/linux/进程线程协程/协程图解.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/协程子程序模型.png b/_posts/linux/进程线程协程/协程子程序模型.png deleted file mode 100644 index 9ec3250..0000000 Binary files a/_posts/linux/进程线程协程/协程子程序模型.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/协程调度.png b/_posts/linux/进程线程协程/协程调度.png deleted file mode 100644 index 291da2b..0000000 Binary files a/_posts/linux/进程线程协程/协程调度.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/用户空间内核空间.png b/_posts/linux/进程线程协程/用户空间内核空间.png deleted file mode 100644 index cfe332c..0000000 Binary files a/_posts/linux/进程线程协程/用户空间内核空间.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/用户线程图解.png b/_posts/linux/进程线程协程/用户线程图解.png deleted file mode 100644 index 1ec8210..0000000 Binary files a/_posts/linux/进程线程协程/用户线程图解.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/看完这篇,彻底区分进程线程协程.md b/_posts/linux/进程线程协程/看完这篇,彻底区分进程线程协程.md deleted file mode 100644 index eaae817..0000000 --- a/_posts/linux/进程线程协程/看完这篇,彻底区分进程线程协程.md +++ /dev/null @@ -1,268 +0,0 @@ -很早之前就在构思这个主题,进程线程可以说是操作系统基础,看过很多关于这方面知识的文章都是纯理论讲述,编程新手有些难以下咽。于是写下这篇文章,用图解的形式带你学习和掌握进程、线程、协程,文字力求简单明了,对于复杂概念做到一个概念一张图解,即使你是编程小白也能看的明明白白,妈妈再也不用担心你的学习。 - - ![看错你了!你居然是个沉迷学习的家伙! - 做个爱学习的 doge_学习_doge_装逼_萌萌哒表情](http://wx3.sinaimg.cn/large/bef3df8agy1fcnwgg2stpj208c08cjs4.jpg) - -这周暂时不更新 Go 基础教程系列,教程更新已接近尾声,对 Go 语言学习感兴趣但还没看过的的同学,可以在公众号历史文章查看。Go 基础教程接下来会进入并发编程和 Go 协程部分,为了更好的理解这部分内容,带大家先了解 Linux 系统基础和进程、线程以及协程的差异与特点。 - -在操作系统课程的学习中,很多人对进程线程有大体的认识,但操作系统教材更偏向于理论叙述,本文会结合 Linux 系统实现分析,更加印象深刻。 - -同时,大部分人都接触进程和线程比较多,对协程知之甚少,然而最近协程并发编程技术火热起来,希望读完本文你对协程也有一个基本的了解。 - -话不多说,我们马上进入本文的学习。 - - - -## 进程 - -关于进程和内存管理我之前有一篇文章单独讲解过,这里再挑选一部分和本文相关的内容学习,温故而知新。 - -首先还是说下「程序」的概念,程序是一些保存在磁盘上的指令的有序集合,是静态的。**进程是程序执行的过程**,包括了动态创建、调度和消亡的整个过程,**进程是程序资源管理的最小单位**。 - -### 进程与资源 - -那么进程都管理哪些资源呢? 通常包括内存资源、IO资源、信号处理等部分。 - -![程序和进程](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\程序和进程.png) - -篇幅有限着重说一下内存管理,进程运行起来必然会涉及到对内存资源的管理。内存资源有限,**操作系统采用虚拟内存技术,把进程虚拟地址空间划分成用户空间和内核空间。** - -### 地址空间 - -`4GB` 的进程虚拟地址空间被分成两部分:用户空间和内核空间 - -![用户空间内核空间](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\用户空间内核空间.png) - -#### 用户空间 - -用户空间按照**访问属性一致的地址空间存放在一起的原则**,划分成 `5`个不同的内存区域。 访问属性指的是“可读、可写、可执行等 。 - -- 代码段 - - 代码段是用来存放可执行文件的操作指令,可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,它是不可写的。 - -- 数据段 - - 数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。 - -- BSS段 - - ` BSS `段包含了程序中未初始化的全局变量,在内存中 `bss` 段全部置零。 - -- 堆 ` heap` - - 堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减) - -- 栈 `stack` - - 栈是用户存放程序临时创建的局部变量,也就是函数中定义的变量(但不包括 `static` 声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。 - -上述几种内存区域中数据段、`BSS` 段、堆通常是被连续存储在内存中,在位置上是连续的,而代码段和栈往往会被独立存放。堆和栈两个区域在 `i386` 体系结构中栈向下扩展、堆向上扩展,相对而生。 -![程序内存分段](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\程序内存分段.png) - -你也可以再 linux 下用`size` 命令查看编译后程序的各个内存区域大小: - -```shell -[lemon ~]# size /usr/local/sbin/sshd - text data bss dec hex filename -1924532 12412 426896 2363840 2411c0 /usr/local/sbin/sshd -``` - - - -#### 内核空间 - -在 `x86 32` 位系统里,Linux 内核地址空间是指虚拟地址从 `0xC0000000` 开始到 `0xFFFFFFFF` 为止的高端内存地址空间,总计 `1G` 的容量, 包括了内核镜像、物理页面表、驱动程序等运行在内核空间 。 - -![内核空间地址映射](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\内核空间地址映射.png) - -## 线程 - -线程是操作操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程中的实际运作单位,一个进程内可以包含多个线程,**线程是资源调度的最小单位。** - -![多线程程序模型](https://imgkr.cn-bj.ufileos.com/73d34b23-bb9d-4193-a496-b00eb036e05e.png) - - - -### 线程资源和开销 - -同一进程中的多条线程共享该进程中的全部系统资源,如虚拟地址空间,文件描述符文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈、寄存器环境、线程本地存储等信息。 - -线程创建的开销主要是线程堆栈的建立,分配内存的开销。这些开销并不大,最大的开销发生在线程上下文切换的时候。 - -![线程切换](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\线程切换.png) - -### 线程分类 - -还记得刚开始我们讲的内核空间和用户空间概念吗?线程按照实现位置和方式的不同,也分为用户级线程和内核线程,下面一起来看下这两类线程的差异和特点。 - -#### 用户级线程 - -实现在用户空间的线程称为用户级线程。用户线程是完全建立在用户空间的线程库,用户线程的创建、调度、同步和销毁全由用户空间的库函数完成,不需要内核的参与,因此这种线程的系统资源消耗非常低,且非常的高效。 - -##### 特点 - -- 用户线级线程只能参与竞争该进程的处理器资源,不能参与全局处理器资源的竞争。 -- 用户级线程切换都在用户空间进行,开销极低。 -- 用户级线程调度器在用户空间的线程库实现,内核的调度对象是进程本身,内核并不知道用户线程的存在。 - -![用户线程图解](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\用户线程图解.png) - -##### 缺点 - -- 如果触发了引起阻塞的系统调用的调用,会立即阻塞该线程所属的整个进程。 -- 系统只看到进程看不到用户线程,所以只有一个处理器内核会被分配给该进程 ,也就不能发挥多核 CPU 的优势 。 - - - -#### 内核级线程 - -内核级线程是指,内核线程建立和销毁都是由操作系统负责、通过系统调用完成的,内核维护进程及线程的上下文信息以及线程切换。 - -##### 特点 - -- 内核级线级能参与全局的多核处理器资源分配,充分利用多核 CPU 优势。 -- 每个内核线程都可被内核调度,因为线程的创建、撤销和切换都是对内核管理的。 -- 一个内核线程阻塞与他同属一个进程的线程仍然能继续运行。 - -![内核线程图解](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\内核线程图解.png) - -##### 缺点 - -- 内核级线程调度开销较大。调度内核线程的代价可能和调度进程差不多昂贵,代价要比用户级线程大很多。 -- 线程表是存放在操作系统固定的表格空间或者堆栈空间里,所以内核级线程的数量是有限的。 - - - -### Linux 线程实现 - -`Linux` 并没有为线程准备特定的数据结构,因为 Linux只有` task_struct`这一种描述进程的结构体。在内核看来只有进程而没有线程,线程调度时也是当做进程来调度的。Linux所谓的线程其实是与其他进程共享资源的**轻量级进程**。 - -为什么说是轻量级呢?在于它只有一个最小的执行上下文和调度程序所需的统计信息,它只带有进程执行相关的信息,与父进程共享进程地址空间 。 - -#### 轻量级进程 - -轻量级线程 `Light-weight Process `简称` LWP ` ,是一种由内核支持的用户线程,每一个轻量级进程都与一个特定的内核线程关联。 - -它是基于内核线程的高级抽象,系统只有先支持内核线程才能有 `LWP`。每一个进程有一个或多个 `LWPs` ,每个` LWP ` 由一个内核线程支持,在这种实现的操作系统中 `LWP` 就是用户线程。 - -![轻量级进程](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\轻量级进程.png) - -轻量级进程最早在`Linux` 内核 `2.0.x` 版本就已实现,应用程序通过一个统一的 `clone()` 系统调用接口,用不同的参数指定创建的进程是轻量进程还是普通进程。 - -#### 特点和缺点 - -由于轻量轻量级进程基于内核线程实现,因此它的特点和缺点就是内核线程的缺点,这里不再赘述。 - -#### 查看 LWP 信息 - -轻量级线程也没什么神秘的,还记得我在这篇文章《资深程序员总结:分析Linux进程的6个方法,我全都告诉你》教你的方法吗?我们用 Linux 的 `pstack` 命令可以查看进程的轻量级线程 LWP 信息。下图的黄色字体就是打印出的轻量级线程 ID ,以及该线程的调用堆栈信息,从最新的栈帧开始往下排列。 - -用法示例: `pstack pid ` - -![pstack查看lwp](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\pstack查看lwp.png) - - - -## 协程 - -协程的知名度好像不是很高,在以前我们谈论高并发,大部分人都知道利用多线程和多进程部署服务,提高服务性能,但一般不会提到协程。其实协程的概念出来的比线程还早,只不过最近才被人们更多的提起。 - -协程之所以最近被大家熟知,个人觉得是 `Python` 和 `Go` 从语言层面提供了对协程更好的支持,尤其是以 `Goroutine` 为代表的 Go 协程实现,很大程度上降低了协程使用门槛,可以说是后起之秀了! - - ![后起之秀都没你秀_后起之秀表情](http://ww2.sinaimg.cn/large/9150e4e5ly1fmwsrnwcc3g208c08cgn5.gif) - -### why 协程 - -当今无数的 Web 服务和互联网服务,本质上大部分都是 IO 密集型服务,什么是 IO 密集型服务?意思是处理的任务大多是和网络连接或读写相关的高耗时任务,高耗时是相对 CPU 计算逻辑处理型任务来说,两者的处理时间差距不是一个数量级的。 - -**IO 密集型服务的瓶颈不在 CPU 处理速度,而在于尽可能快速的完成高并发、多连接下的数据读写。** - -**以前有两种解决方案:** - -- 如果用多线程,高并发场景的大量 IO 等待会导致多线程被频繁挂起和切换,非常消耗系统资源,同时多线程访问共享资源存在竞争问题。 - -- 如果用多进程,不仅存在频繁调度切换问题,同时还会存在每个进程资源不共享的问题,需要额外引入进程间通信机制来解决。 - -**协程出现给高并发和 IO 密集型服务开发提供了另一种选择。** - -当然,世界上没有技术银弹,在这里我想把协程这把钥匙交到你手中,但是它也不是万能钥匙,最好的解决方案是贴合自身业务类型做出最优选择,不一定就选择一种模型,有时候是几种模型的组合,比如多线程搭配协程是常见的组合。 - - - -### 什么是协程 - -**那什么是协程呢?协程 `Coroutines` 是一种比线程更加轻量级的微线程。**类比一个进程可以拥有多个线程,一个线程也可以拥有多个协程,因此协程又称微线程和纤程。 - -![协程图解](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\协程图解.png) - -**可以粗略的把协程理解成子程序调用,每个子程序都可以在一个单独的协程内执行。** - -![协程子程序模型](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\协程子程序模型.png) - -### 调度开销 - -线程是被内核所调度,线程被调度切换到另一个线程上下文的时候,需要保存一个用户线程的状态到内存,恢复另一个线程状态到寄存器,然后更新调度器的数据结构,这几步操作设计用户态到内核态转换,开销比较多。 - -![线程切换](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\线程切换.png) - -协程的调度完全由用户控制,协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作用户空间栈,完全没有内核切换的开销。 - -![协程切换](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\协程切换.png) - - - -### 动态协程栈 - -协程拥有自己的寄存器上下文和栈,协程调度切换时将寄存器上下文和栈保存下来,在切回来的时候,恢复先前保存的寄存器的上下文和栈。 - -Goroutine 是 Golang 的协程实现。Goroutine 的栈只有 2KB大小,而且是动态伸缩的,可以按需调整大小,最大可达 1G 相比线程来说既不浪费又灵活了很多,可以说是相当的nice了! - -线程也都有一个固定大小的内存块来做栈,一般会是 2MB 大小,线程栈会用来存储线程上下文信息。2MB 的线程栈和协程栈相比大了很多。 - -![线程和协程栈对比](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\线程和协程栈对比.png) - - - -### 协程实现 - -#### Python协程实现 - -正如刚才所写的代码示例,`python 2.5` 中引入 `yield/send` 表达式用于实现协程,但这种通过生成器的方式使用协程不够优雅。 - - `python 3.5` 之后引入` async/await` ,简化了协程的使用并且更加便于理解。 - - - -#### Go语言协程实现 - -Golang 在语言层面实现了对协程的支持,`Goroutine` 是协程在 Go 语言中的实现, 在 Go 语言中每一个并发的执行单元叫作一个 `Goroutine` ,Go 程序可以轻松创建成百上千个协程并发执行。 - -Go 协程调度器有三个重要数据结构: - -- G 表示 `Goroutine` ,它是一个待执行的任务; - -- M 表示操作系统的线程,它由操作系统的调度器调度和管理; - -- P 表示处理器 ` Processor `,它可以被看做运行在线程上的本地调度器; - -![协程调度](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\协程调度.png) - -Go 调度器最多可以创建 10000 个线程,但可以通过设置 `GOMAXPROCS` 变量指能够正常运行的运行, 这个变量的默认值 等于 CPU 个数,也就是线程数等于 CPU 核数,这样不会触发操作系统的线程调度和上下文切换,所有的调度由 Go 语言调度器触发,都是在用户态,减少了非常多的调用开销。 - - - -## 总结 - -这篇文章讲解和对比了进程、线程的概念,同时通过进程窥探到操作系统内存管理的冰山一角,另外还讲解了具体到 Linux 系统下线程的实现现状,顺势引出了轻量级进程的概念。最后着重说明了大部分同学不太了解的协程,通过对比不同的服务模型,带你了解协程的特点。 - -说明一下,文中的图片都是我手绘的,原图我没打水印方便阅读,不过微信发出去之后会对图片压缩和打上水印,如果你想要下载高清原图,留着时不时温故知新,我也乐意无偿提供原图给你下载,在公众号「后端技术学堂」回复「进程」即可获取,对知识的理解有时候真的是一图胜千言,也是我文章的价值所在。 - -## 再聊两句 - -最近公众号改版了,我发的文章可能不能出现在你的消息列表中,不敢称自己是小号主,对于我这种 mini 号主影响还是很大的,写公众号文章的都知道,发出去的文章都想让更多人看到,才有机会得到持续的正向反馈,激励我持续创作和分享。 - -**所以,如果觉得文章写的还行,对你有点帮助,就动动手指点个「在看」顺手加个「星标」让我们还能及时再见。** - -![加星标操作](F:\github\lemonchann.github.io\_posts\linux\进程线程协程\加星标操作.png) - -感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。今天的技术分享就到这里,我们下期再见。 \ No newline at end of file diff --git a/_posts/linux/进程线程协程/程序内存分段.png b/_posts/linux/进程线程协程/程序内存分段.png deleted file mode 100644 index 65b705f..0000000 Binary files a/_posts/linux/进程线程协程/程序内存分段.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/程序和进程.png b/_posts/linux/进程线程协程/程序和进程.png deleted file mode 100644 index 481c927..0000000 Binary files a/_posts/linux/进程线程协程/程序和进程.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/线程切换.png b/_posts/linux/进程线程协程/线程切换.png deleted file mode 100644 index b731b78..0000000 Binary files a/_posts/linux/进程线程协程/线程切换.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/线程和协程栈对比.png b/_posts/linux/进程线程协程/线程和协程栈对比.png deleted file mode 100644 index 54473b7..0000000 Binary files a/_posts/linux/进程线程协程/线程和协程栈对比.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/轻量级进程.png b/_posts/linux/进程线程协程/轻量级进程.png deleted file mode 100644 index fb5d6a0..0000000 Binary files a/_posts/linux/进程线程协程/轻量级进程.png and /dev/null differ diff --git a/_posts/linux/进程线程协程/进程线程协程.drawio b/_posts/linux/进程线程协程/进程线程协程.drawio deleted file mode 100644 index 3806705..0000000 --- a/_posts/linux/进程线程协程/进程线程协程.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/_posts/linux/高并发服务模型总结/FIFO图解.png b/_posts/linux/高并发服务模型总结/FIFO图解.png deleted file mode 100644 index 168db7c..0000000 Binary files a/_posts/linux/高并发服务模型总结/FIFO图解.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/IO密集型-网口.jpg b/_posts/linux/高并发服务模型总结/IO密集型-网口.jpg deleted file mode 100644 index 75addb3..0000000 Binary files a/_posts/linux/高并发服务模型总结/IO密集型-网口.jpg and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/cpu.jpg b/_posts/linux/高并发服务模型总结/cpu.jpg deleted file mode 100644 index 4e3faff..0000000 Binary files a/_posts/linux/高并发服务模型总结/cpu.jpg and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/cpuinfo.png b/_posts/linux/高并发服务模型总结/cpuinfo.png deleted file mode 100644 index ab31a0b..0000000 Binary files a/_posts/linux/高并发服务模型总结/cpuinfo.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/socket套接字.png b/_posts/linux/高并发服务模型总结/socket套接字.png deleted file mode 100644 index e8e8001..0000000 Binary files a/_posts/linux/高并发服务模型总结/socket套接字.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/win 查看核心.png b/_posts/linux/高并发服务模型总结/win 查看核心.png deleted file mode 100644 index f5f2a29..0000000 Binary files a/_posts/linux/高并发服务模型总结/win 查看核心.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/互斥锁api.png b/_posts/linux/高并发服务模型总结/互斥锁api.png deleted file mode 100644 index 5067184..0000000 Binary files a/_posts/linux/高并发服务模型总结/互斥锁api.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/信号量API.png b/_posts/linux/高并发服务模型总结/信号量API.png deleted file mode 100644 index 9ff3ca2..0000000 Binary files a/_posts/linux/高并发服务模型总结/信号量API.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/共享内存.png b/_posts/linux/高并发服务模型总结/共享内存.png deleted file mode 100644 index 34274a7..0000000 Binary files a/_posts/linux/高并发服务模型总结/共享内存.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/协程执行时间线.png b/_posts/linux/高并发服务模型总结/协程执行时间线.png deleted file mode 100644 index 2a572bf..0000000 Binary files a/_posts/linux/高并发服务模型总结/协程执行时间线.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/多线程执行模型.png b/_posts/linux/高并发服务模型总结/多线程执行模型.png deleted file mode 100644 index 5104c0d..0000000 Binary files a/_posts/linux/高并发服务模型总结/多线程执行模型.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/执行函数伪代码.png b/_posts/linux/高并发服务模型总结/执行函数伪代码.png deleted file mode 100644 index 79c3e8c..0000000 Binary files a/_posts/linux/高并发服务模型总结/执行函数伪代码.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/投影仪.jpg b/_posts/linux/高并发服务模型总结/投影仪.jpg deleted file mode 100644 index 5b1b013..0000000 Binary files a/_posts/linux/高并发服务模型总结/投影仪.jpg and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/服务模型.drawio b/_posts/linux/高并发服务模型总结/服务模型.drawio deleted file mode 100644 index 56f1126..0000000 --- a/_posts/linux/高并发服务模型总结/服务模型.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/_posts/linux/高并发服务模型总结/条件变量API.png b/_posts/linux/高并发服务模型总结/条件变量API.png deleted file mode 100644 index f5e7331..0000000 Binary files a/_posts/linux/高并发服务模型总结/条件变量API.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/查看核数API.png b/_posts/linux/高并发服务模型总结/查看核数API.png deleted file mode 100644 index 6b8a7c6..0000000 Binary files a/_posts/linux/高并发服务模型总结/查看核数API.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/消息队列图解.png b/_posts/linux/高并发服务模型总结/消息队列图解.png deleted file mode 100644 index 9d00bea..0000000 Binary files a/_posts/linux/高并发服务模型总结/消息队列图解.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/管道图解.png b/_posts/linux/高并发服务模型总结/管道图解.png deleted file mode 100644 index 86be5b3..0000000 Binary files a/_posts/linux/高并发服务模型总结/管道图解.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/自旋锁API.png b/_posts/linux/高并发服务模型总结/自旋锁API.png deleted file mode 100644 index d5cc0db..0000000 Binary files a/_posts/linux/高并发服务模型总结/自旋锁API.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/计算密集型.jpg b/_posts/linux/高并发服务模型总结/计算密集型.jpg deleted file mode 100644 index ab0a061..0000000 Binary files a/_posts/linux/高并发服务模型总结/计算密集型.jpg and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/计算密集型2.jpg b/_posts/linux/高并发服务模型总结/计算密集型2.jpg deleted file mode 100644 index 68e41ac..0000000 Binary files a/_posts/linux/高并发服务模型总结/计算密集型2.jpg and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/设置线程亲和性.png b/_posts/linux/高并发服务模型总结/设置线程亲和性.png deleted file mode 100644 index c597499..0000000 Binary files a/_posts/linux/高并发服务模型总结/设置线程亲和性.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/设置进程亲和性.png b/_posts/linux/高并发服务模型总结/设置进程亲和性.png deleted file mode 100644 index 411b5df..0000000 Binary files a/_posts/linux/高并发服务模型总结/设置进程亲和性.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/读写锁API.png b/_posts/linux/高并发服务模型总结/读写锁API.png deleted file mode 100644 index 1db1a95..0000000 Binary files a/_posts/linux/高并发服务模型总结/读写锁API.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/购物车.jpg b/_posts/linux/高并发服务模型总结/购物车.jpg deleted file mode 100644 index ba4de29..0000000 Binary files a/_posts/linux/高并发服务模型总结/购物车.jpg and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/进程线程关系.png b/_posts/linux/高并发服务模型总结/进程线程关系.png deleted file mode 100644 index f882656..0000000 Binary files a/_posts/linux/高并发服务模型总结/进程线程关系.png and /dev/null differ diff --git a/_posts/linux/高并发服务模型总结/高并发服务模型对比分析.md b/_posts/linux/高并发服务模型总结/高并发服务模型对比分析.md deleted file mode 100644 index 750554c..0000000 --- a/_posts/linux/高并发服务模型总结/高并发服务模型对比分析.md +++ /dev/null @@ -1,426 +0,0 @@ -面试中经常会被问到高性能服务模型选择对比,以及如何提高服务性能和处理能力,这其中涉及操作系统软件和计算机硬件知识,其实都是在考察面试者的基础知识掌握程度,但如果没准备的话容易一头雾水,这次带大家从头到尾学习一遍,学完这一篇再也不怕面试官刨根问底了! - - - -## 任务类型 - -谈高并发服务模型选择之前,我们先来看下程序的的任务类型,程序任务类型一般分为 CPU 密集型任务和 IO 密集型任务,这两种任务有各自的特点,对程序的要求是不一样的需要分开对待。 - - - -### CPU密集型任务 - -一个程序任务大部分是计算类的,比如逻辑处理、数值比较和计算,我们就称它是 CPU 密集型任务或计算密集型任务。CPU 密集型任务的特点是要进行大量的计算,消耗 CPU 资源,比如计算圆周率、视频编解码这些靠的是 CPU 的运算能力。 - -CPU 密集型任务虽然也可以用多任务完成,但是任务越多,任务之间切换的时间就越多,CPU 执行效率反而更低,所以要最高效地利用 CPU,任务并行数应当等于 CPU 的核心数,避免任务在 CPU 核之间频繁切换。 - -![芯片线路](https://upload-images.jianshu.io/upload_images/7842464-fa3f105f2afd7764.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -### IO密集型任务 - -一个程序涉及到大量网络、磁盘等比较耗时的输入输出任务,就称它是 IO 密集型任务,这类任务的特点是 CPU 消耗很少,任务的大部分时间都在等待 IO 操作完成(因为 IO 的速度远远低于 CPU 和内存的速度,不是一个数量级的)。 - -对于 IO 密集型任务,任务越多 CPU 效率越高,但也不是无限的开启多任务,如果任务过多频繁切换的开销也不可忽视。常见的大部分程序都是执行 IO 密集型任务,比如互联网业务的 Web 服务,数据库操作等。 - -![五彩的以太网口](https://upload-images.jianshu.io/upload_images/7842464-3ccf6820d249dffb.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -## 服务模型 - -不管是 CPU 密集型任务还是 IO 密集型任务,**要提高服务器处理能力,可以从软件和硬件两个层面来做文章。** - -先说软件层面,单个任务处理能力有限,可以通过启动多个功能完全相同的服务实例,借此来提高服务整体处理性能,多服务实例的实现主流的技术有三种:**多进程、多线程、多协程**。当然除了用多实例的方式,还有 IO 多路复用、异步 IO 等技术,为了文章主题明确,不在本文展开讨论。 - - - -## 服务模型哪家强 - -既然有三种技术实现,那么你可能会问,在三个模型里选一个最好的来实现服务,该如何选择一个适合的服务模型呢? - - ![](http://wx4.sinaimg.cn/large/6e3e5b9bly1ftfloyx9ygg206y06yhd9.gif) - -抱歉,小孩子才做选择我全都要!哈哈,开个玩笑。 - -答案是没有最好,**服务模型选择要结合自身服务处理的任务类型**。任务类型就是我们上面说的 CPU 密集型和 IO 密集型,只有清楚的知道所处理业务的任务类型,才能在上述服务模型中选择其一或多种模型组合,来搭建适合你的高性能服务框架。 - - - -## 多进程服务模型 - -### 进程概念 - -程序是一些保存在磁盘上的指令的有序集合,是静态的。**进程是程序执行的过程,包括了动态创建、调度和消亡的整个过程,进程是程序资源管理的最小单位**。 - - - -### 多进程模型 - -多进程模型是启动多个服务进程。原来由一个进程做的事,当一个进程忙不过来,创建几个功能一样的进程来帮它一起干活,人多力量大。 - -**由于多进程地址空间不同,数据不能共享,一个进程内创建的变量在另一个进程是无法访问**。操作系统看不下去了,凭什么同在一台机器,彼此相爱的两个进程不能说说话呢? - -于是操作系统提供了各种系统调用,搭建起各个进程间通信的桥梁,这些方法统称为进程间通信 IPC (`IPC InterProcess Communication`) - - - -### 常见进程间通信方式 - -#### 管道 Pipe - -管道的实质是一个内核缓冲区,进程以先进先出 `FIFO` 的方式从缓冲区存取数据。 是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系(父子进程间)的进程间通信。 - -**管道工作原理** - -1. 管道一端的进程顺序的将数据写入缓冲区,另一端的进程则顺序的读出数据。 - -2. 缓冲区可以看做是一个循环队列,一个数据只能被读一次,读出来后在缓冲区就不复存在了。 - -3. 当缓冲区为读空或写满,读数据的进程或写数据进程进入等待队列。 - -4. 空的缓冲区有新数据写入,或者满的缓冲区有数据读出时,唤醒等待队列中的进程继续读写。 - -![管道图解](https://upload-images.jianshu.io/upload_images/7842464-39bdc35e79491189.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -#### 命名管道 FIFO - -上面介绍的管道也称为匿名管道,只能用于亲缘关系的进程间通信。为了克服这个缺点,出现了有名管道 FIFO 。有名管道提供了一个路径名与之关联,以文件形式存在于文件系统中,这样即使不存在亲缘关系的进程,只要可以访问该路径也能相互通信。 - -命名管道支持同一台计算机的不同进程之间,可靠的、单向或双向的数据通信。 -![FIFO图解.png](https://upload-images.jianshu.io/upload_images/7842464-5a23ebc5a22dce10.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - -#### 信号 Signal - -信号是Linux系统中用于进程间互相通信或者操作的一种机制,信号可以在任何时候发给某一进程,无需知道该进程的状态。如果该进程当前不是执行态,内核会暂时保存信号,当进程恢复执行后传递给它。 - -如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消是才被传递给进程。 - -信号在用户空间进程和内核之间直接交互,内核可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件主要有两个来源: - -- 硬件来源:用户按键输入`Ctrl+C`退出、硬件异常如无效的存储访问等。 -- 软件终止:终止进程信号、其他进程调用 kill 函数、软件异常产生信号。 - - - -#### 消息队列 Message Queue - -消息队列是存放在内核中的消息链表,每个消息队列由消息队列标识符表示, 只有在内核重启或主动删除时,该消息队列才会被删除。 - -消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 另外,某个进程往一个消息队列写入消息之前,并不需要另外读进程在该队列上等待消息的到达。 -![消息队列图解](https://upload-images.jianshu.io/upload_images/7842464-6bbd3c9658da6116.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - - - -#### 共享内存 Shared memory - -共享内存是一个进程把地址空间的一段,映射到能被其他进程所访问的内存,一个进程创建、多个进程可访问,进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。 - -共享内存使得多个进程可以可以直接读写同一块内存空间,是最快的可用 IPC 形式,是针对其他通信机制运行效率较低而设计的。共享内存往往与其他通信机制,如信号量配合使用,来实现进程间的同步和互斥通信。 - -![共享内存](https://upload-images.jianshu.io/upload_images/7842464-49dd28489df7c751.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -#### 套接字 Socket - -套接字你可能没听过这个名字,但绝对是接触的最多的一种进程间通信方式。因为我们熟悉的 TCP/IP 协议栈,也是建立在 socket 通信之上,TCP/IP 构建起了当前的互联网通信网络。 - -它是一种通信机制,凭借这种机制,既可以在本机进程间通信,也可以跨网络通过,因为,套接字通过网络接口将数据发送到本机的不同进程或远程计算机的进程。 - -![socket套接字](https://upload-images.jianshu.io/upload_images/7842464-5ce209027aad3ab5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - - - -## 多线程服务模型 - -### 线程概念 - -线程是操作操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程中的实际运作单位,一个进程内可以包含多个线程,**线程是资源调度的最小单位。** -![进程线程关系](https://upload-images.jianshu.io/upload_images/7842464-ceb03bb0f45c2529.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -### 多线程模型 - -启动多个相同功能的进程能提高服务处理能力,但由于各个进程的地址空间相互隔离,通信不便。 - -于是,多线程服务模型出场。通过前面的学习我们知道,一个进程内的多个线程可以共享进程的全部系统资源。进程内创建的多个线程都可以访问进程内的全局变量。 - -当然没有免费的午餐,线程虽然能方便的访问进程资源,但也带来了额外的问题。**比如多线程访公共资源带来的同步与互斥问题,不同线程访问资源的先后顺序会相互影响,如果不做好同步和互斥会产生预期之外的结果,甚至死锁。** - - - -### 什么是多线程同步 - -多线程同步是线程之间的一种直接制约关系,一个线程的执行依赖另一个线程的通知,当它没有得到另一个线程的通知时必须等待,直到消息到达时才被唤醒,即有很强的执行先后关系。 - -比如你搭建了一个商城服务。这个服务的下单流程是这样的:第一步必须要先挑选商品加入购物车,第二步才能结账计算订单金额,假设这两个步骤的操作分别由两个线程去完成,则这两个线程的操作顺序很重要,必须是先下单再结账,这就是线程同步。 -![购物车](https://upload-images.jianshu.io/upload_images/7842464-7d5be1a8cff4d3e4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - - - -### 什么是多线程互斥 - -多线程互斥指的是多线程对资源访问的排他性。所谓排他性,就是当有多个线程都要使用某一共享资源时,任何时刻最多只允许一个线程获得对这个共享资源的使用权,当共享资源被其中一个线程占有时,其他未获得资源的线程必须等待,直到占用资源的线程释放资源。 - -打个比方,你们班只有一台投影仪,当一个同学在上面放电影的时候,如果老师进来上课要用这个投影仪,那就只能由这个同学放弃投影仪的使用权,交给老师上课投影使用,对,教室里唯一的投影仪是共享资源,具有排他性,老师和学生比作是两个线程的话,那这两个线程是互斥的访问共享资源(投影仪)。 - -![投影仪](https://upload-images.jianshu.io/upload_images/7842464-b00adb79e813cb9b.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - -### 多线程同步和互斥方法 - -Linux 系统提供以下几种方法来解决多线程的同步和互斥问题,分别是:互斥锁、条件变量、读写锁、自旋锁、条件变量。 - -#### 互斥锁(同步) - -互斥锁的作用是对临界区加以保护,以使任意时刻只有一个线程能够执行临界区的代码,实现了多线程对临界资源的互斥访问。 - -互斥锁接口函数: - -![互斥锁api](https://upload-images.jianshu.io/upload_images/7842464-ffa131b62388e292.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -#### 条件变量(同步) - -条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。适合多个线程等待某个条件的发生,**不使用条件变量,那么每个线程就不断尝试互斥锁并检测条件是否发生,浪费系统资源**。 - -通常条件变量和互斥锁同时使用。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件,可以用来实现线程间的同步。 - -条件变量系统 API 如下: - -![条件变量API](https://upload-images.jianshu.io/upload_images/7842464-45eba3ee7a65bc69.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -#### 读写锁(同步) - -互斥量要么是加锁状态,要么是不加锁状态,而且一次只有一个线程对其进行加锁。读写锁可以有3种状态:**读加锁状态、写加锁状态和不加锁状态**。 - -一次只有一个线程可以占有写模式读写锁,但是可以有多个线程同时占有读模式的读写锁。**因此,读写锁适合于对数据结构的读次数比写次数多得多的情况,且读写锁比互斥量具有更高的并行性。** - -读写锁加锁规则 - -1:如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁; - -2:如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。 - -读写锁系统 API - -![读写锁API](https://upload-images.jianshu.io/upload_images/7842464-a79fea4427e60dc5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -#### 自旋锁(同步) - -互斥锁得不到锁时,线程会进入休眠,引发任务上下文切换,任务切换涉及一系列耗时的操作,因此用互斥锁一旦遇到阻塞切换代价是十分昂贵的。 - -而自旋锁阻塞后不会引发上下文切换,当锁被其他线程占有时,获取锁的线程便会进入自旋,不断检测自旋锁的状态,直到得到锁,**所谓的自旋就是循环等待的意思。** - -自旋锁在用户态使用的比较少,在内核使用的比较多。自旋锁适用于临界区代码比较短,锁的持有时间比较短的场景,否则会让其他线程一直等待造成饥饿现象。 - -自旋锁 API 接口 - -![自旋锁API](https://upload-images.jianshu.io/upload_images/7842464-d3d5bf6bb398aec8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -#### 信号量(同步与互斥) - -信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。 - -信号量是一个特殊类型的变量,它可以被增加或者减少。可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。但对其的访问被保证是原子操作,即使在一个多线程程序中也是如此。 - -信号量类型: - -- 二进制信号量,它只有0和1两种取值。适用于临界代码每次只能被一个执行线程运行,就要用到二进制信号量。 - -- 计数信号量。它可以有更大的取值范围,适用于临界代码允许有限数目的线程执行,就需要用到计数信号量。 - -信号量 API - -![信号量API](https://upload-images.jianshu.io/upload_images/7842464-6b4c16c2b27701f4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -## 协程服务模型 - -### 什么是协程 - -**那什么是协程呢?协程 `Coroutines` 是一种比线程更加轻量级的微线程。**类比一个进程可以拥有多个线程,一个线程也可以拥有多个协程,因此协程又称微线程和纤程。 - -![协程图解](https://upload-images.jianshu.io/upload_images/7842464-d7578a21d6777d42.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -**可以粗略的把协程理解成子程序调用,每个子程序都可以在一个单独的协程内执行。** - -![协程子程序模型](https://upload-images.jianshu.io/upload_images/7842464-622364388e1ac260.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -### 协程服务模型 - -为了说明什么是协程模型,**先用多线程下的生产者消费者模型举个栗子。** - -启动两个线程分别执行两个函数 `Do_some_IO` 和 `Do_some_process` ,第一个做耗时的 IO 处理操作,第二个对 IO 操作结果做快速的处理计算工作。伪代码如下: - -![函数伪代码](https://upload-images.jianshu.io/upload_images/7842464-2618724977c152b6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -多线程执行过程是这样的: - -1. 生产者线程先调用函数 `Do_some_IO` 做比较耗时的 IO 操作,比如从网络套接字中读取数据这类操作。 - -2. 在生产者线程执行 `Do_some_IO` 完成数据读取之前,消费者线程要阻塞等待。 - -3. 在消费者线程执行 `Do_some_process` 完成数据处理完成之前,生产者线程要阻塞等待。 - -4. 在消费者线程执行 `Do_some_process` 完成数据处理完成之后,要通知生成者线程继续 `Do_some_IO` -![多线程执行模型](https://upload-images.jianshu.io/upload_images/7842464-c1236814c8f46ec6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -**可以看到,多线程模型为了保证各个线程并行工作,需要额外做很多线程间的同步和通知工作,而且线程频繁的在阻塞和唤醒间切换,我们知道 Linux 下线程是轻量级线程 `LWP` ,每次线程切换涉及用户态和内核态的切换,还是很消耗性能的。** - -同样的场景在协程模型里是怎么处理的呢?还是用前面的例子,说明协程模型的执行流程。 - -```go -Do_some_IO() // IO处理协程 -Do_some_process() // 计算处理协程 -``` - -1. 分配生产者协程执行 `Do_some_IO` 做 IO 处理操作,分配消费者协程执行 `Do_some_process` 计算处理操作。 -2. 在生产者协程工作期间,消费者协程保持等待。 -3. 当生产者协程完成 IO 处理,返回处理结果给消费者,并把程序执行权限交给消费者协程向下执行。 - -![协程执行时间线.png](https://upload-images.jianshu.io/upload_images/7842464-a904c0a135b21cfd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -### 协程优势 - -- **由于协程在线程内实现,因此始终都是一个线程操作共享资源,所以不存在多线程抢占资源和资源同步问题。** - -- **生产者协程和消费者协程,互相配合协作完成工作,而不是相互抢占,而且协程创建和切换的开销比线程小得多。** - - - -## 硬件提升性能 - -**前面讲的多线程、多进程、协程都还只是软件层面的提高服务处理能力**。真正硬核的是从**硬件层面**提高处理能力,增加 CPU 物理核心数目,当然硬件都是有成本的,所以只有软件层面已经充分榨干性能才会考虑增加硬件。 - -不过,老板有钱买最好最贵的服务器另说,这是人民币玩家和穷逼玩家的区别了,软件工程师留下了贫困的泪水。 -![](https://upload-images.jianshu.io/upload_images/7842464-4842fc28adbc3f1e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -### 增加机器核心数 - -**CPU领域有一条摩尔定律:大概 18 个月会将芯片的性能提高一倍**。现在这个定律变的越来越难以突破,CPU 晶体管密度工作频率很难再提高,转而通过增加 CPU 核心数目的方式提高处理器性能。 - -![cpu.jpg](https://upload-images.jianshu.io/upload_images/7842464-801dbf9d1282d265.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -目前商用服务器架构基本都是多核处理器,多核的处理器能够真正做到程序并行运行,处理效率大幅度提升,那该如何查看 CPU 核心数目呢? - -对于 Windows 操作系统,打开任务管理器,通过界面的「内核」和「逻辑处理器」能看到。 - -![win 查看核心](https://upload-images.jianshu.io/upload_images/7842464-4386765b9cd315e7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -### 查看 cpu 核心数 - -对于 Linux 操作系统,通过下面 2 种方式查看 CPU 核心相关信息。 - -#### 1. 通过cpuinfo文件查看 - -使用`cat /proc/cpuinfo`查看 cpu 核心信息,如下两个信息: - -- processor,指明第几个cpu处理器 -- cpu cores,指明每个处理器的核心数 - -`cpuinfo` 输出示例: - -![cpuinfo](https://upload-images.jianshu.io/upload_images/7842464-ada58d977683c24c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -#### 2. 通过编程接口查看 - -除了上面以文件的形式查看 cpu 核心信息之外,系统还提供了编程接口可以查询,系统 API 如下。 - -![查看核数API](https://upload-images.jianshu.io/upload_images/7842464-13e58fed363609b9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -### CPU亲和性 - -CPU 亲和性是绑定某一进程或线程到特定的 CPU 或 CPU 集合,从而使得该进程或线程只能被调度运行在绑定的 CPU或 CPU 集合上。 - -> 为什么要设置 CPU 亲和性绑定 CPU 呢?理论上进程上一次运行后的上下文信息会保留在 CPU 的缓存中,如果下一次仍然将该进程调度到同一个 CPU 上,就能避免缓存未命中对 CPU 处理性能的影响,从而使得进程的运行更加高效。 - -假如某些进程或线程是 CPU 密集型的,不希望被频繁调度,又或者你有其他特殊需求,不希望进程或线程被调度在不同 CPU 之间频繁切换,则可以将该进程或线程绑定到特定的 CPU 上 ,可以在特定场景下优化程序性能。 - -### 绑定进程 - -在多进程模型中,绑定进程到特定的核心,下面是绑定进程的系统 API -![设置进程亲和性](https://upload-images.jianshu.io/upload_images/7842464-a5ff062125871f1f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - -### 绑定线程 - -在多线程模型中,绑定线程到特定的核心,下面是绑定线程的系统 API - -![设置线程亲和性](https://upload-images.jianshu.io/upload_images/7842464-c32659e5e7552d3b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - -## 总总结结 - -本文从程序任务类型出发,区分任务为 CPU 密集型和 IO 密集型两大类。接着分别说明提高基于这两类任务的服务性能方法,分为软件层面的方法和硬件层面的方法,其中软件层面主要讲述利用多进程、多线程以及协程模型,当然现有的技术还有 IO 多路复用、异步 IO 、池化技术等方案。讲到多线程和多进程,顺势说明了进程间通信和线程间同步互斥技术。 - -第二部分,讲解了从硬件层面提高服务性能:提高机器核心数,并教你如何查看 CPU 核心数的方法。最后,还可以通过软硬结合的方式,把硬件核心绑定到指定进程或者线程执行,最大程度的利用 CPU 性能。 - -希望通过本文的学习,读者对高性能服务模型有个初步的了解,并能对服务优化的方法和利弊举例一二,就是本文的价值所在。 - -## 再聊两句(求个三连) - -感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。 - -**如果觉得文章写的还行,对你有所帮助,不要白票 lemon,动动手指点个「在看」或「分享」是对我持续创作的最大支持。** - -今天的技术分享就到这里,我们下期再见。 - - -## Reference - - https://cloud.tencent.com/developer/article/1339562 - - https://blog.csdn.net/gatieme/article/details/51481863 - - https://juejin.im/post/5b0014b7518825426e023666 - - https://juejin.im/post/5c13245ee51d455fa5451f33 - - https://blog.csdn.net/zhaoxd200808501/article/details/77067186 - - https://www.jianshu.com/p/5788fb2345ce - - https://zhuanlan.zhihu.com/p/57470627 - - schnappi \ No newline at end of file diff --git a/_posts/other/01/one_month_thinking.md b/_posts/other/01/one_month_thinking.md deleted file mode 100644 index 8025042..0000000 --- a/_posts/other/01/one_month_thinking.md +++ /dev/null @@ -1,48 +0,0 @@ -### 艰难开通的公众号 - -我一直有写技术博客的习惯,很早之前也尝试开通公众号写作,不过那时候公众号申请要用银行卡验证实名信息,很不幸我的招行卡不支持验证,于是这事一直搁置。还保留有当时截图了申请不过的页面 -![申请公号不过](https://upload-images.jianshu.io/upload_images/7842464-bc49115d8049ef78.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -直到今年疫情期间在家,试着去申请发现微信竟然支持招行卡验证了,我的公众号就这么开通了,听说18年之前开通是有评论功能的,想想我第一次申请应该是18年之前,那时候因招行卡不支持搁浅,心痛。 - -万万没想到,我最后还是开通了公众号,那么就从现在开始写吧。 - -### 关于改名 - -细心的小伙伴可能已经发现,周末的时候我改了公众号名称,现在的名字是「后端技术学堂」,为什么要改名字呢?因为一开始取名字是注册的时候拍脑袋想出来的,自己都没想清楚公众号的内容定位。 - -这一个月以来,学习了很多公众号的知识,也参考其他人的经验,觉得很有必要给公众号定一个主题,最好就是,看一眼公号名字就知道是干什么的。 - -于是就改了名字,改名一时爽,改文看瞎眼。名字改了我要把之前发过的文章里带名字的都改一遍,眼睛都快看瞎了,各位没事还是不要学我改名的好。 - - - -### 学习和分享 - -说说开始写公号的初衷。不知道大家有没有听过「学习金字塔」理论。 - -> 学习金字塔是美国缅因州的国家训练实验室研究成果,它用数字形式形象显示了:采用不同的学习方式,学习者在两周以后还能记住内容(平均学习保持率)的多少。它是一种现代学习方式的理论。最早它是由美国学者、著名的学习专家爱德加·戴尔1946年首先发现并提出的。 引用自[百度百科](https://baike.baidu.com/item/%E5%AD%A6%E4%B9%A0%E9%87%91%E5%AD%97%E5%A1%94) - -里面有一张关于「学习内容留存率」和「学习方式」的金字塔关系: - -![学习方式 | 来源百度百科](https://upload-images.jianshu.io/upload_images/7842464-725b737f5daf342c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -按照这个理论,最好的学习方式是「主动学习」并且是「教授给他人」,这种方式学到的知识留存率最高,你能记住很久,大概和「把书读薄」有异曲同工的意思。 - -所以我选择写公众号写博客,把我认为学会的东西再给大家讲一遍,这个过程中我教会了你知识,解决了你的困惑,同时经过「主动学习」加深了自己的知识留存率,对我和读者都是好事,这就是我要坚持写作和分享的根本原因。 - -当然如果我写的好看的人多,给我带来额外的流量收益让我赚到钱,那我会很开心,更有写作动力。「但我不会因为没人看,没收益就放弃写下去」,原因就是我上面说的。 - -另外对于「坚持」这事我还是很有信心的,只要是我认定去做的事都会一直做下去。比如两年前决定坚持每天看两篇国外原版英语文章,因为我渣渣CET6看原版外文吃力的很,直到现在我每天还在做着这事。这里顺便说下我认为英语真的很重要,尤其是做软件技术这一行,一手的资源几乎都是英语世界的,查问题看源码你上stackoverflow、github都需要一定的外语阅读能力。 - -![英语学习打卡](https://upload-images.jianshu.io/upload_images/7842464-7909cdf70cf529e2.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -所以今天先把Flag立在这里,我这个号会维持「每周至少一篇原创文章」的更新频率,文章范畴大部分是技术类,有时候会出来唠唠嗑,就像今天这样,为什么要唠呢,因为我不是服务号我不想给读者冷冰冰机器人的印象,况且我热乎着呢。 - -关于技术类的文章范畴,大部分是我把学的滚瓜烂熟的技术再通俗易懂的讲给你听,当然也会有我刚学习的技术带你一起学,我不是大佬,信奉终身学习,技术路上我也还在打怪升级,如果你愿意可以和我一起学习,一起进阶。 - -以上,感谢阅读。 - diff --git a/_posts/other/01/学习.png b/_posts/other/01/学习.png deleted file mode 100644 index a7bd734..0000000 Binary files a/_posts/other/01/学习.png and /dev/null differ diff --git a/_posts/other/01/打卡.jpg b/_posts/other/01/打卡.jpg deleted file mode 100644 index d9bf744..0000000 Binary files a/_posts/other/01/打卡.jpg and /dev/null differ diff --git a/_posts/other/01/申请公号不过.jpg b/_posts/other/01/申请公号不过.jpg deleted file mode 100644 index 4f13b97..0000000 Binary files a/_posts/other/01/申请公号不过.jpg and /dev/null differ diff --git a/_posts/other/02/996icu.png b/_posts/other/02/996icu.png deleted file mode 100644 index 7327388..0000000 Binary files a/_posts/other/02/996icu.png and /dev/null differ diff --git a/_posts/other/02/买房项目.png b/_posts/other/02/买房项目.png deleted file mode 100644 index 382b389..0000000 Binary files a/_posts/other/02/买房项目.png and /dev/null differ diff --git a/_posts/other/02/我的买保险经历.md b/_posts/other/02/我的买保险经历.md deleted file mode 100644 index 8b76656..0000000 --- a/_posts/other/02/我的买保险经历.md +++ /dev/null @@ -1,147 +0,0 @@ -放假了,大家节日快乐。 - -![](https://imgkr.cn-bj.ufileos.com/39f7afc3-c274-4033-ae41-acc1ef17e471.png) - - -是的,你没看错,这是一个技术号,不卖保险。 - -今天不聊技术,现在这个形势下我没出去玩,聊点轻松的话题,和大家分享我最近半个月买保险的经历,为这事可没少花时间,所以值得拿出来唠唠。 - -说明:以下保险相关观点仅代表个人理解,但经过我实操可行。为避免广告嫌疑,文中不会推销任何保险相关产品,请放心取用。 - -## 动机 - -买保险的念头是几年前就有了。 - -第一次想买的冲动,大概14年刚毕业的时候。那时候移动互联网兴起,各种众筹平台也如雨后春笋般出现,朋友圈经常出现众筹医疗费用的朋友,突然发现原来灾难和疾病离我们并不遥远。当看到身边的朋友突然发朋友圈众筹,我想只有真正经历过的人才会知道「明天和不幸不知道哪个先到来」真正的含义。 - -第二次触动,应该是去年。有个曾经的同事转行卖保险了,天天在朋友圈推销保险,以至于我后来屏蔽掉了她的朋友圈。不过在屏蔽之前,我找她了解下,问她有没有推荐给我的保险类型,当时她推荐了几款,具体是啥我忘了,主要也是那时候纯保险小白,根本记不住那些保险术语。 - -这次疫情的经历,让大家看到生命的脆弱,同时买保险更被我提上了日常,我强迫自己在忙也要抽出时间学保险、买保险,结束裸奔的人生。 - - - -## 从开源项目说起 - -Really? 保险也能给我扯到开源项目上去。 - -![img](https://i03piccdn.sogoucdn.com/203b80064ba764ec) - -别急,听我慢慢说。 - -我的读者中IT从业人员比较多,即使现在你还是学生,以后也大概率是走IT这条路了,众所周知,这个行业的现状(狗头保命)。 - -![](https://www.czxiu.com/assets/z/2018yld/1f/0b69e9fe1a62f049fa44dabd918e59ba.gif) - - - -针对国内互联网公司加班多的情况,`github` 有一个另类项目`996icu` ,该项目收录了各种 996 公司名单,甚至从普法角度分析,有理有据,令人信服。项目一度非常火爆,项目链接:` https://github.com/996icu/996.ICU` - -![](https://upload-images.jianshu.io/upload_images/7842464-d9578beed0658fcf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -真是个神奇的网站, `github` 上还有一个杭州的同学,总结了在杭州买房的经历,洋洋洒洒写了 17 章,从政策谈到交通和选房要点,真可谓大手笔了。项目地址: `https://github.com/houshanren/hangzhou_house_knowledge` - -![](https://upload-images.jianshu.io/upload_images/7842464-e2c7890e60887101.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -**程序员这个群体真是太认真和可爱了,开源精神无处不在**。 - -我现在有 2000 多读者,以后关注我的人也能看到这篇文章,所以,我也希望能把我选保险的经历「开源」给大家(虽然不是发起个项目),这样不仅仅是我知道,我分享给你让你也知道,希望能给你一点参考和帮助。 - -作为一个较真的技术类号主,聊保险我显然不是专业,但我把概念弄清楚了,**这不是一份面面俱到的保险说明书,却是一份简单实用、面向保险小白的绝对干货分享**(甚至自我感觉经过这半个月,我都能当保险业务员了,飘了)。 - - - -## 关于保险你该知道 - -对于我不了解的东西,我喜欢刨根问题彻底搞懂它,如果是别人灌输给我的内容,我也会持怀疑态度,除非得到准确论证。所以总感觉从推销员那里得到的信息是片面的,我更喜欢自己去学习,基于自己掌握的知识做出判断,**至少决定权在我,而不是交给别人。** - -得益于移动互联网的普及,现在网上有很多性价比很高的保险产品,而作为IT从业人员,网上搜集信息的能力正是我们的强项。**这段时间通过各种渠道学习保险知识,通读了好几份每份几十页的保险条款。** - -**整理了下面这些重要概念,买保险之前,大家先了解下。** - -### 我该买保险吗 - -能点进来看这篇文章的人,应该都是至少不排斥买保险的吧。既然我写了这篇文章,那我自己肯定也是认可购买商业保险,尤其是重疾险,不过,这也还是仁者见仁的事,钱和健康都是你的,选择权在你。 - -### 什么时候买 - -如果经济条件允许当然是越早越好,我以后有小孩也会一出生就给他买。更一般的建议,有了自己第一份工作的时候应该考虑买一份,这个时候一方面自己赚钱可以负担保费,另一方面承担的社会责任越来越多需要加一层保障。 - -但如你所见,我现在才在这和你科普保险,说明我自己也是买的迟的,侥幸省了几年保费,庆幸这几年平平安安(我要以后也平平安安)。不过还好,至少还在 30 岁之前,很多保险产品过了 30 岁就贵了很多。 - -### 买保险是在消费 - -说到买保险,很多人比较抵触,因为买保险要花钱啊,花钱谁都心疼,特别是重疾险这种保大病的更贵。个人觉得要买到安心,至少是 5000~10000元一年的保费支出都是在合理的范围之内。 - -我们首先要摆正心态,保险就是花钱买平安,它就是一笔消费,不花钱或者花的少我还不放心呢。同时,也别想着拿保险来赚钱,**个人不建议购买市面上的分红险、万能险这种储蓄型能分红的保险,最好就买纯粹的保障型保险**,其中缘由,大家想想也应该能明白。 - -### 保险公司倒了怎么办 - -不是保险从业者其中的大道理我也不懂,反正就我查了一圈资料,得出的结论是:**中国的保险公司是世界上最安全的保险公司(之一)。** 原因是我们有世界最完善的保险监管体系(再加个之一吧),中国保险公司受到了最全面的监督,卖出去的保单在世界所有国家中中国是最安全的。就算中国的保险公司真的破产,还有保险保障基金来兜底。 - -所以,不用担心买了保险,保险公司倒了保单没有保障,放心买就是。 - -### 保险的分类 - -买保险之前我们先要分清楚保险有哪些分类,保险分为两大类: - -- 人身险。凡是和人生老病死相关的,比如**重疾险、寿险、医疗险、意外险**,都属于这个范畴。 -- 财产险。保财产的,比如车险、房产险等等。 - - - -## 最基本该配置哪些保险 - -下面就说「人身险」的配置。如果你不知道怎么选,就想做个伸手党,那我先说下自己的配置情况,自认比较靠谱,给你做个参考。 - -经过这段时间做的功课,**我个人是买了重疾险和定期寿险,目前单位有买本土医疗险,所以医疗险暂时没买(其实单位也配置了商业重疾险,但是保额太低了,我觉得不够就自己买了)** - -下面一一介绍,其中我认为前两类必选,后两类看自身情况可选。 - -### 重疾险(必选) - -这个就是保重大疾病的保险。重疾的定义是有规定的,由中国保险行业协会与中国医师协会的重疾定义了25种常见的重大疾病,这几种疾病占到了一般人群一辈子能患上重疾的 80%-85%。 - -**所有保险公司不管大小,卖的重疾险都必须包含这基本的25种重疾**,不用担心少了保障。有些公司推出的产品可能会加一些重疾种类,但都是锦上添花的作用,费用合理可以接受,若高太多大可不必。 - -建议重疾险保额最好在 50W 以上,并且至少保到 70 岁。 - -**重疾险是对自己负责,在发生重大疾病时不用朋友圈众筹。** - - - -### 定期寿险(必选) - -寿险顾名思义就是对人寿命保险,定期寿险就是说在你投保的保险期限内(建议最少到70岁),如果发生意外或非意外伤害导致的身故或全残,比如猝死什么的,都是可以赔钱给你的。 - -建议定期寿险保额也至少50W,至少保到70岁。 - -**寿险是在自己发生意外之后,给自己放心不下的人一个保障。** - - - -### 医疗险(可选) - -医疗险是去掉社保部分和1万到2万(具体看产品)免赔额后,花多少报销多少,这个就和重疾险不一样,重疾险一旦符合出险条件,是按照投保的保额给的。 - -而且一般的医疗险是一年期的,也就是说下一年你买的时候可能没了,或者可能涨价了。最近有出了一些新产品可以6年期保证续保(为避免广告嫌疑,名字我就不说了),这款我觉得可以考虑。 - -有了上面推荐的重疾和定寿险,医疗险**可选配置**。 - - - -### 意外险(可选) - -这个没什么好说的,一般是一年一买,长期的又特别贵,不划算。还有就是有些高危职业买不了,对于一般的白领,如果出去旅游为防止意外,买一份是可以的,根据情况**可选配置**。 - - - -## 最后 - -以上就是我最近半个月的买保险学习收获,希望我的这些经验,能帮助到想买保险又无从下手的朋友。 - -最后,唯心主义一下,愿大家买的保险永远用不上,就让这笔钱打水漂去吧! - -节日快乐!我是 lemon 我们下期再见。 \ No newline at end of file diff --git a/_posts/software/git_v2_install.md b/_posts/software/git_v2_install.md deleted file mode 100644 index 85cc39a..0000000 --- a/_posts/software/git_v2_install.md +++ /dev/null @@ -1,86 +0,0 @@ -## yum软件源安装 - -用yum install git 在centos 默认安装的git版本是 1.8.3.1 太低了 - -``` -yum install git -``` - - - -``` -# git --version -git version 1.8.3.1 -``` - - - -## 源码包安装 - -步骤1. 安装依赖包 - -``` -# yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel -# yum install gcc perl-ExtUtils-MakeMaker -``` - -上面的软件不一定全部安装,可以用下面的命令检查,已经安装的就不用安装了。 - -``` -yum list installed |grep xxx -``` - - - -步骤2. 卸载旧的git版本(如果之前有安装rpm包) - -``` -# yum remove git -``` - -步骤3. 下载&解压 - -> 源码文件(当前最新版本 **v2.16.1** @ **2018年2月9日**) -> -> \- 地址1:https://www.kernel.org/pub/software/scm/git/ -> \- 地址2:[https://github.com/git/git/release](https://github.com/git/git/releases) - -``` -# cd /usr/src -# wget https://www.kernel.org/pub/software/scm/git/git-2.5.0.tar.gz -# tar -zxvf git-2.5.0.tar.gz -``` - -步骤4. 编译安装 - -``` -# cd git-2.5.0 -# make prefix=/usr/local/git all -# make prefix=/usr/local/git install -# echo "export PATH=$PATH:/usr/local/git/bin" >> /etc/bashrc -# source /etc/bashrc -``` - -步骤5. 检查git版本 - -``` -# git --version -git version 2.5.0 -``` - - - -**注意:如果安装完查看版本不是我们安装的最新版,请重新执行下面的操作** - -``` -# yum remove -y git -# source /etc/bashrc -# git --version -``` - - - -参考: - -- https://github.com/git/git/blob/master/INSTALL -- http://stackoverflow.com/questions/21820715/how-to-install-latest-version-of-git-on-centos-6-x-7-x \ No newline at end of file diff --git a/_posts/software/vscode的cpp配置/vscode的cpp环境配置.md b/_posts/software/vscode的cpp配置/vscode的cpp环境配置.md deleted file mode 100644 index 78edbbb..0000000 --- a/_posts/software/vscode的cpp配置/vscode的cpp环境配置.md +++ /dev/null @@ -1,112 +0,0 @@ -## 开发环境介绍 - -我工作环境 - - - -## 插件配置 - -### 生成头文件防重复引用宏 - -利用代码片段生成如下的头文件宏定义 - -``` -#ifndef _MY_TEST_FILE_H_ -#define _MY_TEST_FILE_H_ - -// here is you code... - -#endif // _REDIS_FREQ_WRITE_ACTION_H_ -``` - -设置 - -![image-20200510003826796](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200510003826796.png) - -![image-20200510004021821](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200510004021821.png) - -格式可自定义。 - -``` -{ - "C C++ Header": { - "scope": "c, cpp", - "prefix": "header", - "description": "Add #ifndef, #define and #endif", - - "body": [ - "#ifndef _${TM_FILENAME_BASE/(.*)/${1:/upcase}/}_H_", - "#define _${TM_FILENAME_BASE/(.*)/${1:/upcase}/}_H_", - "", - "$0", - "", - "#endif // _${TM_FILENAME_BASE/(.*)/${1:/upcase}/}_H_" - ] - } -} -``` - - - -​ - -### 文件和函数注释 - -安装插件 `koroFileHeader`可以自定义文件头部注释和函数注释。 - -![image-20200510001900009](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200510001900009.png) - -搜索 `filehead` 点击编辑 `settings.json` 打开配置编辑界面。 - -```json -// 文件头部注释 - "fileheader.customMade": { - - "Description": "", - "version": "1.0", - "Author": "lemon", - "Copyright": "(C) BAT", - "Date": "Do not edit", - "History": "Date Author Comment" - }, - - // 函数注释 - "fileheader.cursorMode": { - - - "name": "", - "description":"", - "param": "", - "return": "" - }, - "fileheader.configObj": { - // 自定义语言注释符号,覆盖插件的注释格式 - "language": { - "java": { - "head": "/$$", - "middle": " $ @", - "end": " $/" - }, - // 一次匹配多种文件后缀文件 不用重复设置 - "h/hpp/cpp": { - "head": "/**************************************************", // 注释开头 多个* - "middle": " * @", // 注释中间 * - "end": " ***************************************************/" // 注释结尾 多个* - }, - // 针对有特殊要求的文件如:test.blade.php - "blade.php":{ - "head": "", - } - } - } -``` - - - -### 格式化代码 - -快捷键:Shift+Alt+F - -![image-20200509231323363](C:\Users\linlongchen\AppData\Roaming\Typora\typora-user-images\image-20200509231323363.png) \ No newline at end of file diff --git a/_posts/software/vscode远程开发/SSH程序.png b/_posts/software/vscode远程开发/SSH程序.png deleted file mode 100644 index e6a3b1b..0000000 Binary files a/_posts/software/vscode远程开发/SSH程序.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/SSH配置文件.png b/_posts/software/vscode远程开发/SSH配置文件.png deleted file mode 100644 index 45060ab..0000000 Binary files a/_posts/software/vscode远程开发/SSH配置文件.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/teddy-mother-s-day-love-mama-preview.jpg b/_posts/software/vscode远程开发/teddy-mother-s-day-love-mama-preview.jpg deleted file mode 100644 index 6afa18d..0000000 Binary files a/_posts/software/vscode远程开发/teddy-mother-s-day-love-mama-preview.jpg and /dev/null differ diff --git a/_posts/software/vscode远程开发/vscode远程开发.md b/_posts/software/vscode远程开发/vscode远程开发.md deleted file mode 100644 index fa1481b..0000000 --- a/_posts/software/vscode远程开发/vscode远程开发.md +++ /dev/null @@ -1,134 +0,0 @@ -今天和大家分享远程开发工具,分享一下我平常是如何用 VS Code 进行远程开发工作的,以及一步步教你搭建远程开发环境,拥有比德芙还丝滑的远程开发体验。 - -我们厂里为了最大程度提高工程师生产力,各种研发配套工具非常的齐全,对开发人员每人都有一台云主机,而且是个人主机哦,申请就有的那种,有了云开发主机在家里或者在公司都可以随便折腾,加班也更方便了,好像哪里有点不对。 - -![img](https://i01piccdn.sogoucdn.com/a02eed86bce1545e) - - - -## 传统的远程开发 - -大家都知道我是从事后台服务器开发工作的,主力语言是C/C++。我们的服务端程序一般都是跑在 `Linux` 服务器上面,传统的开发流程是在 window 或 Mac 的 IDE 环境编写代码,然后用 `ftp` 或`sync` 同步代码到开发机上编译,程序或服务最终发布到测试或生产环境运行。 - -就我来说,以前是这么干的:在本地 Windows 和 Linux 云开发机之间开启一个同步程序,本地编辑代码,实时后台同步到 Linux 云开发机,然后用 `xshell` 登录开发机编译、调试,这个过程有点不够优雅,现在有更香的解决方案。 - - - -## 更香的远程开发 - -### 介绍下VS Code - -这是一款开源编辑器,却不止是编辑器。 - -> **Visual Studio Code**(简称**VS Code**)是一个由[微软](https://zh.wikipedia.org/wiki/微软)开发,同时支持[Windows](https://zh.wikipedia.org/wiki/Windows) 、 [Linux](https://zh.wikipedia.org/wiki/Linux)和[macOS](https://zh.wikipedia.org/wiki/MacOS)等操作系统且[开放源代码](https://zh.wikipedia.org/wiki/开放源代码)的[代码编辑器](https://zh.wikipedia.org/wiki/文本编辑器)[[4\]](https://zh.wikipedia.org/wiki/Visual_Studio_Code#cite_note-TechCrunch-4),它支持[测试](https://zh.wikipedia.org/wiki/调试),并内置了[Git 版本控制](https://zh.wikipedia.org/wiki/Git)功能,同时也具有开发环境功能,例如代码补全(类似于 [IntelliSense](https://zh.wikipedia.org/w/index.php?title=IntelliSense&action=edit&redlink=1))、代码片段和[代码重构](https://zh.wikipedia.org/wiki/代码重构)等。该编辑器支持用户个性化配置,例如改变主题颜色、键盘快捷方式等各种属性和参数,同时还在编辑器中内置了扩展程序管理的功能。引用维基百科 - -我最喜欢的是它的插件能力,几乎想要啥功能都能找到插件支持,应该不用我安利你们都会喜欢的。 - -在 2019 年的 Stack Overflow 组织的开发者调研中,VS Code被认为是最受开发者欢迎的开发环境,据调查87317名受访者中有 50.7% 的受访者声称正在使用VS Code。 - -![排名](https://pic3.zhimg.com/v2-3adaf59845ac75ec58b09f8a6c54479d_r.jpg) - - - -说了这么多,反正要表达的就是VS Code很厉害就是了。 - -**重点来了,今天的主角功能是下面这个远程开发扩展插件。** - -微软在 PyCon 2019 大会上发布了VS Code Remote ,从 1.35.0 版本正式提供可以在本地编辑远程开发环境的文件的功能,所以首先确保你的VS Code版本是在这个之上的才能体验到。 - -VS Code远程开发的工作原理,大致是这样的: - -![原理](https://user-gold-cdn.xitu.io/2019/6/7/16b2fdd6d6e51433?imageslim) - - - -下面讲讲如何配置,我的Local OS是 Win10 , Remote OS 是 Linux云主机。 - - - -### 远程开发配置 - -#### 配置SSH环境变量 - -远程开发本地 VS Code 用 SSH 协议与远程服务端通信,所以要先配置SSH环境变量,由于Git自带SSH客户端程序 - -![SSH程序](SSH%E7%A8%8B%E5%BA%8F.png) - -如果你还没装Git的话,这里要先安装 Git,所以配置 Git 的 bin目录到环境变量的 PATH 变量下,这样VS Code连接的时候就能找到它了。 - -![环境变量](%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F.png) - -#### 安装远程开发插件 - -要能连上远程主机,首先我们需要下载VS Code远程开发插件,VS Code其实是提供了一个远程开发插件包,包括: - -![远程开发插件](%E8%BF%9C%E7%A8%8B%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6.png) - -- [Remote - SSH](https://link.zhihu.com/?target=https%3A//marketplace.visualstudio.com/items%3FitemName%3Dms-vscode-remote.remote-ssh) - 通过使用 SSH 链接虚拟或者实体Linux主机。 -- [Remote - Containers](https://link.zhihu.com/?target=https%3A//marketplace.visualstudio.com/items%3FitemName%3Dms-vscode-remote.remote-containers) – 连接 Docker 开发容器。 -- [Remote - WSL](https://link.zhihu.com/?target=https%3A//marketplace.visualstudio.com/items%3FitemName%3Dms-vscode-remote.remote-wsl) - 连接 Windows Subsystem for Linux (Linux子系统)。 - -打开软件的扩展界面,搜索 `Remote` 开头的插件,也能看到这三个的不同远程开发插件,**我们这里连接的是云主机,选择安装 Remote - SSH 插件安装即可。** - -#### 配置远程连接 - -1. 首先点侧边栏的「远程资源管理器」之后点击「设置按钮」,进入远程机器配置界面。 - -![机器配置](%E6%9C%BA%E5%99%A8%E9%85%8D%E7%BD%AE.png) - -2. 修改 ssh 配置文件,用于登录远程机器,各项含义在图中有说明。 - -![SSH配置文件](SSH%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6.png) - -3. 点击连接,登录远程服务器,需要输入几次远程服务器的密码(后面会教你怎么免密登录),输入确认即可。第一次连接会做VS Code Server的初始化工作比较慢,耐心等待。 - -![登录远程服务](%E7%99%BB%E5%BD%95%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1.png) - -4. 登录成功,即可像操作本地环境一样,在VS Code客户端操作远程云主机上的文件。注意,**下图中的「打开文件夹」已经是远端机器上的目录结构了。** - -![打开远程目录](%E6%89%93%E5%BC%80%E8%BF%9C%E7%A8%8B%E7%9B%AE%E5%BD%95.png) - -4. 可以给远程VS Code 安装插件,安装的插件是在云服务器的VS Code上,对本机的VS Code没有影响,插件在远端提供功能,比如代码审查、自动补齐等等,而这所有的一切就像在本地操作一样,对文件的更改也是直接操作的云主机上的文件,丝滑连接。 - -![本地插件和远程插件](%E6%9C%AC%E5%9C%B0%E6%8F%92%E4%BB%B6%E5%92%8C%E8%BF%9C%E7%A8%8B%E6%8F%92%E4%BB%B6.png) - -4. 代码编辑与远程终端调试。打开文件编辑的是云服务器的文件,同时可以打开云服务终端,直接在终端操作编译或者查看云服务器信息。 - -![远程编辑和调试](%E8%BF%9C%E7%A8%8B%E7%BC%96%E8%BE%91%E5%92%8C%E8%B0%83%E8%AF%95.png) - -### 配置SSH免密登录 - -按照上面的配置步骤,每次连接到远程服务器,都需要输入服务器登录密码很麻烦,可以配置SSH免密登录,免去每次输入密码的烦恼,具体操作步骤如下: - -- 打开win cmd终端,输入 ssh-keygen -t rsa 生成秘钥对 - -![秘钥列表](%E7%A7%98%E9%92%A5%E5%88%97%E8%A1%A8.png) - -- 打开生成的秘钥保存路径,拷贝 `id_rsa.pub` 内容,添加到到云服务器的 `~/.ssh/authorized_keys` 文件后面。 - -- 尝试再次连接,不用输密码了,enjoy. - - - -## 写在最后 - -这套远程开发环境体验下来,我整体是比较满意的,最大的好处是不用给电脑装太多软件,选择VS Code是因为需要经常在C++/Python/Go三种IDE之间切换比较麻烦,现在即使是电脑配置低点都没关系,因为所有的编辑器插件扩展和代码都在云端,通过 `SSH` 连接操作一个VS Code打遍所有。 - -而且由于远程开发插件的存在,不论我在哪里,只要有电脑都能方便的打开云端开发环境,非常的方便,这么好用的工具大幅提升生产力,所以今天来分享给大家。 - -老规矩。感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。 - -今天的技术分享就到这里,我们下期再见。 - -对了,今天是母亲节,别忘了给家里打个电话。 - -![泰迪, 母亲节, 爱, 妈妈, 贺卡, 母亲, 欢迎](https://i0.hippopx.com/photos/282/1011/765/teddy-mother-s-day-love-mama-preview.jpg) - -**原创不易,不想被白票,如果在我这有收获,就动动手指「在看」「转发」是对我持续创作的最大支持。** - - - - - - - diff --git a/_posts/software/vscode远程开发/打开远程目录.png b/_posts/software/vscode远程开发/打开远程目录.png deleted file mode 100644 index 79acead..0000000 Binary files a/_posts/software/vscode远程开发/打开远程目录.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/本地插件和远程插件.png b/_posts/software/vscode远程开发/本地插件和远程插件.png deleted file mode 100644 index 06243ee..0000000 Binary files a/_posts/software/vscode远程开发/本地插件和远程插件.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/机器配置.png b/_posts/software/vscode远程开发/机器配置.png deleted file mode 100644 index 5bce4d9..0000000 Binary files a/_posts/software/vscode远程开发/机器配置.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/环境变量.png b/_posts/software/vscode远程开发/环境变量.png deleted file mode 100644 index 3ba0b79..0000000 Binary files a/_posts/software/vscode远程开发/环境变量.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/登录远程服务.png b/_posts/software/vscode远程开发/登录远程服务.png deleted file mode 100644 index 6f62bc3..0000000 Binary files a/_posts/software/vscode远程开发/登录远程服务.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/秘钥列表.png b/_posts/software/vscode远程开发/秘钥列表.png deleted file mode 100644 index 04c82e9..0000000 Binary files a/_posts/software/vscode远程开发/秘钥列表.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/远程开发插件.png b/_posts/software/vscode远程开发/远程开发插件.png deleted file mode 100644 index a9b9f3f..0000000 Binary files a/_posts/software/vscode远程开发/远程开发插件.png and /dev/null differ diff --git a/_posts/software/vscode远程开发/远程编辑和调试.png b/_posts/software/vscode远程开发/远程编辑和调试.png deleted file mode 100644 index 5eab7e8..0000000 Binary files a/_posts/software/vscode远程开发/远程编辑和调试.png and /dev/null differ diff --git a/_posts/todolist.txt b/_posts/todolist.txt deleted file mode 100644 index d60da1e..0000000 --- a/_posts/todolist.txt +++ /dev/null @@ -1,6 +0,0 @@ -进程 线程 协程的对比python vs go -github c++项目推荐 - -谈谈序列化protobuf - -说说RPC \ No newline at end of file diff --git a/_posts/数据库/mongodb/readme.md b/_posts/数据库/mongodb/readme.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/数据库/mysql/01-事务/ACID.png b/_posts/数据库/mysql/01-事务/ACID.png deleted file mode 100644 index 546d707..0000000 Binary files a/_posts/数据库/mysql/01-事务/ACID.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/mysql事务-不可重复读.png b/_posts/数据库/mysql/01-事务/mysql事务-不可重复读.png deleted file mode 100644 index 8ac2664..0000000 Binary files a/_posts/数据库/mysql/01-事务/mysql事务-不可重复读.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/mysql事务-幻读.png b/_posts/数据库/mysql/01-事务/mysql事务-幻读.png deleted file mode 100644 index a0f339c..0000000 Binary files a/_posts/数据库/mysql/01-事务/mysql事务-幻读.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/mysql事务-引擎对比.png b/_posts/数据库/mysql/01-事务/mysql事务-引擎对比.png deleted file mode 100644 index 92a3075..0000000 Binary files a/_posts/数据库/mysql/01-事务/mysql事务-引擎对比.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/mysql事务-脏读.png b/_posts/数据库/mysql/01-事务/mysql事务-脏读.png deleted file mode 100644 index 69eda4d..0000000 Binary files a/_posts/数据库/mysql/01-事务/mysql事务-脏读.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/mysql事务-隔离级别.png b/_posts/数据库/mysql/01-事务/mysql事务-隔离级别.png deleted file mode 100644 index a19d214..0000000 Binary files a/_posts/数据库/mysql/01-事务/mysql事务-隔离级别.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/mysql事务-隔离级别对比.png b/_posts/数据库/mysql/01-事务/mysql事务-隔离级别对比.png deleted file mode 100644 index 25e3602..0000000 Binary files a/_posts/数据库/mysql/01-事务/mysql事务-隔离级别对比.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/mysql事务.drawio b/_posts/数据库/mysql/01-事务/mysql事务.drawio deleted file mode 100644 index 1c13aad..0000000 --- a/_posts/数据库/mysql/01-事务/mysql事务.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/_posts/数据库/mysql/01-事务/官网mysql引擎列表.png b/_posts/数据库/mysql/01-事务/官网mysql引擎列表.png deleted file mode 100644 index f0cc1e5..0000000 Binary files a/_posts/数据库/mysql/01-事务/官网mysql引擎列表.png and /dev/null differ diff --git a/_posts/数据库/mysql/01-事务/面试官:你说对MySQL事务很熟?那我问你10个问题.md b/_posts/数据库/mysql/01-事务/面试官:你说对MySQL事务很熟?那我问你10个问题.md deleted file mode 100644 index 2e5ac41..0000000 --- a/_posts/数据库/mysql/01-事务/面试官:你说对MySQL事务很熟?那我问你10个问题.md +++ /dev/null @@ -1,222 +0,0 @@ -学习关系型数据库MySQL是很好的切入点,大部分人工作中用惯了CRUD,对面试官刨根问底的灵魂拷问你还能对答如流吗?我们有必要了解一些更深层次的数据库基础原理。 - -整理了面试中,关于MySQL事务和存储引擎10个FAQ(Frequently asked questions),你想知道的都在这里。 - -### 什么是事务? - -事务就是「一组原子性的SQL查询」,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。 - -### 事务控制语法知道吗? - -```mysql -BEGIN 或 START TRANSACTION 显式地开启一个事务; -COMMIT / COMMIT WORK二者是等价的。提交事务,并使已对数据库进行的所有修改成为永久性的; -ROLLBACK / ROLLBACK WORK。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改; -SAVEPOINT identifier 在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT; -RELEASE SAVEPOINT identifier 删除一个事务的保存点; -ROLLBACK TO identifier 把事务回滚到标记点; -SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE -``` - -### 用通俗的语言说说你理解的事务 - -用银行业务举个栗子,用户lemon有两银行卡,一张是招商银行CMBC的工资卡,另一张是工商银行ICBC的储蓄卡,每月5号发工资都要把招行卡的100万转到建设银行储蓄卡账户。记住这里的银行缩写后面就是对应的数据表名称,你要记不住,我给你理一理。 - -``` -招商银行(CMBC):“存么?白痴!” -中国工商银行(ICBC): “爱存不存!” -中国建设银行(CCB): “存?存不?” -中国银行(BC): “不存!” -中国农业银行(ABC): “啊,不存!” -民生银行(CMSB):“存么?SB!" -兴业银行(CIB):“存一百。” -国家开发银行(CDB):“存点吧!” -汇丰银行(HSBC):“还是不存!” -``` - -![](https://user-gold-cdn.xitu.io/2020/3/22/171027474da9ba3d?w=240&h=196&f=jpeg&s=13734) - -这个转账的操作可以简化抽成一个事务,包含如下步骤: - -1. 查询CMBC账户的余额是否大于100万 -2. 从CMBC账户余额中减去100万 -3. 在ICBC账户余额中增加100万 - -以下语句对应创建了一个转账事务: - -``` mysql -START TRANSACTION; -SELECT balance FROM CMBC WHERE username='lemon'; -UPDATE CMBC SET balance = balance - 1000000.00 WHERE username = 'lemon'; -UPDATE ICBC SET balance = balance + 1000000.00 WHERE username = 'lemon'; -COMMIT; -``` - -### 事务的ACID特性是什么? - -ACID其实是事务特性的英文首字母缩写,具体的含义是这样的: - -- 原子性(atomicity) - 一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。 -- 致性(consistency) - 数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,CMBC账户中也不会损失100万,不然lemon要哭死因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。 -- 隔离性(isolation) - 通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时如果有其他人也准备给lemon的CMBC账户存钱,那他看到的CMBC账户里还是有100万的。 -- 持久性(durability) - 一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且「不可能有能做到100%的持久性保证的策略」否则还需要备份做什么。 - -![ACID](https://user-gold-cdn.xitu.io/2020/3/22/171028a606036a02?w=342&h=311&f=png&s=25483) - - -### 什么是脏读、不可重复读、幻读? - -#### 脏读 - -在事务A修改数据之后提交数据之前,这时另一个事务B来读取数据,如果不加控制,事务B读取到A修改过数据,之后A又对数据做了修改再提交,则B读到的数据是脏数据,此过程称为脏读Dirty Read。 - -![脏读](https://user-gold-cdn.xitu.io/2020/3/22/171028a6026fb2e6?w=757&h=602&f=png&s=47455) - - -#### 不可重复读 - -一个事务内在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了变更、或者某些记录已经被删除了。 - -![](https://user-gold-cdn.xitu.io/2020/3/25/17110316f8791527?w=792&h=602&f=png&s=53422) - -#### 幻读 - -事务A在按查询条件读取某个范围的记录时,事务B又在该范围内插入了新的满足条件的记录,当事务A再次按条件查询记录时,会产生新的满足条件的记录(幻行 Phantom Row) -![幻读](https://user-gold-cdn.xitu.io/2020/3/22/171028a602c0f45a?w=822&h=551&f=png&s=42410) - - -#### 不可重复读与幻读有什么区别? - -- 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的「数据不一样」。(因为中间有其他事务提交了修改) -- 幻读的重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的「记录数不一样」。(因为中间有其他事务提交了插入/删除) - -### SQL的四个隔离级别知道吗?具体是什么解决了什么问题说说看 - -SQL实现了四个标准的隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。 -![隔离级别](https://user-gold-cdn.xitu.io/2020/3/22/171028a602c46ebb?w=1084&h=276&f=png&s=112418) - -各个隔离级别可以不同程度的解决脏读、不可重复读、幻读。隔离级别各有所长,没有完美的解决方案,脱离业务场景谈具体实施都是耍流氓。 - -![隔离级别对比](https://user-gold-cdn.xitu.io/2020/3/25/17110328ac93181b?w=572&h=221&f=png&s=18094) - -### MySQL中哪些存储引擎支持事务? - -MySQL中InnoDB和NDB Cluster存储引擎提供了事务处理能力,以及其他支持事务的第三引擎。 - -### 什么是自动提交? - -MySQL默认采用自动提交`AUTOCOMMIT`模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。 - -对于MyISAM或者内存表这些事务型的表,修改`AUTOCOMMIT`不会有任何影响。对这类表来说,没有`COMMIT`或者`ROLLBACK`的概念,也可以说是相当于一直处于`AUTOCOMMIT`启用的模式。 - -### 在事务中可以混合使用存储引擎吗? - -尽量不要再同一个事务中使用多种存储引擎,MySQL服务器层不管理事务,事务是由下层的存储引擎实现的。 - -如果在事务中混合使用了事务型和非事务型的表(例如InnoDB和MyISAM表),在正常提交的情况下不会有什么问题。 - -但如果该事务需要回滚,非事务型的表上的变更就无法撤销,这会导致数据库处于不一致的状态,这种情况很难修复,事务的最终结果将无法确定。所以,为每张表选择合适的存储引擎非常重要。 - -### MySQL存储引擎类型有哪些? - -最常用的存储引擎是InnoDB引擎和MyISAM存储引擎,InnoDB是MySQL的默认事务引擎。 - -查看数据库表当前支持的引擎 : - -``` -show table status from 'your_db_name' where name='your_table_name'; -查询结果表中的`Engine`字段指示存储引擎类型。 -``` - - - -### InnoDB存储引擎的特点和应用场景? - -InnoDB是MySQL的默认「事务引擎」,被设置用来处理大量短期(short-lived)事务,短期事务大部分情况是正常提交的,很少会回滚。 - -更多InnoDB事务模型相关,参考MySQL官方手册,这里贴一下链接:https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-model.html - -#### 历史 - -现代MySQL版本中的InnoDB在历史上叫InnoDB plugin,这个MySQL插件在2008年被开发出来,直到2010在Oracle收购了Sun公司后,发布的MySQL5.5才正式使用InnoDB plugin替代了旧版本的InnoDB,至此 「备胎」成功转正成为MySQL的御用引擎而不再是插件,你看一个插件都这么努力。 - -![](https://user-gold-cdn.xitu.io/2020/3/22/1710274942ccb281?w=220&h=220&f=jpeg&s=15634) - -#### 特点 - -采用多版本并发控制(MVCC,MultiVersion Concurrency Control)来支持高并发。并且实现了四个标准的隔离级别,通过间隙锁`next-key locking`策略防止幻读的出现。 - -引擎的表基于聚簇索引建立,聚簇索引对主键查询有很高的性能。不过它的二级索引`secondary index`非主键索引中必须包含主键列,所以如果主键列很大的话,其他的所有索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。另外InnoDB的存储格式是平台独立。 - -InnoDB做了很多优化,比如:磁盘读取数据方式采用的可预测性预读、自动在内存中创建hash索引以加速读操作的自适应哈希索引(adaptive hash index),以及能够加速插入操作的插入缓冲区(insert buffer)等。 - -InnoDB通过一些机制和工具支持真正的热备份,MySQL的其他存储引擎不支持热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。 - - - -#### MyISAM存储引擎的特点和应用场景? - -MyISAM是MySQL 5.1及之前的版本的默认的存储引擎。MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不「支持事务和行级锁」,对于只读数据,或者表比较小、可以容忍修复操作,依然可以使用它。 - -##### 特性 - -MyISAM「不支持行级锁而是对整张表加锁」。读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入。 - -MyISAM表可以手工或者自动执行检查和修复操作。但是和事务恢复以及崩溃恢复不同,可能导致一些「数据丢失」,而且修复操作是非常慢的。 - -对于MyISAM表,即使是`BLOB`和`TEXT`等长字段,也可以基于其前500个字符创建索引,MyISAM也支持「全文索引」,这是一种基于分词创建的索引,可以支持复杂的查询。 - -如果指定了` DELAY_KEY_WRITE `选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成「索引损坏」,需要执行修复操作。 - -#### InnoDB与MyISAM对比 - -说了这么多估计看一眼也没记住,给你一张表,简单罗列两种引擎的主要区别,如下图。 -![引擎对比](https://user-gold-cdn.xitu.io/2020/3/22/171028a642a93a85?w=671&h=221&f=png&s=24480) - - - -#### 其他存储引擎 - -MySQL还支持其他一些存储引擎,比如memory引擎、NDB集群引擎、CSV引擎,由于这些引擎没有上述InnoDB 和MyISAM 常用,这里不作介绍,感兴趣可以去翻MySQL文档了解。这里同样给出官方链接:https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html -![引擎列表](https://user-gold-cdn.xitu.io/2020/3/22/171028a64869fb89?w=1240&h=439&f=png&s=146573) - - -### 再说两句 - -这一篇是MySQL基础篇,我力求用通俗易懂和图表结合的形式给大家梳理这块知识,越是基础和底层的知识越容易被考察掌握程度,以上知识点都可能成为面试中的一个考察点,相信看完对MySQL事务和存储引擎应该有一个比较完整的理解。 - -最后,感谢各位的阅读,文章的目的是分享对知识的理解,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。 - -****我是 lemon 一线互联网大厂程序员,热爱技术,乐于分享。欢迎扫码关注公众号「后端技术学堂」带你一起学编程,回复「资源」送你 3GB 的编程学习大礼包,包括Linux、数据库、C++、Python、数据结构与算法、设计模式、程序员面试指南等资源,欢迎关注,交流学习。** - -![扫码关注.png](https://upload-images.jianshu.io/upload_images/7842464-146a203080f94c9a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - -### References - -https://book.douban.com/subject/23008813/ - -https://juejin.im/post/5c519bb8f265da617831cfff#comment - -https://tech.meituan.com/2014/08/20/innodb-lock.html - - https://blog.csdn.net/shellching/article/details/8106156 - -https://coolshell.cn/articles/6790.html - -https://zhuanlan.zhihu.com/p/29166694 - -https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html - -https://www.zhihu.com/question/27876575 - -https://www.runoob.com/mysql/mysql-transaction.html - -https://blog.csdn.net/qq_35642036/article/details/82820178?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task - -https://github.com/CyC2018/CS-Notes/blob/master/notes/MySQL.md#b-tree-%E5%8E%9F%E7%90%86 \ No newline at end of file diff --git a/_posts/数据库/mysql/01-事务/高性能MySql.xmind b/_posts/数据库/mysql/01-事务/高性能MySql.xmind deleted file mode 100644 index 2fda34a..0000000 Binary files a/_posts/数据库/mysql/01-事务/高性能MySql.xmind and /dev/null differ diff --git a/_posts/数据库/mysql/mysql系列_索引.md b/_posts/数据库/mysql/mysql系列_索引.md deleted file mode 100644 index 07ca43f..0000000 --- a/_posts/数据库/mysql/mysql系列_索引.md +++ /dev/null @@ -1,16 +0,0 @@ -### 什么是索引 ? - -首先我们来看下索引的概念,索引(在MySQL中也叫做“键 - key”)是存储引擎用于快速找到记录的一种数据结构。这是索引的基本功能,除此之外,索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。 - -### 索引类型 - -在MySQL中索引是在**存储引擎**层实现的,所以我们说到索引都是依赖存储引擎,存储引擎不同对索引的实现方式也不同,来看看有哪些MySQL存储引擎。 - -#### B-Tree索引 - -#### hash索引 - -#### R-Tree索引 - -#### 全文索引 - diff --git a/_posts/数据库/redis/readme.md b/_posts/数据库/redis/readme.md deleted file mode 100644 index e69de29..0000000 diff --git a/_posts/架构/01-微服务/Apache_Thrift_architecture.png b/_posts/架构/01-微服务/Apache_Thrift_architecture.png deleted file mode 100644 index 0416460..0000000 Binary files a/_posts/架构/01-微服务/Apache_Thrift_architecture.png and /dev/null differ diff --git a/_posts/架构/01-微服务/TARS.png b/_posts/架构/01-微服务/TARS.png deleted file mode 100644 index 4f94a9d..0000000 Binary files a/_posts/架构/01-微服务/TARS.png and /dev/null differ diff --git a/_posts/架构/01-微服务/grpc框架.png b/_posts/架构/01-微服务/grpc框架.png deleted file mode 100644 index e4a2c1b..0000000 Binary files a/_posts/架构/01-微服务/grpc框架.png and /dev/null differ diff --git a/_posts/架构/01-微服务/motan.jpg b/_posts/架构/01-微服务/motan.jpg deleted file mode 100644 index 494a6f4..0000000 Binary files a/_posts/架构/01-微服务/motan.jpg and /dev/null differ diff --git a/_posts/架构/01-微服务/service-mesh-arch.png b/_posts/架构/01-微服务/service-mesh-arch.png deleted file mode 100644 index f4937f0..0000000 Binary files a/_posts/架构/01-微服务/service-mesh-arch.png and /dev/null differ diff --git a/_posts/架构/01-微服务/单体程序.png b/_posts/架构/01-微服务/单体程序.png deleted file mode 100644 index 14371b2..0000000 Binary files a/_posts/架构/01-微服务/单体程序.png and /dev/null differ diff --git a/_posts/架构/01-微服务/微服务架构-多技术.png b/_posts/架构/01-微服务/微服务架构-多技术.png deleted file mode 100644 index 68651ef..0000000 Binary files a/_posts/架构/01-微服务/微服务架构-多技术.png and /dev/null differ diff --git a/_posts/架构/01-微服务/微服务架构.png b/_posts/架构/01-微服务/微服务架构.png deleted file mode 100644 index 8fdfb84..0000000 Binary files a/_posts/架构/01-微服务/微服务架构.png and /dev/null differ diff --git a/_posts/架构/01-微服务/服务发现.png b/_posts/架构/01-微服务/服务发现.png deleted file mode 100644 index 912773e..0000000 Binary files a/_posts/架构/01-微服务/服务发现.png and /dev/null differ diff --git a/_posts/架构/01-微服务/面试都在问的微服务,一文带你彻底搞懂.md b/_posts/架构/01-微服务/面试都在问的微服务,一文带你彻底搞懂.md deleted file mode 100644 index 1e21051..0000000 --- a/_posts/架构/01-微服务/面试都在问的微服务,一文带你彻底搞懂.md +++ /dev/null @@ -1,260 +0,0 @@ - -## 单体式应用程序 - -与微服务相对的另一个概念是传统的**单体式应用程序**( Monolithic application ),单体式应用内部包含了所有需要的服务。而且各个服务功能模块有很强的耦合性,也就是相互依赖彼此,很难拆分和扩容。 - -说在做的各位都写过单体程序,大家都没意见吧?给大家举个栗子,刚开始写代码你写的helloworld程序就是单体程序,一个程序包含所有功能,虽然 helloworld 功能很简单。 - -#### 单体应用程序的优点 - -- 开发简洁,功能都在单个程序内部,便于软件设计和开发规划。 -- 容易部署,程序单一不存在分布式集群的复杂部署环境,降低了部署难度。 -- 容易测试,没有各种复杂的服务调用关系,都是内部调用方便测试。 - - - -### 单体应用程序的缺点 - -单体程序的缺点一开始不是特别明显,项目刚开始需求少,业务逻辑简单,写代码一时爽,一直爽。噩梦从业务迭代更新,系统日益庞大开始,前期的爽没有了,取而代之的是软件维护和迭代更新的无尽痛苦。 - -![单体架构](https://upload-images.jianshu.io/upload_images/7842464-a3829e92d3f8b90d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -由于单体式应用程序就像一个大型容器一样,里面放置了许多服务,且他们都是密不可分的,这导致应用程序在扩展时必须以「应用程序」为单位。 - -当里面有个业务模块负载过高时,并不能够单独扩展该服务,必须扩展整个应用程序(就是这么霸道),这可能导致额外的资源浪费。 - -此外,单体式应用程序由于服务之间的紧密度、相依性过高,这将导致测试、升级有所困难,且开发曲线有可能会在后期大幅度地上升,令开发不易。相较之下「微服务架构」能够解决这个问题。 - - - -## 微服务 - -微服务 (Microservices) 就是一些协同工作小而自治的服务。 - -> 2014年,[Martin Fowler](https://zh.wikipedia.org/wiki/Martin_Fowler) 与 [James Lewis](https://zh.wikipedia.org/w/index.php?title=James_Lewis&action=edit&redlink=1) 共同提出了微服务的概念,定义了微服务是由以单一应用程序构成的小服务,自己拥有自己的行程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用 HTTP API 通信。同时服务会使用最小的规模的集中管理 (例如 [Docker](https://zh.wikipedia.org/wiki/Docker)) 能力,服务可以用不同的编程语言与数据库等组件实现 。「维基百科」 - - - -### 举例 - -![](http://ww2.sinaimg.cn/large/9150e4e5ly1fswbux3qi6j206y06cmx4.jpg) - -还是拿前面的 helloworld 程序来举栗子,想象一下你是 helloworld 公司的 CTO(老板还缺人吗?会写代码的那种),假设你们公司的 helloworld 业务遍布全球,需要编写不同语种的 helloworld 版本,分别输出英语、日语、法语、俄语...现在世界有6000多种语言(奇怪的知识又增加了)。 - -有人会说这还不简单我用`switch case`语句就完事了,同学,不要较真我就是举个例子,现实中的业务比 helloworld 复杂多了。好了,我们姑且认为按语言输出是个庞大复杂的工作,这时候就可以用微服务架构了,架构图如下: - -![微服务架构](https://upload-images.jianshu.io/upload_images/7842464-fb70f0681d8bdf16.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -## 微服务与SOA - -**面向服务的体系结构** `SOA (Service-Oriented Architecture)` 听起来和微服务很像,但 `SOA` 早期均使用了总线模式,这种总线模式是与某种技术栈强绑定的,比如:`J2EE`。这导致很多企业的遗留系统很难对接,切换时间太长,成本太高,新系统稳定性的收敛也需要一些时间,最终 `SOA` 看起来很美,但却成为了企业级奢侈品,中小公司都望而生畏。 - -此外,实施`SOA`时会遇到很多问题,比如通信协议(例如SOAP)的选择、第三方中间件如何选择、服务粒度如何确定等,目前也存在一些关于如何划分系统的指导性原则,但其中有很多都是错误的。`SOA`并没有告诉你如何划分单体应用成微服务,所以在实施`SOA`时会遇到很多问题。 - -这些问题再微服务框架中得到很好的解决,你可以认为微服务架构是`SOA`的一种特定方法。 - -## 微服务架构 - -合久必分,鉴于「单体应用程序」有上述的缺点,单个应用程序被划分成各种小的、互相连接的微服务,一个微服务完成一个比较单一的功能,相互之间保持独立和解耦合,这就是微服务架构。 - -### 微服务优点 - -相对于单体服务,微服务有很多优点,这里列举几个主要的好处 - -#### 技术异构性 - -不同服务内部的开发技术可以不一致,你可以用java来开发helloworld服务A,用golang来开发helloworld服务B,大家再也不用为哪种语言是世界上最好的语言而争论不休。 -![微服务架构-多技术](https://upload-images.jianshu.io/upload_images/7842464-cb50824fae547f82.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -为不同的服务选择最适合该服务的技术,系统中不同部分也可以使用不同的存储技术,比如A服务可以选择redis存储,B服务你可以选择用MySQL存储,这都是允许的,你的服务你做主。 - - - -#### 隔离性 - -一个服务不可用不会导致另一个服务也瘫痪,因为各个服务是相互独立和自治的系统。这在单体应用程序中是做不到的,单体应用程序中某个模块瘫痪,必将导致整个系统不可用,当然,单体程序也可以在不同机器上部署同样的程序来实现备份,不过,同样存在上面说的资源浪费问题。 - -#### 可扩展性 - -庞大的单体服务如果出现性能瓶颈只能对软件整体进行扩展,可能真正影响性能的只是其中一个很小的模块,我们也不得不付出升级整个应用的代价。这在微服务架构中得到了改善,你可以只对那些影响性能的服务做扩展升级,这样对症下药的效果是很好的。 - -#### 简化部署 - -如果你的服务是一个超大的单体服务,有几百万行代码,即使修改了几行代码也要重新编译整个应用,这显然是非常繁琐的,而且软件变更带来的不确定性非常高,软件部署的影响也非常大。在微服务架构中,各个服务的部署是独立的,如果真出了问题也只是影响单个服务,可以快速回滚版本解决。 - -#### 易优化 - -微服务架构中单个服务的代码量不会很大,这样当你需要重构或者优化这部分服务的时候,就会容易很多,毕竟,代码量越少意味着代码改动带来的影响越可控。 - - - -### 微服务缺点 - -我们上面一直在强调微服务的好处,但是,微服务架构不是万能的,并不能解决所有问题,其实这也是微服务把单体应用拆分成很多小的分布式服务导致的,所谓人多手杂,服务多起来管理的不好各种问题就来了。 - -为了解决微服务的缺点,前辈们提出了下面这些概念。 - -#### 服务注册与发现 - -微服务之间相互调用完成整体业务功能,如何在众多微服务中找到正确的目标服务地址,这就是所谓「服务发现」功能。 - -常用的做法是服务提供方启动的时候把自己的地址上报给「服务注册中心」,这就是「服务注册」。服务调用方「订阅」服务变更「通知」,动态的接收服务注册中心推送的服务地址列表,以后想找哪个服务直接发给他就可以。 - -![服务发现](https://upload-images.jianshu.io/upload_images/7842464-db86dceac215e2fd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -#### 服务监控 - -单体程序的监控运维还好说,大型微服务架构的服务运维是一大挑战。服务运维人员需要实时的掌握服务运行中的各种状态,最好有个控制面板能看到服务的内存使用率、调用次数、健康状况等信息。 - -这就需要我们有一套完备的服务监控体系,包括拓扑关系、监控(Metrics)、日志监控(Logging)、调用追踪(Trace)、告警通知、健康检查等,防患于未然。 - -#### 服务容错 - -任何服务都不能保证100%不出问题,生产环境复杂多变,服务运行过程中不可避免的发生各种故障(宕机、过载等等),工程师能够做的是在故障发生时尽可能降低影响范围、尽快恢复正常服务。 - -程序员为此避免被祭天,需要引入「熔断、隔离、限流和降级、超时机制」等「服务容错」机制来保证服务持续可用性。 - -#### 服务安全 - -有些服务的敏感数据存在安全问题,「服务安全」就是对敏感服务采用安全鉴权机制,对服务的访问需要进行相应的身份验证和授权,防止数据泄露的风险,安全是一个长久的话题,在微服务中也有很多工作要做。 - - -## 服务治理 - -说到「治理」一般都是有问题才需要治理,我们平常说环境治理、污染治理一个意思,微服务架构中的微服务越来越多,上面说的那些问题就更加显现,为了解决上面微服务架构缺陷「服务治理」就出现了。 - -![](http://ww2.sinaimg.cn/large/9150e4e5gy1fx4lsf0onmj20dw0dwwes.jpg) - -微服务的那些问题都要公司技术团队自己解决的话,如果不是大型公司有成熟的技术团队,估计会很头大。幸好,有巨人的肩膀可以借给我们站上去,通过引入「微服务框架」来帮助我们完成服务治理。 - -## 微服务框架 - -介绍一些业界比较成熟的微服务框架。 - -### Dubbo - -是阿里巴巴公司开源的一个Java高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。 Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现 。2011 年末对外开源,仅支持 Java 语言。 - -官网:` http://dubbo.apache.org/zh-cn/ ` - -![Dubbo架构图|图片来源dubbo.apache.org](http://dubbo.apache.org/img/architecture.png) - - - -### Tars - -腾讯内部使用的微服务架构 TAF(Total Application Framework)多年的实践成果总结而成的开源项目。 仅支持 C++ 语言,目前在腾讯内部应用也非常广泛。2017 年对外开源,仅支持 C++ 语言。 - -源码: `https://github.com/TarsCloud/Tars/ ` - -![TARS架构图|来源github.com/TarsCloud](https://upload-images.jianshu.io/upload_images/7842464-66f3c1a50c8518f5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -**本命鹅厂 TARS 框架介绍 PPT 已下载,不想自己麻烦去找的同学,在我公众号「后端技术学堂」回复「tars」获取。** - -### Motan - -是新浪微博开源的一个Java 框架。Motan 在微博平台中已经广泛应用,每天为数百个服务完成近千亿次的调用。于 2016 年对外开源,仅支持 Java 语言。 - -官方指南: `https://github.com/weibocom/motan/wiki/zh_userguide` - - ![Motan框架|图片来源github.com/weibocom/motan](https://github.com/weibocom/motan/wiki/media/14612352579675.jpg) - - - -### gRPC - -是Google开发的高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发。本身它不是分布式的,所以要实现上面的框架的功能需要进一步的开发。2015 年对外开源的跨语言 RPC 框架,支持多种语言。 - -中文教程:` https://doc.oschina.net/grpc?t=58008 ` - - ![gRPC架构图|图片来源www.grpc.io](http://www.grpc.io/img/grpc_concept_diagram_00.png) - -### thrift - -最初是由 Facebook 开发的内部系统跨语言的高性能 RPC 框架,2007 年贡献给了 Apache 基金,成为 Apache 开源项目之一, 跟 gRPC 一样,Thrift 也有一套自己的接口定义语言 IDL,可以通过代码生成器,生成各种编程语言的 Client 端和 Server 端的 SDK 代码,支持多种语言。 - - ![thrift架构 | 图片来源wikimedia](https://upload.wikimedia.org/wikipedia/commons/d/df/Apache_Thrift_architecture.png) - - - -## 微服务框架和RPC - -很多人对这两个概念有点混淆,微服务框架上面我们说过了,我们再来看下RPC的概念。 - -### 什么是RPC - -`RPC (Remote Procedure Call)`远程过程调用是一个计算机通信协议。我们一般的程序调用是本地程序内部的调用,`RPC`允许你像调用本地函数一样去调用另一个程序的函数,这中间会涉及网络通信和进程间通信,但你无需知道实现细节,`RPC`框架为你屏蔽了底层实现。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过**发送请求-接受回应**进行信息交互的系统。 - -### 两者关系 - -`RPC`和微服务框架的关系我的理解,微服务框架一般都包含了`RPC`的实现和一系列「服务治理」能力,是一套软件开发框架。我们可以基于这个框架之上实现自己的微服务,方便的利用微服务框架提供的「服务治理」能力和`RPC能力`,所以微服务框架也被有些人称作`RPC框架`。 - - - -## 下一代微服务架构 - -`Service Mesh`(服务网格)被认为是下一代微服务架构,`Service Mesh`并没有给我们带来新的功能,它是用于解决其他工具已经解决过的服务网络调用、限流、熔断和监控等问题,只不过这次是在`Cloud Native` 的 `kubernetes` 环境下的实现。 - -### 特点 - -Service Mesh 有如下几个特点: - -- 应用程序间通讯的中间层 -- 轻量级网络代理 -- 应用程序无感知 -- 解耦应用程序的重试/超时、监控、追踪和服务发现 - -目前两款流行的 `Service Mesh` 开源软件 `[Istio](https://istio.io/)` 和 `[Linkerd](https://linkerd.io/) `都可以直接在` kubernetes` 中集成,其中` Linkerd `已经成为`云原生计算基金会 CNCF (Cloud Native Computing Foundation)` 成员。 - -### Why Service Mesh - -为什么现有微服务架构已经解决的问题还要用`Service Mesh`呢?这个问题问的好。 - -![](http://wx3.sinaimg.cn/large/006ARE9vgy1fy0s455zzxj303c02pjrb.jpg) - -回答问题之前,先看下`istio.io`上对`service mesh`的解释,我觉得挺好的,摘抄出来: - -> As a service mesh grows in size and complexity, it can become harder to understand and manage. Its requirements can include discovery, load balancing, failure recovery, metrics, and monitoring. A service mesh also often has more complex operational requirements, like A/B testing, canary rollouts, rate limiting, access control, and end-to-end authentication. -> -> makes it easy to create a network of deployed services with load balancing, service-to-service authentication, monitoring, and more, **with few or no code changes in service code. ** - -试着总结一下:随着微服务的增多复杂程度也增加,管理变得更加困难,微服务架构虽然解决了「网络调用、限流、熔断和监控」等问题,但大多数框架和开源软件对原有业务是`侵入式`的,也就是需要在业务服务程序中集成相关的「服务治理」组件。 - -`Service Mesh`之于微服务,就像`TCP/IP`之于互联网,`TCP/IP`为网络通信提供了面向连接的、可靠的、基于字节流的基础通信功能,你不再需要关心底层的重传、校验、流量控制、拥塞控制。 - -用了`Service Mesh`你也不必去操心「服务治理」的细节,不需要对服务做特殊的改造,所有业务之外的功能都由`Service Mesh`帮你去做了。它就像一个`轻量级网络代理` 对应用程序来说是透明,所有应用程序间的流量都会通过它,所以对应用程序流量的控制都可以在 `serivce mesh` 中实现 。 - - ![ Service Mesh架构|图片来自:[Pattern: Service Mesh](http://philcalcado.com/2017/08/03/pattern_service_mesh.html) ](https://jimmysong.io/blog/what-is-a-service-mesh/service-mesh-arch.png) - -## 写在最后 -在IT世界没有什么技术是永不过时的,微服务架构的演进就是一个例子,从单体程序到微服务架构,再到`service mesh`架构,我不知道下一个技术迭代点是什么时候,但我知道微服务架构肯定还会更新,IT人更应该建立终身学习习惯。 -当然更重要的是拥有对技术的热情,热于拥抱变化、接受新技术,当我看到新技术我是兴奋的,内心os是`厉害了,还能这么玩!`,希望你也有这般热情,而不仅仅是面向工资编程,生活会有趣很多。 - -老规矩。感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。 - -**我是 lemon 一线互联网大厂程序员,热爱技术,乐于分享。欢迎扫码关注公众号「后端技术学堂」带你一起学编程,回复「资源」送你 3GB 的编程学习大礼包,包括Linux、数据库、C++、Python、数据结构与算法、设计模式、程序员面试指南等资源,欢迎关注,交流学习。** - -![扫码关注.png](https://upload-images.jianshu.io/upload_images/7842464-146a203080f94c9a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - -## reference - - https://www.cnblogs.com/Zachary-Fan/p/service_manage_discovery.html - - https://www.zhihu.com/question/56125281 - - http://dockone.io/article/3687 - - https://www.infoq.cn/article/micro-service-technology-stack - - https://segmentfault.com/a/1190000010224335 - - https://book.douban.com/subject/26772677/ - - https://jimmysong.io/blog/what-is-a-service-mesh/ - -https://github.com/weibocom/motan/wiki/zh_userguide \ No newline at end of file diff --git a/_posts/架构/02-REST/RESTvsRPC.drawio b/_posts/架构/02-REST/RESTvsRPC.drawio deleted file mode 100644 index 78a8277..0000000 --- a/_posts/架构/02-REST/RESTvsRPC.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/_posts/架构/02-REST/RPC_vs_REST.md b/_posts/架构/02-REST/RPC_vs_REST.md deleted file mode 100644 index a6c0a05..0000000 --- a/_posts/架构/02-REST/RPC_vs_REST.md +++ /dev/null @@ -1,277 +0,0 @@ -本文是后端微服务架构系列的第二篇文章。在微服务架构中服务之间的通信方式常见的有两种:`RPC` 和 ` REST`,关于微服务和 `RPC` 的更多细节,可以参考我上一篇文章[]() - -这篇文章主要介绍什么是 `REST` 风格设计以及 `RESTful` 接口。阅读完本文你将收获以下知识点: - -- 什么是 `REST` 和 `RESTful` -- `REST` 接口设计规范是什么 -- `REST` 为什么要设计成无状态 -- 接口无状态真的是没有状态吗 -- `RPC` 和 `REST` 适用场景 - - - -### REST和RESTful - -`REST(Representational State Transfer,表述性状态转移)` 是一种软件架构风格。REST提出了一组架构约束条件和原则,任何满足 `REST` 约束条件和原则的架构,都称为 `RESTful` 架构。 - -微服务之间需要相互通信以完成特定的业务处理,在典型的客户端-服务端设计模型中,客户端和服务端通通过消息请求-响应的方式交互协作,`REST` 就是这样一套微服务之间交互接口的设计约束和原则规范。 - -乍一看 `REST`「表述性状态转移」每个字都认得,连起来不知道什么意思。也难怪,这是作者 `Roy Thomas Fielding` 在他的博士论文里提出的概念,论文自然都是学术用语,不过感兴趣的同学可以去看看作者论文原文,地址我贴出来:https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm - -今天 lemon 用大白话帮你透彻理解这个概念,我们把「表述性状态转移」掰开来看,先搞明白什么是「表述性」,什么是「状态转移」。 - -### 表述性 - -「表述性」其实是缺少了主语的,主语是「资源」。完整的描述是「资源表述性」,也就是「资源的描述」。在网络通信中用什么描述资源呢?没错就是 `URI(Uniform Resource Identifier,统一资源标识符)`。 - -这里有几个近义词先给大家先科普一下: - -`URI` 是统一资源标识符,用来唯一的标识一个资源。 - -`URL` 是统一资源定位器,它是一种具体的 `URI`,即 `URL` 可以用来标识一个资源,而且还指明了如何定位这个资源,`URL` 是 `URI` 的子集。 - -`URN` 统一资源命名,是通过名字来标识资源。`URN `也是 `URI` 的子集。 -![URI-URN-URL](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LThhM2MzMWM2N2Q0ZDVjM2IucG5n?x-oss-process=image/format,png) - - -在 `HTTP` 协议中用 `URL` 标识资源,也就是浏览器地址栏你看到的那一串网址。 - -![地址栏URL](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWE2ZDU4NWE2OTNhNTE4MTEucG5n?x-oss-process=image/format,png) - - -#### 资源表述性 - -为了说明「资源描述性」接口设计的优点,我们来做一个接口设计方法的对比,举个栗子就清楚了。 - -##### 传统的接口设计 - -先来看下传统的网络通信模式是怎么样的。假设`lemon`这个人物对象,在服务端的存储形式是一个`c++`的`class`类型存储。 - -下面的过程展示,客户端发送请求服务端创建一个 `lemon` 对象的过程。 - -1. 服务端定义存储结构头文件 `lemon.h` - -```c++ -class lemon{ - string name; - string address; - uint64 phone; -} -``` - -2. 客户端代码引用服务端定义的`lemon.h`,**互相引用头文件,增加了服务耦合性!** - -3. 客户端初始化一个 `lemon` 实例并序列化后通过网络接口发送给服务端。 - -```C++ -class lemon lm; -lm.name = "lemon"; -lm.address = "Shenzhen"; -lm.phone = 18666666666; -``` - -4. 服务端接收消息,反序列化,存储传输过来的 `lemon` 对象 - - - -##### 资源表述性接口设计 - -`lemon` 这个服务内部的对象,对外表现可以用一张图片来表示,也可以用包含`lemon` 的姓名、地址、电话等信息的 `xml` 或 `json` 格式的数据表示。 - -```json -{ -name : "lemon", -address: "ShenZhen", -phone : 18666666666 -} -``` - -```xml - - lemon -
ShenZhen
- 18666666666 -``` - -也就是说,`lemon` 这个「资源」在服务内部的存放形式对外不可见,外界客户端发起请求可以用不同的资源表述格式来获取服务端的资源。 - -(如果服务器会说话,他内心`os` 大概是这样的: 客户端你不用管我是如何保存这个对象的,只要你说的清楚想要什么对象,只管发来请求便是!)。 - - - -![](https://imgconvert.csdnimg.cn/aHR0cDovL3d4Mi5zaW5haW1nLmNuL2xhcmdlLzAwNUxRcUtWZ3kxZmVsenpleXp5emozMGswMGswd2ZpLmpwZw?x-oss-process=image/format,png) - - -这样做最显然的好处是,减少了服务之间的耦合。客户端访问服务资源之前不需要知道资源在服务端的具体存储格式,只需描述资源形式即可修改、创建、更新、删除服务端的资源。 - - - -### 状态转移 - -搞懂了「资源描述性」接下来看下什么是「状态转移」?状态转移就是客户端通过一系列请求动作,推动服务端的资源状态发生变化,资源的状态可以在「创建-修改-查看-删除」之间转移。 - -![资源状态转移](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LWI5ZGIzMWFhNTY4ZWFjNGIucG5n?x-oss-process=image/format,png) - - -资源状态的变化在宏观上的反应就是业务流程推进。打个比方,你去银行系统开户、查余额、销户,这个过程你推动了你的银行账户这个「资源」经历了不同的状态转移让你完成了不同的业务操作。 - - - -### REST的约束条件 - -#### 协议选择 - -`REST` 本身并没有提到底层应该使用什么协议,日常实践案例中最常用的是基于 `HTTP` 的 `RESTful` 实现。 - -这是因为 `HTTP` 协议自带的动词 `GET/POST/PUT/DELETE` 可以作为推动状态转移的方法,另外`HTTP` 的制定了规范的状态码。还有其他的一些 `HTTP` 特性,这些特性使得在`HTTP` 之上实现 `REST` 要简单得多,而如果使用其他协议的话,就需要自己实现这些特性。 - - - -#### 请求规范 - -` RESTful` 架构中,发生状态转换的是「资源」,所以`URI` 中一般只能包含代表「资源」的名词,并且推荐是复数,而不应该在 `URI` 中包对资源进行操作的动词。 - -对资源执行的`CURD「增删改查」`动作应该在`HTTP`请求方法的`GET/POST/PUT/DELETE`中体现。 - -符合REST规范的写法: - -```http -POST http://www.test.com/lemon // 创建 -Get http://www.test.com/lemon // 查询 -PUT http://www.test.com/lemon // 修改 -DELETE http://www.test.com/lemon //删除 -``` - -不符合REST规范的写法: - -```http -POST http://www.test.com/Createlemon // 创建 -POST http://www.test.com/Querylemon // 查询 -POST http://www.test.com/Modifylemon // 修改 -POST http://www.test.com/Deletelemon //删除 -``` - - - -#### 状态码 - -服务端消息响应携带状态码,指示客户端进行下一步处理。符合 `RESTful` 规范的接口返回状态码都是通用的,不需要额外约定,利用`HTTP Status Code 状态码` 表示请求处理结果,降低了微服务间互操作成本。 - -| 状态码 | 状态码含义 | -| ------ | ---------------------------------------------- | -| 2xx | 成功,操作被成功接收并处理 | -| 3xx | 重定向,需要进一步的操作以完成请求 | -| 4xx | 客户端错误,请求包含语法错误或无法完成请求 | -| 5xx | 服务器错误,服务器在处理请求的过程中发生了错误 | - -下面是常见的`HTTP`状态码: - -- 200 - 请求成功 -- 301 - 资源(网页等)被永久转移到其它URL -- 404 - 请求的资源(网页等)不存在 -- 500 - 内部服务器错误 - - - -#### 无状态 - -`RESTful`接口要求是「无状态」。无状态指的是任意一个Web请求必须完全与其他请求隔离,当客户端发起请求时,消息本身包含了服务端识别这一请求上下文所需的全部信息。 - - - -##### 无状态不是真的没有状态 - -接口「无状态」更确切的说是服务端无状态,整个会话还是需要状态维持的。要完成一个业务流程,一般客户端与服务端需要多次的消息交互,我们知道`HTTP` 协议是「无状态协议」,这就需要服务端能够识别几个独立 `HTTP` 请求的「状态信息」,从而将他们关联到一个业务流程中。 - -还是举例子银行系统取款的例子: - -- 用户lemon要登录银行系统,首先需要在登录页面输入用户名和密码,这时候产生一个登录请求 -- 服务端收到登录请求,执行登录逻辑并返回操作结果 -- lemon登录之后点击取款100万,产生一个取款请求 -- 服务端收到取款请求,执行取款逻辑并返回操作结果 - -![取款业务流程](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTZhYmRhZjczMDI5ZmIwZGQucG5n?x-oss-process=image/format,png) - - -这里有个问题,服务端在不同时间点收到登录请求和取款请求,这两个请求都是用户 `lemon` 产生的,如果不在技术层面做对独立的 `HTTP` 请求做关联的话,服务端就无法知道这两个请求其实是都是用户`lemon` 「取款业务」的组成部分。 - - - -##### 技术方案 - -服务端要能识别请求的「状态信息」,有两种技术方案: - -1. `Session` 方式。服务端保存会话状态,客户端每次请求携带`session-id`。 - - 服务端维护一个会话状态信息列表,用`session-id`唯一标识一个状态信息,`session-id`一般包含在`HTTP`响应的`Set-Cookie`头部返回给客户端,后续客户端请求携带包含`session-id`信息的`cookie`头部,服务端解析`cookie`取出`session-id`,去维护的状态列表中取回该消息对应的状态信息,这样就把无状态的`HTTP`变成有状态的了。 - -![session会话](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTA1MGExMDA2NzJiZThhNmIucG5n?x-oss-process=image/format,png) - - -2. `Token` 方式。服务端不保存会话状态,客户端每次请求都携带完整的会话状态信息(一般是加密的)给服务端。 - - `Token`也称作是「令牌」或临时证书签名,状态信息都被加密到`token`中,这样每当服务器收到请求后解密`token`就能获取该请求对应的状态信息,也就能把不同的请求消息关联到同一个业务流程中来,和`session`方式有类似的效果,只不过这次的状态信息不保存在服务端。 - -![Token会话](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTIyOGM5YjU3NTgyODYwNTMucG5n?x-oss-process=image/format,png) - - -以上两种实现中,第一种 `Session` 方式是有状态的,第二种 `Token` 方式是无状态的。 - -如果你要实现 `RESTful` 接口最好按第二种技术方案实现,当然要实现无状态也还有其他方式,思路都是「服务端不保持会话状态」就对了。 - - - -##### 为什么要无状态 - -为了高可用性和负载均衡需求,多个微服务通过负载均衡实现分布式集群化部署,集群中每个服务都是独立和对等的。如果服务器在收到客户端请求之时不可用或者宕机,无状态请求可以由任何其他可用服务器处理并作出应答,这在分布式应用中非常重要。 - -![REST无状态接口](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy83ODQyNDY0LTEwNzM5NGM1MjY1N2JjOTgucG5n?x-oss-process=image/format,png) - - -想象一下如果服务端保存状态,一个事务内的每个请求都必须落到同一台服务器去处理,这就失去了分布式的意义和优势。 - -所以, `RESTful` 接口要求是无状态的,是为了更好的适应分布式业务场景,发挥微服务集群优势。 - - - -### REST 和 RPC - -这两个概念经常出现在微服务架构设计中,`REST` 是一种软件架构接口设计风格,`RPC` 是一种计算机通信协议,看起来是两个不同的概念,要把他们放在一起比较的话,我个人倾向于把 `REST` 具体化为一种基于`HTTP` 并按照 `REST` 约束设计的通信协议,两个通信协议之间还是可以比较一下的。 - -#### 回顾下RPC - -`RPC (Remote Procedure Call)`远程过程调用是一个计算机通信协议。我们一般的程序调用是本地程序内部的调用,`RPC`允许你像调用本地函数一样去调用另一个程序的函数,这中间会涉及网络通信和进程间通信,但你无需知道实现细节,`RPC`框架为你屏蔽了底层实现。`RPC` 是一种服务器-客户端`Client/Serv er`模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。 - -#### 适用场景 - -很多 `RPC` 框架提供的消息传输都是基于二进制的,比如`Thrift`、`Protocol buffers`。这样做的好处是消息结构比较紧凑,对于频繁调用或者大流量、低时延要求的应用场景,能够显著减少网络开销;另一个约束是某些 `RPC` 框架有很强的技术耦合性,比如 `Dubbo` 只能用于 `java` 技术栈。综上,`RPC ` **更加适用于系统内部微服务之间的高效通信。** - -`RESTful`接口由于提供了统一的基于 `HTTP `的 `REST` 设计标准,只需 `web` 框架支持 `HTTP` 协议,并设计`RESTful` 风格的接口即可,极大的方便了第三方服务接入调用,**适合用于微服务系统对外暴露的接口设计标准。** - - - -### 写在最后 - -本文是微服务架构设计中接口选型的一个小方面,很多人会觉得现在工作面试,不管是大厂还是小公司,都是面试造飞机,工作拧螺丝。个人认为即使你在入职之后接触不到架构方面的工作,也要有一颗架构的心,高度决定认知,如果只盯着手上的那颗螺丝那和咸鱼有什么区别? - -老规矩。感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。 - -好了,今天的技术分享就到这里,本文是后端开发微服务设计系列的第二篇,这个系列应该还会继续更新,我们下期再见。 - -**原创不易,看到这里,如果在我这有一点点收获,就动动手指「在看」「转发」是对我持续创作的最大支持。** - - - -### Reference - -微服务设计 https://book.douban.com/subject/26772677/ - -Representational State Transfer (REST) https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm - -RPC和RESTful API入门篇 https://juejin.im/post/5c19f94fe51d45069e53c03c - -那些年,我们一起误解过的REST https://juejin.im/post/5b92475a5188255c5644819a - -理解RESTful架构 http://www.ruanyifeng.com/blog/2011/09/restful.html - -彻底弄懂session,cookie,token https://segmentfault.com/a/1190000017831088 \ No newline at end of file diff --git a/_posts/架构/02-REST/Token会话.png b/_posts/架构/02-REST/Token会话.png deleted file mode 100644 index 1ac9f8d..0000000 Binary files a/_posts/架构/02-REST/Token会话.png and /dev/null differ diff --git a/_posts/架构/02-REST/URI-URN-URL.png b/_posts/架构/02-REST/URI-URN-URL.png deleted file mode 100644 index 01d5e28..0000000 Binary files a/_posts/架构/02-REST/URI-URN-URL.png and /dev/null differ diff --git a/_posts/架构/02-REST/session会话.png b/_posts/架构/02-REST/session会话.png deleted file mode 100644 index 9cb6606..0000000 Binary files a/_posts/架构/02-REST/session会话.png and /dev/null differ diff --git a/_posts/架构/02-REST/取款业务流程.png b/_posts/架构/02-REST/取款业务流程.png deleted file mode 100644 index 571424f..0000000 Binary files a/_posts/架构/02-REST/取款业务流程.png and /dev/null differ diff --git a/_posts/架构/02-REST/搜索框.png b/_posts/架构/02-REST/搜索框.png deleted file mode 100644 index de045e6..0000000 Binary files a/_posts/架构/02-REST/搜索框.png and /dev/null differ diff --git a/_posts/架构/02-REST/无状态容灾.png b/_posts/架构/02-REST/无状态容灾.png deleted file mode 100644 index 50e8057..0000000 Binary files a/_posts/架构/02-REST/无状态容灾.png and /dev/null differ diff --git a/_posts/架构/02-REST/状态转移.png b/_posts/架构/02-REST/状态转移.png deleted file mode 100644 index f371274..0000000 Binary files a/_posts/架构/02-REST/状态转移.png and /dev/null differ diff --git a/about.md b/about.md index ed98df1..c7db10a 100644 --- a/about.md +++ b/about.md @@ -4,17 +4,23 @@ title: About permalink: /about/ --- -lemonchann的个人博客。 +Hi,我是 lemon. ### 技能 -Linux后端分布式高性能服务开发,差一点精通C++,Python小玩具,Go见习。 +Linux后端分布式高性能服务开发,差一点精通 C++,Golang 成长中,Python小玩具。 ### 经历 -软件工程师(Software Engineer),电子信息工程学士学位( Electronic and Information Engineering ),通信设备服务端程序开发和互联网服务后台开发,现就职腾讯(Tencent)公司从事社交产品服务器后台开发工作。 +软件工程师(Software Engineer),电子信息工程学士学位( Electronic and Information Engineering ),从事服务器软件后台开发工作多年,现就职腾讯(Tencent)公司。 + +个人技术公众号「后端技术学堂」分享、记录、成长,欢迎扫码添加。 + +![公众号二维码](https://upload-images.jianshu.io/upload_images/7842464-15f939ec039690f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + ### 版权声明 -博客文章存档于**[此处](https://github.com/lemonchann/lemonchann.github.io/tree/master/_posts)** ,所有文章若无特别声明均使用[**CC BY-SA 4.0 International License(知识共享署名-相同方式共享 4.0 国际许可协议)**](http://creativecommons.org/licenses/by-sa/4.0/)授权。 +博客文章是我原创文章,存档于_posts 文件夹下,版权归我所有,转载请与我联系获得授权许可。