我被告知此代码在设计上存在竞争条件,尽管我尽了最大努力,但我无法证明它确实如此。
func (h *handler) loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.Log = log.WithFields(log.Fields{
"method": r.Method,
"requestURI": r.RequestURI,
})
next.ServeHTTP(w, r)
})
}
我试过 go build -race,然后运行二进制文件:PORT=3000 ./main 并加载像 hey -n 10000 -c 200 http 这样的创建者://localhost:3000.
此处的其余代码:https://raw.githubusercontent.com/kaihendry/context-youtube/master/5/main.go
或者
type handler struct{ Log *log.Entry }
func New() (h *handler) { return &handler{Log: log.WithFields(log.Fields{"test": "FAIL"})} }
func (h *handler) index(w http.ResponseWriter, r *http.Request) {
h.Log.Info("index")
fmt.Fprintf(w, "hello")
}
func (h *handler) about(w http.ResponseWriter, r *http.Request) {
h.Log.Info("about")
fmt.Fprintf(w, "about")
}
func main() {
h := New()
app := mux.NewRouter()
app.HandleFunc("/", h.index)
app.HandleFunc("/about", h.about)
app.Use(h.loggingMiddleware)
if err := http.ListenAndServe(":"+os.Getenv("PORT"), app); err != nil {
log.WithError(err).Fatal("error listening")
}
}
如果我不能证明它有竞争条件,我可以假设设置 h.Log 是安全的吗?
最佳答案
有一种编程方式,为此你必须做两件事:
go 工具时使用 -race 选项最好是为它编写单元测试,这样测试也是可重现的,并且在每次构建/部署时自动运行/检查。
好的,那么如何重现呢?
简单地编写一个启动 2 个 goroutines 的测试,一个调用 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 的同步是为了等待2个启动的goroutines,而不是为了同步访问处理程序的记录器(这会导致数据竞争)。如果您修复数据竞争,测试将正常运行并结束(等待 2 个启动的测试 goroutines 完成)。
关于go - 如何以编程方式证明此代码具有竞争条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54058007/
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的