Set connection friendly name in Go amqp client - go

I am using the http://github.com/streadway/amqp package in my application in order to handle connections to a remote RabbitMQ server. Everything is ok and works fine, but I have a question.
The current name for a connection is "ip:port", so when there are multiple connections from the same IP+port, they become hardly distinguishable. It would be great if you can specify a name for each connection.
Is there any way to set a distinct friendly name for each connection?

RabbitMQ 3.6.5 added the facility for the connecting client to report a friendly name string value to identify a connection for management purposes. This is strictly an identifier and, as it is client-reported, it cannot be relied upon for anything other than weak identification of connections. The release notes state:
Clients now can provide a human-readable connection name that will be displayed in the management UI... In order to use this feature, set the connection_name key in client properties. Note that this name doesn’t have to be unique and cannot be used as a connection identifier, for example, in HTTP API requests.
Solution
Provided you are using a sufficiently new version of RabbitMQ, you can set this parameter when making connections using streadway/amqp by passing an instance of amqp.Config when making the initial connection. The Properties field allows custom properties of the connection to be specified.
The example program below opens a connection using the AMQP URL provided in the environment variable AMQP_URL, identified using the connection name passed as the first command line argument to the invocation.
package main
import (
"log"
"os"
"github.com/streadway/amqp"
)
func main() {
amqpUrl := os.Getenv("AMQP_URL")
cfg := amqp.Config{
Properties: amqp.Table{
"connection_name": os.Args[1],
},
}
conn, err := amqp.DialConfig(amqpUrl, cfg)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
<-(chan struct{})(nil)
}
Starting multiple instances to connect to a local RabbitMQ instance using the following command line:
AMQP_URL=amqp://admin:password#localhost:5672 go run ./main.go connX
where a numeral is substituted for X yields the following output in the "Connections" page of the RabbitMQ Management web UI:
and the individual connection detail pages shows the value under the "Client-provided name" detail value:

Related

Go: Send websocket requests to a proxy port

I have an Envoy proxy instance configured to proxy http and websocket requests. Note that this is not a CONNECT proxy. I want my websocket client to create a websocket request as if it was sending it to the original destination and then deliver the payload to the proxy's listener instead.
What's the recommended way to connect to the local proxy? I believe this is dependent on the specific Go websocket package being used. I can see packages that allow overriding the http.Client used, but the destination address is determined using the websocket URL specified. The only alternative I have is to send the request to ws://proxy_ip:proxy_port/path directly, and specify the destination using some custom HTTP header that the proxy is configured to use for routing. I am not a big fan of this approach.
I want to dial an address different from that in the request
Use Gorilla's Dialer.NetDialContext to dial an address different from the request:
d := websocket.Dialer{
NetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.DialContext(ctx, network, "proxy_ip:proxy_port")
},
}
c, r, err := d.Dial("ws://example.com/path", nil)
if err != nil {
// handle error
}

Check for server reachability in Golang conn.Write

I am working on an application that tries to send some data to a remote server. Once I get the hostname, I get a connection by resolving the hostname using net.Dialer.DialContext. Once, I resolve the hostname, I keep on using conn.Write method to write data to the connection.
conn, err := d.DialContext(ctx, string(transport), addr)
_, err := client.conn.Write([]byte(msg))
Error faced: I observed that due to some issues, I was not able to ping my server. Surprisingly, conn obtained from DialContext did not complain while doing conn.Write and it kept writing to the same connection.
Can someone help me in how to modify my writing methods in order to get an error in case the destination server is not reachable?
From this UDP connection example
the best a "connected" UDP socket can do to simulate a send failure is to save the ICMP response, and return it as an error on the next write.
So try and (for testing) make a second conn.Write, to confirm that you would indeed get an error this time.

socks5 proxy client with context support

