聊聊canal-go的SimpleCanalConnector

江枫思渺然 提交于 2020-10-21 20:30:38

本文主要研究一下canal-go的SimpleCanalConnector

SimpleCanalConnector

canal-go-v1.0.7/client/simple_canal_connector.go

type SimpleCanalConnector struct {
	Address           string
	Port              int
	UserName          string
	PassWord          string
	SoTime            int32
	IdleTimeOut       int32
	ClientIdentity    pb.ClientIdentity
	Connected         bool
	Running           bool
	Filter            string
	RollbackOnConnect bool
	LazyParseEntry    bool
}
  • SimpleCanalConnector定义了Address、Port、UserName、PassWord、SoTime、IdleTimeOut、ClientIdentity、Connected、Running、Filter、RollbackOnConnect、LazyParseEntry属性

NewSimpleCanalConnector

canal-go-v1.0.7/client/simple_canal_connector.go

//NewSimpleCanalConnector 创建SimpleCanalConnector实例
func NewSimpleCanalConnector(address string, port int, username string, password string, destination string, soTimeOut int32, idleTimeOut int32) *SimpleCanalConnector {
	s := &SimpleCanalConnector{
		Address:           address,
		Port:              port,
		UserName:          username,
		PassWord:          password,
		ClientIdentity:    pb.ClientIdentity{Destination: destination, ClientId: 1001},
		SoTime:            soTimeOut,
		IdleTimeOut:       idleTimeOut,
		RollbackOnConnect: true,
	}
	return s

}
  • NewSimpleCanalConnector方法创建了SimpleCanalConnector实例

Connect

canal-go-v1.0.7/client/simple_canal_connector.go

//Connect 连接Canal-server
func (c *SimpleCanalConnector) Connect() error {
	if c.Connected {
		return nil
	}

	if c.Running {
		return nil
	}

	err := c.doConnect()
	if err != nil {
		return err
	}
	if c.Filter != "" {
		c.Subscribe(c.Filter)
	}

	if c.RollbackOnConnect {
		c.waitClientRunning()

		c.RollBack(0)
	}

	c.Connected = true
	return nil

}
  • Connect方法主要执行c.doConnect()、c.Subscribe(c.Filter)方法,若RollbackOnConnect为true则再执行c.waitClientRunning()及c.RollBack(0)方法

DisConnection

canal-go-v1.0.7/client/simple_canal_connector.go

//DisConnection 关闭连接
func (c *SimpleCanalConnector) DisConnection() {
	if c.RollbackOnConnect && c.Connected == true {
		c.RollBack(0)
	}
	c.Connected = false
	quitelyClose()
}

//quitelyClose 优雅关闭
func quitelyClose() {
	if conn != nil {
		conn.Close()
	}
}
  • DisConnection方法主要是执行conn.Close()

doConnect

canal-go-v1.0.7/client/simple_canal_connector.go

