
今天是5.1劳动节,首先祝大家 5.1 劳动节快乐,疫情多少对大家又会有所影响,影响您的工作,影响您的出行,影响您的新年的计划。可能因为疫情大家也养成一些好的习惯,勤洗手将强锻炼身体提供抵抗力。

今天带来的是微服务,微服务是一种概念,也是因今天互联网需求所带来解决当今互联网的问题解决方案,已经不算什么新事物,在一些大公司已经开始实现了微服务架构,其实早在提出这个概念前,阿里就已经有了微服务,我们都是有了实现,然后找一个时髦概念对这些技术加以总结和描述。
今天选择 go 语言来实现微服务,分享内容会从很基础 Http 服务开始,一步一步实现微服务,分享也是参考老外教程并搜集一些相关资料。
- 简单服务器搭建
- 实现 Restful 服务
- Gorilla 重构 Restful 服务
- go 语言自带 rpc 包
- 介绍 gRPC
- docker 部署
简单服务器搭建
使用 go 语言内置 http 包来开发客户端和服务端,go 语言 http 包设计是比较优雅的包。从 http 包可以看出go 语言设计人员深厚功底。写一个简单 web 服务仅用 go 提供 http 包就住够了。这个和其他支持服务器开发语言例如 java 有有所不同。有关 http 协议
package main
import "net/http"
func main() {
//创建 web 服务
http.ListenAndServe(":4600", nil)
}
在 go 语言创建一个服务是比较轻松的一件事,通过上面简简单单的几行代码我们就实现了一个服务。运行 go run main.go
我们就启动了服务,可以使用 curl 命令进行验证。
ListenAndServe
接收两个参数第一参数是服务器地址,第二参数是接口类型,这里暂时传 nil
curl -v localhost:4600
使用 curl 的 verbose 模式来对刚刚创建好的服务进行访问。访问会返回 404 ,这是因为我们还没有对请求进行处理的原因。虽然我们写代码很简答,但是 go 语言 http 包背后做了很多事,创建默认服务结构体来处理请求。随后我们会自己实现服务结构体来替换掉 go 默认提供服务结构体。
路由处理程序
我们可以定义路由处理程序来处理和响应用户请求,调用 http
http.HandleFunc("/", func(http.ResponseWriter, *http.Request) {
log.Println("Hello World")
})
每一次访问路径 /
都会由函数 http.HandleFunc
来处理请求,这里简单通过log.Println("Hello World")
来验证一下是否访问 /
路径时会执行该函数。
http.HandleFunc
接收两个参数
- 路由匹配的字符串
-
func(ResponseWriter, *Request)
类型的函数
http 包会将函数进行注册作为默认 ServeMUX 路由管理器的 /
路径上。那么什么是serveMUX
,本质上 ServeMux 只是一个路由管理器。本身也实现了 Handler 接口的 ServeHTTP 方法。我们可自己定义实现 ServeHTTP 的 ServeMUX,随后在代码中将演示如何做这件事。
HandleFunc
在DefaultServeMux
中注册匹配/
路由的 handler函数,接下来注册一个处理/goodbye
路由的处理程序如下
http.HandleFunc("/goodbye", func(http.ResponseWriter, *http.Request) {
log.Println("good bye")
})
其中服务后,在终端输入来检查上面代码是否 work。
curl -v localhost:4600/goodbye
- ResponseWriter
- Request
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
log.Println("Hello World")
d, _ := ioutil.ReadAll(r.Body)
log.Printf("Data %s\n", d)
})
通过ioutil.ReadAll
读取请求体输入,输出到控制台。
curl -v -d 'Zidea' localhost:4600/
-
-d
执行请求在请求体中加入数据
输出结果为
Data Zidea
客户端输出
可以调用fmt.Fprintf
将字符串在客户端进行输出
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
log.Println("Hello World")
d, _ := ioutil.ReadAll(r.Body)
// log.Printf("Data %s\n", d)
fmt.Fprintf(rw, "Hello %s\n", d)
})
异常处理
在 go 语言没有 trycatch 机制,将所有错误一个值形式返回,通过判断 err 是否存在来进行错误处理,这一点对于 nodejs 开发经验应该不会陌生,不过有些人认为这种错误处理机制并不优雅。不过我们暂时接受这种错误处理形式,并且养成一个处理错误好习惯。所以我们现在对于读取请求错误进行处理,读取请求头失败在返回头给出错误信息,有关http请求状态希望大家阅读相关资料进行掌握。
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
d, err := ioutil.ReadAll(r.Body)
if err != nil{
rw.WriteHeader(http.StatusBadRequest)
rw.Write([]byte("Ooops"))
return
}
fmt.Fprintf(rw, "Hello %s\n", d)
})
不过 http 包中已经提供封装好的方法供我们调用,http.Error
,第一个参数为ResponseWriter
,第二个参数为返回信息,第三个参数为错误状态码。
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
d, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(rw, "Oops", http.StatusBadRequest)
return
}
fmt.Fprintf(rw, "Hello %s\n", d)
})
重构代码结构
- 在项目下创建 handlers 文件用于放置 handler 文件
- 创建 hello 文件,因为 hanlders 文件夹下所以无需在文件名后面添加后缀 Handler 表示功能为 Handler。
- 定义结构体 Hello
实现了 ServeHTTP 方法我们就实现 Handler 的接口,
func (h *Hello) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
d, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(rw, "Oops", http.StatusBadRequest)
return
}
fmt.Fprintf(rw, "Hello %s\n", d)
}
网友评论