<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>敬维</title>
    <description>一个喜欢涂涂画画的人，曾经的Rubist，一个CI/CD的践行者，一个探寻最佳实践的人。</description>
    <link>https://jingwei.link//</link>
    <atom:link href="https://jingwei.link/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sat, 04 May 2024 08:56:17 +0800</pubDate>
    <lastBuildDate>Sat, 04 May 2024 08:56:17 +0800</lastBuildDate>
    <generator>Jekyll v4.3.3</generator>
    
      <item>
        <title>go 语言 pprof 入门</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#正文&quot; id=&quot;markdown-toc-正文&quot;&gt;正文&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#在工程中加入-pprof-监控&quot; id=&quot;markdown-toc-在工程中加入-pprof-监控&quot;&gt;在工程中加入 pprof 监控&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#一个-pprofgoroutine-的例子&quot; id=&quot;markdown-toc-一个-pprofgoroutine-的例子&quot;&gt;一个 pprof/goroutine 的例子&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#各个数字的含义&quot; id=&quot;markdown-toc-各个数字的含义&quot;&gt;各个数字的含义&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#一个-pprofheap-的例子&quot; id=&quot;markdown-toc-一个-pprofheap-的例子&quot;&gt;一个 pprof/heap 的例子&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#参考&quot; id=&quot;markdown-toc-参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;

&lt;p&gt;在进行 go 项目开发过程中，精度要求比较高的情况下，需要对 go 的运行时进行分析。go 语言原生默认提供了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip:host/debug/pprof&lt;/code&gt; 工具，通过分析其提供的数据可以一定程度了解运行时状态。&lt;/p&gt;

&lt;p&gt;比如通过分析运行时状态，可以了解资源泄露情况、探究代码中的死锁情况等。&lt;/p&gt;

&lt;p&gt;本文将提供几个小例子的分析，用来帮助大家熟悉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/goroutine&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/heap&lt;/code&gt; 的指标。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文&lt;/h2&gt;

&lt;h3 id=&quot;在工程中加入-pprof-监控&quot;&gt;在工程中加入 pprof 监控&lt;/h3&gt;

&lt;p&gt;比如下面的工程，在 web 项目中引入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_ &quot;net/http/pprof&quot;&lt;/code&gt; 即可（由于 pprof 是通过 web 接口暴露出去的，因此项目必须启动一个 web 服务才可以）。&lt;/p&gt;

&lt;div class=&quot;language-golang highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;io&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;math/rand&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;net/http&quot;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;net/http/pprof&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;strconv&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;helloHandler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int63n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;56&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;noStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Itoa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;no&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;noStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Minute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// 查看多个协程的存在&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HandleFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helloHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;localhost:8848&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的代码启动后，即可通过多次 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl localhost:8848/hello&lt;/code&gt; 创建多个 goroutine，从而方便查看 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/goroutine&lt;/code&gt; 中的内容。&lt;/p&gt;

&lt;h3 id=&quot;一个-pprofgoroutine-的例子&quot;&gt;一个 pprof/goroutine 的例子&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;goroutine profile: total 7
2 @ 0x102e51e90 0x102e4b668 0x102e7df24 0x102f56b94 0x102f56c24 0x102f57c18 0x102fed624 0x102ffcbcc 0x1030ec474 0x102e83ab4
#	0x102e7df23	internal/poll.runtime_pollWait+0x43		/opt/homebrew/Cellar/go/1.21.1/libexec/src/runtime/netpoll.go:343
#	0x102f56b93	internal/poll.(*pollDesc).wait+0x83		/opt/homebrew/Cellar/go/1.21.1/libexec/src/internal/poll/fd_poll_runtime.go:84
#	0x102f56c23	internal/poll.(*pollDesc).waitRead+0x33		/opt/homebrew/Cellar/go/1.21.1/libexec/src/internal/poll/fd_poll_runtime.go:89
#	0x102f57c17	internal/poll.(*FD).Read+0x307			/opt/homebrew/Cellar/go/1.21.1/libexec/src/internal/poll/fd_unix.go:164
#	0x102fed623	net.(*netFD).Read+0x53				/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/fd_posix.go:55
#	0x102ffcbcb	net.(*conn).Read+0x6b				/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/net.go:179
#	0x1030ec473	net/http.(*connReader).backgroundRead+0x73	/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/http/server.go:683

2 @ 0x102e51e90 0x102e80648 0x1031618dc 0x1030f5cd4 0x1030f80a0 0x1030f9270 0x1030f4fec 0x102e83ab4
#	0x102e80647	time.Sleep+0x107			/opt/homebrew/Cellar/go/1.21.1/libexec/src/runtime/time.go:195
#	0x1031618db	main.main.func1+0xbb			/Users/chenman/developer/golang/hello/pprof/main.go:18
#	0x1030f5cd3	net/http.HandlerFunc.ServeHTTP+0x43	/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/http/server.go:2136
#	0x1030f809f	net/http.(*ServeMux).ServeHTTP+0x15f	/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/http/server.go:2514
#	0x1030f926f	net/http.serverHandler.ServeHTTP+0x29f	/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/http/server.go:2938
#	0x1030f4feb	net/http.(*conn).serve+0x155b		/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/http/server.go:2009
... (其他的隐掉)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的内容表示当前总共有 7 个 goroutine, 其中有 2 个 goroutine 的在执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poll.runtime_pollWait&lt;/code&gt; 方法，同时有 2 个 goroutine 在执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time.Sleep&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;具体可以参考 &lt;a href=&quot;https://github.com/golang/go/blob/3f4af1ff0e0c3a83bc48faee3d1f0282ef4d02f2/src/runtime/pprof/pprof.go#L528&quot;&gt;printStackRecord&lt;/a&gt; 方法，下面我们详细讨论几个细节。&lt;/p&gt;

&lt;h3 id=&quot;各个数字的含义&quot;&gt;各个数字的含义&lt;/h3&gt;

&lt;p&gt;在上面的内容中，存在比较多的数字，比如第 2 行的十六进制数组 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x102e51e90 0x102e4b668 0x102e7df24 0x102f56b94 0x102f56c24 0x102f57c18 0x102fed624 0x102ffcbcc 0x1030ec474 0x102e83ab4&lt;/code&gt;, 比如第 3 行的十六进制 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x102e7df23&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x43&lt;/code&gt;以及十进制 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;343&lt;/code&gt; 等等。&lt;/p&gt;

&lt;p&gt;根据源码分析，其中第 2 行的十六进制数组表示的是当前 goroutine 堆栈栈帧的 PC 列表，也就是在监控拉取的瞬间，总共有 2 个栈帧地址为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x102e51e90 0x102e4b668 ... 0x1030ec474 0x102e83ab4&lt;/code&gt; 的 goroutine；这个数值列表目前笔者尚未使用过，看起来只是 pprof 用来做统计使用的。&lt;/p&gt;

&lt;p&gt;第 3 行的十六进制 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x102e7df23&lt;/code&gt; 对应的是堆栈栈帧的 PC 值，和第 2 行中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x102e7df24&lt;/code&gt; 相差 1，之所以会相差 1 好像是个历史问题，目前应该不需要深究。而第 3 行中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x43&lt;/code&gt; 目前笔者也未曾使用过，这个值是 PC 和 栈帧入口 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Entry&lt;/code&gt; 的差值。最后十进制 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;343&lt;/code&gt; 表示的是源码 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/runtime/netpoll.go&lt;/code&gt; 的第 343 行。&lt;/p&gt;

&lt;p&gt;简单总结一下，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/goroutine&lt;/code&gt; 中十六进制表示运行时栈帧的状态，十进制表示的是源码中对应的行数。&lt;/p&gt;

&lt;p&gt;在实践中，我们可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/goroutine&lt;/code&gt; 了解项目运行时的状态，比如上面的内容表示有 2 个 goroutine 卡在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time.Sleep&lt;/code&gt;，即 demo 源码的第 18 行（当然，这里是我们刻意为之，工程实践中是我们需要关注可能存在优化空间的点）。笔者实际项目中经常通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/goroutine&lt;/code&gt; 寻找业务逻辑中的死锁，屡试不爽的体验😄。&lt;/p&gt;

&lt;h2 id=&quot;一个-pprofheap-的例子&quot;&gt;一个 pprof/heap 的例子&lt;/h2&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;heap profile: 1: 704 [1: 705] @ heap/1048576
1: 704 [1: 704] @ 0x102e239f4 0x102e2382c 0x103103db0 0x10310185c 0x102e5e6c4 0x102e5e5a8 0x102e519d4 0x102e83ab4
#	0x103103daf	net/http.map.init.0+0x2f	/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/http/h2_bundle.go:1189
#	0x10310185b	net/http.init+0x3ab		/opt/homebrew/Cellar/go/1.21.1/libexec/src/net/http/h2_bundle.go:1189
#	0x102e5e6c3	runtime.doInit1+0xd3		/opt/homebrew/Cellar/go/1.21.1/libexec/src/runtime/proc.go:6740
#	0x102e5e5a7	runtime.doInit+0x37		/opt/homebrew/Cellar/go/1.21.1/libexec/src/runtime/proc.go:6707
#	0x102e519d3	runtime.main+0x1e3		/opt/homebrew/Cellar/go/1.21.1/libexec/src/runtime/proc.go:249
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面第 1 行的内容表示当前有 1 个使用用的对象（活跃的对象），占用了 704 个字节。中括号&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[1: 705]&lt;/code&gt;中的数字表示历史上总共分配过 1 个对象，总共分配了 705 个字节。最后 1048576 表示平均每分配 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1048576&lt;/code&gt; 个字节进行一次内存分配的采样，这也意味着对象相关的所有数据都是采样获得到的，仅保证统计意义上的准确性，具体数值会存在一些波动。
第 2 行的内容表示运行时堆栈 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x102e239f4 0x102e2382c 0x103103db0 0x10310185c 0x102e5e6c4 0x102e5e5a8 0x102e519d4 0x102e83ab4&lt;/code&gt; 表示的过程当前有 1 个活跃的对象，占用 704 字节；中括号中的数字历史上总共分配了 1 个对象，总共约 705 个字节。&lt;/p&gt;

