redis之unix域套接字應用

        在redis源碼的server.c文件中的initServer()方法中有這樣一段代碼:

/* Open the listening Unix domain socket. */
if (server.unixsocket != NULL) {
    unlink(server.unixsocket); /* don't care if this fails */
    server.sofd = anetUnixServer(server.neterr,server.unixsocket,
        server.unixsocketperm, server.tcp_backlog);
    if (server.sofd == ANET_ERR) {
        serverLog(LL_WARNING, "Opening Unix socket: %s", server.neterr);
        exit(1);
    }
    anetNonBlock(NULL,server.sofd);
}

        根據註釋可以知道這段代碼是要打開unix域套接字進行監聽,但是打開之前做了一個判斷,即server.unixsocket是否爲null。這個值是什麼意思呢?又是從哪來的呢?看配置文件redis.conf

# TCP listen() backlog.
#
# In high requests-per-second environments you need an high backlog in order
# to avoid slow clients connections issues. Note that the Linux kernel
# will silently truncate it to the value of /proc/sys/net/core/somaxconn so
# make sure to raise both the value of somaxconn and tcp_max_syn_backlog
# in order to get the desired effect.
tcp-backlog 511

# Unix socket.
#
# Specify the path for the Unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
unixsocket /tmp/redis.sock
unixsocketperm 700

        可以看到redis中的unix域套接字默認是不打開的,這裏是取消註釋將其打開了,並定義了參數unixsocketunixsocketperm。並截取了上面一部分配置tcp-backlog,這個參數之前在文章:套接字api值listen函數中着重提到過,這裏後面也會用到。可見方法中的值是來自配置文件。如果配置文件中沒打開,那麼server.unixsocket值就是爲null,也就不必打開unix域套接字進行監聽了。

        繼續看if()裏面,調用了一個anetUnixServer()方法,三個參數都來自配置文件。

/**
 * path: socket文件配置路徑,默認/tmp/redis.sock
 * perm: 文件訪問權限,默認700
 * backlog: listen方法參數,默認511
 **/
int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
{
    int s;
    struct sockaddr_un sa;

    if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)
        return ANET_ERR;

    memset(&sa,0,sizeof(sa));
    sa.sun_family = AF_LOCAL;
    strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
    if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
        return ANET_ERR;
    if (perm)
        chmod(sa.sun_path, perm);
    return s;
}

static int anetCreateSocket(char *err, int domain)
{
    int s;
    if ((s = socket(domain, SOCK_STREAM, 0)) == -1) {
        anetSetError(err, "creating socket: %s", strerror(errno));
        return ANET_ERR;
    }

    /* Make sure connection-intensive things like the redis benchmark
     * will be able to close/open sockets a zillion of times */
    if (anetSetReuseAddr(err,s) == ANET_ERR) {
        close(s);
        return ANET_ERR;
    }
    return s;
}

        看方法體發現這裏並沒有調用socketpair()方法創建一對unix域套接字,而是調用的anetCreateSocket()方法創建的,實際也就是socket()方法,只不過第一個參數是AF_LOCALAF_UNIX的別名),返回unix域套接字描述符並賦值給s。然而這時的unix域套接字是沒有名字的,無關進程還不能使用。

        anetUnixServer()方法中還定義了一個struct sockaddr_un結構體,定義如下:

struct sockaddr_un {
    sa_family_t  sun_family;      //AF_UNIX
    char         sun_path[108];   //pathname
};

        該結構的sun_path成員保存一個地址,redis就將該結構體和之前生成的套接字描述符s一起傳給anetListen()方法。

static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog)
{
    if (bind(s,sa,len) == -1) {
        anetSetError(err, "bind: %s", strerror(errno));
        close(s);
        return ANET_ERR;
    }

    if (listen(s, backlog) == -1) {
        anetSetError(err, "listen: %s", strerror(errno));
        close(s);
        return ANET_ERR;
    }
    return ANET_OK;
}

        該方法中主要就是幹了兩件事,先調用bind()方法將創建的unix域套接字s綁到結構體包含的地址上,然後調用listen()方法開始監聽。當啓動redis服務時,就會用該路徑名創建一個socket文件,也就是這裏的/tmp/redis.sock文件。注意該文件無法打開,也不能由應用程序用於通信。

        至此,redis中的unix域套接字部分就介紹到這裏,歡迎交流。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章