开始一个套接字服务器会干扰 Golang 中的 gRPC/HTTP 客户端服务器通信。

huangapple go评论147阅读模式
英文:

Starting a socket server interferes with the gRPC/http client server communication Golang

问题

我有一个服务器,它在这个教程的基础上进行了大量的修改。在我应用了额外的更改之后,它工作得很好。但是现在我尝试添加 socket.io 后,它出现了一些问题。在测试我添加的 socket.io 代码时,似乎影响了使客户端代码(端点)调用服务器端代码(数据库查询、处理)的机制。调用到达服务器端的客户端,因为终端上出现了一个端点调用的日志,但似乎并没有调用服务器端。

这是 socket 服务器的代码:

package helpers

import (
	"fmt"
	"net/http"

	socketio "github.com/googollee/go-socket.io"
	"github.com/googollee/go-socket.io/engineio"
	"github.com/googollee/go-socket.io/engineio/transport"
	"github.com/googollee/go-socket.io/engineio/transport/polling"
	"github.com/googollee/go-socket.io/engineio/transport/websocket"
)

var allowOriginFunc = func(r *http.Request) bool {
	return true
}

func StartSocket() {
	server := socketio.NewServer(&engineio.Options{
		Transports: []transport.Transport{
			&polling.Transport{
				CheckOrigin: allowOriginFunc,
			},
			&websocket.Transport{
				CheckOrigin: allowOriginFunc,
			},
		},
	})

	server.OnConnect("/", func(s socketio.Conn) error {
		s.SetContext("")
		fmt.Println("connected:", s.ID())
		return nil
	})

	server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
		fmt.Println("notice:", msg)
		s.Emit("reply", "have "+msg)
	})

	server.OnError("/", func(s socketio.Conn, e error) {
		fmt.Println("socket error:", e)
	})

	server.OnDisconnect("/", func(s socketio.Conn, reason string) {
		fmt.Println("closed", reason)
	})

	go server.Serve()
	defer server.Close()

	http.Handle("/socket.io/", server)
	http.Handle("/", http.FileServer(http.Dir("./asset")))

	fmt.Println("Socket server serving at localhost:8000...")
	fmt.Print(http.ListenAndServe(":8000", nil))
}

// main.go 服务器端

package main

import (
	"flag"
	"fmt"
	"log"
	"net"

	pb "github.com/<me>/<project_name>/api/proto/out"
	"github.com/<me>/<project_name>/cmd/server/handlers"
	"github.com/<me>/<project_name>/cmd/server/helpers"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func init() {
	helpers.DatabaseConnection()
}

var (
	tls      = flag.Bool("tls", true, "Connection uses TLS if true, else plain TCP")
	certFile = flag.String("cert_file", "", "The TLS cert file")
	keyFile  = flag.String("key_file", "", "The TLS key file")
	port     = flag.Int("port", 50051, "The server port")
)

func main() {
	flag.Parse()

	// helpers.StartSocket()

	lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	var opts []grpc.ServerOption
	if *tls {
		if *certFile == "" {
			*certFile = "service.pem"
		}

		if *keyFile == "" {
			*keyFile = "service.key"
		}

		creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)

		if err != nil {
			log.Fatalf("Failed to generate credentials: %v", err)
		}

		opts = []grpc.ServerOption{grpc.Creds(creds)}
	}

	mServ := grpc.NewServer(opts...)

	fmt.Println("gRPC server running ...")

	pb.RegisterSomethingServiceServer(mServ, &handlers.SomethingServer{})

	log.Printf("Server listening at %v", lis.Addr())

	if err := mServ.Serve(lis); err != nil {
		log.Fatalf("failed to serve : %v", err)
	}
}

// main.go 客户端端

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/<me>/<project_name>/cmd/client/handlers"

	"github.com/gin-gonic/gin"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
)

func main() {
	flag.Parse()

	creds, err := credentials.NewClientTLSFromFile("service.pem", "")

	if err != nil {
		log.Fatalf("could not process the credentials: %v", err)
	}

	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds))

	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}

	defer conn.Close()

	var ginGine = gin.Default()
	StartClient(conn, ginGine)
}

func StartClient(conn *grpc.ClientConn, ginGine *gin.Engine) {

	handlers.SomethingApiHandler(conn, ginGine)

	ginGine.Run(":5000")
}

关于完整的客户端和服务器端代码,你可以查看我上面链接的教程。如果我不调用 StartSocket(),一切都正常工作。

