Kafka学习之三 Kafka线上环境集群部署及客户端应用

本文介绍如何构建Zookeeper集群及Kafka集群,并详细展示了如何使用Java编写Producer和Consumer进行消息的发送与接收。

1、Zookeeper集群构建

我们有3个zk实例,分别为zk-0,zk-1,zk-2;如果你仅仅是测试使用,可以使用1个zk实例.(本示例基于分布式部署)

    1) zk-0

    调整配置文件:

clientPort=2181  

dataDir=/opt/zookeeper-3.4.6/data

server.0=10.10.73.53:2888:3888  

server.1=10.10.73.54:2888:3888    

server.2=10.10.73.58:2888:3888   

##只需要修改上述配置,其他配置保留默认值

启动zookeeper

./zkServer.sh start

 

2) zk-1

    调整配置文件(其他配置和zk-0一只):

clientPort=2182  

##只需要修改上述配置,其他配置保留默认值  

    启动zookeeper

./zkServer.sh start       

 

 3) zk-2

    调整配置文件(其他配置和zk-0一只):

clientPort=2183  

##只需要修改上述配置,其他配置保留默认值  

启动zookeeper

./zkServer.sh start

2. Kafka集群构建

    因为Broker配置文件涉及到zookeeper的相关约定,因此我们先展示broker配置文件.我们使用2个kafka broker来构建这个集群环境,分别为kafka-0,kafka-1.

    1) kafka-0(10.10.73.58)

在config目录下修改配置文件为:

broker.id=0

port=9092

num.network.threads=2

num.io.threads=2

socket.send.buffer.bytes=1048576

socket.receive.buffer.bytes=1048576

socket.request.max.bytes=104857600

log.dir=/opt/env/kafka_2.11-0.9.0.1/logs

num.partitions=2

log.flush.interval.messages=10000

log.flush.interval.ms=1000

log.retention.hours=168

#log.retention.bytes=1073741824  

log.segment.bytes=536870912

##replication机制,让每个topic的partitions在kafka-cluster中备份2个  

##用来提高cluster的容错能力..  

default.replication.factor=1

log.cleanup.interval.mins=10

zookeeper.connect=10.10.73.53:2181,10.10.73.54:2181,10.10.73.58:2181

zookeeper.connection.timeout.ms=1000000

 

启动kafka broker:

> JMS_PORT=9997 bin/kafka-server-start.sh config/server.properties &  

    因为zookeeper环境已经正常运行了,我们无需通过kafka来挂载启动zookeeper.如果你的一台机器上部署了多个kafka broker,你需要声明JMS_PORT.

 

2) kafka-1(10.10.73.53)

broker.id=1  

port=9092  

##其他配置和kafka-0保持一致  

    然后和kafka-0一样执行打包命令,然后启动此broker.

> JMS_PORT=9998 bin/kafka-server-start.sh config/server.properties &  

仍然可以通过如下指令查看topic的"partition"/"replicas"的分布和存活情况.

Kafka集群中broker.id=1参数的value不能重复

 

3Java端的Producer

kafka-producer.properties配置文件

##metadata.broker.list=127.0.0.1:9092,127.0.0.1:9093

##,127.0.0.1:9093

metadata.broker.list=10.10.73.58:9092,10.10.73.53:9092

producer.type=sync

compression.codec=0

serializer.class=kafka.serializer.StringEncoder

#batch.num.messages=100

 

Java客户端程序:

public class KafkaProducerClient {

 

private Producer<String, String> inner;

private String brokerList;//for metadata discovery,spring setter

private String location = "kafka/kafka-producer.properties";//spring setter

private String defaultTopic;//spring setter

 

public void setBrokerList(String brokerList) {

this.brokerList = brokerList;

}

 

public void setLocation(String location) {

this.location = location;

}

 

public void setDefaultTopic(String defaultTopic) {

this.defaultTopic = defaultTopic;

}

 

public KafkaProducerClient(){}

public void init() throws Exception {

Properties properties = new Properties();

properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(location));

if(brokerList != null) {

properties.put("metadata.broker.list", brokerList);

}

 

ProducerConfig config = new ProducerConfig(properties);

inner = new Producer<String, String>(config);

}

 

public void send(String message){

send(defaultTopic,message);

}

