-
Notifications
You must be signed in to change notification settings - Fork 1
/
raft-java.drawio
1061 lines (1061 loc) · 173 KB
/
raft-java.drawio
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
<mxfile host="Electron" modified="2024-01-30T17:20:13.104Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.5 Chrome/114.0.5735.243 Electron/25.3.1 Safari/537.36" etag="wpTWC8uj3mB21U-9H77w" version="21.6.5" type="device">
<diagram name="Raft实现" id="D0lXRkDRS7YjULPVTkGP">
<mxGraphModel dx="2796" dy="671" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="jrdKXmfmH3rlp7NtxQ88-81" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-68" target="SMY1UsQMhVWDZvxuVclS-50" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="2179" y="2530" />
<mxPoint x="2179" y="1290" />
<mxPoint x="750" y="1290" />
<mxPoint x="750" y="1265" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-1" value="<h1 style="font-size: 16px;"><font style="font-size: 16px;">Raft协议实现&nbsp;</font></h1><p style=""><font style="font-size: 12px;">源码:https://github.com/wenweihu86/raft-java。实现、案例代码加起来一共大概5K多行, 总体流程比较简单,适合Raft入门学习,不过里面还是有很多让人困惑的细节也没有给注释说明<br><span style="background-color: initial;">raft-java-examle 案例中启动了3节点的服务集群,使用 brpc 通信(百度RPC框架)</span><br></font></p><p style=""><font style="font-size: 12px;"><br></font></p>" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="1" vertex="1">
<mxGeometry x="40" y="20" width="680" height="80" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-4" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=10;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-2" target="hCxxSbGEtk1TdNpt0RpP-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-2" value="ServerMain#main()<br><font color="#007fff">假设配置的raftDataDir=data</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="40" y="120" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-6" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=10;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-3" target="hCxxSbGEtk1TdNpt0RpP-5" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-3" value="遍历3节点host:port配置创建<br style="font-size: 10px;">RaftProto.Server server = parseServer(serverString);" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="280" y="120" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-5" target="hCxxSbGEtk1TdNpt0RpP-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-5" value="RaftProto.Server localServer = parseServer(args[2]);<br style="font-size: 10px;">RpcServer server = new RpcServer(localServer.getEndpoint()<br style="font-size: 10px;">.getPort());" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="280" y="200" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-7" target="hCxxSbGEtk1TdNpt0RpP-9" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-7" value="RaftOptions raftOptions = new RaftOptions();<br>..." style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="280" y="280" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-12" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-9" target="hCxxSbGEtk1TdNpt0RpP-11" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-51" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-9" target="jrdKXmfmH3rlp7NtxQ88-50" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-9" value="ExampleStateMachine stateMachine = new <b>ExampleStateMachine</b>(<br>raftOptions.getDataDir());" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="280" y="360" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-11" target="hCxxSbGEtk1TdNpt0RpP-13" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-49" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-11" target="jrdKXmfmH3rlp7NtxQ88-48" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-11" value="RaftNode raftNode = new <b>RaftNode</b>(raftOptions, serverList, localServer, stateMachine);" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="280" y="440" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-16" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-13" target="hCxxSbGEtk1TdNpt0RpP-15" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-44" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-13" target="SMY1UsQMhVWDZvxuVclS-43" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="510.5" y="670" />
<mxPoint x="510.5" y="670" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-13" value="RaftConsensusService raftConsensusService = new&nbsp;<b style="font-size: 10px;">RaftConsensusServiceImpl</b>(raftNode);<br style="font-size: 10px;">server.<b style="font-size: 10px;">registerService</b>(raftConsensusService);<br style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">Raft节点一致性服务,提供节点间通信的能力<br style="font-size: 10px;">给其他节点调用用于保持一致性<br style="font-size: 10px;"></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="270.5" y="640" width="220" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-18" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-15" target="hCxxSbGEtk1TdNpt0RpP-17" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-15" value="RaftClientService raftClientService = new RaftClientServiceImpl(raftNode);<br style="font-size: 10px;">server.registerService(raftClientService);<br><font color="#007fff">用于节点管理,节点增减等</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="280" y="880" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-20" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-17" target="hCxxSbGEtk1TdNpt0RpP-19" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-17" target="jrdKXmfmH3rlp7NtxQ88-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-17" value="ExampleService exampleService = new ExampleServiceImpl(raftNode, stateMachine);<br style="font-size: 10px;">server.registerService(exampleService);<br><font color="#007fff">业务服务,这里是设置和读取数据</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="280" y="1680" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-22" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-19" target="hCxxSbGEtk1TdNpt0RpP-21" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-19" value="server.start();" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="280.5" y="2240" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-28" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-21" target="hCxxSbGEtk1TdNpt0RpP-27" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-21" value="raftNode.init();<br><font color="#007fff">服务节点初始化,这里启动Leader选举、心跳、日志复制等任务<br></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="280.5" y="2320" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-24" value="<div style="text-align: center;"><b>RaftNode</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">Raft服务节点</font></p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//Raft协议配置</font></p><p style="margin: 0px 0px 0px 4px;">private RaftOptions <b>raftOptions</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//集群所有节点配置信息,RaftProto.Server</font></p><p style="margin: 0px 0px 0px 4px;">private RaftProto.Configuration <b>configuration</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//封装了连接其他节点的客户端、日志条目索引等信息</font></p><p style="margin: 0px 0px 0px 4px;">private ConcurrentMap&lt;Integer, Peer&gt; <b>peerMap</b> = new ConcurrentHashMap&lt;&gt;();</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//当前节点RPC服务器</font></p><p style="margin: 0px 0px 0px 4px;">private RaftProto.Server <b>localServer</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//状态机,?</font></p><p style="margin: 0px 0px 0px 4px;">private StateMachine <b>stateMachine</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//操作日志,记录系统中操作指令,用于</font></p><p style="margin: 0px 0px 0px 4px;">private <b>SegmentedLog</b> <b>raftLog</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//快照对象</font></p><p style="margin: 0px 0px 0px 4px;">private <b>Snapshot</b> <b>snapshot</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//节点状态,初始Follower</font></p><p style="margin: 0px 0px 0px 4px;">private NodeState <b>state</b> = NodeState.STATE_FOLLOWER;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//服务器最后一次知道的任期号(初始化为 0),</font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//选举超时节点发起二阶段投票时会 + 1;</font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//Follower节点收到二阶段投票请求时如果currentTerm小于请求中的Term值会更新&nbsp;</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>currentTerm</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//票投给了哪个节点(保存节点ID)</font></p><p style="margin: 0px 0px 0px 4px;">private int <b>votedFor</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//记录Leader节点ID</font></p><p style="margin: 0px 0px 0px 4px;">private int <b>leaderId</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 已知的最大的已经被提交的日志条目的索引值</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>commitIndex</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 最后被应用到状态机的日志条目索引值(初始化为 0,持续递增)</font></p><p style="margin: 0px 0px 0px 4px;">private volatile long <b>lastAppliedIndex</b>;</p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;">private Lock lock = new ReentrantLock();</p><p style="margin: 0px 0px 0px 4px;">private Condition commitIndexCondition = lock.newCondition();</p><p style="margin: 0px 0px 0px 4px;">private Condition catchUpCondition = lock.newCondition();</p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;">private ExecutorService <b>executorService</b>;</p><p style="margin: 0px 0px 0px 4px;">private ScheduledExecutorService <b>scheduledExecutorService</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//通过Future接口可以实现取消任务,比如还未超时收到其他节点拉票请求,需要重置超时时间并取消之前设置的定时任务</font></p><p style="margin: 0px 0px 0px 4px;">private ScheduledFuture <b>electionScheduledFuture</b>;</p><p style="margin: 0px 0px 0px 4px;">private ScheduledFuture <b>heartbeatScheduledFuture</b>;</p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="-480" y="120" width="440" height="520" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-25" value="<div style="text-align: center;"><b>RaftOptions</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">Raft协议配置</font></p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 选举超时时间,超过这个时间如果没有接受到Leader的消息会变成Candidate</font></p><p style="margin: 0px 0px 0px 4px;">private int <b>electionTimeoutMilliseconds</b> = 5000;</p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;"><font color="#007fff">// 心跳周期,就是心跳超时时间,Leader为了证明自己还活着需要向其他节点定期发送心跳</font></span></p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;">private int <b>heartbeatPeriodMilliseconds</b> = 500;</span></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// snapshot定时器执行间隔,todo ?</font></p><p style="margin: 0px 0px 0px 4px;">private int <b>snapshotPeriodSeconds</b> = 3600;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// log entry大小达到snapshotMinLogSize,才做snapshot</font></p><p style="margin: 0px 0px 0px 4px;">private int <b>snapshotMinLogSize</b> = 100 * 1024 * 1024;</p><p style="margin: 0px 0px 0px 4px;">private int <b>maxSnapshotBytesPerRequest</b> = 500 * 1024; // 500k</p><p style="margin: 0px 0px 0px 4px;">private int <b>maxLogEntriesPerRequest</b> = 5000;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 单个segment文件大小,默认100m</font></p><p style="margin: 0px 0px 0px 4px;">private int <b>maxSegmentFileSize</b> = 100 * 1000 * 1000;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// follower与leader差距在catchupMargin,才可以参与选举和提供服务</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>catchupMargin</b> = 500;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// replicate最大等待超时时间,单位ms</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>maxAwaitTimeout</b> = 1000;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 与其他节点进行同步、选主等操作的线程池大小</font></p><p style="margin: 0px 0px 0px 4px;">private int raftConsensusThreadNum = 20;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 是否异步写数据;true表示主节点保存后就返回,然后异步同步给从节点;</font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// false表示主节点同步给大多数从节点后才返回。</font></p><p style="margin: 0px 0px 0px 4px;">private boolean asyncWrite = false;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// raft的log和snapshot父目录,绝对路径</font></p><p style="margin: 0px 0px 0px 4px;">private String dataDir = System.getProperty("com.github.wenweihu86.raft.data.dir");</p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-960" y="120" width="440" height="360" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-26" value="<div style="text-align: center; font-size: 10px;"><b style="font-size: 10px;">raft.proto</b></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;">关键的定义:</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br style="font-size: 10px;"></p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">message Endpoint {</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; optional string host = 1;</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; optional uint32 port = 2;</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">}</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br style="font-size: 10px;"></p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">message Server {</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; <font color="#007fff" style="font-size: 10px;">//服务节点ID</font></p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; optional uint32 server_id = 1;</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; optional Endpoint endpoint = 2;</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">}</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br style="font-size: 10px;"></p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">message Configuration {</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; repeated Server servers = 1;</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">}</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br style="font-size: 10px;"></p><p style="margin: 0px 0px 0px 4px;">message VoteRequest {</p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint32 server_id = 1; // 请求选票的候选人的 Id</p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 term = 2; // 候选人的任期号</p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 last_log_term = 3; // 候选人的最后日志条目的任期号</p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 last_log_index = 4; // 候选人最后日志条目的索引值</p><p style="margin: 0px 0px 0px 4px;">};</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">message VoteResponse {</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; optional uint64 term = 1; // 当前任期号,以便于候选人去更新自己的任期号</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">&nbsp; &nbsp; optional bool granted = 2; // 候选人赢得了此张选票时为真</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;">};</p><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><font color="#007fff">//对应 logDir下的 metadata 文件</font></p><p style="margin: 0px 0px 0px 4px;">message <b>LogMetaData</b> {</p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;">&nbsp; &nbsp; <font color="#007fff">//初始为0</font></span></p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 current_term = 1;</p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;"><font color="#007fff">&nbsp; &nbsp; //初始为0</font></span></p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint32 voted_for = 2;</p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; <font color="#007fff">//第一条日志索引,初始为1</font></p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 first_log_index = 3;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">&nbsp; &nbsp; //最后提交的日志索引,初始为0</font></p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 commit_index = 4;</p><p style="margin: 0px 0px 0px 4px;">}</p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//快照(日志压缩) 元数据</font></p><p style="margin: 0px 0px 0px 4px;">message <b>SnapshotMetaData</b> {</p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;"><font color="#007fff">&nbsp; &nbsp; //被压缩的最后一条日志索引</font></span></p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 last_included_index = 1;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">&nbsp; &nbsp; //被压缩的最后一条日志的Term</font></p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional uint64 last_included_term = 2;</p><p style="margin: 0px 0px 0px 4px;">&nbsp; &nbsp; optional Configuration configuration = 3;</p><p style="margin: 0px 0px 0px 4px;">}</p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-960" y="520" width="440" height="640" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-31" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-27" target="hCxxSbGEtk1TdNpt0RpP-30" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-27" target="hCxxSbGEtk1TdNpt0RpP-32" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-27" value="遍历节点列表除当前节点外其他节点<br>Peer peer = new <b>Peer</b>(server);<br>peer.setNextIndex(<br>raftLog.getLastLogIndex() + 1);<br>peerMap.put(server.getServerId(), peer);" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="520.5" y="2320" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-29" value="RaftNode" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="585.5" y="2290" width="70" height="30" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-30" value="this.server = server;<br>this.rpcClient = new <b>RpcClient</b>(...);<br>raftConsensusServiceAsync = BrpcProxy.<b>getProxy</b>(rpcClient, RaftConsensusServiceAsync.class);<br>isCatchUp = false;" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="760.5" y="2310" width="200" height="80" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-35" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-32" target="hCxxSbGEtk1TdNpt0RpP-34" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-32" value="executorService = new ThreadPoolExecutor();<br><font color="#007fff">初始化线程池,默认20个核心线程、空闲时间60s、LinkedBlockingQueue存储任务</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="520.5" y="2400" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-37" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-34" target="hCxxSbGEtk1TdNpt0RpP-36" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-39" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-34" target="hCxxSbGEtk1TdNpt0RpP-38" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-34" value="scheduledExecutorService = Executors.newScheduledThreadPool(2);<br>&nbsp;scheduledExecutorService<br>.scheduleWithFixedDelay(...)<br><font color="#007fff">提交一个日志压缩计划任务,每30s执行一次</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="520.5" y="2480" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-56" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-36" target="jrdKXmfmH3rlp7NtxQ88-55" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-36" value="takeSnapshot();<br><font color="#007fff">日志压缩任务,raft-java 将日志压缩后存储到了 RocketsDB</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="760.5" y="2480" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-41" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-38" target="hCxxSbGEtk1TdNpt0RpP-40" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-38" value="resetElectionTimer();<br><font color="#007fff">其实是提交选举超时后为自己拉票的任务<br>如果到期没有选出其他节点为Leader会执行这里提交的拉票任务<br></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="520" y="2920" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-43" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-40" target="hCxxSbGEtk1TdNpt0RpP-42" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-40" value="electionScheduledFuture = scheduledExecutorService.schedule(...)<br><font style="font-size: 9px;" color="#007fff">节点启动后需要等待 Election Timeout,&nbsp;然后转换成Candidate, Term++, 向其他节点拉票,如果等待期间收到其他节点拉票请求需取消这个任务</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="760" y="2920" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-45" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-42" target="hCxxSbGEtk1TdNpt0RpP-44" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-42" value="startPreVote();" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1001" y="2920" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-47" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-44" target="hCxxSbGEtk1TdNpt0RpP-46" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-44" value="加锁同步更新 state = NodeState.<b>STATE_PRE_CANDIDATE</b>;" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1240" y="2920" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-49" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-46" target="hCxxSbGEtk1TdNpt0RpP-48" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1460" y="3030" />
<mxPoint x="1460" y="3180" />
<mxPoint x="510" y="3180" />
<mxPoint x="510" y="3230" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-51" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-46" target="hCxxSbGEtk1TdNpt0RpP-50" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-46" value="遍历其他节点,异步发送拉票请求<br>executorService.submit(...)" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1240" y="3000" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-55" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-48" target="hCxxSbGEtk1TdNpt0RpP-54" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-88" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-48" target="SMY1UsQMhVWDZvxuVclS-87" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-48" value="preVote(peer);<br><font color="#007fff">向其他节点发起拉票请求</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="520" y="3200" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-52" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-50" target="hCxxSbGEtk1TdNpt0RpP-40" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1450" y="3110" />
<mxPoint x="1450" y="3150" />
<mxPoint x="740" y="3150" />
<mxPoint x="740" y="2965" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-50" value="resetElectionTimer();<br><font color="#007fff">再设置下一个超时任务,如果这段时间没有选举出Leader, 就相当于再次重试</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1240" y="3080" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-53" value="<font style="font-size: 10px;"><font color="#007fff">这里选举超时时间设置的是5-10s</font><br><font color="#007fff">官方不是推荐 150-300ms么?</font><br><font color="#ea6b66">万一Leader崩溃重新选举可能导致有5-10s不可用时间,应该无法接受</font><br></font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="695" y="2980" width="330" height="60" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-57" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-54" target="hCxxSbGEtk1TdNpt0RpP-56" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-60" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-54" target="hCxxSbGEtk1TdNpt0RpP-59" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-55" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-54" target="SMY1UsQMhVWDZvxuVclS-45" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="970" y="3215" />
<mxPoint x="970" y="685" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-56" value="<font color="#007fff" style="font-size: 9px;">RPC请求其他节点</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;" parent="SMY1UsQMhVWDZvxuVclS-55" vertex="1" connectable="0">
<mxGeometry x="-0.9376" y="-1" relative="1" as="geometry">
<mxPoint x="9" y="29" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-54" value="<div>peer.<b>getRaftConsensusServiceAsync</b>()</div><div>.<b>preVote</b>(<span style="background-color: initial;">request, new <b>PreVoteResponseCallback</b>(peer, request));</span></div><div><font color="#007fff">向其他节点发送 RaftProto.VoteRequest 请求</font></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="760" y="3200" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-56" target="SMY1UsQMhVWDZvxuVclS-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-56" value="<div>PreVoteResponseCallback<br></div><div>success&nbsp;<span style="background-color: initial;">回调</span></div><div><span style="background-color: initial;"><font color="#007fff">即其他节点正常响应</font></span></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1000.5" y="3200" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="hCxxSbGEtk1TdNpt0RpP-59" target="SMY1UsQMhVWDZvxuVclS-32" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hCxxSbGEtk1TdNpt0RpP-59" value="PreVoteResponseCallback<br>fail 回调<br><font color="#007fff">即其他节点无响应或异常</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1000" y="3680" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-4" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-1" target="SMY1UsQMhVWDZvxuVclS-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-1" value="peer.setVoteGranted(<br>response.getGranted());<br><font color="#007fff">记录这次拉票对方是否支持</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1240" y="3200" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-3" target="SMY1UsQMhVWDZvxuVclS-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-3" value="<font color="#007fff">校验VoteRequest和本轮拉票的任期是否相同,本地节点是否是 Pre Candidate 状态,<br>任意条件不满足直接退出</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1240" y="3280" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-6" value="<div style="text-align: center;"><b>Peer</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">自身节点外其他节点的一些信息,因为Raft协议需要和其他节点进行通信,记录其他节点投票、任期Term等信息</font></p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 节点的服务器信息,节点ID、host、port</font></p><p style="margin: 0px 0px 0px 4px;">private RaftProto.Server <b>server</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 本地节点连接这个节点的RPC客户端</font>&nbsp;</p><p style="margin: 0px 0px 0px 4px;">private RpcClient <b>rpcClient</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 目标节点的服务代理</font></p><p style="margin: 0px 0px 0px 4px;">private RaftConsensusServiceAsync <b>raftConsensusServiceAsync</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 需要发送给follower的下一个日志条目的索引值,只对leader有效</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>nextIndex</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 已复制日志的最高索引值</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>matchIndex</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 记录最近一次拉票,对方是否支持,每次发起新一轮的投票都需要先清空</font></p><p style="margin: 0px 0px 0px 4px;">private volatile Boolean <b>voteGranted</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//&nbsp;</font></p><p style="margin: 0px 0px 0px 4px;">private volatile boolean <b>isCatchUp</b>;</p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-480" y="680" width="440" height="280" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-7" value="任期Term不同说明非本轮投票请求,所以投票无效,<br style="font-size: 10px;">状态非 Pre Candidate 则是有可能其他节点已经成为 Leader<br>并通知当前节点将状态修改为了Follower" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#007FFF;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1450" y="3285" width="290" height="50" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-13" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-10" target="SMY1UsQMhVWDZvxuVclS-12" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-14" value="Y,拉票无效" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="SMY1UsQMhVWDZvxuVclS-13" vertex="1" connectable="0">
<mxGeometry x="0.5333" y="1" relative="1" as="geometry">
<mxPoint x="-12" y="21" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-16" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-10" target="SMY1UsQMhVWDZvxuVclS-15" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-17" value="N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="SMY1UsQMhVWDZvxuVclS-16" vertex="1" connectable="0">
<mxGeometry x="0.6" y="2" relative="1" as="geometry">
<mxPoint x="-2" y="-154" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-10" value="response.getTerm() &gt; currentTerm ?" style="rhombus;whiteSpace=wrap;html=1;fontSize=10;rounded=1;" parent="1" vertex="1">
<mxGeometry x="1280" y="3390" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-20" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-12" target="SMY1UsQMhVWDZvxuVclS-19" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-12" value="<font>stepDown(response.getTerm());<br><font color="#007fff">其他节点任期term &gt; 当前节点的任期,说明当前拉票无效,然后stepDown是退回Follower状态</font><br></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1480" y="3360" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-72" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-15" target="SMY1UsQMhVWDZvxuVclS-71" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1700" y="3630" />
<mxPoint x="1700" y="3790" />
<mxPoint x="500" y="3790" />
<mxPoint x="500" y="3830" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-15" value="if (response.getGranted())<br>for遍历统计 peerMap 中的投票记录,<br>统计得票数<br>如果得票数超过一半<br>startVote();<br>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1480" y="3600" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-22" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-19" target="SMY1UsQMhVWDZvxuVclS-21" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-27" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-19" target="SMY1UsQMhVWDZvxuVclS-26" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-19" value="<font style="font-size: 9px;">if (currentTerm &lt; newTerm) {<br style="font-size: 9px;"><b style="font-size: 9px;">&nbsp; currentTerm</b> = newTerm;<br style="font-size: 9px;"><b style="font-size: 9px;">&nbsp; leaderId</b> = 0;&nbsp;<b style="font-size: 9px;">votedFor</b> = 0;<br style="font-size: 9px;">&nbsp; raftLog.<b style="font-size: 9px;">updateMetaData</b>(currentTerm, votedFor, null, null);<br style="font-size: 9px;">}<br style="font-size: 9px;"><b style="font-size: 9px;">state</b> = NodeState.<b style="font-size: 9px;">STATE_FOLLOWER</b>;<br style="font-size: 9px;"></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=9;fillColor=#ffcd28;strokeColor=#d79b00;arcSize=9;gradientColor=#ffa500;align=left;" parent="1" vertex="1">
<mxGeometry x="1720.5" y="3350" width="200" height="80" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-24" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-21" target="SMY1UsQMhVWDZvxuVclS-23" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-21" value="<font>检查一下心跳任务是否在执行,<br>是的话也关闭下<br>heartbeatScheduledFuture.<b>cancel</b>(true);<br>因为Follower是不需要向其他节点发心跳的<br></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#000000;arcSize=9;" parent="1" vertex="1">
<mxGeometry x="1720" y="3450" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-25" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-23" target="hCxxSbGEtk1TdNpt0RpP-40" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1940" y="3560" />
<mxPoint x="1940" y="3150" />
<mxPoint x="740" y="3150" />
<mxPoint x="740" y="2965" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-23" value="<font>resetElectionTimer();<br><font color="#007fff">重置选举超时,Leader需要在这个时间之前发送心跳,否则follower节点会重新参与选举</font><br></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#000000;arcSize=9;" parent="1" vertex="1">
<mxGeometry x="1720.5" y="3530" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-26" target="SMY1UsQMhVWDZvxuVclS-28" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-26" value="<font style="font-size: 10px;"><span style="font-size: 10px;">SegmentedLog#</span><b style="font-size: 10px;">updateMetaData</b>(Long currentTerm, Integer votedFor, Long firstLogIndex, Long commitIndex)<br style="font-size: 10px;"></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;arcSize=9;" parent="1" vertex="1">
<mxGeometry x="1960" y="3360" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-31" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-28" target="SMY1UsQMhVWDZvxuVclS-30" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-28" value="<font style="font-size: 10px;">创建LogMetaData对象记录拉票结果数据<br style="font-size: 10px;">存储到名为&nbsp;String fileName = logDir + File.separator + "metadata"; 的文件<br style="font-size: 10px;">RandomAccessFile randomAccessFile = new <b style="font-size: 10px;">RandomAccessFile</b>(file, "rw")<br style="font-size: 10px;">RaftFileUtils.<b style="font-size: 10px;">writeProtoToFile</b>(<br style="font-size: 10px;">randomAccessFile, metaData);<br style="font-size: 10px;"></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;arcSize=9;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="2200" y="3350" width="200" height="80" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-30" value="<font style="">byte[] messageBytes = message.toByteArray();<br>long crc32 = getCRC32(messageBytes);<br><div>raf.writeLong(crc32);</div><div>raf.writeInt(messageBytes.length);</div><div>raf.write(messageBytes);</div></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;arcSize=9;align=left;" parent="1" vertex="1">
<mxGeometry x="2430" y="3350" width="219" height="80" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-32" value="peer.setVoteGranted(new Boolean(false));<br><font color="#007fff">默认对方不支持</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1240" y="3680" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-36" value="<div style="text-align: center;"><b>RaftConsensusService (I)</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><b>保持Raft集群节点数据一致性的服务接口,用于节点间相互调用</b></font></p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//向对方节点发送预拉票请求</font></p><p style="margin: 0px 0px 0px 4px;">RaftProto.VoteResponse <b>preVote</b>(RaftProto.VoteRequest request);</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//向对方节点发送正式拉票请求<br></font></p><p style="margin: 0px 0px 0px 4px;">RaftProto.VoteResponse <b>requestVote</b>(RaftProto.VoteRequest request);</p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;">RaftProto.AppendEntriesResponse <b>appendEntries</b>(RaftProto.AppendEntriesRequest request);</p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;">RaftProto.InstallSnapshotResponse <b>installSnapshot</b>(RaftProto.InstallSnapshotRequest request);</p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-480" y="1520" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-38" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=block;endFill=0;dashed=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-37" target="SMY1UsQMhVWDZvxuVclS-36" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-37" value="<div style="text-align: center;"><b>RaftConsensusServiceImpl</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><span style="color: rgb(0, 127, 255); background-color: initial;">// 节点信息</span><br></p><p style="margin: 0px 0px 0px 4px;">private RaftNode raftNode;<br></p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-480" y="1720" width="440" height="80" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-39" value="<div style="text-align: center;"><b>RaftClientService</b><b style="background-color: initial;">&nbsp;(I)</b></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><b>Raft集群管理接口</b></font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><br></font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//获取raft集群leader节点信息</font></p><p style="margin: 0px 0px 0px 4px;">RaftProto.GetLeaderResponse getLeader(RaftProto.GetLeaderRequest request);</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//获取raft集群所有节点信息</font></p><p style="margin: 0px 0px 0px 4px;">RaftProto.GetConfigurationResponse getConfiguration(RaftProto.GetConfigurationRequest request);</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//向raft集群添加节点</font></p><p style="margin: 0px 0px 0px 4px;">RaftProto.AddPeersResponse addPeers(RaftProto.AddPeersRequest request);</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//从raft集群删除节点</font></p><p style="margin: 0px 0px 0px 4px;">RaftProto.RemovePeersResponse removePeers(RaftProto.RemovePeersRequest request);</p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-960" y="1520" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-40" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=block;endFill=0;dashed=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-41" target="SMY1UsQMhVWDZvxuVclS-39" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-41" value="<div style="text-align: center;"><b>RaftClientServiceImpl</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><span style="color: rgb(0, 127, 255); background-color: initial;">// 节点信息</span><br></p><p style="margin: 0px 0px 0px 4px;">private RaftNode raftNode;<br></p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-960" y="1720" width="440" height="80" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-46" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-43" target="SMY1UsQMhVWDZvxuVclS-45" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="770.5" y="670" />
<mxPoint x="770.5" y="670" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-49" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-43" target="SMY1UsQMhVWDZvxuVclS-47" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-53" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-43" target="SMY1UsQMhVWDZvxuVclS-50" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-54" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-43" target="SMY1UsQMhVWDZvxuVclS-52" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-43" value="<font color="#007fff" style="font-size: 10px;"><b>实际用到的4个接口<br></b>一共有8个接口,4个异步接口、4个同步接口,异步接口实现是Brpc自动封装的同步接口实现<br style="font-size: 10px;"></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="520.5" y="640" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-58" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-45" target="SMY1UsQMhVWDZvxuVclS-57" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-45" value="<div style=""><span style="font-weight: normal;">Future&lt;RaftProto.VoteResponse&gt; </span>preVote<span style="font-weight: normal;">(</span></div><div style="font-weight: normal;">RaftProto.VoteRequest request,</div><div style="font-weight: normal;">RpcCallback&lt;RaftProto.VoteResponse&gt; callback);</div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="761" y="640" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-81" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-47" target="SMY1UsQMhVWDZvxuVclS-80" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-47" value="<div style=""><div><span style="font-weight: normal;">Future&lt;RaftProto.VoteResponse&gt; </span>requestVote<span style="font-weight: normal;">(</span></div><div><span style="font-weight: normal;">RaftProto.VoteRequest request,</span></div><div><span style="font-weight: normal;">RpcCallback&lt;RaftProto.VoteResponse&gt; callback);</span></div></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="761.5" y="980" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-83" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-50" target="jrdKXmfmH3rlp7NtxQ88-82" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-50" value="<div style=""><span style="font-weight: normal;">RaftProto.AppendEntriesResponse </span>appendEntries<span style="font-weight: normal;">(</span></div><div style=""><span style="font-weight: normal;">RaftProto.AppendEntriesRequest request);</span><br></div><div style=""><font style="" color="#007fff"><span style="font-weight: normal;">参考论文章节:</span>追加条目(AppendEntries)RPC</font><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="760.5" y="1220" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-52" value="<div style=""><span style="font-weight: normal;">RaftProto.InstallSnapshotResponse </span>installSnapshot<span style="font-weight: normal;">(</span></div><div style=""><span style="font-weight: normal;">RaftProto.InstallSnapshotRequest request);</span><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="760" y="1460" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-60" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-57" target="SMY1UsQMhVWDZvxuVclS-59" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-57" value="<div style=""><span style="font-weight: normal;">RaftProto.VoteResponse </span>preVote<span style="font-weight: normal;">(RaftProto.VoteRequest request)</span><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1000" y="640" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-64" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-59" target="SMY1UsQMhVWDZvxuVclS-63" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-59" value="<div style=""><span style="font-weight: normal;">raftNode.getLock().lock();</span><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=center;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1281" y="655" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-61" value="<div style=""><span style="font-weight: normal;">raftNode.getLock().unlock();</span><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=center;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1281.5" y="930" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-62" value="<div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">拉票请求里面携带的信息<br style="font-size: 10px;"></font></div><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">requestBuilder.setServerId(localServer.getServerId())</font></div><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setTerm(currentTerm)</font></div><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setLastLogIndex(raftLog.getLastLogIndex())</font></div><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setLastLogTerm(getLastLogTerm());</font></div>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="760" y="3275" width="280" height="70" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-66" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-63" target="SMY1UsQMhVWDZvxuVclS-65" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KmqIpF15YOtQaE7IZBm3-14" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=0;fillColor=#d5e8d4;strokeColor=#82b366;" edge="1" parent="1" source="SMY1UsQMhVWDZvxuVclS-63" target="SMY1UsQMhVWDZvxuVclS-69">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-63" value="<div style=""><span style="font-weight: normal;">经过几个检查:</span></div><div style=""><span style="font-weight: normal;">1 请求来源节点ID是否存在</span></div><div style=""><span style="font-weight: normal;">2 请求的任期Term是否不小于当前节点Term</span></div><div style=""><span style="font-weight: normal;">3&nbsp;request.getLastLogTerm() &gt; raftNode.</span>getLastLogTerm<span style="font-weight: normal;">()</span></div><div><span style="font-weight: normal;">|| (request.getLastLogTerm() == raftNode.getLastLogTerm()</span></div><div><span style="font-weight: normal;">&amp;&amp; request.getLastLogIndex() &gt;= raftNode.</span>getRaftLog<span style="font-weight: normal;">()</span></div><div><span style="font-weight: normal;">.</span>getLastLogIndex<span style="font-weight: normal;">())</span></div><div><span style="font-weight: 400;">任意一条不满足直接返回不支持,并附带上当前节点Term</span></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=1;arcSize=7;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1241.5" y="720" width="279" height="100" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-67" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-65" target="SMY1UsQMhVWDZvxuVclS-61" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-65" value="<div style=""><span style="font-weight: normal;">responseBuilder.</span>setGranted<span style="font-weight: normal;">(true);<br></span></div><div style=""><span style="font-weight: normal;">responseBuilder.</span>setTerm<span style="font-weight: normal;">(raftNode</span></div><div style=""><span style="font-weight: normal;">.getCurrentTerm());<br></span></div><div style=""><span style="font-weight: normal;">return responseBuilder.build();</span><br></div><div style=""><span style="font-weight: 400;"><font color="#007fff">检查通过返回支持票</font></span></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1281.5" y="840" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-68" value="<div style="text-align: center;"><b>SegmentedLog</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><b>以分段方式存储的操作日志。</b></font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><br></font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//raftDataDir+ "/log/data"</font></p><p style="margin: 0px 0px 0px 4px;">private String <b>logDir</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//raftDataDir+ "/log/data"</font></p><p style="margin: 0px 0px 0px 4px;">private String <b>logDataDir</b>;</p><p style="margin: 0px 0px 0px 4px;">private int <b>maxSegmentFileSize</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//</font></p><p style="margin: 0px 0px 0px 4px;">private RaftProto.LogMetaData <b>metaData</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><b>//对应logDataDir下的所有Segment文件, segment.startIndex -&gt; segment</b></font></p><p style="margin: 0px 0px 0px 4px;">private TreeMap&lt;Long, Segment&gt; <b>startLogIndexSegmentMap</b> = new TreeMap&lt;&gt;();</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// segment log占用的内存大小(所有Segment文件大小总和),用于判断是否需要做snapshot</font></p><p style="margin: 0px 0px 0px 4px;">private volatile long <b>totalSize</b>;</p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-480" y="1240" width="440" height="240" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-69" value="1&nbsp;request.getLastLogTerm() &gt; raftNode.<span style="font-size: 10px;">getLastLogTerm</span><span style="border-color: var(--border-color); font-size: 10px;">()<br style="font-size: 10px;">2&nbsp;request.getLastLogTerm() == raftNode.getLastLogTerm()<span style="background-color: initial; border-color: var(--border-color); font-size: 10px;"><span style="background-color: initial; border-color: var(--border-color); font-size: 10px;"><br></span>&nbsp;&nbsp;<span style=""><span style="">&nbsp;&nbsp;&nbsp;&nbsp;</span></span>&amp;&amp; request.getLastLogIndex() &gt;= raftNode.<span style="background-color: initial;">getRaftLog</span><span style="background-color: initial; border-color: var(--border-color);">()<span style="background-color: initial; border-color: var(--border-color);">.</span><span style="background-color: initial;">getLastLogIndex</span><span style="background-color: initial; border-color: var(--border-color);">()</span><br></span></span><div style="font-size: 10px;"><span style="border-color: var(--border-color); font-weight: normal; font-size: 10px;">Raft协议的</span><span style="border-color: var(--border-color); font-size: 10px;"><b>选举限制</b></span><span style="border-color: var(--border-color); font-weight: normal; font-size: 10px;">实现:Raft 通过比较两份日志中最后一条日志条目的索引值和任期号定义谁的日志比较新。</span></div><div style="font-size: 10px;"><span style="border-color: var(--border-color); font-weight: normal; font-size: 10px;">如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。</span></div><div style="font-size: 10px;"><span style="border-color: var(--border-color); font-weight: normal; font-size: 10px;">如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。</span></div><div style="font-size: 10px;"><span style="border-color: var(--border-color); font-size: 10px;"><b>为了阻止未包含全部已提交日志条目的候选人赢得选举</b></span><span style="border-color: var(--border-color); font-weight: normal; font-size: 10px;">。</span></div></span>" style="text;html=1;align=left;verticalAlign=top;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#007FFF;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1519.5" y="940" width="510" height="100" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-74" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-71" target="SMY1UsQMhVWDZvxuVclS-73" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-89" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" source="SMY1UsQMhVWDZvxuVclS-71" target="SMY1UsQMhVWDZvxuVclS-87" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-71" value="startVote();<br><font color="#007fff">更新本地Leader 记录,并异步向其他节点同步数据</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="520" y="3800" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-76" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-73" target="SMY1UsQMhVWDZvxuVclS-75" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-73" value="锁同步下更新本地记录<br><b>currentTerm</b>++;<br>state = NodeState.<b>STATE_CANDIDATE</b>;<br>leaderId = 0;<br>votedFor = localServer.getServerId();" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#ffcd28;gradientColor=#ffa500;strokeColor=#d79b00;" parent="1" vertex="1">
<mxGeometry x="760" y="3800" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-78" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-75" target="SMY1UsQMhVWDZvxuVclS-77" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-75" value="final Peer peer = peerMap.get(server.getServerId());<br>executorService.submit(<br>() -&gt; requestVote(peer));" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="760" y="3880" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-79" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;strokeColor=#6c8ebf;fillColor=#dae8fc;" parent="1" source="SMY1UsQMhVWDZvxuVclS-77" target="SMY1UsQMhVWDZvxuVclS-47" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1220" y="3895" />
<mxPoint x="1220" y="1050" />
<mxPoint x="980" y="1050" />
<mxPoint x="980" y="1025" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-108" value="<font style="font-size: 9px;" color="#007fff">RPC请求其他节点</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="SMY1UsQMhVWDZvxuVclS-79" vertex="1" connectable="0">
<mxGeometry x="-0.9523" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-85" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-77" target="SMY1UsQMhVWDZvxuVclS-86" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1241" y="3910" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-92" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-77" target="SMY1UsQMhVWDZvxuVclS-90" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-77" value="<div>peer.getRaftConsensusServiceAsync()</div><div>.requestVote(</div><div>request, new VoteResponseCallback(</div><div>peer, request));</div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="1001" y="3880" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-83" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-80" target="SMY1UsQMhVWDZvxuVclS-98" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1281" y="1010" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-80" value="<div style=""><span style="font-weight: normal;">RaftProto.VoteResponse </span>requestVote<span style="font-weight: normal;">(RaftProto.VoteRequest request)</span></div><font style="font-size: 9px;" color="#007fff"><span style="font-weight: normal;">参考论文章节:</span>请求投票(RequestVote)RPC</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1001.5" y="980" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-94" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-86" target="SMY1UsQMhVWDZvxuVclS-93" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-86" value="<div>VoteResponseCallback<br></div><div>success&nbsp;<span style="background-color: initial;">回调</span></div><div><span style="background-color: initial;"><font color="#007fff">即其他节点正常响应</font></span></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1241" y="3880" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-87" value="<font color="#007fff">像不像两阶段提交 2PC</font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="545" y="3520" width="150" height="30" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-96" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-90" target="SMY1UsQMhVWDZvxuVclS-95" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-90" value="<div>VoteResponseCallback<br></div><div>fail&nbsp;<span style="background-color: initial;">回调</span></div><div><span style="background-color: initial;"><font color="#007fff">即其他节点无响应或异常</font></span></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1241" y="3960" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-131" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-93" target="SMY1UsQMhVWDZvxuVclS-130" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-93" value="<b>becomeLeader</b>();<br><font color="#007fff">处理和 PreVoteResponseCallback 基本相同,<br>除了最后一步,preVote中是 startVote()</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1480" y="3880" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-95" value="peer.setVoteGranted(new Boolean(false));<br><font color="#007fff">默认对方不支持</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1480" y="3960" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-97" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-98" target="SMY1UsQMhVWDZvxuVclS-101" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-98" value="<div style=""><span style="font-weight: normal;">raftNode.getLock().lock();</span><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=center;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1281.5" y="995" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-99" value="<div style=""><span style="font-weight: normal;">raftNode.getLock().unlock();</span><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=center;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1279.75" y="1460.5" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-100" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-101" target="SMY1UsQMhVWDZvxuVclS-103" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-101" value="<div style=""><span style="font-weight: normal;">先经过2个检查(和preVote()一样):</span></div><div style=""><span style="font-weight: normal;">1 请求来源节点ID是否存在</span></div><div style=""><span style="font-weight: normal;">2 请求的任期Term是否不小于当前节点Term</span></div><div style=""><span style="font-weight: normal;">任意一条不满足直接返回不支持,并附带上当前节点Term</span><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=1;arcSize=7;" parent="1" vertex="1">
<mxGeometry x="1281.5" y="1040" width="200" height="60.5" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-103" target="SMY1UsQMhVWDZvxuVclS-19" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1520.5" y="930" as="targetPoint" />
<Array as="points">
<mxPoint x="1700" y="1151" />
<mxPoint x="1700" y="3370" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-103" value="<div style=""><div><span style="font-weight: normal;">if (request.getTerm() &gt; raftNode.getCurrentTerm()) {</span></div><div><span style="font-weight: normal;">raftNode.</span>stepDown<span style="font-weight: normal;">(request.getTerm());</span></div><div><span style="font-weight: normal;">}</span></div><div><span style="font-weight: normal;"><font color="#007fff">这里会更新currentTerm</font></span></div></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1281.25" y="1120.5" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-112" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-103" target="SMY1UsQMhVWDZvxuVclS-111" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1381.5" y="1180.5" as="sourcePoint" />
<mxPoint x="1381.5" y="1380.5" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-116" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-111" target="SMY1UsQMhVWDZvxuVclS-115" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="KmqIpF15YOtQaE7IZBm3-13" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=1;exitDx=0;exitDy=0;fillColor=#d5e8d4;strokeColor=#82b366;" edge="1" parent="1" source="SMY1UsQMhVWDZvxuVclS-111" target="SMY1UsQMhVWDZvxuVclS-69">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-111" value="<div style="font-size: 9px;"><div style="font-size: 9px;">boolean logIsOk = request.getLastLogTerm() &gt; raftNode.getLastLogTerm()</div><div style="font-size: 9px;"><span style="font-size: 9px;">|| (request.getLastLogTerm() == raftNode.getLastLogTerm()</span></div><div style="font-size: 9px;"><span style="font-size: 9px;">&nbsp; &amp;&amp; request.getLastLogIndex() &gt;= <br>&nbsp; raftNode.getRaftLog().getLastLogIndex());</span></div><div style="font-size: 9px;"><font color="#007fff">和preVote() 中的相同</font></div></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=9;align=left;fontStyle=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1241" y="1200.5" width="279" height="80" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-118" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-115" target="SMY1UsQMhVWDZvxuVclS-117" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-119" value="Y" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="SMY1UsQMhVWDZvxuVclS-118" vertex="1" connectable="0">
<mxGeometry x="-0.1321" y="-3" relative="1" as="geometry">
<mxPoint x="4" y="-13" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-123" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-115" target="SMY1UsQMhVWDZvxuVclS-122" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-115" value="raftNode.getVotedFor() == 0 &amp;&amp; logIsOk<br><font color="#007fff">voteFor == 0 说明是本轮第一次投票,这个判断是防止节点重复投票</font>" style="rhombus;whiteSpace=wrap;html=1;fontSize=9;align=center;rounded=1;fontStyle=0;" parent="1" vertex="1">
<mxGeometry x="1320" y="1300.5" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-126" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-117" target="SMY1UsQMhVWDZvxuVclS-125" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-117" value="<div style="">raftNode.stepDown(request.getTerm());<br></div><div style="">raftNode.setVotedFor(request.getServerId());<br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="1522" y="1300.5" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-124" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-122" target="SMY1UsQMhVWDZvxuVclS-99" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-122" value="<div style="">return responseBuilder.build();<br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=center;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="1280.25" y="1380.5" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-128" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-125" target="SMY1UsQMhVWDZvxuVclS-127" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1621.250000000001" y="1460.5" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-129" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="SMY1UsQMhVWDZvxuVclS-125" target="SMY1UsQMhVWDZvxuVclS-26" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1950" y="1411" />
<mxPoint x="1950" y="3375" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-125" value="<div style="">raftNode.getRaftLog().<b>updateMetaData</b>(</div><div style="">raftNode.getCurrentTerm(), raftNode.getVotedFor(), null, null);<br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="1522" y="1380.5" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-127" value="<div style="">responseBuilder.setGranted(true);<br></div><div style="">responseBuilder.setTerm(</div><div style="">raftNode.getCurrentTerm());<br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=left;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="1522" y="1460.5" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-133" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-130" target="SMY1UsQMhVWDZvxuVclS-132" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-130" value="<div>state = NodeState.<b>STATE_LEADER</b>;</div><div>leaderId = localServer.getServerId();</div><div>关闭还存在的选举定时任务</div><div>electionScheduledFuture.cancel(true);<br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#ffcd28;strokeColor=#d79b00;gradientColor=#ffa500;" parent="1" vertex="1">
<mxGeometry x="1719.5" y="3880" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-135" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-132" target="SMY1UsQMhVWDZvxuVclS-134" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-132" value="<div>startNewHeartbeat();<br></div><div><font color="#007fff">开启心跳定时任务, 定期向peer发送</font></div><div><font color="#007fff">appendEntries请求</font><br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="3960" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-137" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="SMY1UsQMhVWDZvxuVclS-134" target="SMY1UsQMhVWDZvxuVclS-136" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-134" value="遍历&nbsp;peerMap, 向所有其他节点异步发送<br>AppendEntries请求<br>executorService.<b>submit</b>(...)<br>全部提交完再设置心跳定时任务 <b>resetHeartbeatTimer</b>();" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1960" y="3960" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="KmqIpF15YOtQaE7IZBm3-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="SMY1UsQMhVWDZvxuVclS-136" target="jrdKXmfmH3rlp7NtxQ88-45">
<mxGeometry relative="1" as="geometry">
<mxPoint x="2439.5" y="3990.000000000001" as="targetPoint" />
<Array as="points">
<mxPoint x="2420" y="3990" />
<mxPoint x="2420" y="2290" />
<mxPoint x="1940" y="2290" />
<mxPoint x="1940" y="2265" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SMY1UsQMhVWDZvxuVclS-136" value="<div style="border-color: var(--border-color);">RaftNode#<b style="border-color: var(--border-color);">appendEntries</b>(Peer peer)<br style="border-color: var(--border-color);"></div><div style="border-color: var(--border-color);"><font style="border-color: var(--border-color);" color="#007fff">Leader节点将自己的日志同步到Follower节点,同步 snapshot 、raftLog 两部分</font></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="2200" y="3960" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-5" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-1" target="jrdKXmfmH3rlp7NtxQ88-4" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-8" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-1" target="jrdKXmfmH3rlp7NtxQ88-6" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-1" value="<font color="#007fff">2个接口<br>对标业务接口,展示Raft协议下对业务请求的处理<br></font>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="520" y="1680" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-4" target="jrdKXmfmH3rlp7NtxQ88-11" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1000.5000000000005" y="1710" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-4" value="<div style=""><span style="font-weight: normal;">ExampleProto.SetResponse </span>set<span style="font-weight: normal;">(ExampleProto.SetRequest request);</span><br></div><div style=""><font style="" color="#007fff"><span style="font-weight: normal;">赋值接口,客户端不需要先查找Leader再请求,可以发给任意节点,因为内部已经做了</span>转发</font></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="760" y="1680" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="KmqIpF15YOtQaE7IZBm3-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="jrdKXmfmH3rlp7NtxQ88-6" target="KmqIpF15YOtQaE7IZBm3-10">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-6" value="<div style=""><span style="font-weight: normal;">ExampleProto.GetResponse </span>get<span style="font-weight: normal;">(ExampleProto.GetRequest request);</span><br></div><div style=""><span style="font-weight: normal;"><font color="#007fff">查询接口</font></span></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="760" y="2160" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-13" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-11" target="jrdKXmfmH3rlp7NtxQ88-12" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-14" value="<font style="" color="#007fff"><font style="font-size: 10px;">Y </font><br style="font-size: 8px;">说明当前节点不是Leader, <br style="font-size: 8px;">也不知道谁是Leader<br style="font-size: 8px;">一般是集群还没选举出Leader<br>或在重新选举&nbsp;<br style="font-size: 8px;"></font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;" parent="jrdKXmfmH3rlp7NtxQ88-13" vertex="1" connectable="0">
<mxGeometry x="-0.4937" y="-3" relative="1" as="geometry">
<mxPoint x="9" y="27" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-16" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-11" target="jrdKXmfmH3rlp7NtxQ88-15" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-17" value="N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontColor=#007FFF;" parent="jrdKXmfmH3rlp7NtxQ88-16" vertex="1" connectable="0">
<mxGeometry x="-0.0958" y="1" relative="1" as="geometry">
<mxPoint x="-1" y="-6" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-11" value="raftNode.getLeaderId()<br>&nbsp;&lt;= 0" style="rhombus;whiteSpace=wrap;html=1;fontSize=9;align=center;rounded=1;fontStyle=0;" parent="1" vertex="1">
<mxGeometry x="1001.5" y="1680" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-12" value="<div style="">responseBuilder.setSuccess(false);<br></div><div style=""><font color="#007fff">直接返回失败</font></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;align=center;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="1240.5" y="1680" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-19" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-15" target="jrdKXmfmH3rlp7NtxQ88-18" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-20" value="<font style="" color="#007fff"><font style="font-size: 10px;">Y</font><br><font style="font-size: 8px;">已知Leader ID,<br>且不是当前节点ID<br>说明当前节点是Follower</font></font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="jrdKXmfmH3rlp7NtxQ88-19" vertex="1" connectable="0">
<mxGeometry x="-0.1806" relative="1" as="geometry">
<mxPoint x="-9" y="30" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-22" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-15" target="jrdKXmfmH3rlp7NtxQ88-24" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1060.5" y="1880" as="targetPoint" />
<Array as="points">
<mxPoint x="1089.5" y="2010" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-23" value="<font color="#007fff"><font style="font-size: 10px;">N</font><br><font style="font-size: 8px;">说明当前节点是Leader</font><br></font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="jrdKXmfmH3rlp7NtxQ88-22" vertex="1" connectable="0">
<mxGeometry x="-0.2625" y="1" relative="1" as="geometry">
<mxPoint x="70" y="40" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-25" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-15" target="jrdKXmfmH3rlp7NtxQ88-26" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1061.5" y="1970" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-15" value="raftNode.getLeaderId() != raftNode.getLocalServer()<br>.getServerId()" style="rhombus;whiteSpace=wrap;html=1;fontSize=9;align=center;rounded=1;fontStyle=0;" parent="1" vertex="1">
<mxGeometry x="1001.5" y="1780" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-29" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.524;entryY=0.019;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-18" target="jrdKXmfmH3rlp7NtxQ88-28" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1340" y="1850" />
<mxPoint x="1386" y="1850" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-18" value="onLeaderChangeEvent();<br><font style="font-size: 9px;" color="#007fff">如果Leader有变更,加锁同步更新ExampleServiceImpl中 leaderId leaderRpcClient<br>加锁为了保持 leaderId leaderRpcClient 的一致<br></font>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;" parent="1" vertex="1">
<mxGeometry x="1240.5" y="1780" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-24" target="jrdKXmfmH3rlp7NtxQ88-32" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-24" value="byte[] data = request.toByteArray();<br>boolean success = <b>raftNode</b>.<b>replicate</b>(data, RaftProto.EntryType.ENTRY_TYPE_DATA);<br>responseBuilder.setSuccess(success);<br><font color="#007fff">日志复制</font>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1240" y="1980" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-26" value="<font style="font-size: 10px;">ExampleProto.SetResponse response = responseBuilder.build();<br>return response;</font>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;" parent="1" vertex="1">
<mxGeometry x="991.5" y="2040" width="140" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-30" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-28" target="jrdKXmfmH3rlp7NtxQ88-24" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1530" y="1905" />
<mxPoint x="1530" y="1970" />
<mxPoint x="1230" y="1970" />
<mxPoint x="1230" y="1995" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-31" value="<font color="#007fff">转发调用Leader节点<br>set接口<br></font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="jrdKXmfmH3rlp7NtxQ88-30" vertex="1" connectable="0">
<mxGeometry x="-0.8505" y="-1" relative="1" as="geometry">
<mxPoint x="61" y="-21" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-28" value="<div>ExampleService exampleService = BrpcProxy.<b>getProxy</b>(leaderRpcClient, ExampleService.class);</div><div>ExampleProto.SetResponse responseFromLeader = exampleService.<b>set</b>(request);</div><div>responseBuilder.<b>mergeFrom</b>(responseFromLeader);</div><div><font color="#007fff">为何要请求Leader的set接口? 其实这里相当于<b>转发</b>,修改等</font><span style="color: rgb(0, 127, 255); background-color: initial;">操作是需要发给Leader,再经由Leader日志复制同步到其他节点,但是代码实现</span></div><div><font color="#007fff">一般不会先查找Leader节点再请求,而是通过这种转发的方式</font></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=left;arcSize=9;" parent="1" vertex="1">
<mxGeometry x="1239.5" y="1860" width="280" height="90" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-36" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-32" target="jrdKXmfmH3rlp7NtxQ88-35" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-32" value="先校验Leader节点状态<br>state != NodeState.STATE_LEADER<br>不等直接返回false<br>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;" parent="1" vertex="1">
<mxGeometry x="1479.75" y="1980" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-38" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-35" target="jrdKXmfmH3rlp7NtxQ88-37" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-35" value="<div>RaftProto.LogEntry logEntry = RaftProto.<b>LogEntry</b>.newBuilder()</div><div>.<b>setTerm</b>(currentTerm)<span style="background-color: initial;">.<b>setType</b>(entryType)</span></div><div>.<b>setData</b>(ByteString.copyFrom(data)).build();</div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=left;" parent="1" vertex="1">
<mxGeometry x="1479.5" y="2060" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-40" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-37" target="jrdKXmfmH3rlp7NtxQ88-39" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-37" value="<div style="font-size: 9px;">List&lt;RaftProto.LogEntry&gt; entries = new ArrayList&lt;&gt;();</div><div style="font-size: 9px;">entries.add(logEntry);</div><div style="font-size: 9px;">newLastLogIndex = <b>raftLog</b>.<b style="font-size: 9px;">append</b>(entries);</div><div style="font-size: 9px;"><font color="#007fff">先写Leader节点本地的RaftLog</font></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=left;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1479.5" y="2140" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-42" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=10;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-39" target="jrdKXmfmH3rlp7NtxQ88-41" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-46" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-58" target="jrdKXmfmH3rlp7NtxQ88-45" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-39" value="<div style="text-align: center; font-size: 10px;"><font style="font-size: 10px;"><span style="background-color: initial; font-size: 10px;"><b>for</b>&nbsp;</span><span style="background-color: initial; font-size: 10px;">configuration.getServersList():</span></font></div><div style="text-align: center; font-size: 10px;"><font style="font-size: 10px;"><span style="background-color: initial; font-size: 10px;">executorService.submit(() -&gt; </span><b style="background-color: initial; font-size: 10px;">appendEntries</b><span style="background-color: initial; font-size: 10px;">(peer));</span><br style="font-size: 10px;"></font></div><div style="text-align: center; font-size: 10px;"><span style="background-color: initial; font-size: 10px;"><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">再遍历其他节点异步发送日志节点</font></div></span></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=left;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1479.5" y="2220" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-44" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=10;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-41" target="jrdKXmfmH3rlp7NtxQ88-43" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-41" value="<div style="text-align: center; font-size: 10px;"><div style="font-size: 10px;">if (raftOptions.isAsyncWrite())</div><div style="font-size: 10px;">&nbsp; return true;</div><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">如果配置了异步写,主节点写成功后就返回</font></div></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=left;" parent="1" vertex="1">
<mxGeometry x="1479.5" y="2300" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-43" value="<div style="font-size: 9px;">同步写需要同步等待多数节点日志同步成功</div><div style="font-size: 9px;">while (<b style="font-size: 9px;">lastAppliedIndex &lt; newLastLogIndex</b>) {</div><div style="font-size: 9px;">&nbsp; ...<br style="font-size: 9px;"><span style="background-color: initial; font-size: 9px;">&nbsp; <b>commitIndexCondition</b>.await(</span></div><div style="font-size: 9px;"><span style="background-color: initial; font-size: 9px;">raftOptions.getMaxAwaitTimeout(), TimeUnit.MILLISECONDS);</span></div><div style="font-size: 9px;">}<br style="font-size: 9px;"></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=left;arcSize=8;" parent="1" vertex="1">
<mxGeometry x="1479.5" y="2380" width="199" height="80" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-62" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-45" target="jrdKXmfmH3rlp7NtxQ88-61" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-64" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-45" target="jrdKXmfmH3rlp7NtxQ88-63" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-45" value="<div style="text-align: center; font-size: 10px;">首先判断是否需要 install snapshot,&nbsp;</div><div style="text-align: center; font-size: 10px;">peer.getNextIndex() &lt; raftLog LogMetaData.first_log_index</div><div style="text-align: center; font-size: 9px;"><font color="#007fff" style="font-size: 9px;">即Follower节点中可能缺失Leader节点已经压缩了的日志(<b>snapshot</b>), 比如新增的节点</font></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=left;" parent="1" vertex="1">
<mxGeometry x="1960" y="2220" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-15" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-48" target="JQhlRMpWB4Idlw7NUIdd-14" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-16" value="3" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JQhlRMpWB4Idlw7NUIdd-15" vertex="1" connectable="0">
<mxGeometry x="0.3283" y="2" relative="1" as="geometry">
<mxPoint y="2" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-48" value="主要做了下面几件事:<br>1 加载日志Segment文件数据以及元数据文件数据到内存<b>SegmentedLog</b> raftLog对象<br>2 加载快照元数据到内存<b>SnapshotMetaData</b> snapshot对象<br>3 拷贝snapshot数据并用RocksDB加载,将Segment文件中已经提交的日志应用到状态机" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;align=left;arcSize=8;" parent="1" vertex="1">
<mxGeometry x="510" y="430" width="220" height="80" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-50" value="RocksDB.loadLibrary();" style="rounded=1;whiteSpace=wrap;html=1;direction=west;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="520" y="360" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-52" value="<div style="text-align: center;"><b>Segment</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;"><font color="#007fff"><b>Segment对应logDataDir下的一个文件: [open]-&lt;startIndex&gt;-&lt;endindex&gt; ,保存一组操作日志。</b></font></span></p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;"><br></span></p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;">private boolean canWrite;</span><br></p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;"><font color="#007fff">//开始日志索引</font></span></p><p style="margin: 0px 0px 0px 4px;">private long <b>startIndex</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//结束日志索引</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>endIndex</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//文件的大小,bytes</font></p><p style="margin: 0px 0px 0px 4px;">private long <b>fileSize</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//物理文件名</font></p><p style="margin: 0px 0px 0px 4px;">private String fileName;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//文件对象</font></p><p style="margin: 0px 0px 0px 4px;">private RandomAccessFile randomAccessFile;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//操作日志列表,Record 是操作日志的基本单元,服务节点初始化时会从Segment读取数据存入<br>entries</font></p><p style="margin: 0px 0px 0px 4px;">private List&lt;Record&gt; entries = new ArrayList&lt;&gt;();</p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-960" y="1240" width="440" height="240" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-53" value="<div style="text-align: center;"><b>Snapshot</b><br></div><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><b>快照,Raft论文中用快照实现日志压缩。</b></font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><br></font></p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//raftDataDir+ "/snapshot"</font></p><p style="margin: 0px 0px 0px 4px;">private String <b>snapshotDir</b><span style="background-color: initial;">;</span></p><p style="margin: 0px 0px 0px 4px;"><span style="background-color: initial;"><font color="#007fff">//快照元数据</font></span></p><p style="margin: 0px 0px 0px 4px;">private RaftProto.SnapshotMetaData <b>metaData</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 表示是否正在安装snapshot,leader向follower安装(其实就是同步数据),leader和follower同时处于installSnapshot状态</font></p><p style="margin: 0px 0px 0px 4px;">private AtomicBoolean <b>isInstallSnapshot</b> = new AtomicBoolean(false);</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 表示节点自己是否在对状态机做snapshot</font></p><p style="margin: 0px 0px 0px 4px;">private AtomicBoolean <b>isTakeSnapshot</b> = new AtomicBoolean(false);</p><p style="margin: 0px 0px 0px 4px;">private Lock lock = new ReentrantLock();</p><hr style="font-size: 10px;"><p style="margin: 0px 0px 0px 4px; font-size: 10px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=10;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-480" y="1000" width="440" height="200" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-54" value="<font color="#007fff" style="">snapshot 数据拷贝:./data/snapshot/data -&gt;&nbsp;./data/rocksdb_data&nbsp; ? 为何要这么做?<br>Segment文件日志应用到状态机,即 db.put(request.getKey().getBytes(), request.getValue().getBytes());<br></font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="760" y="390" width="480" height="40" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-106" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-55" target="jrdKXmfmH3rlp7NtxQ88-105" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-55" value="条件判断:<br>1 raftLog 不能小于 压缩规定的最小文件大小<br>2 lastAppliedIndex 不能&lt;= 最后被压缩的日志索引<br>条件不满足直接返回" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;align=left;arcSize=10;" parent="1" vertex="1">
<mxGeometry x="1000" y="2480" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-59" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-39" target="jrdKXmfmH3rlp7NtxQ88-58" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1679" y="2250" as="sourcePoint" />
<mxPoint x="1960" y="2250" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-58" value="<div style="font-size: 10px;">RaftNode#<b style="font-size: 10px;">appendEntries</b>(Peer peer)<br style="font-size: 10px;"></div><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">Leader节点将自己的日志同步到Follower节点,同步 snapshot 、raftLog 两部分</font></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=center;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1719.5" y="2220" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-60" value="RaftNode (Leader)" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="1530.5" y="1950" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="KmqIpF15YOtQaE7IZBm3-6" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="jrdKXmfmH3rlp7NtxQ88-61" target="KmqIpF15YOtQaE7IZBm3-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-61" value="<div style="font-size: 10px;"><b>installSnapshot</b>(peer)<br style="font-size: 10px;"></div><div style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">其实就是从主节点快照中将Follower节点缺失的已经被压缩的日志取出来应用到Follower状态机并更新相应元数据,如果正在压缩日志、或正在安装快照,返回false</font></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=center;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="2200" y="2220" width="199" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-66" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-63" target="jrdKXmfmH3rlp7NtxQ88-65" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-63" value="<div style="font-size: 10px;"><div>同步锁控制下获取:</div><div>lastSnapshotIndex = snapshot.getMetaData()<span style="background-color: initial;">.getLastIncludedIndex();</span></div><div>lastSnapshotTerm = snapshot.getMetaData().getLastIncludedTerm();</div></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=left;" parent="1" vertex="1">
<mxGeometry x="1949.5" y="2300" width="220" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-69" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-65" target="jrdKXmfmH3rlp7NtxQ88-68" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-65" value="<div style="font-size: 9px;"><div style="font-size: 9px;">requestBuilder.setServerId(</div><div style="font-size: 9px;">&nbsp; localServer.getServerId());</div><div style="font-size: 9px;">requestBuilder.setTerm(currentTerm);<span style="background-color: initial;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; requestBuilder.<b>setPrevLogTerm</b>(prevLogTerm);</span></div><div style="font-size: 9px;">requestBuilder.<b>setPrevLogIndex</b>(prevLogIndex);</div></div><div style="font-size: 9px;">numEntries = <b style="font-size: 9px;">packEntries</b>(peer.getNextIndex(), requestBuilder);<br style="font-size: 9px;"></div><div style="font-size: 9px;"><font color="#007fff" style="font-size: 9px;">从Leader <b style="font-size: 9px;">raftLog</b>中截取需要同步的日志片段</font></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=left;arcSize=7;" parent="1" vertex="1">
<mxGeometry x="1959.5" y="2380" width="199.5" height="100" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-71" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-68" target="jrdKXmfmH3rlp7NtxQ88-70" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-68" value="<div style=""><div style="">RaftProto.AppendEntriesResponse response = peer.getRaftConsensusServiceAsync()</div><div style="">.<b>appendEntries</b>(request);<br></div><div style=""><font color="#007fff">调用Follower一致性服务同步上一步截取的日志</font></div></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=center;arcSize=12;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="1959.5" y="2500" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-75" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-70" target="jrdKXmfmH3rlp7NtxQ88-74" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-70" value="<div style=""><div style="">如果 response == null&nbsp;</div><div style="">检查配置文件中是否包含此peer节点,不包含则从 peerMap清除,并关闭对此peer的 RPC client</div><div style=""><font color="#007fff">为了支持节点的增删</font></div></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=center;arcSize=15;" parent="1" vertex="1">
<mxGeometry x="1959.5" y="2580" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-72" value="<div style=""><div style=""><b>stepDown</b>(response.getTerm());<br></div><div style=""><font color="#007fff">Y 说明有了新的Leader, 而新旧Leader都可以与此peer通信,什么情况下会出现这种情况?与部分节点的网络连接出现抖动?</font></div></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=center;arcSize=15;" parent="1" vertex="1">
<mxGeometry x="2199.5" y="2660" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-76" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-74" target="jrdKXmfmH3rlp7NtxQ88-72" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-77" value="Y" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="jrdKXmfmH3rlp7NtxQ88-76" vertex="1" connectable="0">
<mxGeometry x="0.4271" y="-1" relative="1" as="geometry">
<mxPoint x="4" y="-1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-79" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-74" target="jrdKXmfmH3rlp7NtxQ88-78" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-80" value="N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="jrdKXmfmH3rlp7NtxQ88-79" vertex="1" connectable="0">
<mxGeometry x="0.4695" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-74" value="response.getTerm() &gt; currentTerm" style="rhombus;whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;arcSize=15;" parent="1" vertex="1">
<mxGeometry x="2000" y="2700" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-89" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-78" target="jrdKXmfmH3rlp7NtxQ88-88" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-78" value="<div style=""><div style=""><div>if (response.getResCode() == RaftProto.ResCode.<b>RES_CODE_SUCCESS</b>) { //同步成功</div><div>&nbsp; &nbsp; peer.<b>setMatchIndex</b>(prevLogIndex + numEntries);</div><div>&nbsp; &nbsp; peer.<b>setNextIndex</b>(peer.getMatchIndex() + 1);</div><div><font color="#007fff">&nbsp; &nbsp;&nbsp;//判断 peer serverId 存在于配置的server列表中,可能通过节点管理接口删除节点</font></div><div>&nbsp; &nbsp; if (ConfigurationUtils.containsServer(configuration, peer.getServer().getServerId())) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; <b>advanceCommitIndex</b>();</div><div>&nbsp; &nbsp; } else { <font color="#007fff">//节点被删除</font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (raftLog.getLastLogIndex() - peer.getMatchIndex() &lt;= raftOptions.getCatchupMargin()) {</div><div><span style="background-color: initial;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; peer.setCatchUp(true);</span><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // signal the caller thread</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <b>catchUpCondition</b>.<b>signalAll</b>();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>} else {</div><div>&nbsp; &nbsp; peer.<b>setNextIndex</b>(response.getLastLogIndex() + 1); <font color="#007fff">//在被跟随者拒绝之后,领导人就会减小 nextIndex 值并进行重试(重试则是靠心跳)。</font></div><div>}</div></div></div>" style="whiteSpace=wrap;html=1;fontSize=9;rounded=1;fontStyle=0;align=left;arcSize=3;" parent="1" vertex="1">
<mxGeometry x="2199.5" y="2740" width="380.5" height="180" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-67" value="<font color="#007fff">packEntries() 即截取raftLog中索引从 peer.nextIndex 到 raftLog最后一条日志索引,<br>如果截取日志数量大于&nbsp;raftOptions.getMaxLogEntriesPerRequest() <br>则截取固定长度,其他的在下一次 appendEntries 再同步</font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="2169.5" y="2405" width="390" height="50" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-85" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-82" target="jrdKXmfmH3rlp7NtxQ88-84" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-82" value="<div style="font-size: 10px;"><font style="font-size: 10px; font-weight: normal;">检查是否满足请求的term不小于当前节点的term,不满足返回失败,并附带上当前节点term及最后一条日志索引</font></div><div style="font-size: 10px;"><font style="font-size: 10px;"><font style="font-weight: normal; font-size: 10px;">raftNode.</font><font style="font-size: 10px;">stepDown</font><font style="font-weight: normal; font-size: 10px;">(request.getTerm());</font><br style="font-size: 10px;"></font></div><div style="font-size: 10px;"><font style="font-size: 10px;"><font style="font-weight: normal; font-size: 10px;">后面还有几条检查,不列举了去看源码吧</font></font></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;align=left;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="1000" y="1219.5" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-87" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-84" target="jrdKXmfmH3rlp7NtxQ88-86" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-84" value="<div style="">覆盖式追加日志</div><div style="">raftNode.getRaftLog().append(entries);<br></div><div style="">responseBuilder.setLastLogIndex(</div><div style="">raftNode.getRaftLog().getLastLogIndex());<br></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;align=center;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="1000" y="1300" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-86" value="<div style=""><b>advanceCommitIndex</b>(request);<br></div><div style="">return responseBuilder.build();<br></div><div style=""><font color="#007fff">Follower节点直接更新commitIndex</font><font color="#007fff">、元数据文件,将日志应用到状态机,不需要像Leader节点那样要求多数节点日志复制成功</font></div><div style=""><font color="#007fff">即Follower状态机保存Leader同步过来的最新的日志的状态</font></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;align=center;fontStyle=0;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1000" y="1380" width="200" height="80" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-95" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-88" target="jrdKXmfmH3rlp7NtxQ88-94" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-88" value="<div style="font-size: 10px;"><div style="font-size: 10px;">RaftNode#<b>advanceCommitIndex</b>()</div><div style="font-size: 10px;"><font color="#007fff">判断是否是多半节点数据同步成功,是的话才更新Leader的commitIndex,并应用到状态机</font></div></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=center;arcSize=15;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="2620" y="2800" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-92" value="<font color="#007fff" style="">注意这里更新到各个Follower的日志不一定都相同<br style="font-size: 10px;">原因看前面的 packEntries()<br>比如集群有3个节点,Leader中有abcdefghij十个日志,<br>节点A滞后Leader9个日志,B滞后Leader 7个日志<br>每次appendEntries最多可以同步5个日志,那么经过这次<br>同步,B仍然滞后Leader 4个(abcdef),<br>A仍然滞后Leader2个日志(abcdefgh),<br>需要下次appendEntries才能实现AB和Leader保持一致<br><br>此时,Leader 记录的 commitIndex&nbsp;= 8,<br>即多数节点日志更新到日志h<br><br>Leader节点和Follower节点的日志提交以及<br>应用到状态机的处理advanceCommitIndex()<br>也不一样<br style="font-size: 10px;"></font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="2599.5" y="2860" width="280" height="190" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-97" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-94" target="jrdKXmfmH3rlp7NtxQ88-96" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-94" value="<div style="font-size: 10px;"><div style="font-size: 10px;">遍历所有节点 peer.matchIndex () 记录的日志复制的最后一条日志的索引值,存入数组,然后排序,获取中间值索引为[peerNum / 2]判断其matchIndex是否&gt; Leader节点的commitIndex, 是说明多数节点数据同步成功</div></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=center;arcSize=15;" parent="1" vertex="1">
<mxGeometry x="2860" y="2800" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-99" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-96" target="jrdKXmfmH3rlp7NtxQ88-98" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-96" value="<div style=""><div style="font-size: 10px;">long newCommitIndex = matchIndexes[peerNum / 2];</div><div style=""><b>commitIndex</b> = newCommitIndex;<br></div><div style="">raftLog.<b>updateMetaData</b>(currentTerm, null, raftLog.getFirstLogIndex(), commitIndex);<br></div></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=center;arcSize=15;fillColor=#e3c800;strokeColor=#B09500;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="2860" y="2880" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-101" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-98" target="jrdKXmfmH3rlp7NtxQ88-100" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-98" value="<div style=""><div style="font-size: 10px;">遍历所有日志(索引从同步前的 commitIndex+1 到 同步后的 newCommitIndex) 应用到状态机</div><div style=""><font style="font-size: 9px;" color="#007fff">里面还有一种日志类型ENTRY_TYPE_CONFIGURATION,用于更新配置的,暂略</font></div></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=center;arcSize=15;" parent="1" vertex="1">
<mxGeometry x="2860" y="2960" width="199.5" height="80" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-102" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.999;entryY=0.435;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-100" target="jrdKXmfmH3rlp7NtxQ88-43" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1720" y="3090" />
<mxPoint x="1720" y="2415" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-103" value="<font color="#007fff">唤醒</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="jrdKXmfmH3rlp7NtxQ88-102" vertex="1" connectable="0">
<mxGeometry x="-0.9606" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-104" value="<font color="#007fff">唤醒</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="jrdKXmfmH3rlp7NtxQ88-102" vertex="1" connectable="0">
<mxGeometry x="0.9512" y="-2" relative="1" as="geometry">
<mxPoint y="12" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-100" value="<div style=""><div style="font-size: 10px;">lastAppliedIndex = commitIndex;</div><div style=""><b>commitIndexCondition</b>.signalAll();<br></div></div>" style="whiteSpace=wrap;html=1;fontSize=10;rounded=1;fontStyle=0;align=center;arcSize=15;" parent="1" vertex="1">
<mxGeometry x="2860" y="3060" width="199.5" height="60" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="jrdKXmfmH3rlp7NtxQ88-105" target="JQhlRMpWB4Idlw7NUIdd-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="jrdKXmfmH3rlp7NtxQ88-105" value="开始做快照<br><font color="#007fff">前面知道了状态机本质是RocksDB, 这里则知道了<b>快照</b>就是RocksDB <b>CheckPoint</b>实现的</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;align=center;arcSize=10;" parent="1" vertex="1">
<mxGeometry x="1000" y="2560" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="JQhlRMpWB4Idlw7NUIdd-1" target="JQhlRMpWB4Idlw7NUIdd-4" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-1" value="String tmpSnapshotDir = snapshot.getSnapshotDir() + ".tmp";<br><div>snapshot.<b>updateMetaData</b>(tmpSnapshotDir, localLastAppliedIndex,</div><div>lastAppliedTerm, localConfiguration.build());</div><div><font color="#007fff">先更新快照元数据信息到文件</font></div>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;align=center;arcSize=10;" parent="1" vertex="1">
<mxGeometry x="1241.5" y="2550" width="200" height="80" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-3" value="<font style="font-size: 10px;" color="#007fff">快照元数据文件:data/snaptshot.tmp/metadata<br></font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="1449.5" y="2575" width="230" height="30" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="JQhlRMpWB4Idlw7NUIdd-4" target="JQhlRMpWB4Idlw7NUIdd-6" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="JQhlRMpWB4Idlw7NUIdd-4" target="JQhlRMpWB4Idlw7NUIdd-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-4" value="String tmpSnapshotDataDir = tmpSnapshotDir + File.separator + "data";<br>stateMachine.<b>writeSnapshot</b>(<br>tmpSnapshotDataDir);<br><font color="#007fff">对状态机中的数据进行快照</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;align=center;arcSize=10;" parent="1" vertex="1">
<mxGeometry x="1241.5" y="2650" width="200" height="80" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-6" value="Checkpoint checkpoint = Checkpoint.<b>create</b>(db);<br>checkpoint.<b>createCheckpoint</b>(snapshotDir);<br><font color="#007fff">通过RocksDB生成数据库快照到snapshotDir,即data/snaptshot.tmp</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;align=left;arcSize=10;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="1481" y="2660" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-12" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="JQhlRMpWB4Idlw7NUIdd-8" target="JQhlRMpWB4Idlw7NUIdd-11" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-8" value="将原data/snaptshot目录清空,将data/snaptshot.tmp 改为 data/snaptshot<br><font color="#007fff"><b>CopyOnWrite</b>的思想</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;align=center;arcSize=10;" parent="1" vertex="1">
<mxGeometry x="1241.5" y="2750" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="JQhlRMpWB4Idlw7NUIdd-11" value="snapshot.<b>reload</b>();<br>lastSnapshotIndex = snapshot.<b>getMetaData</b>()<br>.getLastIncludedIndex();<br>raftLog.<b>truncatePrefix</b>(lastSnapshotIndex + 1);<br><font color="#007fff">快照之后就可以清除raftLog中的日志了</font>" style="rounded=1;whiteSpace=wrap;html=1;direction=east;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;align=center;arcSize=10;" parent="1" vertex="1">