我已经跟随go(go-lang)了一段时间,终于有一些时间来尝试它。 在本文中,我们将创建一个简单的HTTP服务器,该服务器使用mongoDB作为后端,并提供一个非常基本的REST API。
在本文的其余部分中,我假设您已经完成了go环境的设置和工作。 如果不是,请访问go-lang网站以获取说明( https://golang.org/doc/install )。
在开始之前,我们需要获取mongo驱动程序。 在控制台中,键入以下内容:
go get gopkg.in/mgo.v2
这将安装必要的库,以便我们可以从go代码访问mongoDB。
我们还需要一些数据进行试验。 我们将使用与上一篇文章( http://www.smartjava.org/content/building-rest-service-scala-akka-http-a… )中相同的设置。
将数据加载到MongoDB中
我们使用一些与股票相关的信息,您可以从此处下载( http://jsonstudio.com/wp-content/uploads/2014/02/stocks.zip )。 您可以通过执行以下步骤轻松地做到这一点:
首先获取数据:
wget http://jsonstudio.com/wp-content/uploads/2014/02/stocks.zip
在另一个终端中启动mongodb
mongod --dbpath ./data/
最后使用mongoimport导入数据
unzip -c stocks.zip | mongoimport --db akka --collection stocks --jsonArray
作为快速检查,运行查询以查看是否一切正常:
jos@Joss-MacBook-Pro.local:~$ mongo akka
MongoDB shell version: 2.4.8
connecting to: akka
> db.stocks.findOne({},{Company: 1, Country: 1, Ticker:1 } )
{
"_id" : ObjectId("52853800bb1177ca391c17ff"),
"Ticker" : "A",
"Country" : "USA",
"Company" : "Agilent Technologies Inc."
}
>
至此,我们有了测试数据,可以开始创建基于Go的HTTP服务器了。 您可以在下面的Gist中找到完整的代码: https : //gist.github.com/josdirksen/071f26a736eca26d7ea4
在以下部分中,我们将研究本要点的各个部分,以说明如何设置基于Go的HTTP服务器。
主要功能
当您运行go应用程序时,go将查找主要功能。 对于我们的服务器,此主要功能如下所示:
func main() {
server := http.Server{
Addr: ":8000",
Handler: NewHandler(),
}
// start listening
fmt.Println("Started server 2")
server.ListenAndServe()
}
这将配置服务器在端口8000上运行,传入的任何请求都将由我们在第64行中创建的NewHandler()实例处理。我们通过调用server.listenAndServe()函数来启动服务器。
现在,让我们看一下将响应请求的处理程序。
myHandler结构
首先让我们看一下该处理程序的外观:
// Constructor for the server handlers
func NewHandler() *myHandler {
h := new(myHandler)
h.defineMappings()
return h
}
// Definition of this struct
type myHandler struct {
// holds the mapping
mux map[string]func(http.ResponseWriter, *http.Request)
}
// functions defined on struct
func (my *myHandler) defineMappings() {
session, err := mgo.Dial("localhost")
if err != nil {
panic(err)
}
// make the mux
my.mux = make(map[string]func(http.ResponseWriter, *http.Request))
// matching of request path
my.mux["/hello"] = requestHandler1
my.mux["/get"] = my.wrap(requestHandler2, session)
}
// returns a function so that we can use the normal mux functionality and pass in a shared mongo session
func (my *myHandler) wrap(target func(http.ResponseWriter, *http.Request, *mgo.Session), mongoSession *mgo.Session) func(http.ResponseWriter, *http.Request) {
return func(resp http.ResponseWriter, req *http.Request) {
target(resp, req, mongoSession)
}
}
// implements serveHTTP so this struct can act as a http server
func (my *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if h, ok := my.mux[r.URL.String()]; ok {
// handle paths that are found
h(w, r)
return
} else {
// handle unhandled paths
io.WriteString(w, "My server: "+r.URL.String())
}
}
让我们分开看一下各个部分。 我们要做的第一件事是定义一个构造函数:
func NewHandler() *myHandler {
h := new(myHandler)
h.defineMappings()
return h
}
当我们调用此构造函数时,它将实例化myHandler类型并调用defineMappings()函数。 之后,它将返回我们创建的实例。
我们实例化类型的外观如何:
type myHandler struct {
// holds the mapping
mux map[string]func(http.ResponseWriter, *http.Request)
}
您可以定义一个带有mux变量的结构作为映射。 该映射将包含我们在请求路径和可以处理请求的函数之间的映射。
在构造函数中,我们还称为defineMappings函数。 在myHandler结构上定义的此功能如下所示:
func (my *myHandler) defineMappings() {
session, err := mgo.Dial("localhost")
if err != nil {
panic(err)
}
// make the mux
my.mux = make(map[string]func(http.ResponseWriter, *http.Request))
// matching of request path
my.mux["/hello"] = requestHandler1
my.mux["/get"] = my.wrap(requestHandler2, session)
}
在此函数(名称不正确)中,我们定义了传入请求与处理该请求的特定函数之间的映射。 在此函数中,我们还使用mgo.Dial函数创建到mongoDB的会话。 如您所见,我们以两种不同的方式定义requestHandlers。 “ / hello”的处理程序直接指向一个函数,“ / get”路径的处理程序指向一个包装函数,该函数也在myHandler结构上定义:
func (my *myHandler) wrap(target func(http.ResponseWriter, *http.Request, *mgo.Session), mongoSession *mgo.Session) func(http.ResponseWriter, *http.Request) {
return func(resp http.ResponseWriter, req *http.Request) {
target(resp, req, mongoSession)
}
}
这是一个函数,它返回一个函数。 我们这样做的原因是,我们还希望将mongo会话传递到请求处理程序中。 因此,我们创建了一个自定义包装函数,该函数具有正确的签名,并将每个调用传递给我们还提供mongo会话的函数。 (请注意,我们还可以更改我们在下面说明的ServeHTTP实现)
最后,我们在结构上定义ServeHTTP函数。 每当我们收到请求时,都会调用此函数:
func (my *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if h, ok := my.mux[r.URL.String()]; ok {
// handle paths that are found
h(w, r)
return
} else {
// handle unhandled paths
io.WriteString(w, "My server: "+r.URL.String())
}
}
在此函数中,我们检查mux变量中是否有匹配项。 如果这样做,我们将调用已配置的句柄函数。 如果没有,我们只用一个简单的String来响应。
请求句柄功能
让我们从处理“ / hello”路径的handle函数开始:
func requestHandler1(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world!")
}
再简单不过了。 我们只写一个特定的字符串作为HTTP响应。 “ / get”路径更有趣:
func requestHandler2(w http.ResponseWriter, r *http.Request, mongoSession *mgo.Session) {
c1 := make(chan string)
c2 := make(chan string)
c3 := make(chan string)
go query("AAPL", mongoSession, c1)
go query("GOOG", mongoSession, c2)
go query("MSFT", mongoSession, c3)
select {
case data := <-c1:
io.WriteString(w, data)
case data := <-c2:
io.WriteString(w, data)
case data := <-c3:
io.WriteString(w, data)
}
}
// runs a query against mongodb
func query(ticker string, mongoSession *mgo.Session, c chan string) {
sessionCopy := mongoSession.Copy()
defer sessionCopy.Close()
collection := sessionCopy.DB("akka").C("stocks")
var result bson.M
collection.Find(bson.M{"Ticker": ticker}).One(&result)
asString, _ := json.MarshalIndent(result, "", " ")
amt := time.Duration(rand.Intn(120))
time.Sleep(time.Millisecond * amt)
c <- string(asString)
}
我们在这里所做的是利用go的通道功能同时运行三个查询。 我们获取AAPL,GOOG和MSFT的代码信息,并将结果返回到特定通道。 当我们在其中一个频道上收到响应时,我们将返回该响应。 因此,每次我们调用此服务时,我们要么获得AAPL,GOOG或MSFT的结果。
至此,我们结束了进入go-lang的第一步:)
翻译自: https://www.javacodegeeks.com/2015/02/using-go-to-build-a-rest-service-on-top-of-mongodb.html