elasticsearch源码分析---TransportClient

在es中存在两种形式的客户端,NodeClient和TransportClient。两者的主要区别在于是否加入集群。TransportClient是不会加入集群(作为集群的一个节点)。因此TransportClient跟NodeClient相比算是轻量级的。因为不加入集群,因此访问集群需要指定一个或者多个地址。客户端,顾名思义,就是作为调用的发起方,来向服务器请求数据。因此,我们执行的诸如索引、查询等一系列操作在TransportClient中都提供了对应的接口,这些接口无需过多介绍。

在TransportClient中,最重要的莫过于InternalTransportClient实例和NodeService服务。NodeService负责客户端与服务器的连接。

nodesService = injector.getInstance(TransportClientNodesService.class);
internalClient = injector.getInstance(InternalTransportClient.class);
我们重点关注下NodeService。客户端是如何维护与服务器的连接呢?

在NodeService中有维护者三类节点:listedNode(调用addTransportAddress加入的初始连接节点),nodes(真正于服务器建立了连接的节点),filteredNode(没有连接上的节点,比如由于clusterName不一致)。

当我们调用addTransportAddress加入一个或者地址时,会发生什么呢?关注TransportNodeService的如下方法:

public TransportClientNodesService addTransportAddresses(TransportAddress... transportAddresses) {
        synchronized (mutex) {
            if (closed) {
                throw new ElasticsearchIllegalStateException("transport client is closed, can't add an address");
            }
            List<TransportAddress> filtered = Lists.newArrayListWithExpectedSize(transportAddresses.length);
            for (TransportAddress transportAddress : transportAddresses) {
                boolean found = false;
                for (DiscoveryNode otherNode : listedNodes) {
                    if (otherNode.address().equals(transportAddress)) {
                        found = true;
                        logger.debug("address [{}] already exists with [{}], ignoring...", transportAddress, otherNode);
                        break;
                    }
                }
                if (!found) {
                    filtered.add(transportAddress);
                }
            }
            if (filtered.isEmpty()) {
                return this;
            }
            ImmutableList.Builder<DiscoveryNode> builder = ImmutableList.builder();
            builder.addAll(listedNodes());
            for (TransportAddress transportAddress : filtered) {
                DiscoveryNode node = new DiscoveryNode("#transport#-" + tempNodeIdGenerator.incrementAndGet(), transportAddress, minCompatibilityVersion);
                logger.debug("adding address [{}]", node);
                builder.add(node);
            }
            listedNodes = builder.build();
            nodesSampler.sample();
        }
        return this;
    }
初始状态下listedNodes是没有赋值的,因此所有address都进入了filtered的变量中,之后再调用addTransportAddress的时候listedNode就祈祷过滤作用了,防止重复添加。

加入filtered中的address均是没有潜在的未建立连接的节点(也是我们想要建立连接的节点),用这些节点第一次对listedNodes进行了赋值。调用到了关键的nodeSampler的sample方法。这里插播一下nodesSampler实例。在构造函数中:

 if (componentSettings.getAsBoolean("sniff", false)) {
            this.nodesSampler = new SniffNodesSampler();
        } else {
            this.nodesSampler = new SimpleNodeSampler();
        }

nodeSampler是根据client.transport,sniff属性的设置来生成具体的策略,默认false。设置为true意味着处理要连接的节点(listedNode)之外,还会主动去发现集群中的其他存活的节点,也就是SniffNodesSampler的doSample方法。这个sniff操作的频率通过nodes_sampler_interval这个选项来控制,默认5s。

主要对两类节点进行ping操作,一类是listedNode,一类是上一次ping之前确认已经建立连接关系的nodes。

if (!transportService.nodeConnected(listedNode)) {
                                try {

                                    // if its one of the actual nodes we will talk to, not to listed nodes, fully connect
                                    if (nodes.contains(listedNode)) {
                                        logger.trace("connecting to cluster node [{}]", listedNode);
                                        transportService.connectToNode(listedNode);
                                    } else {
                                        // its a listed node, light connect to it...
                                        logger.trace("connecting to listed node (light) [{}]", listedNode);
                                        transportService.connectToNodeLight(listedNode);
                                    }
                                } catch (Exception e) {
                                    logger.debug("failed to connect to node [{}], ignoring...", e, listedNode);
                                    latch.countDown();
                                    return;
                                }
                            }

通过对当前listednode的判断,如果没有建立过连接,就进入了以上的逻辑,进行fully connect还是light connect呢?判断标准在于这个listednode是否是我们要将要实际通讯的node,如果是,就进行fully connect,否则是light connect。fully connect会建立各个专项的channel(bulk,recovery等),而light connect只是为了ping。

之后会通过transport模块发送向listednode发送一个request请求,来获取节点状态。根据返回的节点状态,clusterName不匹配的节点放入filteredNode,而其余节点在通过validateNewNodes来验证,最终赋值给nodes。

protected ImmutableList<DiscoveryNode> validateNewNodes(Set<DiscoveryNode> nodes) {
            for (Iterator<DiscoveryNode> it = nodes.iterator(); it.hasNext(); ) {
                DiscoveryNode node = it.next();
                if (!transportService.nodeConnected(node)) {
                    try {
                        logger.trace("connecting to node [{}]", node);
                        transportService.connectToNode(node);
                    } catch (Throwable e) {
                        it.remove();
                        logger.debug("failed to connect to discovered node [" + node + "]", e);
                    }
                }
            }

            return new ImmutableList.Builder<DiscoveryNode>().addAll(nodes).build();
        }
在validataNewNode中建立了full connect,证明这是我们将要进行实际通信的节点。








评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值