public void send(Collection<String> messages){

send(defaultTopic,messages);

}

public void send(String topicName, String message) {

if (topicName == null || message == null) {

return;

}

KeyedMessage<String, String> km = new KeyedMessage<String, String>(topicName,message);

inner.send(km);

}

 

public void send(String topicName, Collection<String> messages) {

if (topicName == null || messages == null) {

return;

}

if (messages.isEmpty()) {

return;

}

List<KeyedMessage<String, String>> kms = new ArrayList<KeyedMessage<String, String>>();

int i= 0;

for (String entry : messages) {

KeyedMessage<String, String> km = new KeyedMessage<String, String>(topicName,entry);

kms.add(km);

i++;

if(i % 20 == 0){

inner.send(kms);

kms.clear();

}

}

if(!kms.isEmpty()){

inner.send(kms);

}

}

 

public void close() {

inner.close();

}

 

/**

 * @param args

 */

public static void main(String[] args) {

KafkaProducerClient producer = null;

try {

producer = new KafkaProducerClient();

producer.init();

//producer.setBrokerList("");

int i = 0;

while (true) {

producer.send("test-topic", "this is a sample" + i);

i++;

Thread.sleep(2000);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

if (producer != null) {

producer.close();

}

}

}

}

4、Java端的Consumer

kafka-consumer.properties配置文件

##zookeeper.connect=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183

##,127.0.0.1:2182,127.0.0.1:2183

# timeout in ms for connecting to zookeeper

zookeeper.connect=10.10.73.53:2181,10.10.73.54:2181,10.10.73.58:2181

zookeeper.connectiontimeout.ms=100000

#consumer group id

group.id=test-group

#consumer timeout

#consumer.timeout.ms=5000

 

Java客户端程序:

public class KafkaConsumerClient {

 

private String groupid; //can be setting by spring

private String zkConnect;//can be setting by spring

private String location = "kafka/kafka-consumer.properties";//配置文件位置

private String topic;

private int partitionsNum;

private MessageExecutor executor; //message listener

private ExecutorService threadPool;

private ConsumerConnector connector;

private Charset charset = Charset.forName("utf8");

 

public void setGroupid(String groupid) {

this.groupid = groupid;

}

 

public void setZkConnect(String zkConnect) {

this.zkConnect = zkConnect;

}

 

public void setLocation(String location) {

this.location = location;

}

 

public void setTopic(String topic) {

this.topic = topic;

}

 

public void setPartitionsNum(int partitionsNum) {

this.partitionsNum = partitionsNum;

}

 

public void setExecutor(MessageExecutor executor) {

this.executor = executor;

}

 

public KafkaConsumerClient() {}

 

//init consumer,and start connection and listener

public void init() throws Exception {

if(executor == null){

throw new RuntimeException("KafkaConsumer,exectuor cant be null!");

}

Properties properties = new Properties();

properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(location));

if(groupid != null){

properties.put("groupid", groupid);

}

if(zkConnect != null){

properties.put("zookeeper.connect", zkConnect);

}

ConsumerConfig config = new ConsumerConfig(properties);

 

connector = Consumer.createJavaConsumerConnector(config);

Map<String, Integer> topics = new HashMap<String, Integer>();

topics.put(topic, partitionsNum);

Map<String, List<KafkaStream<byte[], byte[]>>> streams = connector.createMessageStreams(topics);

List<KafkaStream<byte[], byte[]>> partitions = streams.get(topic);

threadPool = Executors.newFixedThreadPool(partitionsNum * 2);

//start

for (KafkaStream<byte[], byte[]> partition : partitions) {

threadPool.execute(new MessageRunner(partition));

}

}

 

public void close() {

try {

threadPool.shutdownNow();

} catch (Exception e) {

//

} finally {

connector.shutdown();

}

 

}

 

