Golang amqp reconnect - go

I want to test the restart connection to the rabbitmq server.
On wrote small script to test.
http://play.golang.org/p/l3ZWzG0Qqb
But it's not working.
In step 10, I close the channel and connection. And open them again. And re-create chan amqp.Confirmation ( :75) . And continue the cycle.
But after that, from the chan confirms nothing return.
UPD: code here.
package main
import (
"fmt"
"github.com/streadway/amqp"
"log"
"os"
"time"
)
const SERVER = "amqp://user:pass#localhost:5672/"
const EXCHANGE_NAME = "publisher.test.1"
const EXCHANGE_TYPE = "direct"
const ROUTING_KEY = "publisher.test"
var Connection *amqp.Connection
var Channel *amqp.Channel
func setup(url string) (*amqp.Connection, *amqp.Channel, error) {
conn, err := amqp.Dial(url)
if err != nil {
return nil, nil, err
}
ch, err := conn.Channel()
if err != nil {
return nil, nil, err
}
return conn, ch, nil
}
func main() {
url := SERVER
Connection, Channel, err := setup(url)
if err != nil {
fmt.Println("err publisher setup:", err)
return
}
confirms := Channel.NotifyPublish(make(chan amqp.Confirmation, 1))
if err := Channel.Confirm(false); err != nil {
log.Fatalf("confirm.select destination: %s", err)
}
for i := 1; i <= 3000000; i++ {
log.Println(i)
if err != nil {
fmt.Println("err consume:", err)
return
}
if err := Channel.Publish(EXCHANGE_NAME, ROUTING_KEY, false, false, amqp.Publishing{
Body: []byte(fmt.Sprintf("%d", i)),
}); err != nil {
fmt.Println("err publish:", err)
log.Printf("%+v", err)
os.Exit(1)
return
}
// only ack the source delivery when the destination acks the publishing
confirmed := <-confirms
if confirmed.Ack {
log.Printf("confirmed delivery with delivery tag: %d", confirmed.DeliveryTag)
} else {
log.Printf("failed delivery of delivery tag: %d", confirmed.DeliveryTag)
// TODO. Reconnect will be here
}
if i == 10 {
Channel.Close()
Connection.Close()
while := true
for while {
log.Println("while")
time.Sleep(time.Second * 1)
Connection, Channel, err = setup(url)
if err == nil {
while = false
confirms = Channel.NotifyPublish(make(chan amqp.Confirmation, 1))
log.Printf("%+v", confirms)
}
}
}
time.Sleep(time.Millisecond * 300)
}
os.Exit(1)
}

You should put channel in confirm mode. by calling the channel.Confirm() method.
After closing the connection and even after getting new channel on the same connection, you should call Confirm() method again, since the channel is different from the old channel, and the default for all new channel is not to send confirm.

Related

Go : Cancel Context inside a For loop

