一、问题背景
实习所在公司的A、B两个应用部署在同一台机器上,之前是采用http://127.0.0.1:xxx进行调用,协议选用tcp。而对于同一台机器的进程间通信来说,tcp协议为了确保传输的可靠性带来了一些不必要的负担,因此计划用unix domain socket“取代”tcp。此外采用此种方式也方便后续运维人员维护。
二、什么是unix socket
关注重点在于:进程间通信,同一台主机。更详细的可以自行上网搜索。
三、原生的unix socket通信方式
go中原生的net包包含了对unix socket通信的封装,关键语句为:
服务端:
const sockAddr="file path"
l, err := net.Listen("unix", SockAddr)
注意同tcp方式不同在于,这里的地址不再是一个类似127.0.0.1:xxx这样的端口号,而是一个.sock文件的地址,文件地址可以自己选定。
设定地址和sock文件名后,服务端启动成功后会在对应目录生成该sock文件。
由于已经存在该文件,服务会启动失败,因此在每次启动前都应该先移除该文件。
os.Remove(sockAddr)
注意使用Remove要慎重,更改参数要小心,不要一不小心把自己本身重要的东西删除了。
然后监听成功后就可以进行对应响应了:
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
go handler(conn)
}
func handler(conn *net.UnixConn){
...
}
客户端:
关键语句为:
client, err := net.Dial("unix", sockAddr)
四、http+unix socket通信,服务端基于gin框架
由于http协议位于应用程序,tcp位于传输层,socket位于两层之间,因此http和unix socket是并不冲突的,虽然只依靠unix socket就能实现两个进程间的通信,但是考虑到原有项目是基于http的,因此还是需要保留http调用方式(否则项目改动过大)。
并且原先服务端是采用gin框架做http路由调用,因此这里仍然不变。
实现的一个小demo如下:
服务端代码:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net"
"net/http"
"os"
)
const sockAddr="D:/GoCode/go/src/http_unix_socket_demo/http.sock"
func main(){
router:=gin.New()
router.GET("/testGet",handlerGet)
os.Remove(sockAddr)
unixAddr, err := net.ResolveUnixAddr("unix", sockAddr)
if err!=nil{
fmt.Println(err)
return
}
listener, err := net.ListenUnix("unix", unixAddr)
if err!=nil{
fmt.Println("listening error:",err)
}
fmt.Println("listening...")
http.Serve(listener,router)
}
func handlerGet(c *gin.Context){
c.JSON(http.StatusOK,gin.H{
"resp":"ok",
})
}
客户端代码:
由于客户端服务端在同一个module下面,因此将客户端写在Test方法里面。
package main
import (
"context"
"fmt"
"net"
"net/http"
"testing"
)
func TestClientGet(t *testing.T){
httpc := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix",sockAddr)
},
},
}
resp, err := httpc.Get("http://http.sock/testGet")
if err!=nil{
t.Fail()
return
}
fmt.Println(resp)
}
五、demo的实现移步到项目中
这里初步在两个项目中实现了一个接口的unix socket通信,还存在很多瑕疵,细节后续会完善。
然后对实现的该接口进行了两组测试对比。
原项目采用http+tcp的测试结果如下:
目前采用http+unix socket的测试结果如下:
六、结论
可以看出在同等条件下,unix socket确实比tcp socket快上一点,多次测试的也是如此。这里大概快了七分之一左右。也印证了tcp保证的可靠传输(校验和,流量控制等)确实会导致一些效率上的牺牲。
七、后续工作
后续需要调通java端和golang端的unix socket 通信