go 构建微服务程序

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 进化历史

go 构建微服务程序_第1张图片

  1. 进程间通过 Socket API 通讯
  2. 有了RPC(Remote procedure call)的概念,用 IDL (Interface Description Language) 规范进程间通讯
  3. 面向对象技术的出现:
    • Common Object Request Broker Architecture (CORBA, 1991)
    • remote method invocation (RMI)
    • DCOM
    • EJB
  4. 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 协议家族:

go 构建微服务程序_第2张图片

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


<soap:Envelope xmlns:soap="http://schemas.xmllsoap.org/soap/envelope/">
    <soup:Body>
        <gs:doGoogleSearch xmlns:gs="urn:GoogleSearch">
            <q>RESTq>
            …
        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
  • PUT 与 POST 的区别

跟多关于 RESTful 知识, RESTful Web Services Tutorial

二、API 设计

API 案例: http://lbs.amap.com/api/webservice/guide/api/georegeo

这样的设计好不好? - 请不要妄议,历史因素是每个企业必须面对的问题。

如何表达这样的设计,并知道软件生产与测试是本节要求掌握的知识。

2.1 资源(领域)建模

go 构建微服务程序_第3张图片

建模元素:

  • 实体: 用方框表示,第一栏表示实体的名称,例如 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?

什么是 API Blueprint?

一句话:用 markdown 写 API

*扩展阅读:API 设计: RAML、Swagger、Blueprint三者的比较

竞争产品: apizza

基于云原生应用开发的创新

云服务技术作为新生事物,出现了无数创新型企业,这些企业的丰富并完善了云应用开发的生态环境。中国企业依托国内软件生产的需求,在模仿中获得了成长。以下列表只是一些罗列,对应的国内应用请自己收集:

  • Heroku 云应用开发管理平台
  • wercker CI/CD一体化服务平台
  • apiblueprint HTTP 资源 API

2、Api Blueprint 快速入门

  1. 用 github Open API 登陆 https://apiary.io/
  2. 创建一个在线项目 agenda
  3. 将 agenda 关联到你的代码仓库
  4. 在线输入以下代码, 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":"[email protected]"
            },{
                "id":2
                "username":"li4"
                "password":"li"
                "email":"[email protected]"
            }
        ]

### Create a New User [POST]

+ Request (application/json)

        {
            "username":"zhang3"
            "password":"zhang"
            "email":"[email protected]"
        }

+ Response 201 (application/json)

    + Headers

            Location: /users/1

    + Body

            {
                "id": 1
                "username":"zhang3"
                "password":"zhang"
                "email":"[email protected]"
            }

### 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":"[email protected]"
        }

# 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 文档说明书

进入 https://apiblueprint.org/

  • 系统建议你设计 API 先定义数据,以便于描述接口

进入 tutorial 页面:

  • 先阅读简单介绍
  • Language Specification 是完成接口定义,必须自学的!
  • Examples 官方所用案例,你需要模仿它完成你的 API 设计!

三、Go REST客户端开发

Go REST 客户端主要涉及三个库 net/http ,io/ioutilencode/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 服务的用户:

  1. 在网页中嵌入 URL 来显示一个 html 组件。如在自己的网站上,使用地图 API 做一个标签
  2. 使用 jQuery, NodeJS 框架的浏览器客户端
  3. 非浏览器客户端

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 格式的输出

浏览器跨域请求

  • 对于现代浏览器
    • 一劳永逸的办法: Golang利用Access-Control-Allow-Origin响应头解决跨域请求问题
  • 对于传统浏览器必须支持 jsonp

    • 要检测 url 中 callback 参数
    • 检测请求的 Accept 的文档类型 application/jsonp 或 text/script
    • 必须使用 reader.Jsonp 输出
  • 什么是 jsonp ?

    • 浏览器沙箱中只支持相同网站及子网站的请求
    • 但有 src 属性的 tag 例外
    • XMLHTTPRequest 对象,会接受 callbackName("json string") 的 Responce.Body 通过 eval(jsonp) 得到对应的对象

3、非浏览器客户端访问

  • 按 RESTful 规范即可!

你可能感兴趣的:(golang)