<!-- If you need to report a security issue please visit https://github.com/apache/dubbo/security/policy -->

- [x] I have searched the [issues](https://github.com/apache/dubbo/issues) of this repository and believe that this is not a duplicate.

### Environment

* Dubbo version: 3.2.0, 2.7.x, etc
* Operating System version: Windows 10（irrelevant）
* Java version: java 8（irrelevant）

### Steps to reproduce this issue

1. write the interface implementation class on the provider side, use RpcContext.startAsync() for asynchronous operations. 
2. On the consumer side, call this interface in a loop and verify whether the returned result corresponds to the requested parameter. 
3. Then, use the telnet invoke command to call this interface and observe whether the consumer side encounters an incorrect request and result pairing. 
4. Then close the consumer, and observe whether the AsyncContext value returned by RpcContext.startAsync() on the provider side is the same object multiple times during the telnet invoke execution.



### Expected Behavior

1. the consumer always get the right response
2. RpcContext.startAsync() returns different instance per each request

### Actual Behavior

1. the consumer get mismatch response（only appeared in versions before 3.x）
2. RpcContext.startAsync() returns the same object in multiple requests

---

When using RpcContext.startAsync() to return results asynchronously on the provider side, the context (RpcContext, AsyncContext, etc.) is stored in ThreadLocal by default. Under normal circumstances, the ContextFilter will clear the data in ThreadLocal. However, when the request is triggered by telnet invoke, ContextFilter will not be executed, resulting in ThreadLocal not being cleaned up. When this thread handles the next request, it obtains the context of the previous request, leading to problems.


In versions before 3.x, this can cause consumers to get the wrong data. For example, request B gets the result of request A (same interface, same method), or a request for calling interface C gets the result of interface D (different interface, different method).


In the 3.x version, since the invoke command is assigned to QoS and executed in a separate thread pool, therefore, there will be no issue of response pairing errors with normal consumer requests(if they can be configured to share the same thread pool later, there is still this risk). However, it is still unreasonable to not clean up ThreadLocal after use.


In addition, telnet invoke cannot obtain the results asynchronously, which brings trouble to the operation and maintenance. Therefore, it can be optimized to obtain the results synchronously, making it easier to troubleshoot problems through telnet invoke.


当在provider端使用RpcContext.startAsync()进行异步返回结果时，会使用ThreadLocal存放上下文（RpcContext、AsyncContext等），正常情况下，会由ContextFilter清理ThreadLocal中的数据，当请求是由telnet invoke触发时，不会执行到ContextFilter，导致ThreadLocal未被清理，当此线程去处理下一个请求时，拿到的上下文是上一次请求的结果，从而引发问题。

在3.x以前的版本中，会造成正常的consumer拿到错误的数据，例如B请求拿到的是A请求的结果（同接口同方法），甚至是调用C接口的请求拿到D接口的结果（不同接口不同方法）。

在3.x版本中，由于invoke命令被划分到qos，并且是在单独的线程池中执行，因此不会与正常的consumer的请求发生串数据的情况（如果后续可以通过配置让两者共用线程池，则仍有此风险）。即便如此，ThreadLocal在使用完毕后却不清理仍旧是不合理的。

复现步骤：在provider端编写接口实现类，使用RpcContext.startAsync()进行异步操作；consumer端循环调用此接口，并校验返回结果是否与请求参数对应；然后通过telnet invoke命令调用此接口，观察consumer端是否出现请求与结果不对应的情况，然后关掉consumer，在telnet invoke执行时，观察provider端RpcContext.startAsync()返回的AsyncContext，是否多次为相同的对象。

另外telnet invoke无法拿到异步的结果，给运维时带来麻烦，此处可以优化为同步获取结果，方便通过telnet invoke排查问题

}
            }
        }
        return true;
    }
    private void printSelectMessage(StringBuilder buf, List<Method> methods) {
        buf.append("Methods:\r\n");
        for (int i = 0; i < methods.size(); i++) {
            Method method = methods.get(i);
            buf.append(i + 1).append(". ").append(method.getName()).append('(');
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int n = 0; n < parameterTypes.length; n++) {
                buf.append(parameterTypes[n].getSimpleName());
                if (n != parameterTypes.length - 1) {
                    buf.append(',');
                }
            }
            buf.append(")\r\n");
        }
        buf.append("Please use the select command to select the method you want to invoke. eg: select 1");
    }
    private boolean isInvokedSelectCommand(Channel channel) {
        if (channel.attr(SelectTelnet.SELECT_KEY).get() != null) {
            channel.attr(SelectTelnet.SELECT_KEY).remove();
            return true;
        }
        return false;
    }
}