// Copyright © 2023 OpenIM. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "encoding/json" "log" "sync" "sync/atomic" "time" "github.com/IBM/sarama" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/mw" "github.com/golang/protobuf/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/openimsdk/open-im-server/v3/pkg/apistruct" pbmsg "github.com/openimsdk/open-im-server/v3/tools/data-conversion/openim/proto/msg" ) func main() { var ( topic = "ws2ms_chat" // v2版本配置文件kafka.topic.ws2ms_chat kafkaAddr = "127.0.0.1:9092" // v2版本配置文件kafka.topic.addr rpcAddr = "127.0.0.1:10130" // v3版本配置文件rpcPort.openImMessagePort adminUserID = "openIM123456" // v3版本管理员userID concurrency = 1 // 并发数量 ) getRpcConn := func() (*grpc.ClientConn, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() return grpc.DialContext(ctx, rpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), mw.GrpcClient()) } conn, err := getRpcConn() if err != nil { log.Println("get rpc conn", err) return } defer conn.Close() msgClient := msg.NewMsgClient(conn) conf := sarama.NewConfig() conf.Consumer.Offsets.Initial = sarama.OffsetOldest consumer, err := sarama.NewConsumer([]string{kafkaAddr}, conf) if err != nil { log.Println("kafka consumer conn", err) return } partitions, err := consumer.Partitions(topic) // Get all partitions according to topic if err != nil { log.Println("kafka partitions", err) return } if len(partitions) == 0 { log.Println("kafka partitions is empty") return } log.Println("kafka partitions", partitions) msgCh := make(chan *pbmsg.MsgDataToMQ, concurrency*2) var kfkWg sync.WaitGroup distinct := make(map[string]struct{}) var lock sync.Mutex for _, partition := range partitions { kfkWg.Add(1) go func(partition int32) { defer kfkWg.Done() pc, err := consumer.ConsumePartition(topic, partition, sarama.OffsetOldest) if err != nil { log.Printf("kafka Consume Partition %d failed %s\n", partition, err) return } defer pc.Close() ch := pc.Messages() for { select { case <-time.After(time.Second * 10): // 10s读取不到就关闭 return case message, ok := <-ch: if !ok { return } msgFromMQV2 := pbmsg.MsgDataToMQ{} err := proto.Unmarshal(message.Value, &msgFromMQV2) if err != nil { log.Printf("kafka msg partition %d offset %d unmarshal failed %s\n", message.Partition, message.Offset, message.Value) continue } if msgFromMQV2.MsgData == nil || msgFromMQV2.OperationID == "" { continue } if msgFromMQV2.MsgData.ContentType < constant.ContentTypeBegin || msgFromMQV2.MsgData.ContentType > constant.AdvancedText { continue } lock.Lock() _, exist := distinct[msgFromMQV2.MsgData.ClientMsgID] if !exist { distinct[msgFromMQV2.MsgData.ClientMsgID] = struct{}{} } lock.Unlock() if exist { continue } msgCh <- &msgFromMQV2 } } }(partition) } go func() { kfkWg.Wait() close(msgCh) }() var msgWg sync.WaitGroup var ( success int64 failed int64 ) for i := 0; i < concurrency; i++ { msgWg.Add(1) go func() { defer msgWg.Done() for message := range msgCh { HandlerV2Msg(msgClient, adminUserID, message, &success, &failed) } }() } msgWg.Wait() log.Printf("total %d success %d failed %d\n", success+failed, success, failed) } func HandlerV2Msg(msgClient msg.MsgClient, adminUserID string, msgFromMQV2 *pbmsg.MsgDataToMQ, success *int64, failed *int64) { msgData := &sdkws.MsgData{ SendID: msgFromMQV2.MsgData.SendID, RecvID: msgFromMQV2.MsgData.RecvID, GroupID: msgFromMQV2.MsgData.GroupID, ClientMsgID: msgFromMQV2.MsgData.ClientMsgID, ServerMsgID: msgFromMQV2.MsgData.ServerMsgID, SenderPlatformID: msgFromMQV2.MsgData.SenderPlatformID, SenderNickname: msgFromMQV2.MsgData.SenderNickname, SenderFaceURL: msgFromMQV2.MsgData.SenderFaceURL, SessionType: msgFromMQV2.MsgData.SessionType, MsgFrom: msgFromMQV2.MsgData.MsgFrom, ContentType: msgFromMQV2.MsgData.ContentType, SendTime: msgFromMQV2.MsgData.SendTime, CreateTime: msgFromMQV2.MsgData.CreateTime, Status: msgFromMQV2.MsgData.Status, IsRead: false, Options: msgFromMQV2.MsgData.Options, AtUserIDList: msgFromMQV2.MsgData.AtUserIDList, AttachedInfo: msgFromMQV2.MsgData.AttachedInfo, Ex: msgFromMQV2.MsgData.Ex, } if msgFromMQV2.MsgData.OfflinePushInfo != nil { msgData.OfflinePushInfo = &sdkws.OfflinePushInfo{ Title: msgFromMQV2.MsgData.OfflinePushInfo.Title, Desc: msgFromMQV2.MsgData.OfflinePushInfo.Desc, Ex: msgFromMQV2.MsgData.OfflinePushInfo.Ex, IOSPushSound: msgFromMQV2.MsgData.OfflinePushInfo.IOSPushSound, IOSBadgeCount: msgFromMQV2.MsgData.OfflinePushInfo.IOSBadgeCount, SignalInfo: "", } } switch msgData.ContentType { case constant.Text: data, err := json.Marshal(apistruct.TextElem{ Content: string(msgFromMQV2.MsgData.Content), }) if err != nil { return } msgData.Content = data default: msgData.Content = msgFromMQV2.MsgData.Content } ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() ctx = context.WithValue(context.Background(), constant.OperationID, msgFromMQV2.OperationID) ctx = context.WithValue(ctx, constant.OpUserID, adminUserID) resp, err := msgClient.SendMsg(ctx, &msg.SendMsgReq{MsgData: msgData}) if err != nil { atomic.AddInt64(failed, 1) log.Printf("send msg %+v failed %s\n", msgData, err) return } atomic.AddInt64(success, 1) log.Printf("send msg success %+v resp %+v\n", msgData, resp) }