英文:
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 (
"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))
}
答案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 <- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论