dak ブログ

python、rubyなどのプログラミング、MySQL、サーバーの設定などの備忘録。レゴの写真も。

rsync の timeout エラーについて調査した結果のメモ

2018-11-17 20:16:29 | linux
現在運用中のシステムでは rsync で数十台のサーバのログを、
ログ集計サーバに転送するようにしていますが、
送信側でエラーになっていて、転送に失敗することが多いため、
rsync のソース(rsync-3.1.3)を調べてみました。

結果的には、/etc/rsyncd.conf に "listen backlog = 転送元サーバ台数" を
設定すればよさそう。
ちなみに、listen backlog はデフォルトでは 5 になっています。

■loadparm.c

 /* ==== global_vars ==== */
 {
 ...
 /* listen_backlog; */          5,
 ...
 },

以下、クライアントからの要求をどのように処理しているかを、ソースを追って見てみます。
■clientserver.c

int daemon_main(void)
{
        ...

        start_accept_loop(rsync_port, start_daemon);
        return -1;
}

start_accept_loop() で accept しているので、次に start_accept_loop() を見てみます。
■socket.c

void start_accept_loop(int port, int (*fn)(int, int))
{
        ...

        /* open an incoming socket */
        sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
        if (sp == NULL)
                exit_cleanup(RERR_SOCKETIO);

        /* ready to listen */
        FD_ZERO(&deffds);
        for (i = 0, maxfd = -1; sp[i] >= 0; i++) {
★1             if (listen(sp[i], lp_listen_backlog()) < 0) {

        ...

        while (1) {
                ...

                if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 1)
                        continue;

                for (i = 0, fd = -1; sp[i] >= 0; i++) {
                        if (FD_ISSET(sp[i], &fds)) {
                                fd = accept(sp[i], (struct sockaddr *)&addr,
                                            &addrlen);
                                break;
                        }
                }

                ...

★2             if ((pid = fork()) == 0) {
                        int ret;
                        for (i = 0; sp[i] >= 0; i++)
                                close(sp[i]);
                        /* Re-open log file in child before possibly giving
                         * up privileges (see logfile_close() above). */
                        logfile_reopen();
★3                     ret = fn(fd, fd);
                        close_all();
                        _exit(ret);
                } else if (pid < 0) {
                        rsyserr(FERROR, errno,
                                "could not create child server process");
                        close(fd);
                        /* This might have happened because we're
                         * overloaded.  Sleep briefly before trying to
                         * accept again. */
                        sleep(2);
                } else {
                        /* Parent doesn't need this fd anymore. */
                        close(fd);
                }
        ...
}

★1 で listen() していて、キューの最大長は第2引数の lp_listen_backlog() が指定されます。。
これがデフォルトでは 5 になっています(loadparm.c)。

★2 accept 後に fork しています。
select() で全クライアントとの読み書き処理を行っていたら...と思っていましたが、
fork() していたので、ひと安心。

★3 fn は start_accept_daemon() の第2引数の start_daemon()。
転送処理は start_daemon() で行っているはず。(ここで調査終了)