当使用golang的exec.Command或os.StartProcess与mysql一起使用时。

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

when use golang exec.Command or os.StartProcess with mysql

问题

我想使用Golang编写一个应用程序,通过WebSocket执行SQL或命令。我使用了以下包:"github.com/gorilla/websocket"、"os/exec"和"os"。

我已经完成了redis-cli和mongoshell的部分。但是当我使用mysql客户端连接到mysql时,无法接收到进度的消息。但是当我将进度(mysql客户端)的输出设置为标准输出时,可以显示消息。当发送错误的SQL到进度时,进度会被未知原因关闭。

可以通过github.com/gorilla/websocket的示例来重现这个问题,链接如下:
https://github.com/gorilla/websocket/blob/master/examples/command/main.go

以下是我的代码:

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os/exec"
	"time"

	"github.com/gorilla/websocket"
)

var (
	addr = flag.String("addr", "127.0.0.1:8080", "http service address")
)

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// Maximum message size allowed from peer.
	maxMessageSize = 8192

	// Time allowed to read the next pong message from the peer.
	pongWait = 20 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

func ping(ws *websocket.Conn, done chan struct{}) {
	ticker := time.NewTicker(pingPeriod)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
				log.Println("ping:", err)
			}
			done <- struct{}{}
		case <-done:
			return
		}
	}
}

func pumpStdin(ws *websocket.Conn, w io.Writer) {
	defer ws.Close()
	ws.SetReadLimit(maxMessageSize)
	ws.SetReadDeadline(time.Now().Add(pongWait))
	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := ws.ReadMessage()
		if err != nil {
			break
		}
		message = append(message, '\n')
		if _, err := w.Write(message); err != nil {
			break
		}
	}
}

func pumpStdout(ws *websocket.Conn, r io.ReadCloser, done chan struct{}) {
	for {
		x := make([]byte, 1024)
		n, err := r.Read(x)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println(string(x[:n]))
	}
}

var upgrader = websocket.Upgrader{}

func serveWs(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("upgrade:", err)
		return
	}

	defer ws.Close()

	stopchan := make(chan struct{})
	cmd := exec.Command("/usr/bin/mysql", "-h127.0.0.1", "-ppassword", "-P3306", "-uroot")

	cmdin, err := cmd.StdinPipe()
	if err != nil {
		fmt.Println(err)
	}
	cmdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println(err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}
	go ping(ws, stopchan)
	go pumpStdout(ws, cmdout, stopchan)
	pumpStdin(ws, cmdin)
	if err = cmd.Wait(); err != nil {
		fmt.Println(err)
	}
}

func main() {
	flag.Parse()
	http.HandleFunc("/ws", serveWs)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

希望这可以帮助到你。

英文:

i want to make a application whith golang that can exec sql or command through websocket,i use package "github.com/gorilla/websocket","os/exec","os",

i have finished redis-cli and mongoshell.
but when i use mysql client to connect with mysql, i can't recieve message from the progress.but when i only set the progress(mysql client) output to stdout,it can display.And when an error sql send to the progress,it closed by unknown.
it can be recurrent through the example of github.com/gorilla/websocket,
https://github.com/gorilla/websocket/blob/master/examples/command/main.go

and this is my code

package main

import (
	&quot;flag&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;os/exec&quot;
	&quot;time&quot;

	&quot;github.com/gorilla/websocket&quot;
)

var (
	addr    = flag.String(&quot;addr&quot;, &quot;127.0.0.1:8080&quot;, &quot;http service address&quot;)
)

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// Maximum message size allowed from peer.
	maxMessageSize = 8192

	// Time allowed to read the next pong message from the peer.
	pongWait = 20 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10


)

func ping(ws *websocket.Conn, done chan struct{}) {
	ticker := time.NewTicker(pingPeriod)
	defer ticker.Stop()
	for {
		select {
		case &lt;-ticker.C:
			if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
				log.Println(&quot;ping:&quot;, err)
			}
			done &lt;- struct{}{}
		case &lt;-done:
			return
		}
	}
}

func pumpStdin(ws *websocket.Conn, w io.Writer) {
	defer ws.Close()
	ws.SetReadLimit(maxMessageSize)
	ws.SetReadDeadline(time.Now().Add(pongWait))
	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := ws.ReadMessage()
		if err != nil {
			break
		}
		message = append(message, &#39;\n&#39;)
		if _, err := w.Write(message); err != nil {
			break
		}
	}
}

func pumpStdout(ws *websocket.Conn, r io.ReadCloser, done chan struct{}) {
	for {
		x := make([]byte, 1024)
		n, err := r.Read(x)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println(string(x[:n]))
	}
}

var upgrader = websocket.Upgrader{}

func serveWs(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(&quot;upgrade:&quot;, err)
		return
	}

	defer ws.Close()
	
	stopchan := make(chan struct{})
	cmd := exec.Command(&quot;/usr/bin/mysql&quot;, &quot;-h127.0.0.1&quot;, &quot;-ppassword&quot;, &quot;-P3306&quot;, &quot;-uroot&quot;)
	
	cmdin, err := cmd.StdinPipe()
	if err != nil {
		fmt.Println(err)
	}
	cmdout, err := cmd.StdoutPipe()
	if err!=nil{
		fmt.Println(err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}
	go ping(ws, stopchan)
	go pumpStdout(ws, cmdout, stopchan)
	pumpStdin(ws, cmdin)
	if err = cmd.Wait(); err != nil {
		fmt.Println(err)
	}

}

func main() {
	flag.Parse()
	http.HandleFunc(&quot;/ws&quot;, serveWs)
	log.Fatal(http.ListenAndServe(*addr, nil))
}


答案1

得分: 0

func pumpStdout(ws *websocket.Conn, r io.ReadCloser, done chan struct{}) {
    defer ws.Close()
    defer r.Close()

    buf := make([]byte, 1024)
    for {
        n, err := r.Read(buf)
        if err != nil {
            fmt.Println(err)
            break
        }

        output := buf[:n]
        if _, err := ws.Write(output); err != nil {
            fmt.Println(err)
            break
        }
        
        // 刷新输出缓冲区
        if fw, ok := ws.UnderlyingConn().(*net.TCPConn); ok {
            if err := fw.SetWriteDeadline(time.Now().Add(writeWait)); err != nil {
                fmt.Println(err)
                break
            }
            if err := fw.Flush(); err != nil {
                fmt.Println(err)
                break
            }
        }
    }

    done <- struct{}{}
}

这是一个用于将标准输出发送到 WebSocket 连接的函数。它从一个 io.ReadCloser 接口读取数据,并将数据写入 WebSocket 连接。在写入数据之前,它会刷新输出缓冲区。函数使用一个 done 通道来通知调用者任务已完成。

英文:
func pumpStdout(ws *websocket.Conn, r io.ReadCloser, done chan struct{}) {
defer ws.Close()
defer r.Close()
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if err != nil {
fmt.Println(err)
break
}
output := buf[:n]
if _, err := ws.Write(output); err != nil {
fmt.Println(err)
break
}
// Flush the output buffer
if fw, ok := ws.UnderlyingConn().(*net.TCPConn); ok {
if err := fw.SetWriteDeadline(time.Now().Add(writeWait)); err != nil {
fmt.Println(err)
break
}
if err := fw.Flush(); err != nil {
fmt.Println(err)
break
}
}
}
done &lt;- struct{}{}

}

答案2

得分: 0

我发现MySQL客户端有一个缓冲区打开,当我使用参数--unbuffered时,输出将发送到我的pumpStdout。而且,通过添加参数--force,也可以修复进程退出的问题。这些方法都有效。如果你有更好的解决方案,请告诉我,谢谢!关于MySQL的详细信息,请参考--help命令。

> -n, --unbuffered 在每个查询之后刷新缓冲区。

英文:

i found that the mysql client have buffer open,when i give a arg --unbuffered,the output will send to my pumpStdout.
and the progress exit also can fix with add an arg,--force.
and all this works.
and if you have better solution,tell me,thank you!
Detial for mysql --help

> -n, --unbuffered Flush buffer after each query.

huangapple
  • 本文由 发表于 2023年7月7日 08:44:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76633286.html
匿名

发表评论

匿名网友

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

确定