小白入門微服務(5) - 服務註冊發現實戰(docker+registrator+consul+nodejs+python)
概述
- 前言
- 原始碼
- registrator
- API gateway
- web 服務
- 執行
- 後記
前言
這篇文章真是等了挺久才寫,讓小夥伴們久等了。這篇文章旨在帶你走一下微服務的流程,真實的微服務遠不止這些東西。詳細的介紹敬請關注我後面的文章。**如果我的文章對你有幫助,歡迎關注、點贊、轉發,這樣我會更有動力做原創分享。**OK,進入正題!
原始碼
首先甩出原始碼吧,因為這對於剛接觸的小夥伴來說這可能還是個龐然大物。先大致瀏覽一下程式碼,對後面理解會好一些。關注微信公眾號【zone7】後臺回覆【微服務】獲取原始碼。
docker-compose
version: '2' services: # consul server,對外暴露的ui介面為8500,只有在2臺consul伺服器的情況下叢集才起作用 consulserver: image: progrium/consul:latest hostname: consulserver ports: - "8300" - "8400" - "8500:8500" - "53" command: -server -ui-dir /ui -data-dir /tmp/consul --bootstrap-expect=2 networks: - app # consul server1在consul server服務起來後,加入叢集中 consulserver1: image: progrium/consul:latest hostname: consulserver1 depends_on: - "consulserver" ports: - "8300" - "8400" - "8500" - "53" command: -server -data-dir /tmp/consul -join consulserver networks: - app # consul server2在consul server服務起來後,加入叢集中 consulserver2: image: progrium/consul:latest hostname: consulserver2 depends_on: - "consulserver" ports: - "8300" - "8400" - "8500" - "53" command: -server -data-dir /tmp/consul -join consulserver networks: - app # 監聽容器中暴露的埠,一有新的埠,註冊到註冊中心 registrator: image: gliderlabs/registrator:master hostname: registrator depends_on: - "consulserver" volumes: - "/var/run/docker.sock:/tmp/docker.sock" command: -internal consul://consulserver:8500 networks: - app # web 服務 web-nodejs: build: ./webNodejs image: webapp:latest depends_on: - "consulserver" ports: - "3000" environment: SERVICE_3000_NAME: service-web networks: - app # web 服務 web-py: build: ./webPy image: webpy:latest ports: - "5000" environment: SERVICE_5000_NAME: service-web-py volumes: - ./webPy:/usr/local/work networks: - app command: bash -c "pip install -r requirements.txt && python app.py" # api gateway gateway-py: build: ./gateWayPy image: gatewaypy:latest ports: - "5000:5000" environment: SERVICE_5000_NAME: service-gateway-py volumes: - ./gateWayPy:/usr/local/work networks: - app command: bash -c "pip install -r requirements.txt && python app.py" # api gateway gateway-nodejs: build: ./gateWayJs image: gatewayjs:latest ports: - "3000:3000" environment: SERVICE_3000_NAME: service-gateway-js networks: - app networks: app: driver: bridge
registrator
關於 registrator ,上一篇文章已經有介紹,這裡就不再贅述。這裡找到一箇中文文件,如下:
http://www.mamicode.com/info-detail-2383285.html#%E6%B3%A8%E5%86%8C%E5%90%8E%E7%AB%AF
下面簡單介紹一下,docker-compose 中的命令:
command: -internal consul://consulserver:8500
連線到 consul
environment: SERVICE_5000_NAME: service-web 設定服務發現中的名。
服務名是你在服務發現查詢中使用的。預設情況下,服務名按下面的格式確定:
<base(container-image)>[- if >1 ports]
使用容器映象的基礎,如果映象是 gliderlabs/footbar,服務名就是footbar。如果映象是 redis,服務名就是簡單的 redis。
而且如果一個容器有多個暴露埠,它將各自追加內部暴露埠以區別。例如,一個映象 nginx 有兩個暴露埠,80 和 443,將產生兩個服務,分別命名 nginx-80 和 nginx-443。
你可以使用標籤或者環境變數,SERVICE_NAME 或者 SERVICE_x_NAME,其中 x 是內部暴露埠,覆蓋這些預設名字。注意如果一個容器有多個暴露埠,設定SERVICE_NAME 會導致多個服務命名為 SERVICE_NAME-。
API gateway
關於 API gateway,這裡使用 nodejs 實現了一個簡易版的 API gateway。說他簡易,那真的事很簡易,就只有服務發現和服務註冊的功能。至於 python,也實現了一套,但是值得一提的事 python 的 consul 庫不提供 服務監控功能,就是說當你新新增一個服務,程式是不會自動監控的。python 程式碼就不在貼出來了,詳情請檢視原始碼。
const Consul = require('consul');
const utils = require('./utils');
const serviceLocalStorage = require('./serviceLocalStorage.js');
class Discovery {
connect(...args) {
if (!this.consul) {
console.log("與consul server連線中...")
//建立連線,
//需要注意的時,由於需要動態獲取docker內的consul server的地址,
//所以host需要配置為consulserver(來自docker-compose配置的consulserver)
//發起請求時會經過docker內建的dns server,即可把consulserver替換為具體的consul 伺服器 ip
this.consul =new Consul({
host:'consulserver',
...args,
promisify: utils.fromCallback //轉化為promise型別
});
}
return this;
}
/**
* 根據名稱獲取服務
* @param {*} opts
*/
async getService(opts) {
if (!this.consul) {
throw new Error('請先用connect方法進行連線');
}
const {service} = opts;
// 從快取中獲取列表
const services = serviceLocalStorage.getItem(service);
if (services.length > 0) {
console.log(`命中快取,key:${service},value:${JSON.stringify(services)}`)
return services;
}
//如果快取不存在,則獲取遠端資料
let result = await this
.consul
.catalog
.service
.nodes(opts);
console.log(`獲取服務端資料,key:${service}:value:${JSON.stringify(result[0])} result: ${JSON.stringify(result)}`);
serviceLocalStorage.setItem(service, result[0])
return result[0];
}
}
module.exports = new Discovery();
web 服務
這裡的 web 服務的主要功能就是獲取當前主機的 ip 地址。我分別用 pyhon 和 nodejs 兩種語言實現了一遍,其程式碼如下:
python 實現
app = Flask(__name__)
@app.route('/')
def a():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
finally:
s.close()
return "hello zone !ip address ==> " + ip
@app.route('/index')
def index():
c = consul.Consul(host='consulserver')
(index, data) = c.catalog.service("service-web")
host = str(data[0]["ServiceAddress"]) + ":" + str(data[0]["ServicePort"])
print("==========="+host)
r = requests.get('http://'+host)
return r.text
if __name__ == '__main__':
app.run(host='0.0.0.0')
# app.run(host='0.0.0.0', debug=True)
nodejs 實現
const Application = require('koa');
const app = new Application();
const Router = require('koa-router');
const router = new Router();
const ip = require('ip');
router.get('/', async(ctx) => {
ctx.body = {
ip: ip.address()
}
})
//監聽3000埠
app.listen(3000, () => {
console.log('listen on port 3000')
});
app
.use(router.routes())
.use(router.allowedMethods);
執行
網頁端訪問:
控制檯 log,不斷重新整理網頁邊出現如下 bug:
現在關閉一個微服務:createnewinstance_web-py_1
服務發現監控資訊如下:
[{
"Node": "consulserver",
"Address": "172.22.0.3",
"ServiceID": "registrator:dockermicroservices_web-py_2:5000",
"ServiceName": "service-web",
"ServiceTags": null,
"ServiceAddress": "172.22.0.4",
"ServicePortNode": "consulserver",
"Address": "172.22.0.3",
"ServiceID": "registrator:dockermicroservices_web-py_4:5000",
"ServiceName": "service-web",
"ServiceTags": null,
"ServiceAddress": "172.22.0.6",
"ServicePort": 5000
}, {
"Node": "consulserver",
"Address": "172.22.0.3",
"ServiceID": "registrator:dockermicroservices_web-py_3:5000",
"ServiceName": "service-web",
"ServiceTags": null,
"ServiceAddress": "172.22.0.5",
"ServicePort": 5000
}]
新啟動一個微服務:
version: '2'
services:
web-py:
build: ../webPy
image: webpy:latest
# depends_on:
# - "consulserver"
ports:
- "5000"
environment:
SERVICE_5000_NAME: service-web
volumes:
- ../webPy:/usr/local/work
networks:
- app
command: bash -c "pip install -r requirements.txt && python app.py"
networks:
app:
driver: bridge
執行命令 docker-compose up
監控服務:
[{
"Node": "consulserver",
"Address": "172.22.0.3",
"ServiceID": "registrator:dockermicroservices_web-py_2:5000",
"ServiceName": "service-web",
"ServiceTags": null,
"ServiceAddress": "172.22.0.4",
"ServicePortNode": "consulserver",
"Address": "172.22.0.3",
"ServiceID": "registrator:dockermicroservices_web-py_4:5000",
"ServiceName": "service-web",
"ServiceTags": null,
"ServiceAddress": "172.22.0.6",
"ServicePort": 5000
}, {
"Node": "consulserver",
"Address": "172.22.0.3",
"ServiceID": "registrator:dockermicroservices_web-py_3:5000",
"ServiceName": "service-web",
"ServiceTags": null,
"ServiceAddress": "172.22.0.5",
"ServicePort": 5000
}, {
"Node": "consulserver",
"Address": "172.22.0.3",
"ServiceID": "registrator:createnewinstance_web-py_1:5000",
"ServiceName": "service-web",
"ServiceTags": null,
"ServiceAddress": "172.25.0.2",
"ServicePort": 5000
}]
多了一個微服務,說明服務發現成功。
後記
最重要的還是要自己去操作與實踐,不然看懂了也沒用。
參考文獻:
基於Docker實現服務治理(一)
基於Docker實現服務治理(二)
本文首發於公眾號【zone7】,關注獲取最新推文。