c#连接kafka_Kafka基础教程(三):C#使用Kafka消息队列

接上篇Kafka的安装,我安装的Kafka集群地址:192.168.209.133:9092,192.168.209.134:9092,192.168.209.135:9092,所以这里直接使用这个集群来演示

首先创建一个项目,演示采用的是控制台(.net core 3.1),然后使用Nuget安装 Confluent.Kafka 包:

消息发布

先直接上Demo

static void Main(string[] args)

{

ProducerConfig config= newProducerConfig();

config.BootstrapServers= "192.168.209.133:9092,192.168.209.134:9092,192.168.209.135:9092";var builder = new ProducerBuilder(config);

builder.SetValueSerializer(new KafkaConverter());//设置序列化方式

var producer =builder.Build();

producer.Produce("test", new Message() { Key = "Test", Value = "hello world"});Console.ReadKey();

}

上述代码执行后,就可以使用上一节提到的kafkatool工具查看到消息了。

1、消息发布需要使用生产者对象,它由ProducerBuilder类构造,有两个泛型参数,第一个是路由Key的类型,第二个是消息的类型,开发过程中,我们多数使用ProducerBuilder或者ProducerBuilder。

2、ProducerBuilder在实例化时需要一个配置参数,这个配置参数是一个集合(IEnumerable>),ProducerConfig其实是实现了这个集合接口的一个类型,在旧版本的Confluent.Kafka中,是没有这个ProducerConfig类型的,之前都是使用Dictionary来构建ProducerBuilder,比如上面的Demo,其实也可以写成:

static void Main(string[] args)

{Dictionary config = new Dictionary();

config["bootstrap.servers"]= "192.168.209.133:9092,192.168.209.134:9092,192.168.209.135:9092";var builder = new ProducerBuilder(config);

builder.SetValueSerializer(new KafkaConverter());//设置序列化方式

var producer =builder.Build();

producer.Produce("test", new Message() { Key = "Test", Value = "hello world"});

Console.ReadKey();

}

这两种方式是一样的效果,只是ProducerConfig对象最终也是生成一个KeyValuePair集合,ProducerConfig中的属性都会有一个Key与它对应,比如上面的ProducerConfig的BootstrapServers属性最终会映射成bootstrap.servers,表示Kafka集群地址,多个地址之间使用逗号分隔。

3、Confluent.Kafka还要求提供一个实现了ISerializer或者IAsyncSerializer接口的序列化类型,比如上面的Demo中的KafkaConverter:

public class KafkaConverter : ISerializer{///

///序列化数据成字节///

///

///

///

public byte[] Serialize(objectdata, SerializationContext context)

{var json =JsonConvert.SerializeObject(data);returnEncoding.UTF8.GetBytes(json);

}}

这里我采用的是Json格式序列化,需要使用Nuget安装Newtonsoft.Json。

4、发布消息使用Produce方法,它有几个重载,还有几个异步发布方法。第一个参数是topic,如果想指定Partition,需要使用TopicPartition对象,第二个参数是消息,它是Message类型,Key即路由,Value就是我们的消息,消息会经过ISerializer接口序列化之后发送到Kafka,第三个参数是Action>类型的委托,它是异步执行的,其实是发布的结果通知。

消息消费

先直接上Demo

static void Main(string[] args)

{

ConsumerConfig config= newConsumerConfig();

config.BootstrapServers= "192.168.209.133:9092,192.168.209.134:9092,192.168.209.135:9092";

config.GroupId= "group.1";

config.AutoOffsetReset=AutoOffsetReset.Earliest;

config.EnableAutoCommit= false;

var builder = new ConsumerBuilder(config);

builder.SetValueDeserializer(new KafkaConverter());//设置反序列化方式

var consumer =builder.Build();

consumer.Subscribe("test");//订阅消息使用Subscribe方法//consumer.Assign(new TopicPartition("test", new Partition(1)));//从指定的Partition订阅消息使用Assign方法

while (true)

{var result =consumer.Consume();

Console.WriteLine($"recieve message:{result.Message.Value}");

consumer.Commit(result);//手动提交,如果上面的EnableAutoCommit=true表示自动提交,则无需调用Commit方法

}

}