&lt;p&gt;如果项目中存在明显的内存泄露，可以结合 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/goroutine&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprof/heap&lt;/code&gt; 进行初步的排查（看看有没有协程泄露？有没有内存泄露？）。&lt;/p&gt;
&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;
&lt;p&gt;本文简单介绍了 go 语言中原生的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net/http/pprof&lt;/code&gt; 包的使用，并研究了 pprof 中 goroutine 和 heap 统计数据。通过源码层面的分析，读者若能举一反三应该很容易理解 pprof 中提供的其他内容，在工程中遇到疑难杂症时不妨在 pprof 中找找答案。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/blob/3f4af1ff0e0c3a83bc48faee3d1f0282ef4d02f2/src/runtime/pprof/pprof.go#L528&quot;&gt;go源码 - printStackRecord&lt;/a&gt; 打印 goroutine 内容的方法&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/blob/3f4af1ff0e0c3a83bc48faee3d1f0282ef4d02f2/src/runtime/pprof/pprof.go#L581&quot;&gt;go源码 - writeHeapInternal&lt;/a&gt; 打印 heap 内容的方法&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 04 May 2024 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2024/05/04/golang-pprof.html</link>
        <guid isPermaLink="true">https://jingwei.link/2024/05/04/golang-pprof.html</guid>
        
        <category>golang</category>
        
        <category>实用</category>
        
        <category>基础</category>
        
        
        <category>经验</category>
        
      </item>
    
      <item>
        <title>SSL 证书链的认证</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#正文&quot; id=&quot;markdown-toc-正文&quot;&gt;正文&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#以-baiducom-为例&quot; id=&quot;markdown-toc-以-baiducom-为例&quot;&gt;以 baidu.com 为例&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#源码参考&quot; id=&quot;markdown-toc-源码参考&quot;&gt;源码参考&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#demo源码&quot; id=&quot;markdown-toc-demo源码&quot;&gt;demo源码&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#涉及到的几个断点&quot; id=&quot;markdown-toc-涉及到的几个断点&quot;&gt;涉及到的几个断点&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#参考&quot; id=&quot;markdown-toc-参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;

&lt;p&gt;在 《&lt;a href=&quot;./2024/05/02/how-ca-tls-work.html&quot;&gt;自签名证书中的 CA 证书以及 TLS 证书&lt;/a&gt;》中我们知道，ca 根证书一般在装机的时候便已经被安装，因此系统可以发起签名认证。但是喜欢观察的同学应该会注意到，我们在 https 协议中使用的公钥证书都不是由根证书直接签名，而是经过中间证书的签名。那么这又是一种什么样的机制呢？&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic/what-is-ca-chain_1.jpg&quot; alt=&quot;中间证书涉及到的几个概念&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如上图所示，我们可以把 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca-key.pem&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.pem&lt;/code&gt; 密钥称为根证书，而 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca-key2.pem&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca2.pem&lt;/code&gt; 密钥称为中间证书。按照上图的示例，根证书签名得到中间证书，而中间证书签名得到业务证书（在实践中，中间证书可以存在多层，以此串联起来形成证书链）。&lt;/p&gt;

&lt;p&gt;接下来我们以一个具体的例子 https://baidu.com 为例简单分析一下。&lt;/p&gt;
&lt;h3 id=&quot;以-baiducom-为例&quot;&gt;以 baidu.com 为例&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic/what-is-ca-chain_2.png&quot; alt=&quot;百度网页证书示例&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图是 baidu.com 证书的几个信息，通过 safari 浏览器截取。通过上面的图我们可以看出来，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;baidu.com&lt;/code&gt; 证书是由 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GlobalSign RSA OV SSL CA 2018&lt;/code&gt; 中间证书进行签发，而后者又是由 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GlobalSign&lt;/code&gt; 进行签发。&lt;/p&gt;

&lt;p&gt;其中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GlobalSign（GlobalSign Root CA - R3）&lt;/code&gt; 可以在我本地的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;钥匙串（mac系统管理密钥的工具）&lt;/code&gt;中找到，那么中间证书是如何拿到的呢？&lt;/p&gt;

&lt;p&gt;通过 《&lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate&quot;&gt;ngx_http_ssl_module - ssl_certificate&lt;/a&gt;》 我们可以知道，在 nginx 的配置中，中间证书会要求和业务证书放置在一起。这样方便浏览器或者其他的客户端在获取业务公钥的同时获取到中间证书的公钥，从而便利地验证证书链。&lt;/p&gt;

&lt;h3 id=&quot;源码参考&quot;&gt;源码参考&lt;/h3&gt;

&lt;p&gt;为了方便确认，可以基于 go 语言的一小段源码进行断点调试，确认 tls 连接建立过程中公钥的获取与分析。&lt;/p&gt;

&lt;h4 id=&quot;demo源码&quot;&gt;demo源码&lt;/h4&gt;

&lt;div class=&quot;language-golang highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
	&lt;span class=&quot;s&quot;&gt;&quot;net/http&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://baidu.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;get error err=%s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Read error err=%s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;body=%s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;涉及到的几个断点&quot;&gt;涉及到的几个断点&lt;/h4&gt;

&lt;div class=&quot;language-golang highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;c&quot;&gt;// src/net/http/transport.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// 建立连接&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dialConn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pconn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistConn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;c&quot;&gt;// src/net/http/transport.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// 添加证书&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pconn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistConn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;addTLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httptrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// src/crypto/tls/handshake_client.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// tls 协议中的握手流程&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientHandshake&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// src/crypto/tls/handshake_client.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// tls 协议总的握手全部流程，其中就会尝试从 tpc 连接中获取公钥证书&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientHandshakeState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doFullHandshake&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// src/crypto/tls/handshake_client.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// 对公钥进行验证&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verifyServerCertificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certificates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;

&lt;p&gt;本文简单介绍了 ssl 证书链的几个概念以及运行机制。最后提供了一个 go 语言的例子及断点方法，方便读者自行 debug。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/400267748&quot;&gt;什么是SSL证书链？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://nginx.org/en/docs/http/configuring_https_servers.html#chains&quot;&gt;Nginx configuring_https_servers - SSL certificate chains&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate&quot;&gt;ngx_http_ssl_module - ssl_certificate&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ietf.org/rfc/rfc2560.txt&quot;&gt;rfc2560&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ietf.org/rfc/rfc2459.txt&quot;&gt;rfc2459&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 03 May 2024 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2024/05/03/what-is-ca-chain.html</link>
        <guid isPermaLink="true">https://jingwei.link/2024/05/03/what-is-ca-chain.html</guid>
        
        <category>实用</category>
        
        <category>基础</category>
        
        
        <category>经验</category>
        
      </item>
    
      <item>
        <title>自签名证书中的 CA 证书以及 TLS 证书</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#正文&quot; id=&quot;markdown-toc-正文&quot;&gt;正文&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#生成自签名证书的流程&quot; id=&quot;markdown-toc-生成自签名证书的流程&quot;&gt;生成自签名证书的流程&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#关于-tls-非对称加密协议的几个问题&quot; id=&quot;markdown-toc-关于-tls-非对称加密协议的几个问题&quot;&gt;关于 tls 非对称加密协议的几个问题&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#为什么存在私钥和公钥两个密钥只要一个密钥行不行呀&quot; id=&quot;markdown-toc-为什么存在私钥和公钥两个密钥只要一个密钥行不行呀&quot;&gt;为什么存在私钥和公钥两个密钥（只要一个密钥行不行呀）&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#既然有了私钥和公钥为什么还要-ca-证书的参与&quot; id=&quot;markdown-toc-既然有了私钥和公钥为什么还要-ca-证书的参与&quot;&gt;既然有了私钥和公钥为什么还要 ca 证书的参与？&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#如何拿到不属于自己的-ca-公钥证书呢&quot; id=&quot;markdown-toc-如何拿到不属于自己的-ca-公钥证书呢&quot;&gt;如何拿到不属于自己的 ca 公钥证书呢？&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#证书生成命令行参考&quot; id=&quot;markdown-toc-证书生成命令行参考&quot;&gt;证书生成命令行参考&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#生成-ca-证书私钥以及公钥&quot; id=&quot;markdown-toc-生成-ca-证书私钥以及公钥&quot;&gt;生成 ca 证书私钥以及公钥&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#生成业务需要的私钥及签名对应的公钥&quot; id=&quot;markdown-toc-生成业务需要的私钥及签名对应的公钥&quot;&gt;生成业务需要的私钥及签名对应的公钥&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#查看证书中的内容&quot; id=&quot;markdown-toc-查看证书中的内容&quot;&gt;查看证书中的内容&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#双向认证&quot; id=&quot;markdown-toc-双向认证&quot;&gt;双向认证&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#参考&quot; id=&quot;markdown-toc-参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;
&lt;p&gt;为了保证连接的安全性，一般实践中会采用 tls 协议进行加密。本文不论证 tls 这种非对称协议的安全性等问题，只涉及在具体实践中会遇到的 ca 证书及其签名。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文&lt;/h2&gt;

