|
- package cluster
-
- import (
- "encoding/binary"
- "fmt"
- "io"
-
- "github.com/hashicorp/raft"
- "gitlink.org.cn/cloudream/common/utils/http2"
- "gitlink.org.cn/cloudream/common/utils/serder"
- )
-
- var ErrFSMNotFound = fmt.Errorf("fsm not found")
-
- type FSM interface {
- // 必须唯一且不能变化
- ID() string
- Apply(cmd []byte) ([]byte, error)
- Snapshot() (FSMSnapshot, error)
- Restore(input io.Reader) error
- }
-
- type FSMSnapshot interface {
- Persist(output io.Writer) error
- Release()
- }
-
- type SnapshotMeta struct {
- Version int
- }
-
- type raftFSM struct {
- fsms map[string]FSM
- }
-
- func NewFSM(fsms []FSM) *raftFSM {
- fsmsMp := make(map[string]FSM)
- for _, fsm := range fsms {
- _, ok := fsmsMp[fsm.ID()]
- if ok {
- panic(fmt.Sprintf("duplicate fsm id: %s", fsm.ID()))
- }
- fsmsMp[fsm.ID()] = fsm
- }
-
- return &raftFSM{fsms: fsmsMp}
- }
-
- func (f *raftFSM) Apply(l *raft.Log) interface{} {
- idLen := binary.LittleEndian.Uint32(l.Data[:4])
- fsm, ok := f.fsms[string(l.Data[4:4+idLen])]
- if !ok {
- return ErrFSMNotFound
- }
-
- cmd := l.Data[4+idLen:]
- res, err := fsm.Apply(cmd)
- return applyResult{
- Result: res,
- Error: err,
- }
- }
-
- func (f *raftFSM) Snapshot() (raft.FSMSnapshot, error) {
- snapshots := make(map[string]FSMSnapshot)
- for id, fsm := range f.fsms {
- snapshot, err := fsm.Snapshot()
- if err != nil {
- for _, snapshot := range snapshots {
- snapshot.Release()
- }
- return nil, err
- }
-
- snapshots[id] = snapshot
- }
-
- return &Snapshot{snapshots: snapshots}, nil
- }
-
- func (f *raftFSM) Restore(rc io.ReadCloser) error {
- defer rc.Close()
-
- cr := http2.NewChunkedReader(rc)
-
- _, metaBytes, err := cr.NextDataPart()
- if err != nil {
- return fmt.Errorf("read meta data part: %v", err)
- }
- meta := SnapshotMeta{}
- if err := serder.JSONToObject(metaBytes, &meta); err != nil {
- return fmt.Errorf("unmarshal meta data: %v", err)
- }
- // 进行类似的检查
- if meta.Version != 1 {
- return fmt.Errorf("unsupported version: %d", meta.Version)
- }
-
- for {
- id, reader, err := cr.NextPart()
- if err != nil && err != io.EOF {
- return err
- }
-
- if err == io.EOF {
- // TODO 考虑检查一下是否调用了所有FSM的Restore方法
- break
- }
-
- fsm, ok := f.fsms[id]
- if !ok {
- // TODO 兼容性
- continue
- }
-
- err = fsm.Restore(reader)
- if err != nil {
- // TODO 不知道Raft库在发现Restore失败后是否能够及时停止服务
- return fmt.Errorf("restore fsm %s: %v", id, err)
- }
- }
-
- return nil
- }
-
- var _ raft.FSM = (*raftFSM)(nil)
-
- type Snapshot struct {
- snapshots map[string]FSMSnapshot
- }
-
- func (s *Snapshot) Persist(sink raft.SnapshotSink) error {
- meta := SnapshotMeta{Version: 1}
- metaBytes, err := serder.ObjectToJSON(meta)
- if err != nil {
- sink.Cancel()
- return fmt.Errorf("marshal meta data: %v", err)
- }
-
- cw := http2.NewChunkedWriter(sink)
- err = cw.WriteDataPart("meta", metaBytes)
- if err != nil {
- sink.Cancel()
- return fmt.Errorf("write meta data part: %v", err)
- }
-
- for id, snapshot := range s.snapshots {
- w := cw.BeginPart(id)
- err := snapshot.Persist(w)
- if err != nil {
- sink.Cancel()
- return fmt.Errorf("persist fsm %s: %v", id, err)
- }
- }
-
- return sink.Close()
- }
-
- func (s *Snapshot) Release() {
- for _, snapshot := range s.snapshots {
- snapshot.Release()
- }
- }
-
- type applyResult struct {
- Result []byte
- Error error
- }
|