1、和消息发布一样,消费者的构建是通过ConsumerBuilder对象来完成的,同样也有一个ConsumerConfig配置对象,它在旧版本中也是不存在的,旧版本中也是使用Dictionary来实现的,比如上面的例子等价于:

static void Main(string[] args)

{Dictionary config = new Dictionary();

config["bootstrap.servers"] = "192.168.209.133:9092,192.168.209.134:9092,192.168.209.135:9092";

config["group.id"] = "group.1";

config["auto.offset.reset"] = "earliest";

config["enable.auto.commit"] = "false";var builder = new ConsumerBuilder(config);

builder.SetValueDeserializer(new KafkaConverter());//设置反序列化方式

var consumer =builder.Build();

consumer.Subscribe("test");//订阅消息使用Subscribe方法//consumer.Assign(new TopicPartition("test", new Partition(1)));//从指定的Partition订阅消息使用Assign方法

while (true)

{var result =consumer.Consume();

Console.WriteLine($"recieve message:{result.Message.Value}");

consumer.Commit(result);//手动提交,如果上面的EnableAutoCommit=true表示自动提交,则无需调用Commit方法

}

}

实际上,它和ProducerConfig一样也是一个KeyValuePair集合,它的属性最终

这里顺带提一下这个例子用到的几个配置:

BootstrapServers:Kafka集群地址,多个地址之间使用逗号分隔。

GroupId:消费者的Group,注意了,Kafka以Group的形式消费消息,一个消息只会被同一Group中的一个消费者消费,另外,一般的,同一Group中的消费者应该实现相同的逻辑

EnableAutoCommit:是否自动提交,如果设置成true,那么消费者接收到消息就相当于被消费了,我们可以设置成false,然后在我们处理完逻辑之后手动提交。

AutoOffsetReset:自动重置offset的行为,默认是Latest,这是kafka读取数据的策略,有三个可选值:Latest,Earliest,Error,个人推荐使用Earliest

关于AutoOffsetReset配置,这里再提一点

Latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据

Earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费

Error:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常

上面几句话说得有点蒙,举个例子:

当有一个消费者连接到Kafka,那这个消费者该从哪个位置开始消费呢?

首先,我们知道Kafka的消费者以群组Group的形式去消费,Kafka会记录每个Group在每个Partition中的到哪个位置,也就是offset。

当有消费者连接到Kafka要消费消息是,如果这个消费者所在的群组Group之前有消费过并提交过offset(也就是存在offset记录),那么这个消费者就从这个offset的位置开始消费,这一点Latest,Earliest,Error三个配置的行为是一样的。

但是如果连接的消费者所在的群组是一个新的群组时(也就是不存在offset记录),Latest,Earliest,Error三个配置表现出不一样的行为:

Latest:从连接到Kafka那一刻开始消费之后产生的消息,之前发布的消息不在消费,这也是默认的行为

Earliest:从offset最小值(如果消息全部有效的话,那就是最开头)处开始消费,也就是说会消费连接到Kafka之前发布的消息

Error:简单暴力的抛出异常

2、生产消息需要序列化,消费消息就需要反序列化了,我们需要提供一个实现了IDeserializer接口的类型,比如上面的例子采用Json序列化:

public class KafkaConverter : IDeserializer{///

///反序列化字节数据成实体数据///

///

///

///

public object Deserialize(ReadOnlySpan data, boolisNull, SerializationContext context)

{if (isNull) return null;var json =Encoding.UTF8.GetString(data.ToArray());try{returnJsonConvert.DeserializeObject(json);

}catch{returnjson;

}

}

}

3、Kafka是发布/订阅方式的消息队列,Confluent.Kafka提供了两个订阅的方法:Subscribe和Assign

Subscribe:从一个或者多个topic订阅消息

Assign:从一个或者多个topic的指定partition中订阅消息

另外,Confluent.Kafka还提供了两个取消订阅的方法:Unsubscribe和Unassign

4、获取消息使用Consume方法,方法返回一个ConsumeResult对象,我们要的消息就在这个对象中,它还包含offset等等其他信息。

另外,Consume方法会导致当前线程阻塞,直至有获取到消息可以消费,或者超时。

5、如果我们创建消费者时,设置了EnableAutoCommit=false,那么我们就需要手动调用Commit方法提交消息,切记。

完整的Demo例子

上面有提到,生产消息需要一个实现

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值