go微服務系列之二

這是使用gomicro開發微服務系列的第二篇,在上一篇中我只是使用了user-srv和web-srv實現了一個demo,在這裏我將實用consul實現服務發現。若是想直接查閱源碼或者經過demo學習的,能夠訪問ricoder_demohtml

如何編寫一個微服務?這裏用的是go的微服務框架go micro,具體的狀況能夠查閱:http://btfak.com/%E5%BE%AE%E6%9C%8D%E5%8A%A1/2016/03/28/go-micro/node

1、如何安裝consul

個人開發系統採用的是ubunt16.04,這裏就給出ubuntu下安裝consul的步驟:linux

$ wget https://releases.hashicorp.com/consul/0.7.2/consul_0.7.2_linux_amd64.zip
$ sudo apt-get install unzip

$ ls

$ unzip consul_0.7.2_linux_amd64.zip
$ sudo mv consul /usr/local/bin/consul

$ wget https://releases.hashicorp.com/consul/0.7.2/consul_0.7.2_web_ui.zip
$ unzip consul_0.7.2_web_ui.zip
$ mkdir -p /usr/share/consul
$ mv dist /usr/share/consul/ui

Consul 壓縮包地址:https://www.consul.io/downloads.htmlgit

驗證安裝是否成功的方法:github

$ consul
Usage: consul [--version] [--help] <command> [<args>]

Available commands are:
    agent          Runs a Consul agent
    catalog        Interact with the catalog
    event          Fire a new event
    exec           Executes a command on Consul nodes
    force-leave    Forces a member of the cluster to enter the "left" state
    info           Provides debugging information for operators.
    join           Tell Consul agent to join cluster
    keygen         Generates a new encryption key
    keyring        Manages gossip layer encryption keys
    kv             Interact with the key-value store
    leave          Gracefully leaves the Consul cluster and shuts down
    lock           Execute a command holding a lock
    maint          Controls node or service maintenance mode
    members        Lists the members of a Consul cluster
    monitor        Stream logs from a Consul agent
    operator       Provides cluster-level tools for Consul operators
    reload         Triggers the agent to reload configuration files
    rtt            Estimates network round trip time between nodes
    snapshot       Saves, restores and inspects snapshots of Consul server state
    validate       Validate config files/directories
    version        Prints the Consul version
    watch          Watch for changes in Consul

啓動consul服務的方法:web

$ consul agent -dev
==> Starting Consul agent...
==> Consul agent running!
           Version: 'v0.9.3'
           Node ID: '199ee0e9-db61-f789-b22a-b6b472f63fbe'
         Node name: 'ricoder'
        Datacenter: 'dc1' (Segment: '<all>')
            Server: true (Bootstrap: false)
       Client Addr: 127.0.0.1 (HTTP: 8500, HTTPS: -1, DNS: 8600)
      Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)

優雅的中止服務的方法:json

命令:CTRL+Cubuntu

其餘命令:後端

  • consul members:查看集羣成員
  • consul info:查看當前服務器的情況
  • consul leave:退出當前服務集羣

成功開啓consul服務後能夠登陸後臺訪問地址:http://localhost:8500,以下:api

Screenshot from 2017-10-14 13-35-58.png

2、api-srv的開發,實現動態路由

根據官方對api-srv的介紹:The micro api is an API gateway for microservices. Use the API gateway pattern to provide a single entry point for your services. The micro api serves HTTP and dynamically routes to the appropriate backend service. 粗略翻譯的意思就是:api-srv是微服務的網關,使用API網關模式能夠爲咱們的服務提供一個入口,api-srv提供HTTP服務,並動態路由到相應的後端服務。

步驟1:監聽8082端口,並綁定handler處理http請求

mux := http.NewServeMux()
	mux.HandleFunc("/", handleRPC)
	log.Println("Listen on :8082")
	http.ListenAndServe(":8082", mux)

步驟2:實現handler,並實現跨域處理

if r.URL.Path == "/" {
		w.Write([]byte("ok,this is the server ..."))
		return
	}

	// 跨域處理
	if origin := r.Header.Get("Origin"); cors[origin] {
		w.Header().Set("Access-Control-Allow-Origin", origin)
	} else if len(origin) > 0 && cors["*"] {
		w.Header().Set("Access-Control-Allow-Origin", origin)
	}

	w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-Token, X-Client")
	w.Header().Set("Access-Control-Allow-Credentials", "true")
	if r.Method == "OPTIONS" {
		return
	}

	if r.Method != "POST" {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

步驟3:實現將url轉換爲service和method,這裏我採用了pathToReceiver這個函數來處理

p = path.Clean(p)
	p = strings.TrimPrefix(p, "/")
	parts := strings.Split(p, "/")

	// If we've got two or less parts
	// Use first part as service
	// Use all parts as method
	if len(parts) <= 2 {
		service := ns + strings.Join(parts[:len(parts)-1], ".")
		method := strings.Title(strings.Join(parts, "."))
		return service, method
	}

	// Treat /v[0-9]+ as versioning where we have 3 parts
	// /v1/foo/bar => service: v1.foo method: Foo.bar
	if len(parts) == 3 && versionRe.Match([]byte(parts[0])) {
		service := ns + strings.Join(parts[:len(parts)-1], ".")
		method := strings.Title(strings.Join(parts[len(parts)-2:], "."))
		return service, method
	}

	// Service is everything minus last two parts
	// Method is the last two parts
	service := ns + strings.Join(parts[:len(parts)-2], ".")
	method := strings.Title(strings.Join(parts[len(parts)-2:], "."))
	return service, method

http傳進來的url是http://127.0.0.1:8082/user/userService/SelectUser,我在handler中經過如下方式調用後:

service, method := apid.PathToReceiver(config.Namespace, r.URL.Path)

service和method分別是:

2017/10/14 13:56:12 service:com.class.cinema.user
2017/10/14 13:56:12 method:UserService.SelectUser

注意:var config.Namespace = "com.class.cinema"

步驟4:封裝request,調用服務

br, _ := ioutil.ReadAll(r.Body)

	request := json.RawMessage(br)

	var response json.RawMessage
	req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, &request)
	ctx := apid.RequestToContext(r)
	err := (*cmd.DefaultOptions().Client).Call(ctx, req, &response)

在這裏Call就是調用相應服務的關鍵。

步驟5:對err進行相應的處理和返回調用結果

// make the call
	if err != nil {
		ce := microErrors.Parse(err.Error())
		switch ce.Code {
		case 0:
			// assuming it's totally screwed
			ce.Code = 500
			ce.Id = service
			ce.Status = http.StatusText(500)
			// ce.Detail = "error during request: " + ce.Detail
			w.WriteHeader(500)
		default:
			w.WriteHeader(int(ce.Code))
		}
		w.Write([]byte(ce.Error()))
		return
	}
	b, _ := response.MarshalJSON()
	w.Header().Set("Content-Length", strconv.Itoa(len(b)))
	w.Write(b)

經過對err的處理,在請求的method或者service不存在時,如:

Screenshot from 2017-10-14 14-05-28.png

會有相應的錯誤信息提示返回到客戶端。

3、跑起服務,查看效果

步驟1:首先要先跑起consul服務發現機制,這樣後期加入的服務才能夠被檢測到,如:

Screenshot from 2017-10-14 14-34-13.png

步驟2:跑起user-srv服務,如:

Screenshot from 2017-10-14 14-35-36.png

登陸consul後臺,查看服務是否被發現:

Screenshot from 2017-10-14 14-36-41.png

能夠從中看到多了一個com.class.cinema.user這個服務

步驟3:經過postman訪問user-srv服務

Screenshot from 2017-10-14 14-39-37.png

能夠看到在Body處有數據顯示出來了,再看看服務後臺的日誌輸出

Screenshot from 2017-10-14 14-40-12.png

Screenshot from 2017-10-14 14-40-29.png

由上面兩個圖能夠看出來,客戶端的請求到達了api-srv,再經過api-srv到達了user-srv。

注意:此處的url的書寫曾經碰見過一個bug,那就是我第一次書寫成了 http://127.0.0.1:8082/user/SelectUser,致使出現這種異常:

Screenshot from 2017-10-14 14-44-35.png

有興趣的能夠關注個人我的公衆號 ~

qrcode_for_gh_04e57fbebd02_258.jpg

相關文章
相關標籤/搜索