Package net.tomp2p.p2p

Source Code of net.tomp2p.p2p.DistributedRouting

/*
* Copyright 2009 Thomas Bocek
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.tomp2p.p2p;

import java.util.Collection;
import java.util.Comparator;
import java.util.NavigableSet;
import java.util.Random;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import net.tomp2p.connection.ChannelCreator;
import net.tomp2p.connection.PeerBean;
import net.tomp2p.futures.BaseFutureAdapter;
import net.tomp2p.futures.FutureDone;
import net.tomp2p.futures.FutureForkJoin;
import net.tomp2p.futures.FutureResponse;
import net.tomp2p.futures.FutureRouting;
import net.tomp2p.message.Message;
import net.tomp2p.message.Message.Type;
import net.tomp2p.p2p.builder.RoutingBuilder;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.peers.PeerMap;
import net.tomp2p.rpc.DigestInfo;
import net.tomp2p.rpc.NeighborRPC;
import net.tomp2p.utils.Pair;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Handles routing of nodes to other nodes.
*
* TODO: add timing constraints for the routing. This would allow for slow routing requests to have a chance to report
* the neighbors.
*
* @author Thomas Bocek
*/
public class DistributedRouting {
    private static final Logger LOG = LoggerFactory.getLogger(DistributedRouting.class);

    private final NeighborRPC neighbors;

    private final PeerBean peerBean;

    private final Random rnd;

    /**
     * The routing process involves multiple RPCs, mostly UDP based.
     *
     * @param peerBean
     *            The peer bean
     * @param neighbors
     *            The neighbor RPC that will be issues
     */
    public DistributedRouting(final PeerBean peerBean, final NeighborRPC neighbors) {
        this.neighbors = neighbors;
        this.peerBean = peerBean;
        // stable random number. No need to be truly random
        rnd = new Random(peerBean.serverPeerAddress().peerId().hashCode());
    }

    /**
     * Bootstraps to the given peerAddresses, i.e. looking for near nodes
     *
     * @param peerAddresses
     *            the node to which bootstrap should be performed to
     * @param routingBuilder
     *            All relevant information for the routing process
     * @param cc
     *            The channel creator
     * @return a FutureRouting object, is set to complete if the route has been found
     */
    public FutureDone<Pair<FutureRouting,FutureRouting>> bootstrap(final Collection<PeerAddress> peerAddresses,
            final RoutingBuilder routingBuilder, final ChannelCreator cc) {
        // search close peers
        LOG.debug("broadcast to {}", peerAddresses);
        final FutureDone<Pair<FutureRouting,FutureRouting>> futureDone = new FutureDone<Pair<FutureRouting,FutureRouting>>();

        // first we find close peers to us
        routingBuilder.bootstrap(true);

        final FutureRouting futureRouting0 = routing(peerAddresses, routingBuilder, Type.REQUEST_1, cc);
        // to not become a Fachidiot (expert idiot), we need to know other peers
        // as well. This is important if this peer is passive and only replies on requests from other peers
        futureRouting0.addListener(new BaseFutureAdapter<FutureRouting>() {
            @Override
            public void operationComplete(final FutureRouting future) throws Exception {
                // setting this to null causes to search for a random number
              if(future.isSuccess()) {
                routingBuilder.locationKey(null);
                final FutureRouting futureRouting1 = routing(peerAddresses, routingBuilder, Type.REQUEST_1, cc);
                futureRouting1.addListener(new BaseFutureAdapter<FutureRouting>() {
                  @Override
                  public void operationComplete(FutureRouting future) throws Exception {
                    final Pair<FutureRouting,FutureRouting> pair = new Pair<FutureRouting, FutureRouting>(futureRouting0, futureRouting1);
                    futureDone.done(pair);
                  }
                });
              } else {
                futureDone.failed(future);
              }
            }
        });
        return futureDone;
    }
   
    public FutureRouting quit(final RoutingBuilder routingBuilder, final ChannelCreator cc) {
      Collection<PeerAddress> startPeers = peerBean.peerMap().closePeers(routingBuilder.locationKey(),
                routingBuilder.parallel() * 2);
        return routing(startPeers, routingBuilder, Type.REQUEST_4, cc);
    }