//doConnect 去连接Canal-Server
func (c SimpleCanalConnector) doConnect() error {
	address := c.Address + ":" + fmt.Sprintf("%d", c.Port)
	con, err := net.Dial("tcp", address)
	if err != nil {
		return err
	}
	conn = con

	p := new(pb.Packet)
	data, err := readNextPacket()
	if err != nil {
		return err
	}
	err = proto.Unmarshal(data, p)
	if err != nil {
		return err
	}
	if p != nil {
		if p.GetVersion() != 1 {
			panic("unsupported version at this client.")
		}

		if p.GetType() != pb.PacketType_HANDSHAKE {
			panic("expect handshake but found other type.")
		}

		handshake := &pb.Handshake{}
		err = proto.Unmarshal(p.GetBody(), handshake)
		if err != nil {
			return err
		}
		pas := []byte(c.PassWord)
		ca := &pb.ClientAuth{
			Username:               c.UserName,
			Password:               pas,
			NetReadTimeoutPresent:  &pb.ClientAuth_NetReadTimeout{NetReadTimeout: c.IdleTimeOut},
			NetWriteTimeoutPresent: &pb.ClientAuth_NetWriteTimeout{NetWriteTimeout: c.IdleTimeOut},
		}
		caByteArray, _ := proto.Marshal(ca)
		packet := &pb.Packet{
			Type: pb.PacketType_CLIENTAUTHENTICATION,
			Body: caByteArray,
		}

		packArray, _ := proto.Marshal(packet)

		WriteWithHeader(packArray)

		pp, err := readNextPacket()
		if err != nil {
			return err
		}
		pk := &pb.Packet{}

		err = proto.Unmarshal(pp, pk)
		if err != nil {
			return err
		}

		if pk.Type != pb.PacketType_ACK {
			panic("unexpected packet type when ack is expected")
		}

		ackBody := &pb.Ack{}
		err = proto.Unmarshal(pk.GetBody(), ackBody)
		if err != nil {
			return err
		}
		if ackBody.GetErrorCode() > 0 {

			panic(errors.New(fmt.Sprintf("something goes wrong when doing authentication:%s", ackBody.GetErrorMessage())))
		}

		c.Connected = true

	}
	return nil

}
  • doConnect方法通过net.Dial("tcp", address)建立连接,然后通过readNextPacket读取data,然后通过proto.Unmarshal(data, p)来解析,之后发送PacketType_CLIENTAUTHENTICATION数据进行鉴权,若ack成功则设置c.Connected为true

GetWithOutAck

canal-go-v1.0.7/client/simple_canal_connector.go

//GetWithOutAck 获取数据不Ack
func (c *SimpleCanalConnector) GetWithOutAck(batchSize int32, timeOut *int64, units *int32) (*pb.Message, error) {
	c.waitClientRunning()
	if !c.Running {
		return nil, nil
	}
	var size int32

	if batchSize < 0 {
		size = 1000
	} else {
		size = batchSize
	}
	var time *int64
	var t int64
	t = -1
	if timeOut == nil {
		time = &t
	} else {
		time = timeOut
	}
	var i int32
	i = -1
	if units == nil {
		units = &i
	}
	get := new(pb.Get)
	get.AutoAckPresent = &pb.Get_AutoAck{AutoAck: false}
	get.Destination = c.ClientIdentity.Destination
	get.ClientId = strconv.Itoa(c.ClientIdentity.ClientId)
	get.FetchSize = size
	get.TimeoutPresent = &pb.Get_Timeout{Timeout: *time}
	get.UnitPresent = &pb.Get_Unit{Unit: *units}

	getBody, err := proto.Marshal(get)
	if err != nil {
		return nil, err
	}
	packet := new(pb.Packet)
	packet.Type = pb.PacketType_GET
	packet.Body = getBody
	pa, err := proto.Marshal(packet)
	if err != nil {
		return nil, err
	}
	WriteWithHeader(pa)
	message, err := c.receiveMessages()
	if err != nil {
		return nil, err
	}
	return message, nil
}
  • GetWithOutAck方法主要是执行WriteWithHeader(pa)以及c.receiveMessages()

Get

canal-go-v1.0.7/client/simple_canal_connector.go

//Get 获取数据并且Ack数据
func (c *SimpleCanalConnector) Get(batchSize int32, timeOut *int64, units *int32) (*pb.Message, error) {
	message, err := c.GetWithOutAck(batchSize, timeOut, units)
	if err != nil {
		return nil, err
	}
	err = c.Ack(message.Id)
	if err != nil {
		return nil, err
	}
	return message, nil
}
  • Get方法先执行c.GetWithOutAck(batchSize, timeOut, units),再执行c.Ack(message.Id)

Ack

canal-go-v1.0.7/client/simple_canal_connector.go

//Ack Ack Canal-server的数据(就是昨晚某些逻辑操作后删除canal-server端的数据)
func (c *SimpleCanalConnector) Ack(batchId int64) error {
	c.waitClientRunning()
	if !c.Running {
		return nil
	}

	ca := new(pb.ClientAck)
	ca.Destination = c.ClientIdentity.Destination
	ca.ClientId = strconv.Itoa(c.ClientIdentity.ClientId)
	ca.BatchId = batchId

	clientAck, err := proto.Marshal(ca)
	if err != nil {
		return err
	}
	pa := new(pb.Packet)
	pa.Type = pb.PacketType_CLIENTACK
	pa.Body = clientAck
	pack, err := proto.Marshal(pa)
	if err != nil {
		return err
	}
	WriteWithHeader(pack)
	return nil

}
  • Ack方法主要是发送pb.PacketType_CLIENTACK

