You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cluster.go 6.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package cluster
  2. import (
  3. "crypto/tls"
  4. "encoding/binary"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "sync"
  9. "time"
  10. "github.com/hashicorp/raft"
  11. raftboltdb "github.com/hashicorp/raft-boltdb"
  12. "github.com/samber/lo"
  13. "gitlink.org.cn/cloudream/common/pkgs/async"
  14. "gitlink.org.cn/cloudream/common/pkgs/logger"
  15. "gitlink.org.cn/cloudream/jcs-pub/common/ecode"
  16. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
  17. clirpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/client"
  18. "google.golang.org/grpc"
  19. "google.golang.org/grpc/credentials"
  20. )
  21. type Cluster struct {
  22. cfg Config
  23. poolCfg clirpc.PoolConfig
  24. masterCli MasterClient
  25. doneCh chan any
  26. raft *raft.Raft
  27. fsm *raftFSM
  28. transport *Transport
  29. }
  30. func New(cfg Config) *Cluster {
  31. return &Cluster{
  32. cfg: cfg,
  33. masterCli: MasterClient{
  34. Client: clirpc.NewFusedClient(rpc.Failed(ecode.OperationFailed, "master unknown")),
  35. },
  36. doneCh: make(chan any, 1),
  37. }
  38. }
  39. func (c *Cluster) Start(fsms []FSM) (*ClusterEventChan, error) {
  40. log := logger.WithField("Mod", "Cluster")
  41. ch := async.NewUnboundChannel[ClusterEvent]()
  42. if !c.cfg.Enabled {
  43. log.Infof("cluster disabled")
  44. return ch, nil
  45. }
  46. c.fsm = NewFSM(fsms)
  47. poolCfgJSON := clirpc.PoolConfigJSON{
  48. RootCA: c.cfg.RootCA,
  49. ClientCert: c.cfg.ClientCert,
  50. ClientKey: c.cfg.ClientKey,
  51. }
  52. poolCfg, err := poolCfgJSON.Build()
  53. if err != nil {
  54. return nil, fmt.Errorf("build pool config: %w", err)
  55. }
  56. c.poolCfg = *poolCfg
  57. raftCfg := raft.DefaultConfig()
  58. raftCfg.LocalID = raft.ServerID(c.cfg.Announce)
  59. err = os.MkdirAll(c.cfg.StoreBase, 0755)
  60. if err != nil {
  61. return nil, fmt.Errorf("create store base dir: %w", err)
  62. }
  63. logDB, err := raftboltdb.NewBoltStore(filepath.Join(c.cfg.StoreBase, "log.db"))
  64. if err != nil {
  65. return nil, fmt.Errorf("create raft log store: %w", err)
  66. }
  67. stableDB, err := raftboltdb.NewBoltStore(filepath.Join(c.cfg.StoreBase, "stable.db"))
  68. if err != nil {
  69. return nil, fmt.Errorf("create raft stable store: %w", err)
  70. }
  71. snapshotStore, err := raft.NewFileSnapshotStore(c.cfg.StoreBase, 1, os.Stdout)
  72. if err != nil {
  73. return nil, fmt.Errorf("create raft snapshot store: %w", err)
  74. }
  75. c.transport = NewTransport(c.cfg.Announce, *poolCfg)
  76. rft, err := raft.NewRaft(raftCfg, c.fsm, logDB, stableDB, snapshotStore, c.transport)
  77. if err != nil {
  78. return nil, fmt.Errorf("create raft: %w", err)
  79. }
  80. hasState, err := raft.HasExistingState(logDB, stableDB, snapshotStore)
  81. if err != nil {
  82. return nil, fmt.Errorf("check has existing state: %w", err)
  83. }
  84. if !hasState {
  85. bootCfg := raft.Configuration{}
  86. if !lo.Contains(c.cfg.Peers, c.cfg.Announce) {
  87. bootCfg.Servers = append(bootCfg.Servers, raft.Server{
  88. ID: raft.ServerID(c.cfg.Announce),
  89. Address: raft.ServerAddress(c.cfg.Announce),
  90. })
  91. }
  92. for _, peer := range c.cfg.Peers {
  93. bootCfg.Servers = append(bootCfg.Servers, raft.Server{
  94. ID: raft.ServerID(peer),
  95. Address: raft.ServerAddress(peer),
  96. })
  97. }
  98. bootFut := rft.BootstrapCluster(bootCfg)
  99. if err := bootFut.Error(); err != nil {
  100. return nil, fmt.Errorf("bootstrap cluster: %w", err)
  101. }
  102. log.Infof("bootstrap new cluster")
  103. } else {
  104. log.Infof("start existing cluster")
  105. }
  106. ch.Send(&BootstrapEvent{})
  107. c.raft = rft
  108. eventCh := make(chan raft.Observation, 1)
  109. obs := raft.NewObserver(eventCh, true, nil)
  110. rft.RegisterObserver(obs)
  111. go func() {
  112. loop:
  113. for {
  114. select {
  115. case <-c.doneCh:
  116. break loop
  117. case e := <-eventCh:
  118. state, ok := e.Data.(raft.RaftState)
  119. if !ok {
  120. continue
  121. }
  122. switch state {
  123. case raft.Leader:
  124. log.Info("become leader")
  125. ch.Send(&LeaderEvent{
  126. CurrentIsMaster: true,
  127. Address: c.cfg.Announce,
  128. })
  129. case raft.Follower:
  130. addr, id := rft.LeaderWithID()
  131. log.Infof("become follower, master is: %v, %v", id, addr)
  132. ch.Send(&LeaderEvent{
  133. CurrentIsMaster: false,
  134. Address: string(addr),
  135. })
  136. case raft.Candidate:
  137. log.Info("become candidate")
  138. }
  139. }
  140. }
  141. c.raft.DeregisterObserver(obs)
  142. }()
  143. return ch, nil
  144. }
  145. func (c *Cluster) Stop() {
  146. c.raft.Shutdown().Error()
  147. select {
  148. case c.doneCh <- nil:
  149. default:
  150. }
  151. }
  152. func (c *Cluster) ID() string {
  153. return c.cfg.Announce
  154. }
  155. func (c *Cluster) IsMaster() bool {
  156. addr, _ := c.raft.LeaderWithID()
  157. return string(addr) == c.cfg.Announce
  158. }
  159. func (c *Cluster) Enabled() bool {
  160. return c.cfg.Enabled
  161. }
  162. func (c *Cluster) Name() string {
  163. return c.cfg.NodeName
  164. }
  165. // 由于主节点可能会变化,因此不要缓存MasterClient,每次都重新获取
  166. func (c *Cluster) MasterClient() *MasterClient {
  167. addr, _ := c.raft.LeaderWithID()
  168. c.masterCli.lock.Lock()
  169. defer c.masterCli.lock.Unlock()
  170. addr2 := string(addr)
  171. if addr2 == "" {
  172. if c.masterCli.con != nil {
  173. c.masterCli.con.Close()
  174. }
  175. c.masterCli.Client = clirpc.NewFusedClient(rpc.Failed(ecode.ClusterNoMaster, "no master"))
  176. return &c.masterCli
  177. }
  178. if c.masterCli.addr != addr2 {
  179. if c.masterCli.con != nil {
  180. c.masterCli.con.Close()
  181. }
  182. gcon, err := grpc.NewClient(addr2, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
  183. RootCAs: c.poolCfg.Conn.RootCA,
  184. Certificates: []tls.Certificate{*c.poolCfg.Conn.ClientCert},
  185. ServerName: rpc.InternalAPISNIV1,
  186. NextProtos: []string{"h2"},
  187. })))
  188. if err != nil {
  189. c.masterCli.Client = clirpc.NewFusedClient(rpc.Failed(ecode.OperationFailed, "%v", err))
  190. c.masterCli.addr = ""
  191. } else {
  192. c.masterCli.con = gcon
  193. c.masterCli.Client = clirpc.NewClient(gcon)
  194. c.masterCli.addr = addr2
  195. }
  196. }
  197. return &c.masterCli
  198. }
  199. func (c *Cluster) RaftTransport() *Transport {
  200. return c.transport
  201. }
  202. // 只有Leader才能调用
  203. func (c *Cluster) Apply(fsmID string, data []byte, timeout time.Duration) ([]byte, error) {
  204. fsmIDBytes := []byte(fsmID)
  205. logBytes := make([]byte, 4+len(fsmIDBytes)+len(data))
  206. // 前4个字节表示ID的长度,后面跟着ID和数据
  207. binary.LittleEndian.PutUint32(logBytes[:4], uint32(len(fsmIDBytes)))
  208. copy(logBytes[4:], fsmIDBytes)
  209. copy(logBytes[4+len(fsmIDBytes):], data)
  210. fut := c.raft.Apply(logBytes, timeout)
  211. err := fut.Error()
  212. if err != nil {
  213. return nil, err
  214. }
  215. applyRet := fut.Response().(applyResult)
  216. return applyRet.Result, applyRet.Error
  217. }
  218. type ClusterEvent interface {
  219. IsClusterEvent() bool
  220. }
  221. type ClusterEventChan = async.UnboundChannel[ClusterEvent]
  222. type ExitEvent struct {
  223. Err error
  224. }
  225. func (e *ExitEvent) IsClusterEvent() bool {
  226. return true
  227. }
  228. type BootstrapEvent struct{}
  229. func (e *BootstrapEvent) IsClusterEvent() bool {
  230. return true
  231. }
  232. type LeaderEvent struct {
  233. CurrentIsMaster bool
  234. Address string
  235. }
  236. func (e *LeaderEvent) IsClusterEvent() bool {
  237. return true
  238. }
  239. type MasterClient struct {
  240. *clirpc.Client
  241. con *grpc.ClientConn
  242. addr string
  243. lock sync.Mutex
  244. }
  245. func (c *MasterClient) Release() {
  246. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。