一、前几节讲了简单的数据发送接收,忘了说一个点,就是producer发送数据的时候怎么保证数据成功发送到kafka服务器上。
org.apache.kafka.clients.producer.Producer的send()方法有三个重载,其中一个如下:
producer.send(new ProducerRecord<String, Object>(TOPIC, data), new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception e) {
if (metadata!=null) {
//success
System.out.println("发送成功");
}else {
//fail
System.err.println("发送失败");
}
}
});
1、这个方法有一个回调函数,metadata参数不空则表明数据安全发送成功,否则发送失败。
二、kafka集群搭建。
1、kafka集群搭建,推荐一个 https://blog.csdn.net/a807557328/article/details/78272989 讲的挺详细
2、这个博客讲的是不同服务器上集群搭建,如果在同一个服务器上不同磁盘搭建集群,该怎么办?
kafka配置不变,zookeeper的配置ip一样,端口改变即可。
server.1=192.168.131.130:2888:3888
server.2=192.168.131.130:2889:3889
server.3=192.168.131.130:2887:3880
3、如果不想搭建那么多服务,只需搭建搭建单服务,多节点,那么只需在单个kafka中将config/server.properties文件复制几份,如:config/server-1.properties,config/server-2.properties,
修改配置文件,只需把broker.id,listeners,log.dir三个配置项目修改,broker.id一定不能一样,这是节点的唯一标示。
config/server-1.properties:
broker.id=1
listeners=PLAINTEXT://:9093
log.dir=/tmp/kafka-logs-1
修改配置文件config/server-2.properties:
config/server-2.properties:
broker.id=2
listeners=PLAINTEXT://:9094
log.dir=/tmp/kafka-logs-2
搭建完成后,实现Java代码的生产与消费。
1、生产端代码基本不变,bootstrap.servers=192.168.169.128:9092 主要是这个配置项配置集群即可,主要是消费端,有两种不同的消费方式,(1):直接去zookeeper服务中取数据,由zookeeper和kafka交互。(2):直接去对准kafka消费数据。
所需jar包可点此链接下载:
https://download.csdn.net/download/hezhihuahzh/10647743
三、直接去zookeepe集群服务中取数据:
初始化配置文件
public Properties getProperties() {
Properties propertie = new Properties();
String groupId=type + "_" + prjcode;
//准备连接kafka的参数,主要是zookeeper地址和topic名称
propertie.put("zookeeper.connect", "192.168.39.50:2181,192.168.39.50:2182");
//groupId,自己定义
propertie.put(ConsumerConfig.GROUP_ID_CONFIG ,groupId) ;
propertie.put("zookeeper.sync.time.ms", "2000");//zookeeper follower可以落后zookeeper leader的最大时间
propertie.put("auto.offset.reset", "smallest");
//zookeeper中没有初始化offset时,smallest:自动复位offset为smallest的offset
//largest:自动复位offset为largest的offset
//anything else:向consumer抛出异常
propertie.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");//consumer向zookeeper提交offset的频率,单位是秒
propertie.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
propertie.put("topic", "mytopic");
return propertie;
}
接收数据
public void get() {
TOPIC = "mytopic"
String sqlData="";
KafkaStream<String, String> kafkastream =null;
try {
props = getProperties();
ConsumerConnector connector = (ConsumerConnector) Consumer.createJavaConsumerConnector(new kafka.consumer.ConsumerConfig(props));
Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
topicCountMap.put(props.getProperty("topic"), 1);
StringDecoder keyDecoder = new StringDecoder(new VerifiableProperties());
StringDecoder valueDecoder = new StringDecoder(new VerifiableProperties());
Map<String, List<KafkaStream<String, String>>> consumerMap = connector.createMessageStreams(topicCountMap, keyDecoder, valueDecoder);
List<KafkaStream<String, String>> streamList = consumerMap.get(props.getProperty("topic"));
//得到的数据流
kafkastream = streamList.get(0);
} catch (Exception e) {
e.printStackTrace();
}
while (kafkastream.iterator().hasNext()) {
//获取数据
sqlData = kafkastream.iterator().next().message();
}
}
重点:此方式获取数据,好多方法属于过时方法,不能用。比如这个方法:kafkastream.iterator().hasNext(),一旦数据流没有数据,就会停在此处,不会往下执行。说是socket阻塞,反正不好用。就拿接收sql语句入库来说吧,肯定要批量入库。但是批量入库规格订好后,没有达规格且数据流没有数据也要入库,这个时候这个方法就不行了,根本不会往下执行啊,程序停了。怎么办?
还好想到了方法,在项目中跑的是多线程,那么需要再启动配套的监听线程监听批量入库存储容器的状态。
四、顺带说一下监听线程。
1、先是一个被监听线程。
import java.util.ArrayList;
import java.util.List;
public class DoThread implements Runnable{
String name = "";
public List<String> list=new ArrayList<>();
public DoThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
list.add(name+"==="+i);
System.out.println(name+"==="+i);
}
if ("wukong".equals(name)) {
for (int i = 0; i < list.size(); i++) {
list.remove(i);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2、一个监听线程
public class MonitorThread implements Runnable {
DoThread name = null;
public MonitorThread(DoThread name) {
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
System.err.println("====================" + name.name);
System.err.println(name.name + " " + name.list.size());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3、启动类
public class StartThread {
public static void main(String[] args) {
DoThread wukong=new DoThread("wukong");
DoThread bajie=new DoThread("bajie");
DoThread shaceng=new DoThread("shaseng");
new Thread(wukong).start();
new Thread(bajie).start();
new Thread(shaceng).start();
new Thread(new MonitorThread(wukong)).start();
new Thread(new MonitorThread(bajie)).start();
new Thread(new MonitorThread(shaceng)).start();
}
}
这个demo很好的应用到上面的sql监控。当数据流没有数据的时候,被监听线程会阻塞,监听线程监听sql存储容器,只要容器内有数据就执行入库,此时要注意线程安全问题,要加synchronized处理。
五、直接从kafka服务中消费数据,跟之前消费数据一样,唯一变的就是
propertie.put("bootstrap.servers", ConfParser.bootstrap_servers);这个配置项配置成集群即可。其他完全一样。
希望能给大家带来帮助。