func Get(urls []*url.URL, concurrencyLimit int) []Result {
	semaphoreChan := make(chan struct{}, concurrencyLimit)
	resultsChan := make(chan *Result)
	defer func() {
		close(semaphoreChan)
		close(resultsChan)
	}()
	for i, u := range urls {
		go func(i int, u *url.URL) {
			semaphoreChan <- struct{}{}
			res, err := http.Get(u.String())
			result := &Result{i, res, err}
			resultsChan <- result
			<-semaphoreChan
		}(i, u)
	}
	var results []Result
	for {
		result := <-resultsChan
		results = append(results, *result)
		if len(results) == len(urls) {
			break
		}
	}
	sort.Slice(results, func(i, j int) bool {
		return results[i].Index < results[j].Index
	})
	return results
}
Get sends requests in parallel but only up to a certain
limit, and furthermore it's only parallel up to the amount of CPUs but
is always concurrent up to the concurrency limit
package routing
import (
	"net/http"
	"net/url"
	"sort"
)
type Result struct {
	Index    int
	Response *http.Response
	Err      error
}