使用Go在mongoDB之上构建REST服务

我已经跟随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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值