-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.xml
718 lines (613 loc) · 74.3 KB
/
index.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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Gra55's Blog</title>
<link>/</link>
<description>Recent content on Gra55's Blog</description>
<generator>Hugo -- gohugo.io</generator>
<language>en</language>
<lastBuildDate>Sat, 16 Jul 2022 00:00:00 +0000</lastBuildDate>
<atom:link href="/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>GDB 调试 Python</title>
<link>/blog/2022/python/gdb-debugging-python/</link>
<pubDate>Sat, 16 Jul 2022 00:00:00 +0000</pubDate>
<guid>/blog/2022/python/gdb-debugging-python/</guid>
<description>1. 背景 很多问题在 Python 中很难调试,比如说:
段错误(segfaults),这种错误不是未捕获的 Python 异常,而是程序真正发生的段错误 进程 hung 住(hung processes),这种情况下无法获取堆栈信息,也无法通过 pdb 进行调试 守护进程失控(out of control daemon processes) 出现以上 3 种情况,可以使用 gdb 进行调试。
2. 依赖 使用 gdb 调试 Python 时,需要具体以下条件:
安装好 gdb 安装调试 Python 的扩展包,扩展包包含了: debugging symbols Python 特定的 gdb 命令 各个系统安装 gdb 和 python-debuginfo 的方式:
Fedora sudo yum install gdb python-debuginfo Ubuntu sudo apt-get install gdb python2.</description>
</item>
<item>
<title>GO Runtime 内存状态</title>
<link>/blog/2021/go/go-runtime-memstate/</link>
<pubDate>Mon, 15 Nov 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/go/go-runtime-memstate/</guid>
<description>官方文档:https://pkg.go.dev/runtime#MemStats
0x00 概述 Alloc uint64
堆中分配的对象所占用的字节数,与 HeapAlloc 是一样的 TotalAlloc uint64
堆中分配的对象所占用的字节数的累积值(释放内存这个数值不会减小) Sys uint64
从操作系统获得的内存总字节数,包含 go 运行时的堆、栈和其他数据结构,通常情况下这个值会保持不变 Lookups uint64
runtime 执行的指针查找次数 Mallocs uint64
堆中分配的对象累积的数量,活跃对象计算公式:Mallocs - Frees Frees uint64
释放的堆对象的累积计数,活跃对象计算公式:Mallocs - Frees HeapAlloc uint64
堆中分配的对象所占用的字节数,与 Alloc 是一样的。(这里包含所有可达的对象和 GC 尚未释放的不可达对象) HeapSys uint64
从操作系统获得的堆内存字节数(包含预留的未使用的虚拟地址空间) HeapIdle uint64
堆中未使用的字节数(这些内存字节可以返回给 OS,也可以被堆或栈重复使用。HeapIdle-HeapReleased是可以返回给 OS 的内存,但是现在由 runtime 保留,这样就可以不从 OS 申请内存而来增加堆的大小。如果这个值很大,大于堆大小,则表明活跃的堆大小最近出现过瞬时峰值) HeapInuse uint64</description>
</item>
<item>
<title>【MIT 6.828】5. 用户环境</title>
<link>/blog/2021/mit/6.828-5/</link>
<pubDate>Sun, 31 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/mit/6.828-5/</guid>
<description>lab 3 实验地址:https://pdos.csail.mit.edu/6.828/2018/labs/lab3/
0x00 课程介绍 在本实验中将会实现,运行一个受保护的用户模式(user-mode)环境(例如:“进程”等)所需的基本内核功能。我们需要增强 JOS 内核,设计一个数据结构来跟踪用户环境(user environment),创建单用户环境(single user environment),加载一个程序镜像(program image)并运行它。另外 JOS 内核还需要处理用户环境(user environment)发出的任何系统调用(system calls)和造成的任何其他异常(exceptions)。
注意:本实验中,术语“环境(environment)”和“进程(process)”的可以互换的,都是一个抽象概念:指允许你运行一个程序。
引入术语“环境(environment)”而不使用传统术语“进程(process)”,是为了强调 JOS 环境和 UNIX 进程提供不同的接口,并且不提供相同的语义。
使用命令 git checkout -b lab3 origin/lab3 切换到 lab3 的最新代码分支,lab3 增加了很多新的源文件,如下所示:
inc/ env.h 用户模式环境的公共定义 trap.h trap 处理的公共定义 syscall.h 从用户环境到内核的系统调用的公共定义 lib.h 用户模式支持库的公共定义 kern/ env.h 用户模式环境的内核私有定义 env.c 实现用户模式环境的内核代码 trap.h 内核私有 trap 处理定义 trap.c trap 处理代码 trapentry.S 汇编语言 trap 处理程序入口点 syscall.h 系统调用处理的内核私有定义 syscall.c 系统调用实现代码 lib/ Makefrag Makefile 片段来构建用户模式库,obj/lib/libjos.a entry.</description>
</item>
<item>
<title>【MIT 6.828】4. 内存管理</title>
<link>/blog/2021/mit/6.828-4/</link>
<pubDate>Wed, 27 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/mit/6.828-4/</guid>
<description>lab 2 实验地址:https://pdos.csail.mit.edu/6.828/2018/labs/lab2/
0x00 课程介绍 本次实验中,需要写操作系统内存管理的代码(译者注:我就不写了,学习 6.828 的目的是快速了解操作系统,初步学习且时间有限,能看懂就行了)。内存管理包含两大组件:
内核的物理内存分配器(physical memory allocator):有了物理内存分配器,内核就可以分配和释放内存空间了。该分配器需要以 4096 字节(称为页,page,#define PGSIZE 4096)为单位来运行。需要做的就是维护一个数据结构,该数据结构记录着哪些物理内存页是空闲的、哪些物理内存页是已分配的、多少进程共享了已分配的内存页。还需要编写申请和释放内存页的代码。 虚拟内存(Virtual Memory):将内核和用户程序使用的虚拟地址映射到物理内存中的地址。x86 硬件的 MMU(内存管理单元,memory management unit)在指令将要使用内存的时候,通过查询页表(page table),进行映射操作,实验中会要求根据给定规范,修改 JOS 中 MMU 的页表(page table)。 接下来的所有实验,将会逐步构建出自己的内核代码。使用命令 git checkout -b lab2 origin/lab2 可以切换到 lab2 的代码分支,lab2 中已经加入了一些相关的源代码。
lab2 新增了如下文件:
inc/memlayout.h kern/pmap.c kern/pmap.h kern/kclock.h kern/kclock.c inc/memlayout.h 描述了虚拟地址空间的布局,你需要在 kern/pmap.c 中实现它。inc/memlayout.h 和 kern/pmap.h 定义了 PageInfo 结构体,你需要使用 PageInfo 结构体去跟踪物理内存的哪个页(page)是空闲的。kern/kclock.c 和 kern/kclock.h 控制 PC 的时钟和 CMOS RAM 硬件,在这两个文件里面 BIOS 记录着物理内存的总数和其他内容。kern/pmap.</description>
</item>
<item>
<title>计算机术语【持续更新】</title>
<link>/blog/2021/computer-basics/computer-terms/</link>
<pubDate>Sun, 24 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/computer-basics/computer-terms/</guid>
<description>原文 翻译 解释 endpoint uri Web服务中 stub rpc中使用,是一段代码,用来转换客户端与服务器之间传递的参数 prefork 多进程模式,pre 表示在请求来之前子进程已经创建好了(进程池?),一般用在非线程安全的情况下 用户认证 判断是否是合法用户 用户权限 查看合法用户有没有权限 ELF 可执行连接格式(Executable and Linkable Format) 是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的。扩展名为elf。 GNU GNU's not unix GNU 是追求开源一项运动。Unix 系统被发明以后,大家用的很爽,但是后来闭源开始收费了。一个叫 RMS 的大叔觉得很不爽,于是发起 GNU 计划,模仿 Unix 的界面和使用方式,从头做一个开源的版本。然后他自己做了编辑器 Emacs 和编译器 GCC。做了很多可以运行在 Unix 上的开源软件,但是一直没做出操作系统,于是一个叫 Linus 的博士,写出来 Linux 操作系统,完美符合 GNU 的目的,所以最后 Linux 也纳入了 GNU 中。 AT&amp;T syntax AT&amp;T 是 GNU 汇编语言使用的语法格式,与之相关的是 NASM 汇编语言使用的 Intel 风格的语法 RAM 随机储存器(Random-Access Memory) 断电后数据丢失,也就是电脑上的内存条,程序时放在 RAM 中运行的 ROM 只读储存器(Read-Only Memory) 断电数据不丢失,通俗来讲就是电脑上的硬盘 IA-32 Intel Architecture, 32-bit 的缩写 有时候也叫做 i386 CGI Common Gateway Interface 通用网关接口。早期的 web 服务,后端会绑定一个目录(/usr/local/apache/htdocs/),浏览器请求时,会直接返回目录下 index.</description>
</item>
<item>
<title>【MIT 6.828】3. PC 启动流程</title>
<link>/blog/2021/mit/6.828-3/</link>
<pubDate>Fri, 22 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/mit/6.828-3/</guid>
<description>lab 1 实验地址:https://pdos.csail.mit.edu/6.828/2018/labs/lab1/
0x00 非官方解读 6.828 实验使用的是 386 CPU,但是 386 CPU 一上电以后,处于 16 位的实模式,与 8086 CPU 很相似,不清楚 8086 的可以学习文章操作系统基础知识概览
CPU 一上电,CS=0xF000、IP=0xFFF0,地址为 0xFFFF0,这个地址刚好在 BIOS ROM 中,所以先执行 BIOS 中的代码。
BIOS 执行流程:
上电自检(Power On Self Test) 加载磁盘的第一个可引导扇区,一个扇区大小为 512 字节,如何判断该扇区时可引导的?判断最后两个字节为 0xAA55 则表示该扇区可引导 BIOS 会把该扇区加载到段地址为 0x0000,偏移地址为 0x7c00 内存处 然后使用跳转指令跳到 CS=0x0000、IP=0x7c00 处开始执行 至此,BIOS 完成了自己的使命,把执行流交给了 0x7c00 处的代码,所以我们需要做的就是在磁盘的第一个扇区存放我们的代码。 0x01 官方解读(Lab 1: Booting a PC) 实验分为三部分:
Part 1:主要是熟悉 x86 汇编语言、QEMU x86 仿真器和 PC 的开机引导过程 Part 2:主要是学习 6.</description>
</item>
<item>
<title>【MIT 6.828】2. 实验环境准备</title>
<link>/blog/2021/mit/6.828-2/</link>
<pubDate>Thu, 21 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/mit/6.828-2/</guid>
<description>0x00 简述 我的实验环境:
CentOS 7 0x01 工具安装 整个实验环境需要两类工具:ToolChain、QEMU 模拟器
ToolChain 工具链 ToolChain 包含汇编器、连接器、C 编译器和 debug 工具。
一般的现代 Linux 系统都自带了 6.828 需要的 ToolChain,命令 objdump -i 和 gcc -m32 -print-libgcc-file-name 如果都能执行成功,说明 ToolChain 工具链已经就绪。
我的 CentOS 自带了 ToolChain 工具链,所以没有安装,如果有问题或者需要自己安装 ToolChain 工具链,可以参考官方文档和B 站 UP 主的视频指导,推荐B 站 UP 主的视频指导,该视频讲解的很详细。
更新补充: 使用 linux 自带的工具链还是有些问题,需要到 JOS 中将 conf/env.mk 中的 GCCPREFIX= 注释掉,比较麻烦,估计坑也不少,建议按照官方文档安装。
本人打包好的工具链安装包:点击下载
安装命令如下:
tar xjf gmp-5.0.2.tar.bz2;cd gmp-5.0.2;./configure --prefix=/usr/local;make;make install;cd .. tar xjf mpfr-3.</description>
</item>
<item>
<title>【MIT 6.828】1. 课程介绍</title>
<link>/blog/2021/mit/6.828-1/</link>
<pubDate>Wed, 20 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/mit/6.828-1/</guid>
<description>0x00 简述 6.828 主要讲操作系统基本原理,包括虚拟内存、内核、用户模式等。
课程组成形式,如下所示:
├── 讲座 │ ├── 讲解 xv6 操作系统 │ └── 讲解操作系统新兴的概念,这部分会学习很多研究论文 ├── 实验 │ ├── Lab 1:Booting │ ├── Lab 2:Memory management │ ├── Lab 3:User environments │ ├── Lab 4:Preemptive multitasking │ ├── Lab 5:File system, spawn, and shell │ └── Lab 6:Network driver └── 文档读物 0x01 术语 xv6:xv6 是一个类 Unix 的教学使用的操作系统,MIT 基于 Sixth Edition Unix (aka V6) 版本的重新实现,也是基于 x86 的,但是比 x86 更贴近于教学学习。 JOS:JOS 比 xv6 更早期一些,只支持单核,比 xv6 更适合学生来学习,6.</description>
</item>
<item>
<title>【MIT 6.828】0. 参考资料</title>
<link>/blog/2021/mit/6.828-0/</link>
<pubDate>Tue, 19 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/mit/6.828-0/</guid>
<description>非官方 MIT 6.828-神级OS课程-要是早遇到,我还会是这种 five 系列 [MIT] 6.828 操作系统工程导读 官方 《PC 汇编语言》 PDF 版本 《6.828: PC hardware and x86》PPT 课件 xv6 操作系统介绍 </description>
</item>
<item>
<title>MIT:6.828-操作系统引擎</title>
<link>/awesome/mit-6.828/</link>
<pubDate>Mon, 18 Oct 2021 00:00:00 +0000</pubDate>
<guid>/awesome/mit-6.828/</guid>
<description>0x00 原版视频 document.getElementById("video-bilibili").style.height=document.getElementById("video-bilibili").scrollWidth*0.5+"px"; 0x01 UP 主实操视频【推荐】 document.getElementById("video-bilibili").style.height=document.getElementById("video-bilibili").scrollWidth*0.5+"px"; 0xff 题外话 MIT 6.828 是由 PDOS 发布的操作系统相关的公开课程。
PDOS 是什么呢?PDOS 全称是 MIT Parallel &amp; Distributed Operating Systems Group,即麻省理工学院平行与分布式操作系统研究组,这个小组致力于研究操作系统相关的内容。PDOS 隶属于 MIT CSAIL。
MIT CSAIL 又是什么呢?CSAIL 全称是 MIT Computer Science &amp; Artificial Intelligence Lab,即麻省理工学院计算机科学与人工智能实验室,是计算机领域的先驱,创造了很多有价值的东西。下设很多小组,分别研究不同的方向。</description>
</item>
<item>
<title>操作系统基础知识概览</title>
<link>/blog/2021/computer-basics/os-basic/</link>
<pubDate>Mon, 18 Oct 2021 00:00:00 +0000</pubDate>
<guid>/blog/2021/computer-basics/os-basic/</guid>
<description>0x00 计算机硬件结构 +------------------+ | ======CPU======= | +--------+---------+ | +------------+--------------+ +-----+ | | +------+ | AGP +--------+ MCH(Memory Controller Hub)+----+Memory| +--+--+ | | +------+ | +------------+--------------+ +------+-------+ | | | +------------+--------------+ +-----+ | Display | | | | | | | | ICH(I/O Controller Hub) +-----+ --&gt; +--------------+ | | | PCI | +---+--------+-------+----+-+ | --&gt; | | | | | | +----------+ | +----+---+ | +-+-----+ | --&gt; +---+ USB +-+ | ATA | | |Network| | | | +-------+--+ +----+---+ | +-------+ | --&gt; | | | | | | +---+---+ +----+---+ +------+--+ +--+--------+ | --&gt; | Mouse | |Keyboard| |Hard Disk| | Flash BIOS| | | +-------+ +--------+ +---------+ +-----------+ +-----+ 计算机体系架构需要知道以下内容:</description>
</item>
<item>
<title>MIT:6.824-分布式系统</title>
<link>/awesome/mit-6.824/</link>
<pubDate>Wed, 09 Dec 2020 00:00:00 +0000</pubDate>
<guid>/awesome/mit-6.824/</guid>
<description>0x00 《Distributed Systems》 document.getElementById("video-bilibili").style.height=document.getElementById("video-bilibili").scrollWidth*0.5+"px"; </description>
</item>
<item>
<title>Golang 标准库:context</title>
<link>/blog/2020/go/go-standard-library-context/</link>
<pubDate>Fri, 27 Nov 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/go/go-standard-library-context/</guid>
<description>0x00 简介 Context 是以链式的方式来保存数据:context A 派生出了 context B,context B 派生出了 context C &hellip;&hellip;
当一个 context 被 canceled,所有从它派生的 context 都会被取消:B 被取消,C 同样也会被废弃
调用 WithCancel、WithDeadline、WithTimeout 这三个函数,入参是 parent context,出参是包含了 parent context 的 child context 和 CancelFunc函数。
调用 CancelFunc 函数会废弃 child context,并且会废弃 child context 的 context,同样会切断与 parent context 的关联,也会暂停所有相关的定时器。 如果没有调用 CancelFunc 函数,只能等到 parent context 被废弃或者定时器被触发。 go 的 vet 工具会检查所有的 context 链中 CancelFuncs 函数是否被调用。 0x01 最佳实践 不要将 context 对象保存在结构体类型中。我们应该显式的传递 context 给需要的函数,而且 context 必须是第一个参数,通常命名为 ctx 不要传递一个 nil 的 context,即使你不需要他。此时可以使用 context.</description>
</item>
<item>
<title>设计模式 # 结构型 # 装饰器模式</title>
<link>/blog/2020/architecture/decorator-pattern/</link>
<pubDate>Thu, 16 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/architecture/decorator-pattern/</guid>
<description>结构型设计模式主要用于描述对象之间的组合,通过对象间的组合来完成特定功能。
0x00 模式概述 装饰器模式会动态的给一个对象添加新功能,就增加功能来说,装饰器模式比子类化更灵活(合成复用原则)。
0x01 场景 当需要给一个对象增加新功能时,又不想使用继承,可以考虑使用装饰器模式实现。
0x02 解决方案 装饰器模式通过将对象包装在装饰器类内部来动态更改对象的行为。
0x03 总结 装饰器模式很简单,大多数脚本语言已经在语言层面实现了该模式。
参考:
📌 设计模式(45种)
📌 Design patterns for humans 中文版</description>
</item>
<item>
<title>设计模式 # 创建型 # 单例模式</title>
<link>/blog/2020/architecture/singleton-pattern/</link>
<pubDate>Wed, 15 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/architecture/singleton-pattern/</guid>
<description>0x00 模式概述 单例模式是最简单的设计模式之一,属于创建型,提供了一种创建全局唯一对象的方法。
0x01 场景 在业务代码中,如果需要全局唯一的对象,可以使用单例模式来创建对象。
0x02 解决方案 单例模式根据对象实例化的时机,划分为懒汉式和饿汉式,顾名思义:
饿汉式:在类初始化的时候,就提前实例化一个对象,等到使用的时候直接返回 懒汉式:在第一次准备实例化对象的时候才真正创建一个对象,再此之后的实例化都是直接返回第一次创建的对象 饿汉式的优势是因为它是线程安全的,因为对象在使用之前已经创建了。而懒汉式就存在多线程并发情况下会创建出多个实例对象的问题,所以就需要加锁,加锁会影响性能。
0x03 总结 一般情况下使用饿汉式就可以了,除非明确要求延迟初始化(lazy initialization)。
参考:
📌 设计模式(45种)
📌 Design patterns for humans 中文版</description>
</item>
<item>
<title>设计模式 # 创建型 # 原型模式</title>
<link>/blog/2020/architecture/prototype-pattern/</link>
<pubDate>Wed, 15 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/architecture/prototype-pattern/</guid>
<description>0x00 模式概述 原型模式用于创建重复的对象,该重复对象的创建成本比较高,所以原型模式通过 clone 现有对象来完成。
0x01 场景 适用于创建实例成本比较高的场景,eg:创建对象时需要先连接数据库,这个成本就很高,原型模式很适合。
0x02 解决方案 // 克隆羊多利 class Sheep { protected $name; protected $category; public function __construct(string $name, string $category = &#39;Mountain Sheep&#39;) { $this-&gt;name = $name; $this-&gt;category = $category; } public function setName(string $name) { $this-&gt;name = $name; } public function getName() { return $this-&gt;name; } public function setCategory(string $category) { $this-&gt;category = $category; } public function getCategory() { return $this-&gt;category; } } // client $original = new Sheep(&#39;Jolly&#39;); echo $original-&gt;getName(); // Jolly echo $original-&gt;getCategory(); // Mountain Sheep // Clone and modify what is required $cloned = clone $original; $cloned-&gt;setName(&#39;Dolly&#39;); echo $cloned-&gt;getName(); // Dolly echo $cloned-&gt;getCategory(); // Mountain sheep 0x03 总结 这个模式很简单,当创建的成本比较高时,就可以考虑使用 clone 的方式(原型模式)。</description>
</item>
<item>
<title>设计模式 # 创建型 # 构建器模式</title>
<link>/blog/2020/architecture/builder-pattern/</link>
<pubDate>Wed, 15 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/architecture/builder-pattern/</guid>
<description>0x00 模式概述 构建器模式用来创建复杂的对象,该复杂对象由多个步骤构建而来。
为了分离这种复杂性,新增一个对象 Builder 来一步一步创建最终的对象,这种创建实例的方式真的是非常优雅,后续如果进行需求变更,需要修改的内容就很少。
0x01 场景 创建一个复杂的对象时,基础的对象不会变化,但是其组合会经常变化,此时使用构建器模式就非常有用。
0x02 解决方案 // 复杂对象 class Burger { protected $size; protected $pepperoni = false; protected $lettuce = false; protected $tomato = false; public function __construct(BurgerBuilder $builder) { $this-&gt;size = $builder-&gt;size; $this-&gt;pepperoni = $builder-&gt;pepperoni; $this-&gt;lettuce = $builder-&gt;lettuce; $this-&gt;tomato = $builder-&gt;tomato; } } // 构造器类 class BurgerBuilder { public $size; public $pepperoni = false; public $lettuce = false; public $tomato = false; public function __construct(int $size) { $this-&gt;size = $size; } public function addPepperoni() { $this-&gt;pepperoni = true; return $this; } public function addLettuce() { $this-&gt;lettuce = true; return $this; } public function addTomato() { $this-&gt;tomato = true; return $this; } public function build(): Burger { return new Burger($this); } } // client $burger = (new BurgerBuilder(14)) -&gt;addLettuce() -&gt;addTomato() -&gt;build(); 0x03 总结 这个模式很简单,简单理解就是适用于复杂对象的构造函数会经常变更的场景。</description>
</item>
<item>
<title>基础算法:分治法</title>
<link>/blog/2020/algorithm/divide-and-conquer-algorithm/</link>
<pubDate>Tue, 14 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/algorithm/divide-and-conquer-algorithm/</guid>
<description>0x00 算法概述 分治法的思想就是将不可能或者很难解决的问题,拆解成多个相似的子问题,然后将子问题拆解成更小粒度的子问题,直到这些小问题可以很容易的被解决,然后合并这些小问题的解以得到最终的解。
分治法是很多高效算法的基础,例如快排,归并排序等
0x01 解题步骤 满足以下所有特征才能使用分治法:
该问题缩小到一定规模,就可以很容易的解决 具有最优子结构,即小规模问题和大规模问题是相同的问题(其实就是递归思想,可以使用递归来解决) 子问题的解可以合并成为大问题的解(这是关键特性,如果不满足,可以使用动态规划或贪心算法) 子问题之间是独立的(不独立的话也可以使用分治法,但是就是比较复杂,需要处理公共的子问题) 步骤:
分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题; 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题 合并:将各个子问题的解合并为原问题的解。 0x02 实现方式 递归 参考:
📌 五大常用算法之一:分治算法</description>
</item>
<item>
<title>基础算法:动态规划</title>
<link>/blog/2020/algorithm/dynamic-programming/</link>
<pubDate>Tue, 14 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/algorithm/dynamic-programming/</guid>
<description>0x00 算法概述 动态规划的思想类似于分支法,也是将待处理的问题拆分成多个子问题,按顺序求解子问题,前一个子问题的解为后一个子问题的求解提供了有用的信息。
0x01 解题步骤 满足以下所有特征才能使用动态规划:
最优子结构:问题的最优解所包含的子问题的解也是最优的(因为只有这样,状态转移到最后得出的解才是全局最优解) 无后效性:子问题的状态一旦确定,不会受到后面子问题的影响 有重叠子问题:子问题之间不是独立的,后面的子问题会多次用到前面子问题的解(这个不是必要条件,这个条件会使动态规划算法更具优势) 步骤:
动态规划一般用来处理多阶段决策类问题,一般由初始状态开始,通过中间阶段的决策,最后得到结束的状态。这样会实现一个决策序列:初始状态 → │决策1│ → │决策2│ →…→ │决策n│ → 结束状态
划分阶段:按照问题特征,把问题划分成多个阶段,阶段必须是有序的或者可排序的,否则无法求解 确定状态:一般情况下,各阶段状态就是各子问题的解,这样才能推出最终的解 状态转移方程:由之前一个或者多个状态得出当前状态的公式(这个一般是最难找的) 寻找终止条件:状态转移方程一般是递推式,需要找到终止条件来结束程序 0x02 实现方式 动态规划最难的就是找状态和状态转移方程,有时候状态找不对就很难找到状态转移方程。
代码实现起来也不是很复杂。
参考:
📌 五大常用算法之二:动态规划算法</description>
</item>
<item>
<title>基础算法:贪心算法</title>
<link>/blog/2020/algorithm/greedy-algorithm/</link>
<pubDate>Tue, 14 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/algorithm/greedy-algorithm/</guid>
<description>0x00 算法概述 贪心算法是在求解问题的时候,总是选择当前最优的解,不考虑全局最优解。
选择当前最优的解的方法称为贪心策略,贪心策略必须保证拆分的子问题必须是无后效性的(当前状态不会影响之前的状态)。
0x01 解题步骤 分析问题,抽象成数学模型 将问题拆分成多个子问题(子问题必须是无后效性的) 求解每一个子问题,得到子问题的局部最优解 合并子问题的最优解,得出全局解 贪心算法的核心就是找贪心策略,然后证明贪心策略中子问题的最优解一定会得到全局最优解。
0x02 实现方式 适用场景:
单源最短路经问题 最小生成树问题 可任意分割的背包问题。如果不可以任意分割,就需要用动态规划求解。 某些情况下,即使贪心算法不能得到整体最优解,但其最终结果近似于最优解。 贪心算法的实现很简单,只要能找到贪心策略,代码很容易写出来。
参考:
📌 五大常用算法之三:贪心算法
📌 常见算法及问题场景——贪心算法</description>
</item>
<item>
<title>基础算法:回溯法</title>
<link>/blog/2020/algorithm/back-tracking/</link>
<pubDate>Mon, 13 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/algorithm/back-tracking/</guid>
<description>0x00 算法概述 回溯算法其实是一种枚举算法(穷举法),是一种暴力解法,时间复杂度比较高。
回溯法通过深度优先遍历的方法来尝试所有可能的解,当发现某一个分支无法满足求解条件,立马退回一步重新选择(回溯),尝试其他路径。如果只求一个解时,搜索到可用解后就停止搜索;如果求问题的所有解,就必须遍历所有解空间。
0x01 解题步骤 针对所给问题,确定解空间:需要确定问题的解空间是否存在一个(最优)解 确定搜索规则 以深度优先策略开始搜索解空间,搜索过程中通过剪枝函数避免无效搜索 什么是剪枝函数?
剪枝函数是对无效解的过滤策略(明知道这条路径走下去不会得到解或最优解,所以就提前回溯,提高效率) 可行性剪枝:提前判断当前路径无法求出解,就可以提前回溯 最优化剪枝:声明一个变量存储当前最优解,如果可以提前判断当前路径无法满足最优解的条件,就提前回溯 剪枝函数特别难找,好的剪枝函数可以极大的降低算法的时间复杂度 回溯通常是通过反转动态规划的步骤来实现的 0x02 实现方式 非递归 递归 参考:
📌 五大常用算法之四:回溯法
📌 “通用解题法”之回溯中的“剪枝”</description>
</item>
<item>
<title>AVL Tree 概述</title>
<link>/blog/2020/algorithm/avl-tree/</link>
<pubDate>Fri, 10 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/algorithm/avl-tree/</guid>
<description>0x00 前言 最早出现的是二叉树,随后人们发现二叉树可以用来二分查找,所以出现了二叉搜索树。
但是二叉搜索树在极端情况会退化成单链表的形式,所以出现了平衡二叉搜索树,即AVL 树(AVL 取自发明它的两个人的名字首字母,话说老外都爱这个干,以自己名字命名各种算法)
但是AVL 树也是有缺陷的,删除和插入效率很低(因为需要旋转多次),所以出现了红黑树,红黑树不是严格的平衡树,所以查找效率可能会低一点。
本文主要介绍AVL 树,后续出单独的文章介绍红黑树。
0x01 特性 必须是一颗二叉搜索树 每个节点的左子树和右子树的高度差的绝对值不能大于 1 查找、插入、删除的平均和最坏时间复杂度都是 O(logn) 0x02 术语 平衡因子(Balance Factor)
二叉树节点的左子树高度减去右子树高度的值,称为该节点的平衡因子 最小不平衡子树
距离插入节点最近的,且平衡因子绝对值大于 1 的节点为根的树,就是最小不平衡子树 0x03 实现 节点结构 下面的节点结构包含了节点的高度,也可以存储平衡因子和父节点。
class TreeNode(object): def __init__(self, value): self.value = value self.left = None self.right = None self.height = 0 AVL 类提供的函数 失衡调整 - 左单旋(在最小不平衡子树的右子树中插入右孩子时)
+---+ +---+ +---+ | 4 | | 4 | | 5 | +---+ +---+ +---+ | | | | +---+ +---+ +---+ +---+ | 5 | | 5 | | 4 | | 6 | +---+ +---+ +---+ +---+ | +---+ | 6 | +---+ def leftRotation(proot): &#34;&#34;&#34;单左旋转操作:param proot: 最小失衡子树的根节点:rtype: TreeNode&#34;&#34;&#34; # 左旋 tmpNode = proot.</description>
</item>
<item>
<title>LRU 算法概述</title>
<link>/blog/2020/algorithm/lru-cache/</link>
<pubDate>Mon, 06 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/algorithm/lru-cache/</guid>
<description>0x00 算法概述 LRU(Least-recently-used):最近最少被使用的某个东西。最早使用在内存中,表示最近最少被使用的内存将会被释放。
0x01 算法实现 如果想要这个算法的 get 和 set 时间复杂度都为 O(1),我们这里需要使用两种数据结构 dict 和双向链表。
dict 获取元素的时间复杂度是 O(1),set 元素时通过字典先找到已存在的元素,然后将这个元素挪到链表的尾部,时间复杂度也是 O(1)。结构如下所示:
+----------+ +----------+ +----------+ +----------+ | key-root | | key-A | | key-B | | key-C | +----------+ +----+-----+ +----+-----+ +----+-----+ | | | | | | | | v v v v +----+-----------------+----------------+------------------+----------+ | hash function | +----+-----------------+----------------+------------------+----------+ | | | | v v v v +----+-----+ +----+-----+ +---+------+ +----+-----+ +------&gt;+ +-----&gt;+ +------+ +-----&gt;+ +--------+ | | root | | A | | B | | C | | | +----+ +&lt;-----+ +------+ +&lt;-----+ +&lt;---+ | | | +----------+ +----------+ +----------+ +----------+ | | | | | | | +--------------------------------------------------------------------------+ | +---------------------------------------------------------------------------------+ 0x02 代码实现 在 Python 3 的内置模块 functools 中(Python 2 中没有),给出了 lru_cache 的实现,这应该就是用 Python 来实现 lru cache 的最佳实践了。</description>
</item>
<item>
<title>Python 基础知识点</title>
<link>/blog/2020/python/python-basic-knowledge/</link>
<pubDate>Fri, 03 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/python/python-basic-knowledge/</guid>
<description>以下知识点只给出结论,不给予论证,请自行测试。
0x00 实例属性的访问顺序 __getattribute__ 类的数据描述符属性 实例的属性 类的非数据描述符属性 类的普通属性 __getattr__ 0x01 实例属性的赋值顺序 类的数据描述符属性 实例属性 0x02 内存管理 引用计数 内存池 垃圾回收 0x03 垃圾回收 引用计数 标记清除 分代回收 0x04 作用域 global 关键字用来访问全局作用域的变量值。
nonlocal 关键字(只有 Python 3 支持)用来访问 Enclosing locals(闭包)作用域的变量值。
Python 没有块作用域,for 语句范围不是单独的作用域。
Local Enclosing locals Global Built-in 0x05 is 关键字 is 用来比较变量的地址 == 用来比较变量的值 0x06 单引号/双引号/三引号 单引号和双引号等效,换行需要使用反斜杠(\) 三引号可以直接换行,可以包含任何形式的字符串 0x07 自省 运行时能够查看对象内部的属性或状态 自省函数:type()、isinstance()、dir()、hasattr()、getattr() 0x08 staticmethod &amp; classmethod staticmethod &amp; classmethod 都是内置类型,属于非数据描述符 可以将类内的方法转变成静态方法和类方法(从 Python 2.</description>
</item>
<item>
<title>Python 标准库:abc</title>
<link>/blog/2020/python/python-standard-library-abc/</link>
<pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate>
<guid>/blog/2020/python/python-standard-library-abc/</guid>
<description>0x00 为什么需要 abc 模块 abc 模块为调用者和具体实现类(而不是抽象类)之间提供更高级别的语义化约定。你使用类 A 之前,类 A 就给你保证它有 b 方法和 c 属性,不需要你在使用的时候通过 getattr 来判断,这个就是约定(contract)。
上面说的那句话是什么意思其实我也不是很懂,我理解它其实就类似于静态语言中的接口,子类必须实现抽象基类中的所有抽象方法和属性。而 abc 模块就帮你完成了这些事情。
详情请查看 PEP 3119
0x01 abc.ABCMeta 对象介绍 ABCMeta 是一个元类(metaclass),用来定义 Abstract Base Classes (ABCs)
ABCMeta 可以被继承,可以作为一个 Mixin Class。
可以注册不相关的具体类和 ABCs,作为虚拟类,他和他的子类会被认为是注册的 ABC 的子类(使用内置的 issubclass 函数来判断),但是注册的 ABC 不会出现在他们的 MRO(Method Resolution Order)中,注册 ABC 的方法也不能被调用。
通过 ABCMeta 元类创建的类具有以下方法:
register(subclass)
注册一个子类,作为这个 ABC 的虚拟子类 注意:虚拟子类与 C++ 中的虚拟子类概念不是一回事 from abc import ABCMeta class MyABC: __metaclass__ = ABCMeta MyABC.</description>
</item>
<item>
<title>设计模式 # 创建型 # 简单工厂、工厂方法、抽象工厂</title>
<link>/blog/2019/architecture/design-patterns-simple-factory-and-factory-method-and-abstract-factory/</link>
<pubDate>Tue, 31 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/architecture/design-patterns-simple-factory-and-factory-method-and-abstract-factory/</guid>
<description>0x00 模式概述 这种三种设计模式都是属于创建型,都是为了将类的创建和使用隔离。GoF 23 种设计模式不包含简单工厂模式,因为简单工厂在扩展的时候需要修改源码,违反了开闭原则
工厂方法模式是简单工厂的升级版,解决了扩展需要修改源码的问题,满足了开闭原则。
抽象工厂模式是工厂方法的升级版,解决了工厂方法只能生产一类产品的问题,但是抽象工厂在增加新类型产品的时候会违反开闭原则,这里需要特别注意。
0x01 场景 简单工厂 只有一个工厂生产产品,后期也不会扩展 工厂方法 多个工厂生产同一类产品,后期会扩展更多的工厂 抽象工厂 多个工厂生产多种类型的产品 0x02 解决方案 简单工厂 简单工厂的类图(不是标准的类图,只是凸显一下依赖关系)如下所示,client 需要使用产品类 A 时不直接实例化产品类 A,而是通过 SimpleFactory 工厂类的 createProd() 方法来创建产品类 A 实例。
如果现在需要新增产品类 B,则需要修改 createProd() 方法的源码,这明显违反了开闭原则。
+--------------------+ +---------------------------+ | | | SimpleFactory | | client +-------------&gt;|---------------------------+ | | | +createProd(prodType) | +--------------------+ +---------------------------+ 工厂方法 所以出现了工厂方法,工厂方法其实是依赖倒置原则(面向接口编程,不要面向实现编程)的体现,client 不依赖具体的 SimpleFactory,而是依赖一个抽象的工厂 AbstractFactory,具体的工厂继承自 AbstractFactory,实现 createProd() 方法即可。
增加新产品 C 时,只需要新增加 ConcreteFactoryC 类即可,满足开闭原则。
+--------------------+ +---------------------------+ | | | AbstractFactory | | client +-------------&gt;|---------------------------+ | | | +createProd() | +--------------------+ +----+----------------+-----+ ^ ^ | | | | +--------------+-----+ +----+---------------+ | ConcreteFactoryA | | ConcreteFactoryB | +--------------------+ +--------------------+ | +createProd() | | +createProd() | +--------------------+ +--------------------+ 抽象工厂 抽象工厂在工厂方法的基础上增加了新的产品类型(2 号产品),其他没有太大的区别。但是在扩展新产品类型(3号产品)的时候需要修改已有的代码(AbstractFactory、ConcreteFactoryA、ConcreteFactoryB 都需要增加 createProd3() 方法),破坏了开闭原则。</description>
</item>
<item>
<title>设计模式概述</title>
<link>/blog/2019/architecture/design-patterns-overview/</link>
<pubDate>Mon, 30 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/architecture/design-patterns-overview/</guid>
<description>为什么需要设计模式?
套用树人哥的一句话:其实世上本没有设计模式,走的人多了就有了设计模式。
本来大家都是按照自己的方式写代码,然后根据不同需求,写出各种可扩展的、可维护的代码。然后,1995 年,GoF 四个人就将这些经验性代码总结出来,汇总成 23 种编程范式。
其实就是编程的最佳实践,针对这种问题你就得这么干,因为根据历史经验来看,这么干是最优的。
设计模式的本质是面向对象设计原则的实际应用。
0x00 设计模式分类 按目的来分 创建型:用于描述如何创建对象,将对象的创建与使用分离开 结构型:用于描述如何将类或对象按照某种布局组成更大的结构(类似于聚合) 行为型:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务(类似于组合) 按作用范围来分 类模式:用于描述类与子类的关系,是静态的,在编译时就能确定下来(通过继承实现) 对象模式:用于描述对象之间的关系,在运行时刻是变化的(通过聚合/组合实现) 范围\目的 创建型 结构型 行为型 类模式 工厂方法 适配器(类) 模板方法、解释器 对象模式 单例、原型、抽象工厂、建造者 代理、适配器(对象)、桥接、装饰、外观、享元、组合 策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录 0x01 Prin 1:开闭原则 Software entities should be open for extension, but closed for modification.
需求变更的时候,尽量通过扩展来实现需求,而不是通过修改源码来实现。 开闭原则是面向对象程序设计的终极目标 实现方法:通过抽象约束、封装变化来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在具体实现类中。 0x02 Prin 2:里氏替换原则 Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.</description>
</item>
<item>
<title>MySQL 事务隔离级别</title>
<link>/blog/2019/db/mysql-transaction-isolation-level/</link>
<pubDate>Sun, 29 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/db/mysql-transaction-isolation-level/</guid>
<description>说隔离级别之前,先说说并发会带来什么问题?
脏读:事务 A 读取了事务 B 更新的数据,然后 B 回滚了操作,那么 A 就是读取到了脏数据
不可重复读:事务 A 多次读取同一行记录,事务 B 在 A 多次读取过程中更新并提交了该记录,导致 A 多次读取的数据不一致(没有快照读)
幻读:事务 A 修改 id&gt;10 的记录,使 name='cs&rsquo;。但是事务 B 同时插入了 id=20 的一条记录,最后事务 A 发现 id=20 的记录没有被修改,仿佛出现了幻觉
Note:不可重复读和幻读很容易混淆。不可重复读针对的是 update(更新某几条记录),解决不可重复度只需要锁住需要更新的某几条记录即可;幻读针对的是 insert/delete(增加/删除几条记录),解决幻读则需要给全表加锁。
-- 查看 session 隔离级别: select @@tx_isolation; -- 修改 session 隔离级别: set session transacton isolation level xxxxx; -- 查看全局隔离级别: select @@global.tx_isolation; -- 修改全局隔离级别: set global transacton isolation level xxxxx; 0x00 Read-uncommitted RU 隔离级别下,上面三个问题都没有解决。</description>
</item>
<item>
<title>分布式事务:TCC</title>
<link>/blog/2019/architecture/distributed-transaction-tcc/</link>
<pubDate>Sat, 28 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/architecture/distributed-transaction-tcc/</guid>
<description>什么是分布式?
与分布式对立的概念是单体系统,分布式系统是将不同的功能模块拆分成不同的服务,微服务就是一个典型的分布式系统。 分布式事务常见解决方案:
2PC两段提交协议 3PC三段提交协议(弥补两端提交协议缺点) TCC或者GTS(阿里) 消息中间件最终一致性 使用LCN解决分布式事物,理念“LCN并不生产事务,LCN只是本地事务的搬运工”。 0x00 TCC TCC 是 Try、Confire、Cancel 的缩写。
又称补偿机制,核心思想是:针对每个操作(Try)都要注册一个与其对应的确认(Confirm)和补偿(Cancel)操作。
0x01 5 个步骤 ①、向协调者发起开启事务请求
②、Try 阶段:Try 阶段负责把所有服务的业务资源预留和锁住。类似于 MySQL DML 操作,会加行锁
③、Confirm or Cancel 阶段:如果 Try 阶段涉及的所有服务都确认执行成功,则向协调者发送 Confirm(commit),否则发送 Cancel(rollback)
④、协调者根据业务发送的 Confirm or Cancel,向所有服务发送相应的 Confirm or Cancel 请求
⑤、各服务提交本地事务
+----------------------+ 1 +------------------------+ | +---------------&gt;+ | | APP | | Coordination service | | +---------------&gt;+ | +-+--+--+------+--+--+-+ 3 +------------------------+ | | | | | | 2 | | | | | | 2 +------------------------+ | | | | +--------------------------+ | 4 | | | | 4 | | +------------------------+ | | +--------------------------+ | | | 4&#39;| | 4&#39; | | | | +----------------------+ +-------------------------+ | | | | | | | | v v v v v v +-+--+----+-------------------------+ +---------------------+---+--+-+ | service A | | service B | +----------------------+------------+ +-------+----------------------+ | | 5 | | 5 v v +-----+-------+ +---+---------+ | DB 1 | | DB 2 | +-------------+ +-------------+ 与 2PC 比较: 2PC 是资源层面的分布式事务,强一致,整个操作中资源一直被加锁,不需要开发者参与。</description>
</item>
<item>
<title>分布式事务:2PC & 3PC</title>
<link>/blog/2019/architecture/distributed-transaction-2pc-3pc/</link>
<pubDate>Fri, 27 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/architecture/distributed-transaction-2pc-3pc/</guid>
<description>什么是分布式?
与分布式对立的概念是单体系统,分布式系统是将不同的功能模块拆分成不同的服务,微服务就是一个典型的分布式系统。 分布式事务常见解决方案:
2PC两段提交协议 3PC三段提交协议(弥补两端提交协议缺点) TCC或者GTS(阿里) 消息中间件最终一致性 使用LCN解决分布式事物,理念“LCN并不生产事务,LCN只是本地事务的搬运工”。 0x00 CAP 定理 所谓定理就是别人发现的规律,并且已经证实该规律是正确的(就像勾股定理,它永远是正确的,你直接用它的结论就行,你也可以自己证实其正确性)。
CAP 定理定义:在异步网络中(不可靠的网络),不可能同时实现并发读/写状态下的 availability、consistency。即 Consistency、Availability 和 Partition Tolerance 不能同时满足。
这里证明了 CAP 是成立的
0x01 2PC Two-Phase Commit Protocol(两阶段提交协议),注意 ,2PC 是一个协议。
2PC 是一个强一致、中心化的原子提交协议。这里面包含一个中心化的协调者节点(coordinator)。
Example:订单服务 A,需要调用支付服务 B 去支付,支付成功则处理购物订单为待发货状态,否则就需要将购物订单处理为失败状态。
两个阶段: Prepare 阶段
①、事务询问:Coordinator 收到请求以后,向所有参与者发送事务预处理请求,即 Prepare。开始等待参与者响应
②、执行本地事务:各参与者执行本地事务,但是不会真正提交,而是先向 Coordinator 报告自己的情况
③、返回事务询问响应:参与者根据本地事务的情况返回 Yes or No
Commit 阶段
④、Coordinator 统计参与者的返回值,返送 commit(参与者全回复 Yes) 或者 rollback(有参与者回复 No)
⑤、执行本地 commit 或者 rollbak
| | request v +-----------------+-----------------+ | Coordinator | +-+------+----+-------+---+------+--+ | ^ | | ^ | | | | | | | 4 | 3 | | 1 1 | | 3 | 4 | | | | | | v | v v | v +-----+------+----+-+ ++---+------+-------+ | Server A | | Server B | +------+-----+------+ +---+-----+---------+ | | | | 5 | | 2 2 | | 5 | | | | v v v v +--+-----+---+ +-+-----+----+ | DB 1 | | DB 2 | +------------+ +------------+ 优缺点: 优点</description>
</item>
<item>
<title>视频:奥斯卡最佳动画短片 - Alike</title>
<link>/awesome/academy-award-for-best-animated-short-alike/</link>
<pubDate>Thu, 26 Dec 2019 00:00:00 +0000</pubDate>
<guid>/awesome/academy-award-for-best-animated-short-alike/</guid>
<description>0x00 《Alike》 document.getElementById("video-bilibili").style.height=document.getElementById("video-bilibili").scrollWidth*0.5+"px"; </description>
</item>
<item>
<title>Python 标准库:socket</title>
<link>/blog/2019/python/python-standard-library-socket/</link>
<pubDate>Tue, 24 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/python/python-standard-library-socket/</guid>
<description>Btw:
Python3 中,Queue 模块已经重命名为 queue。Python3.7 中增加了一个 SimpleQueue 类,其他内容同 Python2。
0x00 Overview Queue 模块实现的是多生产者、多消费者的队列。因此它是线程安全的,可以在多线程下使用。因为 Queue 类内部实现了所有必须的锁。
Queue 模块提供了三种类型的队列,三种类型的主要差异是获取数据的顺序不同。
0x01 队列 class:Queue(maxsize=0)
FIFO(先进先出)队列。
maxsize 用来设置队列的最大容量,一旦到达最大值,插入操作就会被阻塞住,直到队列内的内容被消费。
如果 maxsize 小于等于 0,队列的容量是无限大。
class:LifoQueue(maxsize=0)
LIFO(先进后出)队列,类似于栈。
其他规则同 Queue。
class:PriorityQueue(maxsize=0)
优先队列,内部使用 heapq 实现。
其他规则同 Queue。
优先返回优先级低的数据,典型的数据模式是一个元组:(priority_number, data)
exception:Empty
在一个空队列调用非阻塞 get() 或者 get_nowait() 时会抛出此异常。 exception:Full</description>
</item>
<item>
<title>Leetcode:#32 最长有效括号</title>
<link>/blog/2019/algorithm/leetcode-32-longest-valid-parentheses/</link>
<pubDate>Mon, 23 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/algorithm/leetcode-32-longest-valid-parentheses/</guid>
<description>0x00 题目描述 #32
给定一个只包含 ( 和 ) 的字符串,找出最长的包含有效括号的子串的长度。
输入: &#34;(()&#34; 输出: 2 解释: 最长有效括号子串为 &#34;()&#34; 输入: &#34;)()())&#34; 输出: 4 解释: 最长有效括号子串为 &#34;()()&#34; 0x01 约束 注意字符串为空的情况 0x02 题解 一般看到 最长、最优 等求最优解的题目,首先应该想到动态规划。
看到一个题,你首先应该能想到用什么现有的算法来解决,如果第一眼匹配不到现有的算法,那基本这个题你是解不出来的。因为不是专门研究算法的同学,能想到新的优秀的算法是很难的。
算法 1:无头绪 之前遇到一个类似的题,没有任何头绪。暴力破解肯定是能想出来的,但是是蠢办法。
算法 2:动态规划 找状态和状态转移方程(类似于最长回文子串):
状态: dp[i][j] 表示从 i-j 的子串是不是有效的括号 第一次就将状态找错了,DP 也可以是一维的三维的,不要形成思维定式。这里就是一维的状态:dp[i] 中 i 表示在 s 中,下标以 i 结束的子串,有效括号的个数 状态转移方程: DP 最不好找的就是转移方程 这里分 3 种情况: 以 ( 结尾的肯定不是有效括号,直接忽略处理 以 ) 结尾的字符串中,如果 i-1 是 (,那么 dp[i] = dp[i-2] + 2(前两个字符索引处对应的最长括号有效个数 + 新增的 1 个有效括号) 以 ) 结尾的字符串中,如果 i-1 是 ),那么久追溯到 i-1 索引处对应的有效括号的最左端,即 i-dp[i-1],然后查看 i-dp[i-1]-1是不是 (,如果是的话,dp[i] = dp[i-1]+2+dp[i-dp[i-1]-2],其中 dp[i-dp[i-1]-2] 是前面的有效括号个数 def longestValidParentheses(s): sLen = len(s) if sLen &lt; 2: return 0 dp = [0] * sLen for i in range(1, sLen): if s[i] == &#39;)&#39;: if s[i-1] == &#39;(&#39;: tmp = 0 if i-2 &lt; 0 else dp[i-2] dp[i] = tmp + 2 else: if i-dp[i-1]-1 &gt;=0 and s[i-dp[i-1]-1] == &#39;(&#39;: dp[i] = dp[i-1] + 2 + dp[i-dp[i-1]-2] return max(dp) PS:动态规划的时间复杂度其实也不小(O(n²)),只是避免了很多重复的计算。</description>
</item>
<item>
<title>Leetcode:#10 正则表达式匹配</title>
<link>/blog/2019/algorithm/leetcode-10-regular-expression-matching/</link>
<pubDate>Sun, 22 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/algorithm/leetcode-10-regular-expression-matching/</guid>
<description>0x00 题目描述 #10
给定一个字符串 s 和一个字符规则 p,实现一个支持 . 和 * 的正则表达式匹配。其中 . 匹配任意单个字符,* 匹配零个或多个前面的那一个元素。
所谓匹配,是要涵盖整个字符串 s,而不是 s 的子串。类似于 re.match 而不是 re.search。
输入: s = &#34;aa&#34; p = &#34;a&#34; 输出: false 解释: &#34;a&#34; 无法匹配 &#34;aa&#34; 整个字符串。 0x01 约束 s 可能为空,且只包含 a-z 的小写字母 p 可能为空,且只包含 a-z 的小写字母,以及字符 . 和 * 0x02 题解 一般看到 最长、最优 等求最优解的题目,首先应该想到动态规划。
看到一个题,你首先应该能想到用什么现有的算法来解决,如果第一眼匹配不到现有的算法,那基本这个题你是解不出来的。因为不是专门研究算法的同学,能想到新的优秀的算法是很难的。
算法 1:无头绪 开始拿到这个题,我感觉无从下手,想不到什么好的办法(没有头绪)。
只是想到 s 和 p 各拿一个指针,从左到右的扫描。接下来需要进行各种 if else 的判断,而且判断条件很复杂,不一定能做出来。</description>
</item>
<item>
<title>Leetcode:#204 计数质数</title>
<link>/blog/2019/algorithm/leetcode-204-count-primes/</link>
<pubDate>Sun, 22 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/algorithm/leetcode-204-count-primes/</guid>
<description>0x00 题目描述 #204
统计所有小于非负整数 n 的质数的数量。
0x01 约束 n 为非负整数
0 和 1 不是质数
n 溢出的情况
0x02 题解 什么是质数? 质数就是只能被 1 和 自己整除的数字,也叫素数。
算法 1:暴力破解法 暴力破解法是最简单也是最容易想到的算法
遍历 n 个数字,判断每个数字是不是质数,判断是不是质数的方法是遍历小于它的每个数字,看能不能被整除。 这个算法太愚蠢了,时间复杂度也很大 O(n²)。
算法 2:埃拉托斯特尼筛法 我最初卡在如何判断一个数字是不是质数的问题上。其实判断一个数是不是质数,不需要判断比他小的每一个数字可不可以被整除,只需要判断到 [1-sqrt(n)]。
比如说 12 = 2 x 6,12 = 3 x 4,12 = sqrt(12) x sqrt(12),12 = 4 x 3,12 = 6 x 2。从 sqrt(12) 分割,前半部分和后半部分是对称的,所以只需要判断 [1-sqrt(n)] 区间内的数字可不可以被整除就 ok 了。</description>
</item>
<item>
<title>Leetcode:#5 最长回文子串</title>
<link>/blog/2019/algorithm/leetcode-5-longest-palindromic-substring/</link>
<pubDate>Sun, 22 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/algorithm/leetcode-5-longest-palindromic-substring/</guid>
<description>0x00 题目描述 #5
给定一个字符串 s,找到 s 中最长的回文子串。
输入: &#34;babad&#34; 输出: &#34;bab&#34; 注意: &#34;aba&#34; 也是一个有效答案。 0x01 约束 s 最大长度为 1000 注意字符串为空的情况 0x02 题解 一般看到 最长、最优 等求最优解的题目,首先应该想到动态规划。
看到一个题,你首先应该能想到用什么现有的算法来解决,如果第一眼匹配不到现有的算法,那基本这个题你是解不出来的。因为不是专门研究算法的同学,能想到新的优秀的算法是很难的。
算法 1:暴力破解法 这是一个蠢方法,把所有的子串找出来,判断每个子串是不是回文的,然后找出最长的那个子串返回。
找出所有子串的时间复杂度是 O(n²)。
算法 2:动态规划 找状态和状态转移方程:
状态:dp[i][j] 表示从 i-j 的子串是不是回文的 状态转移方程:dp[i][j] = s[i] == s[j] and (j-1 &lt;= 2 or dp[i+1][j-1] == True) def longestPalindrome(s): sLen = len(s) if sLen == 0: return s dp = [[0] * sLen for _ in range(sLen)] ret = s[0] for j in range(1, sLen): # 如果使用 range(sLen) 来循环,提交会超时,可能与题目限制 s 最大为 1000 有关,多一次循环就给超时了 for i in range(j): if s[i] == s[j] and (j-i &lt;= 2 or dp[i+1][j-1]): dp[i][j] = 1 if j-i+1 &gt; len(ret): ret = s[i:j+1] return ret PS:动态规划的时间复杂度其实也不小(O(n²)),只是避免了很多重复的计算。</description>
</item>
<item>
<title>Python 标准库:re</title>
<link>/blog/2019/python/python-standard-library-re/</link>
<pubDate>Sun, 22 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/python/python-standard-library-re/</guid>
<description>NOTE:
正则表达式的 pattern 和被匹配的字符串可以是 Unicode 和 8-bit 字符串 正则表示式使用反斜杠(\)来表示特殊的集合,要匹配反斜杠需要使用双反斜杠(\)来表示。Python 中的字符串恰好也使用反斜杠(\)来表示特殊字符或集合,所以要是用 Python 字符串来表示正则表达式中的反斜杠需要使用 4 个反斜杠(\\)。有一个简单的方法,就是使用 Python 的 raw string(字符串前加前缀 r),raw string 内的字符串不进行任何转译。 0x00 正则表达式语法 这个知识点很复杂,需要单独来讨论,先给个参考链接:Regular Expression Syntax
0x01 模块内容 这里主要讲 re 模块内的函数使用
re.compile(pattern, flags=0)
将正则表达式的 pattern 编译成 _sre.SRE_Pattern 对象。 通过指定 flag 的值可以改变正则表达式匹配的行为,flag 的值可以通过 | 操作符组合多个值。 re.compile() 返回的对象可以多次使用,不需要使用时再次编译,提高效率。但是,如何一个程序中只使用很少的几个正则表达式,那就不需要担心编译的问题了,因为 re.match()、re.search()、re.compile() 等都会将编译后的对象进行缓存(使用的正则表达式多了就不行了,因为毕竟缓存是有限的)。 re.DEBUG
显示被编译的表达式的 debug 信息 re.I &amp;&amp; re.IGNORECASE
re.I 是 re.IGNORECASE 的缩写。 进行匹配的时候忽略大小写,如果需要对 Unicode 类型的字符使用这个效果,可以添加 re.</description>
</item>
<item>
<title>程序员必须知道的常识</title>
<link>/blog/2019/computer-basics/what-programmers-must-know/</link>
<pubDate>Fri, 20 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/computer-basics/what-programmers-must-know/</guid>
<description>写在前面:
这里总结的很多常识只是根据当时的计算机发展水平来预估的,给大家提供一个大致的参考,存在过时的可能(我会不定时更新)。
eg:redis 读操作最高能达到 10W QPS,这个只是针对当下的计算机水平和固定的 redis 版本评估出来的,过几年突破 100w 也是有可能的。
0x00 计算机 从内存读取 1M 的数据需要 250 微秒,SSD 需要 4 倍的时间,磁盘需要 80 倍的时间。 从内存顺序读的速度是 4G/s 从 SSD 顺序读的速度是 1G/s,内存的 1/4 从磁盘顺序读的速度是 30M/s,SSD的 1/30 一个月有 2.5M(250万)秒 位运算:右移一位相当于除以 2,左移一位相当于乘以 2 有一些十进制数的小数无法转换成二进制数(0.1) 内存和 CPU 都是集成电路(IC) 8-bit string 就是 ASCII 编码的字符串 0x01 网络 从 1G 的以太网顺序读的速度是 100M/s 同一个数据中心内数据往返 2000次/s 0x02 数据库 Redis 单机一般可以抗住读 100k QPS,写 80k QPS MySQL 单机一般可以抗住 5k 左右 QPS MySQL 单表大于 2000 万行或者大于 50-100G 就有压力 MySQL 单实例存储到达 3-3.</description>
</item>
<item>
<title>Python 标准库:Queue</title>
<link>/blog/2019/python/python-standard-library-queue/</link>
<pubDate>Thu, 19 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/python/python-standard-library-queue/</guid>
<description>Btw:
Python3 中,Queue 模块已经重命名为 queue。Python3.7 中增加了一个 SimpleQueue 类,其他内容同 Python2。
0x00 Overview Queue 模块实现的是多生产者、多消费者的队列。因此它是线程安全的,可以在多线程下使用。因为 Queue 类内部实现了所有必须的锁。
Queue 模块提供了三种类型的队列,三种类型的主要差异是获取数据的顺序不同。
0x01 队列 class:Queue(maxsize=0)
FIFO(先进先出)队列。
maxsize 用来设置队列的最大容量,一旦到达最大值,插入操作就会被阻塞住,直到队列内的内容被消费。
如果 maxsize 小于等于 0,队列的容量是无限大。
class:LifoQueue(maxsize=0)
LIFO(先进后出)队列,类似于栈。
其他规则同 Queue。
class:PriorityQueue(maxsize=0)
优先队列,内部使用 heapq 实现。
其他规则同 Queue。
优先返回优先级低的数据,典型的数据模式是一个元组:(priority_number, data)
exception:Empty
在一个空队列调用非阻塞 get() 或者 get_nowait() 时会抛出此异常。 exception:Full</description>
</item>
<item>
<title>音乐:Photograph</title>
<link>/awesome/ed-sheeran-photograph-mv/</link>
<pubDate>Thu, 19 Dec 2019 00:00:00 +0000</pubDate>
<guid>/awesome/ed-sheeran-photograph-mv/</guid>
<description>0x00 Photograph - Ed Sheeran document.getElementById("video-bilibili").style.height=document.getElementById("video-bilibili").scrollWidth*0.5+"px"; </description>
</item>
<item>
<title>TED演讲:什么是区块链?</title>
<link>/awesome/what-is-block-chain/</link>
<pubDate>Wed, 11 Dec 2019 00:00:00 +0000</pubDate>
<guid>/awesome/what-is-block-chain/</guid>
<description>0x00 《什么是区块链?》 document.getElementById("video-bilibili").style.height=document.getElementById("video-bilibili").scrollWidth*0.5+"px"; </description>
</item>
<item>
<title>抛弃 Python2,从此使用 Python3</title>
<link>/blog/2019/python/why-upgrade-python3/</link>
<pubDate>Wed, 11 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/python/why-upgrade-python3/</guid>
<description>为什么要使用 Python3?我 2 用的好好的。(py 版本演进过程;2018 年开发者问卷调查;)
Python 2 3 的特性对比
到底写 2 还是写 3,还是两个都写。(如果是自己的项目,只写 py3,如果是提供的第三方库,需要写 3 代码来兼容 2。为什么不是写 2 的代码兼容 3 呢?2 孔的插座好比 Python2,3 孔的好比 Python3,本来是 2 孔的充电器插 2 孔的电源,3 孔的插 3 孔的。现在假如 2 孔的插座快淘汰了,你是愿意给 3 孔的充电器安装一个 2 孔的转接头,让他可以用使用 2 孔的插座呢,还是另外一种做法)
如何逐步迁移到 Python 3(不迁移的话,你永远在使用 py2,因为你迈不出第一步)(需要注意很多老库不支持 py3)
迁移工具有哪些
如何部署 Python 2 和 Python3 的环境,并且还有虚拟环境
0x00 为什么要升级到 Python3 Python2
Python2 算是一种比较古老的语言 2010 年 7 月,发布 2.7.0 版本的时候,就宣布了以后不会再发布 Python 2.x 大版本。参考 PEP 404 2020 年 1月 1 日以后,Python 官方不再支持 Python2。最初是打算 2014 年停止支持,发现大多数人还是没有迁移到 Python3,所以推迟到 2020 年。参考 PEP 373 Python3</description>
</item>
<item>
<title>视频:林轩田机器学习基石</title>
<link>/awesome/machine-learning-foundation/</link>
<pubDate>Wed, 11 Dec 2019 00:00:00 +0000</pubDate>
<guid>/awesome/machine-learning-foundation/</guid>
<description>0x00 《机器学习基石》 document.getElementById("video-bilibili").style.height=document.getElementById("video-bilibili").scrollWidth*0.5+"px"; </description>
</item>
<item>
<title>Python 与 Golang 语法特性对比</title>
<link>/blog/2019/go/go-compare-python/</link>
<pubDate>Tue, 10 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/go/go-compare-python/</guid>
<description>0x00 缘起 之前整体把 golang 的语法过了一遍,花了大概几个小时。
然鹅,2 天没写 go 代码,语法就忘完了,还得回过头去查一次,真是浪费时间。
所以,我就想到了这个联想记忆,通过熟悉的东西,来记住陌生的东西。
0x01 语法特性对比 参考:go 语言之旅
Python Go 包声明 文件名 文件内定义:package main 引入包 import json import &ldquo;math/rand&rdquo; 模块对象暴露 __all__ 包内对象首字母大写 函数 def 关键字 func 关键字 函数多指返回 return x, y return x, y(特例:func split(sum int) (x, y int) { x = sum * 4 / 9; y = sum - x; return }) 变量 动态类型 关键字 var,在变量名后声明(多个变量类型相同时,前边类型可以忽略) 变量初始化 直接赋值 如果提供初始值,则不需要指明类型 短变量声明 / :=(函数外必须使用 var 关键字) 基本类型 int、string、bool、list、dict、tuple bool、string、int[8, 16, 32, 64]、uint[8, 16, 32, 64]、uintptr、byte(uint8 的别名)、rune(int32 的别名;表示一个 Unicode 码点)、float32、float64、complex64、complex128 零值 / 没有赋初值的变量默认为零值(int 为 0;bool 为 false;字符串为 &lsquo;';指针为 nil;切片为 nil,它的长度和容量为 0,且没有底层数组;映射为 nil,nil 映射既没有键,也不能添加键;) 类型转换 同 go 语言 T(v) 将值 v 转换为类型 T(不同类型直接赋值需要显式转换) 常量 与变量相同 关键字 const,不能使用 := 语法(常量不指明类型的话,会根据上下文自动识别其类型) for for i in [1, 2, 3]: for i:=0; i&lt;10; i++ {xxx}(初始化语句和后置语句是可选的) while while True: for sum &lt; 1000 {xxx} 死循环 while True: for {xxx} if if i &lt; 10: xxx else: xxx if v := math.</description>
</item>
<item>
<title>Hugo 配置 staticman 静态评论平台</title>
<link>/blog/2019/blog/hugo-plus-staticman/</link>
<pubDate>Mon, 09 Dec 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/blog/hugo-plus-staticman/</guid>
<description>0x00 起因 我使用的 hugo 主题 hugo-future-imperfect,默认支持 disqus 和 staticman 的评论方式。
奈何有一些不可描述的原因,disqus 无法访问,遂转投 staticman 怀抱,这才开启了踩坑之路。
0x01 太年轻 准备按照 staticman 的 Getting started 一步一步执行。
可谓是出师不利,第一步就过不去:无法把 staticmanapp 账户添加到 Collaborators 中。
随后访问 https://api.staticman.net/v2/connect/LoveXiaoLiu/blog-comment 也是各种报错。
于是去 staticman 的 issue 区找找,果然很多人遇到了这个问题。
出现这个问题的原因是由于使用 staticman 的人太多了,导致部署的公共 staticman app 达到了 Github 的限制(Github 限制每个用户每小时只能调用 API 5K 次)。
为什么会达到这个限制呢? staticman 的工作方式是这样的,你将 staticman 添加到你的博客系统以后,需要将 staticmanapp 账户添加到你的 repo Collaborators 中。当有人在你的博客中提交评论,staticman 会将这些评论内容通过公开的 staticman app,以提交代码的方式提交到你的 repo 中。问题就出在这里,提交代码的账户就是 staticmanapp,随着 staticman 的用户越来越多,staticmanapp 肯定会超过这个限制的。 目前这个账户已经被注销。 针对这个问题,维护人员咨询了 GitHub 官方,官方回复让他创建一个 GitHub App 参考 issues 243 0x02 自己动手 本来想自己动手搭建一个 staticman app(参考这个教程)</description>
</item>
<item>
<title>HTTP 协议透彻解析</title>
<link>/blog/2019/web/http-overview/</link>
<pubDate>Thu, 18 Jul 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/web/http-overview/</guid>
<description>keywords:
http 协议是什么(历史、未来发展) http 协议的传输过程 http 协议包内容 http 协议的问题 0x00 HTTP 协议是什么 btw:什么是协议? 协议不光在计算机中使用,我们平时生活中经常会用到协议。比如说两个网友奔现,双方都不认识,那就必须定义一个约定或者协议:男方来了必须穿黑色牛仔裤,手里拿一束花,女方必须穿白色上衣,背红色包包。这样,才能互相认识,否则就无法完成奔现。
先参照上面的例子简单说下 HTTP 协议:女方就好比是 Client 端,男方就好比是 Server 端。Client 和 Server 进行通信的时候双方不认识,那就必须遵守某种协议才能进行交互。这个协议就是 HTTP 协议。
接下来用正式的、官方的解释说下 HTTP 协议:HTTP 协议是一种通过 Web 获取资源(HTML、CSS、文本等)的协议。它是数据交互的基础,是一种 C-S 结构的协议。
官方的解释很清楚,HTTP 协议的作用就是在网络上获取资源。
Key point:
HTTP 交互的是单独的 message,而不是数据流(stream of data) HTTP 是应用层协议,通过 TCP 或者 TLS 加密的 TCP 连接发送数据 HTTP 协议的高度可扩展性,使它能传输任何类型的文件(video、imag 等) Client 与 Server 中间可以有多层代理 HTTP 协议依靠 HTTP headers 可以自由的扩展功能 HTTP 协议是无状态的(stateless,同一个连接先后发送的两个请求是没有联系的),但是是有会话状态的(not sessionless,通过 HTTP header 的可扩展性,HTTP cookie 支持有状态的会话) 历史 早在 1990 年,HTTP 协议就被设计出来了,它是一个高度可扩展的协议,灵活性高,所以发展的很迅速。</description>
</item>
<item>
<title>开发 Python 第三方包,上传到 PyPI</title>
<link>/blog/2019/python/python-package-pypi/</link>
<pubDate>Tue, 09 Jul 2019 00:00:00 +0000</pubDate>
<guid>/blog/2019/python/python-package-pypi/</guid>
<description>0x00 初始化 skeleton 开源一个 Python 第三方包,需要配置很多额外的东西(pip 需要的 setup.cfg,CI/CD 需要的 .travis.yml 等),这些东西完全没必要自己来逐一创建。
所以我们需要一个脚手架来快速创建项目的 skeleton。这里我推荐使用 cookiecutter-X 系列工具。
我这里开发 pypi 包,使用 cookiecutter-pypackage 模板。
cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git 0x01 生态配置 创建完项目 skeleton 以后,需要关联 Travis 账号、ReadTheDocs 账号等。步骤如下:
将 repo 添加到 GitHub 中 将该 repo 添加到 Travis CI 中(需要注册 Travis 账号) 安装 dev requirement.txt 到虚拟开发环境中:pip install -r requirements_dev.txt 注册项目到 PyPI 中(需要注册 PyPI 账号) 生成 tar&amp;wheel 包:python setup.py sdist bdist_wheel 上传包到 PyPI 中:python -m twine upload dist/* 将该 repo 添加到 ReadTheDocs 中 0x02 迭代开发 更新项目代码,开发新 features 使用 bumpversion 升级版本:bumpversion --current-version 0.</description>
</item>
<item>
<title></title>
<link>/blog/_template/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>/blog/_template/</guid>
<description>template: 使用模板时,删除这句话。+++ title = &ldquo;标题&rdquo; description = &ldquo;文章简述&rdquo; author = &ldquo;gra55&rdquo; date = &ldquo;2021-12-20&rdquo; categories = [&ldquo;计算机基础&rdquo;] tags = [&ldquo;基础&rdquo;, &ldquo;2021&rdquo;] [[images]] src = &ldquo;img/2019/12/macau_back_china.jpg&rdquo; alt = &ldquo;1999年12月20日葡萄牙结束统治澳门,中国政府恢复对澳门行使主权,中华人民共和国澳门特别行政区成立&rdquo; stretch = &ldquo;horizontal&rdquo; +++
引用:
0x00 简述 0x01 章节一 0x02 章节二 0x03 章节三 0xff 参考 </description>
</item>
<item>
<title>About Gra55</title>
<link>/about/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>/about/</guid>
<description>I'm a full stack engineer, working for 6 years.
I like playing basketball 🏀, and I like Kobe too, because he is a man with strong will, we call it MAMBA spirit.</description>
</item>
<item>
<title>Contact</title>
<link>/contact/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>/contact/</guid>
<description></description>
</item>
</channel>
</rss>