&lt;h3 id=&quot;生成自签名证书的流程&quot;&gt;生成自签名证书的流程&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic/how_ca_tls_work_1.jpg&quot; alt=&quot;子签名证书涉及到的几个概念&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们可以通过一张图简单示意自签名证书的生成过程，如上图所示。其中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca-key.pem&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.pem&lt;/code&gt; 分别表示 ca 证书的私钥以及公钥， &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server-key.pem&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server-cert.pem&lt;/code&gt; 则表示被 ca 证书签名的证书（未来用在 server 服务中）的私钥和公钥。图中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server.csr&lt;/code&gt; 表示签名证书的请求（certificate signing request）中间文件。&lt;/p&gt;

&lt;h3 id=&quot;关于-tls-非对称加密协议的几个问题&quot;&gt;关于 tls 非对称加密协议的几个问题&lt;/h3&gt;

&lt;h4 id=&quot;为什么存在私钥和公钥两个密钥只要一个密钥行不行呀&quot;&gt;为什么存在私钥和公钥两个密钥（只要一个密钥行不行呀）&lt;/h4&gt;
&lt;p&gt;首先网络传输中密钥的存在主要是为了加密端到端的信息传输，避免网络链路中存在第三方监听而消息被窃取。传统的对称加密算法有一个致命的问题——密钥不方便分发；也就是对于网络上的客户端，除非事先和服务端商量好，否则无法在网络中协商密钥（协商的过程被监听，那么协商出来的密钥就没有任何安全意义了）。&lt;/p&gt;

&lt;p&gt;非对称加密的概念中，公钥可以在任意网络环境中传输。客户端在发起请求前，只要想办法拿到服务端的公钥 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server-cert.pem&lt;/code&gt;，就可以使用这个公钥加密自己的请求消息，而被加密的消息只有拥有私钥 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server-key.pem&lt;/code&gt; 的服务端才可以解密拿到明文请求，即使网络被监听也可以避免明文泄露。&lt;/p&gt;

&lt;h4 id=&quot;既然有了私钥和公钥为什么还要-ca-证书的参与&quot;&gt;既然有了私钥和公钥为什么还要 ca 证书的参与？&lt;/h4&gt;

&lt;p&gt;虽然公钥的存在一定程度降低了密钥分发的难度，但是通过非安全的网络拿到公钥的客户端却无法确定自己拿到的公钥是合法的公钥；如果网络中存在拦截，那么拿到的公钥可能是被伪造的，从而导致使用公钥加密的明文消息被泄露。&lt;/p&gt;

&lt;p&gt;当存在 ca 证书的时候，因为公钥生成的时候 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.pem&lt;/code&gt; 被签过名（签名信息是一串二进制被附加到证书中），因此客户端可以在拿到公钥 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server-cert.pem&lt;/code&gt; 后使用自己本地的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.pem&lt;/code&gt; 证书进行签名验证，如果验证通过则说明拿到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server-cert.pem&lt;/code&gt; 是合法的，从而进行接下来的消息加密、传输。&lt;/p&gt;

&lt;p&gt;所谓&lt;strong&gt;自签名&lt;/strong&gt;，其实指的就是用&lt;strong&gt;属于自己&lt;/strong&gt;的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.pem&lt;/code&gt; 签名其他业务证书的机制。&lt;/p&gt;

&lt;h4 id=&quot;如何拿到不属于自己的-ca-公钥证书呢&quot;&gt;如何拿到不属于自己的 ca 公钥证书呢？&lt;/h4&gt;
&lt;p&gt;互联网上很多公开的服务，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://baidu.com&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://taobao.com&lt;/code&gt; 等，都基于 tls 协议加密信息传输，他们所使用的证书都不是自签名的，为了验证他们服务端返回的公钥的合法性，应该怎么获取对应的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca&lt;/code&gt; 证书进行签名验证呢？其实这个问题也很简单，那就是客户端所在的操作系统已经内嵌了这些证书；比如 android 系统、ios系统、windows系统等在装机的时候就已经包含了这些 ca 证书（一般被称为 ca根证书）。&lt;/p&gt;

&lt;p&gt;当我们从互联网上拿到一个公钥，从公钥中拿到签名信息，其中包含签名的 ca 证书信息，只要从本地的证书列表中找到对应的 ca 证书（这些证书属于公钥的一种，而且由于是提前内置的因此我们选择无条件信任，除非你的系统被黑客破坏过），就可以对其进行签名验证了。&lt;/p&gt;

&lt;h3 id=&quot;证书生成命令行参考&quot;&gt;证书生成命令行参考&lt;/h3&gt;

&lt;p&gt;为了方便，这里采用了直接搬了 &lt;a href=&quot;https://docs.docker.com/engine/security/protect-access/&quot;&gt;Protect the Docker daemon socket&lt;/a&gt; 文档中的内容。&lt;/p&gt;

&lt;h4 id=&quot;生成-ca-证书私钥以及公钥&quot;&gt;生成 ca 证书私钥以及公钥&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 生成 ca 私钥&lt;/span&gt;
openssl genrsa &lt;span class=&quot;nt&quot;&gt;-aes256&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; ca-key.pem 4096

&lt;span class=&quot;c&quot;&gt;# 生成 ca 公钥（有效时间 365 天，这里只是用来示例，实际中需要根据具体业务进行选择）&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 且下面命令运行后会要求输入一系列的内容（比如国家、省份、域名、邮箱等）&lt;/span&gt;
openssl req &lt;span class=&quot;nt&quot;&gt;-new&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x509&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-days&lt;/span&gt; 365 &lt;span class=&quot;nt&quot;&gt;-key&lt;/span&gt; ca-key.pem &lt;span class=&quot;nt&quot;&gt;-sha256&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; ca.pem
&lt;span class=&quot;c&quot;&gt;# &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;生成业务需要的私钥及签名对应的公钥&quot;&gt;生成业务需要的私钥及签名对应的公钥&lt;/h4&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 生成业务（比如服务端）需要的私钥&lt;/span&gt;
openssl genrsa &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; server-key.pem 4096

&lt;span class=&quot;c&quot;&gt;# 生成证书签名请求文件（certificate signing request, CSR）&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 下面命令参考 docker 文档，其中的 $HOST 修改成自己 docker daemon 主机的 dns 名&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 非 docker 业务可能不需要&lt;/span&gt;
openssl req &lt;span class=&quot;nt&quot;&gt;-subj&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/CN=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOST&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-sha256&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-new&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-key&lt;/span&gt; server-key.pem &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; server.csr


&lt;span class=&quot;c&quot;&gt;# tls 连接可以通过域名建立，也可以通过 ip 建立，这里是追加域名和ip信息&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# $HOST 同上面的情况&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;subjectAltName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; DNS:&lt;span class=&quot;nv&quot;&gt;$HOST&lt;/span&gt;,IP:10.10.10.20,IP:127.0.0.1 &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; extfile.cnf
&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;extendedKeyUsage &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; serverAuth &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; extfile.cnf &lt;span class=&quot;c&quot;&gt;# docker 专用的吧？&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
openssl x509 &lt;span class=&quot;nt&quot;&gt;-req&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-days&lt;/span&gt; 365 &lt;span class=&quot;nt&quot;&gt;-sha256&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-in&lt;/span&gt; server.csr &lt;span class=&quot;nt&quot;&gt;-CA&lt;/span&gt; ca.pem &lt;span class=&quot;nt&quot;&gt;-CAkey&lt;/span&gt; ca-key.pem &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-CAcreateserial&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; server-cert.pem &lt;span class=&quot;nt&quot;&gt;-extfile&lt;/span&gt; extfile.cnf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;查看证书中的内容&quot;&gt;查看证书中的内容&lt;/h4&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
openssl x509 &lt;span class=&quot;nt&quot;&gt;-in&lt;/span&gt; certificate.pem &lt;span class=&quot;nt&quot;&gt;-text&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-noout&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# -in certificate.pem 指定要查看的PEM证书文件。&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# -text 表示输出证书的文本信息。&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# -noout 表示不输出证书的编码信息&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;双向认证&quot;&gt;双向认证&lt;/h3&gt;

&lt;p&gt;双向认证，顾名思义，客户端和服务器端都需要验证对方的身份，在建立HTTPS连接的过程中，握手的流程比单向认证多了几步。单向认证的过程，客户端从服务器端下载服务器端公钥证书进行验证，然后建立安全通信通道。双向通信流程，客户端除了需要从服务器端下载服务器的公钥证书进行验证外，还需要把客户端的公钥证书上传到服务器端给服务器端进行验证，等双方都认证通过了，才开始建立安全通信通道进行数据传输。（摘自《&lt;a href=&quot;https://help.aliyun.com/zh/api-gateway/user-guide/mutual-tls-authentication&quot;&gt;HTTPS双向认证&lt;/a&gt;》）&lt;/p&gt;

&lt;p&gt;在实践过程中，大部分采用的都是单向认证，比如通过浏览器发起的大部分请求都是如此。单向认证并不意味着只客户端侧的请求加密而服务端的请求不加密，而只是意味客户端对服务端身份一个方向的验证，服务端则选择无条件相信客户端的合法性（好比现实中开门做生意，顾客怕老板跑路的多，很少有老板怕顾客跑路的）。&lt;/p&gt;