class MessageRunner implements Runnable {

private KafkaStream<byte[], byte[]> partition;

 

MessageRunner(KafkaStream<byte[], byte[]> partition) {

this.partition = partition;

}

 

public void run() {

ConsumerIterator<byte[], byte[]> it = partition.iterator();

while (it.hasNext()) {

// connector.commitOffsets();手动提交offset,当autocommit.enable=false时使用

MessageAndMetadata<byte[], byte[]> item = it.next();

try{

executor.execute(new String(item.message(),charset));// UTF-8,注意异常

}catch(Exception e){

//

}

}

}

public String getContent(Message message){

            ByteBuffer buffer = message.payload();

            if (buffer.remaining() == 0) {

                return null;

            }

            CharBuffer charBuffer = charset.decode(buffer);

            return charBuffer.toString();

}

}

 

interface MessageExecutor {

 

public void execute(String message);

}

 

/**

 * @param args

 */

public static void main(String[] args) {

KafkaConsumerClient consumer = null;

try {

MessageExecutor executor = new MessageExecutor() {

 

public void execute(String message) {

System.out.println(message);

}

};

consumer = new KafkaConsumerClient();

consumer.setTopic("test-topic");

consumer.setPartitionsNum(2);

consumer.setExecutor(executor);

consumer.init();

Thread.sleep(10000);

} catch (Exception e) {

e.printStackTrace();

} finally {

 if(consumer != null){

 consumer.close();

 } } } }

学习参考:

http://www.infoq.com/cn/articles/kafka-analysis-part-1

http://flychao88.iteye.com/category/350737

http://shift-alt-ctrl.iteye.com/blog/1930791

http://www.tuicool.com/articles/mErEZn


