分类目录归档:Uncategorized

Java中死锁的定位与修复

  死锁应该可以说是并发编程中比较常见的一种情况,可以说如果程序产生了死锁那将会对程序带来致命的影响;所以排查定位、修复死锁至关重要;
  我们都知道死锁是由于多个对象或多个线程之间相互需要对方锁持有的锁而又没有释放对方所持有的锁,导致双方都永久处于阻塞状态

enter image description here

  如上图所示,线程1持有对象1的锁、线程2持有对象2的锁,持此线程1又想去获取对象2对象锁、线程2想获取对象1对象锁,此时由于双方都没有获取到想要的锁,任务没完成所以也没释放锁,导致一直僵持呢,于是阻塞、产生死锁;

死锁检测

  需要检测死锁肯定要先有死锁出现,下面的demo模拟了一个死锁的产生;

 public class DeadlockDemo extends Thread {
private BaseObj first;
private BaseObj second;

public DeadlockDemo(String name, BaseObj first, BaseObj second) {
    super(name);
    this.first = first;
    this.second = second;
}

public void reentrantLock() throws InterruptedException {
    first.lock();
    System.out.println(String.format("%s 持有:%s 对象锁,等待获取:%s对象锁", this.getName(), first, second));
    second.lock();
    first.unlock();
    second.unlock();
}
@Override
public void run() {
    try {
        reentrantLock();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) throws InterruptedException {
    ObjOne one = new ObjOne();
    ObjTwo two = new ObjTwo();

    DeadlockDemo thread1 = new DeadlockDemo("Thread1", one, two);
    DeadlockDemo thread2 = new DeadlockDemo("Thread2", two, one);

    thread1.start();
    thread2.start();

    thread1.join();
    thread2.join();
}
 }

  运行上面的demo将看到程序被阻塞了,没法结束运行;只看到如下运行结果:
Thread1 持有:objOne 对象锁,等待获取:objTwo对象锁 Thread2 持有:objTwo 对象锁,等待获取:objOne对象锁

enter image description here

  这demo没法结束运行就是由于产生了死锁,两个线程都在相互对待获取对方所持有的对象锁;
  这时候要解决问题就需要找出哪里出现了死锁,通过代码走查通常不容易发现死锁,当然我们这程序很容易发现,因为我们刻意产生的死锁;所以就需要工具来检测死锁,这里可用的工具主要有:jconsole、jvisualvm、jstack等,这些工具其实都是jdk自带的,用法都很类似;

  这里使用jvisualvm来检测当前的demo程序是否产生了死锁;打开jvisualvm连接到当前的应用程序即可看到程序的监控信息,如内存、CPU、性能、GC等等;打开进入线程的tab项查看程序的线程信息,这里很明显的就看到了提示该程序被检测除了死锁!

enter image description here
  点击 线程Dump可以看到线程的堆栈信息,从中可以看到线程的详细信息,并定位死锁;
enter image description here
  从上图可以看到线程产生死锁的原因,Thrad2是等待Thread1、Thread1是等待Thread1, 从下图的堆栈信息即可定位死锁产生的位置;
enter image description here

死锁扫描

  除了发现程序出现问题后我们去扫描死锁外,我们还可以实时的去扫描程序用于发现程序中是否存在死锁;
  JDK提供了MXBean Api可用于扫描程序是否存在死锁,ThreadMXBean提供了findDeadlockedThreads()方法,可以用于找到产生死锁的线程;这里在上面的demo程序中添加一个方法用于扫描死锁,虽然这种方法可以扫描到死锁但是由于每次都对线程打快照对程序性能会有比较大的影响,所以慎用;

 public static void scanDeadLock() {
    ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
    Runnable runnable = () -> {
        long[] ids = mxBean.findDeadlockedThreads();
        System.out.println("扫描死锁...");
        if (ids != null) {
            ThreadInfo[] threadInfos = mxBean.getThreadInfo(ids);
            for (ThreadInfo threadInfo : threadInfos) {
                System.out.println(threadInfo);
            }
        }
    };

    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(5, Executors.defaultThreadFactory());
    executorService.scheduleAtFixedRate(runnable, 1, 5, TimeUnit.SECONDS);
}

enter image description here

避免死锁

  解决死锁最好的方法就是避免死锁了,比如上面的demo我们可以把直接使用无参数的lock()方法换为使用tryLock方法,tryLock还可以指定获取锁超时时间,到了超时时间还没获得到锁就会放弃获取锁,当然还有其它方法可以避免死锁;
  1、避免使用多个锁、长时间持有锁;
  2、设计好多个锁的获取顺序
  3、使用带超时的获取锁方法

enter image description here

Kafka监控框架介绍

  前段时间在想Kafka怎么监控、怎么知道生产的消息或消费的消费是否有丢失,目前有几个开源的Kafka监控框架这里整理了下,不过这几个框架都有各自的问题侧重点不一样;

1、Kafka Monitor
2、Availability-Monitor-for-Kafka
3、Kafka Web Console
4、Kafka Manage
5、KafkaOffsetMonitor

kafka-monitor

git clone https://github.com/linkedin/kafka-monitor.git  
cd kafka-monitor  
./gradlew jar    //下载依赖,这个会比较久编译源码  

启动KafkaMonitor

./bin/kafka-monitor-start.sh config/kafka-monitor.properties  

enter image description here
  启动端到端监控Kafka集群:

 ./bin/end-to-end-test.sh --topic test --broker-list localhost:9092 --zookeeper localhost:2181

  打开监控页面:http://localhost:8000/index.html 页面显示 kafka实时的度量监控值(例如,服务可用性,消息丢失率)动态图显示
  enter image description here

  Kafka-monitor把度量监控信息写入JMX中,并使用jolokia框架所以可以通过http请求获取监控的度量值;

curl localhost:8778/jolokia/read/kmf.services:type=produce-service,name=*/produce-availability-avg

  Kafka-monitor与Kafka为同根同源的都是LinkedIn公司开源的不过Kafka-minitor刚开源没半年所有功能相对于其他监控框架来说比较简单;
  kafka-monitor启动后会启动一个produce、一个consume,broker过时时间为10分钟; 用于捕获服务的可用性、消息丢失率、延迟率等,可监控集群与单个kafka;
  kafka-monitor默认自动创建监控topic,可以修改为已经存在的topic,自动增加partition,确保监控topic的partition# >= broker#
  produce用于生成消息到kafka,并产生生成速率、可用性度量数据
  consume从kafka中消费消息,并产生消息丢失率、消息重复率、端到端延迟 依赖生产服务来提供消息内嵌的消息序列号和时间戳。

KafkaManager

  KafkaManager为Yahoo开源的KafkaManager管理工具, 可管理多个集群; 能够更容易地检查集群的状态, 能够创建主题, 执行首选的副本选择,能够基于集群当前的状态生成分区分配,并基于生成的分配执行分区的重分配 检查集群状态可以发现集群中主题分布不均匀、分区分布不均匀等,

KafkaManager 编译生成,下载依赖会比较久
enter image description here

Availability Monitor for Kafka

  Availability Monitor for Kafka微软开源的Kafka可用性、延迟性的监控框架,提供JMX接口,并可存入SqlServer;

  AvailabilityMonitor可监控发送与读取数据的可用性、延迟;
  Producer可用性=所有主题和分区成功发送消息数/尝试发送消息数
  Consumer可用性=所有主题和分区消息成功读取数/消息尝试读取数
  通过往Producer发送数据测试Producer的可用性与延迟,消息以CanaryMessage_为前缀,可以通过producerProperties.json修改;

  通过从所有分区的尾部读取数据来衡量Consumer的可用性与延迟

enter image description here

enter image description here