&lt;p&gt;双向认证中自签名证书涉及到的几个文件如下图所示：
&lt;img src=&quot;/assets/pic/how_ca_tls_work_2.jpg&quot; alt=&quot;子签名证书涉及到的几个概念&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;
&lt;p&gt;简单简单介绍了 tls 协议中自签名证书的几个概念。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/config/daemon/remote-access/&quot;&gt;Configure remote access for Docker daemon&lt;/a&gt; 配置 dockerd 支持远程访问&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/security/protect-access/&quot;&gt;Protect the Docker daemon socket&lt;/a&gt; 配置安全访问 dockerd 服务（配置 ssl 证书）&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://help.aliyun.com/zh/api-gateway/user-guide/mutual-tls-authentication&quot;&gt;HTTPS双向认证&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 02 May 2024 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2024/05/02/how-ca-tls-work.html</link>
        <guid isPermaLink="true">https://jingwei.link/2024/05/02/how-ca-tls-work.html</guid>
        
        <category>实用</category>
        
        <category>容器</category>
        
        <category>基础</category>
        
        
        <category>经验</category>
        
      </item>
    
      <item>
        <title>多年输入后回归输出，文字依然是我喜欢</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#过去一段时间的生活&quot; id=&quot;markdown-toc-过去一段时间的生活&quot;&gt;过去一段时间的生活&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#成丈夫了当爸爸了&quot; id=&quot;markdown-toc-成丈夫了当爸爸了&quot;&gt;成丈夫了，当爸爸了&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#成后端负责人了&quot; id=&quot;markdown-toc-成后端负责人了&quot;&gt;成后端负责人了&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;
&lt;p&gt;事物会发展，人会成长。&lt;/p&gt;

&lt;p&gt;所谓发展，可能向好也可能向坏；所谓成长，可能向外也可能向内。&lt;/p&gt;

&lt;p&gt;我努力让与自己相关的事物向着“好”的方向发展。当然这里的“好”只是我个人所认为的“好”，于是我的成长有时表现内倾有时表现外倾。刚刚写博客的时候是外倾的，后来做了一阵子视频博主也是外倾的，而后静默内倾了几年，今天开始怕又要开始外倾回来。&lt;/p&gt;

&lt;h2 id=&quot;过去一段时间的生活&quot;&gt;过去一段时间的生活&lt;/h2&gt;

&lt;h3 id=&quot;成丈夫了当爸爸了&quot;&gt;成丈夫了，当爸爸了&lt;/h3&gt;

&lt;p&gt;差不多 3 年前，当时疫情还在中程，牵着现任老婆的手在民政局领了红证。差不多 15 个月前，开启了给现在依然是小不点的臭小子换尿布的日子。&lt;/p&gt;

&lt;p&gt;成为丈夫，便要履行丈夫的责任，其中之一便是“好好工作”。这里需要特别说明一下，丈夫角色里的“好好工作”和老板角色里的“好好工作”并不完全一样。从丈夫的角度，要认真对待工作从而赚到足够的钱，但是在奉献自己到工作的时候还要兼顾妻子的权益，于是时间安排上便加了一些限制，因此想“好好工作”变难了。没有办法全身心地投入工作，还要想办法把工作做好做极致，这可真是一个复杂且困难的命题（当前社会背景，提倡男女平等的同时事实却是男人更具竞争优势，也挺无奈的）。我不能说自己找到了什么平衡的方法，目前的情况本质还是自己的妥协以及老婆的包容：在老婆不能容忍我“不顾家”时我的妥协，以及在我无法妥协时老婆对我“不顾家”的包容。&lt;/p&gt;

&lt;p&gt;当爸爸了，便要履行爸爸的责任，目前主要是对小家伙的陪伴，但是考虑到奶粉尿不湿以及未来小家伙成长上需要的一系列的消费，我依然需要“好好工作”，也就面临上面相同的命题，在此不再赘述。&lt;/p&gt;

&lt;p&gt;整体来说，从成为丈夫和爸爸以后，时间相对变少了，可供自己自由支配的时间更少的可怜。幸运的是，当我意识到这个问题的时候我并没有自怨自艾颓唐面对，而是变得更加惜时了，“做事的效率”自然也就提升了。&lt;/p&gt;

&lt;p&gt;活到人生的第三十五个年头，再藉口以“做事的效率”做所有决策已然不妥；比如对家人的陪伴、对小家伙的教育，从来不能从效率的角度思考类似的问题。因此单说效率的提升没有什么意义，一些事情在效率提升后反而向着相反的方向发展，从这个角度来看我的生活整体还是变难了。&lt;/p&gt;

&lt;h3 id=&quot;成后端负责人了&quot;&gt;成后端负责人了&lt;/h3&gt;

&lt;p&gt;生活整体变难了，但是快乐与幸福的事情并没有因此而减少。工作上也是类似的。随着公司业务的发展，渐渐的我扮演了后端负责人的角色。为了能够负起这份责任，我在过去几年时间里看了很多书和源码以求证各个问题的答案，这部分应该会是我未来的博客主要分享的内容。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic/what-i-want-todo-later-01.png&quot; alt=&quot;S 型成长曲线&quot; /&gt;&lt;/p&gt;

&lt;p&gt;目前我对自己工作上的判断，应该经历了 S 曲线的前期和中期目前到达了后期，这是一个亟需第二曲线的阶段。我愿意把自己成长中的感悟记录下来，供未来的朋友们参考，也为探索自己的第二曲线努力。&lt;/p&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;

&lt;p&gt;“以认真的态度做完美的事情”，这是多年前给自己的博客写下的主题语，时隔多年已经略显稚嫩，但是依然能够代表我当前为人处事的准则。所以就这样带着责任与梦想再出发吧。&lt;/p&gt;

</description>
        <pubDate>Sat, 23 Mar 2024 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2024/03/23/what-i-want-todo-later.html</link>
        <guid isPermaLink="true">https://jingwei.link/2024/03/23/what-i-want-todo-later.html</guid>
        
        <category>自己</category>
        
        <category>实用</category>
        
        
        <category>经验</category>
        
      </item>
    
      <item>
        <title>不写文章专注做视频，这里要荒废一段时间了</title>
        <description>&lt;h2 id=&quot;都是输出都挺开心&quot;&gt;都是输出，都挺开心&lt;/h2&gt;

&lt;p&gt;如题，最近一段时间迷上了做视频，因此这里要荒废一段时间了。&lt;/p&gt;

&lt;p&gt;其实无论是写文字还是做视频，于我而言都是一件挺开心的事情。而人活着最重要的是开心。&lt;/p&gt;

&lt;h2 id=&quot;视频链接&quot;&gt;视频链接&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;https://space.bilibili.com/425571569&lt;/a&gt;  B站个人页面&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sun, 13 Sep 2020 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2020/09/13/what-iam-doing-now.html</link>
        <guid isPermaLink="true">https://jingwei.link/2020/09/13/what-iam-doing-now.html</guid>
        
        <category>课程</category>
        
        <category>基础</category>
        
        <category>实用</category>
        
        
        <category>视频</category>
        
      </item>
    
      <item>
        <title>《从0到1学习后端开发》之源码安装Python3及其HelloWorld的执行</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#python-的-hello-world&quot; id=&quot;markdown-toc-python-的-hello-world&quot;&gt;Python 的 hello world&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#简易版的-helloworld&quot; id=&quot;markdown-toc-简易版的-helloworld&quot;&gt;简易版的 helloworld&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#高级版-helloworld&quot; id=&quot;markdown-toc-高级版-helloworld&quot;&gt;高级版 helloworld&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#参考&quot; id=&quot;markdown-toc-参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;

&lt;p&gt;《从0到1学习后端开发》是面向在校大学生、非 IT 工作人员的免费后端开发入门实践教程，可至 &lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;https://space.bilibili.com/425571569&lt;/a&gt; 查看对应的视频资料。&lt;/p&gt;

&lt;h1 id=&quot;python-的-hello-world&quot;&gt;Python 的 hello world&lt;/h1&gt;

&lt;p&gt;基础决定最终可达到的高度。&lt;/p&gt;

&lt;p&gt;本节内容首先带着大家利用 ubuntu 原生的 python 进行简易版 helloworld 的编写与执行；然后带着大家按需安装特定版本的 python，并配置对应的开发环境。&lt;/p&gt;

&lt;h2 id=&quot;简易版的-helloworld&quot;&gt;简易版的 helloworld&lt;/h2&gt;

&lt;p&gt;在前面安装 vscode 的那一节我们已经编写过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt; 的 helloworld。&lt;/p&gt;

&lt;p&gt;由于 ubuntu18.04 系统已经默认安装了 python3 解释器，因此可以很方便写出并运行 helloworld。简单讲分成几步：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;创建一个项目目录，比如我们在家目录下面使用命令 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir&lt;/code&gt; 创建对应的目录；然后通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code .&lt;/code&gt; 命令打开对应的目录作为工作目录。&lt;/li&gt;
  &lt;li&gt;检查 vscode 是否安装 Python 插件；&lt;/li&gt;
  &lt;li&gt;创建 helloworld.py 文件并编写内容 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print(&quot;hello world&quot;)&lt;/code&gt;;&lt;/li&gt;
  &lt;li&gt;配置对应的解释器，可以直接执行输出结果。&lt;/li&gt;
  &lt;li&gt;也可以在命令行使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3 helloworld.py&lt;/code&gt; 来执行对应的文件得出结果。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;上面简易版的 helloworld 直接使用了 ubuntu 系统默认带的 python3 环境，具体的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python 3.6.9&lt;/code&gt; 版本，版本比较老。另一方面，在我们实际项目的开发过程中，大部分会指定特定的 python 版本进行开发，这个时候就需要我们在我们的本地安装对应的版本，搭建匹配的开发环境。接下来就让我们看一下如何从源码安装特定版本的 python。&lt;/p&gt;

&lt;h2 id=&quot;高级版-helloworld&quot;&gt;高级版 helloworld&lt;/h2&gt;

