Vertx实战之如何追踪异步函数调用

本文介绍了在Vertx中如何追踪异步函数调用,包括同步和基于JDK的异步场景,重点讨论了EventBus的消息传递和Netty的异步IO操作。通过绑定唯一ID到请求调用,实现跨线程的追踪。同时,文章提到了在类加载和字节码修改中遇到的问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

日常开发中我们经常需要处理各种系统问题,而这些系统问题通常由一些非预期的因素引起(比如非预期的输入,内存不够,网络波动等)。此时就需要知道

  1. 本次系统问题影响了谁?
  2. 如果本次系统问题是因为非预期的输入而导致的,那么这个非预期的输入是什么?

上述两点在同步编程里可以通过全局try-catch来实现。但在异步编程里该怎么办呢?


思路

我的想法是绑定一个唯一id到一次完整的请求调用中(这个完整取决于我们想要监控的范围),无论程序执行到何处,我们总能拿到与请求一一对应的调用上下文的id。

我将分两种场景进行讨论。一种是同步或基于jut下的线程池类进行异步调用的应用程序(如基于spring5之前的应用程序)。另外一种基于vertx(底层基于netty)的应用程序。


技术栈

vert.x 4netty 4alibaba TransmittableThreadLocaljboss javassist、hotspot类加载&方法调用


1. 同步or基于jdk的异步

关于这种场景,有以下几种实现:

  1. 程序中的函数调用是完全同步的

    则可通过java.lang.ThreadLocal绑定一个唯一id到当前线程。入口处生成,出口处销毁。
    如果我们用的是logback,也可通过 org.slf4j.MDC 来实现这个功能。其内部也是基于ThreadLocal。

  2. 程序中函数调用是异步的,异步通过新建java.lang.Thread 的方式实现。

    这里可通过java.lang.InheritableThreadLocal 绑定一个唯一id到当前线程。Thread的构造函数里会自动把通过InheritableThreadLocal 绑定到当前线程的数据拷贝到这个正在创建的Thread 里。所以,只要这个Thread 是在我们需要监控的区域(当前线程通过InheritableThreadLocal**绑定了id后)**创建的,就能实现这个唯一id的跨线程传递。

  3. 程序中函数调用是异步的,异步通过java.util.concurrent 下的线程池类实现。

    由于线程池中的线程是可以复用的,所以,如果我们往线程池中丢任务时,有两种情况:

    1. 线程池创建新的线程来执行该任务(比如线程池中的线程数<coreSize)。这种情况下InheritableThreadLocal 依然是有效的。
    2. 线程池把任务分配给了已有线程(比如线程池中的线程数≥coreSize并且待执行任务队列没有填满)。这种情况下InheritableThreadLocal 是无法生效的(线程池中的存活线程会循环拉取等待任务队列中的task去执行,而这个过程是没有InheritableThreadLocal 拷贝的)。所以,这里可以用阿里的[TransmittableThreadLocal](https://github.com/alibaba/transmittable-thread-local) 组件来实现这个唯一id在线程池中的传递(其核心原理是在Runnable 中用一个变量来存储当前线程的ThreadLocal值,当线程执行此Runnable时再拿出来)。

实现思路总结

异步环境下的关键在于如何跨线程传递ThreadLocal的值


2. Vertx 中的实现

vertx是一个类似于spring系列的用于构建分布式微服务的框架。基于vertx构建的应用程序最大的特点是无阻塞&全异步。

vertx里的异步主要分两种。一种是在eventbus上传递消息的异步。另一种是基于netty的io操作异步

1. eventbus上异步传递消息

先来看一个简单的示例程序

	static final String ADDRESS_1 = "address1";
  static final String MESSAGE_1 = "message1";
  static final String REPLY_MESSAGE_1 = "replyMessage1";

	public static void sendMsgByEventbus() {
   
   
        //初始化一个具有默认设置的vertx实例
        Vertx vertx = Vertx.vertx();

        //注册一个handler/consumer到eventbus
        vertx.eventBus().consumer(ADDRESS_1, event -> {
   
   
            log.info("receive msg:{}", event.body());
            event.reply(REPLY_MESSAGE_1);
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值