package KafkaUtil import ( "bytes" "context" "dsDataex/MyService/DataEX" "dsDataex/MyTask/Kafka2ES/Kafka2ESTask" "dsDataex/Utils/ConfigUtil" "encoding/json" "fmt" "github.com/segmentio/kafka-go" "github.com/segmentio/kafka-go/snappy" "math/rand" "strconv" "sync" "time" ) var KafkaBroker string var writerPool map[string]*kafka.Writer var kafkaPool map[string]map[int]*kafka.Conn var kafkaParts map[string]int //控制 consume 子线程 关闭 //var ChanTopicProc map[string]chan bool var loc sync.Mutex //记录 consume 子线程状态 ( 10 分钟内是否成功执行 ReadMessage,不执行自动关闭子线程 ) //var StateTopicProc map[string]bool var CountTopicProc map[string]int func init() { KafkaClient, _ := kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], "__consumer_offsets", 0) brokers, _ := KafkaClient.Brokers() KafkaBroker = brokers[0].Host + ":" + strconv.Itoa(brokers[0].Port) + "【" + strconv.Itoa(brokers[0].ID) + "】" writerPool = make(map[string]*kafka.Writer) kafkaPool = make(map[string]map[int]*kafka.Conn) kafkaParts = make(map[string]int) } /** * @Author zhangjun * @Description 发送 Kafka 消息,如果 Topic为空,创建 Topic * @Date 2020-07-27 05:26 * @Param * @return **/ func ProvideLow(topic string, datas []DataEX.KafkaData) (bool, string) { var client *kafka.Conn clients, flag := kafkaPool[topic] if flag == false { client, _ = kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic, 0) clients = make(map[int]*kafka.Conn) clients[0] = client parts, _ := client.ReadPartitions() offset, _ := client.ReadLastOffset() if len(parts) == 1 && offset == 0 { //初始化 Topic DeleteTopic(topic) time.Sleep(100 * time.Millisecond) CreateTopic(topic) client, _ = kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic, 0) clients = make(map[int]*kafka.Conn) clients[0] = client parts, _ = client.ReadPartitions() } if len(parts) > 1 { //TODO:预先加载 Kafka连接池,可能影响性能,暂不实现!!! } kafkaPool[topic] = clients kafkaParts[topic] = len(parts) } else { max := kafkaParts[topic] num := rand.Intn(max) c, f := kafkaPool[topic][num] if f == true { client = c //add by zhangjun 2020-08-02 判断链接超时,异常关闭 _, err := client.ReadLastOffset() if err != nil { client, _ = kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic, num) kafkaPool[topic][num] = client } } else { client, _ = kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic, num) kafkaPool[topic][num] = client } } //client, _ := kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic , 0) var messages []kafka.Message for no := 0; no < len(datas); no++ { var data, _ = json.Marshal(datas[no]) var msg = kafka.Message{Value: data} messages = append(messages, msg) } client.SetWriteDeadline(time.Now().Add(10 * time.Second)) _, err := client.WriteMessages(messages...) //client.Close() if err == nil { return true, "" } else { fmt.Println("Kafka数据存储失败 :", err.Error()) return false, "Kafka数据存储失败" } } /** * @Author zhangjun * @Description * @Date 2020-07-29 02:28 * @Param * @return TODO:低阶接口调用不支持 ConsumerGroup,需要自己实现 ,复杂度太高 ,不采用!!! **/ func ConsumeLow(topic string) { KafkaClient, _ := kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic, 0) KafkaClient.SetReadDeadline(time.Now().Add(5 * time.Second)) batch := KafkaClient.ReadBatch(10e3, 1e6) // fetch 10KB min, 1MB max for { b := make([]byte, 10e3) // 10KB max per message _, err := batch.Read(b) if err != nil { break } index := bytes.IndexByte(b, 0) fmt.Println(string(b[0:index])) } batch.Close() //KafkaClient.Close() } /** * @Author zhangjun * @Description 获取 Kafka 消息,GoRoutine并发, 线程阻塞式调用 !!! * @Date 2020-07-29 02:31 * @Param * @return **/ func Consume(topic string, group string) { defer func() { if err := recover(); err != nil { fmt.Println("KafkaUtil Consume Panic Recover :", err) } delete(CountTopicProc, topic) }() //add by zhangjun 2020-08-03 //Kafka-Sarama 解析压缩消息 snappy.NewCompressionCodec() r := kafka.NewReader(kafka.ReaderConfig{ Brokers: ConfigUtil.KafkaBrokers, Topic: topic, //Partition: 0, GroupID: group, //必须指定 Group,否则需要指定 Partition!!! MinBytes: 10e3, // 10KB MaxBytes: 10e6, // 10MB //CommitInterval: time.Second,// flushes commits to Kafka every second }) //ticker :=time.NewTicker( 10 * time.Second) //count:=0 //myLoop: for { //10分钟无法ReadMessage ,关闭 Consume 线程 ctx, cancle := context.WithTimeout(context.Background(), time.Second * 600) defer cancle() //select { //case f := <-ChanTopicProc[topic]: // if f == true { // // loc.Lock() // StateTopicProc[topic] = false // loc.Unlock() // // r.Close() // fmt.Printf("Dataex Kafka2ES Process Close ,Topic:%s,ConsumerGroup:%s.\n", topic, group) // // return // } //case <- ticker.C: // if count==0{ // r.Close() // // StateTopicProc[topic][index]=false // return // }else { // count=0 // } //default: //阻塞读取 Kafka msg, err := r.ReadMessage(ctx) //fmt.Println("KafkaUtil ReadMessage :", topic , msg.Offset) if err != nil { fmt.Println("KafkaUtil ReadMessage Error :", err.Error()) break } //TODO:此处不能使用 go,否则速度太快,数据会丢失,以后可以考虑优化ES集群 Kafka2ESTask.Process(topic, msg) _, f := CountTopicProc[topic] if f == false { break } else { loc.Lock() //count++ CountTopicProc[topic]++ loc.Unlock() //fmt.Printf("message at partiton %d offset %d: %s ==> %s\n",msg.Partition, msg.Offset, string(msg.Key), string(msg.Value)) } } r.Close() fmt.Printf("Dataex Kafka2ES Process Stop ,Topic:%s,ConsumerGroup:%s.\n", topic, group) } /** * @Author zhangjun * @Description * @Date 2020-07-27 11:13 * @Param * @return * TODO:高阶接口调用速度太慢,1s,不采用!!! **/ func Provide(topic string, datas []DataEX.KafkaData) (bool, string) { var begin = time.Now() w, f := writerPool[topic] if f == false { w = kafka.NewWriter(kafka.WriterConfig{ Brokers: ConfigUtil.KafkaBrokers, Topic: topic, Balancer: &kafka.Hash{}, //.RoundRobin{},//.LeastBytes{}, }) writerPool[topic] = w } var messages []kafka.Message for no := 0; no < len(datas); no++ { var data, _ = json.Marshal(datas[no]) var msg = kafka.Message{Value: data} messages = append(messages, msg) } fmt.Println("Time 9:", time.Now(), ",spend:", time.Since(begin)) err := w.WriteMessages(context.Background(), messages...) fmt.Println("Time 10:", time.Now(), ",spend:", time.Since(begin)) //w.Close() if err == nil { return true, "" } else { return false, "Kafka数据存储错误" } } /** * @Author zhangjun * @Description * @Date 2020-08-03 08:47 * @Param * @return **/ func InitTopic(topic string) { client, _ := kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic, 0) parts, _ := client.ReadPartitions() offset, _ := client.ReadLastOffset() if len(parts) == 1 && offset == 0 { DeleteTopic(topic) time.Sleep(100 * time.Millisecond) CreateTopic(topic) } } /** * @Author zhangjun * @Description 创建 Kafka Topic * @Date 2020-07-29 02:30 * @Param * @return **/ func CreateTopic(topic string) { KafkaClient, _ := kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], "__consumer_offsets", 0) err := KafkaClient.CreateTopics(kafka.TopicConfig{ NumPartitions: int(ConfigUtil.KafkaParts), ReplicationFactor: int(ConfigUtil.KafkaReply), Topic: topic, }) if err != nil { fmt.Println(err.Error()) } } /** * @Author zhangjun * @Description 删除Kafka Topic * @Date 2020-07-29 02:30 * @Param * @return **/ func DeleteTopic(topic string) { KafkaClient, _ := kafka.DialLeader(context.Background(), "tcp", ConfigUtil.KafkaBrokers[0], topic, 0) err := KafkaClient.DeleteTopics(topic) if err != nil { fmt.Println(err.Error()) } }