Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LazyConnectExchangeClient关闭后没有将client重置为null #8880

Closed
zrlw opened this issue Sep 23, 2021 · 1 comment
Closed

LazyConnectExchangeClient关闭后没有将client重置为null #8880

zrlw opened this issue Sep 23, 2021 · 1 comment

Comments

@zrlw
Copy link
Contributor

zrlw commented Sep 23, 2021

Environment

  • Dubbo version: 3.0 / master

ReferenceCountExchangeClient关闭后,其ExchangeClient成员换成了LazyConnectExchangeClient以支持复活,但是目前的代码只能复活一次,复活之后的LazyConnectExchangeClient再次关闭之后就不能复活了。
原因是LazyConnectExchangeClient两个close方法没有将channel已关闭的client对象置为null,再次复活执行initClient方法时直接复用了这个channel已关闭的client,导致DubboInvoker.doInvoke抛RpcException,异常信息如下所示:

org.apache.dubbo.rpc.RpcException: Failed to invoke remote method: hello, provider: dubbo://127.0.0.1:36799/hello?connections=0&lazyclient_request_with_warning=true&shareconnections=1, cause: Failed to send request RpcInvocation [methodName=hello, parameterTypes=[], arguments=[], attachments={path=hello, version=0.0.0}], cause: The channel org.apache.dubbo.remoting.transport.netty4.NettyClient [/192.168.1.23:53179 -> /192.168.1.23:36799] is closed!
	at org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(DubboInvoker.java:121)

此外,ReferenceCountExchangeClientTest的test_counter_error()的测试代码与当前版本的LazyConnectExchangeClient之间存在多处不一致的地方。
比如测试代码认为:
ReferenceCountExchangeClient关闭时,ExchangeClient属性被替换为LazyConnectExchangeClient之后,相应dubboInvoker的available状态应该是false;但是实际情况available默认状态是true:

  1. 新建的LazyConnectExchangeClient内的client对象为null,而client为null时,dubboInvoker.isAvailable方法等同于调用LazyConnectExchangeClient的isConnected()方法;
  2. 当client为null时,isConnected这个方法返回LazyConnectExchangeClient的initialState属性;
  3. initialState这个属性就是url里的connect.lazy.initial.state参数,不配置就取默认值true。

我不清楚设计LazyConnectExchangeClient的目的或者好处是什么,通常写代码时不会专门去使用一个channel已经关掉的client,并且这个client还只支持一次满血复活,复活再关闭之后就没有复活能力了。

@zrlw
Copy link
Contributor Author

zrlw commented Sep 23, 2021

顺便修订了 #7410 引入的缺陷,该缺陷将会导致热加载的dubbo Reference关闭时不释放连接,问题详情见 #8895

重现方法:

  1. install当前master分支(2.7.14-SNAPSHOT)dubbo
  2. 修改dubbo-samples的dubbo-samples-basic工程pom文件的dubbo版本为2.7.14-SNAPSHOT
  3. 在工程中增加热加载Reference类CommuLink
package org.apache.dubbo.samples.basic;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.samples.basic.api.DemoService;

public class CommuLink {
    private ReferenceConfig<DemoService> reference = null;
    private DemoService uChannelInstance = null;
    
    public DemoService getuChannelInstance() {
        return uChannelInstance;
    }

    public boolean init(ApplicationConfig applicationConfig) {
        //需要初始化
        this.reference = new ReferenceConfig<>();
        reference.setApplication(applicationConfig);
        reference.setInterface(DemoService.class);
        //reference.setCheck(false);
        //reference.setConnections(5);
        
        try{
            this.uChannelInstance = reference.get();
            return true;
        }catch(Exception e){
        }
        return false;
    }
    
    public void close() {
        try {
            Method getInvoker = ReferenceConfig.class.getDeclaredMethod("getInvoker");
            getInvoker.setAccessible(true);
            Invoker<?> invoker = (Invoker<?>) getInvoker.invoke(reference);
            invoker.destroyAll();
            reference.destroy();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        
        ApplicationConfig applicationConfig = new ApplicationConfig("commuLink");
        applicationConfig.setQosEnable(false);
        applicationConfig.setRegistry(registryConfig);
        
        CommuLink lastLink = null;
        for(int i = 0;i < 10;i++){
            if(lastLink != null){
                lastLink.close();
            }
            CommuLink commuLink = new CommuLink();
            commuLink.init(applicationConfig);
            String hello = commuLink.getuChannelInstance().sayHello("name-" + i);
            System.out.println(hello);
            lastLink = commuLink;

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }
}
  1. 启动BasicProvider(内置zk)
  2. 在HeaderExchangeChannel的两个close方法设置断点,然后debug跟踪CommuLink,用netstat -an看20880连接是否断开。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant