为什么我在Elixir中使用gen_tcp时一直遇到超时问题?

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

Why I keep getting timeout with gen_tcp in Elixir?

问题

我正在尝试实现一个简单的服务器(使用Go语言)/客户端(使用Elixir语言)。

然而,在我的请求到达服务器之前,我一直在Elixir端遇到超时问题。

服务器

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "net/http"
  6. "os"
  7. )
  8. func main() {
  9. socket := "/tmp/ipc_test.sock"
  10. os.Remove(socket)
  11. listener, err := net.Listen("unix", socket)
  12. if err != nil {
  13. fmt.Printf("Error listening on unix socket: %s\n", err)
  14. return
  15. }
  16. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  17. param := r.URL.Query().Get("param")
  18. fmt.Printf("%s", param)
  19. fmt.Fprint(w, param)
  20. })
  21. fmt.Printf("Server listening on %s\n", socket)
  22. http.Serve(listener, nil)
  23. }

客户端

  1. defmodule IpcTest do
  2. def make_request(socket, request) do
  3. :gen_tcp.send(socket, request)
  4. {:ok, response} = :gen_tcp.recv(socket, 0)
  5. :gen_tcp.close(socket)
  6. response
  7. end
  8. def print_responses(responses) do
  9. responses
  10. |> Enum.each(&IO.puts/1)
  11. end
  12. def run(num_requests) do
  13. 1..num_requests
  14. |> Enum.map(fn i ->
  15. request = "/?param=#{i}"
  16. Task.async(fn ->
  17. {:ok, socket} =
  18. :gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 0, [:binary, packet: 4, active: false])
  19. make_request(socket, request)
  20. end)
  21. end)
  22. |> Enum.map(&Task.await/1)
  23. end
  24. end
  1. > go run src/main.go
  2. Server listening on /tmp/js_runner.sock

如你所见,服务器似乎运行正常。
然而,在我的客户端中,我遇到了以下错误。

  1. > iex -S mix
  2. > IpcTest.run 1
  3. ** (exit) exited in: Task.await(%Task{mfa: {:erlang, :apply, 2}, owner: #PID<0.237.0>, pid: #PID<0.263.0>, ref: #Reference<0.3740412160.2895708166.190144>}, 5000)
  4. ** (EXIT) time out
  5. (elixir 1.14.2) lib/task.ex:830: Task.await/2
  6. (elixir 1.14.2) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
  7. iex:2: (file)
英文:

I am trying to implement a simple Server (in Go) / Client (in Elixir).

However, I keep getting timeouts on Elixir side before my request reaches the server.

Server

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;net&quot;
  5. &quot;net/http&quot;
  6. &quot;os&quot;
  7. )
  8. func main() {
  9. socket := &quot;/tmp/ipc_test.sock&quot;
  10. os.Remove(socket)
  11. listener, err := net.Listen(&quot;unix&quot;, socket)
  12. if err != nil {
  13. fmt.Printf(&quot;Error listening on unix socket: %s\n&quot;, err)
  14. return
  15. }
  16. http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
  17. param := r.URL.Query().Get(&quot;param&quot;)
  18. fmt.Printf(&quot;%s&quot;, param)
  19. fmt.Fprint(w, param)
  20. })
  21. fmt.Printf(&quot;Server listening on %s\n&quot;, socket)
  22. http.Serve(listener, nil)
  23. }

Client

  1. defmodule IpcTest do
  2. def make_request(socket, request) do
  3. :gen_tcp.send(socket, request)
  4. {:ok, response} = :gen_tcp.recv(socket, 0)
  5. :gen_tcp.close(socket)
  6. response
  7. end
  8. def print_responses(responses) do
  9. responses
  10. |&gt; Enum.each(&amp;IO.puts/1)
  11. end
  12. def run(num_requests) do
  13. 1..num_requests
  14. |&gt; Enum.map(fn i -&gt;
  15. request = &quot;/?param=#{i}&quot;
  16. Task.async(fn -&gt;
  17. {:ok, socket} =
  18. :gen_tcp.connect({:local, &quot;/tmp/ipc_test.sock&quot;}, 0, [:binary, packet: 4, active: false])
  19. make_request(socket, request)
  20. end)
  21. end)
  22. |&gt; Enum.map(&amp;Task.await/1)
  23. end
  24. end
  1. &gt; go run src/main.go
  2. Server listening on /tmp/js_runner.sock

