func main() {
	// Parse flags and arguments, print usage message on error.
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `
Usage: %s url [url ...]
Receive messages from all the listed URLs concurrently and print them.
`, os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()
	urls := flag.Args() // Non-flag arguments are URLs to receive from
	if len(urls) == 0 {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "No URL provided")
		os.Exit(1)
	}
	duration := time.Duration(*timeout) * time.Second
	if duration == 0 {
		duration = time.Duration(math.MaxInt64) // Not forever, but 290 years is close enough.
	}
	if *count == 0 {
		*count = math.MaxInt64
	}

	// Create a goroutine for each URL that receives messages and sends them to
	// the messages channel. main() receives and prints them.

	messages := make(chan proton.Message) // Channel for messages from goroutines to main()
	stop := make(chan struct{})           // Closing this channel means the program is stopping.

	var wait sync.WaitGroup // Used by main() to wait for all goroutines to end.

	wait.Add(len(urls)) // Wait for one goroutine per URL.

	// Arrange to close all connections on exit
	connections := make([]*messaging.Connection, len(urls))
	defer func() {
		for _, c := range connections {
			if c != nil {
				c.Close()
			}
		}
	}()

	for i, urlStr := range urls {
		debug.Printf("Connecting to %s", urlStr)
		go func(urlStr string) {
			defer wait.Done()                   // Notify main() that this goroutine is done.
			url, err := proton.ParseURL(urlStr) // Like net/url.Parse() but with AMQP defaults.
			fatalIf(err)

			// Open a standard Go net.Conn for the AMQP connection
			conn, err := net.Dial("tcp", url.Host) // Note net.URL.Host is actually "host:port"
			fatalIf(err)

			pc, err := messaging.Connect(conn) // This is our AMQP connection.
			fatalIf(err)
			connections[i] = pc

			// For convenience a proton.Connection provides a DefaultSession()
			// pc.Receiver() is equivalent to pc.DefaultSession().Receiver()
			r, err := pc.Receiver(url.Path)
			fatalIf(err)

			for {
				var m proton.Message
				select { // Receive a message or stop.
				case m = <-r.Receive:
				case <-stop: // The program is stopping.
					return
				}
				select { // Send m to main() or stop
				case messages <- m: // Send m to main()
				case <-stop: // The program is stopping.
					return
				}
			}
		}(urlStr)
	}
	info.Printf("Listening")

	// time.After() returns a channel that will close when the timeout is up.
	timer := time.After(duration)

	// main() prints each message and checks for count or timeout being exceeded.
	for i := int64(0); i < *count; i++ {
		select {
		case m := <-messages:
			debug.Print(formatMessage{m})
		case <-timer: // Timeout has expired
			i = 0
		}
	}
	info.Printf("Received %d messages", *count)
	close(stop) // Signal all goroutines to stop.
	wait.Wait() // Wait for all goroutines to finish.
}
Beispiel #2
0
func main() {
	// Parse flags and arguments, print usage message on error.
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `
Usage: %s url [url ...]
Send messages to all the listed URLs concurrently.
To each URL, send the string "path-n" where n is the message number.
`, os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()
	urls := flag.Args() // Non-flag arguments are URLs to receive from
	if len(urls) == 0 {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "No URL provided\n")
		os.Exit(1)
	}
	if *count == 0 {
		*count = math.MaxInt64
	}

	// Create a channel to receive all the acknowledgements
	acks := make(chan Ack)

	// Create a goroutine for each URL that sends messages.
	var wait sync.WaitGroup // Used by main() to wait for all goroutines to end.
	wait.Add(len(urls))     // Wait for one goroutine per URL.

	// Arrange to close all connections on exit
	connections := make([]*messaging.Connection, len(urls))
	defer func() {
		for _, c := range connections {
			c.Close()
		}
	}()

	for i, urlStr := range urls {
		url, err := proton.ParseURL(urlStr) // Like net/url.Parse() but with AMQP defaults.
		fatalIf(err)
		debug.Printf("Connecting to %v", url)

		// Open a standard Go net.Conn for the AMQP connection
		conn, err := net.Dial("tcp", url.Host) // Note net.URL.Host is actually "host:port"
		fatalIf(err)

		pc, err := messaging.Connect(conn) // This is our AMQP connection using conn.
		fatalIf(err)
		connections[i] = pc

		// Start a goroutine to send to urlStr
		go func(urlStr string) {
			defer wait.Done() // Notify main() that this goroutine is done.

			// FIXME aconway 2015-04-29: sessions, default sessions, senders...
			// Create a sender using the path of the URL as the AMQP target address
			s, err := pc.Sender(url.Path)
			fatalIf(err)

			for i := int64(0); i < *count; i++ {
				m := proton.NewMessage()
				body := fmt.Sprintf("%v-%v", url.Path, i)
				m.SetBody(body)
				ack, err := s.Send(m)
				fatalIf(err)
				acks <- Ack{ack, body}
			}
		}(urlStr)
	}

	// Wait for all the acknowledgements
	expect := int(*count) * len(urls)
	debug.Printf("Started senders, expect %v acknowledgements", expect)
	for i := 0; i < expect; i++ {
		ack, ok := <-acks
		if !ok {
			info.Fatalf("acks channel closed after only %d acks\n", i)
		}
		d := <-ack.ack
		debug.Printf("acknowledgement[%v] %v", i, ack.info)
		if d != messaging.Accepted {
			info.Printf("Unexpected disposition %v", d)
		}
	}
	info.Printf("Received all %v acknowledgements", expect)
	wait.Wait() // Wait for all goroutines to finish.
}