Ejemplo n.º 1
0
        /// <summary>
        /// Runs a single peer connection, using the IPC link
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="self">This peer's information</param>
        /// <param name="remote">The remote peer's information</param>
        /// <param name="connecthandler">The method used to obtain the connection.</param>
        /// <param name="input">The channel for reading requests.</param>
        /// <param name="maxparallel">The maximum number of parallel handlers</param>
        private static async Task RunSingleConnection(PeerInfo self, PeerInfo remote, Func <Task <Stream> > connecthandler, IReadChannel <ConnectionRequest> input, int maxparallel)
        {
            // Get the local handler for remote requests
            var remotehandler = Channels.RemoteRequests.Get();

            LeanIPC.IPCPeer connection = null;

            try
            {
                if (connecthandler == null)
                {
                    throw new ArgumentNullException(nameof(connecthandler));
                }
                if (input == null)
                {
                    throw new ArgumentNullException(nameof(input));
                }

                log.Debug($"Setting up connection to {remote?.Key}");

                // Connect to the remote peer
                connection = new LeanIPC.IPCPeer(await connecthandler());

                // Setup a handler for remote requests, that forwards responses from the remote handler
                connection.AddUserTypeHandler <Protocol.Request>(
                    async(id, req) =>
                {
                    await connection.SendResponseAsync(id, (await remotehandler.SendConnectionRequestAsync(null, null, id, req)).Response);
                    return(true);
                }
                    );

                var mainTask  = connection.RunMainLoopAsync(self != remote);
                Key targetKey = null;

                // Grab a connection to update the routing table automatically
                var routingrequests = Channels.RoutingTableRequests.Get();

                log.Debug($"Peer connection running {self.Key}, {self.Address}");

                using (var tp = new TaskPool <ConnectionRequest>(maxparallel, (t, ex) => log.Warn("Unexpected error handling request", ex)))
                    while (true)
                    {
                        log.Debug($"Peer connection is waiting for request ...");

                        // Get either a local or a remote request
                        var req = await input.ReadAsync();

                        log.Debug($"Peer connection got request, handling on taskpool ...");

                        await tp.Run(req, () =>
                        {
                            log.Debug($"Peer connection is forwarding a local {req.Request.Operation} request to the remote");
                            return(Task.Run(async() =>
                            {
                                ConnectionResponse res;

                                try
                                {
                                    var p = await connection.SendAndWaitAsync <Protocol.Request, Protocol.Response>(req.Request);
                                    if (targetKey == null)
                                    {
                                        // Record the target key
                                        targetKey = p.Self.Key;
                                        if (remote == null || remote.Key == null)
                                        {
                                            remote = new PeerInfo(p.Self.Key, remote.Address);
                                        }

                                        // Write a registration request to the broker
                                        await Channels.ConnectionBrokerRegistrations.Get().WriteAsync(
                                            new ConnectionRegistrationRequest()
                                        {
                                            IsTerminate = false,
                                            UpdateRouting = true,
                                            Peer = remote
                                        }
                                            );

                                        log.Debug($"Registering peer in routing table: {remote.Key} {remote.Address} ...");
                                        await routingrequests.AddPeerAsync(remote.Key, remote);
                                    }

                                    if (p.Peers != null)
                                    {
                                        log.Debug($"Registering {p.Peers.Count} peers with the routing table ...");
                                        foreach (var peer in p.Peers)
                                        {
                                            await routingrequests.AddPeerAsync(peer.Key, peer);
                                        }

                                        log.Debug($"Registered {p.Peers.Count} peers with the routing table");
                                    }

                                    res = new ConnectionResponse()
                                    {
                                        Key = p.Self.Key,
                                        Response = p
                                    };
                                }
                                catch (Exception ex)
                                {
                                    log.Warn($"Failed to get result, sending error response", ex);
                                    res = new ConnectionResponse()
                                    {
                                        Key = targetKey,
                                        Exception = ex
                                    };

                                    log.Warn($"Killing peer due to the previous exception");
                                    await input.RetireAsync();
                                }

                                if (req.Response != null)
                                {
                                    try { await req.Response.WriteAsync(res); }
                                    catch (Exception ex) { log.Warn("Failed to send response", ex); }
                                }
                            }));
                        });
                    }
            }
            finally
            {
                await remotehandler.RetireAsync();

                if (connection != null)
                {
                    try { await connection.ShutdownAsync(); }
                    catch (Exception ex) { log.Warn("Failed to shut down IPC Peer", ex); }
                }

                // Write a registration request to the broker
                await Channels.ConnectionBrokerRegistrations.Get().WriteAsync(
                    new ConnectionRegistrationRequest()
                {
                    IsTerminate   = false,
                    UpdateRouting = false,
                    Peer          = remote
                }
                    );
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Runs the peer process
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="selfinfo">The information describing the peer.</param>
        /// <param name="k">The redundancy parameter.</param>
        /// <param name="storesize">The maximum size of the local store</param>
        /// <param name="maxage">The maximum age of items in the cache</param>
        /// <param name="initialContactlist">Initial list of peers to contact.</param>
        /// <param name="requests">The request channel for local management</param>
        public static async Task RunPeer(PeerInfo selfinfo, int k, int storesize, TimeSpan maxage, EndPoint[] initialContactlist, IReadChannel <PeerRequest> requests)
        {
            try
            {
                if (selfinfo == null)
                {
                    throw new ArgumentNullException(nameof(selfinfo));
                }
                if (initialContactlist == null)
                {
                    throw new ArgumentNullException(nameof(initialContactlist));
                }

                var ip = selfinfo.Address as IPEndPoint;
                if (ip == null)
                {
                    throw new ArgumentException($"Unable to convert {nameof(selfinfo.Address)} to a {nameof(IPEndPoint)}", nameof(selfinfo));
                }

                log.Debug($"Starting a peer with key {selfinfo.Key} and address {selfinfo.Address}");

                using (var scope = new IsolatedChannelScope())
                {
                    var sock = new TcpListener(ip);
                    sock.Start();

                    // Set up the helper processes
                    var router  = RoutingProcess.RunAsync(selfinfo, k);
                    var broker  = ConnectionBroker.RunAsync(selfinfo);
                    var values  = MRUProcess.RunAsync(selfinfo, storesize, maxage);
                    var remoter = RemoteProcess.RunAsync(selfinfo);

                    log.Debug("Started router, broker, and value store");

                    // Handle new connections
                    var listener = ListenAsync(selfinfo, sock);

                    log.Debug("Started listener");

                    // Start discovery of peers
                    var discovery = DiscoveryProcess.RunAsync(selfinfo, initialContactlist);

                    log.Debug("Started discovery");

                    // The process handling requests to the local node
                    var proc = AutomationExtensions.RunTask(
                        new
                    {
                        Requests = requests,
                        Refresh  = Channels.PeerRequests.ForRead,

                        // Add these, so this process terminates the others as well
                        BrokerReg  = Channels.ConnectionBrokerRegistrations.ForWrite,
                        BrokerReq  = Channels.ConnectionBrokerRequests.ForWrite,
                        BrokerStat = Channels.ConnectionBrokerStats.ForWrite,
                        MRUReq     = Channels.MRURequests.ForWrite,
                        MRUStat    = Channels.MRUStats.ForWrite,
                        RouteReq   = Channels.RoutingTableRequests.ForWrite,
                        RouteStat  = Channels.RoutingTableStats.ForWrite,
                    },
                        async self =>
                    {
                        log.Debug("Running peer main loop");

                        try
                        {
                            while (true)
                            {
                                var req = (await MultiChannelAccess.ReadFromAnyAsync(self.Requests, self.Refresh)).Value;
                                log.Debug($"Peer {selfinfo.Key} got message: {req.Operation}");
                                switch (req.Operation)
                                {
                                case PeerOperation.Add:
                                    await HandleAddOperation(selfinfo, req, k);
                                    break;

                                case PeerOperation.Find:
                                    await HandleFindOperation(selfinfo, req, k);
                                    break;

                                case PeerOperation.Stats:
                                    await HandleStatsOperation(req);
                                    break;

                                case PeerOperation.Refresh:
                                    await HandleRefreshOperation(selfinfo, req, k);
                                    break;

                                default:
                                    await req.Response.WriteAsync(new PeerResponse()
                                    {
                                        SuccessCount = -1
                                    });
                                    break;
                                }
                                log.Debug($"Peer {selfinfo.Key} handled message: {req.Operation}");
                            }
                        }
                        catch (Exception ex)
                        {
                            if (!ex.IsRetiredException())
                            {
                                log.Warn($"Terminating peer {selfinfo.Key} due to error", ex);
                            }
                            throw;
                        }
                        finally
                        {
                            log.Debug($"Terminating peer {selfinfo.Key}");
                        }
                    }
                        );

                    log.Debug("Started main handler");

                    // Set up a process that periodically emits refresh operations
                    var refresher = AutomationExtensions.RunTask(
                        new { Control = Channels.PeerRequests.ForWrite },
                        async self =>
                    {
                        var respchan = Channel.Create <PeerResponse>();
                        while (true)
                        {
                            // Sleep, but exit if the parent does
                            if (await Task.WhenAny(Task.Delay(TimeSpan.FromMinutes(10)), proc) == proc)
                            {
                                return;
                            }

                            await self.Control.WriteAsync(new PeerRequest()
                            {
                                Operation = PeerOperation.Refresh,
                                Response  = respchan
                            });
                            await respchan.ReadAsync();
                        }
                    }
                        );

                    log.Debug("Started refresh process, peer is now live");
                    await proc;
                    await router;
                    await broker;
                    await values;
                    await remoter;
                    await discovery;
                    await refresher;

                    await Task.WhenAll(router, broker, values, remoter, discovery, refresher);
                }
            }
            catch (Exception ex)
            {
                log.Warn("Failed to start peer", ex);

                try { await requests.RetireAsync(); }
                catch (Exception ex2) { log.Warn("Failed to stop the input channel", ex2); }

                log.Debug($"Peer with key {selfinfo.Key} and address {selfinfo.Address} stopped...");

                throw;
            }
        }