func ExampleConnection_NotifyBlocked() { // Simply logs when the server throttles the TCP connection for publishers // Test this by tuning your server to have a low memory watermark: // rabbitmqctl set_vm_memory_high_watermark 0.00000001 conn, err := amqp.Dial("amqp://*****:*****@localhost:5672/") if err != nil { log.Fatalf("connection.open: %s", err) } defer conn.Close() blockings := conn.NotifyBlocked(make(chan amqp.Blocking)) go func() { for b := range blockings { if b.Active { log.Printf("TCP blocked: %q", b.Reason) } else { log.Printf("TCP unblocked") } } }() // Your application domain channel setup publishings publishAllTheThings(conn) }
// AmqpChannel is the same as amqpConnect in boulder, but with even // more aggressive error dropping func AmqpChannel(url string) (ch *amqp.Channel) { conn, err := amqp.Dial(url) FailOnError(err, "Unable to connect to AMQP server") ch, err = conn.Channel() FailOnError(err, "Unable to establish channel to AMQP server") return }
// A simplified way to get a channel for a given AMQP server func amqpConnect(url string) (ch *amqp.Channel, err error) { conn, err := amqp.Dial(url) if err != nil { return } ch, err = conn.Channel() return }
// Every connection should declare the topology they expect func setup(url, queue 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 } if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil { return nil, nil, err } return conn, ch, nil }
func ExampleChannel_Publish() { // Connects opens an AMQP connection from the credentials in the URL. conn, err := amqp.Dial("amqp://*****:*****@localhost:5672/") if err != nil { log.Fatalf("connection.open: %s", err) } // This waits for a server acknowledgment which means the sockets will have // flushed all outbound publishings prior to returning. It's important to // block on Close to not lose any publishings. defer conn.Close() c, err := conn.Channel() if err != nil { log.Fatalf("channel.open: %s", err) } // We declare our topology on both the publisher and consumer to ensure they // are the same. This is part of AMQP being a programmable messaging model. // // See the Channel.Consume example for the complimentary declare. err = c.ExchangeDeclare("logs", "topic", true, false, false, false, nil) if err != nil { log.Fatalf("exchange.declare: %v", err) } // Prepare this message to be persistent. Your publishing requirements may // be different. msg := amqp.Publishing{ DeliveryMode: amqp.Persistent, Timestamp: time.Now(), ContentType: "text/plain", Body: []byte("Go Go AMQP!"), } // This is not a mandatory delivery, so it will be dropped if there are no // queues bound to the logs exchange. err = c.Publish("logs", "info", false, false, msg) if err != nil { // Since publish is asynchronous this can happen if the network connection // is reset or if the server has run out of resources. log.Fatalf("basic.publish: %v", err) } }
func main() { server := *server conn, err := amqp.Dial(server) cmd.FailOnError(err, "Could not connect to AMQP") ch, err := conn.Channel() cmd.FailOnError(err, "Could not connect to AMQP") err = ch.ExchangeDeclare( amqpExchange, amqpExchangeType, amqpExchangeDurable, amqpDeleteUnused, amqpInternal, amqpNoWait, nil) cmd.FailOnError(err, "Declaring exchange") _, err = ch.QueueDeclare( monitorQueueName, amqpQueueDurable, amqpDeleteUnused, amqpExclusive, amqpNoWait, nil) if err != nil { cmd.FailOnError(err, "Could not declare queue") } routingKey := "#" //wildcard err = ch.QueueBind( monitorQueueName, routingKey, amqpExchange, false, nil) if err != nil { txt := fmt.Sprintf("Could not bind to queue [%s]. NOTE: You may need to delete %s to re-trigger the bind attempt after fixing permissions, or manually bind the queue to %s.", monitorQueueName, monitorQueueName, routingKey) cmd.FailOnError(err, txt) } }
// AmqpChannel sets a AMQP connection up using SSL if configuration is provided func AmqpChannel(conf cmd.Config) (*amqp.Channel, error) { var conn *amqp.Connection var err error log := blog.GetAuditLogger() if conf.AMQP.Insecure == true { // If the Insecure flag is true, then just go ahead and connect conn, err = amqp.Dial(conf.AMQP.Server) } else { // The insecure flag is false or not set, so we need to load up the options log.Info("AMQPS: Loading TLS Options.") if strings.HasPrefix(conf.AMQP.Server, "amqps") == false { err = fmt.Errorf("AMQPS: Not using an AMQPS URL. To use AMQP instead of AMQPS, set insecure=true.") return nil, err } if conf.AMQP.TLS == nil { err = fmt.Errorf("AMQPS: No TLS configuration provided. To use AMQP instead of AMQPS, set insecure=true.") return nil, err } cfg := new(tls.Config) // If the configuration specified a certificate (or key), load them if conf.AMQP.TLS.CertFile != nil || conf.AMQP.TLS.KeyFile != nil { // But they have to give both. if conf.AMQP.TLS.CertFile == nil || conf.AMQP.TLS.KeyFile == nil { err = fmt.Errorf("AMQPS: You must set both of the configuration values AMQP.TLS.KeyFile and AMQP.TLS.CertFile") return nil, err } cert, err := tls.LoadX509KeyPair(*conf.AMQP.TLS.CertFile, *conf.AMQP.TLS.KeyFile) if err != nil { err = fmt.Errorf("AMQPS: Could not load Client Certificate or Key: %s", err) return nil, err } log.Info("AMQPS: Configured client certificate for AMQPS.") cfg.Certificates = append(cfg.Certificates, cert) } // If the configuration specified a CA certificate, make it the only // available root. if conf.AMQP.TLS.CACertFile != nil { cfg.RootCAs = x509.NewCertPool() ca, err := ioutil.ReadFile(*conf.AMQP.TLS.CACertFile) if err != nil { err = fmt.Errorf("AMQPS: Could not load CA Certificate: %s", err) return nil, err } cfg.RootCAs.AppendCertsFromPEM(ca) log.Info("AMQPS: Configured CA certificate for AMQPS.") } conn, err = amqp.DialTLS(conf.AMQP.Server, cfg) } if err != nil { return nil, err } err = AMQPDeclareExchange(conn) if err != nil { return nil, err } return conn.Channel() }
// AmqpChannel sets a AMQP connection up using SSL if configuration is provided func AmqpChannel(conf cmd.Config) (*amqp.Channel, error) { var conn *amqp.Connection var err error log := blog.GetAuditLogger() if conf.AMQP.TLS == nil { // Configuration did not specify TLS options, but Dial will // use TLS anyway if the URL scheme is "amqps" conn, err = amqp.Dial(conf.AMQP.Server) } else { // They provided TLS options, so let's load them. log.Info("AMQPS: Loading TLS Options.") if strings.HasPrefix(conf.AMQP.Server, "amqps") == false { err = fmt.Errorf("AMQPS: TLS configuration provided, but not using an AMQPS URL") return nil, err } cfg := new(tls.Config) // If the configuration specified a certificate (or key), load them if conf.AMQP.TLS.CertFile != nil || conf.AMQP.TLS.KeyFile != nil { // But they have to give both. if conf.AMQP.TLS.CertFile == nil || conf.AMQP.TLS.KeyFile == nil { err = fmt.Errorf("AMQPS: You must set both of the configuration values AMQP.TLS.KeyFile and AMQP.TLS.CertFile") return nil, err } cert, err := tls.LoadX509KeyPair(*conf.AMQP.TLS.CertFile, *conf.AMQP.TLS.KeyFile) if err != nil { err = fmt.Errorf("AMQPS: Could not load Client Certificate or Key: %s", err) return nil, err } log.Info("AMQPS: Configured client certificate for AMQPS.") cfg.Certificates = append(cfg.Certificates, cert) } // If the configuration specified a CA certificate, make it the only // available root. if conf.AMQP.TLS.CACertFile != nil { cfg.RootCAs = x509.NewCertPool() ca, err := ioutil.ReadFile(*conf.AMQP.TLS.CACertFile) if err != nil { err = fmt.Errorf("AMQPS: Could not load CA Certificate: %s", err) return nil, err } cfg.RootCAs.AppendCertsFromPEM(ca) log.Info("AMQPS: Configured CA certificate for AMQPS.") } conn, err = amqp.DialTLS(conf.AMQP.Server, cfg) } if err != nil { return nil, err } err = AMQPDeclareExchange(conn) if err != nil { return nil, err } return conn.Channel() }
func ExampleChannel_Confirm_bridge() { // This example acts as a bridge, shoveling all messages sent from the source // exchange "log" to destination exchange "log". // Confirming publishes can help from overproduction and ensure every message // is delivered. // Setup the source of the store and forward source, err := amqp.Dial("amqp://source/") if err != nil { log.Fatalf("connection.open source: %s", err) } defer source.Close() chs, err := source.Channel() if err != nil { log.Fatalf("channel.open source: %s", err) } if err := chs.ExchangeDeclare("log", "topic", true, false, false, false, nil); err != nil { log.Fatalf("exchange.declare destination: %s", err) } if _, err := chs.QueueDeclare("remote-tee", true, true, false, false, nil); err != nil { log.Fatalf("queue.declare source: %s", err) } if err := chs.QueueBind("remote-tee", "#", "logs", false, nil); err != nil { log.Fatalf("queue.bind source: %s", err) } shovel, err := chs.Consume("remote-tee", "shovel", false, false, false, false, nil) if err != nil { log.Fatalf("basic.consume source: %s", err) } // Setup the destination of the store and forward destination, err := amqp.Dial("amqp://destination/") if err != nil { log.Fatalf("connection.open destination: %s", err) } defer destination.Close() chd, err := destination.Channel() if err != nil { log.Fatalf("channel.open destination: %s", err) } if err := chd.ExchangeDeclare("log", "topic", true, false, false, false, nil); err != nil { log.Fatalf("exchange.declare destination: %s", err) } // Buffer of 1 for our single outstanding publishing pubAcks, pubNacks := chd.NotifyConfirm(make(chan uint64, 1), make(chan uint64, 1)) if err := chd.Confirm(false); err != nil { log.Fatalf("confirm.select destination: %s", err) } // Now pump the messages, one by one, a smarter implementation // would batch the deliveries and use multiple ack/nacks for { msg, ok := <-shovel if !ok { log.Fatalf("source channel closed, see the reconnect example for handling this") } err = chd.Publish("logs", msg.RoutingKey, false, false, amqp.Publishing{ // Copy all the properties ContentType: msg.ContentType, ContentEncoding: msg.ContentEncoding, DeliveryMode: msg.DeliveryMode, Priority: msg.Priority, CorrelationId: msg.CorrelationId, ReplyTo: msg.ReplyTo, Expiration: msg.Expiration, MessageId: msg.MessageId, Timestamp: msg.Timestamp, Type: msg.Type, UserId: msg.UserId, AppId: msg.AppId, // Custom headers Headers: msg.Headers, // And the body Body: msg.Body, }) if err != nil { msg.Nack(false, false) log.Fatalf("basic.publish destination: %s", msg) } // only ack the source delivery when the destination acks the publishing // here you could check for delivery order by keeping a local state of // expected delivery tags select { case <-pubAcks: msg.Ack(false) case <-pubNacks: msg.Nack(false, false) } } }
func ExampleChannel_Consume() { // Connects opens an AMQP connection from the credentials in the URL. conn, err := amqp.Dial("amqp://*****:*****@localhost:5672/") if err != nil { log.Fatalf("connection.open: %s", err) } defer conn.Close() c, err := conn.Channel() if err != nil { log.Fatalf("channel.open: %s", err) } // We declare our topology on both the publisher and consumer to ensure they // are the same. This is part of AMQP being a programmable messaging model. // // See the Channel.Publish example for the complimentary declare. err = c.ExchangeDeclare("logs", "topic", true, false, false, false, nil) if err != nil { log.Fatalf("exchange.declare: %s", err) } // Establish our queue topologies that we are responsible for type bind struct { queue string key string } bindings := []bind{ bind{"page", "alert"}, bind{"email", "info"}, bind{"firehose", "#"}, } for _, b := range bindings { _, err = c.QueueDeclare(b.queue, true, false, false, false, nil) if err != nil { log.Fatalf("queue.declare: %v", err) } err = c.QueueBind(b.queue, b.key, "logs", false, nil) if err != nil { log.Fatalf("queue.bind: %v", err) } } // Set our quality of service. Since we're sharing 3 consumers on the same // channel, we want at least 3 messages in flight. err = c.Qos(3, 0, false) if err != nil { log.Fatalf("basic.qos: %v", err) } // Establish our consumers that have different responsibilities. Our first // two queues do not ack the messages on the server, so require to be acked // on the client. pages, err := c.Consume("page", "pager", false, false, false, false, nil) if err != nil { log.Fatalf("basic.consume: %v", err) } go func() { for log := range pages { // ... this consumer is responsible for sending pages per log log.Ack(false) } }() // Notice how the concern for which messages arrive here are in the AMQP // topology and not in the queue. We let the server pick a consumer tag this // time. emails, err := c.Consume("email", "", false, false, false, false, nil) if err != nil { log.Fatalf("basic.consume: %v", err) } go func() { for log := range emails { // ... this consumer is responsible for sending emails per log log.Ack(false) } }() // This consumer requests that every message is acknowledged as soon as it's // delivered. firehose, err := c.Consume("firehose", "", true, false, false, false, nil) if err != nil { log.Fatalf("basic.consume: %v", err) } // To show how to process the items in parallel, we'll use a work pool. for i := 0; i < runtime.NumCPU(); i++ { go func(work <-chan amqp.Delivery) { for _ = range work { // ... this consumer pulls from the firehose and doesn't need to acknowledge } }(firehose) } // Wait until you're ready to finish, could be a signal handler here. time.Sleep(10 * time.Second) // Cancelling a consumer by name will finish the range and gracefully end the // goroutine err = c.Cancel("pager", false) if err != nil { log.Fatalf("basic.cancel: %v", err) } // deferred closing the Connection will also finish the consumer's ranges of // their delivery chans. If you need every delivery to be processed, make // sure to wait for all consumers goroutines to finish before exiting your // process. }