    /**
     * Looks for a route to the given locationKey.
     *
     * @param routingBuilder
     *            All relevant information for the routing process
     * @param type
     *            The type of the routing, there can at most four types
     * @param cc
     *            The channel creator
     *
     * @return a FutureRouting object, is set to complete if the route has been found
     */
    public FutureRouting route(final RoutingBuilder routingBuilder, final Type type, final ChannelCreator cc) {
        // for bad distribution, use large NO_NEW_INFORMATION
        Collection<PeerAddress> startPeers = peerBean.peerMap().closePeers(routingBuilder.locationKey(),
                routingBuilder.parallel() * 2);
        return routing(startPeers, routingBuilder, type, cc);
    }

    /**
     * Looks for a route to the given locationKey.
     *
     * @param peerAddresses
     *            nodes which should be asked first for a route
     * @param locationKey
     *            the node a route should be found to
     * @param domainKey
     *            the domain of the network the current node and locationKey is in
     * @param contentKeys
     *            nodes which we got from another node
     * @param maxDirectHits
     *            number of direct hits to stop at
     * @param maxNoNewInfo
     *            number of nodes asked without new information to stop at
     * @param maxFailures
     *            number of failures to stop at
     * @param parallel
     *            number of routing requests performed concurrently
     * @return a FutureRouting object, is set to complete if the route has been found
     */
    private FutureRouting routing(final Collection<PeerAddress> peerAddresses,
            final RoutingBuilder routingBuilder, final Type type, final ChannelCreator cc) {
        if (peerAddresses == null) {
            throw new IllegalArgumentException("you need to specify some nodes");
        }
        boolean randomSearch = routingBuilder.locationKey() == null;
        //
        final Comparator<PeerAddress> comparator;
        if (randomSearch) {
            comparator = peerBean.peerMap().createComparator();
        } else {
            comparator = PeerMap.createComparator(routingBuilder.locationKey());
        }
        final NavigableSet<PeerAddress> queueToAsk = new TreeSet<PeerAddress>(comparator);
        // we can reuse the comparator
        final SortedSet<PeerAddress> alreadyAsked = new TreeSet<PeerAddress>(comparator);
        // as presented by Kazuyuki Shudo at AIMS 2009, its better to ask random
        // peers with the data than ask peers that ar ordered by distance ->
        // this balances load.
        final SortedMap<PeerAddress, DigestInfo> directHits = new TreeMap<PeerAddress, DigestInfo>(peerBean
                .peerMap().createComparator());
        final NavigableSet<PeerAddress> potentialHits = new TreeSet<PeerAddress>(comparator);
        // fill initially
        queueToAsk.addAll(peerAddresses);
        alreadyAsked.add(peerBean.serverPeerAddress());
        potentialHits.add(peerBean.serverPeerAddress());
        // domainkey can be null if we bootstrap
        if (type == Type.REQUEST_2 && routingBuilder.domainKey() != null && !randomSearch && peerBean.digestStorage() !=null) {
            final Number640 from;
            final Number640 to;
            if (routingBuilder.from()!=null && routingBuilder.to()!=null) {
              from = routingBuilder.from();
              to = routingBuilder.to();
            } else if (routingBuilder.domainKey() == null) {
                from = new Number640(routingBuilder.locationKey(), Number160.ZERO, Number160.ZERO,
                        Number160.ZERO);
                to = new Number640(routingBuilder.locationKey(), Number160.MAX_VALUE, Number160.MAX_VALUE,
                        Number160.MAX_VALUE);
            } else if (routingBuilder.contentKey() == null) {
                from = new Number640(routingBuilder.locationKey(), routingBuilder.domainKey(),
                        Number160.ZERO, Number160.ZERO);
                to = new Number640(routingBuilder.locationKey(), routingBuilder.domainKey(),
                        Number160.MAX_VALUE, Number160.MAX_VALUE);
            } else {
                from = new Number640(routingBuilder.locationKey(), routingBuilder.domainKey(),
                        routingBuilder.contentKey(), Number160.ZERO);
                to = new Number640(routingBuilder.locationKey(), routingBuilder.domainKey(),
                        routingBuilder.contentKey(), Number160.MAX_VALUE);
            }
            DigestInfo digestBean = peerBean.digestStorage().digest(from, to, -1, true);
            if (digestBean.size() > 0) {
                directHits.put(peerBean.serverPeerAddress(), digestBean);
            }
        } else if (type == Type.REQUEST_3 && !randomSearch && peerBean.digestTracker() != null) {
            DigestInfo digestInfo = peerBean.digestTracker().digest(routingBuilder.locationKey(),
                    routingBuilder.domainKey(), routingBuilder.contentKey());
            // we always put ourselfs to the tracker list, so we need to check
            // if we know also other peers on our trackers.
            if (digestInfo.size() > 0) {
                directHits.put(peerBean.serverPeerAddress(), digestInfo);
            }
        }
       
        final FutureRouting futureRouting = new FutureRouting();
        if (peerAddresses.size() == 0) {
            futureRouting.neighbors(directHits, potentialHits, alreadyAsked, routingBuilder.isBootstrap(),
                    false);
        } else {
            // if a peer bootstraps to itself, then the size of peerAddresses
            // is 1 and it contains itself. Check for that because we need to
            // know if we are routing, bootstrapping and bootstrapping to
            // ourselfs, to return the correct status for the future
            boolean isRoutingOnlyToSelf = (peerAddresses.size() == 1 && peerAddresses.iterator().next()
                    .equals(peerBean.serverPeerAddress()));

            RoutingMechanism routingMechanism = routingBuilder.createRoutingMechanism(futureRouting);

            routingMechanism.queueToAsk(queueToAsk);
            routingMechanism.potentialHits(potentialHits);
            routingMechanism.directHits(directHits);
            routingMechanism.alreadyAsked(alreadyAsked);

            routingBuilder.routingOnlyToSelf(isRoutingOnlyToSelf);
            routingRec(routingBuilder, routingMechanism, type, cc);
        }
        return futureRouting;
    }

