go 构建微服务程序
Go 是面向现代云服务架构的语言,服务之间通讯在HTTP应用层仅友好支持 RESTful 的服务 。因此,掌握 HTTP Resource API 的设计方法与工具、golang 客户端与服务器编程要点是必须 get 的技能。 本文介绍 API Blueprint 的使用,以及 golang 相关编程,以 API 为核心,支持测试驱动的编程(TDD)
需要支持 soap 服务或 oracle DB2 等数据库,请使用 java 等。
一、微服务 与 RESTful
1.1 微服务的概念
容器使得进程(应用)一次构建到处运行。人们开始思考一个系统一个进程这种开发、运营模式在云时代的合理性。在已有 SOA(Service-oriented architecture) 架构的基础上,提出了“微服务架构”的概念。
Martin Fowler 在2014年用博客明确了微服务的概念,Microservices - a definition of this new architectural term 【中文翻译】
简单来说,微服务架构风格[1]是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。
- 一个应用由一组服务构成
- 服务运行在自己的进程
- 服务间采用
HTTP Resource API
用现在的技术表达,即是一个应用由若干(容器)服务,通过进程间通讯(IPC)组成一个应用。
1.2 HTTP Resource API
1、分布式计算与 IPC 进化历史
- 进程间通过 Socket API 通讯
- 有了RPC(Remote procedure call)的概念,用 IDL (Interface Description Language) 规范进程间通讯
- 面向对象技术的出现:
- Common Object Request Broker Architecture (CORBA, 1991)
- remote method invocation (RMI)
- DCOM
- EJB
- web 技术的出现:
- Ajax(Asynchronous JavaScript And XML)。 (IE5 with XMLHttpRequest Object 1999)
- SOAP (Simple Object Access Protocol)。XML-based protocol for web service
- XML-RPC is an RPC protocol that uses XML to encode its calls and HTTP as a transport mechanism.
基于 SOAP / XML-RPC 构建的 Web Service 协议家族:
2、RESTful 协议
2000 年 Roy Thomas Fielding 的博士提出面向分布式多媒体软件的 REST (Representational State Transfer) 风格架构,RESTful 服务迅速成为互联网服务的事实标准。
SOAP RPC pk. REST RPC
HTTP message with SOAP for searching on google
GET search/beta2 HTTP/1.1
Host:api.google.com
Content-Type:application/soap+xml
SOAPAction: urn:GoogleSearchAction
<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmllsoap.org/soap/envelope/">
<soup:Body>
<gs:doGoogleSearch xmlns:gs="urn:GoogleSearch">
<q>REST</q>
…
</gs:doGoogleSearch>
</soup:Body>
</soap:Envelope>
HTTP message with REST for searching on google
GET services/rest?api_key=xxx&q=REST HTTP/1.1
Host:api.google.com
程序员的选择?
2.3、HTTP Resource API
HTTP Resource = URL?
HTTP Resource API,使用 GET、POST、PUT、DELETE 等现有 HTTP 指令存取远端资源的 API, 原则上使用 JSON 传输对象
阅读: Gregor Roth REST, 面向资源架构(2009)
原文: RESTful HTTP in practice
要点:
- 资源(Resource)的概念、识别与组织
- 个体资源
- 集合资源
- 子资源
- 操作(Action)/ 常用状态码
- GET, 获取资源
- PUT,创建或更新
- DELETE,删除资源
- POST,创建子资源
- PATCH,更新部分资源
- 查询与过滤
- 查询变量
- 常用 header 信息
- Content-Type: application/x-www-form-urlencoded; charset=utf-8
- text/plan
- application/x-www-form-urlencoded
- application/json
- application/xml
- Accept: application/json
- 仅用与 Request header, 与服务器协商如何输出资源
- Content-Length: 19
- 等于 0 ,不一定没有 body
- Content-Type: application/x-www-form-urlencoded; charset=utf-8
- PUT 与 POST 的区别
跟多关于 RESTful 知识, RESTful Web Services Tutorial
二、API 设计
API 案例: http://lbs.amap.com/api/webservice/guide/api/georegeo
这样的设计好不好? - 请不要妄议,历史因素是每个企业必须面对的问题。
如何表达这样的设计,并知道软件生产与测试是本节要求掌握的知识。
2.1 资源(领域)建模
建模元素:
- 实体: 用方框表示,第一栏表示实体的名称,例如 User;第二栏表示属性,语法:属性名:类型=默认值{描述属性} 例如 id:string {key,auto-inc}
- 关系: 实体之间的约束或需要持久化的状态。关系名称用过去时或状态表达
- 多重性:一对一,一对多,多对多
- 导航: 需要从一个实体导出相关实体,用箭头表示
2.2 API 设计
简单的应用我们可以参考前面的文章设计
更简单,可参考 [RESTful API 设计指南]
(http://www.ruanyifeng.com/blog/2014/05/restful_api.html)
[GET] v1/users - 获取所有用户
[POST] v1/users - 新建用户
[GET] v1/users/{id} - 查询用户明细信息
[PATCH] v1/users/{id} - 修改用户明细信息
[DELETE] v1/users/{id} - 删除用户
[GET] v1/users/{id}/owned-meetings - 获取用户创建的会议
[GET] v1/users/{id}/joined-meetings - 获取用户参与的会议
[GET] v1/meeting
...
单数与复数
上述 API 仅考虑了资源的 CRUD,并没有考虑资源的业务处理。 思考一个,如果我们要验证一个用户或者激活一个用户,它的 Api 应该是什么模样呢?
[GET] v1/user/login{?name,password}
[GET] v1/user/active{?activecode}
[GET] v1/users/login{?name,password}
如果选用复数,则 login
与 用户的 id
将难以区分。使用 单数名词/业务动作{?参数列表}
的设计模式,更加易于理解和处理。
2.3 API 设计工具与文档化
API design First 是微服务架构首先遵循的理念!
上述设计文档不能随着系统同步进化,因此,我们需要一体化工具支持 API 的设计、验证、代码生成(客户端、服务器端)、文档自动化、以及测试自动化。
1、Api Blueprint?
一句话:用 markdown 写 API
*扩展阅读:API 设计: RAML、Swagger、Blueprint三者的比较
竞争产品: apizza
基于云原生应用开发的创新
云服务技术作为新生事物,出现了无数创新型企业,这些企业的丰富并完善了云应用开发的生态环境。中国企业依托国内软件生产的需求,在模仿中获得了成长。以下列表只是一些罗列,对应的国内应用请自己收集:
- Heroku 云应用开发管理平台
- wercker CI/CD一体化服务平台
- apiblueprint HTTP 资源 API
2、Api Blueprint 快速入门
- 用 github Open API 登陆 https://apiary.io/
- 创建一个在线项目 agenda
- 将 agenda 关联到你的代码仓库
- 在线输入以下代码,
push
到仓库
FORMAT: 1A
HOST: http://agenda12.apiblueprint.org/
# Agenda
Agenda is a simple API allowing consumers to schedule meeting on-line
# Group User
Resource operations related to a user in the API.
## User-Key [/v1/user/getkey{?username,password}]
+ Parameters
- username : `root` (string, required) - User Name
- password : `pass` (required)
### Get User Key [GET]
get a security key for operations later
+ Response 200 (application/json)
{
"key":"1e3576bt"
"permissions":["user","admin"]
}
# Group Users
Resources related to a users in the API.
## Users Collection [/v1/users{?key}]
+ Parameters
- key : `1e3576bt` (string, required)
### List all Users [GET]
+ Response 200 (application/json)
[
{
"id":1
"username":"zhang3"
"password":"zhang"
"email":"zhang3@mail2.sysu.edu.cn"
},{
"id":2
"username":"li4"
"password":"li"
"email":"li4@mail2.sysu.edu.cn"
}
]
### Create a New User [POST]
+ Request (application/json)
{
"username":"zhang3"
"password":"zhang"
"email":"zhang3@mail2.sysu.edu.cn"
}
+ Response 201 (application/json)
+ Headers
Location: /users/1
+ Body
{
"id": 1
"username":"zhang3"
"password":"zhang"
"email":"zhang3@mail2.sysu.edu.cn"
}
### Get User by ID [GET /v1/users/{id}]
+ Parameters
- id : `1` (int, required) - User Name
+ Response 200 (application/json)
{
"id" : 1
"username":"zhang3"
"password":"zhang"
"email":"zhang3@mail2.sysu.edu.cn"
}
# Group Meetings
本文档 URL: https://agenda12.docs.apiary.io/#
- 在 documentation 选择 Get User Key, 选择 Mocker server, 发现网站以为你部署完成了 Mocker 测试网站。 在浏览器输入:
https://private-c2bed8-agenda12.apiary-mock.com/v1/user/getkey?username=root&password=pass
你会得到正确的响应。这是一个 令人激动 的功能,我们已经可以通过该 API ,客户端和服务器并行开发开始了!
3、API Blueprint 文档说明书
- 系统建议你设计 API 先定义数据,以便于描述接口
进入 tutorial
页面:
- 先阅读简单介绍
Language Specification
是完成接口定义,必须自学的!Examples
官方所用案例,你需要模仿它完成你的 API 设计!
三、Go REST客户端开发
Go REST 客户端主要涉及三个库 net/http
,io/ioutil
和 encode/json
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
res, err := http.Get("http://private-c2bed8-agenda12.apiary-mock.com/v1/user/getkey?username=root&password=pass")
if err != nil {
panic(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
retJSON := struct {
MyKey string `json:"key"`
Permissions []string
}{"w", []string{"w", "w"}}
w, err := json.Marshal(retJSON)
if err != nil {
panic(err)
}
fmt.Println(string(w), retJSON)
fmt.Println(string(body))
if err := json.Unmarshal(body, &retJSON); err != nil {
panic(err)
}
fmt.Println(retJSON)
}
案例给出用 go http 客户端访问 mock 服务的小程序。
要点
- http 库提供了常用的三个函数
- Get
- Post
- PostForm
- defer res.Body.Close()
- 每个请求都必须关闭 Body 流
- json 读写
- struct 属性名称必须是可导出(大写)
- struct 有三个 json 序列化和反序列化的 tag
json:"-"
// 禁止转换属性json:"myName"
//属性名称映射json:",omitempty"
//空值处理,逗号!json:"myName,omitempty"
//
RESTful 有更多动作、需要特殊的头、或需要处理重定向?
- golang使用http client发起get和post请求示例
- gorilla/http 的重写: https://github.com/gorilla/http
- 一个简单的封装 https://github.com/go-resty/resty
四、go REST 服务端开发
似乎 REST 服务端开发, urfave/negroni
,gorilla/mux
,unrolled.render
的组合几乎能完全满足 REST 的任何开发需求。
你必须考虑 RESTful 服务的用户:
- 在网页中嵌入 URL 来显示一个 html 组件。如在自己的网站上,使用地图 API 做一个标签
- 使用 jQuery, NodeJS 框架的浏览器客户端
- 非浏览器客户端
1、友好传统网站
- 仅使用 GET、POST
- 使用 application/x-www-form-urlencoded 类型输入,使用 application/json 输出
2、使用 nodejs 框架
- 服务端输入要处理:
- 可以使用任意的 GET,POST,PUT,DELETE,PATCH,HEAD …
- 检测 Content-Type 的文档类型 和 字符编码 以正确处理输入
- 服务端输出要处理:
- 检测请求的 Accept 的文档类型,并正确输出对应的文档
- 要处理 HEADERs 以保证客户端处理 Respose
- 要正确处理 status, 不仅是 200,404
- 例如: 201 `创建正确,且在 HEAD 中 LINK 的导航
- 最烦人的就是 jsonp 格式的输出
浏览器跨域请求
- 对于现代浏览器
对于传统浏览器必须支持 jsonp
- 要检测 url 中
callback
参数 - 检测请求的 Accept 的文档类型 application/jsonp 或 text/script
- 必须使用 reader.Jsonp 输出
- 要检测 url 中
什么是 jsonp ?
- 浏览器沙箱中只支持相同网站及子网站的请求
- 但有 src 属性的 tag 例外
- XMLHTTPRequest 对象,会接受
callbackName("json string")
的 Responce.Body 通过eval(jsonp)
得到对应的对象
3、非浏览器客户端访问
- 按 RESTful 规范即可!