├── .gitignore ├── net_test.go ├── init.go ├── send_message_response_header.go ├── admin.go ├── send_message_context.go ├── send_result.go ├── consumer_test.go ├── net.go ├── topic_publish_info.go ├── LICENSE ├── perm.go ├── pull_message.go ├── message_queue.go ├── send_message.go ├── response_code.go ├── mix_all.go ├── message_client_id_setter.go ├── producer_test.go ├── remote_cmd.go ├── client_test.go ├── README.md ├── message_const.go ├── store.go ├── request_code.go ├── rebalance.go ├── message.go ├── consumer.go ├── remoting_client.go ├── producer.go └── mq_client.go /.gitignore: -------------------------------------------------------------------------------- 1 | .svn/ 2 | .idea/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /net_test.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import "testing" 4 | 5 | func TextGetLocalIp4(t testing.T) { 6 | println(GetLocalIp4()) 7 | } 8 | -------------------------------------------------------------------------------- /init.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().UnixNano()) 10 | } 11 | -------------------------------------------------------------------------------- /send_message_response_header.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | type SendMessageResponseHeader struct { 4 | msgId string 5 | queueId int32 6 | queueOffset int64 7 | transactionId string 8 | } 9 | -------------------------------------------------------------------------------- /admin.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | type Admin interface { 4 | createTopic(key string, newTopic string, queueNum int) 5 | createTopic1(key string, newTopic string, queueNum int, topicSysFlag int) 6 | searchOffset(mq MessageQueue, timestamp int64) error 7 | maxOffset(mq MessageQueue) error 8 | minOffset(mq MessageQueue) error 9 | earliestMsgStoreTime(mq MessageQueue) error 10 | queryMessage(topic string, key, string, maxNum int, begin int64, end int64) error 11 | } 12 | -------------------------------------------------------------------------------- /send_message_context.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | const ( 4 | NormalMsg = iota 5 | TransMsgHalf 6 | TransMsgCommit 7 | DelayMsg 8 | ) 9 | 10 | type SendMessageContext struct { 11 | producerGroup string 12 | Message Message 13 | mq MessageQueue 14 | brokerAddr string 15 | bornHost string 16 | communicationMode string 17 | sendResult *SendResult 18 | props map[string]string 19 | producer Producer 20 | msgType string 21 | } 22 | 23 | func newSendMessageContext() *SendMessageContext { 24 | return &SendMessageContext{} 25 | } 26 | 27 | // TODO add decodeMessage 28 | func (s *SendMessageContext) decodeMessage(data []byte) (messageExt []*MessageExt) { 29 | return messageExt 30 | } 31 | -------------------------------------------------------------------------------- /send_result.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | const ( 4 | SendStatusOK = iota 5 | SendStatusFlushDiskTimeout 6 | SendStatusFlushSlaveTimeout 7 | SendStatusSlaveNotAvailable 8 | ) 9 | 10 | type SendResult struct { 11 | sendStatus int 12 | msgId string 13 | messageQueue *MessageQueue 14 | queueOffset int64 15 | transactionId string 16 | offsetMsgId string 17 | regionId string 18 | } 19 | 20 | func NewSendResult(sendStatus int, msgId string, offsetMsgId string, messageQueue *MessageQueue, queueOffset int64) *SendResult { 21 | return &SendResult{ 22 | sendStatus: sendStatus, 23 | msgId: msgId, 24 | offsetMsgId: offsetMsgId, 25 | messageQueue: messageQueue, 26 | queueOffset: queueOffset, 27 | } 28 | } 29 | 30 | func (s *SendResult) SendResult(SendStatus int, msgId string, messageQueue MessageQueue, queueOffset uint64, 31 | transactionId string, offsetMsgId string, regionId string) (ok bool) { 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /consumer_test.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | var consumerGroup = "dev-goProducerConsumerTest" 9 | var consumerTopic = "goProducerConsumerTest" 10 | var sleep = 60 * time.Second 11 | var consumerConf = &Config{ 12 | Namesrv: "192.168.7.101:9876;192.168.7.102:9876;192.168.7.103:9876", 13 | ClientIp: "192.168.23.137", 14 | InstanceName: "DEFAULT_tt", 15 | } 16 | 17 | func TestConsume(t *testing.T) { 18 | consumer, err := NewDefaultConsumer(consumerGroup, consumerConf) 19 | if err != nil { 20 | t.Fatalf("NewDefaultConsumer err, %s", err) 21 | } 22 | consumer.Subscribe(consumerTopic, "*") 23 | consumer.RegisterMessageListener( 24 | func(msgs []*MessageExt) error { 25 | for i, msg := range msgs { 26 | t.Log("msg", i, msg.Topic, msg.Flag, msg.Properties, string(msg.Body)) 27 | } 28 | t.Log("Consume success!") 29 | return nil 30 | }) 31 | consumer.Start() 32 | 33 | time.Sleep(sleep) 34 | } 35 | -------------------------------------------------------------------------------- /net.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | ) 7 | 8 | // Get local IPV4 Address 9 | func GetLocalIp4() (ip string) { 10 | interfaces, err := net.Interfaces() 11 | if err != nil { 12 | return 13 | } 14 | 15 | for _, face := range interfaces { 16 | if strings.Contains(face.Name, "lo") { 17 | continue 18 | } 19 | addrs, err := face.Addrs() 20 | if err != nil { 21 | return 22 | } 23 | for _, addr := range addrs { 24 | if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 25 | if ipnet.IP.To4() != nil { 26 | currIp := ipnet.IP.String() 27 | if !strings.Contains(currIp, ":") && currIp != "127.0.0.1" && isIntranetIpv4(currIp) { 28 | ip = currIp 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | return 36 | } 37 | 38 | func isIntranetIpv4(ip string) bool { 39 | if strings.HasPrefix(ip, "192.168.") || strings.HasPrefix(ip, "169.254.") { 40 | return true 41 | } 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /topic_publish_info.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | type TopicPublishInfo struct { 8 | orderTopic bool 9 | haveTopicRouterInfo bool 10 | messageQueueList []*MessageQueue 11 | topicRouteData *TopicRouteData 12 | } 13 | 14 | func NewTopicPublishInfo() *TopicPublishInfo { 15 | return &TopicPublishInfo{} 16 | } 17 | 18 | func (t *TopicPublishInfo) ok() (ok bool) { 19 | if len(t.messageQueueList) != 0 { 20 | ok = true 21 | } 22 | return 23 | } 24 | 25 | func (t *TopicPublishInfo) selectOneMessageQueue(lastBrokerName string) (messageQueue *MessageQueue) { 26 | // TODO add sendLatencyFaultEnable trigger and handle it 27 | // TODO optimize algorithm of getting message from queue 28 | mqCnt := len(t.messageQueueList) 29 | messageQueue = t.messageQueueList[rand.Intn(mqCnt)] 30 | if lastBrokerName != "" { 31 | for i := 0; i < mqCnt; i++ { 32 | if lastBrokerName == t.messageQueueList[i].brokerName { 33 | messageQueue = t.messageQueueList[i] 34 | return 35 | } 36 | } 37 | } 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2017 sevenNt sevennt.leslie@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /perm.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type permName struct { 8 | PermPriority int 9 | PermRead int 10 | PermWrite int 11 | PermInherit int 12 | } 13 | 14 | var PermName = permName{ 15 | PermPriority: 0x1 << 3, 16 | PermRead: 0x1 << 2, 17 | PermWrite: 0x1 << 1, 18 | PermInherit: 0x1 << 0, 19 | } 20 | 21 | func (p permName) perm2String(perm int) string { 22 | stringBuffer := bytes.NewBuffer([]byte{}) 23 | if PermName.isReadable(perm) { 24 | stringBuffer.WriteString("R") 25 | } else { 26 | stringBuffer.WriteString("-") 27 | } 28 | if PermName.isWritable(perm) { 29 | stringBuffer.WriteString("W") 30 | } else { 31 | stringBuffer.WriteString("-") 32 | } 33 | if PermName.isInherited(perm) { 34 | stringBuffer.WriteString("X") 35 | } else { 36 | stringBuffer.WriteString("-") 37 | } 38 | 39 | return stringBuffer.String() 40 | } 41 | 42 | func (p permName) isReadable(perm int) bool { 43 | return (perm & p.PermRead) == p.PermRead 44 | } 45 | 46 | func (p permName) isWritable(perm int) bool { 47 | return (perm & p.PermWrite) == p.PermWrite 48 | } 49 | 50 | func (p permName) isInherited(perm int) bool { 51 | return (perm & p.PermInherit) == p.PermInherit 52 | } 53 | -------------------------------------------------------------------------------- /pull_message.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | type PullRequest struct { 4 | consumerGroup string 5 | messageQueue *MessageQueue 6 | nextOffset int64 7 | } 8 | 9 | type PullMessageRequestHeader struct { 10 | ConsumerGroup string `json:"consumerGroup"` 11 | Topic string `json:"topic"` 12 | QueueId int32 `json:"queueId"` 13 | QueueOffset int64 `json:"queueOffset"` 14 | MaxMsgNums int32 `json:"maxMsgNums"` 15 | SysFlag int32 `json:"sysFlag"` 16 | CommitOffset int64 `json:"commitOffset"` 17 | SuspendTimeoutMillis int64 `json:"suspendTimeoutMillis"` 18 | Subscription string `json:"subscription"` 19 | SubVersion int64 `json:"subVersion"` 20 | } 21 | 22 | type Service interface { 23 | pullMessage(pullRequest *PullRequest) 24 | } 25 | 26 | type PullMessageService struct { 27 | pullRequestQueue chan *PullRequest 28 | service Service 29 | } 30 | 31 | func NewPullMessageService() *PullMessageService { 32 | return &PullMessageService{ 33 | pullRequestQueue: make(chan *PullRequest, 1024), 34 | } 35 | } 36 | 37 | func (p *PullMessageService) start() { 38 | for { 39 | pullRequest := <-p.pullRequestQueue 40 | p.service.pullMessage(pullRequest) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /message_queue.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | type MessageQueue struct { 4 | topic string 5 | brokerName string 6 | queueId int32 7 | } 8 | 9 | func NewMessageQueue(topic string, brokerName string, queueId int32) *MessageQueue { 10 | return &MessageQueue{ 11 | topic: topic, 12 | brokerName: brokerName, 13 | queueId: queueId, 14 | } 15 | } 16 | 17 | func (m *MessageQueue) clone() *MessageQueue { 18 | no := new(MessageQueue) 19 | no.topic = m.topic 20 | no.queueId = m.queueId 21 | no.brokerName = m.brokerName 22 | return no 23 | } 24 | 25 | func (m MessageQueue) getBrokerName() string { 26 | return m.brokerName 27 | } 28 | 29 | type MessageQueues []*MessageQueue 30 | 31 | func (m MessageQueues) Less(i, j int) bool { 32 | imq := m[i] 33 | jmq := m[j] 34 | 35 | if imq.topic < jmq.topic { 36 | return true 37 | } else if imq.topic < jmq.topic { 38 | return false 39 | } 40 | 41 | if imq.brokerName < jmq.brokerName { 42 | return true 43 | } else if imq.brokerName < jmq.brokerName { 44 | return false 45 | } 46 | 47 | if imq.queueId < jmq.queueId { 48 | return true 49 | } 50 | return false 51 | } 52 | 53 | func (m MessageQueues) Swap(i, j int) { 54 | m[i], m[j] = m[j], m[i] 55 | } 56 | 57 | func (m MessageQueues) Len() int { 58 | return len(m) 59 | } 60 | -------------------------------------------------------------------------------- /send_message.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | type SendRequest struct { 4 | producerGroup string 5 | messageQueue *MessageQueue 6 | nextOffset int64 7 | } 8 | 9 | type SendMessageRequestHeader struct { 10 | ProducerGroup string `json:"producerGroup"` 11 | Topic string `json:"topic"` 12 | DefaultTopic string `json:"defaultTopic"` 13 | DefaultTopicQueueNums int `json:"defaultTopicQueueNums"` 14 | QueueId int32 `json:"queueId"` 15 | SysFlag int `json:"sysFlag"` 16 | BornTimestamp int64 `json:"bornTimestamp"` 17 | Flag int32 `json:"flag"` 18 | Properties string `json:"properties"` 19 | ReconsumeTimes int `json:"reconsumeTimes"` 20 | UnitMode bool `json:"unitMode"` 21 | MaxReconsumeTimes int `json:"maxReconsumeTimes"` 22 | } 23 | 24 | type SendMessageService struct { 25 | pushRequestQueue chan *SendRequest 26 | producer *DefaultProducer 27 | } 28 | 29 | func NewSendMessageService() *SendMessageService { 30 | return &SendMessageService{ 31 | pushRequestQueue: make(chan *SendRequest, 1024), 32 | } 33 | } 34 | 35 | func (s *SendMessageService) start() { 36 | //for { 37 | // pushRequest := <-self.pushRequestQueue 38 | // self.producer.sendMessage(pushRequest) 39 | //} 40 | } 41 | -------------------------------------------------------------------------------- /response_code.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | const ( 4 | // 成功 5 | Success = 0 6 | // 发生了未捕获异常 7 | SysError = 1 8 | // 由于线程池拥堵,系统繁忙 9 | SysBusy = 2 10 | // 请求代码不支持 11 | RequestCodeNotSupported = 3 12 | //事务失败,添加db失败 13 | TransactionFailed = 4 14 | // Broker 刷盘超时 15 | FlushDiskTimeout = 10 16 | // Broker 同步双写,Slave不可用 17 | SlaveNotAvailable = 11 18 | // Broker 同步双写,等待Slave应答超时 19 | FlushSlaveTimeout = 12 20 | // Broker 消息非法 21 | MsgIllegal = 13 22 | // Broker, Namesrv 服务不可用,可能是正在关闭或者权限问题 23 | ServiceNotAvailable = 14 24 | // Broker, Namesrv 版本号不支持 25 | VersionNotSupported = 15 26 | // Broker, Namesrv 无权限执行此操作,可能是发、收、或者其他操作 27 | NoPermission = 16 28 | // Broker, Topic不存在 29 | TopicNotExist = 17 30 | // Broker, Topic已经存在,创建Topic 31 | TopicExistAlready = 18 32 | // Broker 拉消息未找到(请求的Offset等于最大Offset,最大Offset无对应消息) 33 | PullNotFound = 19 34 | // Broker 可能被过滤,或者误通知等 35 | PullRetryImmediately = 20 36 | // Broker 拉消息请求的Offset不合法,太小或太大 37 | PullOffsetMoved = 21 38 | // Broker 查询消息未找到 39 | QueryNotFound = 22 40 | // Broker 订阅关系解析失败 41 | SubscriptionParseFailed = 23 42 | // Broker 订阅关系不存在 43 | SubscriptionNotExist = 24 44 | // Broker 订阅关系不是最新的 45 | SubscriptionNotLatest = 25 46 | // Broker 订阅组不存在 47 | SubscriptionGroupNotExist = 26 48 | // Producer 事务应该被提交 49 | TransactionShouldCommit = 200 50 | // Producer 事务应该被回滚 51 | TransactionShouldRollback = 201 52 | // Producer 事务状态未知 53 | TransactionStateUnknow = 202 54 | // Producer ProducerGroup错误 55 | TransactionStateGroupWrong = 203 56 | // 单元化消息,需要设置 buyerId 57 | NoBuyerId = 204 58 | // 单元化消息,非本单元消息 59 | NotInCurrentUint = 205 60 | // Consumer不在线 61 | ConsumerNotOnline = 206 62 | // Consumer消费消息超时 63 | ConsumeMsgTimeout = 207 64 | ) 65 | -------------------------------------------------------------------------------- /mix_all.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | RocketmqHomeEnv = "ROCKETMQ_HOME" 10 | RocketmqHomeProperty = "rocketmq.home.dir" 11 | NamesrvAddrEnv = "NAMESRV_ADDR" 12 | NamesrvAddrProperty = "rocketmq.namesrv.addr" 13 | MessageCompressLevel = "rocketmq.message.compressLevel" 14 | WsDomainName = "192.168.7.101" 15 | WsDomainSubgroup = "" 16 | WsAddr = "http://" + WsDomainName + ":8080/rocketmq/" + WsDomainSubgroup 17 | DefaultTopic = "TBW102" 18 | BenchmarkTopic = "BenchmarkTest" 19 | DefaultProducerGroup = "DEFAULT_PRODUCER" 20 | DefaultConsumerGroup = "DEFAULT_CONSUMER" 21 | ToolsConsumerGroup = "TOOLS_CONSUMER" 22 | FiltersrvConsumerGroup = "FILTERSRV_CONSUMER" 23 | MonitrorConsumerGroup = "__MONITOR_CONSUMER" 24 | ClientInnerProducerGroup = "CLIENT_INNER_PRODUCER" 25 | SelfTestProducerGroup = "SELF_TEST_P_GROUP" 26 | SelfTestConsumerGroup = "SELF_TEST_C_GROUP" 27 | SelfTestTopic = "SELF_TEST_TOPIC" 28 | OffsetMovedEvent = "OFFSET_MOVED_EVENT" 29 | OnsHttpProxyGroup = "CID_ONS-HTTP-PROXY" 30 | CidOnsapiPermissionGroup = "CID_ONSAPI_PERMISSION" 31 | CidOnsapiOwnerGroup = "CID_ONSAPI_OWNER" 32 | CidOnsapiPullGroup = "CID_ONSAPI_PULL" 33 | CidRmqSysPerfix = "CID_RMQ_SYS_" 34 | 35 | Localhost = "127.0.0.1" 36 | DefaultCharset = "UTF-8" 37 | MasterId = 0 38 | 39 | RetryGroupTopicPrefix = "%RETRY%" 40 | DlqGroupTopicPerfix = "%DLQ%" 41 | SysTopicPerfix = "rmq_sys_" 42 | UniqMsgQueryFlag = "_UNIQUE_KEY_QUERY" 43 | ) 44 | 45 | type MixAll struct{} 46 | 47 | func BrokerVIPChannel(isChange bool, brokerAddr string) (borkerAddrNew string) { 48 | borkerAddrNew = brokerAddr 49 | if isChange { 50 | ipAndPort := strings.Split(brokerAddr, ":") 51 | if port, err := strconv.Atoi(ipAndPort[1]); err == nil { 52 | borkerAddrNew = ipAndPort[0] + ":" + strconv.Itoa(port-2) 53 | } 54 | } 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /message_client_id_setter.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "os" 7 | "time" 8 | ) 9 | 10 | type messageClientIDSetter struct { 11 | counter int 12 | basePos int 13 | startTime int64 14 | nextStartTime int64 15 | stringBuilder *bytes.Buffer // ip + pid + classloaderid + counter + time 16 | buffer *bytes.Buffer 17 | } 18 | 19 | var stringBuilder = bytes.NewBuffer([]byte{}) 20 | 21 | func init() { 22 | binary.Write(stringBuilder, binary.BigEndian, GetLocalIp4()) // 4 23 | binary.Write(stringBuilder, binary.BigEndian, os.Getpid()) // 2 24 | binary.Write(stringBuilder, binary.BigEndian, hashCode()) // 4 25 | MessageClientIDSetter.stringBuilder = stringBuilder 26 | MessageClientIDSetter.setStartTime() 27 | } 28 | 29 | var MessageClientIDSetter = messageClientIDSetter{ 30 | stringBuilder: bytes.NewBuffer([]byte{}), // length := 4 + 2 + 4 + 4 + 2 31 | basePos: stringBuilder.Len() * 2, 32 | counter: 0, 33 | } 34 | 35 | func hashCode() []byte { 36 | tmpByte := []byte{1, 1, 1, 1} 37 | return tmpByte 38 | } 39 | 40 | func (m messageClientIDSetter) setUniqID(msg *Message) { 41 | if msg.Properties[MessageConst.PropertyUniqClientMessageIdKeyidx] == "" { 42 | msg.Properties[MessageConst.PropertyUniqClientMessageIdKeyidx] = m.createUniqID() 43 | } 44 | } 45 | 46 | func (m messageClientIDSetter) getUniqID(msg *Message) string { 47 | return msg.Properties[MessageConst.PropertyUniqClientMessageIdKeyidx] 48 | } 49 | 50 | func (m messageClientIDSetter) createUniqID() string { 51 | current := time.Now().UnixNano() 52 | if current > m.nextStartTime { 53 | m.setStartTime() 54 | } 55 | binary.Write(m.stringBuilder, binary.BigEndian, time.Now().UnixNano()-m.startTime) 56 | m.counter++ 57 | binary.Write(m.stringBuilder, binary.BigEndian, m.counter) 58 | 59 | return m.stringBuilder.String() 60 | } 61 | 62 | func (m messageClientIDSetter) setStartTime() { 63 | m.startTime = time.Now().UnixNano() 64 | m.nextStartTime = time.Now().UnixNano() + 2592000000000000 // next 30 days, 3600 * 24 * 30 * 1000 * 1000 *1000 65 | } 66 | -------------------------------------------------------------------------------- /producer_test.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | var producerGroup = "dev-goProducerConsumerTest" 9 | var topic = "goProducerConsumerTest" 10 | var conf = &Config{ 11 | Namesrv: "192.168.7.101:9876;192.168.7.102:9876;192.168.7.103:9876", 12 | ClientIp: "192.168.23.137", 13 | InstanceName: "DEFAULT_tt", 14 | } 15 | 16 | func TestSend(t *testing.T) { 17 | producer, err := NewDefaultProducer(producerGroup, conf) 18 | producer.Start() 19 | if err != nil { 20 | t.Fatalf("NewDefaultProducer err, %s", err) 21 | } 22 | for i := 0; i < 3; i++ { 23 | msg := NewMessage(topic, []byte("Hello RocketMQ "+strconv.Itoa(i))) 24 | if sendResult, err := producer.Send(msg); err != nil { 25 | t.Fatalf("Sync sending fail!, %s", err.Error()) 26 | } else { 27 | t.Log("sendResult", sendResult) 28 | t.Logf("Sync sending success, %d", i) 29 | //t.Logf("sendResult.sendStatus", sendResult.sendStatus) 30 | //t.Logf("sendResult.msgId", sendResult.msgId) 31 | //t.Logf("sendResult.messageQueue", sendResult.messageQueue) 32 | //t.Logf("sendResult.queueOffset", sendResult.queueOffset) 33 | //t.Logf("sendResult.transactionId", sendResult.transactionId) 34 | //t.Logf("sendResult.offsetMsgId", sendResult.offsetMsgId) 35 | //t.Logf("sendResult.regionId", sendResult.regionId) 36 | } 37 | } 38 | 39 | t.Log("Sync sending success!") 40 | } 41 | 42 | func TestSendOneway(t *testing.T) { 43 | producer, err := NewDefaultProducer(producerGroup, conf) 44 | producer.Start() 45 | if err != nil { 46 | t.Fatalf("NewDefaultProducer err, %s", err) 47 | } 48 | for i := 0; i < 3; i++ { 49 | msg := NewMessage(topic, []byte("Hello RocketMQ "+strconv.Itoa(i))) 50 | if err := producer.SendOneway(msg); err != nil { 51 | t.Fatalf("Oneway sending fail! %s", err.Error()) 52 | } else { 53 | t.Logf("Oneway sending success, %d", i) 54 | } 55 | } 56 | 57 | t.Log("Oneway sending success!") 58 | } 59 | 60 | func TestSendAsync(t *testing.T) { 61 | producer, err := NewDefaultProducer(producerGroup, conf) 62 | producer.Start() 63 | if err != nil { 64 | t.Fatalf("NewDefaultProducer err, %s", err) 65 | } 66 | for i := 0; i < 3; i++ { 67 | msg := NewMessage(topic, []byte("Hello RocketMQ "+strconv.Itoa(i))) 68 | sendCallback := func() error { 69 | t.Logf("I am callback") 70 | return nil 71 | } 72 | if err := producer.SendAsync(msg, sendCallback); err != nil { 73 | t.Fatalf("Async sending fail! %s", err.Error()) 74 | } else { 75 | t.Logf("Async sending success, %d", i) 76 | } 77 | } 78 | 79 | t.Log("Async sending success!") 80 | } 81 | -------------------------------------------------------------------------------- /remote_cmd.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/json" 7 | "log" 8 | "strconv" 9 | "sync" 10 | ) 11 | 12 | const ( 13 | RpcType = 0 14 | RpcOneway = 1 15 | ) 16 | 17 | var opaque int32 18 | var decodeLock sync.Mutex 19 | 20 | var ( 21 | remotingVersionKey string = "rocketmq.remoting.version" 22 | ConfigVersion int = -1 23 | requestId int32 = 0 24 | ) 25 | 26 | type RemotingCommand struct { 27 | // header 28 | Code int `json:"code"` 29 | Language string `json:"language"` 30 | Version int `json:"version"` 31 | Opaque int32 `json:"opaque"` 32 | Flag int `json:"flag"` 33 | remark string `json:"remark"` 34 | ExtFields interface{} `json:"extFields"` 35 | // body 36 | Body []byte `json:"body,omitempty"` 37 | } 38 | 39 | func (r *RemotingCommand) encodeHeader() []byte { 40 | length := 4 41 | headerData := r.buildHeader() 42 | length += len(headerData) 43 | 44 | if r.Body != nil { 45 | length += len(r.Body) 46 | } 47 | 48 | buf := bytes.NewBuffer([]byte{}) 49 | binary.Write(buf, binary.BigEndian, length) 50 | binary.Write(buf, binary.BigEndian, len(r.Body)) 51 | buf.Write(headerData) 52 | 53 | return buf.Bytes() 54 | } 55 | 56 | func (r *RemotingCommand) buildHeader() []byte { 57 | buf, err := json.Marshal(r) 58 | if err != nil { 59 | return nil 60 | } 61 | return buf 62 | } 63 | 64 | func (r *RemotingCommand) encode() []byte { 65 | length := 4 66 | 67 | headerData := r.buildHeader() 68 | length += len(headerData) 69 | 70 | if r.Body != nil { 71 | length += len(r.Body) 72 | } 73 | 74 | buf := bytes.NewBuffer([]byte{}) 75 | binary.Write(buf, binary.LittleEndian, length) 76 | binary.Write(buf, binary.LittleEndian, len(r.Body)) 77 | buf.Write(headerData) 78 | 79 | if r.Body != nil { 80 | buf.Write(r.Body) 81 | } 82 | 83 | return buf.Bytes() 84 | } 85 | 86 | func decodeRemoteCommand(header, body []byte) *RemotingCommand { 87 | decodeLock.Lock() 88 | defer decodeLock.Unlock() 89 | 90 | cmd := &RemotingCommand{} 91 | cmd.ExtFields = make(map[string]string) 92 | err := json.Unmarshal(header, cmd) 93 | if err != nil { 94 | log.Print(err) 95 | return nil 96 | } 97 | cmd.Body = body 98 | return cmd 99 | } 100 | 101 | func (r *RemotingCommand) decodeCommandCustomHeader() (responseHeader SendMessageResponseHeader) { 102 | msgId := r.ExtFields.(map[string]interface{})["msgId"].(string) 103 | queueId, _ := strconv.Atoi(r.ExtFields.(map[string]interface{})["queueId"].(string)) 104 | queueOffset, _ := strconv.Atoi(r.ExtFields.(map[string]interface{})["queueOffset"].(string)) 105 | responseHeader = SendMessageResponseHeader{ 106 | msgId: msgId, 107 | queueId: int32(queueId), 108 | queueOffset: int64(queueOffset), 109 | transactionId: "", 110 | } 111 | return 112 | } 113 | 114 | func (r *RemotingCommand) markOnewayRPC() { 115 | bits := 1 << RpcOneway 116 | r.Flag |= bits 117 | } 118 | -------------------------------------------------------------------------------- /client_test.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "strings" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | var ch = make(chan *RemotingCommand) 13 | var client = NewDefaultRemotingClient() 14 | 15 | func TestConnect(t *testing.T) { 16 | log.SetFlags(log.Lshortfile | log.LstdFlags) 17 | 18 | broker := "192.168.1.197:10911" 19 | namesrv := "192.168.1.234:9876" 20 | 21 | data, err := ioutil.ReadFile("request.txt") 22 | if err != nil { 23 | log.Print(err) 24 | } 25 | 26 | lines := strings.Split(string(data), "\n") 27 | 28 | var lastHeader, lastBody []byte 29 | for _, line := range lines { 30 | if strings.HasPrefix(line, "header=") { 31 | if lastHeader != nil { 32 | cmd := new(RemotingCommand) 33 | cmd.Body = lastBody 34 | 35 | err = json.Unmarshal(lastHeader, cmd) 36 | if err != nil { 37 | log.Print(err) 38 | return 39 | } 40 | callback := func(responseFuture *ResponseFuture) { 41 | } 42 | switch cmd.Code { 43 | case 101: 44 | getKvCallback := func(responseFuture *ResponseFuture) { 45 | jsonCmd, _ := json.Marshal(responseFuture.responseCommand) 46 | log.Printf("resp=%s", string(jsonCmd)) 47 | } 48 | err := client.invokeAsync(namesrv, cmd, 5000, getKvCallback) 49 | if err != nil { 50 | log.Print(err) 51 | } 52 | case 105: 53 | getRouteInfoCallback := func(responseFuture *ResponseFuture) { 54 | jsonCmd, _ := json.Marshal(responseFuture.responseCommand) 55 | 56 | log.Printf("resp=%s", string(jsonCmd)) 57 | log.Print(string(responseFuture.responseCommand.Body)) 58 | } 59 | err := client.invokeAsync(namesrv, cmd, 5000, getRouteInfoCallback) 60 | if err != nil { 61 | log.Print(err) 62 | } 63 | case 34: 64 | err := client.invokeAsync(broker, cmd, 5000, callback) 65 | if err != nil { 66 | log.Print(err) 67 | } 68 | case 38: 69 | log.Print("getConsumerListCallback") 70 | getConsumerListCallback := func(responseFuture *ResponseFuture) { 71 | jsonCmd, _ := json.Marshal(responseFuture.responseCommand) 72 | 73 | log.Printf("getConsumerListCallback=%s", string(jsonCmd)) 74 | log.Print(string(responseFuture.responseCommand.Body)) 75 | } 76 | log.Print(cmd) 77 | err := client.invokeAsync(broker, cmd, 5000, getConsumerListCallback) 78 | if err != nil { 79 | log.Print(err) 80 | } 81 | case 11: 82 | pullCallback := func(responseFuture *ResponseFuture) { 83 | //if responseFuture.responseCommand.Code == 0 && len(responseFuture.responseCommand.Body) > 0 { 84 | //msgs := decodeMessage(responseFuture.responseCommand.Body) 85 | //for _, msg := range msgs { 86 | //log.Print(string(msg.Body)) 87 | //} 88 | //} 89 | } 90 | err := client.invokeAsync(broker, cmd, 5000, pullCallback) 91 | if err != nil { 92 | log.Print(err) 93 | } 94 | } 95 | } 96 | } 97 | 98 | if strings.HasPrefix(line, "header=") { 99 | lastHeader = []byte(strings.TrimLeft(line, "header=")) 100 | } 101 | if strings.HasPrefix(line, "body=") { 102 | lastBody = []byte(strings.TrimLeft(line, "body=")) 103 | } 104 | } 105 | 106 | time.Sleep(1000 * time.Second) 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | A RocketMQ client for golang supportting producer and consumer. 3 | 4 | # Import package 5 | import "github.com/sevenNt/rocketmq" 6 | 7 | # Getting started 8 | ### Getting message with consumer 9 | ``` 10 | group := "dev-VodHotClacSrcData" 11 | topic := "canal_vod_collect__video_collected_count_live" 12 | var timeSleep = 30 * time.Second 13 | conf := &rocketmq.Config{ 14 | Nameserver: "192.168.7.101:9876;192.168.7.102:9876;192.168.7.103:9876", 15 | ClientIp: "192.168.1.23", 16 | InstanceName: "DEFAULT", 17 | } 18 | 19 | consumer, err := rocketmq.NewDefaultConsumer(consumerGroup, consumerConf) 20 | if err != nil { 21 | return err 22 | } 23 | consumer.Subscribe(consumerTopic, "*") 24 | consumer.RegisterMessageListener( 25 | func(msgs []*MessageExt) error { 26 | for i, msg := range msgs { 27 | fmt.Println("msg", i, msg.Topic, msg.Flag, msg.Properties, string(msg.Body)) 28 | } 29 | fmt.Println("Consume success!") 30 | return nil 31 | }) 32 | consumer.Start() 33 | 34 | time.Sleep(timeSleep) 35 | ``` 36 | 37 | ### Sending message with producer 38 | - Synchronous sending 39 | ``` 40 | group := "dev-VodHotClacSrcData" 41 | topic := "canal_vod_collect__video_collected_count_live" 42 | conf := &rocketmq.Config{ 43 | Nameserver: "192.168.7.101:9876;192.168.7.102:9876;192.168.7.103:9876", 44 | ClientIp: "192.168.1.23", 45 | InstanceName: "DEFAULT", 46 | } 47 | 48 | producer, err := rocketmq.NewDefaultProducer(group, conf) 49 | producer.Start() 50 | if err != nil { 51 | return errors.New("NewDefaultProducer err") 52 | } 53 | msg := NewMessage(topic, []byte("Hello RocketMQ!") 54 | if sendResult, err := producer.Send(msg); err != nil { 55 | return errors.New("Sync sending fail!") 56 | } else { 57 | fmt.Println("Sync sending success!, ", sendResult) 58 | } 59 | ``` 60 | 61 | - Asynchronous sending 62 | ``` 63 | group := "dev-VodHotClacSrcData" 64 | topic := "canal_vod_collect__video_collected_count_live" 65 | conf := &rocketmq.Config{ 66 | Nameserver: "192.168.7.101:9876;192.168.7.102:9876;192.168.7.103:9876", 67 | ClientIp: "192.168.1.23", 68 | InstanceName: "DEFAULT", 69 | } 70 | producer, err := rocketmq.NewDefaultProducer(group, conf) 71 | producer.Start() 72 | if err != nil { 73 | return err 74 | } 75 | msg := NewMessage(topic, []byte("Hello RocketMQ!") 76 | sendCallback := func() error { 77 | fmt.Println("I am callback") 78 | return nil 79 | } 80 | if err := producer.SendAsync(msg, sendCallback); err != nil { 81 | return err 82 | } else { 83 | fmt.Println("Async sending success!") 84 | } 85 | ``` 86 | 87 | - Oneway sending 88 | ``` 89 | group := "dev-VodHotClacSrcData" 90 | topic := "canal_vod_collect__video_collected_count_live" 91 | conf := &rocketmq.Config{ 92 | Nameserver: "192.168.7.101:9876;192.168.7.102:9876;192.168.7.103:9876", 93 | ClientIp: "192.168.1.23", 94 | InstanceName: "DEFAULT", 95 | } 96 | producer, err := rocketmq.NewDefaultProducer(group, conf) 97 | producer.Start() 98 | if err != nil { 99 | return err 100 | } 101 | msg := NewMessage(topic, []byte("Hello RocketMQ!") 102 | if err := producer.SendOneway(msg); err != nil { 103 | return err 104 | } else { 105 | fmt.Println("Oneway sending success!") 106 | } 107 | ``` -------------------------------------------------------------------------------- /message_const.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | type messageConst struct { 4 | PropertyKeys string 5 | PropertyTags string 6 | PropertyWaitStoreMsgOk string 7 | PropertyDelayTimeLevel string 8 | PropertyRetryTopic string 9 | PropertyRealTopic string 10 | PropertyRealQueueId string 11 | PropertyTransactionPrepared string 12 | PropertyProducerGroup string 13 | PropertyMinOffset string 14 | PropertyMaxOffset string 15 | PropertyBuyerId string 16 | PropertyOriginMessageId string 17 | PropertyTransferFlag string 18 | PropertyCorrectionFlag string 19 | PropertyMq2Flag string 20 | PropertyReconsumeTime string 21 | PropertyMsgRegion string 22 | PropertyUniqClientMessageIdKeyidx string 23 | PropertyMaxReconsumeTimes string 24 | PropertyConsumeStartTimeStamp string 25 | 26 | KeySeparator string 27 | systemKeySet []string 28 | } 29 | 30 | var MessageConst = &messageConst{ 31 | PropertyKeys: "KEYS", 32 | PropertyTags: "TAGS", 33 | PropertyWaitStoreMsgOk: "WAIT", 34 | PropertyDelayTimeLevel: "DELAY", 35 | PropertyRetryTopic: "RETRY_TOPIC", 36 | PropertyRealTopic: "REAL_TOPIC", 37 | PropertyRealQueueId: "REAL_QID", 38 | PropertyTransactionPrepared: "TRAN_MSG", 39 | PropertyProducerGroup: "PGROUP", 40 | PropertyMinOffset: "MIN_OFFSET", 41 | PropertyMaxOffset: "MAX_OFFSET", 42 | PropertyBuyerId: "BUYER_ID", 43 | PropertyOriginMessageId: "ORIGIN_MESSAGE_ID", 44 | PropertyTransferFlag: "TRANSFER_FLAG", 45 | PropertyCorrectionFlag: "CORRECTION_FLAG", 46 | PropertyMq2Flag: "MQ2_FLAG", 47 | PropertyReconsumeTime: "RECONSUME_TIME", 48 | PropertyMsgRegion: "MSG_REGION", 49 | PropertyUniqClientMessageIdKeyidx: "UNIQ_KEY", 50 | PropertyMaxReconsumeTimes: "MAX_RECONSUME_TIMES", 51 | PropertyConsumeStartTimeStamp: "CONSUME_START_TIME", 52 | 53 | KeySeparator: "", 54 | } 55 | 56 | func init() { 57 | var systemKeySet = make([]string, 0) 58 | systemKeySet = append(systemKeySet, MessageConst.PropertyKeys) 59 | systemKeySet = append(systemKeySet, MessageConst.PropertyTags) 60 | systemKeySet = append(systemKeySet, MessageConst.PropertyWaitStoreMsgOk) 61 | systemKeySet = append(systemKeySet, MessageConst.PropertyDelayTimeLevel) 62 | systemKeySet = append(systemKeySet, MessageConst.PropertyRetryTopic) 63 | systemKeySet = append(systemKeySet, MessageConst.PropertyRealTopic) 64 | systemKeySet = append(systemKeySet, MessageConst.PropertyRealQueueId) 65 | systemKeySet = append(systemKeySet, MessageConst.PropertyTransactionPrepared) 66 | systemKeySet = append(systemKeySet, MessageConst.PropertyProducerGroup) 67 | systemKeySet = append(systemKeySet, MessageConst.PropertyMinOffset) 68 | systemKeySet = append(systemKeySet, MessageConst.PropertyMaxOffset) 69 | systemKeySet = append(systemKeySet, MessageConst.PropertyBuyerId) 70 | systemKeySet = append(systemKeySet, MessageConst.PropertyOriginMessageId) 71 | systemKeySet = append(systemKeySet, MessageConst.PropertyTransferFlag) 72 | systemKeySet = append(systemKeySet, MessageConst.PropertyCorrectionFlag) 73 | systemKeySet = append(systemKeySet, MessageConst.PropertyMq2Flag) 74 | systemKeySet = append(systemKeySet, MessageConst.PropertyReconsumeTime) 75 | systemKeySet = append(systemKeySet, MessageConst.PropertyMsgRegion) 76 | systemKeySet = append(systemKeySet, MessageConst.PropertyUniqClientMessageIdKeyidx) 77 | systemKeySet = append(systemKeySet, MessageConst.PropertyMaxReconsumeTimes) 78 | systemKeySet = append(systemKeySet, MessageConst.PropertyConsumeStartTimeStamp) 79 | 80 | MessageConst.systemKeySet = systemKeySet 81 | } 82 | -------------------------------------------------------------------------------- /store.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | "sync/atomic" 8 | ) 9 | 10 | const ( 11 | MemoryFirstThenStore = 0 12 | ReadFromMemory = 1 13 | ReadFromStore = 2 14 | ) 15 | 16 | type OffsetStore interface { 17 | //load() error 18 | updateOffset(mq *MessageQueue, offset int64, increaseOnly bool) 19 | readOffset(mq *MessageQueue, flag int) int64 20 | //persistAll(mqs []MessageQueue) 21 | //persist(mq MessageQueue) 22 | //removeOffset(mq MessageQueue) 23 | //cloneOffsetTable(topic string) map[MessageQueue]int64 24 | } 25 | 26 | type RemoteOffsetStore struct { 27 | groupName string 28 | mqClient *MqClient 29 | offsetTable map[MessageQueue]int64 30 | offsetTableLock sync.RWMutex 31 | } 32 | 33 | func (r *RemoteOffsetStore) readOffset(mq *MessageQueue, readType int) int64 { 34 | switch readType { 35 | case MemoryFirstThenStore: 36 | case ReadFromMemory: 37 | r.offsetTableLock.RLock() 38 | offset, ok := r.offsetTable[*mq] 39 | r.offsetTableLock.RUnlock() 40 | if ok { 41 | return offset 42 | } else if readType == ReadFromMemory { 43 | return -1 44 | } 45 | case ReadFromStore: 46 | offset, err := r.fetchConsumeOffsetFromBroker(mq) 47 | if err != nil { 48 | fmt.Println(err) 49 | return -1 50 | } 51 | r.updateOffset(mq, offset, false) 52 | return offset 53 | } 54 | 55 | return -1 56 | 57 | } 58 | 59 | func (r *RemoteOffsetStore) fetchConsumeOffsetFromBroker(mq *MessageQueue) (int64, error) { 60 | brokerAddr, _, found := r.mqClient.findBrokerAddressInSubscribe(mq.brokerName, 0, false) 61 | 62 | if !found { 63 | if _, err := r.mqClient.updateTopicRouteInfoFromNameServerKernel(mq.topic, false, DefaultProducer{}); err != nil { 64 | return 0, err 65 | } 66 | brokerAddr, _, found = r.mqClient.findBrokerAddressInSubscribe(mq.brokerName, 0, false) 67 | } 68 | 69 | if found { 70 | requestHeader := &QueryConsumerOffsetRequestHeader{} 71 | requestHeader.Topic = mq.topic 72 | requestHeader.QueueId = mq.queueId 73 | requestHeader.ConsumerGroup = r.groupName 74 | return r.mqClient.queryConsumerOffset(brokerAddr, requestHeader, 3000) 75 | } 76 | 77 | return 0, errors.New("fetch consumer offset error") 78 | } 79 | 80 | func (r *RemoteOffsetStore) persist(mq *MessageQueue) { 81 | offset, ok := r.offsetTable[*mq] 82 | if ok { 83 | err := r.updateConsumeOffsetToBroker(mq, offset) 84 | if err != nil { 85 | fmt.Println(err) 86 | } 87 | } 88 | } 89 | 90 | type UpdateConsumerOffsetRequestHeader struct { 91 | consumerGroup string 92 | topic string 93 | queueId int32 94 | commitOffset int64 95 | } 96 | 97 | func (r *RemoteOffsetStore) updateConsumeOffsetToBroker(mq *MessageQueue, offset int64) error { 98 | addr, found, _ := r.mqClient.findBrokerAddressInSubscribe(mq.brokerName, 0, false) 99 | if !found { 100 | if _, err := r.mqClient.updateTopicRouteInfoFromNameServerKernel(mq.topic, false, DefaultProducer{}); err != nil { 101 | return err 102 | } 103 | addr, found, _ = r.mqClient.findBrokerAddressInSubscribe(mq.brokerName, 0, false) 104 | } 105 | 106 | if found { 107 | requestHeader := &UpdateConsumerOffsetRequestHeader{ 108 | consumerGroup: r.groupName, 109 | topic: mq.topic, 110 | queueId: mq.queueId, 111 | commitOffset: offset, 112 | } 113 | 114 | r.mqClient.updateConsumerOffsetOneway(addr, requestHeader, 5*1000) 115 | return nil 116 | } 117 | return errors.New("not found broker") 118 | } 119 | 120 | func (r *RemoteOffsetStore) updateOffset(mq *MessageQueue, offset int64, increaseOnly bool) { 121 | if mq != nil { 122 | r.offsetTableLock.RLock() 123 | offsetOld, ok := r.offsetTable[*mq] 124 | r.offsetTableLock.RUnlock() 125 | if !ok { 126 | r.offsetTableLock.Lock() 127 | r.offsetTable[*mq] = offset 128 | r.offsetTableLock.Unlock() 129 | } else { 130 | if increaseOnly { 131 | atomic.AddInt64(&offsetOld, offset) 132 | r.offsetTableLock.Lock() 133 | r.offsetTable[*mq] = offsetOld 134 | r.offsetTableLock.Unlock() 135 | } else { 136 | r.offsetTableLock.Lock() 137 | r.offsetTable[*mq] = offset 138 | r.offsetTableLock.Unlock() 139 | } 140 | } 141 | 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /request_code.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | const ( 4 | // Broker 发送消息 5 | SendMsg = 10 6 | // Broker 订阅消息 7 | PullMsg = 11 8 | // Broker 查询消息 9 | QueryMESSAGE = 12 10 | // Broker 查询Broker Offset 11 | QueryBrokerOffset = 13 12 | // Broker 查询Consumer Offset 13 | QueryConsumerOffset = 14 14 | // Broker 更新Consumer Offset 15 | UpdateCconsumerOffset = 15 16 | // Broker 更新或者增加一个Topic 17 | UpdateAndCreateTopic = 17 18 | // Broker 获取所有Topic的配置(Slave和Namesrv都会向Master请求此配置) 19 | GetAllTopicConfig = 21 20 | // Broker 获取所有Topic配置(Slave和Namesrv都会向Master请求此配置) 21 | GetTopicConfigList = 22 22 | // Broker 获取所有Topic名称列表 23 | GetTopicNameList = 23 24 | // Broker 更新Broker上的配置 25 | UpdateBrokerConfig = 25 26 | // Broker 获取Broker上的配置 27 | GetBrokerConfig = 26 28 | // Broker 触发Broker删除文件 29 | TriggerDeleteFILES = 27 30 | // Broker 获取Broker运行时信息 31 | GetBrokerRuntimeInfo = 28 32 | // Broker 根据时间查询队列的Offset 33 | SearchOffsetByTimeStamp = 29 34 | // Broker 查询队列最大Offset 35 | GetMaxOffset = 30 36 | // Broker 查询队列最小Offset 37 | GetMinOffset = 31 38 | // Broker 查询队列最早消息对应时间 39 | GetEarliestMsgStoreTime = 32 40 | // Broker 根据消息ID来查询消息 41 | ViewMsgById = 33 42 | // Broker Client向Client发送心跳,并注册自身 43 | HeartBeat = 34 44 | // Broker Client注销 45 | UnregisterClient = 35 46 | // Broker Consumer将处理不了的消息发回服务器 47 | CconsumerSendMsgBack = 36 48 | // Broker Commit或者Rollback事务 49 | EndTransaction = 37 50 | // Broker 获取ConsumerId列表通过GroupName 51 | GetConsumerListByGroup = 38 52 | // Broker 主动向Producer回查事务状态 53 | CheckTransactionState = 39 54 | // Broker Broker通知Consumer列表变化 55 | NotifyConsumerIdsChanged = 40 56 | // Broker Consumer向Master锁定队列 57 | LockBatchMq = 41 58 | // Broker Consumer向Master解锁队列 59 | UNLockBatchMq = 42 60 | // Broker 获取所有Consumer Offset 61 | GetAllCconsumerOffset = 43 62 | // Broker 获取所有定时进度 63 | GetAllDelayOffset = 45 64 | // Namesrv 向Namesrv追加KV配置 65 | PutKVConfig = 100 66 | // Namesrv 从Namesrv获取KV配置 67 | GetKVConfig = 101 68 | // Namesrv 从Namesrv获取KV配置 69 | DeleteKVConfig = 102 70 | // Namesrv 注册一个Broker,数据都是持久化的,如果存在则覆盖配置 71 | RegisterBroker = 103 72 | // Namesrv 卸载一个Broker,数据都是持久化的 73 | UnregisterBroker = 104 74 | // Namesrv 根据Topic获取Broker Name、队列数(包含读队列与写队列) 75 | GetRouteinfoByTopic = 105 76 | // Namesrv 获取注册到Name Server的所有Broker集群信息 77 | GetBrokerClusterInfo = 106 78 | UpdateAndCreateSubscriptionGroup = 200 79 | GetAllSubscriptionGroupConfig = 201 80 | GetTopicStatsInfo = 202 81 | GetConsumerConnList = 203 82 | GetProducerConnList = 204 83 | WipeWritePermOfBroker = 205 84 | 85 | // 从Name Server获取完整Topic列表 86 | GetAllTopicListFromNamesrv = 206 87 | // 从Broker删除订阅组 88 | DeleteSubscriptionGroup = 207 89 | // 从Broker获取消费状态(进度) 90 | GetConsumeStats = 208 91 | // Suspend Consumer消费过程 92 | SuspendConsumer = 209 93 | // Resume Consumer消费过程 94 | ResumeConsumer = 210 95 | // 重置Consumer Offset 96 | ResetCconsumerOffsetInConsumer = 211 97 | // 重置Consumer Offset 98 | ResetCconsumerOffsetInBroker = 212 99 | // 调整Consumer线程池数量 100 | AdjustCconsumerThreadPoolPOOL = 213 101 | // 查询消息被哪些消费组消费 102 | WhoConsumeTHE_MESSAGE = 214 103 | 104 | // 从Broker删除Topic配置 105 | DeleteTopicInBroker = 215 106 | // 从Namesrv删除Topic配置 107 | DeleteTopicInNamesrv = 216 108 | // Namesrv 通过 project 获取所有的 server ip 信息 109 | GetKvConfigByValue = 217 110 | // Namesrv 删除指定 project group 下的所有 server ip 信息 111 | DeleteKvConfigByValue = 218 112 | // 通过NameSpace获取所有的KV List 113 | GetKvlistByNamespace = 219 114 | 115 | // offset 重置 116 | ResetCconsumerClientOffset = 220 117 | // 客户端订阅消息 118 | GetCconsumerStatusFromClient = 221 119 | // 通知 broker 调用 offset 重置处理 120 | InvokeBrokerToResetOffset = 222 121 | // 通知 broker 调用客户端订阅消息处理 122 | InvokeBrokerToGetCconsumerSTATUS = 223 123 | 124 | // Broker 查询topic被谁消费 125 | // 2014-03-21 Add By shijia 126 | QueryTopicConsumeByWho = 300 127 | 128 | // 获取指定集群下的所有 topic 129 | // 2014-03-26 130 | GetTopicsByCluster = 224 131 | 132 | // 向Broker注册Filter Server 133 | // 2014-04-06 Add By shijia 134 | RegisterFilterServer = 301 135 | // 向Filter Server注册Class 136 | // 2014-04-06 Add By shijia 137 | RegisterMsgFilterClass = 302 138 | // 根据 topic 和 group 获取消息的时间跨度 139 | QueryConsumeTimeSpan = 303 140 | // 获取所有系统内置 Topic 列表 141 | GetSysTopicListFromNS = 304 142 | GetSysTopicListFromBroker = 305 143 | 144 | // 清理失效队列 145 | CleanExpiredConsumequeue = 306 146 | 147 | // 通过Broker查询Consumer内存数据 148 | // 2014-07-19 Add By shijia 149 | GetCconsumerRunningInfo = 307 150 | 151 | // 查找被修正 offset (转发组件) 152 | QueryCorrectionOffset = 308 153 | 154 | // 通过Broker直接向某个Consumer发送一条消息,并立刻消费,返回结果给broker,再返回给调用方 155 | // 2014-08-11 Add By shijia 156 | ConsumeMsgDirectly = 309 157 | 158 | // Broker 发送消息,优化网络数据包 159 | SendMsgV2 = 310 160 | 161 | // 单元化相关 topic 162 | GetUnitTopicList = 311 163 | // 获取含有单元化订阅组的 Topic 列表 164 | GetHasUnitSubTopicList = 312 165 | // 获取含有单元化订阅组的非单元化 Topic 列表 166 | GetHasUnitSubUnunitTopicList = 313 167 | // 克隆某一个组的消费进度到新的组 168 | CloneGroupOffset = 314 169 | 170 | // 查看Broker上的各种统计信息 171 | ViewBrokerStatsData = 315 172 | ) 173 | -------------------------------------------------------------------------------- /rebalance.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sort" 7 | "sync" 8 | ) 9 | 10 | type SubscriptionData struct { 11 | Topic string 12 | SubString string 13 | ClassFilterMode bool 14 | TagsSet []string 15 | CodeSet []string 16 | SubVersion int64 17 | } 18 | type Rebalance struct { 19 | groupName string 20 | messageModel string 21 | topicSubscribeInfoTable map[string][]*MessageQueue 22 | topicSubscribeInfoTableLock sync.RWMutex 23 | subscriptionInner map[string]*SubscriptionData 24 | subscriptionInnerLock sync.RWMutex 25 | mqClient *MqClient 26 | allocateMessageQueueStrategy AllocateMessageQueueStrategy 27 | consumer *DefaultConsumer 28 | producer *DefaultProducer 29 | processQueueTable map[MessageQueue]int32 30 | processQueueTableLock sync.RWMutex 31 | mutex sync.Mutex 32 | } 33 | 34 | func NewRebalance() *Rebalance { 35 | return &Rebalance{ 36 | topicSubscribeInfoTable: make(map[string][]*MessageQueue), 37 | subscriptionInner: make(map[string]*SubscriptionData), 38 | allocateMessageQueueStrategy: new(AllocateMessageQueueAveragely), 39 | messageModel: "CLUSTERING", 40 | processQueueTable: make(map[MessageQueue]int32), 41 | } 42 | } 43 | 44 | func (r *Rebalance) doRebalance() { 45 | r.mutex.Lock() 46 | defer r.mutex.Unlock() 47 | for topic, _ := range r.subscriptionInner { 48 | r.rebalanceByTopic(topic) 49 | } 50 | } 51 | 52 | type ConsumerIdSorter []string 53 | 54 | func (r ConsumerIdSorter) Len() int { return len(r) } 55 | func (r ConsumerIdSorter) Swap(i, j int) { r[i], r[j] = r[j], r[i] } 56 | func (r ConsumerIdSorter) Less(i, j int) bool { 57 | if r[i] < r[j] { 58 | return true 59 | } 60 | return false 61 | } 62 | 63 | type AllocateMessageQueueStrategy interface { 64 | allocate(consumerGroup string, currentCID string, mqAll []*MessageQueue, cidAll []string) ([]*MessageQueue, error) 65 | } 66 | type AllocateMessageQueueAveragely struct{} 67 | 68 | func (r *AllocateMessageQueueAveragely) allocate(consumerGroup string, currentCID string, mqAll []*MessageQueue, cidAll []string) ([]*MessageQueue, error) { 69 | if currentCID == "" { 70 | return nil, errors.New("currentCID is empty") 71 | } 72 | 73 | if mqAll == nil || len(mqAll) == 0 { 74 | return nil, errors.New("mqAll is nil or mqAll empty") 75 | } 76 | 77 | if cidAll == nil || len(cidAll) == 0 { 78 | return nil, errors.New("cidAll is nil or cidAll empty") 79 | } 80 | 81 | result := make([]*MessageQueue, 0) 82 | for i, cid := range cidAll { 83 | if cid == currentCID { 84 | mqLen := len(mqAll) 85 | cidLen := len(cidAll) 86 | mod := mqLen % cidLen 87 | var averageSize int 88 | if mqLen < cidLen { 89 | averageSize = 1 90 | } else { 91 | if mod > 0 && i < mod { 92 | averageSize = mqLen/cidLen + 1 93 | } else { 94 | averageSize = mqLen / cidLen 95 | } 96 | } 97 | 98 | var startIndex int 99 | if mod > 0 && i < mod { 100 | startIndex = i * averageSize 101 | } else { 102 | startIndex = i*averageSize + mod 103 | } 104 | 105 | var min int 106 | if averageSize > mqLen-startIndex { 107 | min = mqLen - startIndex 108 | } else { 109 | min = averageSize 110 | } 111 | 112 | for j := 0; j < min; j++ { 113 | result = append(result, mqAll[(startIndex+j)%mqLen]) 114 | } 115 | return result, nil 116 | 117 | } 118 | } 119 | 120 | return nil, errors.New("cant't find currentCID") 121 | } 122 | 123 | func (r *Rebalance) rebalanceByTopic(topic string) error { 124 | cidAll, err := r.mqClient.findConsumerIdList(topic, r.groupName) 125 | if err != nil { 126 | fmt.Println(err) 127 | return err 128 | } 129 | 130 | r.topicSubscribeInfoTableLock.RLock() 131 | mqs, ok := r.topicSubscribeInfoTable[topic] 132 | r.topicSubscribeInfoTableLock.RUnlock() 133 | if ok && len(mqs) > 0 && len(cidAll) > 0 { 134 | var messageQueues MessageQueues = mqs 135 | var consumerIdSorter ConsumerIdSorter = cidAll 136 | 137 | sort.Sort(messageQueues) 138 | sort.Sort(consumerIdSorter) 139 | } 140 | 141 | allocateResult, err := r.allocateMessageQueueStrategy.allocate(r.groupName, r.mqClient.clientId, mqs, cidAll) 142 | 143 | if err != nil { 144 | fmt.Println(err) 145 | return err 146 | } 147 | 148 | fmt.Printf("rebalance topic[%s]\n", topic) 149 | r.updateProcessQueueTableInRebalance(topic, allocateResult) 150 | return nil 151 | } 152 | 153 | func (r *Rebalance) updateProcessQueueTableInRebalance(topic string, mqSet []*MessageQueue) { 154 | for _, mq := range mqSet { 155 | r.processQueueTableLock.RLock() 156 | _, ok := r.processQueueTable[*mq] 157 | r.processQueueTableLock.RUnlock() 158 | if !ok { 159 | pullRequest := new(PullRequest) 160 | pullRequest.consumerGroup = r.groupName 161 | pullRequest.messageQueue = mq 162 | pullRequest.nextOffset = r.computePullFromWhere(mq) 163 | r.mqClient.pullMessageService.pullRequestQueue <- pullRequest 164 | r.processQueueTableLock.Lock() 165 | r.processQueueTable[*mq] = 1 166 | r.processQueueTableLock.Unlock() 167 | } 168 | } 169 | 170 | } 171 | 172 | func (r *Rebalance) computePullFromWhere(mq *MessageQueue) int64 { 173 | var result int64 = -1 174 | lastOffset := r.consumer.offsetStore.readOffset(mq, ReadFromStore) 175 | 176 | if lastOffset >= 0 { 177 | result = lastOffset 178 | } else { 179 | result = 0 180 | } 181 | return result 182 | } 183 | -------------------------------------------------------------------------------- /message.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "bytes" 5 | "compress/zlib" 6 | "encoding/binary" 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "io/ioutil" 11 | "strconv" 12 | ) 13 | 14 | const ( 15 | CompressedFlag = (0x1 << 0) 16 | MultiTagsFlag = (0x1 << 1) 17 | TransactionNotType = (0x0 << 2) 18 | TransactionPreparedType = (0x1 << 2) 19 | TransactionCommitType = (0x2 << 2) 20 | TransactionRollbackType = (0x3 << 2) 21 | ) 22 | 23 | const ( 24 | NameValueSeparator = 1 + iota 25 | PropertySeparator 26 | ) 27 | 28 | const ( 29 | CharacterMaxLength = 255 30 | ) 31 | 32 | type Message struct { 33 | Topic string 34 | Flag int32 35 | Properties map[string]string 36 | Body []byte 37 | } 38 | 39 | func NewMessage(topic string, body []byte) *Message { 40 | return &Message{ 41 | Topic: topic, 42 | Body: body, 43 | Properties: make(map[string]string), 44 | } 45 | } 46 | 47 | type MessageExt struct { 48 | Message 49 | QueueId int32 50 | StoreSize int32 51 | QueueOffset int64 52 | SysFlag int32 53 | BornTimestamp int64 54 | // bornHost 55 | StoreTimestamp int64 56 | // storeHost 57 | MsgId string 58 | CommitLogOffset int64 59 | BodyCRC int32 60 | ReconsumeTimes int32 61 | PreparedTransactionOffset int64 62 | } 63 | 64 | func decodeMessage(data []byte) []*MessageExt { 65 | buf := bytes.NewBuffer(data) 66 | var storeSize, magicCode, bodyCRC, queueId, flag, sysFlag, reconsumeTimes, bodyLength, bornPort, storePort int32 67 | var queueOffset, physicOffset, preparedTransactionOffset, bornTimeStamp, storeTimestamp int64 68 | var topicLen byte 69 | var topic, body, properties, bornHost, storeHost []byte 70 | var propertiesLength int16 71 | 72 | var propertiesMap map[string]string 73 | 74 | msgs := make([]*MessageExt, 0, 32) 75 | for buf.Len() > 0 { 76 | msg := new(MessageExt) 77 | binary.Read(buf, binary.BigEndian, &storeSize) 78 | binary.Read(buf, binary.BigEndian, &magicCode) 79 | binary.Read(buf, binary.BigEndian, &bodyCRC) 80 | binary.Read(buf, binary.BigEndian, &queueId) 81 | binary.Read(buf, binary.BigEndian, &flag) 82 | binary.Read(buf, binary.BigEndian, &queueOffset) 83 | binary.Read(buf, binary.BigEndian, &physicOffset) 84 | binary.Read(buf, binary.BigEndian, &sysFlag) 85 | binary.Read(buf, binary.BigEndian, &bornTimeStamp) 86 | bornHost = make([]byte, 4) 87 | binary.Read(buf, binary.BigEndian, &bornHost) 88 | binary.Read(buf, binary.BigEndian, &bornPort) 89 | binary.Read(buf, binary.BigEndian, &storeTimestamp) 90 | storeHost = make([]byte, 4) 91 | binary.Read(buf, binary.BigEndian, &storeHost) 92 | binary.Read(buf, binary.BigEndian, &storePort) 93 | binary.Read(buf, binary.BigEndian, &reconsumeTimes) 94 | binary.Read(buf, binary.BigEndian, &preparedTransactionOffset) 95 | binary.Read(buf, binary.BigEndian, &bodyLength) 96 | if bodyLength > 0 { 97 | body = make([]byte, bodyLength) 98 | binary.Read(buf, binary.BigEndian, body) 99 | 100 | if (sysFlag & CompressedFlag) == CompressedFlag { 101 | b := bytes.NewReader(body) 102 | z, err := zlib.NewReader(b) 103 | if err != nil { 104 | fmt.Println(err) 105 | return nil 106 | } 107 | defer z.Close() 108 | body, err = ioutil.ReadAll(z) 109 | if err != nil { 110 | fmt.Println(err) 111 | return nil 112 | } 113 | } 114 | 115 | } 116 | binary.Read(buf, binary.BigEndian, &topicLen) 117 | topic = make([]byte, 0) 118 | binary.Read(buf, binary.BigEndian, &topic) 119 | binary.Read(buf, binary.BigEndian, &propertiesLength) 120 | if propertiesLength > 0 { 121 | properties = make([]byte, propertiesLength) 122 | binary.Read(buf, binary.BigEndian, &properties) 123 | propertiesMap = make(map[string]string) 124 | json.Unmarshal(properties, &propertiesMap) 125 | } 126 | 127 | if magicCode != -626843481 { 128 | fmt.Printf("magic code is error %d", magicCode) 129 | return nil 130 | } 131 | 132 | msg.Topic = string(topic) 133 | msg.QueueId = queueId 134 | msg.SysFlag = sysFlag 135 | msg.QueueOffset = queueOffset 136 | msg.BodyCRC = bodyCRC 137 | msg.StoreSize = storeSize 138 | msg.BornTimestamp = bornTimeStamp 139 | msg.ReconsumeTimes = reconsumeTimes 140 | msg.Flag = flag 141 | //msg.commitLogOffset=physicOffset 142 | msg.StoreTimestamp = storeTimestamp 143 | msg.PreparedTransactionOffset = preparedTransactionOffset 144 | msg.Body = body 145 | msg.Properties = propertiesMap 146 | 147 | msgs = append(msgs, msg) 148 | } 149 | 150 | return msgs 151 | } 152 | 153 | func messageProperties2String(properties map[string]string) string { 154 | StringBuilder := bytes.NewBuffer([]byte{}) 155 | if properties != nil && len(properties) != 0 { 156 | for k, v := range properties { 157 | binary.Write(StringBuilder, binary.BigEndian, k) // 4 158 | binary.Write(StringBuilder, binary.BigEndian, NameValueSeparator) // 4 159 | binary.Write(StringBuilder, binary.BigEndian, v) // 4 160 | binary.Write(StringBuilder, binary.BigEndian, PropertySeparator) // 4 161 | } 162 | } 163 | return StringBuilder.String() 164 | } 165 | 166 | func (msg Message) checkMessage(producer *DefaultProducer) (err error) { 167 | if err = checkTopic(msg.Topic); err != nil { 168 | if len(msg.Body) == 0 { 169 | err = errors.New("ResponseCode:" + strconv.Itoa(MsgIllegal) + ", the message body is null") 170 | } else if len(msg.Body) > producer.maxMessageSize { 171 | err = errors.New("ResponseCode:" + strconv.Itoa(MsgIllegal) + ", the message body size over max value, MAX:" + strconv.Itoa(producer.maxMessageSize)) 172 | } 173 | } 174 | return 175 | } 176 | 177 | func checkTopic(topic string) (err error) { 178 | if topic == "" { 179 | err = errors.New("the specified topic is blank") 180 | } 181 | if len(topic) > CharacterMaxLength { 182 | err = errors.New("the specified topic is longer than topic max length 255") 183 | } 184 | if topic == DefaultTopic { 185 | err = errors.New("the topic[" + topic + "] is conflict with default topic") 186 | } 187 | return 188 | } 189 | -------------------------------------------------------------------------------- /consumer.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strconv" 8 | "sync/atomic" 9 | "time" 10 | ) 11 | 12 | const ( 13 | BrokerSuspendMaxTimeMillis = 1000 * 15 14 | FlagCommitOffset int32 = 0x1 << 0 15 | FlagSuspend int32 = 0x1 << 1 16 | FlagSubscription int32 = 0x1 << 2 17 | FlagClassFilter int32 = 0x1 << 3 18 | ) 19 | 20 | type MessageListener func(msgs []*MessageExt) error 21 | 22 | var DefaultIp = GetLocalIp4() 23 | 24 | type Config struct { 25 | Namesrv string 26 | ClientIp string 27 | InstanceName string 28 | } 29 | 30 | type Consumer interface { 31 | Start() error 32 | Shutdown() 33 | RegisterMessageListener(listener MessageListener) 34 | Subscribe(topic string, subExpression string) 35 | UnSubscribe(topic string) 36 | SendMessageBack(msg MessageExt, delayLevel int) error 37 | SendMessageBack1(msg MessageExt, delayLevel int, brokerName string) error 38 | fetchSubscribeMessageQueues(topic string) error 39 | } 40 | 41 | type DefaultConsumer struct { 42 | conf *Config 43 | consumerGroup string 44 | consumeFromWhere string 45 | consumerType string 46 | messageModel string 47 | unitMode bool 48 | subscription map[string]string 49 | messageListener MessageListener 50 | offsetStore OffsetStore 51 | brokers map[string]net.Conn 52 | rebalance *Rebalance 53 | remotingClient RemotingClient 54 | mqClient *MqClient 55 | } 56 | 57 | func NewDefaultConsumer(consumerGroup string, conf *Config) (Consumer, error) { 58 | if conf == nil { 59 | conf = &Config{ 60 | Namesrv: os.Getenv("ROCKETMQ_NAMESVR"), 61 | InstanceName: "DEFAULT", 62 | } 63 | } 64 | 65 | if conf.ClientIp == "" { 66 | conf.ClientIp = DefaultIp 67 | } 68 | 69 | remotingClient := NewDefaultRemotingClient() 70 | mqClient := NewMqClient() 71 | 72 | rebalance := NewRebalance() 73 | rebalance.groupName = consumerGroup 74 | rebalance.mqClient = mqClient 75 | 76 | offsetStore := new(RemoteOffsetStore) 77 | offsetStore.mqClient = mqClient 78 | offsetStore.groupName = consumerGroup 79 | offsetStore.offsetTable = make(map[MessageQueue]int64) 80 | 81 | pullMessageService := NewPullMessageService() 82 | 83 | consumer := &DefaultConsumer{ 84 | conf: conf, 85 | consumerGroup: consumerGroup, 86 | consumeFromWhere: "CONSUME_FROM_LAST_OFFSET", 87 | subscription: make(map[string]string), 88 | offsetStore: offsetStore, 89 | brokers: make(map[string]net.Conn), 90 | rebalance: rebalance, 91 | remotingClient: remotingClient, 92 | mqClient: mqClient, 93 | } 94 | 95 | mqClient.consumerTable[consumerGroup] = consumer 96 | mqClient.remotingClient = remotingClient 97 | mqClient.conf = conf 98 | mqClient.clientId = conf.ClientIp + "@" + strconv.Itoa(os.Getpid()) 99 | mqClient.pullMessageService = pullMessageService 100 | 101 | rebalance.consumer = consumer 102 | pullMessageService.service = consumer 103 | 104 | return consumer, nil 105 | } 106 | 107 | func (c *DefaultConsumer) Start() error { 108 | c.mqClient.start() 109 | return nil 110 | } 111 | 112 | func (c *DefaultConsumer) Shutdown() { 113 | } 114 | 115 | func (c *DefaultConsumer) RegisterMessageListener(messageListener MessageListener) { 116 | c.messageListener = messageListener 117 | } 118 | 119 | func (c *DefaultConsumer) Subscribe(topic string, subExpression string) { 120 | c.subscription[topic] = subExpression 121 | 122 | subData := &SubscriptionData{ 123 | Topic: topic, 124 | SubString: subExpression, 125 | } 126 | c.rebalance.subscriptionInner[topic] = subData 127 | } 128 | 129 | func (c *DefaultConsumer) UnSubscribe(topic string) { 130 | delete(c.subscription, topic) 131 | } 132 | 133 | func (c *DefaultConsumer) SendMessageBack(msg MessageExt, delayLevel int) error { 134 | return nil 135 | } 136 | 137 | func (c *DefaultConsumer) SendMessageBack1(msg MessageExt, delayLevel int, brokerName string) error { 138 | return nil 139 | } 140 | 141 | func (c *DefaultConsumer) fetchSubscribeMessageQueues(topic string) error { 142 | return nil 143 | } 144 | 145 | func (c *DefaultConsumer) pullMessage(pullRequest *PullRequest) { 146 | 147 | commitOffsetEnable := false 148 | commitOffsetValue := int64(0) 149 | 150 | commitOffsetValue = c.offsetStore.readOffset(pullRequest.messageQueue, ReadFromMemory) 151 | if commitOffsetValue > 0 { 152 | commitOffsetEnable = true 153 | } 154 | 155 | var sysFlag = int32(0) 156 | if commitOffsetEnable { 157 | sysFlag |= FlagCommitOffset 158 | } 159 | 160 | sysFlag |= FlagSuspend 161 | 162 | subscriptionData, ok := c.rebalance.subscriptionInner[pullRequest.messageQueue.topic] 163 | var subVersion int64 164 | var subString string 165 | if ok { 166 | subVersion = subscriptionData.SubVersion 167 | subString = subscriptionData.SubString 168 | 169 | sysFlag |= FlagSubscription 170 | } 171 | 172 | requestHeader := new(PullMessageRequestHeader) 173 | requestHeader.ConsumerGroup = pullRequest.consumerGroup 174 | requestHeader.Topic = pullRequest.messageQueue.topic 175 | requestHeader.QueueId = pullRequest.messageQueue.queueId 176 | requestHeader.QueueOffset = pullRequest.nextOffset 177 | 178 | requestHeader.SysFlag = sysFlag 179 | requestHeader.CommitOffset = commitOffsetValue 180 | requestHeader.SuspendTimeoutMillis = BrokerSuspendMaxTimeMillis 181 | 182 | if ok { 183 | requestHeader.SubVersion = subVersion 184 | requestHeader.Subscription = subString 185 | } 186 | 187 | pullCallback := func(responseFuture *ResponseFuture) { 188 | var nextBeginOffset = pullRequest.nextOffset 189 | 190 | if responseFuture != nil { 191 | responseCommand := responseFuture.responseCommand 192 | if responseCommand.Code == Success && len(responseCommand.Body) > 0 { 193 | var err error 194 | pullResult, ok := responseCommand.ExtFields.(map[string]interface{}) 195 | if ok { 196 | if nextBeginOffsetInter, ok := pullResult["nextBeginOffset"]; ok { 197 | if nextBeginOffsetStr, ok := nextBeginOffsetInter.(string); ok { 198 | nextBeginOffset, err = strconv.ParseInt(nextBeginOffsetStr, 10, 64) 199 | if err != nil { 200 | fmt.Println(err) 201 | return 202 | } 203 | } 204 | } 205 | } 206 | 207 | msgs := decodeMessage(responseFuture.responseCommand.Body) 208 | err = c.messageListener(msgs) 209 | if err != nil { 210 | fmt.Println(err) 211 | //TODO retry 212 | } else { 213 | c.offsetStore.updateOffset(pullRequest.messageQueue, nextBeginOffset, false) 214 | } 215 | } else if responseCommand.Code == PullNotFound { 216 | } else if responseCommand.Code == PullRetryImmediately || responseCommand.Code == PullOffsetMoved { 217 | fmt.Printf("pull message error,code=%d,request=%v", responseCommand.Code, requestHeader) 218 | var err error 219 | pullResult, ok := responseCommand.ExtFields.(map[string]interface{}) 220 | if ok { 221 | if nextBeginOffsetInter, ok := pullResult["nextBeginOffset"]; ok { 222 | if nextBeginOffsetStr, ok := nextBeginOffsetInter.(string); ok { 223 | nextBeginOffset, err = strconv.ParseInt(nextBeginOffsetStr, 10, 64) 224 | if err != nil { 225 | fmt.Println(err) 226 | } 227 | } 228 | } 229 | } 230 | //time.Sleep(1 * time.Second) 231 | } else { 232 | fmt.Println(fmt.Sprintf("pull message error,code=%d,body=%s", responseCommand.Code, string(responseCommand.Body))) 233 | fmt.Println(pullRequest.messageQueue) 234 | time.Sleep(1 * time.Second) 235 | } 236 | } else { 237 | fmt.Println("responseFuture is nil") 238 | } 239 | 240 | nextPullRequest := &PullRequest{ 241 | consumerGroup: pullRequest.consumerGroup, 242 | nextOffset: nextBeginOffset, 243 | messageQueue: pullRequest.messageQueue, 244 | } 245 | 246 | c.mqClient.pullMessageService.pullRequestQueue <- nextPullRequest 247 | } 248 | 249 | brokerAddr, _, found := c.mqClient.findBrokerAddressInSubscribe(pullRequest.messageQueue.brokerName, 0, false) 250 | 251 | if found { 252 | currOpaque := atomic.AddInt32(&opaque, 1) 253 | remotingCommand := new(RemotingCommand) 254 | remotingCommand.Code = PullMsg 255 | remotingCommand.Opaque = currOpaque 256 | remotingCommand.Flag = 0 257 | remotingCommand.Language = "JAVA" 258 | remotingCommand.Version = 79 259 | 260 | remotingCommand.ExtFields = requestHeader 261 | 262 | c.remotingClient.invokeAsync(brokerAddr, remotingCommand, 1000, pullCallback) 263 | } 264 | } 265 | 266 | func (c *DefaultConsumer) updateTopicSubscribeInfo(topic string, info []*MessageQueue) { 267 | if c.rebalance.subscriptionInner != nil { 268 | c.rebalance.subscriptionInnerLock.RLock() 269 | _, ok := c.rebalance.subscriptionInner[topic] 270 | c.rebalance.subscriptionInnerLock.RUnlock() 271 | if ok { 272 | c.rebalance.subscriptionInnerLock.Lock() 273 | c.rebalance.topicSubscribeInfoTable[topic] = info 274 | c.rebalance.subscriptionInnerLock.Unlock() 275 | } 276 | } 277 | } 278 | 279 | func (c *DefaultConsumer) subscriptions() []*SubscriptionData { 280 | subscriptions := make([]*SubscriptionData, 0) 281 | for _, subscription := range c.rebalance.subscriptionInner { 282 | subscriptions = append(subscriptions, subscription) 283 | } 284 | return subscriptions 285 | } 286 | 287 | func (c *DefaultConsumer) doRebalance() { 288 | c.rebalance.doRebalance() 289 | } 290 | 291 | func (c *DefaultConsumer) isSubscribeTopicNeedUpdate(topic string) bool { 292 | subTable := c.rebalance.subscriptionInner 293 | if _, ok := subTable[topic]; ok { 294 | if _, ok := c.rebalance.topicSubscribeInfoTable[topic]; !ok { 295 | return true 296 | } 297 | } 298 | return false 299 | } 300 | -------------------------------------------------------------------------------- /remoting_client.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "math/rand" 10 | "net" 11 | "strings" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | type InvokeCallback func(responseFuture *ResponseFuture) 17 | 18 | type ResponseFuture struct { 19 | responseCommand *RemotingCommand 20 | sendRequestOK bool 21 | err error 22 | opaque int32 23 | timeoutMillis int64 24 | invokeCallback InvokeCallback 25 | beginTimestamp int64 26 | done chan bool 27 | } 28 | 29 | type RemotingClient interface { 30 | connect(addr string) (net.Conn, error) 31 | invokeAsync(addr string, request *RemotingCommand, timeoutMillis int64, invokeCallback InvokeCallback) error 32 | invokeSync(addr string, request *RemotingCommand, timeoutMillis int64) (*RemotingCommand, error) 33 | invokeOneway(addr string, request *RemotingCommand, timeoutMillis int64) error 34 | ScanResponseTable() 35 | getNameServerAddressList() []string 36 | } 37 | 38 | type DefaultRemotingClient struct { 39 | connTable map[string]net.Conn 40 | connTableLock sync.RWMutex 41 | responseTable map[int32]*ResponseFuture 42 | responseTableLock sync.RWMutex 43 | namesrvAddrList []string 44 | namesrvAddrChosen string 45 | } 46 | 47 | func NewDefaultRemotingClient() RemotingClient { 48 | return &DefaultRemotingClient{ 49 | connTable: make(map[string]net.Conn), 50 | responseTable: make(map[int32]*ResponseFuture), 51 | namesrvAddrList: make([]string, 0), 52 | } 53 | } 54 | 55 | func (d *DefaultRemotingClient) ScanResponseTable() { 56 | d.responseTableLock.Lock() 57 | for seq, response := range d.responseTable { 58 | if (response.beginTimestamp + 30) <= time.Now().Unix() { 59 | delete(d.responseTable, seq) 60 | if response.invokeCallback != nil { 61 | response.invokeCallback(nil) 62 | fmt.Printf("remove time out request %v", response) 63 | } 64 | } 65 | } 66 | d.responseTableLock.Unlock() 67 | 68 | } 69 | 70 | func (d *DefaultRemotingClient) getAndCreateConn(addr string) (conn net.Conn, ok bool) { 71 | // TODO optimize algorithm of getting a nameserver connection 72 | if strings.ContainsAny(addr, ";") { 73 | namesrvAddrList := strings.Split(addr, ";") 74 | namesrvAddrListlen := len(namesrvAddrList) 75 | namesrvAddr, namesrvAddrList := removeOne(rand.Intn(namesrvAddrListlen), namesrvAddrList) 76 | 77 | var err error 78 | for i := 0; i < namesrvAddrListlen-1; i++ { 79 | conn, err = d.connect(namesrvAddr) 80 | if err != nil { 81 | fmt.Println("getAndCreateConn error, ", err) 82 | namesrvAddr, namesrvAddrList = removeOne(rand.Intn(namesrvAddrListlen), namesrvAddrList) 83 | } else { 84 | d.namesrvAddrChosen = namesrvAddr 85 | break 86 | } 87 | } 88 | addr = d.namesrvAddrChosen 89 | } 90 | 91 | d.connTableLock.RLock() 92 | conn, ok = d.connTable[addr] 93 | d.connTableLock.RUnlock() 94 | 95 | return 96 | } 97 | 98 | func removeOne(index int, namesrvAddrList []string) (namesrvAddr string, newNamesrvAddrList []string) { 99 | namesrvAddrListlen := len(namesrvAddrList) 100 | index = rand.Intn(namesrvAddrListlen) 101 | namesrvAddr = namesrvAddrList[index] 102 | namesrvAddrList[index] = namesrvAddrList[namesrvAddrListlen-1] 103 | newNamesrvAddrList = namesrvAddrList[:namesrvAddrListlen-1] 104 | 105 | return 106 | } 107 | 108 | func (d *DefaultRemotingClient) connect(addr string) (conn net.Conn, err error) { 109 | if addr == "" { 110 | addr = d.namesrvAddrChosen 111 | } 112 | 113 | d.connTableLock.RLock() 114 | conn, ok := d.connTable[addr] 115 | d.connTableLock.RUnlock() 116 | if !ok { 117 | conn, err = net.Dial("tcp", addr) 118 | if err != nil { 119 | fmt.Println(err) 120 | return nil, err 121 | } 122 | 123 | d.connTableLock.Lock() 124 | d.connTable[addr] = conn 125 | d.connTableLock.Unlock() 126 | fmt.Println("connect to:", addr) 127 | go d.handlerConn(conn, addr) 128 | } 129 | 130 | return conn, nil 131 | } 132 | 133 | func (d *DefaultRemotingClient) invokeSync(addr string, request *RemotingCommand, timeoutMillis int64) (*RemotingCommand, error) { 134 | conn, ok := d.getAndCreateConn(addr) 135 | var err error 136 | if !ok { 137 | conn, err = d.connect(addr) 138 | if err != nil { 139 | fmt.Println(err) 140 | return nil, err 141 | } 142 | } 143 | 144 | response := &ResponseFuture{ 145 | sendRequestOK: false, 146 | opaque: request.Opaque, 147 | timeoutMillis: timeoutMillis, 148 | beginTimestamp: time.Now().Unix(), 149 | done: make(chan bool), 150 | } 151 | 152 | header := request.encodeHeader() 153 | body := request.Body 154 | 155 | d.responseTableLock.Lock() 156 | d.responseTable[request.Opaque] = response 157 | d.responseTableLock.Unlock() 158 | 159 | err = d.sendRequest(header, body, conn, addr) 160 | if err != nil { 161 | fmt.Println("invokeSync:err", err) 162 | return nil, err 163 | } 164 | select { 165 | case <-response.done: 166 | return response.responseCommand, nil 167 | case <-time.After(3 * time.Second): 168 | return nil, errors.New("invoke sync timeout") 169 | } 170 | 171 | } 172 | 173 | func (d *DefaultRemotingClient) invokeAsync(addr string, request *RemotingCommand, timeoutMillis int64, invokeCallback InvokeCallback) (err error) { 174 | d.connTableLock.RLock() 175 | conn, ok := d.connTable[addr] 176 | d.connTableLock.RUnlock() 177 | 178 | if !ok { 179 | conn, err = d.connect(addr) 180 | if err != nil { 181 | fmt.Println(err) 182 | return err 183 | } 184 | } 185 | 186 | response := &ResponseFuture{ 187 | sendRequestOK: false, 188 | opaque: request.Opaque, 189 | timeoutMillis: timeoutMillis, 190 | beginTimestamp: time.Now().Unix(), 191 | invokeCallback: invokeCallback, 192 | } 193 | 194 | d.responseTableLock.Lock() 195 | d.responseTable[request.Opaque] = response 196 | d.responseTableLock.Unlock() 197 | 198 | header := request.encodeHeader() 199 | body := request.Body 200 | err = d.sendRequest(header, body, conn, addr) 201 | if err != nil { 202 | fmt.Println(err) 203 | return err 204 | } 205 | return nil 206 | } 207 | 208 | func (d *DefaultRemotingClient) invokeOneway(addr string, request *RemotingCommand, timeoutMillis int64) (err error) { 209 | d.connTableLock.RLock() 210 | conn, ok := d.connTable[addr] 211 | d.connTableLock.RUnlock() 212 | 213 | if !ok { 214 | conn, err = d.connect(addr) 215 | if err != nil { 216 | fmt.Println(err) 217 | return err 218 | } 219 | } 220 | 221 | request.markOnewayRPC() 222 | header := request.encodeHeader() 223 | body := request.Body 224 | 225 | return d.sendRequest(header, body, conn, addr) 226 | } 227 | 228 | func (d *DefaultRemotingClient) handlerConn(conn net.Conn, addr string) { 229 | b := make([]byte, 1024) 230 | var length, headerLength, bodyLength int32 231 | var buf = bytes.NewBuffer([]byte{}) 232 | var header, body []byte 233 | var flag = 0 234 | for { 235 | n, err := conn.Read(b) 236 | if err != nil { 237 | d.releaseConn(addr, conn) 238 | fmt.Println(err, addr) 239 | 240 | return 241 | } 242 | 243 | _, err = buf.Write(b[:n]) 244 | if err != nil { 245 | d.releaseConn(addr, conn) 246 | return 247 | } 248 | 249 | for { 250 | if flag == 0 { 251 | if buf.Len() >= 4 { 252 | err = binary.Read(buf, binary.BigEndian, &length) 253 | if err != nil { 254 | fmt.Println(err) 255 | return 256 | } 257 | flag = 1 258 | } else { 259 | break 260 | } 261 | } 262 | 263 | if flag == 1 { 264 | if buf.Len() >= 4 { 265 | err = binary.Read(buf, binary.BigEndian, &headerLength) 266 | if err != nil { 267 | fmt.Println(err) 268 | return 269 | } 270 | flag = 2 271 | } else { 272 | break 273 | } 274 | 275 | } 276 | 277 | if flag == 2 { 278 | if (buf.Len() > 0) && (buf.Len() >= int(headerLength)) { 279 | header = make([]byte, headerLength) 280 | _, err = buf.Read(header) 281 | if err != nil { 282 | fmt.Println(err) 283 | return 284 | } 285 | flag = 3 286 | } else { 287 | break 288 | } 289 | } 290 | 291 | if flag == 3 { 292 | bodyLength = length - 4 - headerLength 293 | if bodyLength == 0 { 294 | flag = 0 295 | } else { 296 | 297 | if buf.Len() >= int(bodyLength) { 298 | body = make([]byte, int(bodyLength)) 299 | _, err = buf.Read(body) 300 | if err != nil { 301 | fmt.Println(err) 302 | return 303 | } 304 | flag = 0 305 | } else { 306 | break 307 | } 308 | } 309 | } 310 | 311 | if flag == 0 { 312 | headerCopy := make([]byte, len(header)) 313 | bodyCopy := make([]byte, len(body)) 314 | copy(headerCopy, header) 315 | copy(bodyCopy, body) 316 | go func() { 317 | cmd := decodeRemoteCommand(headerCopy, bodyCopy) 318 | d.responseTableLock.RLock() 319 | response, ok := d.responseTable[cmd.Opaque] 320 | d.responseTableLock.RUnlock() 321 | 322 | d.responseTableLock.Lock() 323 | delete(d.responseTable, cmd.Opaque) 324 | d.responseTableLock.Unlock() 325 | 326 | if ok { 327 | response.responseCommand = cmd 328 | if response.invokeCallback != nil { 329 | response.invokeCallback(response) 330 | } 331 | 332 | if response.done != nil { 333 | response.done <- true 334 | } 335 | } else { 336 | if cmd.Code == NotifyConsumerIdsChanged { 337 | return 338 | } 339 | jsonCmd, err := json.Marshal(cmd) 340 | 341 | if err != nil { 342 | fmt.Println(err) 343 | } 344 | fmt.Println(string(jsonCmd)) 345 | } 346 | }() 347 | } 348 | } 349 | 350 | } 351 | } 352 | 353 | func (d *DefaultRemotingClient) sendRequest(header, body []byte, conn net.Conn, addr string) error { 354 | 355 | buf := bytes.NewBuffer([]byte{}) 356 | binary.Write(buf, binary.BigEndian, int32(len(header)+len(body)+4)) 357 | binary.Write(buf, binary.BigEndian, int32(len(header))) 358 | _, err := conn.Write(buf.Bytes()) 359 | 360 | if err != nil { 361 | d.releaseConn(addr, conn) 362 | return err 363 | } 364 | 365 | _, err = conn.Write(header) 366 | if err != nil { 367 | d.releaseConn(addr, conn) 368 | return err 369 | } 370 | 371 | if body != nil && len(body) > 0 { 372 | _, err = conn.Write(body) 373 | if err != nil { 374 | d.releaseConn(addr, conn) 375 | return err 376 | } 377 | } 378 | 379 | return nil 380 | } 381 | 382 | func (d *DefaultRemotingClient) releaseConn(addr string, conn net.Conn) { 383 | conn.Close() 384 | d.connTableLock.Lock() 385 | delete(d.connTable, addr) 386 | d.connTableLock.Unlock() 387 | } 388 | 389 | func (d *DefaultRemotingClient) getNameServerAddressList() []string { 390 | return d.namesrvAddrList 391 | } 392 | -------------------------------------------------------------------------------- /producer.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | "sync/atomic" 10 | "time" 11 | ) 12 | 13 | type Producer interface { 14 | Start() error 15 | Shutdown() 16 | Send(msg *Message) (*SendResult, error) 17 | SendAsync(msg *Message, sendCallback SendCallback) error 18 | SendOneway(msg *Message) error 19 | } 20 | 21 | // communicationMode 22 | const ( 23 | Sync = iota 24 | Async 25 | Oneway 26 | ) 27 | 28 | // ServiceState 29 | const ( 30 | CreateJust = iota 31 | Running 32 | ShutdownAlready 33 | StartFailed 34 | ) 35 | 36 | type SendCallback func() error 37 | 38 | type DefaultProducer struct { 39 | conf *Config 40 | producerGroup string 41 | producerType string 42 | rebalance *Rebalance 43 | remotingClient RemotingClient 44 | mqClient *MqClient 45 | topicPublishInfoTable map[string]*TopicPublishInfo 46 | instanceName string 47 | sendMsgTimeout int64 48 | serviceState int 49 | createTopicKey string 50 | defaultTopicQueueNums int 51 | compressMsgBodyOverHowmuch int 52 | retryTimesWhenSendFailed int 53 | retryTimesWhenSendAsyncFailed int 54 | retryAnotherBrokerWhenNotStoreOK bool 55 | maxMessageSize int 56 | vipChannelEnabled bool 57 | } 58 | 59 | func NewDefaultProducer(producerGroup string, conf *Config) (Producer, error) { 60 | if conf == nil { 61 | conf = &Config{ 62 | Namesrv: os.Getenv("ROCKETMQ_NAMESVR"), 63 | InstanceName: "DEFAULT", 64 | } 65 | } 66 | 67 | if conf.ClientIp == "" { 68 | conf.ClientIp = DefaultIp 69 | } 70 | 71 | remotingClient := NewDefaultRemotingClient() 72 | mqClient := NewMqClient() 73 | pullMessageService := NewPullMessageService() 74 | producer := &DefaultProducer{ 75 | conf: conf, 76 | producerGroup: producerGroup, 77 | 78 | remotingClient: remotingClient, 79 | mqClient: mqClient, 80 | topicPublishInfoTable: make(map[string]*TopicPublishInfo), 81 | 82 | sendMsgTimeout: 3000, 83 | instanceName: "DEFAULT", 84 | serviceState: CreateJust, 85 | createTopicKey: DefaultTopic, 86 | defaultTopicQueueNums: 4, 87 | compressMsgBodyOverHowmuch: 1024 * 4, 88 | retryTimesWhenSendFailed: 2, 89 | retryTimesWhenSendAsyncFailed: 2, 90 | retryAnotherBrokerWhenNotStoreOK: false, 91 | maxMessageSize: 1024 * 1024 * 4, // 4M 92 | } 93 | 94 | mqClient.remotingClient = remotingClient 95 | mqClient.conf = conf 96 | mqClient.clientId = conf.ClientIp + "@" + strconv.Itoa(os.Getpid()) 97 | mqClient.pullMessageService = pullMessageService 98 | mqClient.defaultProducer = producer 99 | 100 | pullMessageService.service = producer 101 | 102 | return producer, nil 103 | } 104 | 105 | func (d *DefaultProducer) start(startFactory bool) (err error) { 106 | switch d.serviceState { 107 | case CreateJust: 108 | d.serviceState = StartFailed 109 | d.checkConfig() 110 | if d.producerGroup == ClientInnerProducerGroup { 111 | if d.instanceName == "DEFAULT" { 112 | d.instanceName = strconv.Itoa(os.Getpid()) 113 | } 114 | } 115 | 116 | if registerOK := d.mqClient.registerProducer(d.producerGroup, d); !registerOK { 117 | d.serviceState = CreateJust 118 | err = errors.New("The producer group[" + d.producerGroup + "] has been created before, specify another name please.") 119 | return 120 | } 121 | 122 | topicPublishInfo := NewTopicPublishInfo() 123 | d.topicPublishInfoTable[d.createTopicKey] = topicPublishInfo 124 | if startFactory { 125 | d.mqClient.start() 126 | } 127 | d.serviceState = Running 128 | case Running, ShutdownAlready, StartFailed: 129 | err = errors.New("The producer service state not OK, maybe started once," + strconv.Itoa(d.serviceState)) 130 | } 131 | return 132 | } 133 | 134 | func (d *DefaultProducer) Start() error { 135 | return d.start(true) 136 | } 137 | 138 | func (d *DefaultProducer) checkConfig() (err error) { 139 | if d.producerGroup == DefaultProducerGroup { 140 | err = errors.New("producerGroup can not equal " + DefaultProducerGroup + ", please specify another one") 141 | } 142 | return 143 | } 144 | 145 | func (d *DefaultProducer) makeSureStateOK() (err error) { 146 | if d.serviceState != Running { 147 | err = errors.New("The producer service state not OK," + strconv.Itoa(d.serviceState)) 148 | } 149 | return 150 | } 151 | 152 | func (d *DefaultProducer) Shutdown() { 153 | } 154 | 155 | func (d *DefaultProducer) Send(msg *Message) (*SendResult, error) { 156 | return d.send(msg, Sync, nil, d.sendMsgTimeout) 157 | } 158 | 159 | func (d *DefaultProducer) SendAsync(msg *Message, sendCallback SendCallback) (err error) { 160 | _, err = d.send(msg, Async, sendCallback, d.sendMsgTimeout) 161 | return 162 | } 163 | 164 | func (d *DefaultProducer) SendOneway(msg *Message) error { 165 | d.send(msg, Oneway, nil, d.sendMsgTimeout) 166 | return nil 167 | } 168 | 169 | func (d *DefaultProducer) send(msg *Message, communicationMode int, sendCallback SendCallback, timeout int64) (sendResult *SendResult, err error) { 170 | if err = d.makeSureStateOK(); err != nil { 171 | return 172 | } 173 | if err = msg.checkMessage(d); err != nil { 174 | return 175 | } 176 | topicPublishInfo := d.tryToFindTopicPublishInfo(msg.Topic) 177 | 178 | // TODO handle topicPublishInfo 179 | if topicPublishInfo.ok() { 180 | timesTotal := 1 181 | if communicationMode == Sync { 182 | timesTotal = 1 + d.getRetryTimesWhenSendFailed() 183 | } 184 | 185 | var mq *MessageQueue 186 | brokersSent := make([]string, 0) 187 | 188 | for times := 0; times < timesTotal; times++ { 189 | lastBrokerName := "" 190 | if mq == nil { 191 | lastBrokerName = "" 192 | } else { 193 | lastBrokerName = mq.getBrokerName() 194 | } 195 | tmpmq := topicPublishInfo.selectOneMessageQueue(lastBrokerName) 196 | if tmpmq != nil { 197 | mq = tmpmq 198 | brokersSent = append(brokersSent, mq.getBrokerName()) 199 | beginTimestampPrev := time.Now().Unix() 200 | sendResult, err = d.sendKernel(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout) 201 | endTimestamp := time.Now().Unix() 202 | d.updateFaultItem(mq.getBrokerName(), endTimestamp-beginTimestampPrev, false) 203 | 204 | switch communicationMode { 205 | case Async: 206 | return 207 | case Oneway: 208 | return 209 | case Sync: 210 | if sendResult.sendStatus != SendStatusOK { 211 | if d.retryAnotherBrokerWhenNotStoreOK { 212 | continue 213 | } 214 | } 215 | return sendResult, nil 216 | default: 217 | } 218 | } 219 | } 220 | } 221 | 222 | if d.remotingClient.getNameServerAddressList() == nil || len(d.remotingClient.getNameServerAddressList()) == 0 { 223 | err = errors.New("no name server address, please set it") 224 | } 225 | err = fmt.Errorf("No route info of this topic, %s" + msg.Topic) 226 | return 227 | } 228 | 229 | func (d *DefaultProducer) getRetryTimesWhenSendFailed() int { 230 | return d.retryTimesWhenSendFailed 231 | } 232 | 233 | func (d *DefaultProducer) tryToFindTopicPublishInfo(topic string) (topicPublishInfo *TopicPublishInfo) { 234 | ok := false 235 | if topicPublishInfo, ok = d.topicPublishInfoTable[topic]; !ok || !topicPublishInfo.ok() { 236 | d.topicPublishInfoTable[topic] = NewTopicPublishInfo() 237 | d.mqClient.updateTopicRouteInfoFromNameServerKernel(topic, false, DefaultProducer{}) 238 | topicPublishInfo = d.topicPublishInfoTable[topic] 239 | } 240 | 241 | // TODO handle topicPublishInfo 242 | if !topicPublishInfo.haveTopicRouterInfo && !topicPublishInfo.ok() { 243 | d.mqClient.updateTopicRouteInfoFromNameServerKernel(topic, true, *d) 244 | topicPublishInfo = d.topicPublishInfoTable[topic] 245 | } 246 | 247 | return 248 | } 249 | 250 | func (d *DefaultProducer) sendKernel(msg *Message, mq *MessageQueue, communicationMode int, sendCallback SendCallback, 251 | topicPublishInfo *TopicPublishInfo, timeout int64) (sendResult *SendResult, err error) { 252 | brokerAddr := d.mqClient.findBrokerAddressInPublish(mq.getBrokerName()) 253 | if brokerAddr == "" { 254 | d.tryToFindTopicPublishInfo(msg.Topic) 255 | brokerAddr = d.mqClient.findBrokerAddressInPublish(mq.getBrokerName()) 256 | } 257 | context := newSendMessageContext() 258 | if brokerAddr != "" { 259 | brokerAddr = BrokerVIPChannel(d.vipChannelEnabled, brokerAddr) 260 | prevBody := msg.Body 261 | 262 | MessageClientIDSetter.setUniqID(msg) 263 | 264 | sysFlag := 0 265 | if d.tryToCompressMessage(msg) { 266 | sysFlag |= CompressedFlag 267 | } 268 | 269 | tranMsg := msg.Properties[MessageConst.PropertyTransactionPrepared] 270 | if b, err := strconv.ParseBool(tranMsg); tranMsg != "" && err == nil && b { 271 | sysFlag |= TransactionPreparedType 272 | } 273 | 274 | if d.hasCheckForbiddenHook() { 275 | fmt.Fprintf(os.Stderr, brokerAddr) 276 | } 277 | if d.hasSendMessageHook() { 278 | } 279 | 280 | requestHeader := new(SendMessageRequestHeader) 281 | requestHeader.ProducerGroup = d.producerGroup 282 | requestHeader.Topic = msg.Topic 283 | requestHeader.DefaultTopic = d.createTopicKey 284 | requestHeader.DefaultTopicQueueNums = d.defaultTopicQueueNums 285 | requestHeader.QueueId = mq.queueId 286 | requestHeader.SysFlag = sysFlag 287 | requestHeader.Properties = messageProperties2String(msg.Properties) 288 | requestHeader.ReconsumeTimes = 0 289 | 290 | if strings.HasPrefix(requestHeader.Topic, RetryGroupTopicPrefix) { 291 | if MessageConst.PropertyReconsumeTime != "" { 292 | requestHeader.ReconsumeTimes, _ = strconv.Atoi(MessageConst.PropertyReconsumeTime) 293 | delete(msg.Properties, MessageConst.PropertyReconsumeTime) 294 | } 295 | if MessageConst.PropertyMaxReconsumeTimes != "" { 296 | requestHeader.MaxReconsumeTimes, _ = strconv.Atoi(MessageConst.PropertyMaxReconsumeTimes) 297 | delete(msg.Properties, MessageConst.PropertyMaxReconsumeTimes) 298 | } 299 | } 300 | 301 | switch communicationMode { 302 | case Async: 303 | sendResult, err = d.sendMessage( 304 | brokerAddr, 305 | mq.brokerName, 306 | msg, 307 | requestHeader, 308 | timeout, 309 | communicationMode, 310 | sendCallback, 311 | topicPublishInfo, 312 | d.mqClient, 313 | d.retryTimesWhenSendAsyncFailed, 314 | context) 315 | case Oneway, Sync: 316 | sendResult, err = d.sendMessage( 317 | brokerAddr, 318 | mq.brokerName, 319 | msg, 320 | requestHeader, 321 | timeout, 322 | communicationMode, 323 | sendCallback, 324 | topicPublishInfo, 325 | d.mqClient, 326 | d.retryTimesWhenSendFailed, 327 | context) 328 | } 329 | if d.hasSendMessageHook() { 330 | if msg.Properties[MessageConst.PropertyTransactionPrepared] == "true" { 331 | context.msgType = strconv.Itoa(TransMsgHalf) 332 | } 333 | if msg.Properties["__STARTDELIVERTIME"] != "" || msg.Properties[MessageConst.PropertyDelayTimeLevel] != "" { 334 | context.msgType = strconv.Itoa(DelayMsg) 335 | } 336 | } 337 | 338 | msg.Body = prevBody 339 | } 340 | return 341 | } 342 | 343 | func (d *DefaultProducer) tryToCompressMessage(msg *Message) bool { 344 | return false 345 | // TODO add compression 346 | //body := msg.Body 347 | //if body != nil && len(body) != 0 { 348 | //} 349 | } 350 | 351 | func (d *DefaultProducer) hasCheckForbiddenHook() bool { 352 | return false 353 | } 354 | 355 | func (d *DefaultProducer) hasSendMessageHook() bool { 356 | return false 357 | } 358 | 359 | func (d *DefaultProducer) pullMessage(pullRequest *PullRequest) { 360 | return 361 | } 362 | 363 | func (d *DefaultProducer) sendMessage(addr string, brokerName string, msg *Message, requestHeader *SendMessageRequestHeader, 364 | timeoutMillis int64, communicationMode int, sendCallback SendCallback, topicPublishInfo *TopicPublishInfo, mqClient *MqClient, 365 | retryTimesWhenSendFailed int, context *SendMessageContext) (sendResult *SendResult, err error) { 366 | remotingCommand := new(RemotingCommand) 367 | remotingCommand.Code = SendMsg 368 | currOpaque := atomic.AddInt32(&opaque, 1) 369 | remotingCommand.Opaque = currOpaque 370 | remotingCommand.Flag = 0 371 | remotingCommand.Language = "JAVA" 372 | remotingCommand.Version = 79 373 | remotingCommand.ExtFields = requestHeader 374 | remotingCommand.Body = msg.Body 375 | 376 | switch communicationMode { 377 | case Async: 378 | times := atomic.AddInt32(&opaque, 1) 379 | d.sendMessageAsync(addr, brokerName, msg, timeoutMillis, remotingCommand, sendCallback, topicPublishInfo, mqClient, 380 | retryTimesWhenSendFailed, times, context, d) 381 | case Oneway: 382 | err = d.remotingClient.invokeOneway(addr, remotingCommand, timeoutMillis) 383 | case Sync: 384 | sendResult, err = d.sendMessageSync(addr, brokerName, msg, timeoutMillis, remotingCommand) 385 | } 386 | 387 | return 388 | } 389 | 390 | func (d *DefaultProducer) sendMessageSync(addr string, brokerName string, msg *Message, timeoutMillis int64, remotingCommand *RemotingCommand) (sendResult *SendResult, err error) { 391 | var response *RemotingCommand 392 | fmt.Fprintln(os.Stderr, "msg:", msg.Topic, msg.Flag, string(msg.Body), msg.Properties) 393 | if response, err = d.remotingClient.invokeSync(addr, remotingCommand, timeoutMillis); err != nil { 394 | fmt.Fprintln(os.Stderr, "sendMessageSync err", err) 395 | } 396 | return d.processSendResponse(brokerName, msg, response) 397 | } 398 | 399 | func (d *DefaultProducer) sendMessageAsync(addr string, brokerName string, msg *Message, timeoutMillis int64, 400 | remotingCommand *RemotingCommand, sendCallback SendCallback, topicPublishInfo *TopicPublishInfo, mqClient *MqClient, 401 | retryTimesWhenSendFailed int, times int32, context *SendMessageContext, producer *DefaultProducer) (err error) { 402 | invokeCallback := func(responseFuture *ResponseFuture) { 403 | var sendResult *SendResult 404 | responseCommand := responseFuture.responseCommand 405 | 406 | if sendCallback == nil && responseCommand != nil { 407 | sendResult, err = d.processSendResponse(brokerName, msg, responseCommand) 408 | if err != nil && context != nil && sendResult != nil { 409 | context.sendResult = sendResult 410 | // TODO add send hook 411 | } 412 | d.updateFaultItem(brokerName, time.Now().Unix()-responseFuture.beginTimestamp, false) 413 | return 414 | } 415 | 416 | sendCallback() 417 | if responseCommand != nil { 418 | if sendResult, err = d.processSendResponse(brokerName, msg, responseCommand); sendResult == nil || err != nil { 419 | fmt.Fprintln(os.Stderr, "sendResult can't be null, error ", err) 420 | producer.updateFaultItem(brokerName, time.Now().Unix()-responseFuture.beginTimestamp, true) 421 | return 422 | } else { 423 | if context != nil { 424 | context.sendResult = sendResult 425 | // TODO add send hook 426 | producer.updateFaultItem(brokerName, time.Now().Unix()-responseFuture.beginTimestamp, false) 427 | } 428 | } 429 | } 430 | producer.updateFaultItem(brokerName, time.Now().Unix()-responseFuture.beginTimestamp, true) 431 | } 432 | err = d.remotingClient.invokeAsync(addr, remotingCommand, 1000, invokeCallback) 433 | return 434 | } 435 | 436 | func (d *DefaultProducer) processSendResponse(brokerName string, msg *Message, response *RemotingCommand) (sendResult *SendResult, err error) { 437 | var sendStatus int 438 | 439 | if response == nil { 440 | err = errors.New("response in processSendResponse is nil") 441 | return 442 | } 443 | switch response.Code { 444 | // TODO add log 445 | case FlushDiskTimeout: 446 | sendStatus = SendStatusFlushDiskTimeout 447 | case FlushSlaveTimeout: 448 | sendStatus = SendStatusFlushSlaveTimeout 449 | case SlaveNotAvailable: 450 | sendStatus = SendStatusSlaveNotAvailable 451 | case Success: 452 | sendStatus = SendStatusOK 453 | responseHeader := response.decodeCommandCustomHeader() 454 | messageQueue := NewMessageQueue(msg.Topic, brokerName, responseHeader.queueId) 455 | 456 | sendResult = NewSendResult(sendStatus, MessageClientIDSetter.getUniqID(msg), responseHeader.msgId, messageQueue, responseHeader.queueOffset) 457 | sendResult.transactionId = responseHeader.transactionId 458 | 459 | pullResult, ok := response.ExtFields.(map[string]interface{}) 460 | if ok { 461 | if regionId, ok := pullResult[MessageConst.PropertyMsgRegion]; ok { 462 | if regionId == nil || regionId == "" { 463 | regionId = "DefaultRegion" 464 | } 465 | sendResult.regionId = regionId.(string) 466 | } 467 | } 468 | return 469 | } 470 | err = errors.New("processSendResponse error") 471 | 472 | return 473 | } 474 | 475 | func (d *DefaultProducer) updateFaultItem(brokerName string, currentLatency int64, isolation bool) { 476 | } 477 | 478 | func (d *DefaultProducer) getPublishTopicList() (topicList []string) { 479 | topicList = make([]string, 0) 480 | for topic := range d.topicPublishInfoTable { 481 | topicList = append(topicList, topic) 482 | } 483 | 484 | return 485 | } 486 | 487 | func (d *DefaultProducer) isPublishTopicNeedUpdate(topic string) bool { 488 | prev := d.topicPublishInfoTable[topic] 489 | return !prev.ok() 490 | } 491 | 492 | func (d *DefaultProducer) updateTopicPublishInfo(topic string, topicPublishInfo *TopicPublishInfo) { 493 | if topic != "" { 494 | d.topicPublishInfoTable[topic] = topicPublishInfo 495 | } 496 | } 497 | -------------------------------------------------------------------------------- /mq_client.go: -------------------------------------------------------------------------------- 1 | package rocketmq 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "math" 9 | "os" 10 | "strconv" 11 | "strings" 12 | "sync" 13 | "sync/atomic" 14 | "time" 15 | ) 16 | 17 | type GetRouteInfoRequestHeader struct { 18 | topic string 19 | } 20 | 21 | func (g *GetRouteInfoRequestHeader) MarshalJSON() ([]byte, error) { 22 | var buf bytes.Buffer 23 | buf.WriteString("{\"topic\":\"") 24 | buf.WriteString(g.topic) 25 | buf.WriteString("\"}") 26 | return buf.Bytes(), nil 27 | } 28 | 29 | type QueueData struct { 30 | BrokerName string 31 | ReadQueueNums int32 32 | WriteQueueNums int32 33 | Perm int 34 | TopicSynFlag int32 35 | } 36 | 37 | type BrokerData struct { 38 | BrokerName string 39 | BrokerAddrs map[string]string 40 | BrokerAddrsLock sync.RWMutex 41 | } 42 | 43 | type TopicRouteData struct { 44 | OrderTopicConf string 45 | QueueDatas []*QueueData 46 | BrokerDatas []*BrokerData 47 | } 48 | 49 | type MqClient struct { 50 | clientId string 51 | conf *Config 52 | brokerAddrTable map[string]map[string]string //map[brokerName]map[bokerId]addrs 53 | brokerAddrTableLock sync.RWMutex 54 | consumerTable map[string]*DefaultConsumer 55 | consumerTableLock sync.RWMutex 56 | producerTable map[string]*DefaultProducer 57 | producerTableLock sync.RWMutex 58 | topicRouteTable map[string]*TopicRouteData 59 | topicRouteTableLock sync.RWMutex 60 | remotingClient RemotingClient 61 | pullMessageService *PullMessageService 62 | defaultProducer *DefaultProducer 63 | serviceState int 64 | } 65 | 66 | func NewMqClient() *MqClient { 67 | return &MqClient{ 68 | brokerAddrTable: make(map[string]map[string]string), 69 | consumerTable: make(map[string]*DefaultConsumer), 70 | producerTable: make(map[string]*DefaultProducer), 71 | topicRouteTable: make(map[string]*TopicRouteData), 72 | } 73 | } 74 | 75 | type slice interface { 76 | Len() int 77 | } 78 | 79 | func sliceCompare(a, b interface{}) bool { 80 | switch a.(type) { 81 | case []*QueueData, []*BrokerData: 82 | if a == nil && b == nil { 83 | return true 84 | } 85 | if a == nil || b == nil { 86 | return false 87 | } 88 | if len(a.([]interface{})) != len(b.([]interface{})) { 89 | return false 90 | } 91 | for i := range a.([]interface{}) { 92 | if a.([]interface{})[i] != b.([]interface{})[i] { 93 | return false 94 | } 95 | } 96 | return true 97 | } 98 | return false 99 | } 100 | 101 | func (m *MqClient) findBrokerAddressInSubscribe(brokerName string, brokerId int64, onlyThisBroker bool) (brokerAddr string, slave bool, found bool) { 102 | slave = false 103 | found = false 104 | m.brokerAddrTableLock.RLock() 105 | brokerMap, ok := m.brokerAddrTable[brokerName] 106 | m.brokerAddrTableLock.RUnlock() 107 | if ok { 108 | brokerAddr, ok = brokerMap[strconv.FormatInt(brokerId, 10)] 109 | slave = (brokerId != 0) 110 | found = ok 111 | 112 | if !found && !onlyThisBroker { 113 | var id string 114 | for id, brokerAddr = range brokerMap { 115 | slave = (id != "0") 116 | found = true 117 | break 118 | } 119 | } 120 | } 121 | 122 | return 123 | } 124 | 125 | func (m *MqClient) findBrokerAddressInAdmin(brokerName string) (addr string, found, slave bool) { 126 | found = false 127 | slave = false 128 | m.brokerAddrTableLock.RLock() 129 | brokers, ok := m.brokerAddrTable[brokerName] 130 | m.brokerAddrTableLock.RUnlock() 131 | if ok { 132 | for brokerId, addr := range brokers { 133 | 134 | if addr != "" { 135 | found = true 136 | if brokerId == "0" { 137 | slave = false 138 | } else { 139 | slave = true 140 | } 141 | break 142 | } 143 | } 144 | } 145 | 146 | return 147 | } 148 | 149 | func (m *MqClient) findBrokerAddrByTopic(topic string) (addr string, ok bool) { 150 | m.topicRouteTableLock.RLock() 151 | topicRouteData, ok := m.topicRouteTable[topic] 152 | m.topicRouteTableLock.RUnlock() 153 | if !ok { 154 | return "", ok 155 | } 156 | 157 | brokers := topicRouteData.BrokerDatas 158 | if brokers != nil && len(brokers) > 0 { 159 | brokerData := brokers[0] 160 | if ok { 161 | brokerData.BrokerAddrsLock.RLock() 162 | addr, ok = brokerData.BrokerAddrs["0"] 163 | brokerData.BrokerAddrsLock.RUnlock() 164 | 165 | if ok { 166 | return 167 | } 168 | for _, addr = range brokerData.BrokerAddrs { 169 | return addr, ok 170 | } 171 | } 172 | } 173 | return 174 | } 175 | 176 | func (m *MqClient) findConsumerIdList(topic string, groupName string) ([]string, error) { 177 | brokerAddr, ok := m.findBrokerAddrByTopic(topic) 178 | if !ok { 179 | _, err := m.updateTopicRouteInfoFromNameServerKernel(topic, false, DefaultProducer{}) 180 | fmt.Fprintln(os.Stderr, err) 181 | brokerAddr, ok = m.findBrokerAddrByTopic(topic) 182 | } 183 | 184 | if ok { 185 | return m.getConsumerIdListByGroup(brokerAddr, groupName, 3000) 186 | } 187 | 188 | return nil, errors.New("can't find broker") 189 | 190 | } 191 | 192 | type GetConsumerListByGroupRequestHeader struct { 193 | ConsumerGroup string `json:"consumerGroup"` 194 | } 195 | 196 | type GetConsumerListByGroupResponseBody struct { 197 | ConsumerIdList []string 198 | } 199 | 200 | func (m *MqClient) getConsumerIdListByGroup(addr string, consumerGroup string, timeoutMillis int64) ([]string, error) { 201 | requestHeader := new(GetConsumerListByGroupRequestHeader) 202 | requestHeader.ConsumerGroup = consumerGroup 203 | 204 | currOpaque := atomic.AddInt32(&opaque, 1) 205 | request := &RemotingCommand{ 206 | Code: GetConsumerListByGroup, 207 | Language: "JAVA", 208 | Version: 79, 209 | Opaque: currOpaque, 210 | Flag: 0, 211 | ExtFields: requestHeader, 212 | } 213 | 214 | response, err := m.remotingClient.invokeSync(addr, request, timeoutMillis) 215 | if err != nil { 216 | fmt.Fprintln(os.Stderr, err) 217 | return nil, err 218 | } 219 | 220 | if response.Code == Success { 221 | getConsumerListByGroupResponseBody := new(GetConsumerListByGroupResponseBody) 222 | bodyjson := strings.Replace(string(response.Body), "0:", "\"0\":", -1) 223 | bodyjson = strings.Replace(bodyjson, "1:", "\"1\":", -1) 224 | err := json.Unmarshal([]byte(bodyjson), getConsumerListByGroupResponseBody) 225 | if err != nil { 226 | fmt.Fprintln(os.Stderr, err) 227 | return nil, err 228 | } 229 | return getConsumerListByGroupResponseBody.ConsumerIdList, nil 230 | } 231 | 232 | return nil, errors.New("getConsumerIdListByGroup error") 233 | } 234 | 235 | func (m *MqClient) getTopicRouteInfoFromNameServer(topic string, timeoutMillis int64) (*TopicRouteData, error) { 236 | requestHeader := &GetRouteInfoRequestHeader{ 237 | topic: topic, 238 | } 239 | 240 | remotingCommand := new(RemotingCommand) 241 | remotingCommand.Code = GetRouteinfoByTopic 242 | currOpaque := atomic.AddInt32(&opaque, 1) 243 | remotingCommand.Opaque = currOpaque 244 | remotingCommand.Flag = 0 245 | remotingCommand.Language = "JAVA" 246 | remotingCommand.Version = 79 247 | 248 | remotingCommand.ExtFields = requestHeader 249 | 250 | response, err := m.remotingClient.invokeSync(m.conf.Namesrv, remotingCommand, timeoutMillis) 251 | if err != nil { 252 | return nil, err 253 | } 254 | if response.Code == Success { 255 | topicRouteData := new(TopicRouteData) 256 | bodyjson := strings.Replace(string(response.Body), ",0:", ",\"0\":", -1) 257 | bodyjson = strings.Replace(bodyjson, ",1:", ",\"1\":", -1) 258 | bodyjson = strings.Replace(bodyjson, "{0:", "{\"0\":", -1) 259 | bodyjson = strings.Replace(bodyjson, "{1:", "{\"1\":", -1) 260 | err = json.Unmarshal([]byte(bodyjson), topicRouteData) 261 | if err != nil { 262 | fmt.Fprintln(os.Stderr, "json.Unmarshal", err) 263 | return nil, err 264 | } 265 | return topicRouteData, nil 266 | } else { 267 | return nil, errors.New(fmt.Sprintf("get topicRouteInfo from nameServer error[code:%d,topic:%s]", response.Code, topic)) 268 | } 269 | 270 | } 271 | 272 | func (m *MqClient) updateTopicRouteInfoFromNameServer() { 273 | topicList := make([]string, 0) 274 | for _, consumer := range m.consumerTable { 275 | subscriptions := consumer.subscriptions() 276 | for _, subData := range subscriptions { 277 | topicList = append(topicList, subData.Topic) 278 | } 279 | } 280 | 281 | for _, producer := range m.producerTable { 282 | topicList = append(topicList, producer.getPublishTopicList()...) 283 | } 284 | 285 | for _, topic := range topicList { 286 | m.updateTopicRouteInfoFromNameServerKernel(topic, false, DefaultProducer{}) 287 | } 288 | } 289 | 290 | func (m *MqClient) topicRouteDataIsChange(oldData *TopicRouteData, nowData *TopicRouteData) bool { 291 | if !topicRouteDataIsNil(oldData) || !topicRouteDataIsNil(nowData) { 292 | return false 293 | } 294 | if !sliceCompare(oldData.QueueDatas, nowData.QueueDatas) { 295 | return false 296 | } 297 | if !sliceCompare(oldData.BrokerDatas, nowData.BrokerDatas) { 298 | return false 299 | } 300 | return true 301 | } 302 | 303 | func topicRouteDataIsNil(topicRouteData *TopicRouteData) (isNil bool) { 304 | if len(topicRouteData.QueueDatas) == 0 && len(topicRouteData.BrokerDatas) == 0 { 305 | isNil = true 306 | } 307 | return 308 | } 309 | 310 | func (m *MqClient) isNeedUpdateTopicRouteInfo(topic string) (result bool) { 311 | for _, producer := range m.producerTable { 312 | if !result && producer.producerGroup != "" { 313 | result = producer.isPublishTopicNeedUpdate(topic) 314 | } 315 | } 316 | 317 | for _, consumer := range m.consumerTable { 318 | if !result && consumer.consumerGroup != "" { 319 | result = consumer.isSubscribeTopicNeedUpdate(topic) 320 | } 321 | } 322 | return result 323 | } 324 | 325 | func (m *MqClient) updateTopicRouteInfoFromNameServerKernel(topic string, isDefault bool, producer DefaultProducer) (ok bool, err error) { 326 | var topicRouteData *TopicRouteData 327 | if isDefault && producer.producerGroup != "" { 328 | topicRouteData, err = m.getTopicRouteInfoFromNameServer(producer.createTopicKey, 3000*1000) 329 | if err != nil { 330 | fmt.Println(err) 331 | return true, err 332 | } 333 | for _, data := range topicRouteData.QueueDatas { 334 | queueNums := int32(math.Min(float64(producer.defaultTopicQueueNums), float64(data.ReadQueueNums))) 335 | data.ReadQueueNums = queueNums 336 | data.WriteQueueNums = queueNums 337 | } 338 | } else { 339 | topicRouteData, err = m.getTopicRouteInfoFromNameServer(topic, 3000*1000) 340 | } 341 | 342 | if topicRouteData != nil && !topicRouteDataIsNil(topicRouteData) { 343 | old := m.topicRouteTable[topic] 344 | changed := sliceCompare(old, topicRouteData) 345 | 346 | if !changed { 347 | changed = m.isNeedUpdateTopicRouteInfo(topic) 348 | } else { 349 | fmt.Fprintln(os.Stderr, "the topic[{}] route info changed, old[{}] ,new[{}]", topic, old, topicRouteData) 350 | } 351 | 352 | if changed { 353 | for _, bd := range topicRouteData.BrokerDatas { 354 | m.brokerAddrTableLock.Lock() 355 | m.brokerAddrTable[bd.BrokerName] = bd.BrokerAddrs 356 | m.brokerAddrTableLock.Unlock() 357 | } 358 | 359 | // Update Pub info 360 | publishInfo := topicRouteData2TopicPublishInfo(topic, topicRouteData) 361 | publishInfo.haveTopicRouterInfo = true 362 | for _, producer := range m.producerTable { 363 | producer.updateTopicPublishInfo(topic, publishInfo) 364 | } 365 | 366 | // Update sub info 367 | mqList := make([]*MessageQueue, 0) 368 | for _, queueData := range topicRouteData.QueueDatas { 369 | var i int32 370 | for i = 0; i < queueData.ReadQueueNums; i++ { 371 | mq := &MessageQueue{ 372 | topic: topic, 373 | brokerName: queueData.BrokerName, 374 | queueId: i, 375 | } 376 | mqList = append(mqList, mq) 377 | } 378 | } 379 | for _, consumer := range m.consumerTable { 380 | consumer.updateTopicSubscribeInfo(topic, mqList) 381 | } 382 | m.topicRouteTableLock.Lock() 383 | m.topicRouteTable[topic] = topicRouteData 384 | m.topicRouteTableLock.Unlock() 385 | 386 | return true, err 387 | } 388 | } 389 | 390 | return false, errors.New("updateTopicRouteInfoFromNameServer wrong") 391 | } 392 | 393 | func topicRouteData2TopicPublishInfo(topic string, route *TopicRouteData) (publishInfo *TopicPublishInfo) { 394 | publishInfo = NewTopicPublishInfo() 395 | publishInfo.topicRouteData = route 396 | if route.OrderTopicConf != "" { 397 | brokers := strings.Split(route.OrderTopicConf, ";") 398 | for _, borker := range brokers { 399 | item := strings.Split(borker, ":") 400 | nums, _ := strconv.Atoi(item[1]) 401 | for i := int32(0); i < int32(nums); i++ { 402 | mq := NewMessageQueue(topic, item[0], i) 403 | publishInfo.messageQueueList = append(publishInfo.messageQueueList, mq) 404 | } 405 | } 406 | publishInfo.orderTopic = true 407 | } else { 408 | qds := route.QueueDatas 409 | for _, qd := range qds { 410 | if PermName.isWritable(qd.Perm) { 411 | var brokerData *BrokerData 412 | for _, bd := range route.BrokerDatas { 413 | if bd.BrokerName == qd.BrokerName { 414 | brokerData = bd 415 | break 416 | } 417 | } 418 | 419 | if brokerData.BrokerName == "" { 420 | continue 421 | } 422 | 423 | if _, ok := brokerData.BrokerAddrs[strconv.Itoa(MasterId)]; !ok { 424 | continue 425 | } 426 | 427 | for i := int32(0); i < qd.WriteQueueNums; i++ { 428 | mq := NewMessageQueue(topic, qd.BrokerName, i) 429 | publishInfo.messageQueueList = append(publishInfo.messageQueueList, mq) 430 | } 431 | } 432 | } 433 | publishInfo.orderTopic = false 434 | } 435 | 436 | return 437 | } 438 | 439 | type ConsumerData struct { 440 | GroupName string 441 | ConsumerType string 442 | MessageModel string 443 | ConsumeFromWhere string 444 | SubscriptionDataSet []*SubscriptionData 445 | UnitMode bool 446 | } 447 | 448 | type HeartbeatData struct { 449 | ClientId string 450 | ConsumerDataSet []*ConsumerData 451 | } 452 | 453 | func (m *MqClient) prepareHeartbeatData() *HeartbeatData { 454 | heartbeatData := new(HeartbeatData) 455 | heartbeatData.ClientId = m.clientId 456 | heartbeatData.ConsumerDataSet = make([]*ConsumerData, 0) 457 | for group, consumer := range m.consumerTable { 458 | consumerData := new(ConsumerData) 459 | consumerData.GroupName = group 460 | consumerData.ConsumerType = consumer.consumerType 461 | consumerData.ConsumeFromWhere = consumer.consumeFromWhere 462 | consumerData.MessageModel = consumer.messageModel 463 | consumerData.SubscriptionDataSet = consumer.subscriptions() 464 | consumerData.UnitMode = consumer.unitMode 465 | 466 | heartbeatData.ConsumerDataSet = append(heartbeatData.ConsumerDataSet, consumerData) 467 | } 468 | return heartbeatData 469 | } 470 | 471 | func (m *MqClient) sendHeartbeatToAllBrokerWithLock() error { 472 | heartbeatData := m.prepareHeartbeatData() 473 | if len(heartbeatData.ConsumerDataSet) == 0 { 474 | return errors.New("send heartbeat error") 475 | } 476 | 477 | m.brokerAddrTableLock.RLock() 478 | for _, brokerTable := range m.brokerAddrTable { 479 | for brokerId, addr := range brokerTable { 480 | if addr == "" || brokerId != "0" { 481 | continue 482 | } 483 | currOpaque := atomic.AddInt32(&opaque, 1) 484 | remotingCommand := &RemotingCommand{ 485 | Code: HeartBeat, 486 | Language: "JAVA", 487 | Version: 79, 488 | Opaque: currOpaque, 489 | Flag: 0, 490 | } 491 | 492 | data, err := json.Marshal(*heartbeatData) 493 | if err != nil { 494 | fmt.Fprintln(os.Stderr, err) 495 | return err 496 | } 497 | remotingCommand.Body = data 498 | fmt.Fprintln(os.Stderr, "send heartbeat to broker[", addr+"]") 499 | response, err := m.remotingClient.invokeSync(addr, remotingCommand, 3000) 500 | if err != nil { 501 | fmt.Fprintln(os.Stderr, err) 502 | } else { 503 | if response == nil || response.Code != Success { 504 | fmt.Fprintln(os.Stderr, "send heartbeat response error") 505 | } 506 | } 507 | } 508 | } 509 | m.brokerAddrTableLock.RUnlock() 510 | return nil 511 | } 512 | 513 | func (m *MqClient) startScheduledTask() { 514 | go func() { 515 | updateTopicRouteTimer := time.NewTimer(5 * time.Second) 516 | for { 517 | <-updateTopicRouteTimer.C 518 | m.updateTopicRouteInfoFromNameServer() 519 | updateTopicRouteTimer.Reset(5 * time.Second) 520 | } 521 | }() 522 | 523 | go func() { 524 | heartbeatTimer := time.NewTimer(10 * time.Second) 525 | for { 526 | <-heartbeatTimer.C 527 | m.sendHeartbeatToAllBrokerWithLock() 528 | heartbeatTimer.Reset(5 * time.Second) 529 | } 530 | }() 531 | 532 | go func() { 533 | rebalanceTimer := time.NewTimer(15 * time.Second) 534 | for { 535 | <-rebalanceTimer.C 536 | m.doRebalance() 537 | rebalanceTimer.Reset(30 * time.Second) 538 | } 539 | }() 540 | 541 | go func() { 542 | timeoutTimer := time.NewTimer(3 * time.Second) 543 | for { 544 | <-timeoutTimer.C 545 | m.remotingClient.ScanResponseTable() 546 | timeoutTimer.Reset(time.Second) 547 | } 548 | }() 549 | } 550 | 551 | func (m *MqClient) doRebalance() { 552 | for _, consumer := range m.consumerTable { 553 | consumer.doRebalance() 554 | } 555 | } 556 | 557 | func (m *MqClient) start() { 558 | switch m.serviceState { 559 | case CreateJust: 560 | m.serviceState = StartFailed 561 | m.startScheduledTask() 562 | go m.pullMessageService.start() 563 | if m.defaultProducer != nil { 564 | m.defaultProducer.start(false) 565 | } 566 | fmt.Fprintf(os.Stderr, "The client factory [%s] start OK", m.clientId) 567 | m.serviceState = Running 568 | case Running, ShutdownAlready, StartFailed: 569 | fmt.Fprintf(os.Stderr, "The Factory object[%s] has been created before, and failed.", m.clientId) 570 | } 571 | 572 | } 573 | 574 | type QueryConsumerOffsetRequestHeader struct { 575 | ConsumerGroup string `json:"consumerGroup"` 576 | Topic string `json:"topic"` 577 | QueueId int32 `json:"queueId"` 578 | } 579 | 580 | func (m *MqClient) queryConsumerOffset(addr string, requestHeader *QueryConsumerOffsetRequestHeader, timeoutMillis int64) (int64, error) { 581 | currOpaque := atomic.AddInt32(&opaque, 1) 582 | remotingCommand := &RemotingCommand{ 583 | Code: QueryConsumerOffset, 584 | Language: "JAVA", 585 | Version: 79, 586 | Opaque: currOpaque, 587 | Flag: 0, 588 | } 589 | 590 | remotingCommand.ExtFields = requestHeader 591 | response, err := m.remotingClient.invokeSync(addr, remotingCommand, timeoutMillis) 592 | if err != nil { 593 | fmt.Fprintln(os.Stderr, err) 594 | return 0, err 595 | } 596 | if response.Code == QueryNotFound { 597 | return 0, nil 598 | } 599 | 600 | if extFields, ok := (response.ExtFields).(map[string]interface{}); ok { 601 | if offsetInter, ok := extFields["offset"]; ok { 602 | if offsetStr, ok := offsetInter.(string); ok { 603 | offset, err := strconv.ParseInt(offsetStr, 10, 64) 604 | if err != nil { 605 | fmt.Fprintln(os.Stderr, err) 606 | return 0, err 607 | } 608 | return offset, nil 609 | 610 | } 611 | } 612 | } 613 | fmt.Fprintln(os.Stderr, requestHeader, response) 614 | return 0, errors.New("query offset error") 615 | } 616 | 617 | func (m *MqClient) updateConsumerOffsetOneway(addr string, header *UpdateConsumerOffsetRequestHeader, timeoutMillis int64) { 618 | 619 | currOpaque := atomic.AddInt32(&opaque, 1) 620 | remotingCommand := &RemotingCommand{ 621 | Code: QueryConsumerOffset, 622 | Language: "JAVA", 623 | Version: 79, 624 | Opaque: currOpaque, 625 | Flag: 0, 626 | ExtFields: header, 627 | } 628 | 629 | m.remotingClient.invokeSync(addr, remotingCommand, timeoutMillis) 630 | } 631 | 632 | func (m *MqClient) findBrokerAddressInPublish(brokerName string) string { 633 | tmpMap := m.brokerAddrTable[brokerName] 634 | if tmpMap != nil && len(tmpMap) != 0 { 635 | brokerName = tmpMap[strconv.Itoa(MasterId)] 636 | } 637 | return brokerName 638 | } 639 | 640 | func (m *MqClient) registerProducer(group string, producer *DefaultProducer) bool { 641 | if group == "" { 642 | return false 643 | } 644 | if _, err := m.producerTable[group]; err { 645 | fmt.Fprintln(os.Stderr, "the producer group[{}] exist already.", group) 646 | return false 647 | } else { 648 | m.producerTable[group] = producer 649 | return true 650 | } 651 | } 652 | --------------------------------------------------------------------------------