-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
456 lines (292 loc) · 272 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Coding Life</title>
<link href="/atom.xml" rel="self"/>
<link href="http://ajsonx.github.io/"/>
<updated>2021-01-07T14:33:36.000Z</updated>
<id>http://ajsonx.github.io/</id>
<author>
<name>hjx</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>DIY-日历</title>
<link href="http://ajsonx.github.io/2021/01/07/diy-calendar/"/>
<id>http://ajsonx.github.io/2021/01/07/diy-calendar/</id>
<published>2021-01-07T14:33:36.000Z</published>
<updated>2021-01-07T14:33:36.000Z</updated>
<content type="html"><![CDATA[<p>做个日历,新的一年多插几面旗子,多立几个FLAG</p><a id="more"></a><p>2020年买了故宫日历,主题是紫禁城六百年。一天一张,背面是文物介绍,还挺有趣的。</p><p>不过都是被我拿来当<em>TodoList</em>用了。过一天撕一张,撕下来的纸就攒着,数着日子,如果换了个地方就全部丢掉。</p><p>2020年丢三次了。在广州撕了6个月的日历,丢了。搬来深圳第一个地方后又从6月撕到8月。最后还算幸运,2020年安安稳稳的结束了。</p><hr><p>前些天加入购物车里的58元的台历:喜庆的红色、烫金的封面、加厚的卡纸月历,心里忍不住细细磨砂。</p><p>又看了看没有充电器的iPhone12、小米11。为响应环保号召🐶不做韭菜头子,灵机一动,已经想好了怎么打版。果断移出购物车,断绝念想。</p><ol><li>用<strong>鞋盒</strong>做“台” ,红红的也喜庆</li></ol><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20210107/diy_calendar_01.png" alt="拆鞋盒"></p><!--用鞋盒裁剪正好做一个三角形--><ol start="2"><li>用<strong>扎带</strong>连接日历纸和鞋盒</li></ol><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20210107/diy_calendar_02.png" alt="工具"></p><ol start="3"><li>打印的<strong>A4纸</strong>日历</li></ol><p><a href="https://www.calendarpedia.com" target="_blank" rel="noopener">https://www.calendarpedia.com</a> 这里找的Free Download的日历模板,不过都是国外的节假日,凑合啦。<img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20210107/diy_calendar_03.png" alt="A4纸"></p><ol start="4"><li>用胶水、胶布粘住两片纸板,粘连处形成台历的底部就可以了</li></ol><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20210107/diy_calendar_04.png" alt="连接鞋盒"></p><ol start="5"><li>底部做好后就可以对折了,在两片纸板上开孔,用扎带穿过固定住。</li></ol><p>这里扎带的头可以留在日历正上方,先不用藏在里面。多出来的扎带再穿过A4纸日历,再藏到里面。如果要加便签,可以很方便的抽出来穿过去,绑上去。</p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20210107/diy_calendar_05.png" alt="台架子"></p><ol start="6"><li>大功告成,沾沾自喜</li></ol><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20210107/diy_calendar_06.png" alt="DIY-日历"></p>]]></content>
<summary type="html">
<p>做个日历,新的一年多插几面旗子,多立几个FLAG</p>
</summary>
<category term="生活" scheme="http://ajsonx.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
<category term="life" scheme="http://ajsonx.github.io/tags/life/"/>
</entry>
<entry>
<title>TCP/IP协议详解</title>
<link href="http://ajsonx.github.io/2020/08/08/learning-tcp-ip/"/>
<id>http://ajsonx.github.io/2020/08/08/learning-tcp-ip/</id>
<published>2020-08-08T09:30:54.000Z</published>
<updated>2020-08-08T09:30:54.000Z</updated>
<content type="html"><![CDATA[<p>电子科技大学-网络教育课程-杨宁老师的《TCP/IP详解》听课笔记</p><a id="more"></a><hr><h1 id="1~3"><a href="#1~3" class="headerlink" title="1~3"></a>1~3</h1><p>引言、OSI模型与TCP/IP、底层网络技术</p><h2 id="协议和标准"><a href="#协议和标准" class="headerlink" title="协议和标准"></a>协议和标准</h2><ul><li>What is 协议<ul><li>一组控制数据通信的规则</li><li>三要素:语法,语义,时序(控制何时通信)</li></ul></li><li>What is 标准<ul><li>一致同意的规则</li><li>种类:1. 事实上的标准——实际或习惯 2. 合法标准——组织制定的法律或规章</li></ul></li><li>标准化组织有哪些<ul><li>ISO(国际标准化组织)</li><li>ITU-T</li><li>ANSI(美国国家标准化局)</li><li>IEEE(电气电子工程师学会)</li><li>EIA</li></ul></li><li>Internet标准:RFC</li></ul><h2 id="OSI模型和TCP-IP协议族"><a href="#OSI模型和TCP-IP协议族" class="headerlink" title="OSI模型和TCP/IP协议族"></a>OSI模型和TCP/IP协议族</h2><ul><li>什么是OSI参考模型<ul><li>ISO组织制定的参考模型,Open System Interconnection</li><li>使两个不同的系统能够通信的一套参考标准</li></ul></li><li>OSI模型的层次功能<ul><li>七层及其功能<ul><li>应用层:为用户提供网络处理的应用</li><li>表示层:数据编码、转换</li><li>会话层:管理会话的过程</li><li>传输层:端到端的数据传输</li><li>网络层:编址、寻址</li><li>数据链路层:送交给网络介质</li><li>物理层:二进制信号的物理传输</li></ul></li><li>服务:下层为上一层提供服务</li><li>接口:不同层之间,服务的使用和提供</li><li>对等实体:存在通信关系的对等层实体才是对等实体</li><li>对等层通信:只有对等实体之间才能通过对等层协议进行通信</li><li>PDU:Protocol Data Unit. 协议数据单元,对等层之间传输的数据</li><li>帧、分组、数据段、数据分别表示的是第2、3、4、(5 & 6 & 7)层的PDU</li></ul></li><li>TCP/IP协议族<ul><li>层次功能<ul><li>应用层</li><li>传输层</li><li>网际(Internet)层</li><li>网络接入层</li></ul></li></ul></li></ul><h2 id="编址"><a href="#编址" class="headerlink" title="编址"></a>编址</h2><h3 id="Internet-网常用的三个地址概念"><a href="#Internet-网常用的三个地址概念" class="headerlink" title="Internet 网常用的三个地址概念"></a>Internet 网常用的三个地址概念</h3><ul><li>物理地址<ul><li>标识通信节点</li><li>由所属LAN或WAN指定</li><li>LAN/WAN内唯一</li><li>链路地址/硬件地址</li><li>数据链路层上的,不是物理层</li><li>例子:0x0005.5D06.1418(MAC地址)</li></ul></li><li>IP地址<ul><li>网络层上的地址</li><li>标识通信节点的网络连接(物理地址是在物理网内部之间,而IP地址是在不同的物理网之间标识通信节点)</li><li>Internet内唯一</li><li>是互联网上的地址</li></ul></li><li>端口地址<ul><li>标识通信进程</li><li>操作系统指定</li><li>一台计算机内唯一</li><li>例如:21(FTP)、22(SSH)、23(Telnet)、25(SMTP)、80(HTTP)</li><li>传输层</li></ul></li></ul><h3 id="目的地址的分类"><a href="#目的地址的分类" class="headerlink" title="目的地址的分类"></a>目的地址的分类</h3><ul><li>单播<ul><li>一个接收者</li><li>除了下面两个之外的地址都可以作为单播地址</li></ul></li><li>多播<ul><li>一组接收者</li><li>物理地址:前面24bit必须是0x0100.5Exx.xxxx;IP地址中:224.0.0.0~239.255.255.255都是多播地址的可用范围</li><li>IGMP(Internet Group Management Protocol)协议</li></ul></li><li>广播<ul><li>网络中的所有系统</li><li>广播地址:物理地址全1:(0xFFFF.FFFF.FFFF);IP地址全1(255.255.255.255)</li></ul></li></ul><h3 id="TCP-IP的版本"><a href="#TCP-IP的版本" class="headerlink" title="TCP/IP的版本"></a>TCP/IP的版本</h3><p>主要是IP协议的改动,IP协议是TCP/IP协议的核心</p><ul><li>Version 4<ul><li>地址空间不足和低效率:32位的地址(后来是IPv6)</li><li>对服务质量和安全方面支持较弱(音视频传输等,后来出现了UDP)</li></ul></li><li>Version 5<ul><li>基于OSI模型</li><li>层次改变大,代价高,没有实际使用</li></ul></li><li>Version 6<ul><li>仅改动了网络层协议:IPv6、ICMPv6</li><li>扩大了地址空间:128位的地址</li><li>改进了版本4的弱点</li></ul></li></ul><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><ul><li><p>对分层网络协议体系的理解</p><ul><li>不同节点:层次组成不同,作用不同</li><li>横向理解:虚通信、对等实体、协议、PDU</li><li>纵向理解:封装与解封、服务、接口</li></ul></li><li><p>TCP/IP</p><ul><li>层次:掌握与OSI模型的对应关系</li><li>协议:掌握主要协议及其所在层次</li></ul></li><li><p>地址</p><ul><li>掌握物理、IP、端口地址:作用、所在层次</li><li>掌握单播、多播、广播地址:作用</li></ul></li></ul><h2 id="底层网络技术"><a href="#底层网络技术" class="headerlink" title="底层网络技术"></a>底层网络技术</h2><h3 id="传输介质"><a href="#传输介质" class="headerlink" title="传输介质"></a>传输介质</h3><p>不同的传输介质在物理层上(比特流)有不同的实现(电流高低、光的强弱)</p><ul><li>有线介质——导线管(导向媒体)<ul><li>双绞线</li><li>同轴电缆(金属铜导线,电流)</li><li>光纤(玻璃或塑料、光)</li></ul></li><li>无线介质——电磁波(非导向媒体、无方向的)<ul><li>8个频段(无线电、红外)</li></ul></li></ul><h3 id="局域网(LAN)"><a href="#局域网(LAN)" class="headerlink" title="局域网(LAN)"></a>局域网(LAN)</h3><h4 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h4><p>允许一些独立设备在受限地理范围内彼此都能直接通信(有些过时)</p><p>共享介质型——广播式的通信特征</p><h4 id="技术"><a href="#技术" class="headerlink" title="技术"></a>技术</h4><ul><li>Ethernet(以太网)<ul><li>接入方法:CSMA/CD(载波监听多路访问方式),监听信道是否空闲。检查是否会发生冲突,一旦发生冲突,放弃发送数据,物理网段上的数据都不再有效</li><li>地址:6位</li><li>在局域网中又将数据链路层分为了两层:逻辑链路控制子层(靠近网络层),介质访问控制子层(靠近物理层,只有局域网中才有MAC地址的概念,广域网等叫物理地址)</li><li>帧(数据链路层中包的单位):数据帧<ul><li>根据RFC标准制定的不同,帧格式也有所不同,例:RFC 984 和RFC 1042 </li><li>RFC894 : Preamble|SFD|Des. Addr|Source Addr|Type|Data|FCS</li><li>这些帧是对网络层数据(Data)的封装。在IEEE802.2/802.3封装的帧格式中,Data中有3个字节的LLC数据,因为局域网中分了LLC子层和MAC子层,数据帧的封装是靠近物理层的MAC子层,3个字节的LLC是提供给LLC子层使用,用于CSMA/CD等。</li><li>根据Source Addr. 后的字节来判断(可能用到2个,也可能4个。目前在用的以太网帧格式就几种)</li><li>对于物理层来说,这些都是bit流,帧格式是没有意义的。但帧格式的前导符以及帧定位符来告诉物理层应该从哪里开始构成一个帧。最后送交给数据链路层。</li></ul></li><li>除视频课之外我详细了解了下<a href="https://blog.csdn.net/frank_jb/article/details/40626239?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase" target="_blank" rel="noopener">以太网在不同工作模式下的区别</a>:以太网发展初期,共享式以太网下的工作模式是半双工,到了后来出现的是交换式以太网,采用的是全双工的工作模式。全双工模式下,网卡可以同时发送和接受数据包,并且交换机与PC机直连,通信介质是使用双绞线,所以不存在什么信道冲突。也就无需采用CSMA/CD机制。</li></ul></li><li>Token Ring & FDDI (了解即可,是一种无冲突的共享介质型网络)<ul><li>令牌传递</li></ul></li></ul><h3 id="交换"><a href="#交换" class="headerlink" title="交换"></a>交换</h3><p>交换系统:有多个输入和输出的“黑盒子”</p><h4 id="交换技术"><a href="#交换技术" class="headerlink" title="交换技术"></a>交换技术</h4><ul><li><p>电路交换(电话系统)</p><ul><li>建立连接</li><li>数据传输:每个主机都有自己独立的信道,通信的两个节点之间独占介质,其他站点无法与这两个节点通信。如果有公共介质,可能采用了时分复用技术或者频分复用技术。</li><li>拆除连接</li></ul></li><li><p>分组交换</p><ol><li><p>数据报方式:数据发送之前不需要建立连接。将数据分组,每个组独立的发送。数据可能丢失,到达先后次序也不一致。</p></li><li><p>虚电路方式:需要建立连接,沿着一条路发送。</p><ul><li>电路交换数据传输是以流的单位,虚电路是以分组的单位</li><li>电路是物理上的连接,虚电路是逻辑上的一条连接</li></ul></li></ol></li><li><p>报文交换:和分组交换中数据报方式差不多,分组交换中将数据报分为多个组,而报文交换是以数据报为单位。</p></li></ul><h3 id="广域网(WAN)"><a href="#广域网(WAN)" class="headerlink" title="广域网(WAN)"></a>广域网(WAN)</h3><h4 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h4><ul><li>很大的地理范围,长距离传输。</li><li>交换网络——点到点式</li></ul><h4 id="技术-1"><a href="#技术-1" class="headerlink" title="技术"></a>技术</h4><p>很多,没详细讲。</p><p>局域网和广域网的比较:知识点都讲过。</p><h3 id="连接设备"><a href="#连接设备" class="headerlink" title="连接设备"></a>连接设备</h3><ul><li>转发器/集线器:仅在物理层,再生放大网络信号。</li><li>网桥/交换机:<ul><li>网桥可以用软件来实现,交换机是从硬件来实现。不限制连接网段的个数。</li><li>网桥根据数据帧的内容转发数据给相邻的其他网络</li><li>网桥可以连接传输速率不同的网络</li><li>网桥将数据存储于内存,再重新生成信号作为一个全新的帧去转发。</li><li>具备地址(MAC、物理、硬件、适配器)自学机制和过滤功能控制网络流量</li><li>还可以检查数据帧中FCS的值,校验数据。</li></ul></li><li>路由器:<ul><li>跟据IP地址进行转发,连接不同的数据链路</li></ul></li><li>网关/4~7层交换机:<ul><li>负载均衡器</li><li>数据转换</li><li>特殊应用访问加速,带宽控制</li><li>防火墙</li></ul></li></ul><h1 id="4~5(IP编址技术)"><a href="#4~5(IP编址技术)" class="headerlink" title="4~5(IP编址技术)"></a>4~5(IP编址技术)</h1><p>IP编址、构成子网和超网</p><h2 id="第四章"><a href="#第四章" class="headerlink" title="第四章"></a>第四章</h2><p>IP地址:唯一标识互联网上的主机和路由器</p><p>路由器需要连接不同的网络、每个接口都要分配一个IP地址</p><p>主机标识符</p><ul><li>Name:字符串,可读性强(DNS)</li><li>Addr :二进制,软件效率高(IP地址)</li><li>Route:怎样到达(路由协议)</li></ul><h3 id="IP地址结构"><a href="#IP地址结构" class="headerlink" title="IP地址结构"></a>IP地址结构</h3><ul><li><p>32Bit二进制地址</p></li><li><p>模拟物理网的编址机制</p></li><li><p>层次编址,而物理地址是平面编址</p></li><li><p>Network id + Host id:一个用来标识网络,一个标识主机。网络标识区分数据链路层的不同段。</p></li><li><p>点分十进制。实际上使用是不分的。</p></li></ul><h3 id="IP地址分类"><a href="#IP地址分类" class="headerlink" title="IP地址分类"></a>IP地址分类</h3><h4 id="A类地址"><a href="#A类地址" class="headerlink" title="A类地址"></a>A类地址</h4><ul><li>标识位:前一位,值为0</li><li>Netid:7位</li><li>Hostid:24位</li><li>范围:0.0.0.0 ~ 127.255.255.255</li></ul><h4 id="B类"><a href="#B类" class="headerlink" title="B类"></a>B类</h4><ul><li>标识位:前两位,值为 10</li><li>Netid:14</li><li>Hostid:16</li><li>范围:128.0.0.0 ~ 191.255.255.255</li></ul><h4 id="C类"><a href="#C类" class="headerlink" title="C类"></a>C类</h4><ul><li>标识位:前三位,值为110</li><li>Netid:21</li><li>Hostid:8</li><li>范围:192.0.0.0 ~ 223.255.255.255</li></ul><h4 id="D类"><a href="#D类" class="headerlink" title="D类"></a>D类</h4><ul><li>标识位:前四位,值为1110</li><li>标识多播应用</li><li>无网络和主机标识</li><li>范围:224.0.0.0 ~ 239.255.255.255</li></ul><h4 id="E类"><a href="#E类" class="headerlink" title="E类"></a>E类</h4><ul><li>标识位:前四位,值为1111</li><li>无网络和主机标识</li><li>范围:240.0.0.0 ~ 255.255.255.255</li><li>用于研究</li></ul><h3 id="多接口设备"><a href="#多接口设备" class="headerlink" title="多接口设备"></a>多接口设备</h3><ul><li>多接口计算机:可以连接到多个网络</li><li>路由器:必须连接到多个网络、每个网络连接分配一个IP地址,可属于不同的类</li></ul><h3 id="特殊地址"><a href="#特殊地址" class="headerlink" title="特殊地址"></a>特殊地址</h3><ul><li>网络地址:标识整个网络。既不能作为源,也不能作为目的地址。<strong><em>Netid特定,Hostid全0</em></strong></li><li>直接广播地址:标识特定网络的广播。不能作为源,可以作为目的地址。存在安全问题,路由器上设置为不转发。<strong><em>Netid特定,Hostid全1</em></strong></li><li>受限广播地址:标识本地网络的广播。该源所在的网络内广播。<strong><em>Netid和Hostid全1</em></strong></li><li>本网络上的本主机:不知道自身ip地址时,发送请求分组使用。<strong><em>0.0.0.0</em></strong></li><li>本网络的特定主机:标识某网络的主机,实际不使用。<strong><em>Netid全0,Hostid不为0</em></strong></li><li>环回地址:网络层发现IP地址是环回地址时,直接回传给传输层,不经过物理层。<strong><em>Netid全1,Hostid特定</em></strong> 例:127.0.0.1</li></ul><h3 id="互联网实例"><a href="#互联网实例" class="headerlink" title="互联网实例"></a>互联网实例</h3><ul><li>一个物理接口可以有多个逻辑连接,但是多个物理连接不能共用一个逻辑连接。</li></ul><h3 id="单播、多播和广播地址"><a href="#单播、多播和广播地址" class="headerlink" title="单播、多播和广播地址"></a>单播、多播和广播地址</h3><ul><li>多播:一对多。使用D类地址。应用:电话会议</li></ul><h3 id="申请IP地址"><a href="#申请IP地址" class="headerlink" title="申请IP地址"></a>申请IP地址</h3><p>知识点偏课本的性质,不记录了。</p><h2 id="第五章·11"><a href="#第五章·11" class="headerlink" title="第五章·11"></a>第五章·11</h2><p>Classful IP 编址的缺陷:</p><ul><li>C类:少于255台(255-2)主机的网络</li><li>B类:介于255~65535台主机的网络</li><li>A类:超过65535台</li></ul><p>网络标识相同的计算机必须同属于同一个链路,会造成地址浪费过大的情况</p><p>解决方案:子网划分、超网划分。</p><h3 id="构成子网"><a href="#构成子网" class="headerlink" title="构成子网"></a>构成子网</h3><p>子网划分是将主机地址(HostID)的部分用作子网地址</p><p>例如B类网:142.14.0.0 划分为 142.14.2.0 、142.14.7.0等子网</p><ul><li>不能用首字节判定网络大小</li><li>字节边界没有意义</li></ul><p><strong><em>因此引入了掩码</em></strong></p><ul><li>表示网络或子网的大小</li><li>32bit二进制数<ul><li>网络或子网(Netid+Subnetid)部分为1</li><li>主机部分为0</li></ul></li></ul><p>例如:C类网络IP地址:202.115.12.0、掩码(Mask):255.255.255.192 可以得出子网大小 64 - 2 = 62 </p><h4 id="术语"><a href="#术语" class="headerlink" title="术语"></a>术语</h4><ul><li>网络地址<ul><li>特定Net id</li><li>全0 Subnet id + 全0 Host id</li><li>例如:172.16.0.0 / 172.16.0.255 ->广播地址</li></ul></li><li>子网地址<ul><li>特定Net id + 特定 Subnet id</li><li>全0 Host id</li><li>例如:172.16.16.0</li></ul></li><li>主机地址<ul><li>全是特定</li><li>例如:172.16.16.201</li></ul></li></ul><h3 id="掩码"><a href="#掩码" class="headerlink" title="掩码"></a>掩码</h3><h4 id="掩码表示"><a href="#掩码表示" class="headerlink" title="掩码表示"></a>掩码表示</h4><ul><li>点分十进制表示:255.255.255.192</li><li>位数表示: /26</li></ul><h4 id="掩码运算"><a href="#掩码运算" class="headerlink" title="掩码运算"></a>掩码运算</h4><p>&(与)运算</p><ul><li>Net address = IP & Mask</li><li>Host address = IP & Mask的反码</li></ul><h3 id="子网构成举例"><a href="#子网构成举例" class="headerlink" title="子网构成举例"></a>子网构成举例</h3><h1 id="6~8(网络层协议)"><a href="#6~8(网络层协议)" class="headerlink" title="6~8(网络层协议)"></a>6~8(网络层协议)</h1><h1 id="9~10(传输层协议)"><a href="#9~10(传输层协议)" class="headerlink" title="9~10(传输层协议)"></a>9~10(传输层协议)</h1><h1 id="11(客户服务器模型)"><a href="#11(客户服务器模型)" class="headerlink" title="11(客户服务器模型)"></a>11(客户服务器模型)</h1><h1 id="12~14(应用层协议)"><a href="#12~14(应用层协议)" class="headerlink" title="12~14(应用层协议)"></a>12~14(应用层协议)</h1>]]></content>
<summary type="html">
<p>电子科技大学-网络教育课程-杨宁老师的《TCP/IP详解》听课笔记</p>
</summary>
</entry>
<entry>
<title>PHP7中对象的引用传递</title>
<link href="http://ajsonx.github.io/2020/04/17/php-obj-ref/"/>
<id>http://ajsonx.github.io/2020/04/17/php-obj-ref/</id>
<published>2020-04-16T17:05:30.000Z</published>
<updated>2020-04-16T17:05:30.000Z</updated>
<content type="html"><![CDATA[<ol><li>实现链表过程中的疑惑</li><li>PHP对象数据类型的引用传递</li></ol><p>本文代码较多,如果不对/疑惑之处请提出</p><a id="more"></a><hr><h2 id="链表"><a href="#链表" class="headerlink" title="链表"></a>链表</h2><h3 id="PHP实现"><a href="#PHP实现" class="headerlink" title="PHP实现"></a>PHP实现</h3><p>这几天在写链表相关的代码,以前都是用C++打的,没有考虑过PHP实现方式。可能是我深受C++的影响吧,一时竟然不知道PHP中如何表示指针。大家可以试着先实现一下链表,再来看下面这段PHP实现的链表代码:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ListNode</span> </span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">public</span> $val = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">public</span> $next = <span class="keyword">null</span>;</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">($val)</span> </span>{ <span class="keyword">$this</span>->val = $val; }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">$linkList = <span class="keyword">new</span> ListNode(<span class="keyword">null</span>);</span><br><span class="line">$p = $linkList; <span class="comment">//传递链表类,原地址计数+1</span></span><br><span class="line"><span class="keyword">for</span> ($i=<span class="number">1</span>; $i < <span class="number">5</span>; $i++) {</span><br><span class="line"> $t = <span class="keyword">new</span> ListNode($i);</span><br><span class="line"> $p->next = $t; </span><br><span class="line"> $p = $p->next; <span class="comment">//究竟是怎么把p的值传递给$linkList呢?</span></span><br><span class="line"> <span class="comment">//对象不是引用传递吗?此时改变p变量不也就改变了$linkList变量吗?</span></span><br><span class="line">}</span><br><span class="line">var_dump($linkList);</span><br></pre></td></tr></table></figure><p>可以看到其实就是一个单链表的实现过程。先创建了一个头节点,<code>$p</code>是指向 <code>$linkList</code> 的指针,接着使用尾插法插入数据。</p><p>当我一步步阅读代码的时候,疑惑产生了。就在这一行 <code>$p = $p->next;</code></p><p>不知道你有没有理解我在说什么,首先一个前提是:PHP的对象是引用传递的。所以我们才能在下面这行代码中改变<code>$linkList</code>的值</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$p->next = $t;</span><br></pre></td></tr></table></figure><p>接下来是修改变量p的值</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$p = $p->next;</span><br></pre></td></tr></table></figure><p>很幸运,<code>linkList</code>变量没有被修改,我们实现了链表的功能</p><p>why???</p><p>在我的理解中引用传递就是<strong>使用不同变量名访问同一个zval容器</strong>。并且,使用引用时,是<strong>关闭了变量的写时复制</strong>,正如下面这段经典的代码:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$a = <span class="number">1</span>;</span><br><span class="line">$b = $a;</span><br><span class="line">$c = &$a;</span><br><span class="line"></span><br><span class="line">$a = <span class="number">2</span>;</span><br><span class="line"><span class="comment">/*结果</span></span><br><span class="line"><span class="comment">$a = 2</span></span><br><span class="line"><span class="comment">$b = 1</span></span><br><span class="line"><span class="comment">$c = 2</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><h3 id="C-的实现"><a href="#C-的实现" class="headerlink" title="C++的实现"></a>C++的实现</h3><p>接着我开始思考指针和引用的区别,再来看看C++中利用指针的实现吧!</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ListNode</span> {</span></span><br><span class="line"> <span class="keyword">int</span> val;</span><br><span class="line"> ListNode *next;</span><br><span class="line"> ListNode(<span class="keyword">int</span> x) : val(x), next(<span class="literal">NULL</span>) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//头插法</span></span><br><span class="line">ListNode* head1 = <span class="keyword">new</span> ListNode;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++)</span><br><span class="line">{</span><br><span class="line"> ListNode* temp = <span class="keyword">new</span> ListNode(i);</span><br><span class="line"> temp->next = head1->next;</span><br><span class="line">head1->next = temp; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//尾插法</span></span><br><span class="line">ListNode* head2 = <span class="keyword">new</span> ListNode;</span><br><span class="line">ListNode* p = head2;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++)</span><br><span class="line">{</span><br><span class="line"> ListNode* temp = <span class="keyword">new</span> ListNode(i);</span><br><span class="line"> p->next = temp; </span><br><span class="line"> p = p->next;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>指针,就是指向数据内存地址,指针变量就是用来保存这些地址的变量。利用指针,可以轻松的访问修改被指向变量的数据。也就是浅拷贝,修改了指针就等于修改了该内存地址上的数据。然而深拷贝,就像PHP中的写时复制,修改数据互不影响。</p><p>搞清了指针的概念,我们好像看出点什么来了,尽管在PHP中,某些时候<strong><em>引用</em></strong>可以实现<strong><em>指针</em></strong>的功能,但<strong><em>指针</em></strong>和<strong><em>引用</em></strong>并不是一个东西。</p><p>接着这里提一句,如果对PHP7对象引用传递有怀疑的,大家可以自行实现一下。这里仅提供一份代码,不再赘述。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//数组</span></span><br><span class="line">$arrA = [<span class="string">'k1'</span> => <span class="string">'v1'</span>,<span class="string">'k2'</span>=><span class="string">'v2'</span>,<span class="string">'k3'</span>=><span class="string">'v3'</span>];</span><br><span class="line"></span><br><span class="line">$arrB = $arrA;</span><br><span class="line"></span><br><span class="line">$arrB[<span class="string">'k1'</span>] = <span class="string">'v100'</span>;</span><br><span class="line"></span><br><span class="line">var_dump($arrA);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 证明对象是引用传递</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">easyObj</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"><span class="keyword">public</span> $val;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">($val)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">$this</span>->val = $val;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">$objA = <span class="keyword">new</span> easyObj(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">$objB = $objA;</span><br><span class="line"></span><br><span class="line">$objB->val = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">var_dump($objA);</span><br></pre></td></tr></table></figure><h2 id="调试一探究竟"><a href="#调试一探究竟" class="headerlink" title="调试一探究竟"></a>调试一探究竟</h2><p>我们用<em>gdb</em>来调试下</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ListNode</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">public</span> $val = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">public</span> $next = <span class="keyword">null</span>;</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">($val)</span> </span>{ <span class="keyword">$this</span>->val = $val; }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">$linkList = <span class="keyword">new</span> ListNode(<span class="number">0</span>);</span><br><span class="line">$p = $linkList;</span><br><span class="line">$p->val = <span class="number">1</span>;</span><br></pre></td></tr></table></figure><p>此时我们来查看变量<code>$linkList(zval)</code> 中的 <code>zend_object</code>的内存地址</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 以下是运行到此处的gdb调试结果</span></span><br><span class="line"><span class="comment"># $linkList</span></span><br><span class="line">(gdb) p args.value.obj</span><br><span class="line"><span class="variable">$7</span> = (zend_object *) 0x101a65150</span><br><span class="line"><span class="comment"># $p</span></span><br><span class="line">(gdb) p args.value.obj</span><br><span class="line"><span class="variable">$7</span> = (zend_object *) 0x101a65150</span><br><span class="line"><span class="comment"># 相同的zend_object</span></span><br><span class="line">(gdb) p *args.value.obj</span><br><span class="line"><span class="variable">$4</span> = {gc = {refcount = 3, u = {v = {<span class="built_in">type</span> = 8 <span class="string">'\b'</span>, flags = 0 <span class="string">'\000'</span>, gc_info = 49154},</span><br><span class="line"> type_info = 3221356552}}, handle = 1, ce = 0x101a05018, handlers = 0x100b1dd68, properties = 0x0,</span><br><span class="line"> properties_table = {{value = {lval = 1, dval = 4.9406564584124654e-324, counted = 0x1, str = 0x1, arr = 0x1,</span><br><span class="line"> obj = 0x1, res = 0x1, ref = 0x1, ast = 0x1, zv = 0x1, ptr = 0x1, ce = 0x1, func = 0x1, ww = {w1 = 1,</span><br><span class="line"> w2 = 0}}, u1 = {v = {<span class="built_in">type</span> = 4 <span class="string">'\004'</span>, type_flags = 0 <span class="string">'\000'</span>, const_flags = 0 <span class="string">'\000'</span>,</span><br><span class="line"> reserved = 0 <span class="string">'\000'</span>}, type_info = 4}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0,</span><br><span class="line"> fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}}}</span><br></pre></td></tr></table></figure><p>此时的<code>$p</code>和<code>$linkList</code>其中的<code>zend_object</code>地址是相同的,修改了<code>$p->val</code> 就相当于修改了<code>$linkList</code>中的<code>zend_object</code>,并且<code>zend_object</code>的 引用计数为3。这就是对象的引用传递。在zval中的obj指针指向对象的实际地址。</p><p>代码继续执行:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$p = $p->next; <span class="comment">//next 为 null</span></span><br></pre></td></tr></table></figure><p>此时变量<code>$p</code>变为<code>null</code>,那么<code>$linkList</code>会不会也变成<code>null</code>呢?我们来看看<code>$p</code>和<code>$linkList</code>的zval结构:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># $p的zval</span></span><br><span class="line">(gdb) p *args</span><br><span class="line"><span class="variable">$8</span> = {value = {lval = 4322644304, dval = 4.9406564584124654e-324, counted = 0x1, str = 0x1, arr = 0x1, obj = 0x1,</span><br><span class="line"> res = 0x1, ref = 0x1, ast = 0x1, zv = 0x1, ptr = 0x1, ce = 0x1, func = 0x1, ww = {w1 = 1, w2 = 0}}, u1 = {</span><br><span class="line"> v = {<span class="built_in">type</span> = 0 <span class="string">'\004'</span>, type_flags = 0 <span class="string">'\000'</span>, const_flags = 0 <span class="string">'\000'</span>, reserved = 0 <span class="string">'\000'</span>}, type_info = 4},</span><br><span class="line"> u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0,</span><br><span class="line"> property_guard = 0}}</span><br><span class="line"></span><br><span class="line"><span class="comment"># $linkList</span></span><br><span class="line">(gdb) p *args</span><br><span class="line"><span class="variable">$9</span> = {value = {lval = 4322644304, dval = 2.1356700497977457e-314, counted = 0x101a65150, str = 0x101a65150,</span><br><span class="line"> arr = 0x101a65150, obj = 0x101a65150, res = 0x101a65150, ref = 0x101a65150, ast = 0x101a65150,</span><br><span class="line"> zv = 0x101a65150, ptr = 0x101a65150, ce = 0x101a65150, func = 0x101a65150, ww = {w1 = 27677008, w2 = 1}},</span><br><span class="line"> u1 = {v = {<span class="built_in">type</span> = 8 <span class="string">'\b'</span>, type_flags = 12 <span class="string">'\f'</span>, const_flags = 0 <span class="string">'\000'</span>, reserved = 0 <span class="string">'\000'</span>},</span><br><span class="line"> type_info = 3080}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0,</span><br><span class="line"> access_flags = 0, property_guard = 0}}</span><br></pre></td></tr></table></figure><p>可以看到,<code>$p</code>已经变成 <code>type=0</code>的<code>NULL</code>类型,而<code>$linkList</code>还是原先的结构,保留着之前对象。这时发生了分离。</p><p>至此,我们可以想到,对象的引用传递是不发生在zval上的,当你修改zval的值,那么zval结构当然会产生变化。当你修改zval中的zend_object时,保存该对象的所有zval也发生改变。(把它说成是指针传递会不会更好理解?)</p><h2 id="自问自答"><a href="#自问自答" class="headerlink" title="自问自答"></a>自问自答</h2><ul><li><p>Q:既然zval和对象是分开的,是否意味着对象一旦创建就无法真正的销毁?</p><p>A:不会的,PHP7中对象本身自带引用计数,不再是PHP5中的双重引用了。当zend_object的引用计数为0时,也就进入了垃圾回收管理。</p></li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>PHP对象是引用传递</li><li>引用不等于指针,引用可以看作是变量的别名</li><li>这些内容平时开发相对少用到,但使用时需要注意数据的严谨性</li></ul><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><ul><li><a href="https://segmentfault.com/q/1010000010641993?_ea=2363326" target="_blank" rel="noopener">segmentfault-关于PHP对象传值</a></li><li><a href="https://0x1.im/blog/php/Internal-value-representation-in-PHP-7-part-2.html" target="_blank" rel="noopener">变量在PHP7内部的实现</a></li></ul>]]></content>
<summary type="html">
<ol>
<li>实现链表过程中的疑惑</li>
<li>PHP对象数据类型的引用传递</li>
</ol>
<p>本文代码较多,如果不对/疑惑之处请提出</p>
</summary>
</entry>
<entry>
<title>laravel服务容器实践</title>
<link href="http://ajsonx.github.io/2020/04/04/laravel-service-container/"/>
<id>http://ajsonx.github.io/2020/04/04/laravel-service-container/</id>
<published>2020-04-04T10:59:44.000Z</published>
<updated>2020-04-04T10:59:44.000Z</updated>
<content type="html"><![CDATA[<p>很多时候发现光看并不能很好的学习新东西,必须要动手操作才有更深刻的理解。这篇文章边敲代码边学习laravel的核心功能</p><a id="more"></a><hr><h2 id="类的反射与依赖注入"><a href="#类的反射与依赖注入" class="headerlink" title="类的反射与依赖注入"></a>类的反射与依赖注入</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Tel</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">public</span> $number;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">($number = <span class="number">110</span>)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">$this</span>->number = $number;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Phone</span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">const</span> Time_ZONE = <span class="string">'CN'</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> $time;</span><br><span class="line"> <span class="keyword">public</span> $tel;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">(Tel $tel)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">$this</span>->tel = $tel;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">call</span><span class="params">(Tel $tel)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">echo</span> $tel->number . PHP_EOL;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">$reflectionClass = <span class="keyword">new</span> ReflectionClass(Phone::class);</span><br><span class="line"></span><br><span class="line">var_dump($reflectionClass);</span><br><span class="line">var_dump($reflectionClass->getConstructor());</span><br><span class="line"></span><br><span class="line">$tel = <span class="keyword">new</span> Tel(<span class="number">123</span>);</span><br><span class="line"><span class="comment">//Creates a new class instance from given arguments.</span></span><br><span class="line">var_dump($reflectionClass->newInstance($tel));</span><br><span class="line"></span><br><span class="line"><span class="comment">//反射类的一些方法,详细需要看手册理解,或者追踪该方法</span></span><br><span class="line">var_dump($reflectionClass->getConstants());</span><br><span class="line">var_dump($reflectionClass->getProperty(<span class="string">'time'</span>));</span><br><span class="line">var_dump($reflectionClass->getProperties());</span><br><span class="line">var_dump($reflectionClass->getFileName());</span><br><span class="line">var_dump($reflectionClass->getMethods());</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">$parameters = $reflectionClass->getMethod(<span class="string">'call'</span>)->getParameters();</span><br><span class="line"><span class="keyword">foreach</span> ($parameters <span class="keyword">as</span> $parameter) {</span><br><span class="line"> var_dump($parameter->isDefaultValueAvailable()); <span class="comment">// 是否是默认参数</span></span><br><span class="line"> var_dump($parameter->getClass()); <span class="comment">//得到反射类对象</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">//依赖注入</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">make</span><span class="params">(string $className)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $reflectionClass = <span class="keyword">new</span> ReflectionClass($className);</span><br><span class="line"> $constructor = $reflectionClass->getConstructor(); <span class="comment">// 获取构造方法</span></span><br><span class="line"> $parameters = $constructor->getParameters(); <span class="comment">//获取方法中的参数</span></span><br><span class="line"> $dependencies = getDependencies($parameters);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> $reflectionClass->newInstanceArgs($dependencies);<span class="comment">//最后实例化出所依赖的类,实现依赖注入</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取依赖</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> array $parameters 构造函数里的参数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> array</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getDependencies</span><span class="params">(array $parameters)</span>: <span class="title">array</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $dependencies = [];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> ($parameters <span class="keyword">as</span> $parameter) {</span><br><span class="line"> $dependency = $parameter->getClass(); <span class="comment">//获取构造函数中参数的对象</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (is_null($dependency)) { <span class="comment">//如果没有则返回默认值或者设定值</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ($parameter->isDefaultValueAvailable()) {</span><br><span class="line"> $dependencies[] = $parameter->getDefaultValue(); <span class="comment">//获取默认值</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $dependencies[] = <span class="string">'0'</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> { <span class="comment">//如果构造函数中的参数依赖某个类,则递归地实例化依赖类</span></span><br><span class="line"> $temp = make($dependency->name);</span><br><span class="line"> $dependencies[] = $temp;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $dependencies;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">$phone = make(<span class="string">'Phone'</span>);</span><br><span class="line">var_dump($phone);</span><br><span class="line"><span class="comment">//$tel = $phone->call();</span></span><br></pre></td></tr></table></figure><h2 id="服务容器"><a href="#服务容器" class="headerlink" title="服务容器"></a>服务容器</h2><p>服务容器的两个概念,依赖注入和控制反转:</p><blockquote><p>依赖注入和控制反转是对同一件事情的不同描述,它们描述的角度不同。依赖注入是从应用程序的角度在描述,应用程序依赖容器创建并注入它所需要的外部资源。而控制反转是从容器的角度在描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。</p></blockquote><h2 id="服务提供者"><a href="#服务提供者" class="headerlink" title="服务提供者"></a>服务提供者</h2><p>//todo</p><p>最近多线学习,更新不完整,抱歉!</p>]]></content>
<summary type="html">
<p>很多时候发现光看并不能很好的学习新东西,必须要动手操作才有更深刻的理解。这篇文章边敲代码边学习laravel的核心功能</p>
</summary>
<category term="技术" scheme="http://ajsonx.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="tcp ip 网络" scheme="http://ajsonx.github.io/tags/tcp-ip-%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>PHP7函数源码逐步阅读(一)debug_zval_dump</title>
<link href="http://ajsonx.github.io/2020/03/23/debug-zval-dump/"/>
<id>http://ajsonx.github.io/2020/03/23/debug-zval-dump/</id>
<published>2020-03-23T06:12:33.000Z</published>
<updated>2020-04-16T11:12:33.000Z</updated>
<content type="html"><![CDATA[<ol><li>debug_zval_dump() 无法输出int等类型的引用计数?</li><li>一起读源码</li><li>xdebug_debug_zval与它的区别</li></ol><a id="more"></a><hr><p><em>环境变量:</em></p><p><em>MacOS High Sierra</em></p><p><em>PHP-7.2.17-debug</em></p><p><em>GDB-8.0.1</em></p><h2 id="Q"><a href="#Q" class="headerlink" title="Q"></a>Q</h2><p><code>debug_zval_dump()</code>该函数可以打印变量的相关信息,记录变量被引用的次数并支持不定长参数。但是我在使用过程中发现对于不可变变量,例如FALSE、TRUE、INT、DOUBLE、NULL这些类型的变量。虽然被引用后增加了引用计数,但是该函数不会打印出来。运行结果如下:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"> $a = <span class="number">1</span>;</span><br><span class="line"> $b = &$a;</span><br><span class="line"> debug_zval_dump($a);</span><br><span class="line"> xdebug_debug_zval(<span class="string">"a"</span>);</span><br><span class="line"><span class="comment">//结果:</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> int(1)</span></span><br><span class="line"><span class="comment"> a: (refcount=2, is_ref=1)=1</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>以下是我使用GDB调试时的观察,执行完下面一段代码发生的变化有:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$b = &$a;</span><br></pre></td></tr></table></figure><ul><li><p>变量的地址不变,类型变为引用类型。</p></li><li><p>值存放在<code>zend_reference</code>中,引用计数为1。</p></li><li><p>接着赋值给<code>$b</code>后,引用计数加1(了解这些可以看上一篇有关zval结构的文章)。</p></li></ul><p>所以xdebug输出的信息符合预期。那在PHP7中,自带的这个debug函数,为什么不能输出我们预期的结果,它又是怎么实现该方法的呢?</p><h2 id="一起读源码"><a href="#一起读源码" class="headerlink" title="一起读源码"></a>一起读源码</h2><h3 id="主函数部分"><a href="#主函数部分" class="headerlink" title="主函数部分"></a>主函数部分</h3><p>下面这部分代码是主函数。第一块是变量的定义;</p><p>第二块宏是解析可变参数的,将传来的一个或多个参数放入args;</p><p>第三块for是调用函数<code>php_debug_zval_dump</code>对参数args进行循环打印。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* {{{ proto void debug_zval_dump(mixed var)</span></span><br><span class="line"><span class="comment"> Dumps a string representation of an internal zend value to output. */</span></span><br><span class="line">PHP_FUNCTION(debug_zval_dump)</span><br><span class="line">{</span><br><span class="line">zval *args;</span><br><span class="line"><span class="keyword">int</span> argc;</span><br><span class="line"><span class="keyword">int</span>i;</span><br><span class="line"></span><br><span class="line">ZEND_PARSE_PARAMETERS_START(<span class="number">1</span>, <span class="number">-1</span>)</span><br><span class="line">Z_PARAM_VARIADIC(<span class="string">'+'</span>, args, argc)</span><br><span class="line">ZEND_PARSE_PARAMETERS_END();</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i < argc; i++) {</span><br><span class="line">php_debug_zval_dump(&args[i], <span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="php-debug-zval-dump"><a href="#php-debug-zval-dump" class="headerlink" title="php_debug_zval_dump"></a>php_debug_zval_dump</h3><p>紧接着我们来阅读这个函数的代码,可以看到。在初始化完变量后,进入到一个对<code>level</code>的判断。不要迷惑,这是针对多级结构的<em>空格</em>输出,从而使输出格式规范。</p><p>然后进入again块,根据zval中的type去判断类型从而走不同的逻辑。</p><p>一眼就能看出从<code>case IS_FALSE</code> 到 <code>case IS_DOUBLE</code>都是直接输出,%s 对应 COMMON这个局部常量,判断<code>is_ref</code>是否为1。如果为1,则在前方输出一个<code>&</code>取地址符。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">PHPAPI <span class="keyword">void</span> <span class="title">php_debug_zval_dump</span><span class="params">(zval *struc, <span class="keyword">int</span> level)</span> <span class="comment">/* {{{ */</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">HashTable *myht = <span class="literal">NULL</span>;</span><br><span class="line">zend_string *class_name;</span><br><span class="line"><span class="keyword">int</span> is_ref = <span class="number">0</span>;</span><br><span class="line">zend_ulong index;</span><br><span class="line">zend_string *key;</span><br><span class="line">zval *val;</span><br><span class="line"><span class="keyword">uint32_t</span> count;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (level > <span class="number">1</span>) {</span><br><span class="line">php_printf(<span class="string">"%*c"</span>, level - <span class="number">1</span>, <span class="string">' '</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">again:</span><br><span class="line"><span class="keyword">switch</span> (Z_TYPE_P(struc)) {</span><br><span class="line"><span class="keyword">case</span> IS_FALSE:</span><br><span class="line">php_printf(<span class="string">"%sbool(false)\n"</span>, COMMON);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> IS_TRUE:</span><br><span class="line">php_printf(<span class="string">"%sbool(true)\n"</span>, COMMON);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> IS_NULL:</span><br><span class="line">php_printf(<span class="string">"%sNULL\n"</span>, COMMON);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> IS_LONG:</span><br><span class="line">php_printf(<span class="string">"%sint("</span> ZEND_LONG_FMT <span class="string">")\n"</span>, COMMON, Z_LVAL_P(struc));</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> IS_DOUBLE:</span><br><span class="line">php_printf(<span class="string">"%sfloat(%.*G)\n"</span>, COMMON, (<span class="keyword">int</span>) EG(precision), Z_DVAL_P(struc));</span><br><span class="line"><span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><h3 id="STRING"><a href="#STRING" class="headerlink" title="STRING"></a>STRING</h3><p><code>case IS_STRING</code>这块,第一行输出固定格式,意义同上。字符串值的输出在第二行:<code>PHPWRITE</code>,以流形式输出字符串,这里需要传递两个参数,一个是value的指针,一个是value的长度指针。第三行接着打印出它的引用计数。三元运算符判断如果zend_string中不存在,则默认为1。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> IS_STRING:</span><br><span class="line">php_printf(<span class="string">"%sstring(%zd) \""</span>, COMMON, Z_STRLEN_P(struc));</span><br><span class="line">PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));</span><br><span class="line">php_printf(<span class="string">"\" refcount(%u)\n"</span>, Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : <span class="number">1</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><h3 id="ARRAY"><a href="#ARRAY" class="headerlink" title="ARRAY"></a>ARRAY</h3><p><code>case IS_ARRAY</code>这块代码一开始就进行一个level判断,但主函数进行调用时,level的值为1,所以第一次不会进入此处。然后计算array的长度,按格式输出array的长度和引用计数,例如:<code>array(3) refcount(2)</code>。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> IS_ARRAY:</span><br><span class="line">myht = Z_ARRVAL_P(struc);</span><br><span class="line"><span class="keyword">if</span> (level > <span class="number">1</span> && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {</span><br><span class="line"><span class="keyword">if</span> (GC_IS_RECURSIVE(myht)) {</span><br><span class="line">PUTS(<span class="string">"*RECURSION*\n"</span>);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line">GC_PROTECT_RECURSION(myht);</span><br><span class="line">}</span><br><span class="line">count = zend_array_count(myht);</span><br><span class="line">php_printf(<span class="string">"%sarray(%d) refcount(%u){\n"</span>, COMMON, count, Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : <span class="number">1</span>);</span><br><span class="line">ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {</span><br><span class="line">zval_array_element_dump(val, index, key, level);</span><br><span class="line">} ZEND_HASH_FOREACH_END();</span><br><span class="line"><span class="keyword">if</span> (level > <span class="number">1</span> && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {</span><br><span class="line">GC_UNPROTECT_RECURSION(myht);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (level > <span class="number">1</span>) {</span><br><span class="line">php_printf(<span class="string">"%*c"</span>, level - <span class="number">1</span>, <span class="string">' '</span>);</span><br><span class="line">}</span><br><span class="line">PUTS(<span class="string">"}\n"</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><p>程序继续执行,到了宏定义<code>ZEND_HASH_FOREACH_KEY_VAL_IND</code>此处,循环hash数组,输出数组内的元素。但注意在宏内调用了<code>zval_array_element_dump</code>,此函数先输出键名,我们可以从它的注释看出来。分为数字型键名和string型键名,接着调用之前的方法,传递zval,根据类型不同输出不同格式。直到<code>ZEND_HASH_FOREACH_END()</code>结束。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">zval_array_element_dump</span><span class="params">(zval *zv, zend_ulong index, zend_string *key, <span class="keyword">int</span> level)</span> <span class="comment">/* {{{ */</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (key == <span class="literal">NULL</span>) { <span class="comment">/* numeric key */</span></span><br><span class="line">php_printf(<span class="string">"%*c["</span> ZEND_LONG_FMT <span class="string">"]=>\n"</span>, level + <span class="number">1</span>, <span class="string">' '</span>, index);</span><br><span class="line">} <span class="keyword">else</span> { <span class="comment">/* string key */</span></span><br><span class="line">php_printf(<span class="string">"%*c[\""</span>, level + <span class="number">1</span>, <span class="string">' '</span>);</span><br><span class="line">PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));</span><br><span class="line">php_printf(<span class="string">"\"]=>\n"</span>);</span><br><span class="line">}</span><br><span class="line">php_debug_zval_dump(zv, level + <span class="number">2</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>关于<code>case IS_ARRAY</code>的部分大致上结束了,其中部分关于<code>GC</code>的逻辑代码我没有探究,推测是垃圾回收的一些判断。</p><h3 id="OBJECT到RESOURCE"><a href="#OBJECT到RESOURCE" class="headerlink" title="OBJECT到RESOURCE"></a>OBJECT到RESOURCE</h3><p>接下来的<code>case IS_OBJECT</code>也是差不多的逻辑。<code>case IS_RESOURCE</code>也是短短几行,非常容易举一反三。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">case</span> IS_OBJECT:</span><br><span class="line">myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);</span><br><span class="line"><span class="keyword">if</span> (myht) {</span><br><span class="line"><span class="keyword">if</span> (GC_IS_RECURSIVE(myht)) {</span><br><span class="line">PUTS(<span class="string">"*RECURSION*\n"</span>);</span><br><span class="line">zend_release_properties(myht);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line">GC_PROTECT_RECURSION(myht);</span><br><span class="line">}</span><br><span class="line">class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));</span><br><span class="line">php_printf(<span class="string">"%sobject(%s)#%d (%d) refcount(%u){\n"</span>, COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : <span class="number">0</span>, Z_REFCOUNT_P(struc));</span><br><span class="line">zend_string_release_ex(class_name, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (myht) {</span><br><span class="line">ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {</span><br><span class="line">zend_property_info *prop_info = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (Z_TYPE_P(val) == IS_INDIRECT) {</span><br><span class="line">val = Z_INDIRECT_P(val);</span><br><span class="line"><span class="keyword">if</span> (key) {</span><br><span class="line">prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!Z_ISUNDEF_P(val) || prop_info) {</span><br><span class="line">zval_object_property_dump(prop_info, val, index, key, level);</span><br><span class="line">}</span><br><span class="line">} ZEND_HASH_FOREACH_END();</span><br><span class="line">GC_UNPROTECT_RECURSION(myht);</span><br><span class="line">zend_release_properties(myht);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (level > <span class="number">1</span>) {</span><br><span class="line">php_printf(<span class="string">"%*c"</span>, level - <span class="number">1</span>, <span class="string">' '</span>);</span><br><span class="line">}</span><br><span class="line">PUTS(<span class="string">"}\n"</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> IS_RESOURCE: {</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));</span><br><span class="line">php_printf(<span class="string">"%sresource(%d) of type (%s) refcount(%u)\n"</span>, COMMON, Z_RES_P(struc)->handle, type_name ? type_name : <span class="string">"Unknown"</span>, Z_REFCOUNT_P(struc));</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="REFERENCE(答案)"><a href="#REFERENCE(答案)" class="headerlink" title="REFERENCE(答案)"></a>REFERENCE(答案)</h3><p>我们来看<code>case IS_REFERENCE</code>,它直接隐藏了真实的引用计数,当一个类型变为引用类型,它的引用计数在这个函数中只会输出1。在注释中我们也可以看出,它这样做是为了兼容性。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> IS_REFERENCE:</span><br><span class="line"><span class="comment">//??? hide references with refcount==1 (for compatibility)</span></span><br><span class="line"><span class="keyword">if</span> (Z_REFCOUNT_P(struc) > <span class="number">1</span>) {</span><br><span class="line">is_ref = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line">struc = Z_REFVAL_P(struc);</span><br><span class="line"><span class="keyword">goto</span> again;</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line">php_printf(<span class="string">"%sUNKNOWN:0\n"</span>, COMMON);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>至此,我们看完了这个函数的源码。在这个函数中,对于没有引用计数的类型(<code>LONG,FALSE,TRUE,DOUBLE,NULL</code>)仅仅输出它的值。</p><p>对于有引用计数的类型(<code>STRING,ARRAY,OBJECT,RESOURCE</code>)根据逻辑判断,输出存放在zval结构中的refcount的值。</p><p>对于引用类型,则是用原有的数据重新判断输出。</p><h2 id="xdebug-debug-zval和debug-zval-dump的区别"><a href="#xdebug-debug-zval和debug-zval-dump的区别" class="headerlink" title="xdebug_debug_zval和debug_zval_dump的区别"></a>xdebug_debug_zval和debug_zval_dump的区别</h2><p>我们来看两段代码的运行结果:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"> $a = <span class="string">"hello"</span>;</span><br><span class="line"> debug_zval_dump($a);</span><br><span class="line"> xdebug_debug_zval(<span class="string">'a'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 运行结果</span></span><br><span class="line"><span class="comment"> string(5) "hello" refcount(1)</span></span><br><span class="line"><span class="comment"> a: (interned, is_ref=0)='hello'</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>一个字符串的默认引用计数为1。在上面这段代码的输出结果中,<code>debug_zval_dump</code>的结果是符合预期的。而xdebug输出的<code>interned</code>是什么意思?我们先保留疑问,再敲一段代码。</p><p>接下来这段代码输出的类型也是字符串:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"> $a = <span class="string">"timestamp is : "</span> . time();</span><br><span class="line"> debug_zval_dump($a);</span><br><span class="line"> xdebug_debug_zval(<span class="string">'a'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 运行结果</span></span><br><span class="line"><span class="comment">string(25) "timestamp is : 1585102731" refcount(2)</span></span><br><span class="line"><span class="comment">a: (refcount=1, is_ref=0)='timestamp is : 1585102731'</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>为什么<code>debug_zval_dump</code>输出2?而xdebug这次的输出结果却是符合预期的?!</p><h2 id="深入探究"><a href="#深入探究" class="headerlink" title="深入探究"></a>深入探究</h2><h3 id="Interned-String"><a href="#Interned-String" class="headerlink" title="Interned String"></a>Interned String</h3><p>在PHP5.4的时候, 引入了Interned String机制, 用于优化PHP对字符串的存储和处理。</p><p>Interned String 可以包括<strong>变量名称、类名、方法名、字符串、注释</strong>等。第一段代码中的<code>$a = 'hello'</code>,在多次编译下结果都不会产生变化。<strong>它们的生存周期存在于整个请求期间,请求结束后会统一销毁释放</strong>,自然也就无需通过引用计数进行内存管理。</p><h3 id="Immutable-array不可变数组"><a href="#Immutable-array不可变数组" class="headerlink" title="Immutable array不可变数组"></a>Immutable array不可变数组</h3><p>所有多次编译结果恒定不变的数组,都会被优化为<strong>不可变数组</strong>,是 <code>opcache</code> 扩展优化出的一种数组类型,多次编译结果不会产生变化,便会放入到OPcache里面进行优化。</p><h3 id="函数调用增加引用计数"><a href="#函数调用增加引用计数" class="headerlink" title="函数调用增加引用计数"></a>函数调用增加引用计数</h3><p>函数调用会增加引用计数,但在函数调用完成之后,函数内部变量销毁,引用计数减少到保持原有的值。而<em>debug_zval_dump</em> 这是一个php函数,它的输出是在函数内部的,所以输出的是它增加的引用计数。</p><h2 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h2><ul><li>debug_zval_dump是PHP函数,函数调用会增加引用,而的输出正是在函数内部的,所以输出的引用计数是增加过的。</li><li>使用xdebug_debug_zval是正确的,但需要理解<code>Interned String</code> 和 <code>Immutable array</code>的概念,这些内容可以在参考文章中详细了解。</li><li>学会GDB调试,逐步理解PHP源码执行过程</li></ul><p>猜测,推导,证明的过程。所以要不断动手尝试,不能浮于表面。</p><p>以上的内容还涉及到了写时复制、写时分离、引用计数等等,每一个概念在PHP7的实现都值得深入探究。</p><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="https://juejin.im/post/5bbf50e86fb9a05ce02a9c19" target="_blank" rel="noopener">PHP7各种数据类型的引用计数</a></p><p><a href="https://www.awaimai.com/2684.html#42_Interned_String" target="_blank" rel="noopener">OPcache工作原理</a></p>]]></content>
<summary type="html">
<ol>
<li>debug_zval_dump() 无法输出int等类型的引用计数?</li>
<li>一起读源码</li>
<li>xdebug_debug_zval与它的区别</li>
</ol>
</summary>
<category term="php" scheme="http://ajsonx.github.io/categories/php/"/>
<category term="zend php" scheme="http://ajsonx.github.io/tags/zend-php/"/>
</entry>
<entry>
<title>PHP7中zval的实现</title>
<link href="http://ajsonx.github.io/2020/03/21/php-zval/"/>
<id>http://ajsonx.github.io/2020/03/21/php-zval/</id>
<published>2020-03-21T09:26:24.000Z</published>
<updated>2020-04-15T02:04:38.000Z</updated>
<content type="html"><![CDATA[<p>“有点追求的PHPer都应该了解zval”</p><ol><li>ZVAL</li><li>OPcache原理</li></ol><p>本文完全是学习大牛博文的记录</p><a id="more"></a><hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><em>关于zval这块可以说涉及到非常多的知识,比如我们常见的引用计数、垃圾回收、写时复制。如果要深入的去理解这几个常见的问题,你需要学习了解PHP中变量是如何设计的。但我总喜欢问的干净点,为什么在PHP7中对int、double、bool、null不使用引用计数,string只是有些计数,有些不计数。如果不使用的话又如何进行垃圾回收?又或者说,对于单进程,生命周期结束就释放资源的PHP有必要进行垃圾回收吗?又是如何靠垃圾回收进行优化的?</em></p><p><em>本来一开始已经写好了有关PHP7中zval的结构,但是我们毕竟是站在巨人肩膀上的菜鸟,已经写不出比大佬更优秀的文章了。这里就记录一下阅读过程的总结和思考</em></p><h2 id="第一篇·鸟哥"><a href="#第一篇·鸟哥" class="headerlink" title="第一篇·鸟哥"></a>第一篇·鸟哥</h2><blockquote><p>一个良好的设计, 一旦有了意外, 就会导致整个结构变得复杂, 维护性降低。——<a href="https://www.laruence.com/author/laruence" target="_blank" rel="noopener">laruence</a></p></blockquote><p><a href="https://www.laruence.com/2018/04/08/3170.html" target="_blank" rel="noopener">深入理解PHP7内核之zval——作者laruence</a></p><h3 id="上半段"><a href="#上半段" class="headerlink" title="上半段"></a>上半段</h3><p>鸟哥的这篇文章分为两段,上半段主要介绍了六个方面的PHP5设计局限性:</p><ol><li><p>zval.value 在64bit机器下需要24个字节,而在32bit下需要16字节。<code>zend_object_value</code>最不常用,但却是最大的长板,因为它占到16字节</p></li><li><p>无预留字段,不方便实现复杂的功能</p></li><li><p>对象和资源(引用)传递时,需要一个全局引用计数才能保证它被回收。所以,在PHP5中它有两套引用计数</p></li><li><p>用到相同字符串时,只能复制整个zval</p></li><li><p>发生写时分离,导致数组复制,拖慢性能</p></li><li><p>PHP5源码的tmp临时变量优化</p></li></ol><h3 id="下半段"><a href="#下半段" class="headerlink" title="下半段"></a>下半段</h3><p>文章的下半段详细介绍了PHP7zval的实现,以及解决的性能问题,我在这里顺序梳理一遍。</p><ul><li>现在PHP7的<code>zval</code>分为了三个部分,分别是负责存储类型信息(<code>Type_Info</code>)的<code>u1</code>,一些预留字段的<code>u2</code>,以及存储变量的值的<code>zend_value</code>。以上三个结构都是联合体,其中再有复杂的结构就用指针指向,所占字节不超过16字节。(<code>zend_value</code> 8字节,<code>u1</code>,<code>u2</code>对齐占8字节,共16字节。ps:64bit机器C++ && 32bit同样)</li><li>zval的类型现在分为17种,值得注意的是<code>TRUE</code>和<code>FALSE</code>是两种类型了</li><li><code>LONG,DOUBLE,NULL,FALSE,TRUE</code>这些类型无需引用计数了</li><li>对于复杂类型,仅需在zval中用一个指针指向该类型,引用计数也保存在该类型上。类似<code>zend_string,zend_array,zend_refcounted</code>这些结构体。</li><li><code>ZEND_ENDIAN_LOHI_4</code>这个宏的作用是简化赋值(用来解决跨平台大小端问题的)</li><li>关于常量类型和不可变类型等标志位的介绍</li><li>最后一部分留了个坑,大概意思就是随着zval的内存分配方式改变,zval传递的方式也将改变。</li></ul><h2 id="第二篇·PHP开发组成员"><a href="#第二篇·PHP开发组成员" class="headerlink" title="第二篇·PHP开发组成员"></a>第二篇·PHP开发组成员</h2><p><a href="https://0x1.im/blog/php/Internal-value-representation-in-PHP-7-part-1.html" target="_blank" rel="noopener">[译]变量在 PHP7 内部的实现(一)</a></p><p>这篇文章翻译自国外PHP开发大佬的博客,内容上有重复,相对更加详细具体,挑着看就好了。</p><p>在浏览第二篇文章之前,可以试着先思考下面的这些问题,在文章中都可以找到答案。</p><ul><li>C++中<code>struct</code>字节对齐是什么,<code>union,struct</code>所占字节大小计算方式,PHP7是如何利用字节对齐减少了<code>zval</code>所占大小?</li><li>循环引用如何产生的,PHP5是如何解决的?</li><li>什么是写时复制</li><li>如果你了解PHP的引用计数,它是为了方便进行内存管理,实现垃圾回收。那么思考一下,既然Java有常量池的概念,并且PHP5.4后也引入了<strong>Interned String</strong>的概念,那么PHP有没有?它是如何实现的?</li></ul><p>最后一个问题又引入了另一个概念:<strong>OPcache</strong>(越学不懂的越多,太真实了!)</p><h2 id="第三篇·OPcache"><a href="#第三篇·OPcache" class="headerlink" title="第三篇·OPcache"></a>第三篇·OPcache</h2><p><a href="https://www.awaimai.com/2684.html#42_Interned_String" target="_blank" rel="noopener">OPcache工作原理</a></p><p>也是一篇好文章,浅显易懂。</p><ul><li>OPcache 是Zend官方出品的缓存拓展,还具有代码优化功能,省去了每次加载和解析 PHP 脚本的开销。</li><li>缓存两类内容:<ul><li>OPCode(这里关于OPCode可以使用拓展<code>VLM</code>去了解)</li><li>Interned String,如注释、变量名等</li></ul></li><li>OPCache缓存的机制主要是:<strong>将编译好的操作码放入共享内存,提供给其他进程访问</strong>。<ul><li>文章还详细讲了共享内存,有兴趣可以深入了解</li></ul></li><li>Interned String 缓存的内容包括: <strong>变量名称、类名、方法名、字符串、注释</strong>等。</li><li>文章的第5,6点接触不多,暂时感觉不到,先搁置。</li></ul><hr><p>多用GDB感受一下zval的结构!</p><p>好好学就是了!</p>]]></content>
<summary type="html">
<p>“有点追求的PHPer都应该了解zval”</p>
<ol>
<li>ZVAL</li>
<li>OPcache原理</li>
</ol>
<p>本文完全是学习大牛博文的记录</p>
</summary>
<category term="php" scheme="http://ajsonx.github.io/categories/php/"/>
<category term="php zval" scheme="http://ajsonx.github.io/tags/php-zval/"/>
</entry>
<entry>
<title>一年经验的PHP程序员面试准备以及面筋</title>
<link href="http://ajsonx.github.io/2020/03/12/first-year-interview/"/>
<id>http://ajsonx.github.io/2020/03/12/first-year-interview/</id>
<published>2020-03-12T07:13:58.000Z</published>
<updated>2020-03-12T07:13:58.000Z</updated>
<content type="html"><![CDATA[<p>祝大家拿到好offer</p><ol><li>近期面试心得</li><li>一年的PHPer面试复习手册</li><li>深圳PHP岗位面经</li></ol><a id="more"></a><hr><ul><li>有些问题抛出来了,没有回答。</li><li>还有一部分基础知识需要靠积累,没有整理出来。</li><li>面试还会继续,本篇文章今年内不定时更新。</li></ul><h1 id="自我介绍-别紧张-讲慢点-开录音-警惕面试官挖坑-大胆讲"><a href="#自我介绍-别紧张-讲慢点-开录音-警惕面试官挖坑-大胆讲" class="headerlink" title="自我介绍 别紧张 讲慢点 开录音 警惕面试官挖坑 大胆讲"></a>自我介绍 别紧张 讲慢点 开录音 警惕面试官挖坑 大胆讲</h1><h1 id="自由发挥的问题"><a href="#自由发挥的问题" class="headerlink" title="自由发挥的问题"></a>自由发挥的问题</h1><h2 id="项目中遇到的困难"><a href="#项目中遇到的困难" class="headerlink" title="项目中遇到的困难"></a>项目中遇到的困难</h2><h2 id="职业规划"><a href="#职业规划" class="headerlink" title="职业规划"></a>职业规划</h2><h2 id="接受一个新事物能力如何"><a href="#接受一个新事物能力如何" class="headerlink" title="接受一个新事物能力如何"></a>接受一个新事物能力如何</h2><hr><h1 id="系统设计题"><a href="#系统设计题" class="headerlink" title="系统设计题"></a>系统设计题</h1><h2 id="评论回复点赞如何设计"><a href="#评论回复点赞如何设计" class="headerlink" title="评论回复点赞如何设计"></a>评论回复点赞如何设计</h2><ul><li><p>设计表,针对业务场景进行分析:</p><ol><li>需要查询出帖子下的所有评论</li><li>需要查询出评论下的所有回复</li><li>需要查询出回复的回复</li><li>评论是否点赞标识,以及点赞数</li></ol><p>考虑到以上,思考设计出表结构即可。</p></li><li><p>大数据量场景</p><ol><li>点赞取消频繁(Redis缓存)</li><li>频繁查询评论(ES)</li><li>数据量过大(分表策略)</li></ol></li></ul><h2 id="高并发系统考点"><a href="#高并发系统考点" class="headerlink" title="高并发系统考点"></a>高并发系统考点</h2><p>如何应对突增的流量?(可预测流量与不可预测流量)</p><h3 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h3><ul><li><a href="https://www.cnblogs.com/jasonZh/p/9513948.html" target="_blank" rel="noopener">Redis 5种基本数据结构及常见使用场景</a><ul><li>String<ol><li>K-V 缓存</li></ol></li><li>Hash<ol><li>存储对象,部分数据变更</li></ol></li><li>List-双向链表<ol><li>消息队列</li><li><a href="https://mp.weixin.qq.com/s/uzuz7rqctQ-bjdRcf1tO9g" target="_blank" rel="noopener">利用zipList来代替大量的K-V</a></li><li>粉丝列表(非严格实时场景)</li></ol></li><li>Set-HashTable<ol><li>无序不重复集合</li><li>共同关注</li></ol></li><li>Zset-HashMap&SkipList<ol><li>有序不重复集合,并且带一个权重参数</li><li>排行榜</li></ol></li></ul></li><li>Redis加锁,原子操作 EX | NX</li></ul><h4 id="缓存故障"><a href="#缓存故障" class="headerlink" title="缓存故障"></a>缓存故障</h4><ul><li><p>缓存穿透:大量查询持久层中为空的值。</p><ul><li>把为空的值也放入缓存</li></ul></li><li><p>缓存雪崩:某一个时间段,缓存集中过期失效</p><ul><li>设置随机过期时间</li></ul></li><li><p>缓存击穿:热点Key失效的瞬间,直接并发访问持久层</p><ul><li>Key永不过期</li></ul></li></ul><h3 id="降级"><a href="#降级" class="headerlink" title="降级"></a>降级</h3><ul><li>功能暂时下线</li></ul><h3 id="限流"><a href="#限流" class="headerlink" title="限流"></a>限流</h3><p>三种常见的限流算法:</p><ul><li>计数器算法:单个接口1分钟访问次数不能超过100。此算法可能在59秒到1分01秒内被访问200次</li><li>滑动窗口算法:将时间分为多个格子,每个格子有独立的计数器</li><li>令牌桶算法:所有请求在处理之前都需要拿到一个可用的令牌,根据限流大小,按一定速率往桶里添加令牌,桶设置最大的放置令牌限制,业务逻辑处理完后将令牌删除</li><li>漏桶算法:水以任意速率流入,一定速率出水,超过桶容量就丢弃。</li></ul><h2 id="既然有-HTTP-请求,为什么还要用-RPC-调用?"><a href="#既然有-HTTP-请求,为什么还要用-RPC-调用?" class="headerlink" title="既然有 HTTP 请求,为什么还要用 RPC 调用?"></a>既然有 HTTP 请求,为什么还要用 RPC 调用?</h2><ul><li>通用定义的http1.1协议的tcp报文包含太多废信息</li><li>牺牲可读性提升效率、易用性是可取的</li></ul><h1 id="算法"><a href="#算法" class="headerlink" title="算法"></a>算法</h1><h2 id="O-logN-究竟是多少?"><a href="#O-logN-究竟是多少?" class="headerlink" title="O(logN)究竟是多少?"></a>O(logN)究竟是多少?</h2><p>为什么没有底数?</p><p>由于log级别的时间复杂度都是使用了分治思想,这个底数直接由分治的复杂度决定,如果是二分法,底数就是2。无论底数是什么,log级别的渐进意义是一样的。高数中极限的思想。</p><h2 id="递归和回溯的区别"><a href="#递归和回溯的区别" class="headerlink" title="递归和回溯的区别"></a>递归和回溯的区别</h2><p>递归可以理解为是函数自己调用自己,有一个终点。而回溯就是我们经常说DFS(深搜)的一种,特点是有回头路。</p><h2 id="单调栈"><a href="#单调栈" class="headerlink" title="单调栈"></a>单调栈</h2><ul><li><p>问题描述:蓄水问题,高度为 i 的方块能蓄多少水。例:[1,3,2,4,3,5]</p></li><li><p>将水池抽象为数组,维护一个单调递减的栈。对当前元素进行遍历,如果当前元素大于栈顶元素,则进行弹出(pop)操作,利用此时的栈顶元素进行蓄水容量计算(为什么可以?因为我们维护的是单调递减的栈,所以栈顶的元素减去弹出的元素一定大于等于0)。栈空或栈顶元素大于当前元素,进行一次入栈。</p></li><li><p>这个问题不用单调栈去解决更容易理解,这里还有两种值得学习的思想</p><ul><li><p>常规的一种思路是O(N^2),遍历找出当前位置左边最高的(O(N))和右边最高的(O(N))。</p><p>这种方法很好优化,我们可以利用备忘录记录一下。即用两个数组<code>Left</code>和 <code>Right</code>数组预先处理一下,记录 在下标为 i 时,左边最高的和右边最高的方块高度。</p><p>此时时间复杂度为O(N),空间复杂度为O(N)</p></li><li><p>还有一种做法比较不容易想到。对于这个数组,它的最大值假设为<code>arr[i]</code>,它的左边(0…i-1)是非严格单调递增的,它的右边是非严格单调递减的。我们可以找出这个最大值,分别遍历0…i-1 和 n…i+1。实时更新相对另一侧的最大值(中间的最大值我们已经固定了,我们只要固定旁边两侧的最大值。)并计算出积水量就好了。还有一种双指针做法与之类似。</p></li></ul></li><li><p>来LeetCode试试吧 <a href="https://leetcode-cn.com/problems/trapping-rain-water/" target="_blank" rel="noopener">Leetcode #接雨水</a></p></li></ul><h2 id="Hash-链地址法"><a href="#Hash-链地址法" class="headerlink" title="Hash 链地址法"></a>Hash 链地址法</h2><ul><li>问题描述:当两个不同的关键字求得的索引相同时,如何存储。是处理hash冲突的其中一种方法。</li><li>将冲突的值,以链表的形式连起来。</li><li>应用:PHP 数组底层实现</li></ul><h2 id="二叉查找树"><a href="#二叉查找树" class="headerlink" title="二叉查找树"></a>二叉查找树</h2><ul><li>左子树不为空,左子树的值均小于根节点的值</li><li>右子树不为空,右子树的值均大于根节点的值</li><li>任意节点的左右子树均是二叉查找树</li><li>没有值相等的节点</li></ul><p>中序遍历输出从小到大的顺序排序</p><p>缺点:退化为有n个节点的线性链</p><h2 id="平衡二叉查找树"><a href="#平衡二叉查找树" class="headerlink" title="平衡二叉查找树"></a>平衡二叉查找树</h2><ul><li>平衡条件:左右子树高度差不超过1</li><li>解决了二叉查找树的退化问题</li></ul><h2 id="B树"><a href="#B树" class="headerlink" title="B树"></a>B树</h2><ul><li>非叶子结点最多只有M个儿子,M>2</li><li>根节点的儿子数 [2,M]</li><li>除根节点外的非叶子节点的儿子数 [M/2,M]</li><li>每个节点存放至少 [M/2-1]</li><li>对于每个非叶子结点上的指针。P[1] 指向小于K[1]的子树,P[M]指向大于K[M]的子树。在两区间的P[i]指向属于 (K[i-1],K[i])的子树</li><li>所有叶子节点位于同一层</li><li>关键字(值)=指针数-1</li></ul><p>图片转自CSDN<br><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200312/b_tree.png" alt="B树-图片转自CSDN"></p><h2 id="B-树"><a href="#B-树" class="headerlink" title="B+树"></a>B+树</h2><ul><li>非叶子节点的子树指针和关键字个数相同</li><li>为所有叶子节点增加了一个链指针</li><li>所有关键字都在叶子节点出现,用链表存,且是有序的</li><li>非叶子节点只是叶子节点的索引,叶子节点是存储数据(关键字)的数据层</li><li>更适合文件系统</li><li>特点:查询效率更稳定,读写磁盘代价更低</li></ul><p>图片转自CSDN<br><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200312/b%2B_tree.png" alt="B+树-图片转自CSDN"></p><p>B树在提高了IO性能的同时并没有解决元素遍历效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。</p><h2 id="最短路"><a href="#最短路" class="headerlink" title="最短路"></a>最短路</h2><h3 id="Dijkstra"><a href="#Dijkstra" class="headerlink" title="Dijkstra"></a>Dijkstra</h3><ul><li>贪心,不能处理负边</li><li>维护一个<code>d[i]</code> ,表示起点到<code>i</code>的最短距离。初始化一个二维数组,表示有向图的路径长度,如果不存在通路则为无穷大。从起点开始选择一个最近的点<code>i</code>,然后将<code>i</code>作为中间点,更新到达的点<code>d[n]</code>值,不断贪心寻找。</li></ul><h3 id="Floyed"><a href="#Floyed" class="headerlink" title="Floyed"></a>Floyed</h3><ul><li>多源最短路、能处理负边,但是不能出现负环。DP思想,时间复杂度:O(n3)</li><li>枚举中间点,枚举起点,枚举终点。dp表记录最短路。</li><li>状态转移方程:<code>d[i][j][k] = min( d[i][k][k-1] + d[k][j][k-1] ,d[i][j][k-1] )</code> </li></ul><h2 id="最小生成树"><a href="#最小生成树" class="headerlink" title="最小生成树"></a>最小生成树</h2><h3 id="Kruskal"><a href="#Kruskal" class="headerlink" title="Kruskal"></a>Kruskal</h3><ul><li>贪心,不断加边</li></ul><h3 id="Prim"><a href="#Prim" class="headerlink" title="Prim"></a>Prim</h3><ul><li>贪心,不断加点</li></ul><h2 id="DP"><a href="#DP" class="headerlink" title="DP"></a>DP</h2><p>Todo,不常考,了解DP思想即可(自底向上的解决问题)</p><h1 id="MySQL"><a href="#MySQL" class="headerlink" title="MySQL"></a>MySQL</h1><h2 id="Mysql优化"><a href="#Mysql优化" class="headerlink" title="Mysql优化"></a>Mysql优化</h2><p><a href="https://www.zhihu.com/question/19719997/answer/549041957" target="_blank" rel="noopener">知乎回答,推荐前2</a></p><ol><li>选择合适的数据库引擎</li><li>配置优化</li><li>Sql优化</li><li>索引优化</li><li>读写分离</li><li>表结构优化</li><li>硬件升级</li></ol><h2 id="数据库隔离级别"><a href="#数据库隔离级别" class="headerlink" title="数据库隔离级别"></a>数据库隔离级别</h2><ol><li>读未提交 RU</li><li>读提交 RC</li><li>可重复读 RR (MySQL默认级别)</li><li>序列化 Serializable</li></ol><p>分别解决了 脏读,不可重复读,幻读。事务级别越高,性能越差。</p><h2 id="什么是事务"><a href="#什么是事务" class="headerlink" title="什么是事务"></a>什么是事务</h2><p>事务是一个工作单元、包含了一组数据操作命令,要么都执行,要么都不执行。(结合实际工作去理解用事务的意义)</p><h2 id="ACID"><a href="#ACID" class="headerlink" title="ACID"></a>ACID</h2><ul><li>Atomicity原子性:一个事务中所有操作都必须全部完成,要么全部不完成。</li><li>Consistency一致性:在事务开始或结束时,数据库应该在一致状态。</li><li>Isolation隔离性:事务将假定只有它自己在操作数据库,彼此不知晓。</li><li>Durability持久性:一旦事务完成,就不能返回。</li></ul><h2 id="MVCC是一种多版本并发控制机制"><a href="#MVCC是一种多版本并发控制机制" class="headerlink" title="MVCC是一种多版本并发控制机制"></a>MVCC是一种多版本并发控制机制</h2><h2 id="InnoDB和MyISAM区别"><a href="#InnoDB和MyISAM区别" class="headerlink" title="InnoDB和MyISAM区别"></a>InnoDB和MyISAM区别</h2><ul><li>InnoDB<ul><li>支持事务</li><li>非主键索引的BTree节点存的是主键,需要根据主键二次查找相应的数据块。</li><li>一般是行锁,加在索引上。</li><li>读写之间可以并发,普通级别的select不需要锁(还有更新级别for update)。当查询的记录遇到锁时,用的是一致性的非锁定快照,读被锁定行的快照。其它更新或加锁读用的是当前读,读取原始行</li></ul></li><li>MyISAM<ul><li>不是事务安全的</li><li>数据是顺序存储的,存储方式是非聚簇索引</li><li>所有索引的BTree节点是一个指向数据物理位置的指针,所以不建立主键都行</li><li>表锁,只有读读之间是并发的,写写是串行的</li><li>读和写可以并发,需要设置<code>concurrent_insert</code> 参数</li><li>写优先级高于读,会导致读请求饿死</li></ul></li></ul><h2 id="一条SQL执行的很慢的原因"><a href="#一条SQL执行的很慢的原因" class="headerlink" title="一条SQL执行的很慢的原因"></a>一条SQL执行的很慢的原因</h2><ul><li>分两种情况:1.突然很慢 2.本来就慢</li><li>redo-log 满了去同步到磁盘而繁忙</li><li>拿不到锁。<code>show processlist</code> 查看当前状态</li><li>索引失效</li></ul><h2 id="索引失效的原因"><a href="#索引失效的原因" class="headerlink" title="索引失效的原因"></a>索引失效的原因</h2><ul><li>where子句中使用 <code>!=</code>或者<code><></code>操作符</li><li>where子句中使用<code>or</code>来连接条件</li><li>避免where子句中对字段进行表达式操作 where num/2 =100</li><li>where子句中对字段进行mysql函数操作</li><li>最左前缀原则</li><li>列类型是字符串,一定要在条件中使用引号</li><li>like的模糊查询以%开头</li></ul><p>###关于联合索引的特别提醒</p><ul><li>对于联合索引(a,b,c)来说,有效的索引查询分别是(a),(a,b) ,(a,b,c) 联合索引在数据结构下相当于 Order By a b c</li><li>对于查询语句来说,不管你where语句怎么写的,b写在前面,a在后面也没关系。MySQL会优化你的查询语句,依然会命中索引</li><li>当表中的字段仅仅只有联合索引,没有其他的字段时,不管你怎么写都会命中索引。因为索引覆盖了。</li></ul><h2 id="IN-和-EXISTS的区别"><a href="#IN-和-EXISTS的区别" class="headerlink" title="IN 和 EXISTS的区别"></a>IN 和 EXISTS的区别</h2><ul><li>IN:把外表和内表作hash连接(了解什么是hash join)</li><li>EXISTS:对外表作loop循环,每次loop循环再对内表进行查询</li><li>如果两表中一个较小,一个较大。则子查询表大的用EXISTS,子查询小的用IN。</li><li>NOT IN 和 NOT EXISTS</li><li>NOT IN:内外表都进行全表扫描</li><li>NOT EXISTS:子查询依然能用到表上的索引</li></ul><h2 id="分表的策略"><a href="#分表的策略" class="headerlink" title="分表的策略"></a>分表的策略</h2><ul><li>垂直和水平</li><li>取模</li><li>hash(crc32)</li><li>一致性hash (就是一个环。如果hash环偏斜,所有请求打到同一台机器上,可以增加虚拟节点。)</li></ul><h2 id="分布式事务如何保证一致性"><a href="#分布式事务如何保证一致性" class="headerlink" title="分布式事务如何保证一致性"></a>分布式事务如何保证一致性</h2><ul><li>2PC</li><li>3PC</li><li>TCC</li><li>MQ,发布订阅</li></ul><h1 id="NOSQL"><a href="#NOSQL" class="headerlink" title="NOSQL"></a>NOSQL</h1><h2 id="MongoDB和Redis的区别"><a href="#MongoDB和Redis的区别" class="headerlink" title="MongoDB和Redis的区别"></a>MongoDB和Redis的区别</h2><h2 id="Redis的connect和pconnect"><a href="#Redis的connect和pconnect" class="headerlink" title="Redis的connect和pconnect"></a>Redis的connect和pconnect</h2><ul><li>脚本结束之后连接释放</li><li>脚本结束之后连接不释放,连接保持在php-fpm中</li></ul><h1 id="架构"><a href="#架构" class="headerlink" title="架构"></a>架构</h1><ul><li>网关层</li><li>业务逻辑层</li><li>数据访问层</li><li>DB</li></ul><p>了解各层的实现方式、实现工具,各层之间的调用方式。了解垂直拆分、水平拆分,了解各种中间件。内容过多,不在此赘述。/不常考</p><h2 id="什么是注册中心,常见的注册中心组件"><a href="#什么是注册中心,常见的注册中心组件" class="headerlink" title="什么是注册中心,常见的注册中心组件"></a>什么是注册中心,常见的注册中心组件</h2><ul><li><p><a href="https://www.jianshu.com/p/5014bb302c7d" target="_blank" rel="noopener">服务发现、服务注册</a></p></li><li><p>zookeeper(CP)追求数据的强一致性。</p></li><li><p>consul(AP)追求高可用与最终一致。</p></li></ul><h2 id="CAP"><a href="#CAP" class="headerlink" title="CAP"></a>CAP</h2><p> 一致性、可用性、分区容错性</p><h2 id="服务冗余"><a href="#服务冗余" class="headerlink" title="服务冗余"></a>服务冗余</h2><p>多机配置</p><h2 id="无状态化"><a href="#无状态化" class="headerlink" title="无状态化"></a>无状态化</h2><p>应用服务可以不存储状态有关的值在自己内部。</p><p>会话数据放在外部缓存、结构化数据保存在统一的数据库、文件图片通过CDN下发、非结构化数据(文本、评论),可以存在在统一的搜索引擎里面。保存在统一的服务,便于横向拓展</p><h1 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h1><h2 id="设置防盗链"><a href="#设置防盗链" class="headerlink" title="设置防盗链"></a>设置防盗链</h2><ul><li>valid_referers</li></ul><h2 id="正向代理和反向代理"><a href="#正向代理和反向代理" class="headerlink" title="正向代理和反向代理"></a>正向代理和反向代理</h2><ul><li>VPN就是正向,使用者可以感知到</li><li>Nginx等服务器就是反向,感受不到</li></ul><h2 id="Nginx与PHP通信两种方式"><a href="#Nginx与PHP通信两种方式" class="headerlink" title="Nginx与PHP通信两种方式"></a>Nginx与PHP通信两种方式</h2><ul><li>unix socket</li><li>tcp socket</li></ul><h1 id="HTTP-计算机网络"><a href="#HTTP-计算机网络" class="headerlink" title="HTTP/计算机网络"></a>HTTP/计算机网络</h1><h2 id="HTTP状态码"><a href="#HTTP状态码" class="headerlink" title="HTTP状态码"></a>HTTP状态码</h2><blockquote><p>想想还是全写出来了,以后也方便查找。不过这东西问的频率也不高。</p></blockquote><ul><li>100:客户端应重新发送初始请求</li><li>101:改用另一个协议</li><li>200:成功</li><li>201:创建新资源成功</li><li>202:请求稍后会被处理</li><li>203:响应报头可能来自其他服务器</li><li>204:服务器成功处理、但没有返回任何内容</li><li>205:处理成功、客户端应该重置数据源</li><li>206:partial content 处理了部分内容 用于大文件的断点续传</li><li>300:若被请求的资源在服务器端存在多个表示,而服务器不知道客户端想要的是哪一个</li><li>301:请求的页面已经永久跳转到新的URL</li><li>302:临时移动</li><li>303:查看其他位置</li><li>304:未修改、 305 只能使用代理访问、307 临时重定向</li><li>400:错误请求,服务器不理解</li><li>401:未授权、403 服务器拒绝请求</li><li>404:未找到、 405 禁用请求中的方法 406 不接受、407 需要代理授权 、408 请求超时…</li><li>500:服务器内部错误、502 错误网关、 503 服务不可用、504 网关超时、 505 不支持版本</li></ul><h2 id="TCP与UDP"><a href="#TCP与UDP" class="headerlink" title="TCP与UDP"></a>TCP与UDP</h2><p>TCP通信就像打电话,双方通信之前需要建立连接、双方就位后方可开始会话;而UDP通信就像发短信,一方给另一方发送数据前,并不需要对方就位。</p><ul><li>TCP:可靠性、顺序性、高损耗</li><li>UDP:不可靠、无序、低耗</li></ul><h2 id="TCP沾包解决方案"><a href="#TCP沾包解决方案" class="headerlink" title="TCP沾包解决方案"></a>TCP沾包解决方案</h2><ul><li>报头添加消息类型和数据长度信息</li><li>报尾添加结束分割符</li></ul><h2 id="用户输入-url-到页面显示经历了哪些"><a href="#用户输入-url-到页面显示经历了哪些" class="headerlink" title="用户输入 url 到页面显示经历了哪些"></a>用户输入 url 到页面显示经历了哪些</h2><ul><li>通过DNS找到对应的IP<ul><li>浏览器缓存</li><li>本地的Host找对应的IP</li><li>路由器缓存查找记录</li><li>DNS域名解析</li></ul></li><li>通过IP向对应的web服务器发送请求<ul><li>如果是静态文件,Nginx服务器转发</li><li>如果是php文件,则交由php-fpm处理</li></ul></li></ul><h2 id="OSI-7层模型"><a href="#OSI-7层模型" class="headerlink" title="OSI 7层模型"></a>OSI 7层模型</h2><blockquote><p>已经没人问了</p></blockquote><ul><li>物理层:</li><li>数据链路层:</li><li>网络层:IP、ICMP、ARP</li><li>传输层:TCP、UDP</li><li>会话层:SMTP、DNS</li><li>表示层:Telnet</li><li>应用层:HTTP、TFTP、FTP、NFS</li></ul><h1 id="PHP"><a href="#PHP" class="headerlink" title="PHP"></a>PHP</h1><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><ul><li>array_slice</li><li>array_splice</li><li>compact</li><li>chunk_split</li><li>addcslashes</li><li>chr</li></ul><h2 id="PHP执行"><a href="#PHP执行" class="headerlink" title="PHP执行"></a>PHP执行</h2><ul><li>编译为opcode</li><li>Zend引擎处理转换为C指令</li><li>C转换为汇编语言</li><li>最后变成机器码执行</li></ul><p>针对这块问题的深入性探讨,<a href="http://gywbd.github.io/posts/2016/2/zend-execution-engine.html" target="_blank" rel="noopener">整理自Gong Yong的Blog-值得读</a>:</p><ul><li>什么是解释性语言?<ul><li>所谓“解释型语言”就是指用这种语言写的程序不会被直接编译为本地机器语言(native machine language),而是会被编译为一种中间形式(代码),很显然这种中间形式不可能直接在CPU上执行(因为CPU只能执行本地机器指令),但是这种中间形式可以在使用本地机器指令(如今大多是使用C语言)编写的软件上执行。这个软件就是所谓的软件虚拟机(software virtual machine)。</li><li>我们也可以这样去理解PHP是动态语言的其中的特点:运行时才确定数据类型的语言。<a href="https://www.zhihu.com/question/268303059" target="_blank" rel="noopener">但按解释型与编译型去区分语言是不准确的。</a></li></ul></li><li>软件虚拟机又是什么?<ul><li><strong>程序虚拟机</strong> 被设计用来在与平台无关的环境中执行计算机程序。</li><li>所以我们可以跨平台(Linux,MacOS,Windows)的使用而不用担心环境问题(类似不同平台字节长度不同的问题)</li></ul></li><li>为什么会编译为OPcode?<ul><li>有了上述两点的基础知识,我们可以简单知道PHP的Zend虚拟机引擎就是一种程序虚拟机,将我们编写的PHP代码进行翻译。这里可以分为两大部分:1.编译栈 2.执行栈。我们假设Zend VM的一个OPCode对应虚拟机的一个底层操作。Zend虚拟机有很多OPCode:它们可以做很多事情。随着PHP的发展,也引入了越来越多的OPCode,这都是源于PHP可以做越来越多的事情。你可以在PHP的源代码文件Zend/zend_vm_opcodes.h中看到所有的OPCode。</li></ul></li></ul><p>综上,我们完整地理解了PHP到Zend引擎这块的详细内容。再接下去就是《计算机操作系统》的事情啦。</p><h2 id="CGI、Fast-CGI、PHP-FPM"><a href="#CGI、Fast-CGI、PHP-FPM" class="headerlink" title="CGI、Fast-CGI、PHP-FPM"></a>CGI、Fast-CGI、PHP-FPM</h2><ul><li>CGI 公共网关接口,是Nginx与PHP的协议,Fast-CGI是进程管理器</li><li>传统的cgi协议在连接请求时,会开启一个进程进行处理,而FAST-CGI处理完请求后,不会kill掉这个进程,而是保留复用。</li><li>php-fpm是管理php-cgi的进程管理器。在更改配置后,新的进程加载新的配置,实现平滑过度。</li></ul><h2 id="进制转换函数"><a href="#进制转换函数" class="headerlink" title="进制转换函数"></a>进制转换函数</h2><p>二进制 bin、八进制oct、十进制dec、十六进制hex</p><h2 id="Array数组的底层实现"><a href="#Array数组的底层实现" class="headerlink" title="Array数组的底层实现"></a>Array数组的底层实现</h2><ul><li>Hash Table</li><li>链地址法</li></ul><h2 id="自动加载如何实现"><a href="#自动加载如何实现" class="headerlink" title="自动加载如何实现"></a>自动加载如何实现</h2><ul><li><code>spl_autoload_register</code>:我们new一个不存在的类的时候会触发,进而可以解决类引入(include)</li></ul><h2 id="框架路由如何实现"><a href="#框架路由如何实现" class="headerlink" title="框架路由如何实现"></a>框架路由如何实现</h2><ul><li><code>$_SERVER["PHP_SELF"]=>"/index.php"</code>定位入口文件 </li><li>通过对 <code>$_SERVER["REQUEST_URI"]=>"/article/get?id=1"</code> 获取请求参数,然后<code>?,&</code>切割参数</li></ul><h2 id="魔术方法"><a href="#魔术方法" class="headerlink" title="魔术方法"></a>魔术方法</h2><ul><li><code>__get() __set()</code></li><li><code>__call(),__callStatic()</code><ul><li>调用不可访问或不存在的方法时被调用</li></ul></li><li><code>__toString()</code></li><li><code>__construct(),__destruct()</code></li><li><code>__isset(),__unset()</code><ul><li>对不可访问或不存在的属性调用<code>isset()</code>或<code>empty()</code>时被调用</li><li>对不可访问或不存在的属性进行<code>unset</code>时被调用</li></ul></li><li><code>__sleep(),__wakeup()</code><ul><li>当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用</li><li>当使用unserialize时被调用,可用于做些对象的初始化操作</li></ul></li><li><code>__invoke()</code> 当以函数方式调用对象时被调用</li></ul><h2 id="魔术常量"><a href="#魔术常量" class="headerlink" title="魔术常量"></a>魔术常量</h2><ul><li><code>__FUNCTION__</code></li><li><code>__METHOD__</code></li><li><code>__CLASS__</code></li><li><code>__FILE__</code></li><li><code>__DIR__</code></li><li><code>__LINE__当前行号</code></li><li><code>__TRAIT__</code></li><li><code>__NAMESPACE__</code></li></ul><h2 id="var-dump-1…9-输出什么"><a href="#var-dump-1…9-输出什么" class="headerlink" title="var_dump(1…9) 输出什么"></a>var_dump(1…9) 输出什么</h2><p>复习的时候看到鸟哥的微博,顺便转载进来了。<a href="https://www.laruence.com/2020/02/23/1990.html" target="_blank" rel="noopener">风雪之隅</a></p><ul><li><p>输出:10.9 </p><ul><li>被分成三个部分 <code>1.</code> + <code>.</code> + <code>.9</code> </li><li>最后变成 1 拼接 0.9 </li></ul></li><li><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="comment">//...在PHP5.6之后是个新的操作符叫做Splat operator, 可以用来定义可变参数函数,或者解数组</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span><span class="params">($a, $b, $c)</span> </span>{</span><br><span class="line"> var_dump($a + $b + $c);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line">$parameters = <span class="keyword">array</span> (<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line"> </span><br><span class="line">foo(...$parameters);</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure></li></ul><h1 id="计算机系统"><a href="#计算机系统" class="headerlink" title="计算机系统"></a>计算机系统</h1><h2 id="进程、线程、协程"><a href="#进程、线程、协程" class="headerlink" title="进程、线程、协程"></a>进程、线程、协程</h2><ul><li>进程是系统调度的一个独立单位</li><li><strong>线程</strong>(英语:thread)是CPU调度和分配的基本单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。线程可以看做轻量级的进程。</li><li>每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。</li><li>系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。</li><li>没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分</li></ul><h3 id="PHP为什么是单线程的?"><a href="#PHP为什么是单线程的?" class="headerlink" title="PHP为什么是单线程的?"></a>PHP为什么是单线程的?</h3><ul><li><p>方便自上而下的去编辑/理解代码</p></li><li><blockquote><p>虽然pthreads可以实现多线程,但这会造成PHP-FPM的不稳定,线程安全检查也会产生额外的开销,所以PHP的多线程扩展pthreads的作者也只是建议在cli命令行下使 用</p></blockquote></li><li><p>多线程可能会引入其他问题:</p><ul><li>线程读写变量存在着同步问题,需要加锁</li><li>锁粒度过大会存在性能问题。一个线程运行,其他线程都在等待,无法实现并行</li><li>可能产生死锁</li><li>某个线程崩溃,则整个进程崩溃</li></ul></li><li><p>多进程更稳定</p></li></ul><h3 id="PHP多进程同时读写一个文件"><a href="#PHP多进程同时读写一个文件" class="headerlink" title="PHP多进程同时读写一个文件"></a>PHP多进程同时读写一个文件</h3><ul><li>flock 函数,可设置模式,但性能并不是很好,经常造成死锁<ul><li>读共享锁</li><li>写独占锁</li><li>释放锁定</li><li>锁定堵塞</li></ul></li><li>版本号概念的解决方案:拷贝一份,记录修改时间,修改后与对比原文件更新时间。如果不一致则此期间被修改过</li></ul><h2 id="进程间通信-IPC"><a href="#进程间通信-IPC" class="headerlink" title="进程间通信(IPC)"></a>进程间通信(IPC)</h2><ul><li>常用管道(短小、频率高,两进程之间通信)、</li><li>Socket 套接字,主要使用TCP</li><li>共享内存(庞大的数据,多进程通信,配合信号量使用)<ul><li>管道是半双工的,两进程间使用(需要适当的访问权限,如父子),是特殊的文件</li></ul></li><li>消息队列不常用</li></ul><h3 id="单工、半双工、全双工"><a href="#单工、半双工、全双工" class="headerlink" title="单工、半双工、全双工"></a>单工、半双工、全双工</h3><ul><li>单工:只能单向传输数据。例:遥控</li><li>半双工:可以沿两个方向传送,但一个信道同一时刻只能单向传输</li><li>全双工:两边同时接受、传输数据。例:电话</li></ul><h2 id="并行、串行、并发、异步"><a href="#并行、串行、并发、异步" class="headerlink" title="并行、串行、并发、异步"></a>并行、串行、并发、异步</h2><ul><li>串行:一次只能取得一个任务并执行一个</li><li>并行:可以通过多进程/多线程的方式执行这些任务</li><li>并发:并发是一种现象,同时运行多个程序或都个任务,任务可能是并行、也可能是串行执行,是操作系统进程调度和CPU上下文切换的结果</li></ul><h2 id="IO多路复用是什么"><a href="#IO多路复用是什么" class="headerlink" title="IO多路复用是什么"></a>IO多路复用是什么</h2><p>多路网络连接复用一个io线程。Linux——Epoll</p><h2 id="INT占几个字节?"><a href="#INT占几个字节?" class="headerlink" title="INT占几个字节?"></a>INT占几个字节?</h2><ul><li>C++在32位编译器下INT占4字节、LONG占4字节、FLOAT 4字节、DOUBLE 8字节、char 1字节</li></ul><h1 id="设计思想、设计模式"><a href="#设计思想、设计模式" class="headerlink" title="设计思想、设计模式"></a>设计思想、设计模式</h1><h2 id="MVC是什么"><a href="#MVC是什么" class="headerlink" title="MVC是什么"></a>MVC是什么</h2><blockquote><p>基本没人问</p></blockquote><p>MVC是模型(model)-视图(view)-控制器(controller)的缩写,是一种设计模式。model层负责提供数据,和数据库有关的操作都交给模型层来处理,view层则提供交互的界面,并输出数据,而controller层则负责接收请求,并分发给相应的model来处理,然后调用view层来显示。</p><h2 id="OOP面向对象编程"><a href="#OOP面向对象编程" class="headerlink" title="OOP面向对象编程"></a>OOP面向对象编程</h2><blockquote><p>基本没人问</p></blockquote><ul><li>将现实世界的事物抽象成对象,关系抽象成类、继承,帮助人们实现对现实世界的抽象与数字建模</li><li>三大特性:封装、继承、多态</li><li>五大基本原则:<ul><li>单一指责原则(类的功能单一)</li><li>开放封闭原则(对于拓展是开放的,对于修改是封闭的</li><li>里氏替换原则(子类可以代替父类出现</li><li>依赖倒置原则(高层次的模块不应该依赖与低层次的模块。具体实现应该依赖于抽象</li><li>接口分离原则(采用多个接口比一个通用接口好</li></ul></li></ul><h2 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h2><p>创建型、结构型、行为型</p><ul><li><p>单例、工厂</p></li><li><p>适配器:将一个类的接口转换成另外一个接口</p></li><li><p>中介者:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。</p></li></ul><h1 id="网络安全"><a href="#网络安全" class="headerlink" title="网络安全"></a>网络安全</h1><blockquote><p>看公司产品,如果有涉及到还是很有必要准备一下的。我准备的比较少</p></blockquote><h2 id="XSS(跨站脚本攻击)"><a href="#XSS(跨站脚本攻击)" class="headerlink" title="XSS(跨站脚本攻击)"></a>XSS(跨站脚本攻击)</h2><ul><li>往Web页面里插入恶意Script代码,攻击用户</li><li>反射型XSS,</li><li>存储型XSS,往数据库里写代码</li><li>HTTPONLY可以防止客户端从document读取cookie</li><li>使用<code>htmlspecialchars()</code>函数</li></ul><h2 id="CSRF(跨站请求伪造)"><a href="#CSRF(跨站请求伪造)" class="headerlink" title="CSRF(跨站请求伪造)"></a>CSRF(跨站请求伪造)</h2><ul><li>用户C访问受信任网站A、登陆后产生了cookie,用户未退出A网站之前,打开了网站B,网站B返回了攻击性代码,并在用户不知情的情况下利用cookie间接访问了A网站</li><li>解决方案:验证码、检测Refer、利用Token</li></ul><h2 id="DDoS"><a href="#DDoS" class="headerlink" title="DDoS"></a>DDoS</h2><ul><li>攻击者借助代理服务器生成指向目标系统的合法请求,造成服务器资源耗尽</li><li>微信过年发红包就是一次人肉DDoS攻击</li></ul><h1 id="LINUX"><a href="#LINUX" class="headerlink" title="LINUX"></a>LINUX</h1><blockquote><p>基本没人问,用到查</p></blockquote><h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h2><ul><li>netstat</li><li>w :显示正在登录的用户</li><li>cat /proc/meminfo 显示内存信息</li><li>df -h 查看当前目录 </li><li>df -h /usr/ 查看指定目录磁盘使用情况 </li><li>du -sh /usr/ 计算文件夹大小</li></ul><h2 id="Top命令查看服务器状态的几个指标"><a href="#Top命令查看服务器状态的几个指标" class="headerlink" title="Top命令查看服务器状态的几个指标"></a>Top命令查看服务器状态的几个指标</h2><ul><li>load average:1、5、15分钟内的服务器平均负载。<ul><li>数值以CPU核数为准。多核处理器中,你的Load Average不应该高于处理器核心的总数量的70%。</li><li>各家的公式不尽相同,但都是用于衡量正在使用CPU的进程数量和正在等待CPU的进程数量,一句话就是runnable processes的数量。</li></ul></li><li>CPU usage: user,sys的百分比</li><li>PhysMem:物理内存信息</li></ul><h1 id="反向面试"><a href="#反向面试" class="headerlink" title="反向面试"></a>反向面试</h1><p><a href="https://github.com/yifeikong/reverse-interview-zh" target="_blank" rel="noopener">这点同样重要</a></p><ul><li>进去以后我主要负责什么?</li><li>能介绍一下团队情况吗?</li><li>团队氛围如何?</li><li>开发主要流程是怎样的?</li><li>常用的技术栈?</li></ul><h1 id="小鹅通"><a href="#小鹅通" class="headerlink" title="小鹅通"></a>小鹅通</h1><ul><li><p>冒泡排序变形、某一遍排序下,两两之间比较,没有调换过位置,我是否能理解为整体有序?</p></li><li><p>快排思想、左右指针、一边排序结果是怎样的</p></li><li><p>公司项目的问题、没有提出问题</p></li><li><p>数据库innodb和myams的差距、(数据结构上)</p></li><li><p>索引优化方案、只回答,他没深入考察</p></li><li><p>如何理解框架中的自动加载</p></li><li><p>命名空间解决了什么问题</p></li><li><p>有没有写过框架</p></li><li><p>进程线程协程的区别</p></li><li><p>session和cookie的区别</p></li><li><p>cookie中的用户token请求 负载打到不同的机子上,登陆态不同,你觉得是什么原因,如何解决</p></li><li><p>并发和并行的区别</p></li></ul><h2 id="二"><a href="#二" class="headerlink" title="二"></a>二</h2><ul><li>上台阶问题</li><li>针对笔试题继续深入。SQL优化可以从(ES)服务解决</li></ul><blockquote><p>然后面试官对我说,好我知道你的情况了。当我收到offer就知道他们挺缺人的。因为我对自己的表现也不是很满意,这是第一次裸面。</p></blockquote><h1 id="深信服"><a href="#深信服" class="headerlink" title="深信服"></a>深信服</h1><ul><li><p>进程与线程的区别</p></li><li><p>进程间通信的几种方式</p></li><li><p>进程生存周期的5个阶段(创建、销毁)</p></li><li><p>HTTP状态码504是什么</p></li><li><p>Session和Cookie的区别</p></li><li><p>SSO思路</p></li><li><p>PHP数组相加和数组合并的区别</p></li><li><p>PHP数组取前十个的操作</p></li><li><p>PHP一个数组中插入另一个数组</p></li><li><p>找出一个数组中的在另一个数组是否重复,O(2n)</p></li><li><p>常见数据库引擎,以及之间的特点、差别</p></li><li><p>常见的几种索引</p></li><li><p>什么字段适合建立索引,什么字段不适合</p></li><li><p>索引未匹配到的原因</p></li><li><p>二分查找复杂度</p></li><li><p>职业规划</p></li></ul><h2 id="二-1"><a href="#二-1" class="headerlink" title="二"></a>二</h2><ul><li>没怎么问问题,对着项目随便聊了聊,这个就看个人发挥了,不要太紧张。主要是想看你是什么样的人。</li></ul><h2 id="三"><a href="#三" class="headerlink" title="三"></a>三</h2><ul><li>HR面,同上。</li></ul><blockquote><p>目前是池里一只鱼,等待打捞</p></blockquote><h1 id="反思总结"><a href="#反思总结" class="headerlink" title="反思总结"></a>反思总结</h1><ul><li>表达能力需要不断提升</li><li>面试的过程需要懂得察言观色</li><li>注意每一个细节都会成为被筛掉的可能</li><li>世纪难题——我为了什么而活?</li></ul><h1 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h1><ul><li><a href="https://segmentfault.com/a/1190000020189975" target="_blank" rel="noopener">头条面经</a></li><li><a href="https://www.v2ex.com/t/478824" target="_blank" rel="noopener">v2老哥的面试经验-值得看</a></li><li><a href="https://www.cnblogs.com/jasonZh/p/9513948.html" target="_blank" rel="noopener">Redis数据结构及应用场景</a></li></ul>]]></content>
<summary type="html">
<p>祝大家拿到好offer</p>
<ol>
<li>近期面试心得</li>
<li>一年的PHPer面试复习手册</li>
<li>深圳PHP岗位面经</li>
</ol>
</summary>
<category term="面试" scheme="http://ajsonx.github.io/categories/%E9%9D%A2%E8%AF%95/"/>
<category term="PHP Mysql Algorithm" scheme="http://ajsonx.github.io/tags/PHP-Mysql-Algorithm/"/>
</entry>
<entry>
<title>MySQL分组排序引发的问题</title>
<link href="http://ajsonx.github.io/2020/02/15/mysql-grouping-sorting/"/>
<id>http://ajsonx.github.io/2020/02/15/mysql-grouping-sorting/</id>
<published>2020-02-14T16:57:33.000Z</published>
<updated>2020-02-14T16:57:33.000Z</updated>
<content type="html"><![CDATA[<p>都是一些MySQL的基础知识</p><ol><li>分组排序解决方案</li><li>MySQL 函数、存储过程</li><li>SQL执行顺序(牢记)</li><li>MySQL 8.0 窗口函数</li></ol><a id="more"></a><hr><blockquote><p>以下所有SQL语句在 (Mysql5.8 , Navicat 12) 测试成功执行</p></blockquote><h1 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h1><p>Retrieving the last record in each group,类似Oracle 中 Top 函数。</p><p>分组Top N问题。在商品中取相同分类下最新创建的一个。</p><p>先来建表:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`goods`</span>(</span><br><span class="line"><span class="string">`id`</span> <span class="built_in">bigint</span>(<span class="number">20</span>) <span class="keyword">UNSIGNED</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT <span class="keyword">COMMENT</span> <span class="string">'主键ID'</span>,</span><br><span class="line"><span class="string">`category_id`</span> <span class="built_in">bigint</span>(<span class="number">20</span>) <span class="keyword">UNSIGNED</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'分类ID'</span>,</span><br><span class="line"><span class="string">`unit_price`</span> <span class="built_in">decimal</span>(<span class="number">11</span>,<span class="number">4</span>) <span class="keyword">UNSIGNED</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'商品单价/元'</span>,</span><br><span class="line"><span class="string">`create_time`</span> datetime(<span class="number">0</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'创建时间'</span>,</span><br><span class="line"><span class="string">`update_time`</span> datetime(<span class="number">0</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'更新时间'</span>,</span><br><span class="line">PRIMARY <span class="keyword">KEY</span>(<span class="string">`id`</span>),</span><br><span class="line"><span class="keyword">KEY</span> <span class="string">`idx_category_id_create_time`</span> (<span class="string">`category_id`</span>,<span class="string">`create_time`</span>) <span class="keyword">USING</span> BTREE</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> AUTO_INCREMENT=<span class="number">13</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8mb4 <span class="keyword">COLLATE</span>=utf8mb4_bin</span><br></pre></td></tr></table></figure><h1 id="分组排序"><a href="#分组排序" class="headerlink" title="分组排序"></a>分组排序</h1><h2 id="Step-1-分析"><a href="#Step-1-分析" class="headerlink" title="Step 1.分析"></a>Step 1.分析</h2><ul><li><p>相同分类:可以用到<code>group by</code></p></li><li><p>最新一行:可以考虑取<code>id</code>或者<code>create_time</code>较大值,也可以尝试使用<code>order by</code></p></li></ul><h2 id="Step-2-编写"><a href="#Step-2-编写" class="headerlink" title="Step 2.编写"></a>Step 2.编写</h2><p>尝试写了以下几条SQL:</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">❌ SELECT * </span><br><span class="line"> <span class="keyword">FROM</span> goods </span><br><span class="line"> <span class="built_in"> GROUP </span>BY category_id </span><br><span class="line"> ORDER BY create_time DESC</span><br></pre></td></tr></table></figure><p>然而MYSQL的执行顺序并没有那么理想化。并不会对组内排序,而是先分组,后排序。并且这条SQL在5.7下也无法执行。</p><p>针对最大值考虑使用聚合函数:</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">❌ SELECT *,category_id,max(create_time) </span><br><span class="line"> <span class="keyword">FROM</span> goods </span><br><span class="line"> <span class="built_in"> GROUP </span>BY category_id</span><br></pre></td></tr></table></figure><p>看上去没什么大问题,但它无法执行,因为(SELECT *)违反了MySQL <code>only_full_group_by</code>的规则。这里先不做解释,我们来试着改造下这个sql。</p><p>上一条sql中我们已经有了最大时间和分类id,我们可以考虑使用联表查询:</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">✅ SELECT <span class="selector-tag">a</span><span class="selector-class">.unit_price</span>, <span class="selector-tag">a</span><span class="selector-class">.category_id</span>, <span class="selector-tag">a</span><span class="selector-class">.create_time</span> </span><br><span class="line"> FROM goods <span class="selector-tag">a</span> INNER JOIN (</span><br><span class="line"> SELECT category_id,max(create_time) AS max_time </span><br><span class="line"> FROM goods </span><br><span class="line"> GROUP BY category_id</span><br><span class="line"> ) AS <span class="selector-tag">b</span> </span><br><span class="line"> ON <span class="selector-tag">a</span><span class="selector-class">.category_id</span> = <span class="selector-tag">b</span><span class="selector-class">.category_id</span> </span><br><span class="line"> AND <span class="selector-tag">a</span><span class="selector-class">.create_time</span> = <span class="selector-tag">b</span>.max_time</span><br></pre></td></tr></table></figure><p>执行成功,是我们想要的结果。</p><p><code>INNER JOIN</code> 可以考虑换成<code>LEFT JOIN</code> 加上 <code>IS NULL</code>,作为参照对比性能:</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">✅ SELECT <span class="selector-tag">a</span><span class="selector-class">.unit_price</span>, <span class="selector-tag">a</span><span class="selector-class">.category_id</span>, <span class="selector-tag">a</span><span class="selector-class">.create_time</span> </span><br><span class="line"> FROM goods <span class="selector-tag">a</span> LEFT JOIN (</span><br><span class="line"> SELECT category_id,max(create_time) AS max_time </span><br><span class="line"> FROM goods GROUP BY category_id</span><br><span class="line"> ) AS <span class="selector-tag">b</span> </span><br><span class="line"> ON <span class="selector-tag">a</span><span class="selector-class">.category_id</span> = <span class="selector-tag">b</span><span class="selector-class">.category_id</span> </span><br><span class="line"> AND <span class="selector-tag">a</span><span class="selector-class">.create_time</span> = <span class="selector-tag">b</span>.max_time</span><br><span class="line"> WHERE <span class="selector-tag">b</span><span class="selector-class">.category_id</span> IS NOT NULL</span><br></pre></td></tr></table></figure><p>也可以写在<code>WHERE</code>后的子查询 这里取巧的按ID大小来排序:</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">✅ SELECT unit_price,category_id,create_time </span><br><span class="line"> <span class="keyword">FROM</span> goods WHERE id <span class="keyword">IN</span> (</span><br><span class="line"> SELECT MAX(id) </span><br><span class="line"> <span class="keyword">FROM</span> goods </span><br><span class="line"> <span class="built_in"> GROUP </span>BY category_id</span><br><span class="line"> )</span><br></pre></td></tr></table></figure><p>在网上看到这种方法,MYSQL5.7之前默认取第一条,可以利用这个特性来做。<br>5.7之后,会对组内排序优化,从而忽视组内排序。加上limit max_int 就可以无视:</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">❓ SELECT * <span class="keyword">FROM</span> (</span><br><span class="line"> SELECT * <span class="keyword">FROM</span> test </span><br><span class="line"> ORDER BY seq DESC limit 10000000000</span><br><span class="line"> ) tmp </span><br><span class="line"> <span class="built_in"> GROUP </span>BY NAME</span><br></pre></td></tr></table></figure><p>个人并不是很喜欢这种解法。首先逻辑上并不成立,排序完再分组取的不一定是第一条,同样在5.7下依然因为<code>only_full_group_by</code>无法运行。</p><p>至此,根据所学写了三条可用的SQL。但还不够,我们接下来造数据调试一下。</p><h2 id="Step-3-优化"><a href="#Step-3-优化" class="headerlink" title="Step 3.优化"></a>Step 3.优化</h2><p> mysql 创建存储过程: </p><figure class="highlight oxygene"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">-- 以下是在 Mac/Navicat/<span class="number">12.0</span>下执行的 可以不加DEFINER 命令行执行需要加上DELIMITER $$ 声明开始结束</span><br><span class="line"><span class="keyword">CREATE</span> DEFINER=`root`@`localhost` <span class="function"><span class="keyword">PROCEDURE</span> `<span class="title">RandomInsert</span>`<span class="params">(<span class="keyword">IN</span> time_len INT,<span class="keyword">IN</span> category_len INT,<span class="keyword">IN</span> len INT)</span> -- 三个参数分别是时间戳增加值,分类数,插入条数</span></span><br><span class="line"><span class="function"><span class="title">BEGIN</span></span></span><br><span class="line"><span class="function"> <span class="title">DECLARE</span> <span class="title">i</span> <span class="title">INT</span>;</span></span><br><span class="line">DECLARE ts INT;</span><br><span class="line"><span class="keyword">SET</span> i = <span class="number">0</span>; </span><br><span class="line">START TRANSACTION;</span><br><span class="line"><span class="keyword">WHILE</span> i <= len <span class="keyword">DO</span></span><br><span class="line"><span class="keyword">SET</span> ts = UNIX_TIMESTAMP( NOW()) + CEIL(RAND() * i); -- 随机时间戳</span><br><span class="line">INSERT goods(category_id, unit_price, create_time, update_time) VALUES(FLOOR(</span><br><span class="line">RAND() * category_len), FLOOR( RAND() * <span class="number">1000</span>) , FROM_UNIXTIME(ts),FROM_UNIXTIME(ts) );</span><br><span class="line"><span class="keyword">SET</span> i = i+<span class="number">1</span>;</span><br><span class="line"><span class="keyword">END</span> <span class="keyword">WHILE</span>;</span><br><span class="line">COMMIT;</span><br><span class="line"><span class="keyword">END</span></span><br><span class="line"></span><br><span class="line">-- 新建另一个函数去调用我们刚刚创建的方法</span><br><span class="line">CALL RandomInsert(<span class="number">1000</span>,<span class="number">1000</span>,<span class="number">10000000</span>) -- 这里我插入了一千万条,只分了<span class="number">1000</span>个类(本机最终耗时<span class="number">3326</span>秒。i7-<span class="number">4700</span>MQ)</span><br></pre></td></tr></table></figure><p>以下是平均查询耗时</p><ul><li><code>INNER JOIN</code> : 0.014s</li><li><code>LEFT JOIN</code> : 0.028s</li><li><code>WHERE IN</code> : 6.5s</li></ul><p>利用<code>EXPLAIN</code> && <code>SHOW WARNINGS</code>可以看到<code>WHERE IN</code> 的查询方式多了一次全表扫描</p><p>结论:相同需求时<code>LEFT JOIN</code>需要多出 <code>n*n/2 + n/2</code> 次的 <code>IS NULL</code> 操作,故最好选择<code>INNER JOIN</code></p><h1 id="SQL执行顺序"><a href="#SQL执行顺序" class="headerlink" title="SQL执行顺序"></a>SQL执行顺序</h1><p>基础知识,不过多赘述。(<a href="https://blog.csdn.net/J080624/article/details/80703903" target="_blank" rel="noopener">图片转自CSND</a>)</p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200216/sql_exec_order.jpeg" alt="SQL执行顺序"></p><h1 id="MySQL8-0的窗口函数"><a href="#MySQL8-0的窗口函数" class="headerlink" title="MySQL8.0的窗口函数"></a>MySQL8.0的窗口函数</h1><p>MYSQL从8.0开始支持窗口函数。经过短暂的学习后我们可以写出分组排序的另一种解决方案:</p><figure class="highlight n1ql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">✅ <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> (</span><br><span class="line"> <span class="keyword">SELECT</span> </span><br><span class="line"> category_id, </span><br><span class="line"> unit_price,</span><br><span class="line"> create_time,</span><br><span class="line"> ROW_NUMBER() <span class="keyword">OVER</span> (</span><br><span class="line"> <span class="keyword">PARTITION</span> <span class="keyword">BY</span> category_id </span><br><span class="line"> <span class="keyword">ORDER</span> <span class="keyword">BY</span> create_time <span class="keyword">DESC</span></span><br><span class="line"> ) <span class="keyword">as</span> rn</span><br><span class="line"> <span class="keyword">FROM</span></span><br><span class="line"> goods) t</span><br><span class="line"> <span class="keyword">WHERE</span> rn <= <span class="number">1</span>;</span><br></pre></td></tr></table></figure><p><code>ROW_NUMBER()</code> 就是一个窗口函数</p><p>调用窗口函数的格式是:<code>函数名([expr]) OVER子句</code></p><p>其中,<code>PARTITION BY</code> 是分区的意思,我暂时理解为<code>GROUP BY</code> 不做深究</p><p>最关键的就是 <code>WHERE</code> 子句后面的内容 :<code>row_number <= 1</code> 感觉是不是回到了<code>ORACLE</code>?</p><p><strong>优点就是真的太方便了,缺点就是执行效率感人(与联表查询相同索引的情况下,执行了两次全表扫描)</strong></p><h1 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h1><ul><li><p><a href="https://stackoverflow.com/questions/1313120/retrieving-the-last-record-in-each-group-mysql" target="_blank" rel="noopener">stackoverflow</a> </p></li><li><p><a href="https://www.cnblogs.com/DataArt/p/9961676.html" target="_blank" rel="noopener">Mysql8.0窗口函数</a></p></li></ul>]]></content>
<summary type="html">
<p>都是一些MySQL的基础知识</p>
<ol>
<li>分组排序解决方案</li>
<li>MySQL 函数、存储过程</li>
<li>SQL执行顺序(牢记)</li>
<li>MySQL 8.0 窗口函数</li>
</ol>
</summary>
<category term="mysql" scheme="http://ajsonx.github.io/categories/mysql/"/>
<category term="mysql sql" scheme="http://ajsonx.github.io/tags/mysql-sql/"/>
</entry>
<entry>
<title>我在做什么——什么是中台?</title>
<link href="http://ajsonx.github.io/2020/02/06/middleground/"/>
<id>http://ajsonx.github.io/2020/02/06/middleground/</id>
<published>2020-02-06T08:10:30.000Z</published>
<updated>2020-02-06T08:10:30.000Z</updated>
<content type="html"><![CDATA[<p>以下内容是对我自己提出的几个问题的回答:</p><ol><li>中台的相关概念</li><li>中台与微服务的关系</li><li>仅代表个人的,从入职以来对公司业务的理解、架构的不断学习</li></ol><a id="more"></a><hr><p>写在开头</p><p>我明白,思考中台,微服务以及架构。对我们这种初级程序员没有什么意义,学而无用。脑子里总是不停的出现类似的话,”要多学点基础知识,linux、https、tcp、计算机原理….”。思考这些狗屁中台的确是80%的无意义。同样我也会因为看了一本烂书而愤愤,浪费了我的时间。但我不能接受一知半解,潦草的对待,抛在一边不管。几年前读完柴静的《看见》,文中写道一段话我感触颇深,我一直记着。原文是这样的:</p><blockquote><p>我采访过一个与女儿有严重隔阂的母亲,她二十几年后反思说:“不论是谁,都不要对别人说‘应该’这两个字”。 “应该”,是一个人认识生活的模式,一旦形成,很难摆脱,对人对事的看法往往是只是一再加强这个模式。 要想完成客观的采访,就要尽力削除这个“应该”,<strong>只陈述,只发问,不评判,唯有如此,才能了解更多事实</strong> </p></blockquote><p>By the way, 对待有争议的话题时我尽力保持不发声,网络暴力越演愈烈的今天,需要学会不说话。</p><p>扯远了,回到学习这个话题,把想做的做完就行了。生活与工作的意义在于不断刷新你认知的上限,或者是下限。</p><h1 id="我在做什么——什么是中台?"><a href="#我在做什么——什么是中台?" class="headerlink" title="我在做什么——什么是中台?"></a>我在做什么——什么是中台?</h1><p>从入职开始的那天起,部门的架构师和产品经理循循善诱的告诉我:”我们在做中台的项目呀~作为餐饮业最重要的就是菜品啦~这就是我们的数据源“。</p><p>那天,我上网搜索了非常多关于中台的文章,一篇篇的看下去,接连不断的Infos OR Knowledge冲击我的大脑。无上的荣誉感净化我的心灵(跳槽可以拿去吹牛了~)</p><p>但有一天,随着我真正的开始反思公司的架构,思考中台存在的作用、必要性。我的脑子里不断的出现了疑问。</p><p>如果你也对此感兴趣,请往下看:</p><h1 id="Q-amp-A"><a href="#Q-amp-A" class="headerlink" title="Q & A"></a>Q & A</h1><h2 id="什么是数字化建设?"><a href="#什么是数字化建设?" class="headerlink" title="什么是数字化建设?"></a>什么是数字化建设?</h2><p>在介绍中台之前我们先来看看数字化这个概念,什么是数字化?与以前的信息化建设有何不同?</p><p>数据的阶段:</p><h3 id="阶段一:数据不被存储"><a href="#阶段一:数据不被存储" class="headerlink" title="阶段一:数据不被存储"></a>阶段一:数据不被存储</h3><p>软件只为了解决一次性的问题。比如计算机,画图工具,扫雷等等</p><h3 id="阶段二:少量的数据被存储和查询"><a href="#阶段二:少量的数据被存储和查询" class="headerlink" title="阶段二:少量的数据被存储和查询"></a>阶段二:少量的数据被存储和查询</h3><p>应对业务上的需求,软件从解决单点问题的工具演进到处理一类业务问题,从而有了多个功能模块,这时候有了数据的产生。类比扫雷,添加了用户名、排行榜、通关时间、游戏次数、玩家点击次数等等</p><h3 id="阶段三:数据仓库的出现,数据被大量存储"><a href="#阶段三:数据仓库的出现,数据被大量存储" class="headerlink" title="阶段三:数据仓库的出现,数据被大量存储"></a>阶段三:数据仓库的出现,数据被大量存储</h3><p>这个阶段是当数据规模大到一定量级,软件应对更多的需求作出的改变。继续拿扫雷这个游戏举例:当我玩了100万次扫雷之后,有了1PB的扫雷对战记录、通关时间等等数据。这时候就会有“平均通关时间”,“通关率是多少”,“开局第一个次点击的位置分布在哪”等等更加复杂的需求,软件不停的变更才能更好满足用户的需求。然而这些信息很难单纯的从业务数据库中查询出来,业务数据库只是为了完成记录,并不是为了分析而记录。因此就来到了阶段四</p><h3 id="阶段四:从数据中挖掘价值"><a href="#阶段四:从数据中挖掘价值" class="headerlink" title="阶段四:从数据中挖掘价值"></a>阶段四:从数据中挖掘价值</h3><p>业务从数据中发现市场的规律,洞察客户的兴趣,产生一些人们不知道的信息。这个阶段在市场营销、生产调度等影响因子较多,动态性较大的业务领域,数据的重要性愈加凸显。(扫雷搞个彩蛋?通关送个彩票、设立个世界最快通关纪录、再打打广告…)</p><h3 id="阶段五:数据成为企业核心资产"><a href="#阶段五:数据成为企业核心资产" class="headerlink" title="阶段五:数据成为企业核心资产"></a>阶段五:数据成为企业核心资产</h3><p>数据成为了企业的核心资产,所有的业务都被数据化。数据能够产生价值,数据无比重要。</p><blockquote><p>总结一下,我们会发现在信息化时代,数据是流程的副产品,流程是预先设计好的,然后在设计好的流程中产生了数据</p><p>在数字化时代,业务流程应用软件(业务流程的显形载体)会随着市场的变化快速而不断动态迭代甚至消亡,而数据成为了物理世界映射到数字化世界的原子,数据思维(”Data First” )成为战略核心之一。</p></blockquote><h2 id="什么是数据中台(CDP)?"><a href="#什么是数据中台(CDP)?" class="headerlink" title="什么是数据中台(CDP)?"></a>什么是数据中台(CDP)?</h2><p>既然中台这概念火了起来,那就有“前台”,“后台”吧。我们把它与企业中实际应用场景一一对应,这样就能对中台有个大概的理解。我用京东作为例子:</p><ul><li>前台:京东金融、京东到家(线上买菜)、京东拍拍(二手)、京东(自营商品)</li><li>后台:内部OA系统、ERP、CRM、财务系统等等</li></ul><p>这下就很好理解吧。对于传统信息化的企业来说,后台系统都是过于繁重的。应对前台业务的需求不能及时响应。甚至需要大改内部系统。中台就是介于这两个之间的缓冲层。将复杂的后台功能解耦,前台的通用模块复用。就是一个独立的数据中台。</p><p>正因此,我们可以从业务垂直划分出 用户中心、商品中心、交易中心等等作为中台服务之一。中台的意义在于建立通用化服务,链接前台和后台,避免重复造轮子,使业务可持续发展。前期实现核心功能,完成业务接入,中期灵活满足个性化需求、提升中台数据表现,以便让业务大量接入。后期则是组件化、新业务能灵活接入</p><h2 id="中台和微服务的关系?"><a href="#中台和微服务的关系?" class="headerlink" title="中台和微服务的关系?"></a>中台和微服务的关系?</h2><h3 id="微服务"><a href="#微服务" class="headerlink" title="微服务"></a>微服务</h3><p>伴随中台火起来的微服务概念究竟是什么呢?</p><p>微服务是<a href="https://zh.wikipedia.org/wiki/Martin_Fowler" target="_blank" rel="noopener">Martin Fowler</a> 与 <a href="https://zh.wikipedia.org/w/index.php?title=James_Lewis&action=edit&redlink=1" target="_blank" rel="noopener">James Lewis</a> 早在2004年共同提出的概念,在2014年正式命名微服务。根据业务功能设施,全自动的方式部署,与其他服务使用HTTP API通信,服务可以用不同的编程语言与数据库等组件实现。</p><p>企业中需要持续交付、快速迭代的业务、系统性能高可用(不适用于证券系统等性能要求苛刻的场景)、数据一致性层面可以使用微服务架构来拆分业务单元。常见的架构拆分方法一般有两种: </p><p>功能单元垂直拆分,如图(图片无法显示请挂vpn):</p><p>这种将商品、用户、交易这些模块按照业务逻辑进行拆分,但业务有交集</p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200206/vertical_split.png" alt="功能单元垂直拆分"></p><p>水平拆分,如图(图片无法显示请挂vpn):</p><ul><li>网关层Gateway:过滤器、路由转发</li><li>业务逻辑层:RPC或者是Restful的形式去调用数据访问层,做业务逻辑处理的一些工作</li><li>数据访问层:CURD等一些数据交互的工作</li><li>DB:关系非关系型数据库</li><li>独立于这些之外的配置中心(Apollo), 注册中心(CP模型-Zookeeper,AP-Erukal)</li></ul><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200206/horizontal_split.png" alt="水平拆分"></p><h3 id="常见架构模式"><a href="#常见架构模式" class="headerlink" title="常见架构模式"></a>常见架构模式</h3><ul><li><p>链式架构模式(交易系统,业务单一、下单减库存)</p></li><li><p>聚合器架构模式(聚合各个微服务)</p></li><li><p>数据共享架构模式(读写分离、数据量小的DB可以共享一个DB实例)</p></li><li><p>异步消息架构模式(MQ转发请求)</p></li></ul><p><strong>由此可见,微服务只是中台的一种解决方案,中台真正需要的不一定是微服务架构。微服务架构能很好解决引入中台的问题,而微服务带来的问题,又是否能很好的解决呢?</strong></p><h2 id="Where-are-we-?"><a href="#Where-are-we-?" class="headerlink" title="Where are we ?"></a>Where are we ?</h2><p>如果对比理想化的中台系统来说,我们正在路上——目前只是建设好了“数据仓库”,可以持续接入业务中(提供对大数据分析的数据、小程序的数据接口、POS端点餐、ERP系统的物料成本等、财务系统的税率法人信息等等)。<br>我们在现阶段上可以针对订单数据、用户数据这两块做交易中台、用户中台以此完备中台。但所投入的人力与成本,是否值得回报?<br>中台真的值得做吗?引入中台又将带来什么问题?由微服务架构带来的维护成本真的承受得起吗?<br>这些问题值得我们去思考,道路是曲折的。一名好的架构师,我觉得最重要的是能在技术与成本之间平衡,为公司找到可持续发展的架构。既不拒绝旧技术,也不盲目追赶新技术。这样在浪潮退去之后,留给企业的才是真正的数据资产,数据价值。</p><blockquote><p>半个月前,36kr报道,云徙科技外包茅台集团的项目成了中台概念下的第一个牺牲品。一份包治百病的PPT导致悲剧的发生。人们总是过于理想化,却没有看到他背后的缺陷。</p></blockquote><p>on the way…</p><h1 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h1><ul><li><a href="https://pan.baidu.com/s/11MrqLFamQIS0-pMy4PUPdg" target="_blank" rel="noopener">孙玄-百万架构师公开课</a></li><li><a href="https://www.zhihu.com/question/365139319" target="_blank" rel="noopener">知乎-数据中台是什么?</a></li><li><a href="https://www.zhihu.com/question/65502802" target="_blank" rel="noopener">知乎-什么是微服务架构</a></li></ul>]]></content>
<summary type="html">
<p>以下内容是对我自己提出的几个问题的回答:</p>
<ol>
<li>中台的相关概念</li>
<li>中台与微服务的关系</li>
<li>仅代表个人的,从入职以来对公司业务的理解、架构的不断学习</li>
</ol>
</summary>
<category term="架构" scheme="http://ajsonx.github.io/categories/%E6%9E%B6%E6%9E%84/"/>
<category term="中台 微服务" scheme="http://ajsonx.github.io/tags/%E4%B8%AD%E5%8F%B0-%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
</entry>
<entry>
<title>杂七杂八</title>
<link href="http://ajsonx.github.io/2020/01/09/attention1/"/>
<id>http://ajsonx.github.io/2020/01/09/attention1/</id>
<published>2020-01-09T08:51:49.000Z</published>
<updated>2020-01-19T16:00:00.000Z</updated>
<content type="html"><![CDATA[<p>这几天写了两个东西</p><ol><li>公众号文章转换及处理 </li><li>定时领券</li></ol><a id="more"></a><hr><h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><p>一直关注品城记,发现它推荐的店铺信息每期都会整理在公众号文章下,大部分格式是正确的(预想的太美好,其实最后也就30%的转换率。人工维护的信息都有偏差)</p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200111/pcj.jpeg" alt="XX记公众号文章截图"></p><p>直接用<code>charles</code>抓包,然后使用<code>php</code>进行批量处理。</p><blockquote><p>思路:文件处理、正则匹配、字符处理、简易ORM、小程序云开发 </p></blockquote><p> 最近<strong>京东</strong>年货节,一直放大量的券,京东领券对消费者还是比较友好的。不像猫超,只能靠抢,说你火爆你就没办法。</p><blockquote><p>思路:CURL、url参数处理、执行脚本、并行 (这玩意的效果感觉跟js差不多,太菜了我)</p></blockquote><h2 id="公众号文章转换"><a href="#公众号文章转换" class="headerlink" title="公众号文章转换"></a>公众号文章转换</h2><h3 id="Step-1"><a href="#Step-1" class="headerlink" title="Step 1"></a>Step 1</h3><p>抓包抓到的<code>Responding</code>直接保存为文件(只抓了列表页,其中携带文章<strong>URL</strong>和<strong>标题</strong>),扫描文件获取文本内容</p><ul><li><code>glob($pattern,$flags=0)</code> <ol><li>模式匹配文件名 例:<code>*.jpg / *.txt</code></li><li>特殊的设定 使用 <code>GLOB_BRACE</code> <code>{a,b,c}</code>来匹配 <code>'a','b','c'</code></li><li>成功返回绝对路径,否则<code>false</code></li></ol></li></ul><ul><li><code>scandir ($dir,$sort_order,$context)</code> <ol><li>完整路径</li><li>排序方式</li><li>返回相对路径</li></ol></li></ul><ul><li><code>readdir(resource $dir_handle)</code><ol><li><code>opendir()</code> 打开的资源参数</li></ol></li></ul><h3 id="Step-2"><a href="#Step-2" class="headerlink" title="Step 2"></a>Step 2</h3><p><strong>提取目标格式中的信息。文本数据不完全规范,需要多次处理</strong>。一开始想使用正则,但是对于相交的区间,正则并不能处理,后改用explode。使用explode后就需要对数组进行多次处理了,相对麻烦。</p><ul><li><code>str_replace(mixed $search,mixed $replace,mixed $subject,int $count = 0)</code><ol><li><code>search</code> 与 <code>needle</code> 同义 </li><li>如果 <code>replace</code> 的值的个数少于 <code>search</code> 的个数,多余的替换将使用空字符串来进行。 </li><li>如果 <code>search</code> 是一个数组而 <code>replace</code> 是一个字符串,那么 <code>search</code> 中每个元素的替换将始终使用这个字符串。</li><li><code>count</code> 如果被指定,它的值将被设置为<strong>替换发生的次数</strong></li><li>替换多维数组中的字符串时可用<strong>json_encode而不是遍历,效率更高</strong>。 <a href="https://www.php.net/manual/zh/function.str-replace.php" target="_blank" rel="noopener">参考php手册中高赞笔记</a></li></ol></li></ul><ul><li><p><code>strstr($haystack,$needle,$before)</code></p><blockquote><p>如果你仅仅想确定 <code>needle</code> 是否存在于 <code>haystack</code> 中,请使用速度更快、耗费内存更少的 <a href="https://www.php.net/manual/zh/function.strpos.php" target="_blank" rel="noopener">strpos()</a> 函数。</p></blockquote><ol><li>返回<code>needle</code>之后/之前的字符串</li></ol></li></ul><ul><li><p><code>substr()</code> / <code>mb_substr($str,$start,$length,$encoding)</code></p><p>这个太熟悉了。说下mb_substr吧,是按字符的字节长度来截取。</p><p>意思就是utf-8编码下占三个字节的汉字算一个截取长度</p></li></ul><ul><li><p><code>strip_tags($str,$allowable_tags)</code></p><blockquote><p>由于 <strong>strip_tags()</strong> 无法实际验证 HTML,不完整或者破损标签将导致更多的数据被删除。</p><p>输入 HTML 标签名字如果大于 1023 字节(bytes)将会被认为是无效的,无论 <code>allowable_tags</code> 参数是怎样的。</p></blockquote><ol><li>从字符串中去除 HTML 和 PHP 标记</li><li>使用可选的第二个参数指定不被去除的字符列表。</li><li><a href="https://www.php.net/manual/zh/function.htmlspecialchars.php" target="_blank" rel="noopener">htmlspecialchars()</a> - 将特殊字符转换为 HTML 实体</li></ol></li></ul><ul><li><code>addslashes($str)</code><ol><li>使用反斜线引用字符串</li><li>可用于数据库插入时去掉引号,避免插入失败</li></ol></li></ul><ul><li><p><code>preg_replace_callback($pattern,$callback,$subject)</code></p><ol><li>执行一个正则表达式搜索并且使用一个回调进行替换</li></ol><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="function"><span class="keyword">function</span> <span class="title">filter_Emoji</span><span class="params">($str)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $str = preg_replace_callback( <span class="comment">//执行一个正则表达式搜索并且使用一个回调进行替换</span></span><br><span class="line"> <span class="string">'/./u'</span>,</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="params">(array $match)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> strlen($match[<span class="number">0</span>]) >= <span class="number">4</span> ? <span class="string">''</span> : $match[<span class="number">0</span>];</span><br><span class="line"> },</span><br><span class="line"> $str);</span><br><span class="line"> <span class="keyword">return</span> $str;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><ul><li><p><code>preg_match_all($pattern,$subject,$matches,$flag,$offset)</code></p><ol><li><p><code>flag</code> 有几个不同的标记值,用于不同的输出</p></li><li><p><code>offset</code> 是偏移量,从目标串偏移位置开始匹配,1开始计数</p></li><li><p>好奇这个函数为什么不能匹配相交的区间,看了以下的C源码。</p><p>分了三个部分:</p><p>1)regex 2)match 3)error </p><p>用到了挺多的<code>goto</code> (以前写C++打题的时候从来不用switch、goto以及do while,很有意思。)</p><p>如何查找源码呢,<code>php-src</code> 仓库 <code>Cmd+shift+F</code> -> <code>_function(func_name)</code></p></li></ol><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200111/pcre.png" alt="pcre"></p><p> <strong>此处使用了jit(即时编译)后,速度比不使用快10倍之多,JIT可以决定是否将代码编译,从而提升执行速度</strong> </p><p><a href="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/index.html" target="_blank" rel="noopener">这篇关于JAVA的JIT编译器讲的很不错</a></p><p> 除了一堆宏定义与异常处理之外,<code>pcre</code>就是个库。本质上还是C的<code>strcmp()</code>与<code>for</code>循环。发现新世界的大门…….</p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200111/pcre2_match.png" alt="pcre2_match"></p></li></ul><p><a href="https://www.cnblogs.com/Sabre/p/5710649.html" target="_blank" rel="noopener">关于“此函数可以安全用于二进制对象”的释义</a></p><h3 id="Step-3"><a href="#Step-3" class="headerlink" title="Step 3"></a>Step 3</h3><p>PDO写入数据库,需要正确输出,捕获异常等,这个函数涉及较少,实现调用时思路都大同小异吧。</p><ol><li>单例模式复用数据库连接</li><li><code>insert($field)</code> 拆分数组键值对,拼装sql,绑定参数(需要判断参数类型)</li><li><code>exec()</code> 需要数据输出则使用<code>fetch</code> 不需要则返回 <code>bool</code> 值</li><li>异常捕获</li></ol><h2 id="定时领券"><a href="#定时领券" class="headerlink" title="定时领券"></a>定时领券</h2><h3 id="Step-1-1"><a href="#Step-1-1" class="headerlink" title="Step 1"></a>Step 1</h3><p>使用charles 抓取京东的请求,提取url和请求参数,必须拿到cookie。使用代码封装请求,并测试。</p><ol><li><strong>CURL</strong>系列函数 (不多说,用到直接查就好,其实用composer的扩展包更友好)</li><li><code>urlencode()</code> 看参数中是否会携带引号等字符,使用这个函数转换</li></ol><h3 id="Step-2-1"><a href="#Step-2-1" class="headerlink" title="Step 2"></a>Step 2</h3><p>使用代理ip、多线程跑。</p><blockquote><p> 测试的代理ip被拦截的严重,最后也没用上。</p></blockquote><ul><li>检测代理ip有效性 <strong>以下代码引用,from 搜索引擎结果,仅提供思路</strong></li></ul><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 检查代理ip信息有效性</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> string $proxy_ip</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> int $times 执行检查次数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> array</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">check_proxy_ip_info</span><span class="params">($proxy_ip = <span class="string">''</span>, $times = <span class="number">10</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $header = <span class="keyword">array</span>(</span><br><span class="line"> <span class="comment">// "GET / HTTP/1.1",</span></span><br><span class="line"> <span class="comment">// "HOST: www.baidu.com",</span></span><br><span class="line"> <span class="string">"accept: application/json"</span>,</span><br><span class="line"> <span class="string">"accept-encoding: gzip, deflate"</span>,</span><br><span class="line"> <span class="string">"accept-language: en-US,en;q=0.8"</span>,</span><br><span class="line"> <span class="string">"content-type: application/json"</span>,</span><br><span class="line"> <span class="string">"user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36"</span>,</span><br><span class="line"> );</span><br><span class="line"> $url = <span class="string">'http://www.baidu.com/'</span>;</span><br><span class="line"> $result[<span class="string">'succeed_times'</span>] = <span class="number">0</span>; <span class="comment">//成功次数</span></span><br><span class="line"> $result[<span class="string">'defeat_times'</span>] = <span class="number">0</span>; <span class="comment">//失败次数</span></span><br><span class="line"> $result[<span class="string">'total_spen'</span>] = <span class="number">0</span>; <span class="comment">//总用时</span></span><br><span class="line"> <span class="keyword">for</span> ($i = <span class="number">0</span>; $i < $times; $i++) {</span><br><span class="line"> $s = microtime();</span><br><span class="line"> $curl = curl_init();</span><br><span class="line"> curl_setopt($curl, CURLOPT_URL, $url); <span class="comment">//设置传输的url</span></span><br><span class="line"> curl_setopt($curl, CURLOPT_HTTPHEADER, $header); <span class="comment">//发送http报头</span></span><br><span class="line"> curl_setopt($curl, CURLOPT_RETURNTRANSFER, <span class="number">1</span>);</span><br><span class="line"> curl_setopt($curl, CURLOPT_ENCODING, <span class="string">'gzip,deflate'</span>); <span class="comment">// 解码压缩文件</span></span><br><span class="line"> curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, <span class="keyword">false</span>); <span class="comment">//不验证证SSL书</span></span><br><span class="line"> curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, <span class="keyword">false</span>); <span class="comment">//不验证SSL证书</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (@$proxy_ip != <span class="keyword">false</span>) { <span class="comment">//使用代理ip</span></span><br><span class="line"> curl_setopt($curl, CURLOPT_HTTPHEADER, <span class="keyword">array</span>(</span><br><span class="line"> <span class="string">'Client_Ip: '</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>) . <span class="string">'.'</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>) . <span class="string">'.'</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>) . <span class="string">'.'</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>),</span><br><span class="line"> ));</span><br><span class="line"> curl_setopt($curl, CURLOPT_HTTPHEADER, <span class="keyword">array</span>(</span><br><span class="line"> <span class="string">'X-Forwarded-For: '</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>) . <span class="string">'.'</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>) . <span class="string">'.'</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>) . <span class="string">'.'</span> . mt_rand(<span class="number">0</span>, <span class="number">255</span>),</span><br><span class="line"> ));</span><br><span class="line"> curl_setopt($curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);</span><br><span class="line"> curl_setopt($curl, CURLOPT_PROXY, $proxy_ip);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> curl_setopt($curl, CURLOPT_COOKIEFILE, dirname(<span class="keyword">__FILE__</span>) . <span class="string">'/cookie.txt'</span>);</span><br><span class="line"> curl_setopt($curl, CURLOPT_COOKIEJAR, dirname(<span class="keyword">__FILE__</span>) . <span class="string">'/cookie.txt'</span>);</span><br><span class="line"> curl_setopt($curl, CURLOPT_TIMEOUT, <span class="number">3</span>); <span class="comment">// 设置超时限制防止死循环</span></span><br><span class="line"> <span class="comment">// $response_header = curl_getinfo($curl); // 获取返回response报头</span></span><br><span class="line"> $content = curl_exec($curl);</span><br><span class="line"> <span class="keyword">if</span> (strstr($content, <span class="string">'百度一下,你就知道'</span>)) {</span><br><span class="line"> $result[<span class="string">'list'</span>][$i][<span class="string">'status'</span>] = <span class="number">1</span>;</span><br><span class="line"> $result[<span class="string">'succeed_times'</span>] += <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $result[<span class="string">'list'</span>][$i][<span class="string">'status'</span>] = <span class="number">0</span>;</span><br><span class="line"> $result[<span class="string">'defeat_times'</span>] += <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> $e = microtime();</span><br><span class="line"> $result[<span class="string">'total_spen'</span>] += abs($e - $s);</span><br><span class="line"> $result[<span class="string">'list'</span>][$i][<span class="string">'spen'</span>] = abs($e - $s);</span><br><span class="line"> $result[<span class="string">'list'</span>][$i][<span class="string">'content'</span>] = json_encode($content, <span class="keyword">true</span>);</span><br><span class="line"> <span class="comment">// $result['list'][$i]['response_header'] = $response_header;</span></span><br><span class="line"> }</span><br><span class="line"> $result[<span class="string">'precent'</span>] = (number_format($result[<span class="string">'succeed_times'</span>] / $times, <span class="number">4</span>) * <span class="number">100</span>) . <span class="string">'%'</span>;</span><br><span class="line"> $result[<span class="string">'average_spen'</span>] = number_format($result[<span class="string">'total_spen'</span>] / $times, <span class="number">4</span>);</span><br><span class="line"> <span class="keyword">return</span> $result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><p>使用<code>curl_multi_*()</code> 相关函数 并行地处理批处理cURL句柄</p><p>效果如下图:</p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20200111/coupon_result.jpeg" alt="coupon_result"></p></li></ul><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p>在文中已全部引用</p>]]></content>
<summary type="html">
<p>这几天写了两个东西</p>
<ol>
<li>公众号文章转换及处理 </li>
<li>定时领券</li>
</ol>
</summary>
<category term="技术" scheme="http://ajsonx.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
<category term="php 函数 zend" scheme="http://ajsonx.github.io/tags/php-%E5%87%BD%E6%95%B0-zend/"/>
</entry>
<entry>
<title>docker</title>
<link href="http://ajsonx.github.io/2019/12/09/docker/"/>
<id>http://ajsonx.github.io/2019/12/09/docker/</id>
<published>2019-12-09T06:21:50.000Z</published>
<updated>2019-12-09T06:21:50.000Z</updated>
<content type="html"><![CDATA[<p>Docker 简单使用记录</p><ol><li>简明教程</li><li>制作自己的镜像</li><li>深入理解Docker命令</li><li>Dockerfile详解</li></ol><a id="more"></a><hr><h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><p>在一台新机器上部署环境、无需浪费生命</p><h2 id="简明教程"><a href="#简明教程" class="headerlink" title="简明教程"></a>简明教程</h2><h3 id="Step-1"><a href="#Step-1" class="headerlink" title="Step 1"></a>Step 1</h3><p><strong>拉镜像</strong></p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker <span class="built_in">image</span> pull hello-world</span><br></pre></td></tr></table></figure><p>国内有Docker的官方仓库,例如:mysql、php、nginx</p><p>也可以使用阿里云的Docker镜像,搜索 <strong>容器镜像服务</strong>,使用前本地需要先设置访问凭证</p><p>拉取成功后,就可以看到了</p><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker <span class="keyword">image</span> <span class="keyword">ls</span></span><br></pre></td></tr></table></figure><h3 id="Step-2"><a href="#Step-2" class="headerlink" title="Step 2"></a>Step 2</h3><p><strong>生成实例</strong></p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker container <span class="keyword">run</span><span class="bash"> hello-world</span></span><br></pre></td></tr></table></figure><p>该命令具有自动抓取image文件的功能,所以可以省略上一步</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker container <span class="built_in">run</span> -<span class="keyword">it</span> swoole bash</span><br></pre></td></tr></table></figure><p>有些容器提供的是服务,可供在命令行使用,但不会自动终止</p><h3 id="Step-3"><a href="#Step-3" class="headerlink" title="Step 3"></a>Step 3</h3><p><strong>终止|删除容器</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> docker container <span class="built_in">kill</span> containerID</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> docker container rm containerID</span></span><br></pre></td></tr></table></figure><p>此命令并不会删除image文件,因为镜像和容器的概念是不同的</p><p>到目前为止你完全可以使用Docker进行开发、测试、部署自己的代码了</p><p>以下内容会介绍docker的深入使用</p><h2 id="制作自己的镜像"><a href="#制作自己的镜像" class="headerlink" title="制作自己的镜像"></a>制作自己的镜像</h2><h3 id="Step-1-1"><a href="#Step-1-1" class="headerlink" title="Step 1"></a>Step 1</h3><p><strong>编写两个文件</strong></p><ul><li><p><code>.dockerignore</code> 写入你要忽略的文件</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.git</span></span><br><span class="line"><span class="selector-tag">node_modules</span></span><br><span class="line"><span class="selector-tag">npm-debug</span><span class="selector-class">.log</span></span><br></pre></td></tr></table></figure></li><li><p><code>Dockerfile</code> 写入一些配置参数</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> hello-world</span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">3000</span></span><br></pre></td></tr></table></figure><ul><li><p>FROM:以一个镜像为基础,在其上进行定制。是必备的指令,并且必须是第一条。</p><p>特殊的空白镜像:<code>FROM scratch</code> 意味着不以任何镜像为基础,接下来写的指令作为镜像的第一层</p></li><li><p>COPY:复制文件到镜像</p></li><li><p>WORKID:指定工作目录</p></li><li><p>EXPOSE:暴露端口</p></li></ul></li></ul><h3 id="Step-2-1"><a href="#Step-2-1" class="headerlink" title="Step 2"></a>Step 2</h3><p><strong>构建镜像命令</strong> (此外还有多阶段构建)</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker build -t <span class="keyword">my</span>-hello-world .</span><br></pre></td></tr></table></figure><p>参数 <strong>-t</strong> 表示指定image文件名字, 点<code>.</code> 表示当前路径</p><h3 id="Step-3-1"><a href="#Step-3-1" class="headerlink" title="Step 3"></a>Step 3</h3><p><strong>发布镜像</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker login <span class="attribute">--username</span>=xxx (可指定仓库域名)</span><br></pre></td></tr></table></figure><p>打tag</p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker image <span class="keyword">tag</span> <span class="title">[imageName</span>] [username]/[repository]:[<span class="keyword">tag</span>]</span><br></pre></td></tr></table></figure><p>然后push到仓库上</p><figure class="highlight inform7"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker image push <span class="comment">[username]</span>/<span class="comment">[repository]</span>:<span class="comment">[tag]</span></span><br></pre></td></tr></table></figure><h2 id="深入理解Docker命令"><a href="#深入理解Docker命令" class="headerlink" title="深入理解Docker命令"></a>深入理解Docker命令</h2><h3 id="Commit"><a href="#Commit" class="headerlink" title="Commit"></a>Commit</h3><p>在解释<code>commit</code>的作用前,先看一段话:</p><blockquote><p>镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层。</p></blockquote><p>当我们想修改官方nginx容器的主页内容时,进入已经启动的容器,修改nginx的index.html。</p><p>然后:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]</span><br><span class="line">docker commit \</span><br><span class="line"> --author <span class="string">"ajsonx<ajsonx@gmail.com>"</span> \</span><br><span class="line"> --message <span class="string">"修改了默认网页"</span> \</span><br><span class="line"> webserver \</span><br><span class="line"> nginx:v2</span><br></pre></td></tr></table></figure><p>重要的两段话:</p><blockquote><p> <code>docker commit</code> 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 <code>docker commit</code> 定制镜像,定制镜像应该使用 <code>Dockerfile</code> 来完成。如果你想要定制镜像请查看下一小节。</p><p> 使用 <code>docker commit</code> 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 <strong>黑箱镜像</strong>,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 <code>docker diff</code> 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。</p></blockquote><p>查看镜像内的历史记录:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> docker <span class="built_in">history</span> nginx:v2</span></span><br></pre></td></tr></table></figure><h3 id="Container"><a href="#Container" class="headerlink" title="Container"></a>Container</h3><p>Container作为操作容器的入口,有很多重要的命令,以下一一来介绍</p><h4 id="run"><a href="#run" class="headerlink" title="run"></a>run</h4><p>run后面可带的参数非常多,也十分常用,需要理解和记忆</p><blockquote><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">></span><span class="bash"> $ docker container run \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> -d \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> -p 127.0.0.2:8080:80 \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> --rm \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> --name wordpress \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> --env WORDPRESS_DB_PASSWORD=123456 \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> --link wordpressdb:mysql \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> --volume <span class="string">"<span class="variable">$PWD</span>/wordpress"</span>:/var/www/html \</span></span><br><span class="line"><span class="meta">></span><span class="bash"> wordpress</span></span><br><span class="line"><span class="meta">></span><span class="bash"> </span></span><br><span class="line"><span class="meta">></span></span><br></pre></td></tr></table></figure></blockquote><blockquote><ul><li><strong>-d:</strong> 后台运行容器,并返回容器ID;</li><li><strong>-i:</strong> 以交互模式运行容器,通常与 -t 同时使用;</li><li><strong>-p:</strong> 指定端口映射,格式为:<strong>主机(宿主)端口:容器端口</strong></li><li><strong>–rm:</strong> 停止运行后该容器文件会被自动删除</li><li><strong>–volume , -v:</strong> 绑定一个卷</li><li><strong>–link=[]:</strong> 添加链接到另一个容器;</li><li><strong>–name=”nginx-lb”:</strong> 为容器指定一个名称;</li><li><strong>–env MYSQL_ROOT_PASSWORD=123456:</strong>向容器进程传入一个环境变量<code>MYSQL_ROOT_PASSWORD</code>,该变量会被用作 MySQL 的根密码。</li></ul></blockquote><h4 id="star"><a href="#star" class="headerlink" title="star"></a>star</h4><p>启动容器服务,启动已经生成的容器文件,非run命令的创建</p><blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">> $ bash container stop [containerID]</span><br><span class="line">></span><br></pre></td></tr></table></figure></blockquote><h4 id="stop"><a href="#stop" class="headerlink" title="stop"></a>stop</h4><blockquote><p>前面的<code>docker container kill</code>命令终止容器运行,相当于向容器里面的主进程发出 SIGKILL 信号。而<code>docker container stop</code>命令也是用来终止容器运行,相当于向容器里面的主进程发出 SIGTERM 信号,然后过一段时间再发出 SIGKILL 信号。</p></blockquote><blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">> $ bash container stop [containerID]</span><br><span class="line">></span><br></pre></td></tr></table></figure></blockquote><blockquote><p>这两个信号的差别是,应用程序收到 SIGTERM 信号以后,可以自行进行收尾清理工作,但也可以不理会这个信号。如果收到 SIGKILL 信号,就会强行立即终止,那些正在进行中的操作会全部丢失。</p></blockquote><h4 id="logs"><a href="#logs" class="headerlink" title="logs"></a>logs</h4><blockquote><p><code>docker container logs</code>命令用来查看 docker 容器的输出,即容器里面 Shell 的标准输出。如果<code>docker run</code>命令运行容器的时候,没有使用<code>-it</code>参数,就要用这个命令查看输出。</p></blockquote><blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">> $ docker container logs [containerID]</span><br><span class="line">></span><br></pre></td></tr></table></figure></blockquote><h4 id="exec"><a href="#exec" class="headerlink" title="exec"></a>exec</h4><blockquote><p><code>docker container exec</code>命令用于进入一个正在运行的 docker 容器。如果<code>docker run</code>命令运行容器的时候,没有使用<code>-it</code>参数,就要用这个命令进入容器。一旦进入了容器,就可以在容器的 Shell 执行命令了。</p></blockquote><h4 id="cp"><a href="#cp" class="headerlink" title="cp"></a>cp</h4><blockquote><p><code>docker container cp</code>命令用于从正在运行的 Docker 容器里面,将文件拷贝到本机。下面是拷贝到当前目录的写法。</p><figure class="highlight inform7"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">> $ docker <span class="keyword">container</span> cp <span class="comment">[containID]</span>:<span class="comment">[/path/to/file]</span> .</span><br><span class="line">></span><br></pre></td></tr></table></figure></blockquote><p><strong>以上Container详解引用自阮一峰老师,文末有他的文章链接,更加完整</strong></p><h2 id="Dockerfile详解"><a href="#Dockerfile详解" class="headerlink" title="Dockerfile详解"></a>Dockerfile详解</h2><h4 id="指令"><a href="#指令" class="headerlink" title="指令"></a>指令</h4><ul><li>todo</li></ul><h4 id="多阶段构建"><a href="#多阶段构建" class="headerlink" title="多阶段构建"></a>多阶段构建</h4><ul><li>todo</li></ul><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><ul><li><a href="https://yeasy.gitbooks.io/docker_practice/" target="_blank" rel="noopener">Docker–从入门到实践</a></li><li><a href="http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html" target="_blank" rel="noopener">阮一峰-Docker入门教程</a></li><li><a href="http://www.ruanyifeng.com/blog/2018/02/docker-wordpress-tutorial.html" target="_blank" rel="noopener">阮一峰-Docker 微服务教程</a></li></ul>]]></content>
<summary type="html">
<p>Docker 简单使用记录</p>
<ol>
<li>简明教程</li>
<li>制作自己的镜像</li>
<li>深入理解Docker命令</li>
<li>Dockerfile详解</li>
</ol>
</summary>
<category term="容器" scheme="http://ajsonx.github.io/categories/%E5%AE%B9%E5%99%A8/"/>
<category term="docker" scheme="http://ajsonx.github.io/tags/docker/"/>
</entry>
<entry>
<title>Hackintosh</title>
<link href="http://ajsonx.github.io/2019/12/02/Hackintosh/"/>
<id>http://ajsonx.github.io/2019/12/02/Hackintosh/</id>
<published>2019-12-02T01:49:13.000Z</published>
<updated>2020-04-22T01:54:29.910Z</updated>
<content type="html"><![CDATA[<p>折腾自己的黑苹果也有小半年了,总结下吧</p><a id="more"></a><hr><h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><ul><li>学长在群里提了一下黑苹果、很好奇</li><li>系统顺畅、更接近linux</li><li>不玩游戏,只玩玩炉石</li><li>装B需要</li></ul><h2 id="初始概念"><a href="#初始概念" class="headerlink" title="初始概念"></a>初始概念</h2><blockquote><p> 当时急着快速装好,只会单纯的按教程操作。遇到问题就百度。这也是最开始埋下的一个坑,今年花了太多时间去填。特别是工作之后,时间变得更少的情况下。</p></blockquote><ul><li><p>DMG镜像</p><p>根据mac原始的系统制作出来的适合Intel主板系列安装的系统镜像文件</p></li><li><p>CLOVER</p><p>DMG镜像中包含了所有的资源、驱动文件,CLOVER负责选择部分的适合本机机型的驱动启动系统</p></li></ul><p> 没错,就这么简单。只要你运气好,一次性就可以成功</p><p> <a href="https://blog.daliansky.net/macOS-Catalina-10.15.1-19B88-Release-version-with-Clover-5098-original-image-Double-EFI-Version.html" target="_blank" rel="noopener">黑果小兵的教程传送门</a></p><h2 id="补坑之路"><a href="#补坑之路" class="headerlink" title="补坑之路"></a>补坑之路</h2><blockquote><p>补坑之路难于上青天,一定要找一模一样的Clover文件,如果你有兴趣的话,可以自己制作DSDT</p></blockquote><p>我的笔记本型号是神舟K610Di5-D3,但由于我更换了CPU为i7-4710MQ,因此我的显卡驱动是个严重的问题(留下了没技术的泪水)</p><ul><li>wifi驱动 (直接kext untity重建即可)</li><li>蓝牙驱动(购买了一个bcm-usb蓝牙驱动)</li><li>亮度调节 (Kext untity 重建) </li><li>显存2048mb (Config打入补丁)</li><li>HDMI输出 (尝试过各种办法,最后更换了大佬的Clover)</li></ul><p> 系列教程都可以远景、黑苹果乐园、Google上找到,如果你需要这些账号,可以联系我,无偿提供账号密码。如果你和我的配置一样,可以使用我github-star的仓库的那两个Clover,亲测有效。</p><h2 id="进阶知识"><a href="#进阶知识" class="headerlink" title="进阶知识"></a>进阶知识</h2><ul><li>Clover使用</li><li>工具Clover Configurator的使用,可用于注入EDID、打驱动补丁、添加引导参数、一些基础问题</li><li>ig-platform-ID以及对应的不同平台架构</li><li>显存修改、分辨率修改</li><li>DSDT编写、iasl语法</li></ul><h2 id="最终结果"><a href="#最终结果" class="headerlink" title="最终结果"></a>最终结果</h2><blockquote><p>前几天买了1代妙控板、HDMI转VGA的线,终于爽到了,黑苹果完美!</p></blockquote><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20191208/main.png" alt="系统信息"></p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20191208/display.png" alt="显示器信息"></p><blockquote><p>被我折腾的主板,已经加了一根铜管、两个散热片、mSATA-SSD-120G</p></blockquote><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20191208/mainBoard.jpeg" alt="主板硬件"></p><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20191208/magicTrackpad.jpeg" alt="妙控板一代"></p><blockquote><p>在闲鱼上收二手真是太快乐啦</p></blockquote><h2 id="成本"><a href="#成本" class="headerlink" title="成本"></a>成本</h2><ul><li>2015年—笔记本—3289.00</li><li>2015年—金士顿4GDDR3—169.00(2020年69包顺丰卖出了)</li><li>2017年—120GB-SSD—284.00</li><li>2018年—i7-4710MQ-CPU—580.80(实际只花了230左右,因为把i5卖了)</li><li>2018年—改造耗材—100.00</li><li>2018年—金士顿8GDDR3L—288.00(实际也只花了188,一条4G卖了)</li><li>2018年—三星860EVO-500GB–450.00</li><li>2019年—绿联HDMI转接线—32.00</li><li>2019年—妙控板一代—350.00</li><li>2020年—金士顿8GDDR3L—188(没想到一年跌了这么多)</li><li>前前后后无数的时间、熬夜通宵、精气神</li></ul><p>总计RMB:<strong>5730.8</strong>(5280.8)</p><p>换了2*8G双通道后快了很多,打开PHPStorm基本是秒开</p><p>我觉得值就值吧。已经服役4年有余,希望能再用个5年!主板撑住。</p><p>下一步计划</p><ul><li>做一个水冷、彻底解决散热问题,目前温度50度左右。</li></ul><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ul><li><p>Mac的迁移助理是将整个系统复制迁移过去。</p><p>包括系统驱动文件什么的。(10.13.6的迁移到10.14.6,会导致系统文件(驱动等)不一致,无法开机,无限风火轮,无限菊花等。)</p></li><li><p>可以直接通过备份来安装一个完整的mac系统,前提是机型一致</p></li><li><p><a href="https://blog.daliansky.net/Common-problems-and-solutions-in-macOS-Mojave-10.14-installation.html" target="_blank" rel="noopener">黑果小兵-安装常见注意事项</a></p></li><li><p>多看黑果小兵的教程,当你发现跟着教程无法解决问题时,去找原理。</p></li></ul>]]></content>
<summary type="html">
黑苹果折腾之路
</summary>
<category term="黑苹果" scheme="http://ajsonx.github.io/categories/%E9%BB%91%E8%8B%B9%E6%9E%9C/"/>
<category term="黑苹果" scheme="http://ajsonx.github.io/tags/%E9%BB%91%E8%8B%B9%E6%9E%9C/"/>
</entry>
<entry>
<title>HTTP代理的知识</title>
<link href="http://ajsonx.github.io/2019/10/20/http-proxy/"/>
<id>http://ajsonx.github.io/2019/10/20/http-proxy/</id>
<published>2019-10-20T06:47:22.000Z</published>
<updated>2019-10-20T06:47:22.000Z</updated>
<content type="html"><![CDATA[<p>因为公司有上网行为限制,部署在网络层(路由器),小小的看了点资料,了解原理。</p><ol><li>OSI</li><li>http代理</li><li>socks协议</li></ol><a id="more"></a><hr><h1 id="HTTP代理"><a href="#HTTP代理" class="headerlink" title="HTTP代理"></a>HTTP代理</h1><h2 id="OSI七层模型"><a href="#OSI七层模型" class="headerlink" title="OSI七层模型"></a>OSI七层模型</h2><ol><li><strong>应用层</strong> :SMTP FTP HTTP</li><li><strong>表示层</strong></li><li><strong>会话层</strong></li><li><strong>传输层</strong> :TCP UDP 网关</li><li><strong>网络层</strong>:IP 路由器</li><li><strong>数据链路层</strong>: 以太网 网桥</li><li><strong>物理层</strong>:转发器 </li></ol><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20191021/osi-seven.png" alt="OSI"></p><h2 id="普通代理"><a href="#普通代理" class="headerlink" title="普通代理"></a>普通代理</h2><blockquote><p>HTTP 客户端向代理发送请求报文,代理服务器需要正确地处理请求和连接(例如正确处理 Connection: keep-alive),同时向服务器发送请求,并将收到的响应转发给客户端。</p></blockquote><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20191021/web-proxy.png" alt="web-proxy"></p><h3 id="正向代理"><a href="#正向代理" class="headerlink" title="正向代理"></a>正向代理</h3><blockquote><p>正向代理:是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。</p><p>正向代理的典型用途是为在防火墙内的局域网客户端提供访问Internet的途径。</p></blockquote><h3 id="反向代理"><a href="#反向代理" class="headerlink" title="反向代理"></a>反向代理</h3><blockquote><p>访问 A 网站时,实际上访问的是代理,代理收到请求报文后,再向真正提供服务的服务器发起请求,并将响应转发给浏览器。这种情况一般被称之为反向代理,它可以用来隐藏服务器 IP 及端口。一般使用反向代理后,需要通过修改 DNS 让域名解析到代理服务器 IP,这时浏览器无法察觉到真正服务器</p></blockquote><p> 和<a href="https://baike.baidu.com/item/反向代理" target="_blank" rel="noopener">反向代理</a>不同之处在于,典型的正向代理是一种最终用户知道并主动使用的代理方式。例如Chrome浏览器中安装了switchysharp以后,通过switchysharp方便地进行代理转发服务。而为此用户必须要提前在switchysharp中做好设置才能达到相应的效果。</p><h2 id="隧道代理"><a href="#隧道代理" class="headerlink" title="隧道代理"></a>隧道代理</h2><blockquote><p>HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发。</p></blockquote><p><img src="https://raw.githubusercontent.com/ajsonx/blog-IMG/master/20191021/tunnel-proxy.png" alt="tunnel"></p><p> 通过代理访问 A 网站,浏览器首先通过 CONNECT 请求,让代理创建一条到 A 网站的 TCP 连接;一旦 TCP 连接建好,代理无脑转发后续流量即可。所以这种代理,理论上适用于任意基于 TCP 的应用层协议,HTTPS 网站使用的 TLS 协议当然也可以。这也是这种代理为什么被称为隧道的原因。对于 HTTPS 来说,客户端透过代理直接跟服务端进行 TLS 握手协商密钥,所以依然是安全的。</p><p>对于 CONNECT 请求来说,只是用来让代理创建 TCP 连接,所以只需要提供服务器域名及端口即可,并不需要具体的资源路径。代理收到这样的请求后,需要与服务端建立 TCP 连接,并响应给浏览器这样一个 HTTP 报文:</p><h2 id="Sock5协议"><a href="#Sock5协议" class="headerlink" title="Sock5协议"></a>Sock5协议</h2><p>简单来说就是一个</p><ul><li>比HTTP协议出生还早</li><li>基于TCP的代理协议</li><li>仅仅转发TCP/UDP连接</li><li>ping,ssh等不可以被代理</li><li>socks工作在会话层,HTTP在应用层</li></ul><p>SOCKS5 协议并不负责代理服务器的数据传输环节,此协议只是在 C/S 两端真实交互之间,建立起一条从客户端到代理服务器的授信连接。</p><blockquote><p>TLS,TLS 又名 SSL,是针对数据流的安全传输协议。简单来说,一个 TCP 链接,把其中原本要传输的数据,按照 TLS 协议去进行加密传输,那么即可保证其中传输的数据安全。这个安全至少体现在以下几个方面:</p><p>明文的 HTTP 套上一层 TLS,也就变成了 HTTPS,SOCKS5 套上 TLS,就变成了 SOCKS5-TLS。TLS 协议是整个互联网安全的基石,几乎所有需要安全传输的地方都使用了 TLS,如银行、政府等等。</p></blockquote><h2 id="与HTTP代理的区别"><a href="#与HTTP代理的区别" class="headerlink" title="与HTTP代理的区别"></a>与HTTP代理的区别</h2><blockquote><p><em>SOCKS</em>工作在比HTTP代理更低的层次:SOCKS使用握手协议来通知代理软件其客户端试图进行的连接SOCKS,然后尽可能透明地进行操作,而常规代理可能会解释和重写报头(例如,使用另一种底层协议,例如<a href="https://zh.wikipedia.org/wiki/文件传输协议" target="_blank" rel="noopener">FTP</a>;然而,HTTP代理只是将HTTP请求转发到所需的HTTP服务器)。虽然HTTP代理有不同的使用模式,<a href="https://zh.wikipedia.org/wiki/超文本传输协议" target="_blank" rel="noopener">CONNECT</a>方法允许转发TCP连接;然而,SOCKS代理还可以转发<a href="https://zh.wikipedia.org/wiki/用户数据报协议" target="_blank" rel="noopener">UDP</a>流量和<a href="https://zh.wikipedia.org/wiki/反向代理" target="_blank" rel="noopener">反向代理</a>,而HTTP代理不能。HTTP代理通常更了解HTTP协议,执行更高层次的过滤(虽然通常只用于GET和POST方法,而不用于CONNECT方法)。</p></blockquote><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li><p>代理和VPN不是一个东西</p></li><li><p>用阿里云的服务器部署了ssr,打开端口、限制ip访问后。可以绕过公司的上网限制,正常访问国内的网站。</p><p>使用境外服务器其实会发现用 网易云不太行,很多歌听不了。现有Mac版本的 SS-NG-R8 功能太弱了…</p></li></ul><p>暂时不清楚公司是在网络层做了多具体的行为分析(监控),能拿到多具体的信息…所以上网基本开着全局国内代理,不知道有没有🐦用。</p><p><strong>需求使人类进步~~</strong></p><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="https://imququ.com/post/web-proxy.html" target="_blank" rel="noopener">HTTP 代理原理及实现</a></p><p><a href="https://zh.wikipedia.org/wiki/SOCKS" target="_blank" rel="noopener">WIKI-SOCKS</a></p><p><a href="https://loggerhead.me/posts/shadowsocks-yuan-ma-fen-xi-xie-yi-yu-jie-gou.html" target="_blank" rel="noopener">Shadowsocks 源码分析——协议与结构</a></p><p><a href="https://medium.com/@Blankwonder/%E5%90%84%E7%A7%8D%E5%8A%A0%E5%AF%86%E4%BB%A3%E7%90%86%E5%8D%8F%E8%AE%AE%E7%9A%84%E7%AE%80%E5%8D%95%E5%AF%B9%E6%AF%94-1ed52bf7a803" target="_blank" rel="noopener">各种加密代理协议的简单对比</a></p>]]></content>
<summary type="html">
<p>因为公司有上网行为限制,部署在网络层(路由器),小小的看了点资料,了解原理。</p>
<ol>
<li>OSI</li>
<li>http代理</li>
<li>socks协议</li>
</ol>
</summary>
<category term="计算机网络" scheme="http://ajsonx.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
<category term="HTTP Socks" scheme="http://ajsonx.github.io/tags/HTTP-Socks/"/>
</entry>
<entry>
<title>RabbitMQ</title>
<link href="http://ajsonx.github.io/2019/09/21/rabbitMQ/"/>
<id>http://ajsonx.github.io/2019/09/21/rabbitMQ/</id>
<published>2019-09-21T08:39:35.000Z</published>
<updated>2019-09-21T08:39:35.000Z</updated>
<content type="html"><![CDATA[<p>这周忙于部门的技术分享会,整理了一份消息队列中间件的技术知识文档</p><ol><li>什么是消息队列(MQ)?</li><li>使用MQ的优劣势?</li><li>RabbitMQ构成</li><li>AMQP协议</li><li>深入了解RabbitMQ</li><li>代码<a id="more"></a></li></ol><hr><h1 id="消息队列中间件"><a href="#消息队列中间件" class="headerlink" title="消息队列中间件"></a>消息队列中间件</h1><p>队列:先进先出</p><p>消息队列:把要传输的数据放在队列里</p><h2 id="为什么使用MQ?"><a href="#为什么使用MQ?" class="headerlink" title="为什么使用MQ?"></a>为什么使用MQ?</h2><ul><li><p><strong>应用解耦</strong></p><p>在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?</p><p>一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦,也是可以的,你就需要去考虑在你的项目里,是不是可以运用这个 MQ 去进行系统的解耦。</p><p><strong>总结</strong>:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。</p></li><li><p><strong>异步处理</strong></p><p>再来看一个场景,A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s</p><p>一般要求是每个请求都必须在 <strong>200 ms</strong> 以内完成,对用户几乎是无感知的。</p><p>如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了</p></li><li><p><strong>流量削峰</strong></p><p>每天 0:00 到 12:00,系统风平浪静,每秒并发请求数量就 50 个。结果每次一到 12:00 ~ 13:00 ,每秒并发请求数量暴增,大量的请求涌入 MySQL,可能就直接把 MySQL 给打死了,导致系统崩溃。但是高峰期一过,到了下午的时候,就成了低峰期,对整个系统几乎没有任何的压力</p><p>如果使用 MQ,A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过自己每秒能处理的最大请求数量就 ok,这样下来,哪怕是高峰期的时候,A 系统也绝对不会挂掉。只要高峰期一过,A 系统就会快速将积压的消息给解决掉。</p></li></ul><h2 id="使用MQ的劣势"><a href="#使用MQ的劣势" class="headerlink" title="使用MQ的劣势"></a>使用MQ的劣势</h2><ul><li><p>系统可用性降低</p><p>系统引入的外部依赖越多,越容易挂掉。MQ 一挂,整套系统崩溃,你不就完了?如何保证消息队列的高可用?</p></li><li><p>系统复杂度提高</p><p>怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?</p></li><li><p>一致性问题</p><p>数据写入多个系统时需要保证一致性</p><p>订单系统、如果用户下单支付成功,需要更新订单状态、减少库存、赠送积分。</p></li></ul><h1 id="深入了解RabbitMQ"><a href="#深入了解RabbitMQ" class="headerlink" title="深入了解RabbitMQ"></a>深入了解RabbitMQ</h1><h2 id="AMQP"><a href="#AMQP" class="headerlink" title="AMQP"></a>AMQP</h2><h3 id="AMQP协议"><a href="#AMQP协议" class="headerlink" title="AMQP协议"></a>AMQP协议</h3><ul><li><p>在OSI七层模型中属于<strong>应用层</strong>的协议(类似的还有HTTP、DNS、FTP)</p></li><li><p>一个AMQP服务器类似于邮件服务器,exchange类似于消息传输代理(email里的概念),message queue类似于邮箱。Binding定义了每一个传输代理中的消息路由表,发布者将消息发给特定的传输代理,然后传输代理将这些消息路由到邮箱中,消费者从这些邮箱中取出消息。</p></li><li><p>AMQP术语</p><ul><li><p><strong>Channel(信道):</strong> 在AMQP模型中,我们不需要通过建立太多的TCP连接来实现。假如针对每一个AMQP连接都建立一个TCP连接的话,会占用大量的系统资源。对此,AMQP提供了通道(channel)机制。即,共享一个TCP连接,可创建多个通道。</p><p> 在多线程/进程的应用程序中正确做法是,对于每一个线程/进程,应分别建立一个通道,而不是多个线程/进程之间去共享一个通道。</p></li><li><p><strong>Exchange(交换器):</strong>用于接受、分配消息;可以有好几种模式、相当于邮箱</p></li><li><p><strong>Queue(队列)</strong>:用于存储生产者的消息;</p></li><li><p><strong>RoutingKey(路由键)</strong>:用于把生成者的数据分配到交换器上;</p></li><li><p><strong>BindingKey(绑定键)</strong>:用于把交换器的消息绑定到队列上;</p></li><li><p><strong>Broker</strong>(消息代理):消息代理会接收来自生产者(publishers/producers)生产的消息,并将它们路由(route,可以理解成按指定规则转发)给相应的消费者(consumers)手中。</p></li><li><p><strong>VHOST(虚拟主机)</strong>:为了在一个单独的代理上实现多个隔离的环境(用户、用户组、交换机、队列 等),AMQP 提供了一个虚拟主机(virtual hosts - vhosts)的概念。这跟 <strong>Web servers 虚拟主机</strong>概念非常相似,这为 AMQP 实体提供了完全隔离的环境。当连接被建立的时候,AMQP 客户端来指定使用哪个虚拟主机。</p></li></ul></li></ul><h3 id="Exchange交换机"><a href="#Exchange交换机" class="headerlink" title="Exchange交换机"></a>Exchange交换机</h3><p> 交换机是用来发送消息的 AMQP 实体。</p><p> 交换机拿到一个消息之后将它<strong>路由</strong>给一个或零个队列。</p><p> 它使用哪种路由算法是由<strong>交换机类型</strong>和<strong>绑定(Bindings)规则</strong>所决定的。</p><h4 id="默认交换机"><a href="#默认交换机" class="headerlink" title="默认交换机"></a>默认交换机</h4><p> type:Default</p><p> 自动命名的直交换机</p><h4 id="直连交换机"><a href="#直连交换机" class="headerlink" title="直连交换机"></a>直连交换机</h4><p> type:Direct</p><p> Routing Key==Binding Key,严格匹配</p><h4 id="扇形交换机"><a href="#扇形交换机" class="headerlink" title="扇形交换机"></a>扇形交换机</h4><p> type:Fanout</p><p> 把发送到该 Exchange 的消息路由到所有与它绑定的 Queue 中</p><h4 id="主题交换机"><a href="#主题交换机" class="headerlink" title="主题交换机"></a>主题交换机</h4><p> type:Topic</p><p> Routing Key==Binding Key,模糊匹配</p><h4 id="头交换机"><a href="#头交换机" class="headerlink" title="头交换机"></a>头交换机</h4><p> type:Headers</p><p> 根据发送的消息内容中的 headers 属性进行匹配</p><h3 id="Queue队列"><a href="#Queue队列" class="headerlink" title="Queue队列"></a>Queue队列</h3><ul><li>队列属性 todo</li><li>队列创建 todo</li><li>队列持久化 todo</li></ul><h3 id="Consumer消费者"><a href="#Consumer消费者" class="headerlink" title="Consumer消费者"></a>Consumer消费者</h3><ul><li>将消息投递给应用(push 模型)</li><li>应用主动获取消息(pull 模型)</li></ul><h3 id="消息机制"><a href="#消息机制" class="headerlink" title="消息机制"></a>消息机制</h3><p>1)自动确认模式:当消息代理(broker)将消息发送给应用后立即删除。(使用 AMQP 方法:basic.deliver 或 basic.get-ok)</p><p>2)显式确认模式:待应用(application)发送一个确认回执(acknowledgement)后再删除消息。(使用 AMQP 方法:basic.ack)</p><h2 id="RabbitMQ"><a href="#RabbitMQ" class="headerlink" title="RabbitMQ"></a>RabbitMQ</h2><h1 id="使用MQ可能遇到的各种情况"><a href="#使用MQ可能遇到的各种情况" class="headerlink" title="使用MQ可能遇到的各种情况"></a>使用MQ可能遇到的各种情况</h1><h2 id="消息丢失怎么办?"><a href="#消息丢失怎么办?" class="headerlink" title="消息丢失怎么办?"></a>消息丢失怎么办?</h2><p>发生在哪里,出现问题如何去预防排查解决。</p><ul><li><p>Producer丢失</p><p>就是生产者<strong>发送数据之前</strong>开启 RabbitMQ 事务<code>channel.txSelect</code>,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务<code>channel.txRollback</code>,然后重试发送消息;如果收到了消息,那么可以提交事务<code>channel.txCommit</code>。</p><p>开启 <code>confirm</code> 模式,在生产者那里设置开启 <code>confirm</code> 模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 <code>ack</code> 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 <code>nack</code> 接口,告诉你这个消息接收失败,你可以重试。</p></li><li><p>MQ内存中丢失</p><p><strong>开启 RabbitMQ 的持久化</strong></p><p>注意,哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ 中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的一点点数据丢失。</p><p>所以,持久化可以跟生产者那边的 <code>confirm</code> 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 <code>ack</code> 了,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,生产者收不到 <code>ack</code>,你也是可以自己重发的。</p></li><li><p>Comsumer丢失</p><ul><li>关闭自动ack,开启应用系统发送ack回执。</li></ul></li></ul><h2 id="大量消息积压如何处理?"><a href="#大量消息积压如何处理?" class="headerlink" title="大量消息积压如何处理?"></a>大量消息积压如何处理?</h2><p>仅仅只修复consumer重新消费可能需要几小时</p><ol><li>修复consumer</li><li>建立多个consumer进程去处理queue积压的数据</li><li>恢复原有部署</li></ol><ul><li>讨论:假如出现死循环Producer,也要去考虑是否是生产者的问题。</li></ul><h2 id="消息过期失效?"><a href="#消息过期失效?" class="headerlink" title="消息过期失效?"></a>消息过期失效?</h2><ul><li>RabbitMQ可以设置TTL过期时间</li><li>在用户低峰期将过期的数据查出后重新写入MQ</li></ul><h2 id="如何保证消息的顺序性?"><a href="#如何保证消息的顺序性?" class="headerlink" title="如何保证消息的顺序性?"></a>如何保证消息的顺序性?</h2><p>一个queue,多个consumer</p><ul><li>每个consumer对应一个queue,consumer可以用队列排序</li></ul><h2 id="如何保证消息不被重复消费?即保证数据幂等性"><a href="#如何保证消息不被重复消费?即保证数据幂等性" class="headerlink" title="如何保证消息不被重复消费?即保证数据幂等性"></a>如何保证消息不被重复消费?即保证数据<strong>幂等性</strong></h2><p>结合业务,多个层面控制</p><ul><li>设置每个请求的全局id</li><li>数据库设置唯一键</li><li>先对数据校验再做操作</li></ul><h1 id="会后复习资料"><a href="#会后复习资料" class="headerlink" title="会后复习资料"></a>会后复习资料</h1><ul><li><a href="https://www.jianshu.com/p/4491cba335d1" target="_blank" rel="noopener">消息中间件</a></li><li><a href="https://blog.csdn.net/weixin_37641832/article/details/83270778#Consumer_128" target="_blank" rel="noopener">AMQP</a></li><li><a href="https://www.cnblogs.com/vipstone/p/9275256.html" target="_blank" rel="noopener">RabbitMQ</a></li></ul><h1 id="讨论"><a href="#讨论" class="headerlink" title="讨论"></a>讨论</h1><ul><li><p>要保证库存一致性的时候,即库存商品不会超买超卖,MQ很难做到这一点。MQ不适用于秒杀业务</p></li><li><p>当消费端处于unack时,消费端会锁定队列中前<strong>3(默认值)</strong>个的消息。</p><ul><li><code>$ch->setPrefetchCount($count); //设定最大unacked消息的条数,默认为3;值得注意的是,设置为0时表示无限制</code></li></ul></li><li><p>消息不被重复消费:</p><ul><li>使用uuid保证请求唯一性</li><li>Redis天然的幂等性</li></ul></li><li><p>关于Channel信道:</p><p>在AMQP模型中,我们不需要通过建立太多的TCP连接来实现。假如针对每一个AMQP连接都建立一个TCP连接的话,会占用大量的系统资源。对此,AMQP提供了通道(channel)机制。即,共享一个TCP连接,可创建多个通道。</p></li><li><p>关于vhost虚拟主机:</p><p>为了在一个单独的代理上实现多个隔离的环境(用户、用户组、交换机、队列 等)</p></li><li><p>关于消息丢失的三种情况,生产者发送消息丢失过程中使用confirm模式的解释</p><p><a href="https://www.cnblogs.com/vipstone/p/9350075.html" target="_blank" rel="noopener">RabbitMQ系列四</a></p></li></ul><h1 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h1><p><strong>consumer.php</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line">$conn = <span class="keyword">new</span> AMQPConnection($config);</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> $conn->connect();</span><br><span class="line">} <span class="keyword">catch</span> (\AMQPConnectionException $AMQPConnectionException) {</span><br><span class="line"> <span class="keyword">exit</span>(<span class="string">'connect failed'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建一个channel</span></span><br><span class="line">$ch = <span class="keyword">new</span> AMQPChannel($conn);</span><br><span class="line">$ch.basic_qos(prefetch_count=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建一个交换机</span></span><br><span class="line">$ex = <span class="keyword">new</span> AMQPExchange($ch);</span><br><span class="line"></span><br><span class="line"><span class="comment">//声明路由键</span></span><br><span class="line">$routingKey = <span class="string">'key_1'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//声明交换机名称</span></span><br><span class="line">$exchangeName = <span class="string">'exchange_1'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置交换机名称</span></span><br><span class="line">$ex->setName($exchangeName);</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置交换机类型</span></span><br><span class="line"><span class="comment">//AMQP_EX_TYPE_DIRECT:直连交换机</span></span><br><span class="line"><span class="comment">//AMQP_EX_TYPE_FANOUT:扇形交换机</span></span><br><span class="line"><span class="comment">//AMQP_EX_TYPE_HEADERS:头交换机</span></span><br><span class="line"><span class="comment">//AMQP_EX_TYPE_TOPIC:主题交换机</span></span><br><span class="line">$ex->setType(AMQP_EX_TYPE_DIRECT);</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置交换机持久</span></span><br><span class="line">$ex->setFlags(AMQP_DURABLE);</span><br><span class="line"></span><br><span class="line"><span class="comment">//声明交换机</span></span><br><span class="line">$ex->declareExchange();</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建一个消息队列</span></span><br><span class="line">$q = <span class="keyword">new</span> AMQPQueue($ch);</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置队列名称</span></span><br><span class="line">$q->setName(<span class="string">'queue_1'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置队列持久</span></span><br><span class="line">$q->setFlags(AMQP_DURABLE);</span><br><span class="line"></span><br><span class="line"><span class="comment">//声明消息队列</span></span><br><span class="line">$q->declareQueue();</span><br><span class="line"></span><br><span class="line"><span class="comment">//交换机和队列通过$routingKey进行绑定</span></span><br><span class="line">$q->bind($ex->getName(), $routingKey);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//接收消息并进行处理的回调方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">receive</span><span class="params">($envelope, $queue)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">//休眠两秒,</span></span><br><span class="line"> sleep(<span class="number">2</span>);</span><br><span class="line"> <span class="comment">//echo消息内容</span></span><br><span class="line"> <span class="keyword">echo</span> $envelope->getBody() . <span class="string">"\n"</span>;</span><br><span class="line"> <span class="comment">//显式确认,队列收到消费者显式确认后,会删除该消息</span></span><br><span class="line"> <span class="keyword">echo</span> <span class="string">'without ack'</span>;</span><br><span class="line"> <span class="comment">//$queue->ack($envelope->getDeliveryTag());</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置消息队列消费者回调方法,并进行阻塞</span></span><br><span class="line">$q->consume(<span class="string">"receive"</span>);</span><br><span class="line"><span class="comment">//$q->consume("receive", AMQP_AUTOACK);//隐式确认,不推荐</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>这周忙于部门的技术分享会,整理了一份消息队列中间件的技术知识文档</p>
<ol>
<li>什么是消息队列(MQ)?</li>
<li>使用MQ的优劣势?</li>
<li>RabbitMQ构成</li>
<li>AMQP协议</li>
<li>深入了解RabbitMQ</li>
<li>代码
</summary>
<category term="消息队列" scheme="http://ajsonx.github.io/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"/>
<category term="RabbitMQ" scheme="http://ajsonx.github.io/tags/RabbitMQ/"/>
</entry>
<entry>
<title>新裤子乐队--没有理想的人不伤心</title>
<link href="http://ajsonx.github.io/2019/08/25/new-paints/"/>
<id>http://ajsonx.github.io/2019/08/25/new-paints/</id>
<published>2019-08-25T07:22:42.000Z</published>
<updated>2019-08-25T07:22:42.000Z</updated>
<content type="html"><![CDATA[<h1 id="没有理想的人不伤心"><a href="#没有理想的人不伤心" class="headerlink" title="没有理想的人不伤心"></a>没有理想的人不伤心</h1><a id="more"></a><p>歌词:</p><blockquote><p>那些为了理想的战斗<br> 也不过是为了钱</p><p>还没年轻就变得苍老<br> 这一生无解<br> 没有我的空间</p><p>我不要在失败孤独中死去<br> 我不要一直活在地下里<br> 物质的骗局<br> 匆匆的蚂蚁</p></blockquote><h2 id="感情"><a href="#感情" class="headerlink" title="感情"></a>感情</h2><h3 id="现状:"><a href="#现状:" class="headerlink" title="现状:"></a>现状:</h3><p>没有我的空间</p><h3 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h3><p>没有我的空间</p><h2 id="工作"><a href="#工作" class="headerlink" title="工作"></a>工作</h2><h3 id="现状"><a href="#现状" class="headerlink" title="现状"></a>现状</h3><p>没有我的空间</p><h3 id="未来-1"><a href="#未来-1" class="headerlink" title="未来"></a>未来</h3><p>没有我的空间</p>]]></content>
<summary type="html">
<h1 id="没有理想的人不伤心"><a href="#没有理想的人不伤心" class="headerlink" title="没有理想的人不伤心"></a>没有理想的人不伤心</h1>
</summary>
<category term="音乐 情感" scheme="http://ajsonx.github.io/categories/%E9%9F%B3%E4%B9%90-%E6%83%85%E6%84%9F/"/>
<category term="life" scheme="http://ajsonx.github.io/tags/life/"/>
</entry>
<entry>
<title>Learning PHP设计模式</title>
<link href="http://ajsonx.github.io/2019/08/06/book-LearnningPhpDesignPatterns/"/>
<id>http://ajsonx.github.io/2019/08/06/book-LearnningPhpDesignPatterns/</id>
<published>2019-08-06T08:59:28.000Z</published>
<updated>2019-08-06T09:17:25.000Z</updated>
<content type="html"><![CDATA[<p>读书笔记 <img src="../images/learning_php_design_patterns.jpg" width="200px" height="300px" align="center"> </p><a id="more"></a><h1 id="Learning-PHP-设计模式"><a href="#Learning-PHP-设计模式" class="headerlink" title="Learning PHP 设计模式"></a>Learning PHP 设计模式</h1><ul><li><em>中国电力出版社</em> </li><li><em>翻译有点烂,一搜书评 同感的人不在少数。</em></li><li><em>不太严谨,本书74和75页的插图是一样的。</em></li><li><em>本书实例很多,代码基本占了书的篇幅一半。</em></li></ul><p>作为设计模式入门书籍看吧。之前了解了很多概念,跟着书操作一遍。</p><p>电力出版社很多书都翻译的不好,还是人民邮电出版社比较好。拔草。</p><p>本书讲了非常多的概念,比较简单易懂(有基础)</p><p>以下读书笔记 不保证包含全书知识点 <strong>尽量使用一句话的形式来概括一个概念,大部分摘选书中的语言,少部分使用自己的理解。</strong></p><p>如果需要代码辅以说明会给出链接。以下按章节记录。</p><p>代码使用sublime编写,没有代码提示,部分简答易懂的代码没有运行过,可能会出现错误,谅解。</p><h2 id="第一章·PHP与面向对象编程"><a href="#第一章·PHP与面向对象编程" class="headerlink" title="第一章·PHP与面向对象编程"></a>第一章·PHP与面向对象编程</h2><blockquote><p>不要期望工作适合你的能力,而要祈望自己的能力去适应工作。</p></blockquote><ul><li><p>模块化:一个问题分解为小的问题 <!--这个解释也可以说是分治、动态规划、二分...这说明了解决问题的方法在不同领域虽然有不同的叫法,但是模式是一样的。注重培养解决问题的思维。--></p></li><li><p>单一职责原则:一个类应当只有一个原则</p></li><li><p>Client类作为请求者</p><p>一开始也不清楚作者or翻译想要表达的意思,结合代码发现其实就是:(使用Client类调用具体的功能类)[http://]</p></li><li><p>OOP和设计模式的主要作用之一就是能够改变单个模块而不会破坏整个程序</p></li><li><p>算法处理的是操作速度,而设计模式解决的是开发速度</p></li><li><p>即时回报还是长期回报取决于设计的代价</p></li></ul><h2 id="第二章·OOP基本概念"><a href="#第二章·OOP基本概念" class="headerlink" title="第二章·OOP基本概念"></a>第二章·OOP基本概念</h2><h3 id="抽象"><a href="#抽象" class="headerlink" title="抽象"></a>抽象</h3><ul><li><p>抽象abstract:指示一个对象的基本特征,使它与所有其他对象区分开,从而从查看者的角度提供了清晰定义的概念边界。<br>在某种程度上,所有类都是对数据的一组操作的抽象<br>抽象是用来处理复杂性的主要工具,问题越复杂,越需要抽象来解决。</p></li><li><p>抽象类特点:</p><p>1.抽象类不能实例化,只能继承</p><p>2.一个类至少有一个抽象方法,必然是一个抽象类</p></li></ul><p>3.与抽象方法不同,不强制使用这些抽象属性</p><figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">4.</span>子类必须实现父类的抽象方法</span><br><span class="line"></span><br><span class="line"><span class="number">5.</span>抽象类中可以有普通方法</span><br></pre></td></tr></table></figure><h3 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h3><ul><li><p>接口interface:</p><p>1.接口中不能包含变量,可以包含常量 使用作用域解析符(::)</p><p>2.接口中不能包含具体方法和属性 ???try</p><p>3.</p></li><li><p>Citrus 柑橘类水果</p></li></ul><h3 id="封装"><a href="#封装" class="headerlink" title="封装"></a>封装</h3><ul><li>封装就是划分一个抽象的诸多元素的过程,封装的作用就是将抽象的契约接口与其实现分离</li><li><a href>通过可见性保护封装</a> try</li><li>获取和设置 Getters Setters:允许公开访问获取方法和设置方法只会破坏封装</li></ul><blockquote><p>要修改一个过程,只需要重新组织消息序列,而不是改变一个过程 ——OOP</p></blockquote><h3 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h3><ul><li>继承抽象类,注重使用浅继承,只有一层子类,减少破坏</li></ul><h3 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h3><ul><li>多态的真正价值:可以调用相同接口的对象来完成不同的工作</li><li>接口(父类)的不同实现方式</li><li>php7中可以声明返回类型</li><li>设计模式中已经充分内建有多态性</li></ul><h2 id="第三章·基本设计模式概念"><a href="#第三章·基本设计模式概念" class="headerlink" title="第三章·基本设计模式概念"></a>第三章·基本设计模式概念</h2><h3 id="MVC模式"><a href="#MVC模式" class="headerlink" title="MVC模式"></a>MVC模式</h3><ul><li>模型-视图-控制器</li><li>MVC的<u><em>表现部分</em></u>有两个元素:视图和控制器。视图是一个窗口,模型提供数据,控制器提供打开和关闭数据,并发送到视图。</li></ul><h3 id="设计模式基本原则"><a href="#设计模式基本原则" class="headerlink" title="设计模式基本原则"></a>设计模式基本原则</h3><p>这里写的也是让我一脸懵逼,只写了两个基本原则。</p><ol><li></li><li>按接口而不是按实现来编程</li></ol>]]></content>
<summary type="html">
<p>读书笔记 <img src="../images/learning_php_design_patterns.jpg" width="200px" height="300px" align="center"> </p>
</summary>
<category term="读书笔记" scheme="http://ajsonx.github.io/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="php 设计模式" scheme="http://ajsonx.github.io/tags/php-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title>《高性能PHP7》</title>
<link href="http://ajsonx.github.io/2019/08/03/book-PHP7HighPerformance/"/>
<id>http://ajsonx.github.io/2019/08/03/book-PHP7HighPerformance/</id>
<published>2019-08-03T08:20:52.000Z</published>
<updated>2019-08-06T09:21:56.000Z</updated>
<content type="html"><![CDATA[<p>读书笔记 <img src="../images/php7_high-performance.jpg" width="400px" height="500px" align="center"> </p><a id="more"></a><h1 id="PHP7新特性·第二章"><a href="#PHP7新特性·第二章" class="headerlink" title="PHP7新特性·第二章"></a>PHP7新特性·第二章</h1><h2 id="类型声明"><a href="#类型声明" class="headerlink" title="类型声明"></a>类型声明</h2><h3 id="形参类型声明"><a href="#形参类型声明" class="headerlink" title="形参类型声明"></a>形参类型声明</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">age</span><span class="params">(int $age)</span></span>{ </span><br><span class="line"> <span class="keyword">return</span> $age;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">echo</span> age(<span class="number">1.1</span>);</span><br></pre></td></tr></table></figure><blockquote><p>按形参声明类型返回,输出 1</p></blockquote><h3 id="返回类型声明"><a href="#返回类型声明" class="headerlink" title="返回类型声明"></a>返回类型声明</h3><p><code>function age(int age) : int</code></p><blockquote><p>让函数、方法的形参与返回值有所预期,避免出现不必要的数据传递,代码更清晰,可读性更强</p></blockquote><h3 id="严格类型检查模式"><a href="#严格类型检查模式" class="headerlink" title="严格类型检查模式"></a>严格类型检查模式</h3><p><code>declare(strict_type = 1)</code> </p><blockquote><p>此模式下不遵守类型返回会引起 Fatal Error</p></blockquote><h2 id="命名空间与use关键字批量声明"><a href="#命名空间与use关键字批量声明" class="headerlink" title="命名空间与use关键字批量声明"></a>命名空间与use关键字批量声明</h2><ul><li><p>非混合模式</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> Publishers\Packt\{Book,Ebook,Video,Presentation};</span><br><span class="line"><span class="keyword">use</span> <span class="keyword">function</span> Publishers\Packt\{getBook,saveBook};</span><br><span class="line"><span class="keyword">use</span> const Publishers\Packt\{<span class="keyword">COUNT</span>,<span class="keyword">KEY</span>};</span><br></pre></td></tr></table></figure></li><li><p>混合模式</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> Publishers\Packt\{Book,Ebook,getBook,<span class="keyword">COUNT</span>};</span><br></pre></td></tr></table></figure></li><li><p>复合模式</p><figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use Publishers<span class="symbol">\P</span>ackt<span class="symbol">\{</span>Paper<span class="symbol">\B</span>ook,Electronic<span class="symbol">\E</span>book,Media<span class="symbol">\V</span>ideo};</span><br></pre></td></tr></table></figure></li></ul><h2 id="匿名类"><a href="#匿名类" class="headerlink" title="匿名类"></a>匿名类</h2><ul><li><p>参数可以直接设置再匿名类中</p></li><li><p>匿名类可以继承父类,父类中<code>public</code>,<code>protected</code>属性依然有效</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$number = <span class="keyword">new</span> <span class="class"><span class="keyword">class</span>(<span class="params">5</span>) <span class="keyword">extends</span> <span class="title">packt</span></span>{</span><br><span class="line"> public function __construct(float $number){ </span><br><span class="line"> parent::__constuct(); $<span class="keyword">this</span>->number = $number; </span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>同样可以继承接口<code>implement</code></p></li></ul><ul><li><p>匿名类可以嵌套在一个类中使用 ⭐⭐(有点厉害)</p><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Math</span></span>{</span><br><span class="line"><span class="keyword">public</span> $first_number = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">add</span><span class="params">()</span> : float</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> $<span class="keyword">this</span>->first_number + <span class="number">10</span> ;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">multiply_sum</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="class"><span class="keyword">class</span>() <span class="keyword">extends</span> <span class="title">Math</span> //这里继承了本身</span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">multiply</span><span class="params">(float $num)</span> : float</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> $<span class="keyword">this</span>->add() * $num;</span><br><span class="line">}</span><br><span class="line">};</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">echo <span class="keyword">new</span> Math()->multiply_sum()->multiply(<span class="number">2</span>);</span><br></pre></td></tr></table></figure><p>调用multiply_sum方法生成一个由匿名类创建的对象执行multiply方法</p><p>Math类可以被外部类调用,匿名类可以被内部类调用,但内部类不需要调用外部类,直接就可以用。</p><p>在这个例子中,为了证明内部类可以通过继承外部类的方式来调用外部类中被声明为保护权限的方法</p></li></ul><h2 id="Throwable接口"><a href="#Throwable接口" class="headerlink" title="Throwable接口"></a>Throwable接口</h2><ul><li><p>PHP7之前 异常可以被截获,而错误不能。</p></li><li><p>PHP7开始可以,但为了更好的截获诸多的错误,提供了<code>Throwable</code>接口</p></li><li><p>现在大多数的错误会抛出一个<code>Error</code>实例,我们自己写的php类是不能继承<code>Throwable</code>接口的,如果希望继承<code>Throwable</code>,需要继承某个异常类。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AppException</span> <span class="keyword">extends</span> \<span class="title">Exception</span> <span class="keyword">implements</span> \<span class="title">Throwable</span></span>{</span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">exceptionHandler</span><span class="params">()</span></span>;</span><br><span class="line">}</span><br><span class="line">error_reporting(E_ALL); <span class="comment">//设置错误的报告级别,返回给客户端</span></span><br><span class="line"></span><br><span class="line">set_error_handler(<span class="keyword">array</span>($appException, <span class="string">'_errorHandler'</span>)); <span class="comment">//自定义 error 处理逻辑。但是以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING</span></span><br><span class="line"></span><br><span class="line"> set_exception_handler(<span class="keyword">array</span>($appException, <span class="string">'_exceptionHandler'</span>));<span class="comment">//当发现某个 exception 没有被 catch 的时候,就会调用这个函数,不管这个自定义的异常处理逻辑运行状况如何,在异常处理完之后,程序一定会被中断</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>{}</span><br><span class="line"><span class="keyword">catch</span>(Throwable $t){}</span><br></pre></td></tr></table></figure></li></ul><h2 id="太空飞船操作符-lt-gt"><a href="#太空飞船操作符-lt-gt" class="headerlink" title="太空飞船操作符(<=>)"></a>太空飞船操作符(<=>)</h2><ul><li>当符号两边相等时返回 <strong>0</strong></li><li>右边大于左边 返回 <strong>-1</strong> (0 <=> 1)</li><li>左边大于右边 返回 <strong>1</strong> (1 <=> 0)</li></ul><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//php也可以自定义排序方式,(试试其他规则)</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">space_sort</span><span class="params">($a, $b)</span> : <span class="title">int</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> $a <=> $b;</span><br><span class="line">}</span><br><span class="line">$arrays = [<span class="number">1</span>,<span class="number">34</span>,<span class="number">56</span>,<span class="number">67</span>,<span class="number">45</span>,<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line">usort($arrys, <span class="string">'space_sort'</span>);</span><br><span class="line"><span class="keyword">foreach</span>($arrays <span class="keyword">as</span> $val)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">echo</span> $val.PHP_EOL;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="合并运算符"><a href="#合并运算符" class="headerlink" title="合并运算符(??)"></a>合并运算符(??)</h2><p>经常用,不写了</p><h2 id="统一变量语法"><a href="#统一变量语法" class="headerlink" title="统一变量语法"></a>统一变量语法</h2><ul><li>在PHP5.X版本中可以使用可变变量正常输出,但在PHP7引入之后,为了遵守从左到右解析的原则需要做出修改</li></ul><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//PHP5.x</span></span><br><span class="line">$first =[<span class="string">'name'</span> => <span class="string">'second'</span>];</span><br><span class="line">$second = <span class="string">'Howdy'</span>;</span><br><span class="line"><span class="keyword">echo</span> $$first[<span class="string">'name'</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">//PHP7</span></span><br><span class="line"><span class="keyword">echo</span> ${$first[<span class="string">'name'</span>]}; <span class="comment">//需要添加大括号</span></span><br></pre></td></tr></table></figure><h2 id="其他特性变更"><a href="#其他特性变更" class="headerlink" title="其他特性变更"></a>其他特性变更</h2><ul><li>PHP5.6开始,常量数组可以使用<code>const</code>声明,PHP7中常量数组可以通过<code>define</code>函数来初始化。</li></ul><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">const <span class="symbol">STORES</span> = [<span class="string">'en'</span>,<span class="string">'fr'</span>,<span class="string">'ar'</span>];</span><br><span class="line"></span><br><span class="line">define(<span class="string">'STORES'</span>,[<span class="string">'ec'</span>,<span class="string">'a'</span>,<span class="string">'de'</span>]);</span><br></pre></td></tr></table></figure><ul><li>PHP7之前,<code>switch</code>多个<code>default</code>是允许的,但<strong>现在会提示Fatal Error</strong></li><li><code>Session_start</code>函数支持传递参数选项数组,不需要去修改php.ini</li></ul><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">session_start([</span><br><span class="line"><span class="string">'cookie_lifetime'</span> => <span class="number">3600</span>,</span><br><span class="line"><span class="string">'read_and_close'</span> => <span class="keyword">true</span></span><br><span class="line">]);</span><br></pre></td></tr></table></figure><ul><li><p><code>unserialize</code>函数引入过滤器。</p><ul><li><p><code>unserialize</code>函数并不安全,它没有任何过滤器,可以反序列化任何类型的对象。<br>PHP7引入了过滤器,默认情况下允许反序列化所有类型的对象。</p><p><code>$result = unserialize($object,['allowed_classes' => ['Packt','Books']]);</code></p></li></ul></li></ul><h1 id="PHP7应用性能提升·第三章"><a href="#PHP7应用性能提升·第三章" class="headerlink" title="PHP7应用性能提升·第三章"></a>PHP7应用性能提升·第三章</h1><ul><li><p>Apache</p><p><strong>Apache 2.X</strong> 支持插入式并行处理模块,称为多进程处理模块(MPM)。在编译apache时必须选择也只能选择一个MPM。对类UNIX系统,有几个不同的MPM可供选择,它们会影响到apache的速度和可伸缩性。</p><ul><li><p><strong><code>prefork</code></strong>模式(线程创建进程)</p><p>子进程一对一处理请求,处理请求快,内存消耗大。</p></li><li><p><strong><code>worker</code></strong>模式(进场创建线程)</p><p>子进程创建多线程处理请求,共享内存空间。</p><p>一个线程出现了问题也会导致同一进程下的线程出现问题</p></li><li><p><strong><code>events</code></strong>驱动模式</p><p>与worker模式类似,不同的是在于它解决了<code>keep-alive</code>长连接的时候占用线程资源被浪费的问题(HTTP的Keepalive方式能减少TCP连接数量和网络负载),在<code>event</code>工作模式中,会有一些专门的线程用来管理这些<code>keep-alive</code>类型的线程,当有真实请求过来的时候,将请求传递给服务器的线程,执行完毕后,又允许它释放。这增强了在高并发场景下的请求处理</p></li></ul></li><li><p>Nginx</p><ul><li>异步、事件驱动、非阻塞请求处理。</li><li>无内置解释性语言,解析脚本语言的进程则在Nginx之外。</li><li>对于静态资源,Apache也有自己的优势。</li></ul></li><li><p>HTTP Server优化(以下配置可在代理服务器开启,如Nginx)</p><ul><li><p>缓存静态文件 </p></li><li><p>HTTP Keep-alive (与TCP的Keepalive不同 )</p><p>1.cpu和内存占用减少。TCP链接数减少,后续请求和响应无需打开新链接</p><p>2.请求等待时间减少</p></li><li><p>GZIP压缩</p><p>将网络传输内容进行压缩后传递,同时,浏览器若支持GZIP压缩,服务器端程序在输出内容时便使用GZIP压缩</p></li><li><p>在Apache或Nginx关闭不必要的模块</p></li></ul></li><li><p>内容分发网络(CDN)</p><ul><li>给用户就近节点访问,加速。</li><li>如何进行CDN节点的选择? 参考<code>HTTP.智能DNS</code> </li></ul></li><li><p>CSS和JavaScript优化</p><ul><li>合并</li><li>缩小</li></ul></li><li><p>一些工具</p><ul><li>Minify - php缩小合并文件</li><li>Grunt - js 自动化任务</li><li>Varnish web应用程序加速器</li><li>HAProxy 负载均衡器</li></ul></li></ul><h1 id="提升数据库性能·第四章"><a href="#提升数据库性能·第四章" class="headerlink" title="提升数据库性能·第四章"></a>提升数据库性能·第四章</h1><ul><li><p>查询缓存<br>缓存了SELECT 查询及其结果数据集,同一个SELECT查询发生时,从内存中直接取出结果。<br>打开查询缓存 <code>show variables like</code>have_query_cache<code>;</code><br>Mysql.md=>my.cnf</p></li><li><p>存储引擎<br>修改存储引擎 <code>Alter table pkt_user ENGINE=INNODB</code>;<br>MyISAM和InnoDB存储引擎的区别详见 Mysql.md=>存储引擎</p></li></ul><ul><li><p>Mysql的一些辅助工具</p><ul><li>Percona Server</li><li>phpMyAdmin</li><li>Percona工具箱</li><li>Percona XtraDB 集群 (PXC)</li></ul></li><li><p>Redis</p><ul><li>基础用法介绍</li><li>Redis桌面管理 - RDM</li></ul></li><li><p>Memcached </p><ul><li>类似于Redis,基础用法的介绍</li></ul></li></ul><h1 id="调试和分析·第五章"><a href="#调试和分析·第五章" class="headerlink" title="调试和分析·第五章"></a>调试和分析·第五章</h1><ul><li>Xdebug拓展</li><li>Sublime Text调试</li></ul><h1 id="PHP应用的压力-负载测试·第六章"><a href="#PHP应用的压力-负载测试·第六章" class="headerlink" title="PHP应用的压力/负载测试·第六章"></a>PHP应用的压力/负载测试·第六章</h1><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p><strong>第5,6章的实践内容较多,需要自己去亲手操作,所谓的性能提升不能只停留在字面上,还需要利用各种工具,去观察实际的运行状况。</strong></p><p> 读完这本书,对于PHP7的一些新特性有相当的了解。</p><p> 本书大章按一整套的开发流程,从php-sql-debug-测试。大部分讲了工具的使用。</p><p> 技术和工具是不断更新的,还需要不断学习,才不会和社会脱节。</p>]]></content>
<summary type="html">
<p>读书笔记 <img src="../images/php7_high-performance.jpg" width="400px" height="500px" align="center"> </p>
</summary>
<category term="读书笔记" scheme="http://ajsonx.github.io/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="php" scheme="http://ajsonx.github.io/tags/php/"/>
</entry>
<entry>
<title>Hello World</title>
<link href="http://ajsonx.github.io/2019/07/31/hello-world/"/>
<id>http://ajsonx.github.io/2019/07/31/hello-world/</id>
<published>2019-07-31T12:00:00.000Z</published>
<updated>2019-08-05T01:40:32.000Z</updated>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p><a id="more"></a><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p>]]></content>
<summary type="html">
<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p>
</summary>
<category term="1" scheme="http://ajsonx.github.io/tags/1/"/>
</entry>
</feed>