As you can see, the server seems to be running fine.
However, I am getting the following error in my client.

  1. &gt; iex -S mix
  2. &gt; IpcTest.run 1
  3. ** (exit) exited in: Task.await(%Task{mfa: {:erlang, :apply, 2}, owner: #PID&lt;0.237.0&gt;, pid: #PID&lt;0.263.0&gt;, ref: #Reference&lt;0.3740412160.2895708166.190144&gt;}, 5000)
  4. ** (EXIT) time out
  5. (elixir 1.14.2) lib/task.ex:830: Task.await/2
  6. (elixir 1.14.2) lib/enum.ex:1658: Enum.&quot;-map/2-lists^map/1-0-&quot;/2
  7. iex:2: (file)

答案1

得分: 0

默认情况下,:gen_tcp.connect/3函数的超时值为5000毫秒(5秒)。将其增加到10秒:

  1. {:ok, socket} = :gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 10000, [])

但你也可以尝试以下命令:

  1. socat - UNIX-CONNECT:/tmp/ipc_test.sock

或者参考这个链接中的其他方法:https://unix.stackexchange.com/questions/26715/how-can-i-communicate-with-a-unix-domain-socket-via-the-shell-on-debian-squeeze

以查看该套接字文件是否对任何正常的Linux命令行工具有效。

英文:

By default, the :gen_tcp.connect/3 function has a timeout value of 5000 milliseconds (5 seconds). Increase to 10:

  1. {:ok, socket} = :gen_tcp.connect({:local, &quot;/tmp/ipc_test.sock&quot;}, 10000, [:

But you should also try stuff like

  1. socat - UNIX-CONNECT:/tmp/ipc_test.sock

Or the other stuff in: https://unix.stackexchange.com/questions/26715/how-can-i-communicate-with-a-unix-domain-socket-via-the-shell-on-debian-squeeze

to see if the socket file is even working for ANY normal linux cli tool.

答案2

得分: 0

搞定了!我的问题实际上出在基于Go的服务器中。

以下是更新后的代码,如果有人感兴趣的话:

  1. package main
  2. import (
  3. "io"
  4. "log"
  5. "net"
  6. "os"
  7. )
  8. const SockAddr = "/tmp/ipc_test.sock"
  9. func echoServer(c net.Conn) {
  10. log.Printf("客户端已连接 [%s]", c.RemoteAddr().Network())
  11. io.Copy(c, c)
  12. c.Close()
  13. }
  14. func main() {
  15. if err := os.RemoveAll(SockAddr); err != nil {
  16. log.Fatal(err)
  17. }
  18. l, err := net.Listen("unix", SockAddr)
  19. if err != nil {
  20. log.Fatal("监听错误:", err)
  21. }
  22. defer l.Close()
  23. for {
  24. conn, err := l.Accept()
  25. if err != nil {
  26. log.Fatal("接受错误:", err)
  27. }
  28. go echoServer(conn)
  29. }
  30. }

参考链接:https://eli.thegreenplace.net/2019/unix-domain-sockets-in-go

英文:

Figured it out! My issue was actually in the Go based server.

Here is the updated code if anyone is interested in:

  1. package main
  2. import (
  3. &quot;io&quot;
  4. &quot;log&quot;
  5. &quot;net&quot;
  6. &quot;os&quot;
  7. )
  8. const SockAddr = &quot;/tmp/ipc_test.sock&quot;
  9. func echoServer(c net.Conn) {
  10. log.Printf(&quot;Client connected [%s]&quot;, c.RemoteAddr().Network())
  11. io.Copy(c, c)
  12. c.Close()
  13. }
  14. func main() {
  15. if err := os.RemoveAll(SockAddr); err != nil {
  16. log.Fatal(err)
  17. }
  18. l, err := net.Listen(&quot;unix&quot;, SockAddr)
  19. if err != nil {
  20. log.Fatal(&quot;listen error:&quot;, err)
  21. }
  22. defer l.Close()
  23. for {
  24. conn, err := l.Accept()
  25. if err != nil {
  26. log.Fatal(&quot;accept error:&quot;, err)
  27. }
  28. go echoServer(conn)
  29. }
  30. }

Reference: https://eli.thegreenplace.net/2019/unix-domain-sockets-in-go

huangapple
  • 本文由 发表于 2023年7月13日 23:25:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76681056.html
匿名

发表评论

匿名网友

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

确定