它是一个分布式消息系统,由linkedin使用scala编写,用作LinkedIn的活动流(Activity Stream)和运营数据处理管道(Pipeline)的基础。具有高水平扩展和高吞吐量。
定义解释:
1、Java 和 scala都是运行在JVM上的语言。
2、erlang和最近比较火的和go语言一样是从代码级别就支持高并发的一种语言,所以RabbitMQ天生就有很高的并发性能,但是 有RabbitMQ严格按照AMQP进行实现,受到了很多限制。kafka的设计目标是高吞吐量,所以kafka自己设计了一套高性能但是不通用的协议,他也是仿照AMQP( Advanced Message Queuing Protocol 高级消息队列协议)设计的。
3、事物的概念:在数据库中,多个操作一起提交,要么操作全部成功,要么全部失败。举个例子, 在转账的时候付款和收款,就是一个事物的例子,你给一个人转账,你转成功,并且对方正常行收到款项后,这个操作才算成功,有一方失败,那么这个操作就是失败的。
对应消在息队列中,就是多条消息一起发送,要么全部成功,要么全部失败。3个中只有ActiveMQ支持,这个是因为,RabbitMQ和Kafka为了更高的性能,而放弃了对事物的支持 。
4、集群:多台服务器组成的整体叫做集群,这个整体对生产者和消费者来说,是透明的。其实对消费系统组成的集群添加一台服务器减少一台服务器对生产者和消费者都是无感之的。
5、负载均衡,对消息系统来说负载均衡是大量的生产者和消费者向消息系统发出请求消息,系统必须均衡这些请求使得每一台服务器的请求达到平衡,而不是大量的请求,落到某一台或几台,使得这几台服务器高负荷或超负荷工作,严重情况下会停止服务或宕机。
6、动态扩容是很多公司要求的技术之一,不支持动态扩容就意味着停止服务,这对很多公司来说是不可以接受的。
注:
阿里巴巴的Metal,RocketMQ都有Kafka的影子,他们要么改造了Kafka或者借鉴了Kafka,最后Kafka的动态扩容是通过Zookeeper来实现的。
Zookeeper是一种在分布式系统中被广泛用来作为:分布式状态管理、分布式协调管理、分布式配置管理、和分布式锁服务的集群。kafka增加和减少服务器都会在Zookeeper节点上触发相应的事件kafka系统会捕获这些事件,进行新一轮的负载均衡,客户端也会捕获这些事件来进行新一轮的处理。
搭建kafka集群首先要搭建zookeeper集群
Ip |
系统类型 |
|
192.168.1.221 |
centos7 |
Server1 |
192.168.1.138 |
centos7 |
Server2 |
192.168.1.89 |
centos7 |
Server3 |
yum list java*
yum -y install java-1.7.0-openjdk*
#进入文件夹下
cd /use/local/zookeeper
#快照日志
mkdir data
#事务日志
mkdir logs
#命令下载
#进入conf目录
cd /usr/local/zookeeper/apache-zookeeper-3.5.6-bin/conf/
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/data
dataLogDir=/usr/local/zookeeper/logs
clientPort=2181
server.1=192.168.1.221:2888:3888
server.2=192.168.1.138:2888:3888
server.3=192.168.1.89:2888:3888
#server.1 这个1是服务器的标识也可以是其他的数字, 表示这个是第几号服务器,用来标识服务器,这个标识要写到快照目录下面myid文件里
#192.168.7.107为集群里的IP地址,第一个端口是master和slave之间的通信端口,默认是2888,第二个端口是leader选举的端口,集群刚启动的时候选举或者leader挂掉之后进行新的选举的端口默认是3888
#server1
echo "1" > /usr/local/zookeeper/data/myid
#server2
echo "2" > /usr/local/zookeeper/data/myid
#server3
echo "3" > /usr/local/zookeeper/data/myid
#进入到Zookeeper的bin目录下
cd /opt/zookeeper/zookeeper-3.4.6/bin
#启动服务(3台都需要操作)
./zkServer.sh start
#检查服务器状态
./zkServer.sh status
#执行命令jps
Jps
QuorumPeerMain
进入目录下 cd /use/local/kafka
创建消息目录 mkdir logs,主要存放 kafka消息
在线下载 wget https://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.4.0/ kafka_2.13-2.4.0.tgz
解压 tar -zxvf kafka_2.13-2.4.0.tgz
进入到config目录
cd /usr/local/kafka/kafka_2.13-2.4.0/config/
编辑server.poperties
vim server.poperties
broker.id=0 #当前机器在集群中的唯一标识,和zookeeper的myid性质一样
port=9092 #当前kafka对外提供服务的端口默认是9092
host.name=192.168.1.221 #这个参数默认是关闭的,在0.8.1有个bug,DNS解析问题,失败率的问题。
num.network.threads=3 #这个是borker进行网络处理的线程数
num.io.threads=8 #这个是borker进行I/O处理的线程数
log.dirs=/opt/kafka/kafkalogs/ #消息存放的目录,这个目录可以配置为“,”逗号分割的表达式,上面的num.io.threads要大于这个目录的个数这个目录,如果配置多个目录,新创建的topic他把消息持久化的地方是,当前以逗号分割的目录中,那个分区数最少就放那一个
socket.send.buffer.bytes=102400 #发送缓冲区buffer大小,数据不是一下子就发送的,先回存储到缓冲区了到达一定的大小后在发送,能提高性能
socket.receive.buffer.bytes=102400 #kafka接收缓冲区大小,当数据到达一定大小后在序列化到磁盘
socket.request.max.bytes=104857600 #这个参数是向kafka请求消息或者向kafka发送消息的请请求的最大数,这个值不能超过java的堆栈大小
num.partitions=1 #默认的分区数,一个topic默认1个分区数
log.retention.hours=168 #默认消息的最大持久化时间,168小时,7天
message.max.byte=5242880 #消息保存的最大值5M
default.replication.factor=2 #kafka保存消息的副本数,如果一个副本失效了,另一个还可以继续提供服务
replica.fetch.max.bytes=5242880 #取消息的最大直接数
log.segment.bytes=1073741824 #这个参数是:因为kafka的消息是以追加的形式落地到文件,当超过这个值的时候,kafka会新起一个文件
log.retention.check.interval.ms=300000 #每隔300000毫秒去检查上面配置的log失效时间(log.retention.hours=168 ),到目录查看是否有过期的消息如果有,删除
log.cleaner.enable=false #是否启用log压缩,一般不用启用,启用的话可以提高性能
zookeeper.connect=192.168.1.221: 2181,192.168.1.138:2181,192.168.1.89:218 #设置zookeeper的连接端口
#从后台启动Kafka集群(3台都需要启动)
cd /usr/local/kafka/kafka_2.13-2.4.0/bin #进入到kafka的bin目录
#前台启动看看报错不
./kafka-server-start.sh ../config/server.properties
#后台启动
./kafka-server-start.sh -daemon ../config/server.properties
#是否启动
Jps
QuorumPeerMain
Kafka
1.jar包依赖
org.springframework.kafka
spring-kafka
io.springfox
springfox-swagger2
2.8.0
io.springfox
springfox-swagger-ui
2.8.0
2.yml配置
server:
port: 18888
topinfo:
# kafka集群配置 ,bootstrap-servers 是必须的
kafka:
# 生产者的kafka集群地址
bootstrap-servers: 192.168.1.221:9092,192.168.1.138:9092,192.168.1.89:9092
producer:
topic-name: topinfo-01
consumer:
group-id: ci-data
3.yml变实体类
import com.uximt.kaa.kafka.bean.Consumer;
import com.uximt.kaa.kafka.bean.Producer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix = "topinfo.kafka")
@Component
public class KafKaConfiguration {
/**
* @Fields bootstrapServer : 集群的地址
*/
private String bootstrapServers;
private Producer producer;
private Consumer consumer;
public String getBootstrapServers() {
return bootstrapServers;
}
public void setBootstrapServers(String bootstrapServers) {
this.bootstrapServers = bootstrapServers;
}
public Producer getProducer() {
return producer;
}
public void setProducer(Producer producer) {
this.producer = producer;
}
public Consumer getConsumer() {
return consumer;
}
public void setConsumer(Consumer consumer) {
this.consumer = consumer;
}
}
4.topic配置
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.KafkaAdmin;
import java.util.HashMap;
import java.util.Map;
/**
* @date: 2019/12/31 14:36
* @Explanation:
*/
@Configuration
public class KafKaTopicConfig {
@Autowired
private KafKaConfiguration configuration;
/**
*@Description: kafka管理员,委派给AdminClient以创建在应用程序上下文中定义的主题的管理员。
*@return
*/
@Bean
public KafkaAdmin kafkaAdmin() {
Map props = new HashMap<>();
// 配置Kafka实例的连接地址
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, configuration.getBootstrapServers());
KafkaAdmin admin = new KafkaAdmin(props);
return admin;
}
/**
*@Description: kafka的管理客户端,用于创建、修改、删除主题等
*@return
*/
@Bean
public AdminClient adminClient() {
return AdminClient.create(kafkaAdmin().getConfig());
}
/**
* @Description: 创建一个新的 topinfo 的Topic,如果kafka中topinfo 的topic已经存在,则忽略。
* @return
*/
@Bean
public NewTopic topinfo() {
// 主题名称
String topicName = configuration.getProducer().getTopicName();
// 第二个参数是分区数, 第三个参数是副本数量,确保集群中配置的数目大于等于副本数量
return new NewTopic(topicName, 2, (short) 2);
}
}
5.配置kafka生产者消费者
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.*;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import java.util.HashMap;
import java.util.Map;
/**
* @date: 2019/12/31 14:30
* @Explanation: kafka配置类
*/
@Configuration
public class KafKaConfig {
@Autowired
private KafKaConfiguration configuration;
/**
* @Description: 生产者的配置
* @return
*/
public Map producerConfigs() {
Map props = new HashMap<>();
// 集群的服务器地址
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, configuration.getBootstrapServers());
// 消息缓存
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 40960);
// 生产者空间不足时,send()被阻塞的时间,默认60s
props.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, 6000);
// 生产者重试次数
props.put(ProducerConfig.RETRIES_CONFIG, 0);
// 指定ProducerBatch(消息累加器中BufferPool中的)可复用大小
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 4096);
// 生产者会在ProducerBatch被填满或者等待超过LINGER_MS_CONFIG时发送
props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
// key 和 value 的序列化
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
"org.apache.kafka.common.serialization.StringSerializer");
// 客户端id
props.put(ProducerConfig.CLIENT_ID_CONFIG, "producer.client.id.topinfo");
return props;
}
/**
* @Description: 生产者工厂
* @return
*/
@Bean
public ProducerFactory producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
/**
* @Description: KafkaTemplate
* @return
*/
@Bean
public KafkaTemplate kafkaTemplate() {
return new KafkaTemplate(producerFactory());
}
// ------------------------------------------------------------------------------------------------------------
/**
* @Description: 消费者配置
* @return
*/
public Map consumerConfigs() {
Map props = new HashMap();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, configuration.getBootstrapServers());
// 消费者组
props.put(ConsumerConfig.GROUP_ID_CONFIG, configuration.getConsumer().getGroupId());
// 自动位移提交
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
// 自动位移提交间隔时间
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 100);
// 消费组失效超时时间
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10000);
// 位移丢失和位移越界后的恢复起始位置
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
// key 和 value 的反序列化
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
"org.apache.kafka.common.serialization.StringDeserializer");
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
"org.apache.kafka.common.serialization.StringDeserializer");
return props;
}
/**
* @Description: 消费者工厂
* @return
*/
@Bean
public ConsumerFactory consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfigs());
}
/**
* @Description: kafka 监听容器工厂
* @return
*/
@Bean
public KafkaListenerContainerFactory> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>();
// 设置消费者工厂
factory.setConsumerFactory(consumerFactory());
// 要创建的消费者数量(10 个线程并发处理)
factory.setConcurrency(10);
return factory;
}
}
6.swagger配置
package com.uximt.kaa.kafka.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger配置
* @ClassName: Swagger2Config
*/
@Configuration
@EnableSwagger2
@ConditionalOnProperty(name = "enabled", prefix = "swagger", havingValue = "true", matchIfMissing = true)
public class Swagger2Config {
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Springboot Swagger项目文档")
.version("1.0")
.build();
}
}
7.消费者和生产者实体建立
public class Consumer {
private String groupId;
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
}
public class Producer {
private String topicName;
public String getTopicName() {
return topicName;
}
public void setTopicName(String topicName) {
this.topicName = topicName;
}
}
8.建立发送者controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author:hemingzhu
* @date: 2019/12/31 14:38
* @Explanation:
*/
@RestController
@RequestMapping("kafka")
public class TestKafKaProducerController {
@Autowired
private KafkaTemplate kafkaTemplate;
@PostMapping("send")
public String send(String name) {
ListenableFuture> future = kafkaTemplate.send("topinfo", name);
future.addCallback(new ListenableFutureCallback>() {
@Override
public void onSuccess(SendResult result) {
System.out.println("生产者-发送消息成功:" + result.toString());
}
@Override
public void onFailure(Throwable ex) {
System.out.println("生产者-发送消息失败:" + ex.getMessage());
}
});
return "test-ok";
}
9.消费者监听器
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
/**
* @date: 2019/12/31 14:39
* @Explanation:
*/
@Component
public class KafKaConsumer {
private final Logger logger = LoggerFactory.getLogger(KafKaConsumer.class);
/**
* @Description: 可以同时订阅多主题,只需按数组格式即可,也就是用“,”隔开
* @param record
*/
@KafkaListener(topics = { "topinfo" })
public void receive(ConsumerRecord, ?> record) {
logger.info("消费得到的消息---key: " + record.key());
logger.info("消费得到的消息---value: " + record.value().toString());
}
}