분산 시스템의 주요 선택 장면 분석 및 실현

하나 : 마스터를 선택해야하는 장면

1 : 서비스에 여러 컴퓨터가 있으며 그중 하나가 작업을 수행하는 데 사용됩니다. 여러 시스템을 동시에 실행하면 문제가 발생합니다. 예를 들어 데이터베이스에서 오류 상태의 레코드를 꺼내고 다시 실행하면 여러 시스템이 동시에 실행되면 실패한 작업이 여러 시스템에서 동시에 실행됩니다.

2 : 서비스에 여러 머신이 있으며 그중 하나를 마스터로 선택하고 마스터는 작업 배포를 담당하며 모든 사람이 함께 작업을 소비하고 처리합니다. 또는 데이터베이스에서 실패한 레코드를 꺼내서 다시 실행하면 하나의 시스템이 처리하지 못할 수 있으므로 여러 시스템이 함께 처리해야합니다. 이때 메인 머신은 데이터베이스에서 실패한 레코드를 찾아 메시지 큐에 쓰는 역할을하고 다른 머신은 큐의 작업을 함께 소비하고 실패한 레코드를 처리합니다.

 

2 : 마스터 선택

위의 마스터 선택 시나리오에 따르면 실제로 여러 기계에서 무작위로 하나를 선택할 수 있는데, 이는 뗏목의 마스터 선택 알고리즘보다 훨씬 간단합니다. 구성 파일에 기계를 지정할 수도 있으며이 기계 만 관련 기능을 수행하고 다른 기계는 수행하지 않습니다. 고정 된 수의 기계이고 하나의 기계가 우리의 요구를 충족시킬 수 있다면 이것은 실제로 괜찮습니다. 기계가 고정되어 있지 않고 단일 기계로 처리 할 수없는 경우 구성 파일 방법이 적합하지 않습니다.

경쟁을 사용하여 마스터를 선택할 수 있으며 먼저 획득 한 사람이 마스터입니다.

 

1 : 옵션 1

redis 체계를 사용하여 실현하십시오. 지정된 키가 없으면이 키에 머신 정보를 씁니다. 성공적으로 기록한 머신이 마스터입니다. 머신이 비정상적으로 중단되지 않도록 만료 시간을 설정합니다. 모든 머신은 정기적으로 redis 잠금을 확보해야합니다. SETNX 명령은 우리의 요구를 충족시켜줍니다. redis 작성에 성공한 사람이 마스터이고, 쓰기에 실패한 사람이 슬레이브입니다.

이점:

  • 1 : 구현이 간단하고 구성 파일보다 약간 낫으며 기계 역학을 지원합니다.

단점 :

  • 1 : 잠금 장치를 정기적으로 잡아야 함
  • 2 : 마스터는 수시로 변경 될 수 있으며 전환 과정에서 마스터의 비즈니스 로직의 정확성을 보장해야합니다.
  • 3 : 일부 시간 조각에는 마스터가 없을 수 있습니다. 즉, 마스터가 전화를 끊고 다른 시스템이 잠금을 잡을 시간에 도달하지 않았습니다.이 시간 조각에는 마스터가 없습니다.

 

2 : 옵션 2

etcd 체계로 구현되었습니다. Etcd는 존재하지 않기 때문에 기록 할 수있는 트랜잭션을 지원하여 redis SETNX와 동일한 효과를 달성하고 etcd의 임대 메커니즘을 통해 마스터가 중단 될 때 모든 시스템에 알림을 보내고 모든 사람이 자동으로 새로운 마스터 선택 라운드를 시작하도록합니다. 그 문장에서 가장 먼저 잡힌 것은 주님입니다.

이점:

  • 디자인 결함없이 우리의 요구를 충족
  • 마스터가 전화를 끊을 때만 마스터가 재 선출되며, 전환 과정에서 마스터가 비즈니스 로직에 미치는 영향에 대해 걱정할 필요가 없습니다.

단점 :

  • 구현하기가 비교적 복잡하므로 시도해 보겠습니다. 
  •  