&lt;p&gt;为什么提出“高阶版的 helloworld”呢？这是一个“基础决定最终可达到的高度”的问题；&lt;strong&gt;“要想楼层盖的高，就看地基打多牢”&lt;/strong&gt;，话糙理不糙。作为一名开发者，如果只会扎架子（比如动口只是架构、方案、设计模式），优雅而丰满的项目大厦是建不成的。不过很多人把扎好架子作为目标，不去关注基础上细节，因此丢失了很多乐趣，也限制了自己可达的高度，还是有点惋惜的。&lt;/p&gt;

&lt;p&gt;我们高阶版的 helloworld 希望可以给大家开一个好头，尝试从一个简单的 helloworld 里挖掘一些其他有意思的东西。&lt;/p&gt;

&lt;p&gt;接下来让我们安装特定版本的 python，比如选定 3.8.3 进行安装 。&lt;/p&gt;

&lt;p&gt;首先让我们下载 python3.8.3 的源码。无论是 python 还是 go，它们都是开源的语言，因此我们都是到它们的官网找到对应的源码的下载链接，相对其他一些地方的下载链接要靠谱很多。这里顺便建议大家平日里可以多逛官网，作为我们最重要的的一手资料进行学习。&lt;/p&gt;

&lt;p&gt;为了适应终端命令行，我们采用命令行的方式下载并安装。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;首先选择一个合适的目录，然后从官网找到对应的链接后复制，通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wget https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz&lt;/code&gt; 进行下载。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;下载完成后得到的是一个为方便传输的压缩包，需要我们把它解压缩，通过在当前目录执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tar -xzf Python-3.8.3.tgz&lt;/code&gt; 即可完成，在当前目录生成一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python-3.8.3&lt;/code&gt; 的目录。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;我们可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code Python-3.8.3&lt;/code&gt; 来打开对应的目录，简单预览一下 python 的源码项目（源码阅读一般流程）。&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;大概浏览一下，咦，发现 python 源码库里有好多 c 语言文件，可以知道 python 是基于 c 语言进行开发的，是不是很神奇 ^_^；&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;查看对应的 README，可以看到 源码库中有一个 README.rst 文件，查看一遍说了什么（英文学习多么重要啊）。我们目前只关注安装相关的内容，因此找到一段命令做参考，并看到有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./configure --help&lt;/code&gt;，那我们有必要探索一番。（在命令行环境多打 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; 是一个好习惯）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;其他的我们暂时不关注，先放着不管，不过可以看到原来一个牛x的项目是这个样子的。&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./configure --help&lt;/code&gt; 查看配置项，然后通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./configure --prefix=$HOME/software/mypy&lt;/code&gt; 进行配置；也可以有选择地添加建议的参数，比如添加优化参数保证使用所有稳定的优化方案 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./configure --prefix=$HOME/software/mypy --enable-optimizations&lt;/code&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make install&lt;/code&gt; 来编译并安装源码。在整个过程中可能会有一些警告的提示或者错误的提示，静待看最后的总结报告。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;安装依赖（整个需要我们通过比较久的经验做积累才可以慢慢理解为什么需要这些依赖）。这里我积累了几个必要的，大家先安装一下。如果未来在项目开发过程中使用到了其他的依赖，则可以按照类似的步骤进行安装。&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt install zlib1g.dev&lt;/code&gt; 安装 zlib 依赖（整个是在安装 python 的时候就会需要）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt install libssl-dev&lt;/code&gt; 安装 ssl 依赖（编写 web 相关的服务的时候会用到）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt install libffi-dev&lt;/code&gt; 安装 libffi 依赖（当 pip install 的时候会用到）&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;重新通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./configure --prefix=$HOME/software/mypy&lt;/code&gt; 来配置生成对应的 make 文件。并通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make install&lt;/code&gt; 编译安装。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;安装完成以后，就可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/software/mypy/bin/python3&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/software/mypy/bin/pip3&lt;/code&gt;把玩了。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;为了便利性，我们可以把新安装的 python 的 bin 目录添加到 PATH 中去，这样就可以直接使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip3&lt;/code&gt; 命令了。具体地，在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME/.bashrc&lt;/code&gt; 文件的最后添加 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export PATH=&quot;$HOME/software/mypy/bin:$PATH&quot;&lt;/code&gt;，然后重启终端或对应的服务即可。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;

&lt;p&gt;本小节介绍了 Python 的 helloworld，首先介绍利用 ubuntu 原带的 python 作为解释器，然后重点带大家安装自己的 python 解释器。并使用自己安装的解释器执行了 hellworld 脚本。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/chalvern/books&quot;&gt;本系列课程教案库&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;视频链接地址（视频课程8）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 24 May 2020 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2020/05/24/python-helloworld.html</link>
        <guid isPermaLink="true">https://jingwei.link/2020/05/24/python-helloworld.html</guid>
        
        <category>课程</category>
        
        <category>基础</category>
        
        <category>实用</category>
        
        
        <category>课程</category>
        
      </item>
    
      <item>
        <title>《从0到1学习后端开发》（3）Go、Java、Python语言模型对比</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#为什么是-python-和-go-这两门语言&quot; id=&quot;markdown-toc-为什么是-python-和-go-这两门语言&quot;&gt;为什么是 python 和 go 这两门语言&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#简单对比gopythonjava的模型&quot; id=&quot;markdown-toc-简单对比gopythonjava的模型&quot;&gt;简单对比go/python/java的模型&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#go语言&quot; id=&quot;markdown-toc-go语言&quot;&gt;go语言&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#java-语言&quot; id=&quot;markdown-toc-java-语言&quot;&gt;java 语言&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#python-语言&quot; id=&quot;markdown-toc-python-语言&quot;&gt;python 语言&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#python-执行速度慢的三点原因&quot; id=&quot;markdown-toc-python-执行速度慢的三点原因&quot;&gt;python 执行速度慢的三点原因&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#对技术包括编程语言怀有敬畏之心&quot; id=&quot;markdown-toc-对技术包括编程语言怀有敬畏之心&quot;&gt;对技术（包括编程语言）怀有敬畏之心&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#参考&quot; id=&quot;markdown-toc-参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;

&lt;p&gt;《从0到1学习后端开发》是面向在校大学生、非 IT 工作人员的免费后端开发入门实践教程，可至 &lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;https://space.bilibili.com/425571569&lt;/a&gt; 查看对应的视频资料。&lt;/p&gt;

&lt;h2 id=&quot;为什么是-python-和-go-这两门语言&quot;&gt;为什么是 python 和 go 这两门语言&lt;/h2&gt;

&lt;p&gt;作为一个系列课程，可能有必要详尽地说明一下，为什么选择的是 python 和 go 这两门语言；有的同学可能更希望选定的是 java、Rust、C/C++ 等其他语言。&lt;/p&gt;

&lt;p&gt;在前面的课程里有提到过， python 是我目前工作的主要编程语言， go 是我曾经主力使用且一直跟踪其发展的编程语言。而且站在一个稍微有点经验的程序员的视角，&lt;strong&gt;不存在最好的语言，存在最适合的语言&lt;/strong&gt;。作为一名中高级的开发者，大家会发现自己平日里的工作所解决的困难都是业务相关的复杂度，编程语言的迁移（假如真的存在这种情况）相对反而会是比较容易的，只要团队里有比较靠谱的人带队，从入门到有产出花不了太多时间。&lt;/p&gt;

&lt;p&gt;不过，根据 &lt;a href=&quot;https://www.tiobe.com&quot;&gt;TIOBE&lt;/a&gt; 指数 java 依然是最流行的工业级编程语言，其热度长期占据排行榜首位，大家可以自行斟酌吧。&lt;/p&gt;

&lt;h2 id=&quot;简单对比gopythonjava的模型&quot;&gt;简单对比go/python/java的模型&lt;/h2&gt;

&lt;p&gt;在正式开始 python 和 go 的学习之前，让我们首先简单地比较一下几种编程语言的模型，这里我选择了比较有代表性的 go、python 和 java。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic/004_why_python_and_go.png&quot; alt=&quot;几种编程语言的模型&quot; title=&quot;几种编程语言的模型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们看一下各个编程语言“从源码到机器执行”所经历的环节。大家都是从源码开始，最终都是电脑的 CPU 和 内存来承载执行源码对应的逻辑。&lt;/p&gt;

&lt;p&gt;首先我先给大家注点鸡血，程序员写代码控制的主要是 CPU 和 内存，它们属于机器的一种；挖掘机或其他的车也是机器的一种，如果我们能够合理地摆弄那几个操纵杆学会开车，那么没有理由学不会开电脑。唯手熟尔，仅此而已。&lt;/p&gt;

&lt;h3 id=&quot;go语言&quot;&gt;go语言&lt;/h3&gt;
&lt;p&gt;让我们首先看一下 go 语言。&lt;/p&gt;

&lt;p&gt;go 诞生自 2009 年，和 python（1991）、java（1995）比起来算是一门比较新的语言，理论上具有后发优势，语言特性会比较先进。这也是建议大家学习它的一个原因。&lt;/p&gt;

&lt;p&gt;go 语言和 c 语言类似，经过编译后会生成可直接执行的二进制文件。图中使用“很快地编译“，是因为 go 语言编译起来非常快，这也是 go 语言涉及之处的一个目标；我们可以拿另一个比较新且流行的编程语言 Rust 简单对比，go 的编译速度比 rust 可以快一个数量级。举个例子，如果 Rust 项目编译使用 30 分钟（TiKV 项目），那么 go 语言复杂度相当的项目（TiDB）可能三分钟就构建完成了。&lt;/p&gt;