当调用我的 API 的一个端点时,我得到了这个错误,并且它是由调用服务器端代码的代码抛出的:

"rpc error: code = Unavailable desc = connection error: desc =
"transport: Error while dialing: dial tcp [::1]:50051: connectex: No
connection could be made because the target machine actively refused
it.""

下面是代码的样子:

ginGine.POST("/account/login", func(ctx *gin.Context) {
	var account models.Account

	err := ctx.ShouldBind(&account)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"error1": err.Error(),
		})
		return
	}

	res, err := srvClient.service.ValidateAccount(ctx, &pb.ValidateAccountRequest{
		Account: &pb.Account{
			Id:        account.ID,
			FirstName: account.FirstName,
			LastName:  account.LastName,
			UserName:  account.UserName,
			Password: account.Password,
		},
	})

	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"error2": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":       res.Status,
		"access_token": res.AccessToken,
	})
})

error2 是在 API 调用时返回的错误。

英文:

I have a server that is heavily based on this tutorial. It works fine after the additional changes I applied to it. But now that I'm trying to add socket.io to it, it now is having some issue. Upon testing the code for socket.io that I added, it seems like it's affecting the mechanism that makes the client code(endpoints) call the server side code(db query, processing). The call reaches the client side of the server as the log for the an endpoint call appears on the terminal, but it doesn't seem to be calling the server side.

Here's the code for the socket server:

package helpers
import (
&quot;fmt&quot;
&quot;net/http&quot;
socketio &quot;github.com/googollee/go-socket.io&quot;
&quot;github.com/googollee/go-socket.io/engineio&quot;
&quot;github.com/googollee/go-socket.io/engineio/transport&quot;
&quot;github.com/googollee/go-socket.io/engineio/transport/polling&quot;
&quot;github.com/googollee/go-socket.io/engineio/transport/websocket&quot;
)
var allowOriginFunc = func(r *http.Request) bool {
return true
}
func StartSocket() {
server := socketio.NewServer(&amp;engineio.Options{
Transports: []transport.Transport{
&amp;polling.Transport{
CheckOrigin: allowOriginFunc,
},
&amp;websocket.Transport{
CheckOrigin: allowOriginFunc,
},
},
})
server.OnConnect(&quot;/&quot;, func(s socketio.Conn) error {
s.SetContext(&quot;&quot;)
fmt.Println(&quot;connected:&quot;, s.ID())
return nil
})
server.OnEvent(&quot;/&quot;, &quot;notice&quot;, func(s socketio.Conn, msg string) {
fmt.Println(&quot;notice:&quot;, msg)
s.Emit(&quot;reply&quot;, &quot;have &quot;+msg)
})
server.OnError(&quot;/&quot;, func(s socketio.Conn, e error) {
fmt.Println(&quot;socket error:&quot;, e)
})
server.OnDisconnect(&quot;/&quot;, func(s socketio.Conn, reason string) {
fmt.Println(&quot;closed&quot;, reason)
})
go server.Serve()
defer server.Close()
http.Handle(&quot;/socket.io/&quot;, server)
http.Handle(&quot;/&quot;, http.FileServer(http.Dir(&quot;./asset&quot;)))
fmt.Println(&quot;Socket server serving at localhost:8000...&quot;)
fmt.Print(http.ListenAndServe(&quot;:8000&quot;, nil))
}

// main.go server side

