Ejemplo n.º 1
0
        /// <summary>
        /// サインインした全アカウントのツイート, フォロー等を取得する
        /// 常に最大数のツイートを取得する
        /// </summary>
        /// <returns></returns>
        public async Task <int> Proceed(bool getTweet = false)
        {
            var tokens = (await db.SelectUserStreamerSetting(DBHandler.SelectTokenMode.All).ConfigureAwait(false)).ToArray();

            if (tokens.Length > 0)
            {
                Console.WriteLine("App: {0} Accounts to REST", tokens.Length);
            }
            var RestProcess = new ActionBlock <Tokens>(async(t) =>
            {
                //無条件でAPIの最大数のツイートを取得するためにToken以外は捨てる
                var s = new UserStreamer(new UserStreamer.UserStreamerSetting()
                {
                    Token = t
                });
                await s.RestFriend().ConfigureAwait(false);
                await s.RestBlock().ConfigureAwait(false);
                if (getTweet)
                {
                    await s.RestMyTweet().ConfigureAwait(false);
                }
                await s.VerifyCredentials().ConfigureAwait(false);
            }, new ExecutionDataflowBlockOptions()
            {
                MaxDegreeOfParallelism = config.crawl.RestTweetThreads,
                BoundedCapacity        = config.crawl.RestTweetThreads << 1
            });
            var sw = Stopwatch.StartNew();

            foreach (var t in tokens)
            {
                await RestProcess.SendAsync(t.Token).ConfigureAwait(false);

                while (true)
                {
                    if (sw.ElapsedMilliseconds > 60000)
                    {
                        Counter.PrintReset();
                        GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; //これは毎回必要らしい
                        GC.Collect();
                        sw.Restart();
                    }
                    //ツイートが詰まってたら休む
                    if (UserStreamerStatic.NeedConnectPostpone())
                    {
                        await Task.Delay(1000).ConfigureAwait(false);
                    }
                    else
                    {
                        break;
                    }
                }
            }
            RestProcess.Complete();
            await RestProcess.Completion.ConfigureAwait(false);

            await UserStreamerStatic.Complete().ConfigureAwait(false);

            Counter.PrintReset();
            return(tokens.Length);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// これを定期的に呼んで再接続やFriendの取得をやらせる
        /// 1分経過しても終わらないときは打ち切る
        /// 一度も取得していないアカウント→取得待ちが多い順 に処理する
        /// </summary>
        public async Task ConnectStreamers()
        {
            //アカウントが1個も割り当てられなくなってたら自殺する
            if (!await db.ExistThisPid().ConfigureAwait(false))
            {
                Environment.Exit(1);
            }

            var LastCrawlTime = new Dictionary <long, long>();

            while (ConnectBlockProceeeded.TryTake(out var proceeded))
            {
                //ツイートの取得時刻を保存する(たぶん)
                //ここで(前回のConnectStreamers()で)REST取得したやつとUser streamに接続済みのやつだけ処理する(もうないけど)
                if (proceeded.LastReceivedTweetId != 0 || proceeded.NeedConnect() == UserStreamer.NeedConnectResult.StreamConnected)
                {
                    LastCrawlTime[proceeded.Token.UserId] = proceeded.LastMessageTime.ToUnixTimeSeconds();
                }
            }
            var StoreLastCrawlTimeTask = db.StoreCrawlInfo_Timeline(LastCrawlTime);

            Counter.PrintReset();

            //TLがある程度進んでいるアカウントと一定時間取得してないアカウントだけ接続する
            var now = DateTimeOffset.UtcNow;
            var StreamersSelected = Streamers.Select(s => s.Value)
                                    .Where(s => s.EstimatedTweetToReceive >= config.crawl.StreamSpeedTweets ||
                                           (now - s.LastMessageTime).TotalSeconds > config.crawl.MaxRestInterval)
                                    .OrderByDescending(s => s.EstimatedTweetToReceive)
                                    .ThenBy(s => s.LastMessageTime)
                                    .ToArray();

            // UserStreamerの再接続やRESTによるツイート取得などを行うやつ
            var ConnectBlock = new ActionBlock <UserStreamer>(async(Streamer) =>
            {
                try
                {
                    var NeedConnect = Streamer.NeedConnect();
                    //VerifyCredentialsを実行して成功したらtrue
                    bool RevokeCheckSuccess = false;
                    //初回とRevoke疑いのときだけVerifyCredentials()する
                    //プロフィールを取得したい
                    if (NeedConnect == UserStreamer.NeedConnectResult.Postponed)
                    {
                        return;
                    }
                    else if (NeedConnect == UserStreamer.NeedConnectResult.First || RevokeRetryUserID.ContainsKey(Streamer.Token.UserId))
                    {
                        switch (await Streamer.VerifyCredentials().ConfigureAwait(false))
                        {
                        case UserStreamer.TokenStatus.Locked:
                            Streamer.PostponeConnect(); return;

                        case UserStreamer.TokenStatus.Revoked:
                            MarkRevoked(Streamer); return;

                        case UserStreamer.TokenStatus.Failure:
                            return;

                        case UserStreamer.TokenStatus.Success:
                            RevokeCheckSuccess = true;
                            break;
                        }
                    }

                    //Streamに接続したりRESTだけにしたり
                    if (NeedConnect == UserStreamer.NeedConnectResult.StreamConnected)
                    {
                        if (Streamer.NeedStreamSpeed() == UserStreamer.NeedStreamResult.RestOnly)
                        {
                            Streamer.DisconnectStream(); return;
                        }
                        else
                        {
                            Counter.ActiveStreamers.Increment();
                        }
                    }
                    else
                    {
                        //TLの速度を測定して必要ならStreamに接続
                        Counter.RestedStreamers.Increment();
                        switch (await Streamer.RestTimeline().ConfigureAwait(false))
                        {
                        case UserStreamer.TokenStatus.Revoked:
                            //長期間ログインされていないアカウントはVerifyCredentialsしか通らなくなるっぽい
                            //またVerifyCredentialsに失敗するまで放っとく
                            if (RevokeCheckSuccess)
                            {
                                Streamer.PostponeConnect();
                            }
                            //普通のRevoke疑い
                            else
                            {
                                MarkRevoked(Streamer);
                            }
                            break;

                        case UserStreamer.TokenStatus.Locked:
                            if (RevokeCheckSuccess)
                            {
                                UnmarkRevoked(Streamer);
                            }
                            Streamer.PostponeConnect();
                            break;

                        default:
                            if (RevokeCheckSuccess)
                            {
                                UnmarkRevoked(Streamer);
                            }
                            UserStreamer.NeedStreamResult NeedStream = Streamer.NeedStreamSpeed();
                            if (NeedStream == UserStreamer.NeedStreamResult.Stream)
                            {
                                Streamer.RecieveStream(); Counter.ActiveStreamers.Increment();
                            }
                            //DBが求めていればToken読み込み直後だけ自分のツイートも取得(初回サインイン狙い
                            if (Streamer.NeedRestMyTweet)
                            {
                                await Streamer.RestMyTweet().ConfigureAwait(false);
                                //User streamに繋がない場合はこっちでフォローを取得する必要がある
                                if (NeedStream != UserStreamer.NeedStreamResult.Stream)
                                {
                                    await Streamer.RestFriend().ConfigureAwait(false);
                                }
                                await Streamer.RestBlock().ConfigureAwait(false);
                                Streamer.NeedRestMyTweet = false;
                            }
                            break;
                        }
                    }
                    ConnectBlockProceeeded.Add(Streamer);
                }
                catch (Exception e) { Console.WriteLine("ConnectBlock Faulted: {0}", e); }
            }, new ExecutionDataflowBlockOptions()
            {
                MaxDegreeOfParallelism    = ConnectBlockConcurrency,
                BoundedCapacity           = ConnectBlockConcurrency + 1, //これでもawaitする
                SingleProducerConstrained = true,
            });

            var  connectStopWatch = Stopwatch.StartNew();
            bool connectTimedout  = false;

            using (var cancel = new CancellationTokenSource(60000))
            {
                try
                {
                    foreach (var s in StreamersSelected)
                    {
                        if (!await ConnectBlock.SendAsync(s, cancel.Token).ConfigureAwait(false))
                        {
                            break;
                        }                                                                                       //そんなバナナ

                        //ツイートが詰まってたら休む
                        while (UserStreamerStatic.NeedConnectPostpone() && !cancel.Token.IsCancellationRequested)
                        {
                            await Task.Delay(1000, cancel.Token).ConfigureAwait(false);
                        }
                    }
                    ConnectBlock.Complete();
                    await ConnectBlock.Completion.ConfigureAwait(false);
                }
                catch (TaskCanceledException)
                {
                    ConnectBlock.Complete();
                    connectTimedout = true;
                }
            }

            //ConnectBlockの同時接続数を調整する
            connectStopWatch.Stop();
            int oldConnectBlockConcurrency = ConnectBlockConcurrency;

            if (connectTimedout)
            {
                ConnectBlockConcurrency = config.crawl.MaxReconnectThreads;
            }
            else if (connectStopWatch.ElapsedMilliseconds < 40000)
            {
                ConnectBlockConcurrency = Math.Max(1, ConnectBlockConcurrency - 1);
            }
            else if (50000 < connectStopWatch.ElapsedMilliseconds)
            {
                ConnectBlockConcurrency = Math.Clamp((int)Math.Ceiling(ConnectBlockConcurrency * (connectStopWatch.ElapsedMilliseconds / 50000d)), 1, config.crawl.MaxReconnectThreads);
            }
            if (oldConnectBlockConcurrency != ConnectBlockConcurrency)
            {
                Console.WriteLine("ConnectBlockConcurrency: {0} -> {1}", oldConnectBlockConcurrency, ConnectBlockConcurrency);
            }

            await StoreLastCrawlTimeTask.ConfigureAwait(false);

            //Revoke後再試行にも失敗したTokenはここで消す
            await RemoveRevokedTokens().ConfigureAwait(false);

            //接続が全然進まないときは殺してもらう
            if (0 < Counter.ActiveStreamers.Get() + Counter.RestedStreamers.Get())
            {
                await WatchDogUdp.SendAsync(BitConverter.GetBytes(ThisPid), sizeof(int), WatchDogEndPoint).ConfigureAwait(false);
            }

            UserStreamerStatic.ShowCount();
        }