Существует программный способ, для которого вам нужно сделать 2 вещи:
- Воспроизведение колоритного состояния
- и используйте опцию
-race
при запуске инструмента go
Лучше всего, если вы напишите для него модульный тест, поэтому этот тест также воспроизводим и запускается / проверяется автоматически при каждой сборке / развертывании.
Хорошо, как это воспроизвести?
Просто напишите тест, который запускает 2 процедуры, одну, которая вызывает обработчик index
, и другую, которая вызывает обработчик about
, намеренно без синхронизации, это то, что запускает детектор гонки.
Используйте пакет net/http/httptest
для простого тестирования обработчиков. httptest.NewServer()
вручает вам готовый сервер, «вооруженный» обработчиком, который вы передаете ему.
Вот простой тестовый пример, который вызовет состояние гонки. Поместите его в файл с именем main_test.go
рядом с файлом main.go
:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"sync"
"testing"
"github.com/gorilla/mux"
)
func TestRace(t *testing.T) {
h := New()
app := mux.NewRouter()
app.HandleFunc("/", h.index)
app.HandleFunc("/about", h.about)
app.Use(h.loggingMiddleware)
server := httptest.NewServer(app)
defer server.Close()
wg := &sync.WaitGroup{}
for _, path := range []string{"/", "/about"} {
path := path
wg.Add(1)
go func() {
defer wg.Done()
req, err := http.NewRequest(http.MethodGet, server.URL+path, nil)
fmt.Println(server.URL + path)
if err != nil {
panic(err)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
}()
}
wg.Wait()
}
Вы должны запустить его с
go test -race
И пример вывода будет:
http://127.0.0.1:33007/
http://127.0.0.1:33007/about
==================
WARNING: DATA RACE
Write at 0x00c000098030 by goroutine 17:
play.(*handler).loggingMiddleware.func1()
/home/icza/tmp/gows/src/play/main.go:16 +0x1ce
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:1964 +0x51
github.com/gorilla/mux.(*Router).ServeHTTP()
/home/icza/tmp/gows/src/github.com/gorilla/mux/mux.go:212 +0x12e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2741 +0xc4
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1847 +0x80a
Previous write at 0x00c000098030 by goroutine 16:
play.(*handler).loggingMiddleware.func1()
/home/icza/tmp/gows/src/play/main.go:16 +0x1ce
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:1964 +0x51
github.com/gorilla/mux.(*Router).ServeHTTP()
/home/icza/tmp/gows/src/github.com/gorilla/mux/mux.go:212 +0x12e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2741 +0xc4
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1847 +0x80a
Goroutine 17 (running) created at:
net/http.(*Server).Serve()
/usr/local/go/src/net/http/server.go:2851 +0x4c5
net/http/httptest.(*Server).goServe.func1()
/usr/local/go/src/net/http/httptest/server.go:280 +0xac
Goroutine 16 (running) created at:
net/http.(*Server).Serve()
/usr/local/go/src/net/http/server.go:2851 +0x4c5
net/http/httptest.(*Server).goServe.func1()
/usr/local/go/src/net/http/httptest/server.go:280 +0xac
==================
2019/01/06 14:58:50 info index method=GET requestURI=/
2019/01/06 14:58:50 info about method=GET requestURI=/about
--- FAIL: TestRace (0.00s)
testing.go:771: race detected during execution of test
FAIL
exit status 1
FAIL play 0.011s
Тест не пройден, показывая, что существуют гонки данных.
Примечания:
Синхронизация с sync.WaitGroup
заключается в ожидании двух запущенных подпрограмм, а не в синхронизации доступа к регистратору обработчика (который вызывает гонку данных). Это так, если вы исправите гонку данных, тест запустится и завершится должным образом (ожидая завершения двух запущенных тестовых программ).