I am trying to create a UDP server in Golang to Listen at a port for eg. 1234. I have a client which sends the start/stop message to this server.
On receiving of message "start", the server will start sending random data to this client and on the stop, the server will stop sending to the client.
For this purpose, I am using context to create a goroutine to send the data and cancel it when it gets "stop".
The error I am getting is the program works fine for one client, but if I start the client again the data is not sent again.
Any help would be appreciated?
UDP server Code:
package main
import (
"context"
"fmt"
"math/rand"
"net"
"time"
)
func generateMessageToUDP(ctx context.Context, addr *net.UDPAddr) {
// stop writing to UDP
done := false
fmt.Println("Generating message to UDP client", addr)
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
fmt.Println("Error: ", err)
}
defer func(conn *net.UDPConn) {
err := conn.Close()
if err != nil {
fmt.Println("Error in closing the UDP Connection: ", err)
}
}(conn)
// write to address using UDP connection
go func() {
for i := 0; !done; i++ {
RandomInt := rand.Intn(100)
fmt.Println("Random Int: ", RandomInt)
_, err = conn.Write([]byte(fmt.Sprintf("%d", RandomInt)))
fmt.Println("Sent ", RandomInt, " to ", addr)
time.Sleep(time.Second * 1)
}
}()
<-ctx.Done()
fmt.Println("Stopping writing to UDP client", addr)
done = true
}
//var addr *net.UDPAddr
//var conn *net.UDPConn
func main() {
fmt.Println("Hi this is a UDP server")
udpServer, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 5010})
if err != nil {
fmt.Println("Error: ", err)
}
defer func(udpServer *net.UDPConn) {
err := udpServer.Close()
if err != nil {
fmt.Println("Error in closing the UDP Connection: ", err)
}
}(udpServer)
// create a buffer to read data into
buffer := make([]byte, 1024)
ctx, cancel := context.WithCancel(context.Background())
for {
// read the incoming connection into the buffer
n, addr, err := udpServer.ReadFromUDP(buffer)
fmt.Println("Recieved ", string(buffer[0:n]), " from ", addr)
if err != nil {
fmt.Println("Error: ", err)
}
fmt.Println("Received ", string(buffer[0:n]), " from ", addr)
if string(buffer[0:n]) == "stop" {
fmt.Println("Stopped listening")
cancel()
continue
} else if string(buffer[0:n]) == "start" {
// send a response back to the client
_, err = udpServer.WriteToUDP([]byte("Hi, I am a UDP server"), addr)
if err != nil {
fmt.Println("Error: ", err)
}
// start a routine to generate messages to the client
generateMessageToUDP(ctx, addr)
} else {
fmt.Println("Unknown command")
}
}
}
Client Code:
package main
import (
"fmt"
"net"
"time"
)
func main() {
fmt.Println("Hello, I am a client")
// Create a new client
localAddr, err := net.ResolveUDPAddr("udp", ":5011")
client3, err := net.DialUDP("udp", localAddr, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 5010})
if err != nil {
fmt.Println(err)
return
}
defer client3.Close()
_, err = client3.Write([]byte("start"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Message sent. Sleeping for 5 seconds")
time.Sleep(time.Second * 5)
fmt.Println("Sending stop message")
_, err = client3.Write([]byte("stop"))
if err != nil {
fmt.Println(err)
}
}
You must take care to what you are doing.
avoid data races (done variable is read/write by two different routines without synchronization mechanism) https://go.dev/doc/articles/race_detector
dont make a new dialer everytime the program start sending messages to a new client. This will open a new local address and use it to send it to the client. The client will receive messages from another address, which it should normally ignore, because it did not initiated any exchange with that remote.
dont mixup client lifetime span with the program context lifetime span. In the code provided a client sending a stop message will trigger the cancel function of the whole program, it will stop all clients. Make a new context for each client, derived from the program context, cancel the related client context upon receiving a stop message.
UDP conns are shared by all clients, they must not be stopped from listening incoming packets because the program is serving a client. IE the call to generateMessageToUDP should be executed into another routine.
Following is a revised version accounting for those comments.
A var peers map[string]peer is added to match a remote address with a context. The type peer is defined as struct {stop func();since time.Time}. Upon receiving a start message, the peer is added to the map with a derived context, pctx, pcancel := context.WithCancel(ctx). The new client is then served in a different routine, go generateMessageToUDP(pctx, udpServer, addr), which is bond to the newly created context and the server socket. Upon receiving a stop message, the program performs a lookup peer, ok := peers[addr.String()], it then cancels the associated peer context peer.stop(); delete(peers, addr.String()) and forgets the peer.
package main
import (
"context"
"fmt"
"math/rand"
"net"
"time"
)
func generateMessageToUDP(ctx context.Context, conn *net.UDPConn, addr *net.UDPAddr) {
fmt.Println("Generating message to UDP client", addr)
go func() {
for i := 0; ; i++ {
RandomInt := rand.Intn(100)
d := []byte(fmt.Sprintf("%d", RandomInt))
conn.WriteTo(d, addr)
time.Sleep(time.Second * 1)
}
}()
<-ctx.Done()
fmt.Println("Stopping writing to UDP client", addr)
}
//var addr *net.UDPAddr
//var conn *net.UDPConn
func main() {
fmt.Println("Hi this is a UDP server")
udpServer, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 5010})
if err != nil {
fmt.Println("Error: ", err)
}
defer func(udpServer *net.UDPConn) {
err := udpServer.Close()
if err != nil {
fmt.Println("Error in closing the UDP Connection: ", err)
}
}(udpServer)
// create a buffer to read data into
type peer struct {
stop func()
since time.Time
}
peers := map[string]peer{}
buffer := make([]byte, 1024)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for {
// read the incoming connection into the buffer
n, addr, err := udpServer.ReadFromUDP(buffer)
if err != nil {
fmt.Println("Error: ", err)
}
fmt.Println("Received ", string(buffer[0:n]), " from ", addr)
if string(buffer[0:n]) == "stop" {
fmt.Println("Stopped listening")
peer, ok := peers[addr.String()]
if !ok {
continue
}
peer.stop()
delete(peers, addr.String())
continue
} else if string(buffer[0:n]) == "start" {
peer, ok := peers[addr.String()]
if ok {
continue
}
pctx, pcancel := context.WithCancel(ctx)
peer.stop = pcancel
peer.since = time.Now()
peers[addr.String()] = peer
// send a response back to the client
_, err = udpServer.WriteToUDP([]byte("Hi, I am a UDP server"), addr)
if err != nil {
fmt.Println("Error: ", err)
}
// start a routine to generate messages to the client
go generateMessageToUDP(pctx, udpServer, addr)
} else if string(buffer[0:n]) == "ping" {
peer, ok := peers[addr.String()]
if !ok {
continue
}
peer.since = time.Now()
peers[addr.String()] = peer
} else {
fmt.Println("Unknown command")
}
for addr, p := range peers {
if time.Since(p.since) > time.Minute {
fmt.Println("Peer timedout")
p.stop()
delete(peers, addr)
}
}
}
}
-- go.mod --
module play.ground
-- client.go --
package main
import (
"fmt"
"log"
"net"
"time"
)
func main() {
fmt.Println("Hello, I am a client")
// Create a new client
localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:5011")
client3, err := net.DialUDP("udp", localAddr, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 5010})
if err != nil {
fmt.Println(err)
return
}
defer client3.Close()
var n int
n, err = client3.Write([]byte("start"))
if err != nil {
fmt.Println(err)
return
}
log.Println(n)
now := time.Now()
b := make([]byte, 2048)
for time.Since(now) < time.Second*10 {
n, addr, err := client3.ReadFrom(b)
fmt.Println(n, addr, err)
if err != nil {
fmt.Println(err)
continue
}
if addr.String() == "127.0.0.1:5010" {
m := b[:n]
fmt.Println("message:", string(m))
}
}
fmt.Println("Sending stop message")
_, err = client3.Write([]byte("stop"))
if err != nil {
fmt.Println(err)
}
}
In
go func() {
for i := 0; ; i++ {
RandomInt := rand.Intn(100)
d := []byte(fmt.Sprintf("%d", RandomInt))
conn.WriteTo(d, addr)
time.Sleep(time.Second * 1)
}
}()
I left as an exercise to the reader the writing of the missing select on the context channel to figure out if the routine should exit.
Okay, I did a simple hack on the server and added a label Start before creating a context and when I cancel the context, I addded goto label. This means when the task get cancelled it will again create the context and start doings its job

Golang Redis websocket handler

I need help with Golang websocket. I'm using Fiber with websocket and redis.
Here is the code:
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/go-redis/redis/v8"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
"log"
"test4/controllers"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
var ctx = context.Background()
var redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
func TestSocket() fiber.Handler {
socket := websocket.New(func(c *websocket.Conn) {
go deliverMessages(c)
var (
msg []byte
err error
)
for {
if _, msg, err = c.ReadMessage(); err != nil {
log.Println("read:", err)
break
}
if err := redisClient.Publish(ctx, "chat", msg).Err(); err != nil {
log.Println("publish:", err)
break
}
}
})
return socket
}
func deliverMessages(c *websocket.Conn) {
subscriber := redisClient.Subscribe(ctx, "chat")
user := User{}
for {
msg, err := subscriber.ReceiveMessage(ctx)
if err != nil {
log.Println("subscriber:", err)
panic(err)
}
if err := json.Unmarshal([]byte(msg.Payload), &user); err != nil {
log.Println("Unmarshal:", err)
panic(err)
}
text := []byte(fmt.Sprintf("{\"name\":\"%s\", \"email\":\"%s\"}", user.Name, user.Email))
if err = c.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println("write:", err)
break
}
}
}
func main() {
app := fiber.New(fiber.Config{
Prefork: true,
CaseSensitive: true,
StrictRouting: true,
DisableStartupMessage: true,
ServerHeader: "Test v3",
})
app.Get("/", controllers.Home)
app.Get("/ws", TestSocket())
log.Fatal(app.Listen("0.0.0.0:3000"))
}
How to produce the error:
Install Redis and run go run main.go
Now open http://127.0.0.1:3000/ in two tabs
click open on both tabs, and then you will see OPEN on right side of browser
click send on both tabs and you will get SEND and RESPONSE
Now close one tab and on go program terminal you will see error (see attached screenshot)
Now publish data to chat channel on redis-cli
Here is the error I am getting:
I think this is nil pointer websocket.Conn issue.
When close websocket connection, goroutine's c *websocket.Conn is loose data.
Pointer point nil.
solution
use channel, use local redisClient var
func TestSocket() fiber.Handler {
socket := websocket.New(func(c *websocket.Conn) {
var redisClient = redis.NewClient(&redis.Options{ // <-- use local redisClient var
Addr: "localhost:6379",
})
go deliverMessages(c)
var (
msg []byte
err error
)
defer func() {
redisClient.Close() // <-- then close, when websocket connection close
quitSubscribeGoRutine <- true // <-- change true, when websocket connection close
}()
for {
if _, msg, err = c.ReadMessage(); err != nil {
log.Println("read:", err)
...
func deliverMessages(c *websocket.Conn) {
subscriber := redisClient.Subscribe(ctx, "chat")
user := User{}
for {
select {
case <-quitSubscribeGoRutine: // <-- exit goroutine, when channel is true
return
default:
msg, err := subscriber.ReceiveMessage(ctx) // <-- exit goroutine, when redisClient close
if err != nil {
log.Println("subscriber:", err)
break // <-- use break instead of panic
}
if err := json.Unmarshal([]byte(msg.Payload), &user); err != nil {
log.Println("Unmarshal:", err)
panic(err)
}
text := []byte(fmt.Sprintf("{\"name\":\"%s\", \"email\":\"%s\"}", user.Name, user.Email))
if err = c.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println("write:", err)
break
}
}
}
}

Golang: Recursive function for reconnecting a TCP client... Bad idea?

I have this working TCP client code. When it fails to Write or Read on the TCP connection, it creates a new connection with the recursive function tcpReconnect().
Is this safe or will it fill up the RAM? It is possible that it may be trying to reconnect over several days (weekend or holidays). This is code is part of a Driver that monitors the state of an industrial Machine.
Maybe there is a better solution for this problem. I was not able to find one.
PS: I don't like polling
package main
import (
"fmt"
"net"
"time"
)
var pollTime = 1000 //ms
var host = "127.0.0.1"
var port = "11000"
func main() {
finished := make(chan bool)
go Driver()
<-finished
}
func tcpReconnect() net.Conn {
newConn, err := net.Dial("tcp", host+":"+port)
if err != nil {
fmt.Println("Failed to reconnect:", err.Error())
time.Sleep(time.Millisecond * time.Duration(2000))
newConn = tcpReconnect()
}
return newConn
}
func Driver() {
var conn net.Conn
conn, err := net.Dial("tcp", host+":"+port)
if err != nil {
fmt.Println("Failed to initialize Connection, trying to reconnect:", err.Error())
conn = tcpReconnect()
}
for {
_, err = conn.Write([]byte("11|5546|STATUS" + "\r\n"))
if err != nil {
println("Write to server failed:", err.Error())
println("Trying reset the connection...")
conn = tcpReconnect()
}
var replyBuffer = make([]byte, 256)
_, err = conn.Read(replyBuffer)
if err != nil {
println("Read from server failed:", err.Error())
println("Trying reset the connection...")
conn = tcpReConnect()
}
var reply string
for i, val := range replyBuffer {
if val == 13 { //13 is CR and marks the end of the message
reply = string(replyBuffer[:i])
break
}
}
fmt.Printf("reply from server=%s\n", reply)
time.Sleep(time.Millisecond * time.Duration(pollTime))
}
}
This is what I came up with. Credits go to #tkausl and #ThunderCat
func Driver() {
for {
conn, err := net.Dial("tcp", host+":"+port)
if err != nil {
fmt.Println("Failed to connect:", err.Error())
fmt.Println("Trying reset the connection...")
time.Sleep(time.Millisecond * time.Duration(2000))
} else {
for {
_, err = conn.Write([]byte("11|5546|STATUS" + "\r\n"))
if err != nil {
fmt.Println("Write to server failed:", err.Error())
fmt.Println("Trying reset the connection...")
break
}
var replyBuffer = make([]byte, 256)
_, err = conn.Read(replyBuffer)
if err != nil {
fmt.Println("Read from server failed:", err.Error())
fmt.Println("Trying reset the connection...")
break
}
var reply string
for i, val := range replyBuffer {
if val == 13 { //13 is CR and marks the end of the message
reply = string(replyBuffer[:i])
break
}
}
fmt.Printf("reply from server=_%s_\n", reply)
time.Sleep(time.Millisecond * time.Duration(pollTime))
}
}
}
}

TCP connection returns 'broken pipe' error when used multiple times

This question relates to go and its net package.
I wrote a simple tcp server handles some RPC. the client is using a chan net.Conn to manage all tcp connection on the client side. Server is running with a tcp listener.
here's the code:
client:
package server
import (
"errors"
"log"
"net"
)
var tcpPool chan net.Conn
func NewClient(connections int, address string) {
tcpPool = make(chan net.Conn, connections)
for i := 0; i < connections; i++ {
conn, err := net.Dial("tcp4", address)
if err != nil {
log.Panic(err)
}
tcpPool <- conn
}
}
func SendMessage(msg []byte) ([]byte, error) {
conn := getConn()
log.Println("check conn: ", conn)
log.Println("msg: ", msg)
defer releaseConn(conn)
// send message
n, err := conn.Write(msg)
if err != nil {
log.Panic(err)
} else if n < len(msg) {
log.Panic(errors.New("Message did not send in full"))
}
// receiving a message
inBytes := make([]byte, 0)
for {
// bufsize 1024, read bufsize bytes each time
b := make([]byte, bufSize)
res, err := conn.Read(b)
log.Println("server sends >>>>>>>>>>>>: ", res)
if err != nil {
b[0] = ReError
break
}
inBytes = append(inBytes, b[:res]...)
// message finished.
if res < bufSize {
break
}
}
// check replied message
if len(inBytes) == 0 {
return []byte{}, errors.New("empty buffer error")
}
log.Println("SendMessage gets: ", inBytes)
return inBytes, nil
}
func releaseConn(conn net.Conn) error {
log.Println("return conn to pool")
select {
case tcpPool <- conn:
return nil
}
}
func getConn() (conn net.Conn) {
log.Println("Take one from pool")
select {
case conn := <-tcpPool:
return conn
}
}
server
func StartTCPServer(network, addr string) error {
listener, err := net.Listen(network, addr)
if err != nil {
return errors.Wrapf(err, "Unable to listen on address %s\n", addr)
}
log.Println("Listen on", listener.Addr().String())
defer listener.Close()
for {
log.Println("Accept a connection request.")
conn, err := listener.Accept()
if err != nil {
log.Println("Failed accepting a connection request:", err)
continue
}
log.Println("Handle incoming messages.")
go onConn(conn)
}
}
//onConn recieves a tcp connection and waiting for incoming messages
func onConn(conn net.Conn) {
inBytes := make([]byte, 0)
defer func() {
if e := recover(); e != nil {
//later log
if err, ok := e.(error); ok {
println("recover", err.Error())
}
}
conn.Close()
}()
// load msg
for {
buf := make([]byte, bufSize)
res, err := conn.Read(buf)
log.Println("server reading: ", res)
inBytes = append(inBytes, buf[:res]...)
if err != nil || res < bufSize {
break
}
}
var req RPCRequest
err := json.Unmarshal(inBytes, &req)
if err != nil {
log.Panic(err)
}
log.Println("rpc request: ", req)
var query UserRequest
err = json.Unmarshal(req.Query, &query)
if err != nil {
log.Panic(err)
}
log.Println("rpc request query: ", query)
// call method to process request
// good now we can proceed to function call
// some actual function calls gets a output
// outBytes, err := json.Marshal(out)
conn.Write(outBytes)
}
I think this is very standard. but for some reason, I can only send message on the client side one, and then the follow 2nd and 3rd start to show some irregularity.
1st ---> success, gets response
2nd ---> client can send but nothing gets back, logs on server side shows no in coming message
3rd ---> if I send from client side one more time, it shows broken pipe error..
There are some bad handling way.
First, the flag to insure the msg from server finished is depending on io.EOF,not length
// message finished.
if res < 512 {
break
}
instead of this, reader returns an io.EOF is the only symbol that shows message finished.
Second, chan type has its property to block and not need to use select.by the way, you really need to start a goroutine to release. The same requirement for getConn
func releaseConn(conn net.Conn) {
go func(){
tcpPool <- conn
}()
}
func getConn() net.Conn {
con := <-tcpPool
return con
}
Third, listener should not be close, code below is bad
defer listener.Close()
The most important reason is
on the client side,
res, err := conn.Read(b) this receive the reply from the server.
when nothing reply ,it block rather than io.EOF, nor some error else.
It means ,you cann't box a lasting communicating part into a function send().
You can do a single thing to use sendmsg() to send, but never use sendmsg() to handle the reply.
you can handle reply like this
var receive chan string
func init() {
receive = make(chan string, 10)
}
func ReceiveMessage(con net.Conn) {
// receiving a message
inBytes := make([]byte, 0, 1000)
var b = make([]byte, 512)
for {
// bufsize 1024, read bufsize bytes each time
res, err := con.Read(b)
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err.Error())
break
}
inBytes = append(inBytes, b[:res]...)
msg := string(inBytes)
fmt.Println("receive msg from server:" + msg)
receive <- msg
}
}
I found several problem in your code, but I can't tell which one leads your failure.
This is my code according to what you write and did some fixing.
client.go:
package main
import (
"fmt"
"io"
"log"
"net"
)
var tcpPool chan net.Conn
var receive chan string
func init() {
receive = make(chan string, 10)
}
func NewClient(connections int, address string) {
tcpPool = make(chan net.Conn, connections)
for i := 0; i < connections; i++ {
conn, err := net.Dial("tcp", address)
if err != nil {
log.Panic(err)
}
tcpPool <- conn
}
}
func SendMessage(con net.Conn, msg []byte) error {
// send message
_, err := con.Write(msg)
if err != nil {
log.Panic(err)
}
return nil
}
func ReceiveMessage(con net.Conn) {
// receiving a message
inBytes := make([]byte, 0, 1000)
var b = make([]byte, 512)
for {
// bufsize 1024, read bufsize bytes each time
res, err := con.Read(b)
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err.Error())
break
}
inBytes = append(inBytes, b[:res]...)
msg := string(inBytes)
fmt.Println("receive msg from server:" + msg)
receive <- msg
}
}
func getConn() net.Conn {
con := <-tcpPool
return con
}
func main() {
NewClient(20, "localhost:8101")
con := <-tcpPool
e := SendMessage(con, []byte("hello, i am client"))
if e != nil {
fmt.Println(e.Error())
return
}
go ReceiveMessage(con)
var msg string
for {
select {
case msg = <-receive:
fmt.Println(msg)
}
}
}
server.go
package main
import (
"fmt"
"io"
"net"
)
func StartTCPServer(network, addr string) error {
listener, err := net.Listen(network, addr)
if err != nil {
return err
}
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err.Error())
continue
}
onConn(conn)
}
}
//onConn recieves a tcp connection and waiting for incoming messages
func onConn(conn net.Conn) {
inBytes := make([]byte, 0)
// load msg
for {
buf := make([]byte, 512)
res, err := conn.Read(buf)
if err != nil {
if err == io.EOF {
return
}
fmt.Println(err.Error())
return
}
inBytes = append(inBytes, buf[:res]...)
fmt.Println("receive from client:" + string(inBytes))
conn.Write([]byte("hello"))
}
}
func main() {
if e := StartTCPServer("tcp", ":8101"); e != nil {
fmt.Println(e.Error())
return
}
}
this works and no error.
By the way, I can't see where either on the client side or the server side you do con.Close(). It's nessasary to close it.This means a connection once got from the pool, you don't put it back. When you think a connection is over, then close it and build a new connection to fill the pool rather than put it back,beause it's a fatal operation to put a closed con back to the pool.