Subscribe

canal-go-v1.0.7/client/simple_canal_connector.go

//Subscribe 订阅
func (c *SimpleCanalConnector) Subscribe(filter string) error {
	c.waitClientRunning()
	if !c.Running {
		return nil
	}
	body, _ := proto.Marshal(&pb.Sub{Destination: c.ClientIdentity.Destination, ClientId: strconv.Itoa(c.ClientIdentity.ClientId), Filter: filter})
	pack := new(pb.Packet)
	pack.Type = pb.PacketType_SUBSCRIPTION
	pack.Body = body

	packet, _ := proto.Marshal(pack)
	WriteWithHeader(packet)

	p := new(pb.Packet)

	paBytes, err := readNextPacket()
	if err != nil {
		return err
	}
	err = proto.Unmarshal(paBytes, p)
	if err != nil {
		return err
	}
	ack := new(pb.Ack)
	err = proto.Unmarshal(p.Body, ack)
	if err != nil {
		return err
	}

	if ack.GetErrorCode() > 0 {
		return fmt.Errorf("failed to subscribe with reason::%s", ack.GetErrorMessage())
	}

	c.Filter = filter

	return nil
}
  • Subscribe方法主要是发送pb.PacketType_SUBSCRIPTION

UnSubscribe

canal-go-v1.0.7/client/simple_canal_connector.go

//UnSubscribe 取消订阅
func (c *SimpleCanalConnector) UnSubscribe() error {
	c.waitClientRunning()
	if c.Running {
		return nil
	}

	us := new(pb.Unsub)
	us.Destination = c.ClientIdentity.Destination
	us.ClientId = strconv.Itoa(c.ClientIdentity.ClientId)

	unSub, err := proto.Marshal(us)
	if err != nil {
		return err
	}

	pa := new(pb.Packet)
	pa.Type = pb.PacketType_UNSUBSCRIPTION
	pa.Body = unSub

	pack, err := proto.Marshal(pa)
	WriteWithHeader(pack)

	p, err := readNextPacket()
	if err != nil {
		return err
	}
	pa = nil
	err = proto.Unmarshal(p, pa)
	if err != nil {
		return err
	}
	ack := new(pb.Ack)
	err = proto.Unmarshal(pa.Body, ack)
	if err != nil {
		return err
	}
	if ack.GetErrorCode() > 0 {
		panic(errors.New(fmt.Sprintf("failed to unSubscribe with reason:%s", ack.GetErrorMessage())))
	}
	return nil
}
  • UnSubscribe方法主要是发送pb.PacketType_UNSUBSCRIPTION

RollBack

canal-go-v1.0.7/client/simple_canal_connector.go

//RollBack 回滚操作
func (c *SimpleCanalConnector) RollBack(batchId int64) error {
	c.waitClientRunning()
	cb := new(pb.ClientRollback)
	cb.Destination = c.ClientIdentity.Destination
	cb.ClientId = strconv.Itoa(c.ClientIdentity.ClientId)
	cb.BatchId = batchId

	clientBollBack, err := proto.Marshal(cb)
	if err != nil {
		return err
	}

	pa := new(pb.Packet)
	pa.Type = pb.PacketType_CLIENTROLLBACK
	pa.Body = clientBollBack
	pack, err := proto.Marshal(pa)
	if err != nil {
		return err
	}
	WriteWithHeader(pack)
	return nil
}
  • RollBack方法主要是发送pb.PacketType_CLIENTROLLBACK

小结

SimpleCanalConnector定义了Address、Port、UserName、PassWord、SoTime、IdleTimeOut、ClientIdentity、Connected、Running、Filter、RollbackOnConnect、LazyParseEntry属性;它提供了Connect、DisConnection、GetWithOutAck、Get、Ack、Subscribe、UnSubscribe、RollBack方法

doc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!