&lt;p&gt;go 编译生成的二进制文件可以直接被加载到机器上运行，也就是说，go 编译生成的二进制文件直接是机器可识别的内容(或者说，go 编译生成的汇编代码就是 CPU 指令集对应的代码)。这与 python 和 java 是完全不同的。&lt;/p&gt;

&lt;h3 id=&quot;java-语言&quot;&gt;java 语言&lt;/h3&gt;

&lt;p&gt;java 生态里非常引人注目的是 JVM（java virtual machine，java虚拟机）。对于虚拟机，大部分人的认知可能局限在 virtualBox、VMware 等，本课程最初也介绍了在虚拟机 virtualBox 上安装 Ubuntu 系统的具体步骤。我们可以把虚拟机认为是一种对硬件的抽象，基于我们电脑硬件的 CPU 的指令集，模拟出特定指令集合的计算单元。同样地，java 虚拟机也对硬件 CPU 和内存资源进行了抽象，而且抽象后可以运行 java 字节码。&lt;/p&gt;

&lt;p&gt;java 源码编译后生成 java 字节码，字节码由 java 虚拟机的指令集组成，可以在 JVM 平台执行。&lt;/p&gt;

&lt;p&gt;我们经常见到的 Jar 包和 War 包都是字节码文件包，是源码编译后的产物。相对 go 来说， java 的编译过程要慢很多，常常会让电脑卡顿好一会儿。&lt;/p&gt;

&lt;p&gt;简单讲，java 从源码到执行的过程是： 源码中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1+1&lt;/code&gt; 语句，首先被编译成为 JVM 的命令（字节码），然后字节码由 JVM 加载，并被 JVM 转义成为 CPU 的指令从而得以执行。CPU 运行 java 代码时，我们的 CPU ①一边做字节码到CPU指令的转义，②一边运行实际转义后的语句。相对于 go 语言只运行CPU指令的情况， java 的 CPU 使用效率会低一些。&lt;/p&gt;

&lt;p&gt;不过 java 作为一门工业级的编程语言，很多大牛为 JVM 的优化做了很多的事情，这也使得基于 JVM 的编程语言的运行效率与裸 CPU 的编程语言的运行效率差距没那么大了。因此，在 JVM 的平台上，还诞生了 kotlin 、Jython 这种编程语言，它们都是编译生成 JVM 指令集的字节码，然后在 JVM 上运行。&lt;/p&gt;

&lt;h3 id=&quot;python-语言&quot;&gt;python 语言&lt;/h3&gt;

&lt;p&gt;使用 python 开发，从源码到机器执行并没有显式地编译过程，只需把源码加载到解释器就可以正常运行了。&lt;/p&gt;

&lt;p&gt;就像前面提到的，类似 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1+1&lt;/code&gt; 这样的源码是不可能直接在 CPU 上执行的，那么 python 源码是如何运行在&lt;strong&gt;机器&lt;/strong&gt;上的呢？实际上 python 解释器会在内部把 python 源码编译形成 python 字节码，然后再把这些字节码加载到解释器的运行时（python 虚拟机）进行执行。&lt;/p&gt;

&lt;p&gt;除了“隐式编译”这个步骤，python 几乎与 java 是一样的。而且 python 解释器只会在第一次加载运行 python 源码的时候存在“隐式编译”这个步骤，并且把编译生成的 python 字节码保存为 .pyc 文件，再以后执行时会首先加载 pyc 文件，从而省去了重复编译的步骤。&lt;/p&gt;

&lt;p&gt;既然 python 与 java 类似，那么为什么我们经常听到大家讲“python运行速度慢”而很少听到“java执行速度慢”呢？这其实涉及到语言另外的问题。&lt;/p&gt;

&lt;h3 id=&quot;python-执行速度慢的三点原因&quot;&gt;python 执行速度慢的三点原因&lt;/h3&gt;

&lt;p&gt;首先应该科普一下：80%的业务还没跑到编程语言出现性能瓶颈就被淘汰了。因此有时候&lt;strong&gt;偏执&lt;/strong&gt;执行效率并不睿智，&lt;strong&gt;“不存在最好的语言，存在最适合的语言”&lt;/strong&gt;就是这个道理。&lt;/p&gt;

&lt;p&gt;① 首先看&lt;strong&gt;静态语言与动态语言&lt;/strong&gt;的区别。静态语言的类型检查在编译时进行，而动态语言的类型检查在运行时进行。java 是一种静态语言，其类型检查在我们编译 java 源码时就完成了；比如当有类型赋值错误时，会直接编译失败。而 python 是一种动态语言，其类型检查放在了运行时，因此会占用运行时的计算资源，导致执行速度慢。&lt;/p&gt;

&lt;p&gt;② 另一方面，为了便利性 python 的数据类型的粒度取的比较粗（比如整型不区分精度，可以表示任意大的值），而 CPU 固有的数据位（单次读写最大位数）有限，在转换过程中会存在性能损耗。&lt;/p&gt;

&lt;p&gt;③ python 在 1991 年诞生时，CPU 是单核的，因此语言模型对多核的支持不好。在 python 虚拟机（运行时）中，存在一个 GIL（Global interpreter lock，解释器全局锁），限制一个解释器同一时间只能转义一条字节码为机器码，无疑这会限制 python 对多核的使用。&lt;/p&gt;

&lt;h2 id=&quot;对技术包括编程语言怀有敬畏之心&quot;&gt;对技术（包括编程语言）怀有敬畏之心&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;对编程语言怀有敬畏之心;&lt;/li&gt;
  &lt;li&gt;对后端开发怀有敬畏之心；&lt;/li&gt;
  &lt;li&gt;对技术怀有敬畏之心；&lt;/li&gt;
  &lt;li&gt;对生活怀有敬畏之心。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;发现很多人因为各种原因忘了初心，达到某个水平后不再精进自己。希望我们都可以心怀敬畏，不断进取吧。&lt;/p&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;

&lt;p&gt;本文简单介绍了　go、java、python 的语言模型。介绍了本系列课程选择 python 和 go 两种语言的理由。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/chalvern/books&quot;&gt;本系列课程教案库&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;视频链接地址（视频课程7）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 21 May 2020 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2020/05/21/go-java-python-language-model.html</link>
        <guid isPermaLink="true">https://jingwei.link/2020/05/21/go-java-python-language-model.html</guid>
        
        <category>课程</category>
        
        <category>基础</category>
        
        <category>实用</category>
        
        
        <category>课程</category>
        
      </item>
    
      <item>
        <title>《从0到1学习后端开发》（2）git安装、概念图解及简单使用</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#下载并安装-git&quot; id=&quot;markdown-toc-下载并安装-git&quot;&gt;下载并安装 git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#git-概论&quot; id=&quot;markdown-toc-git-概论&quot;&gt;Git 概论&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#git中的概念&quot; id=&quot;markdown-toc-git中的概念&quot;&gt;git中的概念&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#在-vscode-中使用-git&quot; id=&quot;markdown-toc-在-vscode-中使用-git&quot;&gt;在 vscode 中使用 git&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#通过图形界面实战&quot; id=&quot;markdown-toc-通过图形界面实战&quot;&gt;通过图形界面实战&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#通过命令行实战&quot; id=&quot;markdown-toc-通过命令行实战&quot;&gt;通过命令行实战&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#参考&quot; id=&quot;markdown-toc-参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;

&lt;p&gt;《从0到1学习后端开发》是面向在校大学生、非 IT 工作人员的免费后端开发入门实践教程，可至 &lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;https://space.bilibili.com/425571569&lt;/a&gt; 查看对应的视频资料。&lt;/p&gt;

&lt;h2 id=&quot;下载并安装-git&quot;&gt;下载并安装 git&lt;/h2&gt;

&lt;p&gt;在 ubuntu 中下载安装 git 还是比较简单的，&lt;/p&gt;

&lt;p&gt;我们可以打开 terminal 终端，首先敲击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; 命令，如果我们系统还没有安装 git， console 会提示我们“尚未安装 git”，并且给出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt install git&lt;/code&gt; 的命令，这个时候我们只需要复制这条命令，然后粘贴到命令行执行就可以了。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;git 

&lt;span class=&quot;c&quot;&gt;# 下面就是长篇的安装输出提示，可能会要求我们输入 Y，输入即可&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装完成之后， git 就可以作为我们的一个工具来使用了。&lt;/p&gt;

&lt;h2 id=&quot;git-概论&quot;&gt;Git 概论&lt;/h2&gt;

&lt;p&gt;对于一个完整的项目来说，可能包含了很多的模块，整体向外提供一套完整的服务。我们在项目的开发的过程中，可能会首先编写一个模块的代码，然后再开发另一个模块的代码。对于一个中大型的项目，甚至会不同的人负责不同的模块进行开发。&lt;/p&gt;

&lt;p&gt;在项目创建初期，各个模块之间的关联关系比较清晰，每个开发者的职责划分也比较清晰。不过为了更好地审视整个系统的功能，依然需要一个明确的方式来 review（审核） 代码。比如 模块（或类）A 的同事依赖调用 模块（或类）B 的接口，那么架构层面的负责人可能会同时查看两个模块的代码，从而保证各自的逻辑是恰当的。同时也要能够跟踪每条语句的作者，一方面可以记录贡献，另一方面在出现疑问或问题的时候也可以第一时间找到编码者进行答疑或修正。&lt;/p&gt;

&lt;p&gt;到功能迭代阶段，除去新的模块或类，一般还会修改已有的模块或类，这个时候就特别需要收集“老代码”的各种信息，比如作者、修改记录、注释、文档等；同时为了以后方便进一步迭代，需要留下当前的记录、注释、文档等。按照时间线把一次次变更详情记录下来，这就是 git 工具的核心功能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic/003_git_concepts_01.png&quot; alt=&quot;git概念&quot; title=&quot;Git概念图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图中，左边表示我们项目视角的结构图，每个方框表示一个模块，各个模块相互联系耦合到一起共同形成一个系统，整体向外提供服务；右边表示 git 的提交（commit）视图，一次次的提交形成一个可追溯、“有序”（当引入分支以后局部次序也会显得比较乱）的链条。&lt;/p&gt;

&lt;h2 id=&quot;git中的概念&quot;&gt;git中的概念&lt;/h2&gt;
&lt;p&gt;为了实现可追溯的链条，git 中包含了几个概念。&lt;/p&gt;

&lt;p&gt;大家可以看下面这张图，我们把上一张图里项目对应的 Git 仓库提交记录横过来展示了，现在左上角的这个躺着的是由一次次的提交记录组成的仓库。整个仓库直接保存在我们本地的磁盘上面，这个仓库可以来自于下面两种方式：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic/003_git_concepts_02.png&quot; alt=&quot;git命令图示&quot; title=&quot;Git命令及各环节流转&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;整个仓库可以来自于某个远端仓库，比如 github 里的 &lt;a href=&quot;https://github.com/chalver/book.git&quot;&gt;https://github.com/chalver/book.git&lt;/a&gt; 仓库，我们可以直接 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone remote-repository&lt;/code&gt; 到我们的本地；&lt;/li&gt;
  &lt;li&gt;除此之外呢我们也可以在我们的工作目录直接通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git init&lt;/code&gt; 命令初始化一个本地的仓库。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;大家可以看到 workspace 就是我们正常编写内容的工作区域。当我们打开一个由 git 管理的项目的时候，里面可能已经包含了很多的代码，而且代码是以文件的形式分模块存在的。我们可能会在已有的某几个模块修改已有的函数或者提交几个新的函数，为了把我们修改的内容提交到 git 仓库，我们首先需要把我们的修改 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add&lt;/code&gt; 到 stage 暂存空间，我们可以每修改一点内容都 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add&lt;/code&gt; 到整个空间，从而为我们创建新的 commit 提交做准备。&lt;/p&gt;

&lt;p&gt;在 stage 空间，工作区域分散在各处的修改点被整合到一起。我们可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt; 命令查看 stage 空间里暂存的内容。&lt;/p&gt;

&lt;p&gt;当我们对工作区域的修改感到满意了，把内容全部添加到缓存区，那么我们就可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit&lt;/code&gt; 来把我们的修改提交到本地仓库了。这里我们可以通过在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit -m &quot;本次提交的内容&quot;&lt;/code&gt; 后添加注释来注明本地提交了什么内容。&lt;/p&gt;

&lt;p&gt;如果我们本地的仓库来自于一个远端仓库，或者我们本地初始化的仓库配置了对应的远端仓库，我们可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; 来把本地仓库的更新同步到远端仓库。&lt;/p&gt;

&lt;p&gt;当多人协作的时候，可能出现多人同时编写同一个仓库的情况，我们 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; 本地仓库的时候可能远端的仓库已经被别人更新过了，此时我们就没有办法 push 仓库了。不过这个时候我们可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; 来 merge 别人的更改，这样就可以继续 push 我们的代码到远端了。&lt;/p&gt;

&lt;p&gt;git 中还存在分支的概念，尤其在多人协作的时候尤其重要。不过这部分的内容比较复杂且对开发者的实践要求比较高，我会在后面的课程里再单独拎出来介绍。大家可以先尝试熟练掌握本节的内容。&lt;/p&gt;

&lt;h2 id=&quot;在-vscode-中使用-git&quot;&gt;在 vscode 中使用 git&lt;/h2&gt;

&lt;p&gt;vscode 天然集成了 git 环境，在安装 git 工具以后就可以通过左边的版本控制按钮来到对应的界面。&lt;/p&gt;

&lt;p&gt;我们可以通过 vscode 里提供的图形界面进行上面提到的各项操作，也可以在 vscode 的 terminal 里手动输入各个命令进行操作。&lt;/p&gt;

&lt;p&gt;我个人比较习惯通过命令行的方式操作，避免点错按钮（有的按钮挨着比较近，一个不小心可能导致自己丢失辛辛苦苦修改的内容）。大家可以根据喜好做选择，不过如果大家选择图形界面进行操作，建议大家多多练习一下，避免误操作导致不可挽回的损失=。=。&lt;/p&gt;

&lt;p&gt;大家可以通过查看 output 标签来查看图形界面每个操作的具体 git 命令，大家可以看到我们的一个按钮可能引发这么多的 git 命令的执行。里面有很多的命令可能是我们根本不会手动去敲的，因此不推荐大家去看它，不过在大家对 git 的感知到了某个境界以后，可以通过这里的内容进行探索，比如了解一下各个命令的实际作用，尝试打开另外一个世界的大门。&lt;/p&gt;

&lt;h3 id=&quot;通过图形界面实战&quot;&gt;通过图形界面实战&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;克隆 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://github.com/chalvern/books.git&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;修改内容并添加到暂缓区&lt;/li&gt;
  &lt;li&gt;提交&lt;/li&gt;
  &lt;li&gt;推到远端&lt;/li&gt;
  &lt;li&gt;课堂展示2。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;通过命令行实战&quot;&gt;通过命令行实战&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone https://github.com/chalvern/books.git&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add .&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit -m &quot;add sth&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;课堂展示。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;

&lt;p&gt;本文简单介绍了 git 的安装与初级使用，可至 &lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;https://space.bilibili.com/425571569&lt;/a&gt; 查看对应的视频资料。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/chalvern/books&quot;&gt;本系列课程教案库&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;视频链接地址（视频课程6）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 17 May 2020 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2020/05/17/git-installation-and-usage.html</link>
        <guid isPermaLink="true">https://jingwei.link/2020/05/17/git-installation-and-usage.html</guid>
        
        <category>课程</category>
        
        <category>基础</category>
        
        <category>实用</category>
        
        
        <category>课程</category>
        
      </item>
    
      <item>
        <title>《从0到1学习后端开发》（1）搭建 Linux 环境</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#写在前面&quot; id=&quot;markdown-toc-写在前面&quot;&gt;写在前面&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#初衷&quot; id=&quot;markdown-toc-初衷&quot;&gt;初衷&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#假如目前的你处于下面描述的处境&quot; id=&quot;markdown-toc-假如目前的你处于下面描述的处境&quot;&gt;假如目前的你处于下面描述的处境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#获取-linux-环境的三种方式&quot; id=&quot;markdown-toc-获取-linux-环境的三种方式&quot;&gt;获取 Linux 环境的三种方式&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#虚拟机安装-linux-环境&quot; id=&quot;markdown-toc-虚拟机安装-linux-环境&quot;&gt;虚拟机安装 Linux 环境&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#windows-系统简单使用&quot; id=&quot;markdown-toc-windows-系统简单使用&quot;&gt;Windows 系统简单使用&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#安装虚拟机-virtualbox&quot; id=&quot;markdown-toc-安装虚拟机-virtualbox&quot;&gt;安装虚拟机 virtualBox&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#下载-ubuntu-镜像&quot; id=&quot;markdown-toc-下载-ubuntu-镜像&quot;&gt;下载 Ubuntu 镜像&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#在-virtualbox-安装-ubuntu-系统&quot; id=&quot;markdown-toc-在-virtualbox-安装-ubuntu-系统&quot;&gt;在 virtualBox 安装 Ubuntu 系统&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#ubuntu-系统熟悉&quot; id=&quot;markdown-toc-ubuntu-系统熟悉&quot;&gt;Ubuntu 系统熟悉&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#小结&quot; id=&quot;markdown-toc-小结&quot;&gt;小结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#参考&quot; id=&quot;markdown-toc-参考&quot;&gt;参考&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;

&lt;p&gt;本文是《从0到1学习后端开发》的首篇教案文档。《从0到1学习后端开发》是面向在校大学生、非 IT 工作人员的免费后端开发入门实践教程，可至 &lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;https://space.bilibili.com/425571569&lt;/a&gt; 查看对应的视频资料。&lt;/p&gt;

&lt;h2 id=&quot;初衷&quot;&gt;初衷&lt;/h2&gt;
&lt;p&gt;这一小节我们尝试搭建 Linux 环境。&lt;/p&gt;

&lt;p&gt;之所以搭建 Linux 环境，主要是因为太多的人只接触过 Windows 系统，没有接触过甚至不知道有 Linux 操作系统这件事情。而在我的认知里，后端开发者的基本技能之一就应该是能够使用 Linux 做一些基础的事情，比如查看/编辑文件、安装/配置应用、搭建/维护某种开发环境，等等。&lt;/p&gt;

&lt;p&gt;更为重要的，后端开发者编写的代码大概率是部署在 Linux 服务器上面的，和 Linux 靠的越近，意味着与代码的运行环境越近，写出优质代码的概率更高，排查线上问题的能力也会更好。&lt;/p&gt;

&lt;p&gt;论证使用 Linux 的好处并不是本教程的重点，本教程会假设大家已经意识到这一点，并正在尝试寻找一套靠谱的教程入门后端开发，那么选择本套教程就对了。即使未来因为各种原因本套视频进行不下去，也可以私下联系我（&lt;a href=&quot;https://jingwei.link&quot;&gt;个人主页&lt;/a&gt;，发个邮件总会的吧=。=)讨论各种问题。&lt;/p&gt;