Is it possible to use a context with the Dialer returned by the SOCKS5 function from net/proxy library?
If a SOCKS5 proxy Dialer blocks while establishing the connection, an HTTP client using the proxy Dialer could get stuck without a way to abort the connection.
Under the hood of golang.org/x/net/proxy.SOCKS5 it uses golang.org/x/net/internal/socks.Dialer, which has an exported method DialContext, and since Go 1.7, http.Transport supports a custom DialContext as a field. So you can cast the dialer to an interface with method DialContext and set it to a custom transport.
dc := dialer.(interface {
DialContext(ctx context.Context, network, addr string) (net.Conn, error)
})
httpTransport.DialContext = dc.DialContext
playground: https://play.golang.org/p/tSi9IZ-2Zqg
Note: while this is valid Go code, some would argue it relies on implementation details that the package does not want to export (hidden in internal). I think it is best to send an issue to Go team to ask for export it; if it is too much an issue for you, you can either copy the code and preserve it from changing, or use http.Client.Timeout or write start every http.Do concurrently and wraps a select over it for context like dealing with any blocking operation.

EOF when connecting to IBM MQ with AMQP 1.0 from golang

I've installed IBM MQ (8.0.0.4) on a local Windows VM and followed the instructions in the docs to enable AMQP (https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_8.0.0/com.ibm.mq.con.doc/tamqp_creating.htm), and also disabled authentication since this is just a local development environment. With that done, I am able to connect from my host OS using the node.js sample in IBM's mqlight package:
>npm install mqlight
...
>node node_modules/mqlight/samples/recv.js -s amqp://windows-10:5672
Connected to amqp://windows-10:5672 using client-id recv_126117c
Subscribed to pattern: public
However, when attempting to connect from a golang app using vcabbage/amqp (version 0.12.5), it returns an EOF error on the attempt to dial the host. Minimal example:
package main
import (
"fmt"
"pack.ag/amqp"
)
func main() {
_, err := amqp.Dial("amqp://windows-10:5672")
fmt.Println(err) // EOF
}
Nothing is appearing in the IBM MQ error logs.
Unfortunately Google does not turn up any relevant results for connecting to IBM MQ via AMQP 1.0 in golang, so I'm stuck. Does anyone have any ideas?
So the solution is apparently to use SASL Anonymous mode; this allows the client to connect.
package main
import (
"fmt"
"pack.ag/amqp"
)
func main() {
_, err := amqp.Dial("amqp://windows-10:5672", amqp.ConnSASLAnonymous())
fmt.Println(err) // nil
}
If anybody wants to try to make it work in "normal" mode, it appears that IBM MQ was closing the channel as soon as the initial header packet was sent. The EOF was bubbling up from the receiving goroutine in any case.

Network programming in Go

I'm studying Go for network programming. The problem is Go documentation is too simple. For example, I don't know when to use net.DialTCP, and when to use TCPListener object to AcceptTCP, what's the difference? How about client communicate with another client? Not client to server.
Connecting
In Go, you use the Dial function from net to connect to a remote machine.
net.Dial("tcp","google.com:80")
net.Dial("udp","tracker.thepiratebay.org:6969")
net.Dial("ip","kremvax.su")
net.Dial("unix","/dev/log")
This gives you an abstract Conn object that represents the connection you just established. Conn implements the ReadWriteCloser interface from io and a couple of other functions. You can use this object to send and receive data.
Listening
To listen, i.e. open a port, you use the Listen function from net. Calling Listen gives you a Listener object. Use Accept to accept incoming connections. Accept returns another Conn object that can be used as above.
ls, err := net.Listen("tcp",":1337")
if err != nil {
// port probably blocked, insert error handling here
}
conn, err := ls.Accept()
if err != nil {
// error handling
}
conn.Write("Hello, world!")
DialTCP and ListenTCP
These functions give you more control over TCP connections. I suggest you to only use them if they are definitly needed for your program as Dial and Listen are simpler, more generic and easily allow you to adapt your program to other types of network connections.
net.DialTCP is used on the client side to create a connection to remote server.
net.TCPListener.AcceptTCP is used on the server side to accept new connection (possibly initiated by net.DialTCP if client is written in Go). Note that listener may accept multiple connections, one by one, thus serving multiple clients at once (e.g. each in different goroutine).
Depending on whether you are writing client or server, you use net.DialTCP or net.TCPListener
Maybe you should learn about network programming in general first? Then these would make more sense I think.

Resources