Concurrently send bytes coming from stream to two destinations - go
What's the best way to broadcast values from a stream to two network destination simultaneously? Here is simplified code:
func main() {
resp, _ := http.Get("http://origin.com/image.jpeg")
var buf bytes.Buffer
// tee, when read, writes to &buf
respBodyTee := io.TeeReader(resp.Body, &buf)
sendToClient(respBodyTee)
uploadeToServer(&buf)
}
A stream cannot be read twice, so TeeReader is used to populate &buf with whatever is read from respo.Body.
However, the functions (sendToClient and uploadToServer) will run synchronously while I'd like to them to make their work concurrently.
Solution that I have on mind is to pass a channel to sendToClient that will populate channel with bytes already sent to client. Later have uploadToServer read from the same channel. Something along these lines:
func main() {
resp, _ := http.Get("http://origin.com/image.jpeg")
ch := make(chan byte)
go sendToClient(respBodyTee, ch) // pass 'ch' for writing and run in a goroutine
uploadeToServer(ch) // will read from 'ch' (synchronous)
}
I'm new to Go and am not sure if the above is the right direction.
in your scenario, it's better to have 2 independent byte streams for 2 network calls. If they rely on one source stream, when sendToClient stalls uploadeToServer will hang. channel wouldn't solve the problem above but introducing locking overhead.
you can try io.MultiWriter to make 2 independent byte streams
var buf1, buf2 bytes.Buffer
mw := io.MultiWriter(&buf1, &buf2)
if _, err := io.Copy(mw, r.Body); err != nil {
...
}
go sendToClient(buf1)
go uploadeToServer(buf2)
...
Related
Streaming values from websocket, determining if I am lagging behind processing the data
I am connecting to a websocket that is stream live stock trades. I have to read the prices, perform calculations on the fly and based on these calculations make another API call e.g. buy or sell. I want to ensure my calculations/processing doesn't slow down my ability to stream in all the live data. What is a good design pattern to follow for this type of problem? Is there a way to log/warn in my system to know if I am falling behind? Falling behind means: the websocket is sending price data, and I am not able to process that data as it comes in and it is lagging behind. While doing the c.ReadJSON and then passing the message to my channel, there might be a delay in deserializing into JSON When inside my channel and processing, calculating formulas and sending another API request to buy/sell, this will add delays How can I prevent lags/delays and also monitor if indeed there is a delay? func main() { c, _, err := websocket.DefaultDialer.Dial("wss://socket.example.com/stocks", nil) if err != nil { panic(err) } defer c.Close() // Buffered channel to account for bursts or spikes in data: chanMessages := make(chan interface{}, 10000) // Read messages off the buffered queue: go func() { for msgBytes := range chanMessages { logrus.Info("Message Bytes: ", msgBytes) } }() // As little logic as possible in the reader loop: for { var msg interface{} err := c.ReadJSON(&msg) if err != nil { panic(err) } chanMessages <- msg } }
You can read bytes, pass them to the channel, and use other goroutines to do conversion.
I worked on a similar crypto market bot. Instead of creating large buffured channel i created buffered channel with cap of 1 and used select statement for sending socket data to channel. Here is the example var wg sync.WaitGroup msg := make(chan []byte, 1) wg.Add(1) go func() { defer wg.Done() for data := range msg { // decode and process data } }() for { _, data, err := c.ReadMessage() if err != nil { log.Println("read error: ", err) return } select { case msg <- data: // in case channel is free default: // if not, next time will try again with latest data } } This will insure that you'll get the latest data when you are ready to process.
Data race issues while reading data from file and sending it simultaneously
I am trying to read data from a file and send it immediately the chunk read without waiting the other goroutine to complete file read. I have two function func ReadFile(stream chan []byte, stop chan bool) { file.Lock() defer file.Unlock() dir, _ := os.Getwd() file, _ := os.Open(dir + "/someFile.json") chunk := make([]byte, 512*4) for { bytesRead, err := file.Read(chunk) if err == io.EOF { break } if err != nil { panic(err) break } stream <- chunk[:bytesRead] } stop <- true } First one is responsible for reading the file and sending data chunks to the "stream" channel received from the other function func SteamFile() { stream := make(chan []byte) stop := make(chan bool) go ReadFile(stream, stop) L: for { select { case data := <-stream: //want to send data chunk by socket here fmt.Println(data) case <-stop: break L } } } Second function creates 2 channel, sends them to the first function and starts to listen to the channels. The problem is that sometimes data := <-stream is missing. I do not receive for example the first part of the file but receive all the others. When I run the program with -race flag it tells that there is a data race and two goroutines write to and read from the channel simultaneously. To tell the truth I thought that's the normal way to use channels but now I see that it brings to even more issues. Can someone please help me to solve this issue?
When I run the program with -race flag it tells that there is a data race and two goroutines write to and read from the channel simultaneously. To tell the truth I thought that's the normal way do use channels. It is, and you are almost certainly misunderstanding the output of the race detector. You're sharing the slice in ReadFile (and therefore it's underlying array), so the data is overwritten in ReadFile while it's being read in SteamFile [sic]. While this shouldn't trigger the race detector it is definitely a bug. Create a new slice for each Read call: func ReadFile(stream chan []byte, stop chan bool) { file.Lock() defer file.Unlock() dir, _ := os.Getwd() file, _ := os.Open(dir + "/someFile.json") - chunk := make([]byte, 512*4) for { + chunk := make([]byte, 512*4) bytesRead, err := file.Read(chunk)
Synchronizing two goroutines with a size 1 channel
In an effort to learn golang, I was looking through the go source for reverseproxy: https://golang.org/src/net/http/httputil/reverseproxy.go I found this block of code (truncated): ... errc := make(chan error, 1) spc := switchProtocolCopier{user: conn, backend: backConn} go spc.copyToBackend(errc) go spc.copyFromBackend(errc) <-errc return } // switchProtocolCopier exists so goroutines proxying data back and // forth have nice names in stacks. type switchProtocolCopier struct { user, backend io.ReadWriter } func (c switchProtocolCopier) copyFromBackend(errc chan<- error) { _, err := io.Copy(c.user, c.backend) errc <- err } func (c switchProtocolCopier) copyToBackend(errc chan<- error) { _, err := io.Copy(c.backend, c.user) errc <- err } The portion that caught my attention was the creation of the errc buffered channel. I thought (probably naively) that we would use an unbuffered channel and the later receive from errc would need to run twice, like this: <-errc <-errc As written, I understand that reading from the channel will ensure at least one of the copy methods has run. I also understand that the first send to the channel will not block, while the second will block only if the first one has not yet been received. What I don't understand, is why it is written like this. Is it to ensure that only one of the methods completes? If that is the case, couldn't they technically both run? Thanks!
The channel of size one helps realize a binary semaphore. Since at most one value is consumed from the channel (on line 549), changing the size of the channel to be greater than one will not affect the currently exhibited behavior, which is wait until at least one of the two go routines complete executing the Copy operation.
Golang reading from serial
I'm trying to read from a serial port (a GPS device on a Raspberry Pi). Following the instructions from http://www.modmypi.com/blog/raspberry-pi-gps-hat-and-python I can read from shell using stty -F /dev/ttyAMA0 raw 9600 cs8 clocal -cstopb cat /dev/ttyAMA0 I get well formatted output $GNGLL,5133.35213,N,00108.27278,W,160345.00,A,A*65 $GNRMC,160346.00,A,5153.35209,N,00108.27286,W,0.237,,290418,,,A*75 $GNVTG,,T,,M,0.237,N,0.439,K,A*35 $GNGGA,160346.00,5153.35209,N,00108.27286,W,1,12,0.67,81.5,M,46.9,M,,*6C $GNGSA,A,3,29,25,31,20,26,23,21,16,05,27,,,1.11,0.67,0.89*10 $GNGSA,A,3,68,73,83,74,84,75,85,67,,,,,1.11,0.67,0.89*1D $GPGSV,4,1,15,04,,,34,05,14,040,21,09,07,330,,16,45,298,34*40 $GPGSV,4,2,15,20,14,127,18,21,59,154,30,23,07,295,26,25,13,123,22*74 $GPGSV,4,3,15,26,76,281,40,27,15,255,20,29,40,068,19,31,34,199,33*7C $GPGSV,4,4,15,33,29,198,,36,23,141,,49,30,172,*4C $GLGSV,3,1,11,66,00,325,,67,13,011,20,68,09,062,16,73,12,156,21*60 $GLGSV,3,2,11,74,62,177,20,75,53,312,36,76,08,328,,83,17,046,25*69 $GLGSV,3,3,11,84,75,032,22,85,44,233,32,,,,35*62 $GNGLL,5153.35209,N,00108.27286,W,160346.00,A,A*6C $GNRMC,160347.00,A,5153.35205,N,00108.27292,W,0.216,,290418,,,A*7E $GNVTG,,T,,M,0.216,N,0.401,K,A*3D $GNGGA,160347.00,5153.35205,N,00108.27292,W,1,12,0.67,81.7,M,46.9,M,,*66 $GNGSA,A,3,29,25,31,20,26,23,21,16,05,27,,,1.11,0.67,0.89*10 $GNGSA,A,3,68,73,83,74,84,75,85,67,,,,,1.11,0.67,0.89*1D $GPGSV,4,1,15,04,,,34,05,14,040,21,09,07,330,,16,45,298,34*40 (I've put some random data in) I'm trying to read this in Go. Currently, I have package main import "fmt" import "log" import "github.com/tarm/serial" func main() { config := &serial.Config{ Name: "/dev/ttyAMA0", Baud: 9600, ReadTimeout: 1, Size: 8, } stream, err := serial.OpenPort(config) if err != nil { log.Fatal(err) } buf := make([]byte, 1024) for { n, err := stream.Read(buf) if err != nil { log.Fatal(err) } s := string(buf[:n]) fmt.Println(s) } } But this prints malformed data. I suspect that this is due to the buffer size or the value of Size in the config struct being wrong, but I'm not sure how to get those values from the stty settings. Looking back, I think the issue is that I'm getting a stream and I want to be able to iterate over lines of the stty, rather than chunks. This is how the stream is outputted: $GLGSV,3 ,1,09,69 ,10,017, ,70,43,0 69,,71,3 2,135,27 ,76,23,2 32,22*6F $GLGSV ,3,2,09, 77,35,30 0,21,78, 11,347,, 85,31,08 1,30,86, 72,355,3 6*6C $G LGSV,3,3 ,09,87,2 4,285,30 *59 $GN GLL,5153 .34919,N ,00108.2 7603,W,1 92901.00 ,A,A*6A
The struct you get back from serial.OpenPort() contains a pointer to an open os.File corresponding to the opened serial port connection. When you Read() from this, the library calls Read() on the underlying os.File. The documentation for this function call is: Read reads up to len(b) bytes from the File. It returns the number of bytes read and any error encountered. At end of file, Read returns 0, io.EOF. This means you have to keep track of how much data was read. You also have to keep track of whether there were newlines, if this is important to you. Unfortunately, the underlying *os.File is not exported, so you'll find it difficult to use tricks like bufio.ReadLine(). It may be worth modifying the library and sending a pull request. As Matthew Rankin noted in a comment, Port implements io.ReadWriter so you can simply use bufio to read by lines. stream, err := serial.OpenPort(config) if err != nil { log.Fatal(err) } scanner := bufio.NewScanner(stream) for scanner.Scan() { fmt.Println(scanner.Text()) // Println will add back the final '\n' } if err := scanner.Err(); err != nil { log.Fatal(err) }
Change fmt.Println(s) to fmt.Print(s) and you will probably get what you want. Or did I misunderstand the question?
Two additions to Michael Hamptom's answer which can be useful: line endings You might receive data that is not newline-separated text. bufio.Scanner uses ScanLines by default to split the received data into lines - but you can also write your own line splitter based on the default function's signature and set it for the scanner: scanner := bufio.NewScanner(stream) scanner.Split(ownLineSplitter) // set custom line splitter function reader shutdown You might not receive a constant stream but only some packets of bytes from time to time. If no bytes arrive at the port, the scanner will block and you can't just kill it. You'll have to close the stream to do so, effectively raising an error. To not block any outer loops and handle errors appropriately, you can wrap the scanner in a goroutine that takes a context. If the context was cancelled, ignore the error, otherwise forward the error. In principle, this can look like var errChan = make(chan error) var dataChan = make(chan []byte) ctx, cancelPortScanner := context.WithCancel(context.Background()) go func(ctx context.Context) { scanner := bufio.NewScanner(stream) for scanner.Scan() { // will terminate if connection is closed dataChan <- scanner.Bytes() } // if execution reaches this point, something went wrong or stream was closed select { case <-ctx.Done(): return // ctx was cancelled, just return without error default: errChan <- scanner.Err() // ctx wasn't cancelled, forward error } }(ctx) // handle data from dataChan, error from errChan To stop the scanner, you would cancel the context and close the connection: cancelPortScanner() stream.Close()
Concurrent writing to file from ajax request
I want to write requests to one file from some ajax script. The problem arises when there will be many of those in a second and writing to file will take more time than the break between requests, and when there will be two requests at the same time. How could I solve this? I've came up using mutex, like: var mu sync.Mutex func writeToFile() { mu.Lock() defer mu.Unlock() // write to file } But it makes the whole thing synchronous and I don't really know what happens when there are two requests at the same time. And it still does not lock the file itself. Uh, what's the proper way to do this?
You only need to make writing to the file "sequential", meaning don't allow 2 concurrent goroutines to write to the file. Yes, if you use locking in the writeToFile() function, serving your ajax requests may become (partially) sequential too. What I suggest is open the file once, when your application starts. And designate a single goroutine which will be responsible writing to the file, no other goroutines should do it. And use a buffered channel to send data that should be written to the file. This will make serving ajax requests non-blocking, and still the file will not be written concurrently / parallel. Note that this way ajax requests won't even have to wait while the data is actually written to the file (faster response time). This may or may not be a problem. For example if later writing fails, your ajax response might already be committed => no chance to signal failure to the client. Example how to do it: var ( f *os.File datach = make(chan []byte, 100) // Buffered channel ) func init() { // Open file for appending (create if doesn't exist) var err error f, err = os.OpenFile("data.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666) if err != nil { panic(err) } // Start goroutine which writes to the file go writeToFile() } func writeToFile() { // Loop through any data that needs to be written: for data := range datach { if _, err := f.Write(data); err != nil { // handle error! } } // We get here if datach is closed: shutdown f.Close() } func ajaxHandler(w http.ResponseWriter, r *http.Request) { // Assmeble data that needs to be written (appended) to the file data := []byte{1, 2, 3} // And send it: datach <- data } To gracefully exit from the app, you should close the datach channel: when it's closed, the loop in the writeToFile() will terminate, and the file will be closed (flushing any cached data, releasing OS resources). If you want to write text to the file, you may declare the data channel like this: var datach = make(chan string, 100) // Buffered channel And you may use File.WriteString() to write it to the file: if _, err := f.WriteString(data); err != nil { // handle error! }