&lt;h2 id=&quot;假如目前的你处于下面描述的处境&quot;&gt;假如目前的你处于下面描述的处境&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;有一台安装了 Windows 系统的电脑（笔记本或者台式机）；如果已经有 Linux 系统安装在物理机上面，或者财力雄厚有苹果电脑（类 Unix 系统，和 Linux 有相同的祖上），都可以直接跳到后面的内容去。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;想要学习后端开发的知识，尤其指 python 或者 Go 语言；这两个语言前者学习比较简单，后者比较流行，而本人主要在这两个技术栈，因此选定它们两个。如果想学习其他的语言，考虑到知识的迁移性，学习 python 和 Go 也会对自己学习其他语言有所帮助。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;自律（比如不怎么睡懒觉）；我奶奶经常教育我们几个孙辈们说，“如果什么都不想去做，那就什么都不会获得”。想要生活改变必须自己首先改变，想学习知识必须要付出或者牺牲一些东西，大道理这里不讲太多。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;到此，万事具备只欠一个 Linux 环境了。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;获取-linux-环境的三种方式&quot;&gt;获取 Linux 环境的三种方式&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Linux 系统直接安装在物理机上面，就是说电脑开机后直接加载运行的就是 Linux 系统；&lt;/li&gt;
  &lt;li&gt;一个物理机上面安装了 Windows 系统和 Linux 系统（所谓的双系统）；&lt;/li&gt;
  &lt;li&gt;在虚拟机上安装 Linux 系统。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;上面的三种方式各有利弊，&lt;/p&gt;

&lt;p&gt;第一种意味着你下了很大的决心学习使用 Linux，并且与 Windows 系统生态下的各种绚丽的应用（包括游戏）说了再见。&lt;/p&gt;

&lt;p&gt;第二种意味着你花了很多时间调研安装双系统的细节，并且很可能因为某些操作失误格式化过磁盘（数据丢失）。&lt;/p&gt;

&lt;p&gt;第三种最简单，但是意味着你的机器配置足够高（至少两个核四个线程，且 4G 以上的内存），而且还要忍受速率慢的问题。&lt;/p&gt;

&lt;p&gt;鉴于前两种的牺牲比较大，而本系列课程定位是入门，因此默认大家按照第三种的方式进行学习，可以学习一定时间找到感觉以后再考虑另外两种方式。&lt;/p&gt;

&lt;h2 id=&quot;虚拟机安装-linux-环境&quot;&gt;虚拟机安装 Linux 环境&lt;/h2&gt;

&lt;h3 id=&quot;windows-系统简单使用&quot;&gt;Windows 系统简单使用&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;桌面整理，把用户文件夹调整出来放桌面上面；&lt;/li&gt;
  &lt;li&gt;查看系统配置；&lt;/li&gt;
  &lt;li&gt;安装/卸载软件；&lt;/li&gt;
  &lt;li&gt;调整启动项；&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;安装虚拟机-virtualbox&quot;&gt;安装虚拟机 virtualBox&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;下载 virtualBox 虚拟机；通过搜索引擎查找到官方网站的下载地址并下载安装包。&lt;/li&gt;
  &lt;li&gt;根据安装包进行安装。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;下载-ubuntu-镜像&quot;&gt;下载 Ubuntu 镜像&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;认识 Ubuntu 官网；&lt;/li&gt;
  &lt;li&gt;认识 “开源镜像站”；&lt;/li&gt;
  &lt;li&gt;下载 ubuntu-18.04 版本&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;在-virtualbox-安装-ubuntu-系统&quot;&gt;在 virtualBox 安装 Ubuntu 系统&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;新建实例，并配置；&lt;/li&gt;
  &lt;li&gt;安装界面；&lt;/li&gt;
  &lt;li&gt;启动&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;ubuntu-系统熟悉&quot;&gt;Ubuntu 系统熟悉&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;启动关机按钮&lt;/li&gt;
  &lt;li&gt;基本信息&lt;/li&gt;
  &lt;li&gt;查看快捷方式，并打开 terminal&lt;/li&gt;
  &lt;li&gt;认识并调整设置 terminal 的快捷键&lt;/li&gt;
  &lt;li&gt;认识应用列表&lt;/li&gt;
  &lt;li&gt;认识软件商城，并安装一个软件（VLC）&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;

&lt;p&gt;本节课程主要带领大家在虚拟机上面安装 Ubuntu（Linux）操作系统，可至 &lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;https://space.bilibili.com/425571569&lt;/a&gt; 查看对应的视频资料。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/chalvern/books&quot;&gt;本系列课程教案库&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://space.bilibili.com/425571569&quot;&gt;视频链接地址（视频课程1、2、3）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 16 May 2020 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2020/05/16/install-ubuntu-on-virtualbox.html</link>
        <guid isPermaLink="true">https://jingwei.link/2020/05/16/install-ubuntu-on-virtualbox.html</guid>
        
        <category>课程</category>
        
        <category>基础</category>
        
        <category>实用</category>
        
        
        <category>课程</category>
        
      </item>
    
      <item>
        <title>学习在一朝一夕，更在长长久久</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#坚持下去的理由与动力&quot; id=&quot;markdown-toc-坚持下去的理由与动力&quot;&gt;坚持下去的理由与动力&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#应该学什么&quot; id=&quot;markdown-toc-应该学什么&quot;&gt;应该学什么&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#如何坚持学习&quot; id=&quot;markdown-toc-如何坚持学习&quot;&gt;如何坚持学习&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;思考一下，我们在自己的技术栈里是否每天都有提升？&lt;/p&gt;

&lt;h2 id=&quot;坚持下去的理由与动力&quot;&gt;坚持下去的理由与动力&lt;/h2&gt;

&lt;p&gt;平时的生活里，不知不觉就懈怠了。&lt;/p&gt;

&lt;p&gt;可怕的是自己意识不到这种懈怠，甚至有时候还把“仰望星空/思考人生”作为懈怠的理由堂而皇之地颓废自己。&lt;/p&gt;

&lt;p&gt;如果我们接受自己一生平庸，那么生活处于懈怠的常态也无可厚非。但是大部分人对自己的生活都是有诉求的，而且在自己当前的诉求得到满足之后还会衍生出另外的诉求（人贪婪的本性）。&lt;/p&gt;

&lt;p&gt;有时候我就会感慨，如果之前的岁月里（每天/每周/每月/每年）做了某件事情，那么现在的自己该有多么厉害。这种感慨说明当前的自己知道了需要的技能，但是对当下状况的改善几乎无济于事。不过反过来想，&lt;strong&gt;如果能够预判到未来自己的短板或需要的技能，是不是现在就可以（每天/每周/每月/每年）做某件事情准备起来呢&lt;/strong&gt;？&lt;/p&gt;

&lt;p&gt;那么未来的自己需要哪些技能？&lt;/p&gt;

&lt;h2 id=&quot;应该学什么&quot;&gt;应该学什么&lt;/h2&gt;

&lt;p&gt;如果知道自己需要的技能，并且清楚掌握这些技能所要学习的教材、书籍、文档等，技能的提升一般是很“迅猛”的。&lt;/p&gt;

&lt;p&gt;难点在于&lt;strong&gt;大部分时候我们不清楚自己需要什么技能&lt;/strong&gt;，不清楚应该学习哪本教材、书籍，不清楚应该阅读什么文档、源码。针对这种情况，笔者根据自己的经验总结出下面几个点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;想一想自己期望的营生方式，以及在这种营生方式下具体的工作内容是什么，从而确定自己所需技能的范畴领域；&lt;/li&gt;
  &lt;li&gt;确定技能领域后，①有条件的情况下锁定一个领路人（比如工作上以学徒的方式向有经验的人学习）②没有条件的情况下，从最基础的官方教程开始自行学习，这里强调以官方教程入手，避免被带偏；&lt;/li&gt;
  &lt;li&gt;坚持学习 10000+ 个小时。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;如何坚持学习&quot;&gt;如何坚持学习&lt;/h2&gt;

&lt;p&gt;阻碍学习的两个拦路障：一个是无知，也就是不知道自己应该学习什么（可以参照上一小节的内容）；另一个是懒惰——人的另一个本性。&lt;/p&gt;

&lt;p&gt;首先应该提一个老少皆宜的大道理：&lt;strong&gt;不努力学习，能力是不会稳定提升的；不持续学习，个人也会局限在某个特定的水平&lt;/strong&gt;。因此”如何坚持学习“就变成了如何克服懒惰的本性。&lt;/p&gt;

&lt;p&gt;根据经验，不妨从一个简单的习惯开始：比如坚持每天跑 1000 米，比如坚持每天做 20 个俯卧撑，比如坚持每天做 50 个仰卧起坐，再比如坚持每天温习一首古诗词，或者坚持每天背诵一个英文单词…… 坚持做一件简单又无聊的事情的价值是什么呢？除去身体素质和精神境界上的提升（哪怕是微弱的），还可以&lt;strong&gt;逐渐养成自律与坚持的品质&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;自律与坚持&lt;/strong&gt;在成年人世界里是两个非常稀缺的品质。如果想培养这两种品质，总要付出一些东西；可能是少放松几局游戏，可能是少仰望一会儿星空，还可能是少睡几刻钟……&lt;/p&gt;

&lt;p&gt;总之，耐心地磨时间吧。没有其他的捷径。&lt;/p&gt;

</description>
        <pubDate>Sat, 25 Apr 2020 00:00:00 +0800</pubDate>
        <link>https://jingwei.link/2020/04/25/learning-is-long-journey.html</link>
        <guid isPermaLink="true">https://jingwei.link/2020/04/25/learning-is-long-journey.html</guid>
        
        <category>碎碎念</category>
        
        <category>基础</category>
        
        <category>实用</category>
        
        
        <category>碎碎念</category>
        
      </item>
    
  </channel>
</rss>