1/kafka是一个分布式的消息缓存系统 2/kafka集群中的服务器都叫做broker 3/kafka有两类客户端,一类叫producer(消息生产者),一类叫做consumer(消息消费者),客户端和broker服务器之间采用tcp协议连接 4/kafka中不同业务系统的消息可以通过topic进行区分,而且每一个消息topic都会被分区,以分担消息读写的负载 5/每一个分区都可以有多个副本,以防止数据的丢失 6/某一个分区中的数据如果需要更新,都必须通过该分区所有副本中的leader来更新 7/消费者可以分组,比如有两个消费者组A和B,共同消费一个topic:order_info,A和B所消费的消息不会重复 比如 order_info 中有100个消息,每个消息有一个id,编号从0-99,那么,如果A组消费0-49号,B组就消费50-99号 8/消费者在具体消费某个topic中的消息时,可以指定起始偏移量 每个partition只能同一个group中的同一个consumer消费,但多个Consumer Group可同时消费同一个partition。 n个topic可以被n个Consumer Group消费,每个Consumer Group有多个Consumer消费同一个topic Topic在逻辑上可以被认为是一个queue,每条消费都必须指定它的Topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件。若创建topic1和topic2两个topic,且分别有13个和19个分区 Kafka的设计理念之一就是同时提供离线处理和实时处理。根据这一特性,可以使用Storm这种实时流处理系统对消息进行实时在线处理,同时使用Hadoop这种批处理系统进行离线处理,还可以同时将数据实时备份到另一个数据中心,只需要保证这三个操作所使用的Consumer属于不同的Consumer Group即可。
<think> 嗯,用户想了解如何在线上订单服务集群部署中查找耗时方法,这其实是个很典型的性能排查问题。参考之前的引用内容,用户提供的资料覆盖了排查步骤、工具使用和配置优化等方向,尤其是引用[1][5]特别相关。 用户可能是个Java后端工程师,最近遇到了订单服务的性能问题。ta提到“集群部署”,说明环境复杂度较高,需要分布式排查手段。从ta简洁的提问方式看,可能正在紧急处理线上问题,情绪上有些焦虑,需要快速可落地的方案。 核心思路该从观测、分析、验证三个层次展开:首先用监控工具定位可疑服务节点,再通过链路追踪和线程分析深入方法级耗时,最后结合日志和本地调试确认根因。特别要注意集群环境特有的干扰因素,比如负载均衡或网络分区。 引用[1]提到的分布式追踪和日志分析是关键突破口,可以结合引用[5]的JVM排查命令。对于订单这类核心服务,还要考虑数据库慢查询的影响(引用[1]的数据库检查部分)。用户提供的引用[4]关于超时设置的代码样例虽然不直接相关,但提醒了配置参数优化的重要性。 需要强调工具链的组合使用:比如Arthas的trace命令观测方法耗时时,要配合jstack看线程状态(引用[5]示例),避免把阻塞等待误判为CPU耗时。集群环境下还要注意采样率对统计的影响,这点在引用[3]的Kafka部署规范里也有体现。 </think> 在线上订单服务集群部署中查找耗时方法,可参考以下结构化排查方案: --- ### 📊 一、分布式追踪定位慢请求(核心工具) 1. **集成链路追踪工具**(如SkyWalking、Zipkin) - 在服务入口添加TraceID,自动追踪跨服务调用链 - 识别 **P99/P95耗时突增** 的接口(如图) ```java // SkyWalking 自动埋点示例(通过Agent实现) @GetMapping("/createOrder") public Response createOrder(@RequestBody OrderRequest request) { // 业务逻辑自动被追踪 } ``` - 重点关注$$ \textcolor{red}{\text{Span耗时}>\text{200ms}} $$ 的方法节点 [^1] 2. **分析调用链火焰图** - 定位具体服务节点中的高耗时方法(如支付校验方法`PaymentService.validate()`) - 检查是否存在嵌套循环/远程调用阻塞 --- ### ⚙️ 二、JVM层面深度剖析(单节点) 1. **Arthas实时诊断** ```bash # 1. 附加到目标Java进程 $ arthas-boot.jar # 2. 监控方法执行耗时(采样统计) [arthas@]$ trace com.service.OrderService processOrder '#cost > 100' ``` - 输出示例: ``` `---ts=2023-05-01 10:00:00;thread_name=http-nio-8080-exec-1;id=1e4f3;is_daemon=true;priority=5;TCCL=jdk.loader.WebAppClassLoader `---[102ms] com.service.OrderService:processOrder() +---[15%] 数据库查询: OrderDao.getDetail() `---[70%] 风控校验: RiskService.check() <-- 瓶颈点! ``` 2. **JStack分析线程状态** ```bash $ jstack -l <pid> > thread_dump.log ``` - 检查阻塞线程(`BLOCKED`/`WAITING`状态)[^5] - 结合`grep "nid=0x1e4f" thread_dump.log` 定位对代码行 --- ### 📈 三、监控系统多维分析 | 监控维度 | 工具示例 | 关键指标 | |----------------|-------------------------|-----------------------------| | **应用性能** | Prometheus+Grafana | 方法QPS/平均耗时/错误率 | | **数据库** | Slow Query Log | SQL执行时间>500ms的记录 | | **JVM性能** | VisualVM | GC耗时/堆内存使用率 | | **系统资源** | Nagios | CPU 软中断/磁盘IO等待 | > 📌 **典型耗时场景**: > - 数据库连接池耗尽(表现为`getConnection()`阻塞) > - 第三方HTTP调用未设超时(参考引用[4]的超时配置) > - 锁竞争激烈(`synchronized`代码段过长) --- ### 🔍 四、日志精准定位 ```java // 在关键方法添加耗时日志 public Order processOrder(Order order) { long start = System.currentTimeMillis(); // 业务逻辑... log.info("processOrder cost={}ms", System.currentTimeMillis()-start); } ``` **日志分析技巧**: ```bash # 检索特定耗时阈值日志 $ grep "cost=[5-9][0-9][0-9]" order-service.log | head -n 50 ``` --- ### 🛠️ 五、优化与验证 1. **针对性优化** - 异步化:将非核心逻辑(如通知发送)提交到线程池 - 缓存优化:对高频查询结果缓存(Redis/Memcached) ```java @Cacheable("orderCache") public Order getOrderById(Long id) { ... } ``` 2. **压测验证** - 使用JMeter模拟集群流量 - 对比优化前后TP99耗时曲线 --- 🔗 **相关实践参考** > 某电商平台通过SkyWalking定位到优惠计算服务的方法级瓶颈,将订单创建耗时从450ms降至120ms [^2]。 --- ### ⁉️ 相关问题 1. 如何区分网络延迟和方法执行耗时? 2. 线程池配置不当会导致哪些性能问题? 3. 数据库慢查询如何快速定位? 4. 分布式追踪工具在微服务中的埋点原理是什么? [^1]: 分布式追踪与日志分析流程 [^2]: 微服务治理中的性能优化案例 [^4]: HTTP客户端超时配置最佳实践 [^5]: JVM线程状态分析方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值