Could one connection support multiple channels in go api for rabbitmq?

package main
import (
"fmt"
"github.com/streadway/amqp"
"time"
)
// Every connection should declare the topology they expect
func setup(url, queue string) (*amqp.Connection, *amqp.Channel, error) {
//setup connection
conn, err := amqp.Dial(url)
if err != nil {
return nil, nil, err
}
//build channel in the connection
ch, err := conn.Channel()
if err != nil {
return nil, nil, err
}
//queue declare
if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
return nil, nil, err
}
return conn, ch, nil
}
func main() {
//amqp url
url := "amqp://guest:guest#127.0.0.1:5672";
for i := 1; i <= 2; i++ {
fmt.Println("connect ", i)
//two goroutine
go func() {
//queue name
queue := fmt.Sprintf("example.reconnect.%d", i)
//setup channel in the tcp connection
_, pub, err := setup(url, queue)
if err != nil {
fmt.Println("err publisher setup:", err)
return
}
// Purge the queue from the publisher side to establish initial state
if _, err := pub.QueuePurge(queue, false); err != nil {
fmt.Println("err purge:", err)
return
}
//publish msg
if err := pub.Publish("", queue, false, false, amqp.Publishing{
Body: []byte(fmt.Sprintf("%d", i)),
}); err != nil {
fmt.Println("err publish:", err)
return
}
//keep running
for{
time.Sleep(time.Second * 20)
}
}()
}
//keep running
for {
time.Sleep(time.Second * 20)
}
}
I thought there is only one connection between the program and mq-server,
but there are two connection,one connection can only support one channel,why?
can't the two goroutine share the same tcp connection?
Socket descriptor can share in all threads of a process in the theory.
Why the two goroutine don't share one socket but have their own channel?
The model by hand:
The real model in rabbitmq:
Looking at the source for the library it appears as though you can call conn.Channel() as many times as you like and it creates a new stream of communication over the same connection.
Ok, I tried it, here's a working example... One goroutine, one connection, two channels
I setup the receiver, then send a message, then read from the receiver channel
if you wanted multiple queue's bound in one goroutine, you would call rec.Consume twice and then select across the queues.
package main
import (
"fmt"
"github.com/streadway/amqp"
"os"
)
func main() {
conn, err := amqp.Dial("amqp://localhost")
e(err)
defer conn.Close()
fmt.Println("Connected")
rec, err := conn.Channel()
e(err)
fmt.Println("Setup receiver")
rq, err := rec.QueueDeclare("go-test", false, false, false, false, nil)
e(err)
msgs, err := rec.Consume(rq.Name, "", true, false, false, false, nil)
e(err)
fmt.Println("Setup sender")
send, err := conn.Channel()
e(err)
sq, err := send.QueueDeclare("go-test", false, false, false, false, nil)
e(err)
fmt.Println("Send message")
err = send.Publish("", sq.Name, false, false, amqp.Publishing{
ContentType: "text/plain",
Body: []byte("This is a test"),
})
e(err)
msg := <-msgs
fmt.Println("Received from:", rq, "msg:", string(msg.Body))
}
func e(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Output on my box:
$ go run rmq.go
Connected
Setup receiver
Setup sender
Send message
Received from: {go-test 0 0} msg: This is a test

Resources