package main
import (
&quot;flag&quot;
&quot;fmt&quot;
&quot;log&quot;
&quot;net&quot;
pb &quot;github.com/&lt;me&gt;/&lt;project_name&gt;/api/proto/out&quot;
&quot;github.com/&lt;me&gt;/&lt;project_name&gt;/cmd/server/handlers&quot;
&quot;github.com/&lt;me&gt;/&lt;project_name&gt;/cmd/server/helpers&quot;
&quot;google.golang.org/grpc&quot;
&quot;google.golang.org/grpc/credentials&quot;
)
func init() {
helpers.DatabaseConnection()
}
var (
tls      = flag.Bool(&quot;tls&quot;, true, &quot;Connection uses TLS if true, else plain TCP&quot;)
certFile = flag.String(&quot;cert_file&quot;, &quot;&quot;, &quot;The TLS cert file&quot;)
keyFile  = flag.String(&quot;key_file&quot;, &quot;&quot;, &quot;The TLS key file&quot;)
port     = flag.Int(&quot;port&quot;, 50051, &quot;The server port&quot;)
)
func main() {
flag.Parse()
// helpers.StartSocket()
lis, err := net.Listen(&quot;tcp&quot;, fmt.Sprintf(&quot;localhost:%d&quot;, *port))
if err != nil {
log.Fatalf(&quot;failed to listen: %v&quot;, err)
}
var opts []grpc.ServerOption
if *tls {
if *certFile == &quot;&quot; {
*certFile = &quot;service.pem&quot;
}
if *keyFile == &quot;&quot; {
*keyFile = &quot;service.key&quot;
}
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
if err != nil {
log.Fatalf(&quot;Failed to generate credentials: %v&quot;, err)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
}
mServ := grpc.NewServer(opts...)
fmt.Println(&quot;gRPC server running ...&quot;)
pb.RegisterSomethingServiceServer(mServ, &amp;handlers.SomethingServer{})
log.Printf(&quot;Server listening at %v&quot;, lis.Addr())
if err := mServ.Serve(lis); err != nil {
log.Fatalf(&quot;failed to serve : %v&quot;, err)
}
}

// main.go client-side

package main
import (
&quot;bytes&quot;
&quot;encoding/json&quot;
&quot;flag&quot;
&quot;fmt&quot;
&quot;log&quot;
&quot;sort&quot;
&quot;strings&quot;
&quot;github.com/&lt;me&gt;/&lt;project_name&gt;/cmd/client/handlers&quot;
&quot;github.com/gin-gonic/gin&quot;
&quot;google.golang.org/grpc&quot;
&quot;google.golang.org/grpc/credentials&quot;
)
var (
addr = flag.String(&quot;addr&quot;, &quot;localhost:50051&quot;, &quot;the address to connect to&quot;)
)
func main() {
flag.Parse()
creds, err := credentials.NewClientTLSFromFile(&quot;service.pem&quot;, &quot;&quot;)
if err != nil {
log.Fatalf(&quot;could not process the credentials: %v&quot;, err)
}
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatalf(&quot;did not connect: %v&quot;, err)
}
defer conn.Close()
var ginGine = gin.Default()
StartClient(conn, ginGine)
}
func StartClient(conn *grpc.ClientConn, ginGine *gin.Engine) {
handlers.SomethingApiHandler(conn, ginGine)
ginGine.Run(&quot;:5000&quot;)
}

For the full client and server code, you can check the tutorial I linked above. If I don't call StartSocket(), everything works fine.

When calling the an endpoint of my API, I get this error, and it's throw by the code calling the server side code:

> "rpc error: code = Unavailable desc = connection error: desc =
> &quot;transport: Error while dialing: dial tcp [::1]:50051: connectex: No
> connection could be made because the target machine actively refused
> it.&quot;"

Here's what the code looks like:

ginGine.POST(&quot;/account/login&quot;, func(ctx *gin.Context) {
var account models.Account
err := ctx.ShouldBind(&amp;account)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
&quot;error1&quot;: err.Error(),
})
return
}
res, err := srvClient.service.ValidateAccount(ctx, &amp;pb.ValidateAccountRequest{
Account: &amp;pb.Account{
Id:        account.ID,
FirstName: account.FirstName,
LastName:  account.LastName,
UserName:  account.UserName,
Password: account.Password,
},
})
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
&quot;error2&quot;: err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
&quot;status&quot;:       res.Status,
&quot;access_token&quot;: res.AccessToken,
})
})

error2 is what gets returned on the API call

答案1

得分: 0

这行代码是阻塞的,函数StartSocket()永远不会返回:

fmt.Print(http.ListenAndServe(":8000", nil))

为了测试,你可以在它之后添加日志,该日志消息将不会被打印出来。

你需要在一个单独的非阻塞的goroutine中运行StartSocket()

附注:在示例中,那行代码是log.Fatal(http.ListenAndServe(":8000", nil))。你的代码忽略了一个错误。

英文:

This line of code is blocking and function StartSocket() never returns:

fmt.Print(http.ListenAndServe(&quot;:8000&quot;, nil))

To test, you can add log after it and that log message will be not printed.

You need to run StartSocket() in a separate not blocking goroutine.

P.S. In example, that line is log.Fatal(http.ListenAndServe(&quot;:8000&quot;, nil)). Your code swallows an error.

huangapple
  • 本文由 发表于 2023年7月4日 15:20:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76610192.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定