golang 소스 코드는 다음과 같이 구현됩니다.

  1 패키지 etcdDemo 
  2 
  3 import ( 
  4 "context" 
  5 "fmt" 
  6 "github.com/coreos/etcd/clientv3"7 
  "github.com/google/uuid"8 
  "time" 
  9) 
 10 
 11 유형 콜백 func (isMaster bool) 
 12 
 13 type SelectMaster struct { 
 14 endPoints [] string 
 15 key string 
 16 cli * clientv3.Client 
 17 임대 * clientv3.LeaseGrantResponse 
 18 chClose chan int 
 19 callback Callback 
 20 token string 
 21 isMaster bool 
 22} 
 23
 24 func NewSelectMaster (endPoints [] string, key string) (* SelectMaster, error) { 
 25 sm : = & SelectMaster { 
 26 endPoints : endPoints, 
 27 key : key, 
 28 chClose : make (chan int, 0), 
 29 token : uuid .New (). String (), 
 30} 
 31 
 32 cli, err : = clientv3.New (clientv3.Config { 
 33 Endpoints : endPoints, 
 34 DialTimeout : 3 * time.Second, 
 35}) 
 36 if err! = nil { 
 37 return sm, err 
 38} 
 39 sm.cli = cli 
 40 go sm.ioLoop () 
 41 return sm, nil 
 42} 
 43
 44 func (sm * SelectMaster) ioLoop () { 
 45 fmt.Println ( "SelectMaster.ioLoop start") 
 46 ticker : = time.NewTicker (time.Second * 3) 
 47 defer ticker.Stop () 
 48 chWatch : = sm. cli.Watch (context.TODO (), sm.key) 
 49 for { 
 50 select { 
 51 case <-ticker. C : 
 52 if sm.lease == nil { 
 53 rentResp, err : = sm.cli.Grant (context .Background (), 4) 
 54 if err! = nil { 
 55 fmt.Println ( "cli.Grant error =", err.Error ()) 
 56} else { 
 57 sm.lease = rentResp 
 58} 
 59}
 60 if sm.lease! = nil { 
 74 if sm.callback == nil {
 61 _, err : = sm.cli.KeepAliveOnce (context.Background (), sm.lease.ID) 
 62 if err! = nil { 
 63 fmt.Println ( "cli.KeepAliveOnce error =", err.Error ()) 
 64 break 
 65} 
 66} 
 67 case c : = <-chWatch : 
 68 for _, e : = range c.Events { 
 69 if e == nil || e.Kv == nil { 
 70 continue 
 71} 
 72 token : = string (e.Kv.Value) 
 73 sm.isMaster = sm.token == token 
 75 fmt.Println ( "SelectMaster.callback is nil") 
 76} else {
 77 sm.callback (sm.isMaster) 
 78 fmt.Println ( "SelectMaster.isLoop token =", token) 
 79 if token == ""{// 主 挂 了 , 开始 竞选
 80 sm.election () 
 81} 
 82} 
 83} 
 84 case <-sm.chClose : 
 85 goto stop 
 86} 
 87} 
 88 stop : 
 89 fmt.Println ( "SelectMaster.ioLoop end") 
 90} 
 91 
 92 func (sm * SelectMaster) IsMaster () bool { 
 93 return sm .isMaster 
 94}  
 95
 96 func (sm * SelectMaster) Close () { 
 97 sm.chClose <-1 
 98}
 99 
100 FUNC (SM * SelectMaster) 선거 (콜백 콜백) (BOOL 에러) { 
101 sm.callback = 콜백 
102 복귀 sm.election () 
103} 
104 
105 FUNC (SM * SelectMaster) 선거 () (BOOL 에러) { 
106 ctx, cancel : = context.WithTimeout (context.Background (), time.Second * 3) 
107 defer cancel () 
108 임대 Resp, err : = sm.cli.Grant (ctx, 10) 
109 if err! = nil { 
110 return false, err 
111} 
112 sm.lease = 임대 Resp 
113 txn : = clientv3.NewKV (sm.cli) .Txn (context.TODO ()) 
116 txnResp, err : = txn.Commit () 
114 txn.If (clientv3.Compare (clientv3.CreateRevision (sm.key), "=", 0)).
115 Then (clientv3.OpPut (sm.key, sm.token, clientv3.WithLease (leaseResp.ID))). Else () 
133 isSuccess, err : = sm.Election (콜백)
117 if err! = nil { 
118 return false, err 
119} 
120 return txnResp.Succeeded, nil 
121} 
122 
123 func testSelectMaster () * SelectMaster { 
124 endPoints : = [] string { "172.25.20.248:2379"} 
125 sm , err : = NewSelectMaster (endPoints, "/ test / lock") 
126 if err! = nil { 
127 fmt.Println (err.Error ()) 
128 return nil 
129} 
130 callback : = func (isMaster bool) { 
131 fmt .Println (sm.token, "callback =", isMaster) 
132} 
134 if err! = nil { 
135 fmt.Println (sm.token, "Election =", err.Error ()) 
136} else { 
137 fmt.Println (sm.token "선거 ="isSuccess) 
(138)} 
(139) 리턴 SM 
(140)} 
(141) 
(142)의 FUNC TestSelectMaster () { 
143 VAR 마스터 SelectMaster * 
I 144 : = 0; 나는 <3; i ++ { 
145 sm : = testSelectMaster () 
146 if sm.IsMaster () { 
147 master = sm 
148} 
149} 
150 if master! = nil { 
151 
master.Close ( ) 
152} 153 time.Sleep (time.Second * 10 ) 
154}

추천

출처blog.csdn.net/a159357445566/article/details/108550744