好用的进程间通信方式---UnixDomainSocket

好用的进程间通信方式---UnixDomainSocket

什么是unixsocket

socket大家应该很熟悉,以tcp/ip协议族为传输协议,用于跨主机通信,而unixsocket就是在socket的框架上发展出一种IPC机制(进程间通信),UDS(UNIX Domain Socket)提供面向流和面向数据包两种API接口,类似于TCP和UDP,其中SOCK_STREAM是很可靠的,消息既不会丢失也不会顺序错乱,比传统的socket效率更高,一般是tcp传输的两倍,并且不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程

unixsocket服务端和客户端连接及发送消息的过程如下:

unixsocket在k8s sidecar中的实践

unixsocket用途也很广泛,比如docker与宿主机的数据传输和信息交换:

其次,还有一个好处是,这两个进程完全可以用不同的语言写,然后都用unixsocket去通信。

好啦,说了这么多让我介绍一种k8s的pod业务场景,比如说我们主业务是一个数据分析或规则引擎,它是需要有一些配置可供用户在前端配置的,比如说规则的开关啊,数据分析文件大小啊等等,然后分析出来的结果也需要发送出去,向用户展示,或者是有一些指标需要定期进行健康检查,比如说cpu使用率啊,内存占用率啊等等,因为性能的要求,一般数据分析或规则匹配引擎都是C/C++写的,而C非常不擅长去与restful接口去通信,以及配置的json序列反序列化的字符串处理,因此,我可以用考虑在同一个pod中起一个sidecar,而这个sidecar服务可以用Go写,然后主服务和sidecar可以用unixsocket进行通信,前提只要这两者有一个共享挂载的目录,比如说是:"/root/unixsocket/sidecar.sock",整体思路如下所示:

//C服务端,无所谓了,两边都需要传一些信息

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
 
#define UNIX_DOMAIN "/root/unixsocket/sidecar.sock"
#define READ_FROM_CLIENT 0X01
#define WRITE_TO_CLIENT 0x02


int main(void)
{
	socklen_t clt_addr_len;
	int listen_fd;
	int com_fd;
	int ret;
	//int i;
	static char data_buf[1024];
	int len;
	struct sockaddr_un clt_addr;
	struct sockaddr_un srv_addr;
	listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
	if(listen_fd < 0) {
		perror("cannot create communication socket");
		return 1;
	}
	//set server addr_param
	srv_addr.sun_family = AF_UNIX;
	strcpy(srv_addr.sun_path,UNIX_DOMAIN);
	unlink(UNIX_DOMAIN);
	//bind sockfd & addr
	ret = bind(listen_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
	if(ret == -1) {
		perror("cannot bind server socket");
		close(listen_fd);
		unlink(UNIX_DOMAIN);
		return 1;
	}
	//listen sockfd
	ret = listen(listen_fd,1);
	if(ret == -1) {
		perror("cannot listen the client connect request");
		close(listen_fd);
		unlink(UNIX_DOMAIN);
		return 1;
	}
	while(1) {
		//have connect request use accept
		len = sizeof(clt_addr);
		com_fd = accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
		if(com_fd < 0) {
			perror("cannot accept client connect request");
			close(listen_fd);
			unlink(UNIX_DOMAIN);
			return 1;
		}
		//read and printf sent client info
		printf("\nReceive from client:\n");	
		memset(data_buf, 0, 1024);
		read(com_fd, data_buf, sizeof(data_buf));
		printf("Request is %s\n", data_buf);
		
                memset(data_buf, 0, 1024);
		strcpy(data_buf, "send message to client!!");
		write(com_fd, data_buf, sizeof(data_buf));
		printf("send message to client\n:");

		memset(data_buf, 0, 1024);
		close(com_fd);
	}
	close(listen_fd);
	unlink(UNIX_DOMAIN);
	return 0;
}

//Go客户端
package main

import (
        "bufio"
        "fmt"
        "net"
        "time"
)

var quitSemaphore chan bool

func main() {
        var unixAddr *net.UnixAddr
        unixAddr, _ = net.ResolveUnixAddr("unix", "/root/unixsocket/sidecar.sock")

        conn, _ := net.DialUnix("unix", nil, unixAddr)
        defer conn.Close()
        fmt.Println("connected!")

        go onMessageRecived(conn)

        b := []byte("time\n")
        conn.Write(b)

        <-quitSemaphore
}

func onMessageRecived(conn *net.UnixConn) {
        reader := bufio.NewReader(conn)
        for {
                msg, err := reader.ReadString('\n')
                fmt.Println(msg)
                if err != nil {
                        quitSemaphore <- true
                        break
                }
                time.Sleep(time.Second)
                b := []byte(msg)
                conn.Write(b)
        }
}

Reference

本地socket unix domain socket_bingqingsuimeng的博客-CSDN博客

本机网络 IO 之 Unix Domain Socket 性能分析 - 知乎 (zhihu.com)

blog.csdn.net/Nurke/art

go语言实现unix domain socket 客户端/服务端_ebayboy的技术博客_51CTO博客

编辑于 2022-10-03 21:07