-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
2042 lines (1611 loc) · 202 KB
/
index.html
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
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.2.0">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<link rel="stylesheet" href="/lib/pace/pace-theme-minimal.min.css">
<script src="/lib/pace/pace.min.js"></script>
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"example.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
</script>
<meta name="description" content="永远不要高估自己">
<meta property="og:type" content="website">
<meta property="og:title" content="安妮的心动录的园子">
<meta property="og:url" content="http://example.com/index.html">
<meta property="og:site_name" content="安妮的心动录的园子">
<meta property="og:description" content="永远不要高估自己">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="安妮的心动录">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="http://example.com/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : true,
isPost : false,
lang : 'zh-CN'
};
</script>
<title>安妮的心动录的园子</title>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">安妮的心动录的园子</h1>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="home fa-fw"></i>首页</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories/" rel="section"><i class="th fa-fw"></i>分类</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签</a>
</li>
<li class="menu-item menu-item-about">
<a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a>
</li>
<li class="menu-item menu-item-resources">
<a href="/resources/" rel="section"><i class="download fa-fw"></i>资源</a>
</li>
</ul>
</nav>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content index posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="安妮的心动录">
<meta itemprop="description" content="永远不要高估自己">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="安妮的心动录的园子">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/" class="post-title-link" itemprop="url">你真的了解分布式理论吗?</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-01-11 11:13:28 / 修改时间:11:21:33" itemprop="dateCreated datePublished" datetime="2024-01-11T11:13:28+08:00">2024-01-11</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%88%86%E5%B8%83%E5%BC%8F/" itemprop="url" rel="index"><span itemprop="name">分布式</span></a>
</span>
</span>
<br>
<span class="post-meta-item" title="本文字数">
<span class="post-meta-item-icon">
<i class="far fa-file-word"></i>
</span>
<span class="post-meta-item-text">本文字数:</span>
<span>4.7k</span>
</span>
<span class="post-meta-item" title="阅读时长">
<span class="post-meta-item-icon">
<i class="far fa-clock"></i>
</span>
<span class="post-meta-item-text">阅读时长 ≈</span>
<span>4 分钟</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="分布式系统"><a href="#分布式系统" class="headerlink" title="分布式系统"></a>分布式系统</h2><p>随着时代的发展,传统的单台机器可能无法完成我们期待的任务,所以发展出了分布式系统这么一个概念,目的是为了提高系统的性能、可伸缩性、可用性、容错性。其本质就是<strong>堆机子</strong>,通过增加机器数量,完成之前单台机器难以完成的任务。<br><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1704869043389-1cd50b6b-7594-4807-a8f9-6f514f51e568.png" alt="image.png"><br><strong>分布式系统:多台独立的计算机组成的系统,我们将每一台计算机视为一个结点,这些节点通过网络互相通信和协作。</strong><br><strong>但是引入了分布式系统之后,随之而来的是单体架构不会出现的问题。</strong><br>比如</p>
<ul>
<li>通信和网络问题:在单体架构中,只有一台机器,无需和其他机器进行通信。而在分布式系统中,节点之间通过网络进行通信,这就导致可能会出现消息延迟、丢包、网络不稳定等问题造成的系统稳定性下降</li>
<li>一致性和可用性的权衡:在单体架构中,一台机器接受请求之后进行处理,处理完成即可响应。但是在分布式系统中,在出现网络分区时,你可以选择一致性(先同步数据、再进行响应),也可以选择可用性(先进行响应、再进行数据同步)。</li>
<li>数据一致性问题:多节点环境下,确保数据的一致性变得更为复杂,而且无法做到数据的实时强一致性,只能保证系统在一定时间的稳定运行之后,各节点的数据趋于一致。</li>
</ul>
<p>为了解决这些问题,业界提出了一些分布式基石级别的理论以及落地方案。<br>本文会向大家介绍分布式的一些基础理论,以及流行的分布式算法。</p>
<h2 id="分布式基础理论"><a href="#分布式基础理论" class="headerlink" title="分布式基础理论"></a>分布式基础理论</h2><h3 id="CAP理论"><a href="#CAP理论" class="headerlink" title="CAP理论"></a>CAP理论</h3><p>CAP是分布式系统方向中的一个非常重要的理论,可以粗略的将它看成是分布式系统的起点,CAP分别代表的是分布式系统中的三种性质,分别是<strong>Consistency(可用性)、Availability(一致性)、Partition tolerance(网络分区容忍性),它们的第一个字母分别是C A P,于是这个理论被称为CAP理论。</strong></p>
<ul>
<li>一致性(Consistency):所有节点在同一时间看到的数据是一致的。在分布式系统中,一致性要求所有节点对于某个操作的执行都具有相同的视图。</li>
<li>可用性(Availability):系统在有限时间内能够为用户提供满足要求的响应,即系统对于请求的响应不能无期限的延迟或失败。</li>
<li>分区容忍性(Partition Tolerance):成熟的分布式系统必须满足的性质,系统能够在节点之间发生网络分区的请款修改继续工作。</li>
</ul>
<p>理论上来说,CAP三者同时最多满足两者,但是并不是必须满足两个,许多系统最多只能同时满足0、1个</p>
<p><strong>为什么CAP最多只能满足两个呢?</strong></p>
<p>我们可以以电商系统来当做例子,这个电商系统有两台服务器,彼此之间使用网络进行通信。</p>
<p>网络正常的时候,可以同步数据到另一台机器之后,再进行返回,或者返回之后再进行数据的同步。但是一旦出现了网络隔离,那么就可以有两个选择,即<strong>先同步数据</strong>还是<strong>先返回响应</strong>。</p>
<p><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1704869386111-1a091a25-3d9e-476d-a986-caccda9b22f8.png" alt="image.png"><br>如下图所示,假设当一个请求打到了Server2这里<br><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1704869467081-c434fed3-a734-48d1-bbcc-0cf1aee5b6e0.png" alt="image.png"><br>C: 追求的是数据一致性 当有一个请求来了之后 它会等待网络隔离的情况结束之后 向另一个机器进行数据的同步</p>
<p>首先,他会在本地处理好请求,这个请求常常会伴随着某些数据的变化,比如缓存内容的变化,程序内部某些共享变量的变化等等。<br><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1704869539296-362287d4-2a9a-42c1-9b99-b3e825f081c7.png" alt="image.png"><br>之后,它会等待网络恢复,即能够和Server1进行通信。<br><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1704869568358-fab0af25-b094-4e9e-b9d6-3fb5f7477423.png" alt="image.png"><br>网络恢复后,Server1和Server2处于同一个网络中,彼此可以通信,这时进行数据的同步。<br><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1704869637891-dce91a09-8771-4d1d-9fe7-70c7dfe4e057.png" alt="image.png"><br>完成数据同步之后,返回响应。<br><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1704869833707-b7ccfa89-b9d6-4bb2-81fa-aaefb9cd252c.png" alt="image.png"></p>
<p>A: 追求的是可用性 也就是尽可能提供有效服务 当一个请求来了之后 它会立即返回 哪怕数据是陈旧的 也得优先提供服务,其他分区的节点返回的结果(数据)可能是不一样,图也是反过来的。</p>
<p>注意:这里的AC不可同时满足指的是<strong>当整个分布式系统中出现网络隔离的时候,我们不能既想着保证数据的实时强一致性,又去追求服务的可用性</strong>。</p>
<p>但是当没有网络隔离的时候,其实这两个性质是可以同时满足的,因为『同步数据』和『返回结果』这两个操作都是在同一个网络中,只有先后关系,不会因为某个操作导致另一个操作的『死等』。</p>
<p>在分布式系统中,<strong>P是会必然发生的</strong>,造成P的原因可能是网络隔离,也可能是节点宕机。</p>
<p>我们无法保证分布式系统每一时刻都不出现网络隔离,如果不满足P的特性,一旦发生分区错误,那么分布式系统就无法工作,这显然违背了分布式的理念,连最基本的分布式系统条件都没有满足</p>
<p><strong>典型的CP和AP的产品</strong></p>
<p>CP:Zookeeper 当系统在发生分区故障之后 客户端的所有请求都会被卡死或者超时 但是系统总会返回一致的数据</p>
<p>AP:Eureka 分区发生故障之后 客户端依然可以访问系统 但是获取的数据有的是新数据 有的是老数据</p>
<p>当然 ,CAP这几个特性不是BOOL类型的,而是一个范围类型,完全是看系统具体需要什么样的要求。</p>
<p>比如分区容错,有的系统一台机器出错,系统会认为不影响业务的话,认为分区不存在。只有多台机器都出问题了,系统受到严重影响才认为出现分区</p>
<h3 id="PACELC理论"><a href="#PACELC理论" class="headerlink" title="PACELC理论"></a>PACELC理论</h3><p>PACELC理论是对CAP理论的扩展,在维基百科上的定义是</p>
<p><code>It states that in case of network partitioning (P) in a distributed computer system, one has to choose between availability (A) and consistency (C) (as per the CAP theorem), but else (E), even when the system is running normally in the absence of partitions, one has to choose between latency (L) and consistency (C).</code></p>
<p>翻译:如果有分区(P),那么系统就必须在可用性(A)和一致性(C)之间取得平衡,否则(E),当系统运行在无分区的情况下,系统需要在延迟(L)和一致性(C)之间取得平衡。</p>
<p>它相比于CAP,多引入了一个延迟Latency的概念,在出现分区错误的时候,取前半部分PAC,理论和CAP的内容一致。没有出现分区错误的时候取LC,也就是Latency与Consistency。</p>
<p>当前分布式系统指导理论更替代CAP理论,理由如下</p>
<ul>
<li>PACELC更能满足实际操作中分布式系统的工作场景,是更好的工程实现策略</li>
<li>当P存在的场景下,需要在A C之间做取舍,但是实际上分布式系统大部分时间里P是不存在的,那么在L和C之间做取舍是一个更好的选择</li>
<li>PACELC可以在latency与consistency之间获得平衡</li>
</ul>
<p>要保证系统的高可用,那么就得采用冗余的思想,我的其他博文有提到4个9的异地多活策略,也是采用的数据冗余思想,而一旦涉及到了复制数据,在分布式中就一定会在Consistency和Latency之间做一个取舍<br><img src="/2024/01/11/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E5%90%97/1_1683621125204-c5637883-11bb-42f3-940d-4defe9f90030.png"><br><strong>举个例子</strong></p>
<p>在强一致性的场景下,需要三个从节点都落盘数据,才能给客户端返回OK,这个时候当master向slave同步数据的时候,超过20ms触发超时了,整个系统还是会不断的重试这个过程,这显然造成了系统的可用性比较低<br>所以我们一般都会在数据一致性和请求时延之间做一个balance</p>
<p>例如:当同步超过五次之后,认为这个节点故障,选择直接返回,可以消除写时的长尾抖动,同时给节点打上故障标签,进行后续的处理</p>
<h3 id="BASE模型"><a href="#BASE模型" class="headerlink" title="BASE模型"></a>BASE模型</h3><p>base模型是Basically Avaliable(基本可用)、Soft State(软状态)、Eventually Consistent(最终一致性)三个短语的缩写,核心思想如下</p>
<p>即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。对应到CAP中的概念,<strong>就是牺牲C,来保证AP的满足</strong>,这是对传统ACID模型的取舍,适用于大规模的分布式系统,尤其是与存储相关的分布式组件,例如分布式缓存、分布式存储等等。</p>
<p>BA:基本可用指的是当系统出现了不可预知的故障,系统依旧可用,不过可用度也许会降低,比如响应时间上出现损失,功能上只能满足基本功能等等</p>
<p>S:基于原子性而言的话,当要求多个节点数据一致时,我们认为这是一种『硬』状态,而允许系统中的数据存在中间状态,并认为其不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据时延</p>
<p>E:最终一致性,系统不可能一直都处于一个软状态中,必须有个时间期限。在期限过后,应该保证所有副本保持数据一致性,从而达到数据的最终一致性。这个时间期限取决于时延、负载、方案等等</p>
<p>在工程实践中,有这么几种最终一致性的实现策略,通常都是多种策略混合实现</p>
<ul>
<li>因果一致性:如果节点A在更新完某个数据后通知了节点B,那么节点B之后对该数据的访问和修改都是基于A更新后的值。与此同时,与节点A无因果关系的节点C的数据访问没有这样的限制</li>
<li>读已知所写:节点A更新一个数据之后,自身总是能访问到更新过的最新值,而不会访问旧值</li>
<li>会话一致性:将对系统数据的访问过程框定在了一个会话当中,系统能保证同一个有效的会话中实现客户端在一个会话中读取到该数据项永远是最新值</li>
<li>单调读一致性:如果一个节点从系统中读取出一个数据项的某个值之后,那么系统对于该节点后续的任何数据访问都不该返回更旧的值</li>
<li>单调写一致性:一个系统要能够保证来自同一个节点的写操作被顺序的执行。</li>
</ul>
<h3 id="NWR多数派理论"><a href="#NWR多数派理论" class="headerlink" title="NWR多数派理论"></a>NWR多数派理论</h3><p>NWR多数派理论是分布式系统共识和分布式一致性算法的基础,只有理解了NWR才能理解<code>Raft</code>、<code>Paxos</code>、<code>Gossip</code>这些分布式一致性算法。这个理论是分布式系统中一种常见的一致性模型,被广泛应用于保证数据的一致性和可靠性,以及系统的可用性。</p>
<p>它指的是:<strong>在多数副本的一致性模型中,只有大多数副本确认了某个操作,才认为这个操作已经完成</strong>。</p>
<p>NWR中N代表的是副本数量,W代表写入的副本数量,R则为读取的副本数量。在多数的一致性模型中,一般要求W+R>N,以保证读写操作的一致性。同样的,NWR也有着三个阶段。</p>
<ul>
<li>Negotiation(协商):所有节点通过相互通信来达成一致性决策,在协商阶段,节点之间需要同步信息,最后达成一致。</li>
<li>Write(写入):决策被转化为实际的操作,节点进行写入操作。</li>
<li>Read(读取):节点读取其他节点的值,并使用协商阶段的决策和写入阶段的结果,也就是说读一定是读的最新值。</li>
</ul>
<p>在写入操作的时候,只有W个副本被成功写入才返回成功,而在读取操作时,只有R个副本成功返回相同的数据才返回成功。这样,只要大多数副本成功确认了操作,就可以认为这个操作已经完成。</p>
<p>NWR在现有组件的应用还是很广泛的,比如Raft选主判断逻辑为投票数量>=n/2则成功选主,比如Redis的哨兵机制,有哨兵标记下线则为主观下线,>=n/2标记下线则为客观下线。</p>
<h3 id="分布式一致性算法"><a href="#分布式一致性算法" class="headerlink" title="分布式一致性算法"></a>分布式一致性算法</h3><p>分布式一致性算法用于确保在分布式系统中不同节点之间达成一致决策的算法。这些算法致力于解决由于节点故障、网络分区或并发操作等原因导致的数据不一致的问题。<br>常见的分布式一致性算法有Raft、Paxos、Gossip等等,在分布式一致性算法中,每台计算机都视为<strong>等同的节点</strong>,这和分布式事务完全相反。<br>分布式事务和分布式一致性算法的目的是一样的,本质都是将一个任务放在分布式的环境下处理和解决,但是实现则不一样。</p>
<ul>
<li>在分布式事务中,不同机器的作用是不一样的,一个任务会被拆解成多个步骤,分别交给不同的角色进行处理,全部成功则提交,一旦有失败则回滚,也就是不同机器各司其职。</li>
<li>在分布式算法中,每台机器的作用都是一样的,每一个节点都能处理请求,它的作用是进行各个节点之间的同步,并解决可能出现的问题,比如数据竞态问题(data racing)等等。</li>
</ul>
<p>具体算法的实现细节我们另开一篇文章来阐述。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文向大家介绍了常见的分布式基础理论,后面还会向大家介绍Raft、Paxos、Gossip等分布式算法。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2023/06/25/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1%E5%AE%9E%E7%8E%B0/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="安妮的心动录">
<meta itemprop="description" content="永远不要高估自己">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="安妮的心动录的园子">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2023/06/25/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1%E5%AE%9E%E7%8E%B0/" class="post-title-link" itemprop="url">微服务进程间通信实现</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2023-06-25 18:41:55 / 修改时间:18:43:09" itemprop="dateCreated datePublished" datetime="2023-06-25T18:41:55+08:00">2023-06-25</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/" itemprop="url" rel="index"><span itemprop="name">微服务</span></a>
</span>
</span>
<br>
<span class="post-meta-item" title="本文字数">
<span class="post-meta-item-icon">
<i class="far fa-file-word"></i>
</span>
<span class="post-meta-item-text">本文字数:</span>
<span>4.6k</span>
</span>
<span class="post-meta-item" title="阅读时长">
<span class="post-meta-item-icon">
<i class="far fa-clock"></i>
</span>
<span class="post-meta-item-text">阅读时长 ≈</span>
<span>4 分钟</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>博客:cbb777.fun</p>
<p>全平台账号:安妮的心动录</p>
<p>github: <a target="_blank" rel="noopener" href="https://github.com/anneheartrecord">https://github.com/anneheartrecord</a></p>
<p>下文中我说的可能对,也可能不对,鉴于笔者水平有限,请君自辨。有问题欢迎大家找我讨论</p>
<h1 id="微服务通信的难点"><a href="#微服务通信的难点" class="headerlink" title="微服务通信的难点"></a>微服务通信的难点</h1><p>在微服务架构中,不同的服务是托管在不同的代码库,运行在不同的进程甚至是机器上的,这会比单体架构中服务之间进行通信付出更大的成本</p>
<p>目前有很多进程间通信的技术可供开发者选择,服务可以基于同步请求/响应的通信机制,比如HTTP RESTFUL 或者 gRPC。另外也可以使用具有异步的消息队列,比如AMQP 或者 STOMP。消息的格式也不尽相同,可以选择具有可读性的JSON,也可以使用更加高效的、基于二进制的Protocol Buffers</p>
<p>在通信中最重要的东西其实是协议<code>Protocol</code>,只要双方进行通信,那就都是基于某种<code>Protocol</code>实现的。比如说使用MQ进行通信,以Pulsar进行举例,那么一条消息的ID通常是以下格式</p>
<p><code>分区ID:分块ID:偏移量</code></p>
<p>如果使用HTTP进行通信,那么其实<code>URL Header Body</code>如何编码及解析 也是一种协议 比如<code>HTTP HEADER</code>必带的某些属性,比如版本 以及空行等等</p>
<h2 id="语义化版本控制规范(Semvers)"><a href="#语义化版本控制规范(Semvers)" class="headerlink" title="语义化版本控制规范(Semvers)"></a>语义化版本控制规范(Semvers)</h2><p>Semvers要求API版本号由三个部分组成</p>
<ul>
<li>MAJOR 当你对API进行不兼容的更改时</li>
<li>MINOR 当你对API进行向后兼容的增强时</li>
<li>PATCH 当你进行向后兼容的错误修复时</li>
</ul>
<p>有几个地方可以在API中使用版本号,比如实现REST API,则可以使用主要版本作为URL路径的第一个元素;或者如果要实现使用消息机制的服务,则可以在发布的消息中包含版本号。这些做法都是为了正确地为API设置版本,并以受控的方式更改它们</p>
<p>例如 REST 的API路径为 <code>/v1/...</code>为前缀 就是进行主要并且不向后兼容的改变</p>
<h2 id="两类消息"><a href="#两类消息" class="headerlink" title="两类消息"></a>两类消息</h2><p>消息的格式大体可以分为文本和二进制两大类</p>
<p>文本:<code>JSON</code>和<code>XML</code>,好处是它们的可读性很高,同事也是自描述的。<code>XML</code>和<code>JSON</code>都可以允许消息的接收方只挑选它们感兴趣的值,而忽略掉其他。因此,对消息的修改可以做到很强的后向兼容性</p>
<p>弊端主要是消息往往会冗余过长,特别是XML。消息的每一次传递都必须反复包含除了值意外的属性名称,这样会造成额外的开销。另一个弊端是解析文本引入的额外开销,尤其是消息比较大的时候,因此在对性能和效率比较敏感的场景下,更得倾向于基于二进制格式的消息<br>一个典型的XML数据如下</p>
<figure class="highlight plaintext"><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"><root></span><br><span class="line"> <person></span><br><span class="line"> <name>John</name></span><br><span class="line"> <age>30</age></span><br><span class="line"> <address></span><br><span class="line"> <street>123 Street</street></span><br><span class="line"> <city>New York</city></span><br><span class="line"> <country>USA</country></span><br><span class="line"> </address></span><br><span class="line"> </person></span><br><span class="line"></root></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>一个典型的JSON数据如下</p>
<figure class="highlight plaintext"><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><br><span class="line"> "person": {</span><br><span class="line"> "name": "John",</span><br><span class="line"> "age": 30,</span><br><span class="line"> "address": {</span><br><span class="line"> "street": "123 Street",</span><br><span class="line"> "city": "New York",</span><br><span class="line"> "country": "USA"</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>二进制消息:有几种不同的二进制格式,比如<code>Protocol buffers</code> 和 <code>Thrift</code>,这两种格式都提供了一个强类型定义的<code>IDL</code>(接口描述文件),用于定义消息的格式。编译器会自动根据这些格式生成序列化和反序列化的代码。因此你不得不采用API优先的方法来进行服务设计 </p>
<p>Protocol Buffers使用tagged filed(带标记的字段)来标记数据和格式</p>
<h2 id="REST成熟度模型"><a href="#REST成熟度模型" class="headerlink" title="REST成熟度模型"></a>REST成熟度模型</h2><p><strong>REST成熟度模型</strong></p>
<ul>
<li>LEVEL 0 客户端只是向服务端发起POST 请求 进行服务调用 所有操作都通过POST进行实现</li>
<li>LEVEL 1 引入了资源的概念。要执行对资源的操作,客户端需要发出执行要执行的操作,和包含任何参数的POST请求 也是所有操作都通过POST实现</li>
<li>LEVEL 2 使用HTTP动词来执行操作,譬如GET 获取 POST创建 PUT更新</li>
<li>LEVEL 3 在由GET请求返回的资源中包含链接,这些链接能够执行该资源允许的操作。例如客户端通过订单资源的链接取消某一个订单,或者发送GET请求去获取该订单。这样也可以避免在客户端代码中写入硬链接的URL</li>
</ul>
<h2 id="在一个请求中获取多个资源的挑战–GraphQL"><a href="#在一个请求中获取多个资源的挑战–GraphQL" class="headerlink" title="在一个请求中获取多个资源的挑战–GraphQL"></a>在一个请求中获取多个资源的挑战–GraphQL</h2><p>REST资源通常以业务为导向,因此在设计REST API时的一个常见问题就是如何使得客户端可以在单个请求中检索多个相关的对象。假设客户端想要检索Order和这个Order的Consumer,纯REST API要求客户端至少发出两个请求,一个用于Order,另一个用于Consumer。更复杂的情况需要往返并且遭受更多的延迟</p>
<p>一个解决方案是API允许客户端在获取资源时检索相关资源,例如客户可以受用<code>GET /orders/order-id-1345?expand=consumer</code> 检索Order及其consumer 。这种方法在许多场景中都很有效,但对于更加复杂的场景来说,它通常是不够的,实现它也可能很耗时。</p>
<p>这就导致了GraphQL的出现,它们旨在支持高效的数据获取,以下是一个典型的GraphQL请求数据 来进行参数的请求</p>
<figure class="highlight plaintext"><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><br><span class="line"> "hero": {</span><br><span class="line"> "name": "Luke Skywalker",</span><br><span class="line"> "height": 1.72,</span><br><span class="line"> "mass": 77</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>REST优缺点</p>
<p>优点:<br>1.简单 开发者熟悉<br>2.可以使用浏览器拓展或者命令行进行测试 比如postman和curl<br>3.直接支持请求/响应方式的通信<br>4.HTTP对防火墙友好<br>5.不需要中间代理 简化结构</p>
<p>缺点:<br>1.只支持请求/响应方式的通信<br>2.可能导致可用性降低 由于客户端和服务直接通信而没有使用代理来缓冲 因此它们必须在REST API调用期间都保持在线<br>3.客户端必须知道服务实例的位置 (URL) 这是现代应用程序中的一个重要问题<br>4.在单个请求中获取多个资源具有挑战性<br>5.有时很难将多个更新操作映射到http动词</p>
<p><strong>grpc</strong>:使用REST的一个挑战是由于HTTP仅提供优先数量的动词,因此设计支持多个更新操作的REST API并不容易,避免这个问题的进程间通信方式就是GRPC 这是一个用于编写跨语言客户端和服务端的框架</p>
<p>grpc是一种基于二进制消息的协议,可以通过<code>Protocol Buffer</code>的IDL来定义grpc 的api 可以使用<code>protocol buffe</code>r编译器来生成客户端的桩 和服务端的骨架 ,分别称为<code>stub</code> 和 <code>skeleton</code>,编译器可以为各种语言生成代码,包括j<code>ava c ``nodejs</code>和<code>golang</code>等 </p>
<p>客户端和服务器底层使用的是<code>http 2</code>协议,<code>grpc api</code>由一个或者多个请求/响应消息定义组成,服务定义类似于接口,是强类型方法的集合,除了支持简单的请求/响应RPC之外,<code>grpc</code>还支持流式rpc,分为客户端流式、服务端流式、双向流式等等,并以<code>protocol buffer</code>格式交换二进制的消息数据,<code>protocol buffer</code>是一种高效且紧凑的二进制标记格式。<code>protocol buffers</code>消息的每个字段都有编号,并且有一个类型代码,消息接收方可以提取所需的字段,并跳过其无法识别的字段,因此grpc使api能够在保持向后兼容的同时进行变更</p>
<p>grpc的好处</p>
<ul>
<li>设计具有复杂更新操作的API很简单</li>
<li>具有高效、紧凑的进程间通信消息,效率很高,尤其是在交换大量消息的时候</li>
<li>支持在远程过程调用和消息传递过程中使用双向流式消息方式</li>
<li>实现了客户端和用各种语言编写的服务端之间的互操作性</li>
</ul>
<p>弊端</p>
<ul>
<li>基于REST JSON的API机制相比,请求的客户端需要做更多的工作,比如定义对应的protocol buffer文件</li>
<li>老的防火墙可能不支持http 2</li>
</ul>
<h3 id="使用服务发现"><a href="#使用服务发现" class="headerlink" title="使用服务发现"></a>使用服务发现</h3><p>只要我们进行某些远程调用,不管是<code>rpc</code>还是<code>restful ap</code>i,为了发出请求,我们都需要知道服务实例的网络位置,我们把这个过程叫做服务发现。</p>
<p>通常RPC调用的服务发现通过服务注册与发现中心来实现,而<code>restful api</code>的服务发现是通过<code>DNS + IP</code>和端口来实现的</p>
<p>在传统的应用程序中,服务实例的网络位置通常是静态的。例如,代码可以从偶尔更新的配置文件中读取网络位置。但在现代的基于云微服务的应用程序中,通常不那么简单,IP地址和服务实例并不是强关联的了,某些时候服务实例会动态的销毁、创建,空出IP地址和占用IP地址</p>
<p>因此服务实例需要具有动态分配的网络地址,并且服务实例会因为自动扩展、故障和升级的原因进行动态的更改</p>
<p>服务发现在概念上非常简单:关键是一个服务注册表,这是包含服务实例网络位置信息的一个数据库,当服务实例启动和停止时,服务发现机制会更新服务注册表。当客户端调用服务时,服务发现机制会查询服务注册表以获取可用服务实例的列表,并将请求路由到其中一个服务实例</p>
<p>实现服务发现有一下两种主要方式</p>
<ul>
<li>服务及其客户直接与服务注册表交互,比如<code>CONSUL</code>和<code>ETCD</code>等等 也就是自注册+客户端服务发现 服务实例掉哟个服务注册表的注册API来注册起网络位置,并且会定期进行健康检查,通过心跳机制防止过期;当客户端想要调用服务的时候,会查询服务注册表一获取服务实例的列表。为了提高性能,客户端可以选择缓存服务实例,然后通过负载均衡算法来选择服务实例之后发出请求。并且可以处理多平台部署的问题。比如,有一些服务在K8S上部署,其余服务在遗留环境中运行。在这种情况下,使用<code>consul</code>就能同时适用于两种环境,而基于k8s的服务发现仅能用于部署在K8S平台上的部分服务</li>
<li>基础设施来处理服务发现 Docker\K8s,部署平台为每个服务提供DNS 虚拟IP 和解析的DNS名称等等 服务注册、发现、路由完全都通过部署平台进行处理</li>
</ul>
<h3 id="消息队列"><a href="#消息队列" class="headerlink" title="消息队列"></a>消息队列</h3><p>消息队列和上述提到的不同的最大的点就是 不管是HTTP RPC 还是服务注册与发现中心</p>
<p>它们的调用都是同步的 即发出请求 立马就能得到结果</p>
<p>而消息队列可以是异步的 生产者生产完成消息之后 消费者并不一定需要立马去进行消息的消费和后续处理 </p>
<p>异步 是消息队列最重要的功能之一</p>
<p>具体的消息队列学习内容可以看笔者之前的关于消息队列系统学习的一篇博客</p>
<p>简单来说 消息队列就是</p>
<p>通过生产者、消息中间件、消费者这几个组件实现点对点和发布订阅、PULL/PUSH几种模式</p>
<p>消息队列又根据有没有消息中间件这个组件分为无代理消息和有代理消息</p>
<p>无代理消息:</p>
<ul>
<li>允许更轻的网络流量和更低的延迟 少了两次转发</li>
<li>避免了消息中间件可能成为性能瓶颈的可能性</li>
<li>不需要维护消息 降低操作复杂度</li>
</ul>
<p>弊端:</p>
<ul>
<li>服务之间需要知道彼此的位置 必须采用服务发现机制</li>
<li>导致可用性降低 因为在交换消息时 消息的发送方和接收方都必须在线</li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2023/06/08/%E4%B8%80%E5%91%A8%E6%92%B8%E5%AE%8CK8S%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5Day2/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="安妮的心动录">
<meta itemprop="description" content="永远不要高估自己">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="安妮的心动录的园子">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2023/06/08/%E4%B8%80%E5%91%A8%E6%92%B8%E5%AE%8CK8S%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5Day2/" class="post-title-link" itemprop="url">一周撸完K8S基础概念Day2</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2023-06-08 07:02:16 / 修改时间:07:07:10" itemprop="dateCreated datePublished" datetime="2023-06-08T07:02:16+08:00">2023-06-08</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
</span>
</span>
<br>
<span class="post-meta-item" title="本文字数">
<span class="post-meta-item-icon">
<i class="far fa-file-word"></i>
</span>
<span class="post-meta-item-text">本文字数:</span>
<span>7k</span>
</span>
<span class="post-meta-item" title="阅读时长">
<span class="post-meta-item-icon">
<i class="far fa-clock"></i>
</span>
<span class="post-meta-item-text">阅读时长 ≈</span>
<span>6 分钟</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>博客:cbb777.fun</p>
<p>全平台账号:安妮的心动录</p>
<p>github: <a target="_blank" rel="noopener" href="https://github.com/anneheartrecord">https://github.com/anneheartrecord</a></p>
<p>下文中我说的可能对,也可能不对,鉴于笔者水平有限,请君自辨。有问题欢迎大家找我讨论</p>
<h2 id="K8S对象"><a href="#K8S对象" class="headerlink" title="K8S对象"></a>K8S对象</h2><h3 id="什么是K8S对象"><a href="#什么是K8S对象" class="headerlink" title="什么是K8S对象"></a>什么是K8S对象</h3><p>在k8s中,对象是持久化的实体,k8s使用这些实体去表示整个集群的状态,它们描述了以下信息</p>
<ul>
<li>哪些容器化应用正在运行</li>
<li>可以被应用使用的资源</li>
<li>关于应用运行时行为的策略,比如重启策略、升级策略以及容错策略</li>
</ul>
<p>k8s对象是一种”意向表达(Record of Intent)”,一旦创建该对象,K8S系统将不断工作以确保该对象存在。最终系统应该达到K8S所谓的期望状态<br>我们操作K8S对象需要用到K8S的API,可以直接使用<code>kubectl</code>命令行,也可以在程序中使用客户端库,直接调用K8S API</p>
<p><strong>对象规约(spec)与状态(status)</strong></p>
<p>几乎每个Kubernetes对象都包含两个嵌套的字段,它们负责管理对象的配置,分别是<code>spec</code>与<code>status</code><br>对于具有spec的对象,你必须在创建对象时设置其内容,描述希望对象所具有的特征:期望状态(desired state)<br>status描述了对象的当前状态(current state),它是由k8s系统和组件设置并更新的。<br><strong>在任何时刻,控制平面都在管理对象的实际状态,以使其达成期望状态</strong></p>
<p><strong>描述K8S对象</strong></p>
<p>创建K8S对象的时候必须提供对象的spec用来描述对象的期望状态,以及对象的 一些基本信息(name kind),当时用API创建独享的时候,API在请求主体中应该包含JSON格式的数据,大部分情况下我们提供的是<code>.yaml</code>文件来为<code>kubectl</code>提供这些信息,当<code>kubectl</code>发起API请求的时候,这些信息会被转换成JSON格式</p>
<p>下面是一个.yaml的示例</p>
<figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">apiVersion: apps/v1</span><br><span class="line"></span><br><span class="line">kind: Deployment</span><br><span class="line"></span><br><span class="line">metadata:</span><br><span class="line"></span><br><span class="line"> name: nginx-deployment</span><br><span class="line"></span><br><span class="line">spec:</span><br><span class="line"></span><br><span class="line"> selector:</span><br><span class="line"></span><br><span class="line"> matchLabels:</span><br><span class="line"></span><br><span class="line"> app: nginx</span><br><span class="line"></span><br><span class="line"> replicas: 2 # 告知 Deployment 运行 2 个与该模板匹配的 Pod</span><br><span class="line"></span><br><span class="line"> template:</span><br><span class="line"></span><br><span class="line"> metadata:</span><br><span class="line"></span><br><span class="line"> labels:</span><br><span class="line"></span><br><span class="line"> app: nginx</span><br><span class="line"></span><br><span class="line"> spec:</span><br><span class="line"></span><br><span class="line"> containers:</span><br><span class="line"></span><br><span class="line"> - name: nginx</span><br><span class="line"></span><br><span class="line"> image: nginx:1.14.2</span><br><span class="line"></span><br><span class="line"> ports:</span><br><span class="line"></span><br><span class="line"> - containerPort: 80</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>除了通过<code>.yaml</code>这种声明式API的方式创建Deployment, 也可以通过<code>kubectl</code>命令行的方式,通过命令将<code>.yaml</code>文件作为参数进行创建,下面是一个栗子</p>
<figure class="highlight plaintext"><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">kubectl apply -f https://k8s.io/examples/application/deployment.yaml //应用某yaml文件 </span><br><span class="line"></span><br><span class="line">deployment.apps/nginx-deployment created // output </span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>必需字段<br>在创建K8S对象中使用的<code>.yaml</code>文件里,需要配置的字段如下</p>
<ul>
<li><code>apiVersion</code> 创建对象使用的K8S API的版本</li>
<li><code>kind</code> 想要创建的对象类型</li>
<li><code>metadata</code> 帮助唯一标识对象的一些数据 比如name uid和可选的namespace </li>
<li><code>spec</code> 所期望的该对象的状态</li>
</ul>
<h3 id="K8S对象管理"><a href="#K8S对象管理" class="headerlink" title="K8S对象管理"></a>K8S对象管理</h3><p><code>kubectl</code>命令行工具支持多种不同的方式来创建和管理k8s对象,需要注意的是应该只使用一种技术来管理k8s对象,混合和匹配技术作用在同一对象上将会导致未被定义的行为</p>
<ul>
<li>指令式命令 作用于活跃对象 支持多个写者 难度低</li>
<li>指令式对象配置 作用于单个文件 支持一个写者 难度中等</li>
<li>声明式对象配置 作用于文件目录 支持多个写者 难度高</li>
</ul>
<p><strong>指令式命令</strong></p>
<p>使用指令式命令时,用户可以在集群中的活动对象上进行操作,用户将操作传递给<code>kubectl</code>命令作为参数或者标志<br>推荐在开始的时候或者在集群中运行一次性任务使用这种方式,因为它直接作用在活动对象上,所以它不提供以前配置的历史记录<br>下面是一个创建deployment对象来运行nginx的实例</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl create deployment nginx --image nginx</span><br></pre></td></tr></table></figure>
<p><strong>指令式对象配置</strong></p>
<p><code>kubectl</code>命令制定操作,可选标志和至少一个文件名,文件内需要包含<code>YAML</code>或者<code>JSON</code>格式的对象的完整定义<br>下面是一些栗子</p>
<figure class="highlight plaintext"><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">kubectl create -f nginx.yaml //创建配置文件中定义的对象</span><br><span class="line">kubectl delete -f nginx.yaml -f redis.yaml //删除两个配置文件中定义的对象</span><br><span class="line">kubectl replace -f nginx.yaml //覆盖配置文件中定义的对象</span><br></pre></td></tr></table></figure>
<p><strong>声明式对象配置</strong></p>
<p>使用声明式对象配置时,用户对本地存储的对象配置文件进行操作,但是用户未定义要对文件执行的操作,<code>kubectl</code>会自动检测每个文件的创建、更新和删除操作,这使得配置可以在目录上工作,根据目录中配置文件对不同的对象执行不同的操作</p>
<figure class="highlight plaintext"><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">kubectl diff -f configs/ //查看configs目录下所有对象配置文件要进行的更改</span><br><span class="line">kubectl apply -f configs/ //将configs目录下要进行的更改应用</span><br></pre></td></tr></table></figure>
<h3 id="对象名称与ID"><a href="#对象名称与ID" class="headerlink" title="对象名称与ID"></a>对象名称与ID</h3><p>集群中的每一个对象都有一个名称来标识其在同类资源中的唯一性<br>每个K8S对象也有一个UID来标识在整个集群中的唯一性<br>比如同一个<code>namespace</code>下只能有一个名为abc的pod,但是可以命名一个pod和一个deployment同为abc </p>
<p><strong>名称</strong></p>
<p>是客户端提供的字符串,引用资源URL中的对象,如<code>/api/v1/pods/xxx</code><br>某一时刻,只能有一个给定类型的对象具有给定的名称。但是如果删除该对象就可以创建同名的新对象<br>名称在同一资源的所有API版本中必须是唯一的,这些API资源通过各自的API组、资源类型、命名空间和名称来区分,也就是说API版本在上下文中不相关的<br>资源命名约束有<code>DNS子域名</code>、<code>RFC 1123标签名</code>、<code>RFC 1035标签名</code>、<code>路径分段名称</code>这四种命名约束</p>
<p><strong>UID</strong></p>
<p>uid是系统生成的字符串,唯一标识对象,在K8S集群中每个生命周期中创建的每个对象都有一个不同的UID</p>
<h3 id="标签与选择算法"><a href="#标签与选择算法" class="headerlink" title="标签与选择算法"></a>标签与选择算法</h3><p><strong>标签(Labels)</strong>是附加到K8S对象(比如Pod)上的键值对,旨在指定对用户有意义并且相关的对象的标识属性,但不直接对核心系统有语义含义。标签可以用于组织和选择对象的子集,可以在创建时附加到对象,随后可以随便添加和修改,每个对象都可以定义一组键值标签,每个键对于给定的对象必须是唯一的</p>
<figure class="highlight plaintext"><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">"metadata": {</span><br><span class="line"> "labels": {</span><br><span class="line"> "key1" : "value1",</span><br><span class="line"> "key2" : "value2"</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>标签能够允许用户以松耦合的方式将他们自己的组织结构映射到系统对象,而无需客户端存储这些映射<br>常见标签</p>
<ul>
<li>“release” : “stable”, “release” : “canary”</li>
<li>“environment” : “dev”, “environment” : “qa”, “environment” : “production”</li>
<li>“tier” : “frontend”, “tier” : “backend”, “tier” : “cache”</li>
<li>“partition” : “customerA”, “partition” : “customerB”</li>
<li>“track” : “daily”, “track” : “weekly”</li>
</ul>
<p>下面是一个lables带有enviroment和app两个标签的pod </p>
<figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">apiVersion: v1</span><br><span class="line">kind: Pod</span><br><span class="line">metadata:</span><br><span class="line"> name: label-demo</span><br><span class="line"> labels:</span><br><span class="line"> environment: production</span><br><span class="line"> app: nginx</span><br><span class="line">spec:</span><br><span class="line"> containers:</span><br><span class="line"> - name: nginx</span><br><span class="line"> image: nginx:1.14.2</span><br><span class="line"> ports:</span><br><span class="line"> - containerPort: 80</span><br></pre></td></tr></table></figure>
<p>标签选择算符<br>与名称和UID不同的是,标签不支持唯一性,通常我们希望许多对象携带相同的标签<br>通过标签选择算符,客户端/用户可以识别一组对象<br>API目前支持两种类型的选择算符:等值和集合</p>
<p>等值支持三个运算符 分别是 <code>=</code> <code> ==</code> <code>!=</code> 前两者表示相等,含义相同,后者表示不相等<br>例如</p>
<figure class="highlight plaintext"><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">environment = production //environment label等于xx </span><br><span class="line">tier != fronted // tier label != xx </span><br><span class="line"></span><br><span class="line">//pod选择accelerator = nvidia </span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: Pod</span><br><span class="line">metadata:</span><br><span class="line"> name: cuda-test</span><br><span class="line">spec:</span><br><span class="line"> containers:</span><br><span class="line"> - name: cuda-test</span><br><span class="line"> image: "registry.k8s.io/cuda-vector-add:v0.1"</span><br><span class="line"> resources:</span><br><span class="line"> limits:</span><br><span class="line"> nvidia.com/gpu: 1</span><br><span class="line"> nodeSelector:</span><br><span class="line"> accelerator: nvidia-tesla-p100</span><br></pre></td></tr></table></figure>
<p>基于集合的标签允许通过一组值来过滤间 支持三种操作符 in notin 和 exists </p>
<figure class="highlight plaintext"><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">environment in (a,b) //env = a 或 b </span><br><span class="line">tirer notin (a,b) // tire != a & tirer != b </span><br><span class="line">partition //包含了partition标签</span><br><span class="line">!partition //没包含partition标签</span><br></pre></td></tr></table></figure>
<p>API</p>
<figure class="highlight plaintext"><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">kubectl get pods -l environment=production,tier=frontend </span><br><span class="line">kubectl get pods -l 'environment in (production),tier in (frontend)'</span><br></pre></td></tr></table></figure>
<h3 id="命名空间"><a href="#命名空间" class="headerlink" title="命名空间"></a>命名空间</h3><p><code>Namespace</code>提供了一种机制,将同一集群中的资源划分为相互隔离的组,同一<code>Namespace</code>内的资源名称要唯一,每个K8s资源只能在一个<code>Namespace</code>中,这其实是在多个用户之间划分集群资源的一种方法<br>初始命名空间<br>k8s启动的时候会创建四个初始化命名空间</p>
<ul>
<li><code>default</code> k8s包含这个命名空间,以便于你无需创建新的命名空间就可以使用新集群</li>
<li><code>kube-node-lease</code> 包含用于各个节点关联的<code>Lease</code>租约对象,节点租约允许<code>kubelet</code>发送心跳,由此控制面板能够检测到结点故障</li>
<li><code>kube-public</code> 所有的客户端(包括未经身份验证的客户端)都可以读取该命名空间,该命名空间主要预留为集群使用,以便某些资源可以在整个集群中可见可读</li>
<li><code>kube-system</code> 用于k8s系统创建的对象</li>
</ul>
<figure class="highlight plaintext"><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">kubectl get namespace //列出集群中现存的namespace </span><br><span class="line">kubectl run nginx --image=nginx --namespace=<namespacename></span><br></pre></td></tr></table></figure>
<p>当创建一个服务的时候,k8s会创建一个相应的DNS条目<br>该条目的形式是<code><服务名称>.<名字空间名称>.svc.cluster.local</code>,这意味着如果容器只使用服务名称,就会被解析到本地命名空间的服务<br>大多数对象都存在<code>namespace</code>中,比如pod、service 等,但是<code>namespace</code>的资源本身并不在<code>namespace</code>中,而且底层资源,比如node和持久化卷,不属于任何命名空间</p>
<h3 id="注解"><a href="#注解" class="headerlink" title="注解"></a>注解</h3><p>可以通过注解给对象添加任意的非标识的元数据,客户端可以获取这些元数据信息,注解只是添加一些元数据信息,不用来表示和选择对象,类似于编程中的注释,不过这是对于K8S对象的注释</p>
<figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">"metadata": {</span><br><span class="line"> "annotations": {</span><br><span class="line"> "key1" : "value1",</span><br><span class="line"> "key2" : "value2"</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: Pod</span><br><span class="line">metadata:</span><br><span class="line"> name: annotations-demo</span><br><span class="line"> annotations:</span><br><span class="line"> imageregistry: "https://hub.docker.com/"</span><br><span class="line">spec:</span><br><span class="line"> containers:</span><br><span class="line"> - name: nginx</span><br><span class="line"> image: nginx:1.14.2</span><br><span class="line"> ports:</span><br><span class="line"> - containerPort: 80</span><br></pre></td></tr></table></figure>
<p>通常我们可以用注解来记录以下这些信息</p>
<ul>
<li>由声明性配置所管理的字段。 将这些字段附加为注解,能够将它们与客户端或服务端设置的默认值、 自动生成的字段以及通过自动调整大小或自动伸缩系统设置的字段区分开来。</li>
<li>构建、发布或镜像信息(如时间戳、发布 ID、Git 分支、PR 数量、镜像哈希、仓库地址)。</li>
<li>指向日志记录、监控、分析或审计仓库的指针。</li>
<li>可用于调试目的的客户端库或工具信息:例如名称、版本和构建信息。</li>
<li>用户或者工具/系统的来源信息,例如来自其他生态系统组件的相关对象的 URL。</li>
<li>轻量级上线工具的元数据信息:例如配置或检查点。</li>
<li>负责人员的电话或呼机号码,或指定在何处可以找到该信息的目录条目,如团队网站。</li>
<li>从用户到最终运行的指令,以修改行为或使用非标准功能。</li>
</ul>
<h3 id="字段选择器"><a href="#字段选择器" class="headerlink" title="字段选择器"></a>字段选择器</h3><p>字段选择器<code>Field Selectors</code>允许你根据一个或者多个资源字段的值筛选K8S对象 </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl get pods --field-selector status.phase=Running //筛出来status.phase字段值为runnning的所有Pod</span><br></pre></td></tr></table></figure>
<p>不同的k8s资源类型支持不同的字段选择器,所有资源类型都支持<code>metadata.name</code>和<code>metadata.namespace</code>字段,使用不被支持的字段选择器会产生错误</p>
<h3 id="终结器"><a href="#终结器" class="headerlink" title="终结器"></a>终结器</h3><p>终结器(Finalizer)是带有命名空间的键,告诉k8s等到特定的条件被满足后,再完全删除被标记为删除的资源,<code>Finalizer</code>提醒控制器清理被删除的对象拥有的资源</p>
<p>当你告诉K8S删除一个指定了<code>Finalizer</code>的对象时,K8S API通过填充<code>.metadata.deletionTimestamp</code>来标记要删除的对象,并返回202状态码使其进入只读状态,此时控制平面或者其他组件会采取<code>Finalizer</code>所定义的行动,而目标对象仍然处于终止中(Terminating)的状态,这些行为完成后,控制器会删除目标对象相关的<code>Finalizer</code>。当<code>metadata.finalizers</code>字段为空时,kubernetes认为删除已完成并删除对象</p>
<p>工作原理<br>当使用清单文件创建资源的时候,可以在<code>metadata.finalizers</code>中指定Finalizers。当视图删除该资源的时候,处理删除请求的API服务器会注意到<code>finalizers</code>字段中的值,并进行以下操作</p>
<ul>
<li>修改对象 将开始执行删除的时间添加到<code>metadata.deletionTimestamp</code>字段</li>
<li>禁止对象被删除,直到其<code>metadata.finalizers</code>字段为空</li>
<li>返回202状态码</li>
</ul>
<h3 id="属主与附属"><a href="#属主与附属" class="headerlink" title="属主与附属"></a>属主与附属</h3><p>在K8S中,一些对象是其他对象的<code>Owner</code>。例如ReplicaSet是一组Pod的属主,具有属主的对象是属主的附属(Dependent)<br>附属对象中有一个<code>metadata.ownerReferences</code>字段,用于引用其属主对象,一个有效的属主引用,包含与属主对象同在一个命名空间下的对象名称和一个UID,K8S自动为一些对象的附属资源设置属主引用的值,这些对象包含了<code>ReplicaSet</code> <code>DaemonSet</code> <code>Deployment</code> <code>Job</code> <code>CronJob</code> <code>ReplicationController</code>等</p>
<h3 id="推荐使用的标签"><a href="#推荐使用的标签" class="headerlink" title="推荐使用的标签"></a>推荐使用的标签</h3><p>除了<code>kubectl</code>和<code>dashboard</code>之外,还可以使用其他工具来可视化和管理K8S对象,一组通用的标签可以让多个工具之间相互操作,用所有工具都能理解的通用方式描述对象<br>元数据围绕应用的概念进行组织,K8S不是平台即服服务,应用程序是非正式的,并且使用元数据进行描述,应用程序包含的定义应该是送伞的<br>共享标签和注解都是用同一个前缀:<code>app.kubernetes.io</code>,没有前缀的标签是用户私有的,共享前缀可以确保共享标签不会干扰用户自定义的标签</p>
<table>
<thead>
<tr>
<th>键</th>
<th>描述</th>
<th>示例</th>
<th>类型</th>
</tr>
</thead>
<tbody><tr>
<td>app.kubernetes.io/name</td>
<td>应用程序的名称</td>
<td>mysql</td>
<td>字符串</td>
</tr>
<tr>
<td>app.kubernetes.io/instance</td>
<td>用于唯一确定应用实例的名称</td>
<td>mysql-abcxzy</td>
<td>字符串</td>
</tr>
<tr>
<td>app.kubernetes.io/version</td>
<td>应用程序的当前版本(例如<a target="_blank" rel="noopener" href="https://semver.org/spec/v1.0.0.html">语义版本 1.0</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td>、修订版哈希等)</td>
<td>5.7.21</td>
<td>字符串</td>
<td></td>
</tr>
<tr>
<td>app.kubernetes.io/component</td>
<td>架构中的组件</td>
<td>database</td>
<td>字符串</td>
</tr>
<tr>
<td>app.kubernetes.io/part-of</td>
<td>此级别的更高级别应用程序的名称</td>
<td>wordpress</td>
<td>字符串</td>
</tr>
<tr>
<td>app.kubernetes.io/managed-by</td>
<td>用于管理应用程序的工具</td>
<td>helm</td>
<td>字符串</td>
</tr>
</tbody></table>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2023/06/08/%E4%B8%BA%E4%BB%80%E4%B9%88K8S%E8%A6%81%E6%94%BE%E5%BC%83Docker/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="安妮的心动录">
<meta itemprop="description" content="永远不要高估自己">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="安妮的心动录的园子">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2023/06/08/%E4%B8%BA%E4%BB%80%E4%B9%88K8S%E8%A6%81%E6%94%BE%E5%BC%83Docker/" class="post-title-link" itemprop="url">为什么K8S要放弃Docker</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2023-06-08 07:01:58 / 修改时间:07:07:03" itemprop="dateCreated datePublished" datetime="2023-06-08T07:01:58+08:00">2023-06-08</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
</span>
</span>
<br>
<span class="post-meta-item" title="本文字数">
<span class="post-meta-item-icon">
<i class="far fa-file-word"></i>
</span>
<span class="post-meta-item-text">本文字数:</span>
<span>3k</span>
</span>
<span class="post-meta-item" title="阅读时长">
<span class="post-meta-item-icon">
<i class="far fa-clock"></i>
</span>
<span class="post-meta-item-text">阅读时长 ≈</span>
<span>3 分钟</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>博客:cbb777.fun</p>
<p>全平台账号:安妮的心动录</p>
<p>github: <a target="_blank" rel="noopener" href="https://github.com/anneheartrecord">https://github.com/anneheartrecord</a></p>
<p>下文中我说的可能对,也可能不对,鉴于笔者水平有限,请君自辨。有问题欢迎大家找我讨论</p>
<h3 id="K8S与Docker"><a href="#K8S与Docker" class="headerlink" title="K8S与Docker"></a>K8S与Docker</h3><p>K8S是从14年发布的,到现在已经成为了容器编排领域的龙头,大部分的个人开发或者团队都会选择使用Kubernetes进行容器的管理 </p>
<p>我们可以把集群简单的理解为:一组能够在一起协同工作的计算机</p>
<p>K8S虽然是现在容器编排领域的龙头,但是他也有他的缺点<br>1.虽然Kubernetes对外宣传的是单个集群最多支持5000结点,Pod总数不超过150000,容器总数不超过30000,但是在具体生产环境中,集群可能就2000左右<br>2.多集群管理还不够成熟,是K8S社区正在探索的方向</p>
<p>集群接口:<br>Cluster API也是Kubernetes社区中和多集群管理相关的项目,目标是通过声明式的API简化多集群的准备、 更新和运维工作,也就是通过声明式API定义机器和集群的状态</p>
<p>K8S的一些应用场景<br>1.应用分发 K8S提供了几种部署应用的最基本方式,分别是Deployment StatefulSet 和 DaemonSet 这些资源分别适用于无状态服务、有状态服务和节点上的 守护进程,这些资源能够提供最基本的策略但是无法应对更复杂的应用<br>2.批处理调度<br>3.硬多租户</p>
<p>K8S是容器编排领域的事实标准,而Docker从诞生之日到今天都在容器中扮演着举足轻重的地位,也一直是K8S的默认容器引擎,然而在2020年12月,K8S社区决定着手移除仓库中Dockershim的相关代码</p>
<h3 id="Dockershim是什么?"><a href="#Dockershim是什么?" class="headerlink" title="Dockershim是什么?"></a>Dockershim是什么?</h3><p>它是Docker的<strong>垫片</strong>,K8S中的结点代理Kubelet为了访问Docker提供的服务,会先访问Dockershim,Dockershim会将请求转发给管理容器的Docker服务</p>
<p>移除的原因</p>
<ul>
<li>K8S引入容器运行时接口(CRI) 隔离不同容器运行时的实现机制,容器编排系统不应该依赖于某个具体的运行时隔离</li>
<li>Docker没有支持也不打算支持K8S中的CRI接口,需要K8S社区在仓库中维护Dockershim</li>
</ul>
<h3 id="从可扩展性的角度看问题"><a href="#从可扩展性的角度看问题" class="headerlink" title="从可扩展性的角度看问题"></a>从可扩展性的角度看问题</h3><p>K8S通过引入新的容器运行时接口将容器管理与具体的运行时解耦,不再依赖某个具体的运行时实现,K8S通过下面的一系列接口为不同模块提供了扩展性</p>
<p>K8S在较早期的版本中引入了CRD CNI CRI CSI等接口,而CRI是1.5版本引入的新接口,Kubelet可以通过这个接口使用各种各样的容器运行时,其实CRI的发布就意味着K8S一定会将Dockershim的代码从仓库中移除。</p>
<p>CRI是一系列用于管理容器运行时和镜像的GRPC接口,我们能在它的定义中找到RuntimeService和ImageService两个服务</p>
<figure class="highlight plaintext"><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">service RuntimeService { //Runtime的grpc接口 </span><br><span class="line"> rpc Version(VersionRequest) returns (VersionResponse) {}</span><br><span class="line"> rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}</span><br><span class="line"> rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}</span><br><span class="line"> rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}</span><br><span class="line"> rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}</span><br><span class="line"> rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}</span><br><span class="line"> rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}</span><br><span class="line"> rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}</span><br><span class="line"> rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}</span><br><span class="line"> rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}</span><br><span class="line"> rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}</span><br><span class="line"> rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}</span><br><span class="line"> rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}</span><br><span class="line"> rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight plaintext"><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">service ImageService { //镜像的grpc接口 </span><br><span class="line"> rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}</span><br><span class="line"> rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}</span><br><span class="line"> rpc PullImage(PullImageRequest) returns (PullImageResponse) {}</span><br><span class="line"> rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}</span><br><span class="line"> rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>而这些接口都是容器运行时需要暴露给Kubelet的接口</p>
<p>Kubernetes作为松散的开源社区,每个成员都只会在开源社区上花费有限时间,所以既然Docker社区没有打算支持K8s的CRI接口,维护Dockershim又需要很多精力,所以K8S会移除对Dockershim的支持</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2023/05/31/%E5%A6%82%E4%BD%95%E8%A7%A3%E6%9E%90%E4%B8%8D%E5%AE%9AJSON%E6%95%B0%E6%8D%AE/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="安妮的心动录">
<meta itemprop="description" content="永远不要高估自己">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="安妮的心动录的园子">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2023/05/31/%E5%A6%82%E4%BD%95%E8%A7%A3%E6%9E%90%E4%B8%8D%E5%AE%9AJSON%E6%95%B0%E6%8D%AE/" class="post-title-link" itemprop="url">“如何解析不定JSON数据”</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2023-05-31 15:34:41 / 修改时间:15:58:49" itemprop="dateCreated datePublished" datetime="2023-05-31T15:34:41+08:00">2023-05-31</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
</span>
</span>
<br>
<span class="post-meta-item" title="本文字数">
<span class="post-meta-item-icon">
<i class="far fa-file-word"></i>
</span>
<span class="post-meta-item-text">本文字数:</span>
<span>4.9k</span>
</span>
<span class="post-meta-item" title="阅读时长">
<span class="post-meta-item-icon">
<i class="far fa-clock"></i>
</span>
<span class="post-meta-item-text">阅读时长 ≈</span>
<span>4 分钟</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>博客:cbb777.fun</p>
<p>全平台账号:安妮的心动录</p>
<p>github: <a target="_blank" rel="noopener" href="https://github.com/anneheartrecord">https://github.com/anneheartrecord</a></p>
<p>下文中我说的可能对,也可能不对,鉴于笔者水平有限,请君自辨。有问题欢迎大家找我讨论</p>
<p>在开发中常常会碰到很多JSON类型的数据进行交互,而其中有很多JSON数据你是不能确定它的字段和结构的,而Go语言是一门静态强类型的语言,在进行JSON解析的时候必须要确定字段的类型,定义出对应的结构体,然后再进行Unmarshal,那这二者之间的冲突我们该如何解决呢?</p>
<h2 id="什么是JSON"><a href="#什么是JSON" class="headerlink" title="什么是JSON"></a>什么是JSON</h2><ul>
<li>json是JavaScript Object Notation(JavaScript对象表示法)</li>
<li>json是轻量级的文本数据交换格式</li>
<li>json独立于语言</li>
<li>json具有自我描述性,更容易理解</li>
<li>json使用js语法来描述数据对象,但是json仍然独立于语言和平台,json解析器和json库支持许多不同的编程语言</li>
</ul>
<p>json是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,之所以json这么流行,是因为json的结构和多级结构体(对象)刚好能对应上,而前后端交互的时候后端通常会返回给前端一个多级的结构体,于是json慢慢开始流行了,且json是跨语言和跨平台的,自身也足够轻量级。<br>json的标准格式</p>
<figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">一个标准的json数据</span><br><span class="line">//每个key对应的是一个value</span><br><span class="line">{</span><br><span class="line">“k1": 1,</span><br><span class="line">"k2": 2 //注意结尾的这个不能有逗号</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">json字符串</span><br><span class="line">{</span><br><span class="line">"k1": "1",</span><br><span class="line">"k2": "2"</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">json数组</span><br><span class="line">{</span><br><span class="line">“k1”: [1,2],</span><br><span class="line">“k2”: [3,4]</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">json对象</span><br><span class="line">{</span><br><span class="line">“k1”: {“1”: “haihai”},</span><br><span class="line">“k2”: {“2”:”haihahai”}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">json对象数组</span><br><span class="line">{</span><br><span class="line">“k1”: [</span><br><span class="line">{“k11”: “hellohello”},</span><br><span class="line">{“k12”: “badbad”}</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"></span><br><span class="line">json数组对象</span><br><span class="line">{</span><br><span class="line">“k2”: {</span><br><span class="line"> “hello”: [1,2,3]</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">所有的JSON数据都是由上述几种JSON数据组合而成</span><br></pre></td></tr></table></figure>
<h2 id="如何在Go中解析不确定的JSON数据"><a href="#如何在Go中解析不确定的JSON数据" class="headerlink" title="如何在Go中解析不确定的JSON数据"></a>如何在Go中解析不确定的JSON数据</h2><h3 id="通过测试、看文档的方式去确定对应的JSON数据,然后构造对应的结构体"><a href="#通过测试、看文档的方式去确定对应的JSON数据,然后构造对应的结构体" class="headerlink" title="通过测试、看文档的方式去确定对应的JSON数据,然后构造对应的结构体"></a>通过测试、看文档的方式去确定对应的JSON数据,然后构造对应的结构体</h3><figure class="highlight plaintext"><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">// 请求其他服务 </span><br><span class="line">jsonStr := xxx</span><br><span class="line"></span><br><span class="line">var data interface{}</span><br><span class="line"></span><br><span class="line">err := json.Unmarshal([]byte(jsonStr),&data)</span><br><span class="line"></span><br><span class="line">fmt.Println(data)</span><br></pre></td></tr></table></figure>
<p>比如可以先拿一个interface{}类型来接住JSON数据,然后看这个interface{}的值,来确定这个JSON数据哪些字段是string 哪些是object 哪些是int float等等<br>当然这也不是完全适用的,比如下面这种情况,有一个字段如下<br>type : []<br>能看出来type是一个切片类型的值,但是具体的类型你并不知道,可能是[]int 也有可能是[]string []float等等</p>
<h3 id="map-string-interface"><a href="#map-string-interface" class="headerlink" title="map[string] interface{}"></a>map[string] interface{}</h3><p>这个类型是map键值对,值可以是任意类型,因为在go中任意类型都实现了空接口interface{},而json数据也是key value的键值对,所以map[string] interface{}天然支持解析json类型数据</p>
<figure class="highlight plaintext"><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">jsonStr := xxx</span><br><span class="line">var data map[string]interface{} </span><br><span class="line">err := json.Unmarshal([]byte(jsonStr),&data)</span><br><span class="line"></span><br><span class="line">// 你想取的字段</span><br><span class="line">fieldValue := data["field"]</span><br><span class="line"></span><br><span class="line">// 类型断言</span><br><span class="line">if value,ok := data["field"].(float64);ok {</span><br><span class="line"></span><br><span class="line">} else if vluae,ok := data["field"].(int64); ok {</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">理论上所有的合法的JSON数据都可以被反序列化到map[string]interface{}中</span><br><span class="line">但是实际应用中 可能会出现一些无法被map[string]interface{}解析的JSON数据</span><br></pre></td></tr></table></figure>
<ul>
<li>JSON 数据中包含了多层嵌套的数据结构。在这种情况下,如果没有使用递归或者其他方式对嵌套数据进行处理,可能会导致反序列化失败。</li>
<li>JSON 数据中包含了数组类型,但是数组元素类型不一致或者无法转换成相应的类型。在这种情况下,可能需要手动处理数组元素或者使用其他数据类型来保存数组数据。</li>
<li>JSON 数据中包含了自定义数据类型或者复杂的数据结构,无法使用 map[string]interface{} 类型来反序列化。在这种情况下,需要定义相应的结构体或者使用其他适合的数据类型来反序列化。</li>
</ul>
<h2 id="第三方库"><a href="#第三方库" class="headerlink" title="第三方库"></a>第三方库</h2><p>除了encoding/json之外,还有很多第三方库可以用来解析不确定的JSON数据,例如gjson和jsonparser,这些库通常提供了更加灵活和高效的JSON解析方式,可以根据具体的需求选择合适的库来使用</p>
<h2 id="json-RawMessage与json-Number"><a href="#json-RawMessage与json-Number" class="headerlink" title="json.RawMessage与json.Number"></a>json.RawMessage与json.Number</h2><ul>
<li>json.RawMessage 是一个非常高效的数据类型,因为她不需要进行任何解析和类型转换,直接保存了未经处理的原始JSON数据,在反序列化的时候只需要将<code>json.RawMessage</code>转化为对应的数据类型即可,无需重新解析JSON数据</li>
<li>json.Number 表示JSON中的数字类型,可以用来保存任意精度的数字。这个数字可以特别大,可能会无法用Go中的整数或者浮点数来表示</li>
</ul>
<figure class="highlight plaintext"><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><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line">package main</span><br><span class="line"></span><br><span class="line">import (</span><br><span class="line"> "encoding/json"</span><br><span class="line"> "fmt"</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">func main() {</span><br><span class="line"> jsonData := []byte(`{</span><br><span class="line"> "id": 12345,</span><br><span class="line"> "name": "John Doe",</span><br><span class="line"> "age": 30,</span><br><span class="line"> "score": 95.5,</span><br><span class="line"> "is_student": true,</span><br><span class="line"> "tags": ["tag1", "tag2", "tag3"],</span><br><span class="line"> "extra": {</span><br><span class="line"> "field1": "value1",</span><br><span class="line"> "field2": 123</span><br><span class="line"> }</span><br><span class="line"> }`)</span><br><span class="line"></span><br><span class="line"> var m map[string]json.RawMessage</span><br><span class="line"> err := json.Unmarshal(jsonData, &m)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> var id int</span><br><span class="line"> err = json.Unmarshal(m["id"], &id)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("id: %d\n", id)</span><br><span class="line"></span><br><span class="line"> var name string</span><br><span class="line"> err = json.Unmarshal(m["name"], &name)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("name: %s\n", name)</span><br><span class="line"></span><br><span class="line"> var age int</span><br><span class="line"> err = json.Unmarshal(m["age"], &age)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("age: %d\n", age)</span><br><span class="line"></span><br><span class="line"> var score float64</span><br><span class="line"> err = json.Unmarshal(m["score"], &score)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("score: %f\n", score)</span><br><span class="line"></span><br><span class="line"> var isStudent bool</span><br><span class="line"> err = json.Unmarshal(m["is_student"], &isStudent)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("is_student: %v\n", isStudent)</span><br><span class="line"></span><br><span class="line"> var tags []string</span><br><span class="line"> err = json.Unmarshal(m["tags"], &tags)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("tags: %v\n", tags)</span><br><span class="line"></span><br><span class="line"> var extra map[string]json.RawMessage</span><br><span class="line"> err = json.Unmarshal(m["extra"], &extra)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> var field1 string</span><br><span class="line"> err = json.Unmarshal(extra["field1"], &field1)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("extra.field1: %s\n", field1)</span><br><span class="line"></span><br><span class="line"> var field2 int</span><br><span class="line"> err = json.Unmarshal(extra["field2"], &field2)</span><br><span class="line"> if err != nil {</span><br><span class="line"> panic(err)</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf("extra.field2: %d\n", field2)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 不确定的类型</span><br><span class="line">data := make(map[string]interface{})</span><br><span class="line">if err := json.Unmarshal(rawData, &data); err != nil {</span><br><span class="line"> log.Fatal(err)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">if value, ok := data["age"].(float64); ok {</span><br><span class="line"> // 处理年龄为浮点数的情况</span><br><span class="line">} else if value, ok := data["age"].(int); ok {</span><br><span class="line"> // 处理年龄为整数的情况</span><br><span class="line">} else {</span><br><span class="line"> // 处理年龄为其他类型或不存在的情况</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>需要注意的是:类型断言的底层为反射,因为在运行时需要判断一个接口值的具体类型,而这个类型是在编译时无法确定的,需要在运行时动态地获取。效率比正常的代码低一到两个数量级,而且需要消耗额外的时间和内存</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2023/05/31/Go%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="安妮的心动录">
<meta itemprop="description" content="永远不要高估自己">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="安妮的心动录的园子">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2023/05/31/Go%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95/" class="post-title-link" itemprop="url">Go模糊测试</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2023-05-31 15:34:32 / 修改时间:15:58:27" itemprop="dateCreated datePublished" datetime="2023-05-31T15:34:32+08:00">2023-05-31</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
</span>
</span>
<br>
<span class="post-meta-item" title="本文字数">
<span class="post-meta-item-icon">