Как выйти из main после завершения всех рутин?
https://golang.org/pkg/sync/#example_WaitGroup 
Хотя с каналами и быстрее, но я предпочитаю контроль через wg. 
upd. 
Контролируйте количество рутин. Раздробите приложение на маленькие функции. 
в главном сразу запускается 10 рутин на выполнение запросов, 10 рутин на анализ ответа от сторонего сервиса, 10 рутин ещё чего-либо. 
Рутины для выполнения запросов принимают, например, из единого канала урл, на который надо сделать запрос. Отправляете урл в канал, свободная рутина хватает и выполняет запрос, получет ответ, передает в другой канал, из которого уже хватает свободная рутина для анализа ответа. Ну и т.д.
Вот топорный пример, как делаю яpackage main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	urls := []string{"http://google.ru", "http://vk.com", "http://ya.ru"}
	chanUrls := make(chan string, 100)
	chanRes := make(chan http.Response, 100)
	chanPrint := make(chan string, 100)
	for _, url := range urls {
		chanUrls <- url
	}
	close(chanUrls)
	go httpGet(chanUrls, chanRes)
	go analys(chanRes, chanPrint)
	printRes(chanPrint)
}

func httpGet(chanUrls <-chan string, chanRes chan<- http.Response) {
	defer close(chanRes)
	for url := range chanUrls {
		res, err := http.DefaultClient.Get(url)
		if err == nil {
			chanRes <- *res
		}
	}
}

func analys(chanRes <-chan http.Response, chanPrint chan<- string) {
	defer close(chanPrint)
	for res := range chanRes {
		body, err := ioutil.ReadAll(res.Body)
		if err == nil {
			chanPrint <- string(body)
		}
	}
}

func printRes(chanPrint <-chan string) {
	for pr := range chanPrint {
		fmt.Println(pr)
	}
}
Канал то нужно или закрыть или записать что-нибудь в него.