    /**
     * Looks for a route to the given locationKey, performing recursively. Since this method is not called concurrently,
     * but sequentially, no synchronization is necessary.
     *
     * @param futureResponses
     *            expected responses
     * @param futureRouting
     *            the current routing future used
     * @param queueToAsk
     *            all nodes which should be asked for routing information
     * @param alreadyAsked
     *            nodes which already have been asked
     * @param directHits
     *            stores direct hits received
     * @param nrNoNewInfo
     *            number of nodes contacted without any new information
     * @param nrFailures
     *            number of nodes without a response
     * @param nrSucess
     *            number of peers that responded
     * @param maxDirectHits
     *            number of direct hits to stop at
     * @param maxNoNewInfo
     *            number of nodes asked without new information to stop at
     * @param maxFailures
     *            number of failures to stop at
     * @param maxSuccess
     *            number of successful requests. To avoid looping if every peer gives a new piece of information.
     * @param parallel
     *            number of routing requests performed concurrently
     * @param locationKey
     *            the node a route should be found to
     * @param domainKey
     *            the domain of the network the current node and locationKey is in
     * @param contentKeys
     *            nodes which we got from another node
     */

    private void routingRec(final RoutingBuilder routingBuilder, final RoutingMechanism routingMechanism,
            final Type type, final ChannelCreator channelCreator) {

        final boolean randomSearch = routingBuilder.locationKey() == null;
        int active = 0;
        for (int i = 0; i < routingMechanism.parallel(); i++) {
            if (routingMechanism.futureResponse(i) == null && !routingMechanism.isStopCreatingNewFutures()) {
                final PeerAddress next;
                if (randomSearch) {
                    next = routingMechanism.pollRandomInQueueToAsk(rnd);
                } else {
                    next = routingMechanism.pollFirstInQueueToAsk();
                }
                if (next != null) {
                    routingMechanism.addToAlreadyAsked(next);
                    active++;
                    // if we search for a random peer, then the peer should
                    // return the address farest away.
                    final Number160 locationKey2 = randomSearch ? next.peerId().xor(Number160.MAX_VALUE)
                            : routingBuilder.locationKey();
                    routingBuilder.locationKey(locationKey2);
                    
                    if(LOG.isWarnEnabled() ) {
                      //routing is per default UDP, don't show warning if the other TCP/UDP is used
                      if(channelCreator.availableUDPPermits()==0 && !routingBuilder.isForceTCP()) {
                        LOG.warn("sanity check faild UDP: {}, {}",i,Thread.currentThread().getName());
                      } else if(channelCreator.availableTCPPermits()==0 && routingBuilder.isForceTCP()) {
                        LOG.warn("sanity check faild TCP: {}, {}",i,Thread.currentThread().getName());
                      }
                    }
                    routingMechanism.futureResponse(i, neighbors.closeNeighbors(next,
                            routingBuilder.searchValues(), type, channelCreator, routingBuilder));
                    LOG.debug("get close neighbors: {} on {}", next, i);
                }
            } else if (routingMechanism.futureResponse(i) != null) {
                LOG.debug("activity on {}", i);
                active++;
            }
        }
        if (active == 0) {
            LOG.debug("no activity, closing");

            routingMechanism.neighbors(routingBuilder);
            routingMechanism.cancel();
            return;
        }
        final boolean last = active == 1;
        final FutureForkJoin<FutureResponse> fp = new FutureForkJoin<FutureResponse>(1, false,
                routingMechanism.futureResponses());
        fp.addListener(new BaseFutureAdapter<FutureForkJoin<FutureResponse>>() {
            @Override
            public void operationComplete(final FutureForkJoin<FutureResponse> future) throws Exception {
                final boolean finished;
                if (future.isSuccess()) {
                    Message lastResponse = future.last().responseMessage();
                    PeerAddress remotePeer = lastResponse.sender();
                    routingMechanism.addPotentialHits(remotePeer);
                    Collection<PeerAddress> newNeighbors = lastResponse.neighborsSet(0).neighbors();

                    Integer resultSize = lastResponse.intAt(0);
                    Number160 keyDigest = lastResponse.key(0);
                    Number160 contentDigest = lastResponse.key(1);
                    DigestInfo digestBean = new DigestInfo(keyDigest, contentDigest, resultSize == null ? 0
                            : resultSize);
                    LOG.debug("Peer ({}) {} reported {} in message {}", (digestBean.size() > 0 ? "direct" : "none"),
                            remotePeer, newNeighbors, lastResponse);
                    finished = routingMechanism.evaluateSuccess(remotePeer, digestBean, newNeighbors, last, routingBuilder.locationKey());
                    LOG.debug("Routing finished {} / {}", finished,
                            routingMechanism.isStopCreatingNewFutures());
                } else {
                    // if it failed but the failed is the closest one, its good to try again, since the peer might just
                    // be busy
                    LOG.debug("routing error {}", future.failedReason());
                    finished = routingMechanism.evaluateFailed();
                    routingMechanism.stopCreatingNewFutures(finished);
                }
                if (finished) {
                    LOG.debug("finished routing, direct hits: {} potential: {}",
                            routingMechanism.directHits(), routingMechanism.potentialHits());

                    routingMechanism.neighbors(routingBuilder);
                    routingMechanism.cancel();
                    // stop all operations, as we are finished, no need to go further
                } else {

                    routingRec(routingBuilder, routingMechanism, type, channelCreator);
                }
            }
        });
    }

    public PeerMap peerMap() {
        return peerBean.peerMap();
    }

 

    /**
     * Cancel the future that causes the underlying futures to cancel as well.
     *
     * @param futureResponses
     *            The array with the future responses some items may be null
     */
    /*
     * public static void cancel(final AtomicReferenceArray<? extends BaseFuture> futureResponses) { int len =
     * futureResponses.length(); for (int i = 0; i < len; i++) { BaseFuture baseFuture = futureResponses.get(i); if
     * (baseFuture != null) { baseFuture.cancel(); } } }
     */
TOP

Related Classes of net.tomp2p.p2p.DistributedRouting

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.