前言
在微服務(wù)架構(gòu)中,服務(wù)發(fā)現(xiàn)一直是一件比較復(fù)雜的事嫌吠。而且服務(wù)發(fā)現(xiàn)式的架構(gòu)處理不好止潘,容易產(chǎn)生集中化。同時(shí)居兆,微服務(wù)的提供覆山,不可避免的需要一些負(fù)載均衡方案,實(shí)現(xiàn)服務(wù)的高可用和可擴(kuò)展泥栖,這無疑增加了很多復(fù)雜度簇宽。
筆者認(rèn)為勋篓,使用異步、基于消息的方式魏割,可能更適合微服務(wù)架構(gòu)譬嚣。
基于消息的微服務(wù)架構(gòu),對于所有微服務(wù)的部署條件非常簡單钞它,只需要能訪問到消息服務(wù)即可拜银。同時(shí)微服務(wù)節(jié)點(diǎn)的移除和增加不會(huì)影響到服務(wù)的提供。相比服務(wù)發(fā)現(xiàn)的架構(gòu)遭垛,簡單太多了尼桶,簡單即是美。
在這次實(shí)踐中锯仪,使用到了seneca泵督,一個(gè)nodejs 微服務(wù)框架。seneca,使用seneca-amqp-transport插件庶喜,可以輕松構(gòu)建基于消息的微服務(wù)小腊。
下面是架構(gòu)圖:
https://www.processon.com/view/link/59dc2491e4b0ef561379bc25
在這個(gè)架構(gòu)中,我們使用的是標(biāo)準(zhǔn)的seneca定義的命令規(guī)范久窟,這可能是所有微服務(wù)都需要遵守的一個(gè)規(guī)范秩冈,至于說使用其他語言,也很簡單斥扛。封裝一個(gè)seneca命令規(guī)范的庫即可入问。不知道官方有沒開發(fā),開發(fā)起來難度也不會(huì)太大犹赖。
接口層比較靈活队他,可以根據(jù)上層應(yīng)用特性卷仑,來決定如何封裝傳輸協(xié)議峻村,最后將轉(zhuǎn)化成標(biāo)準(zhǔn)命令發(fā)送到消息服務(wù)。不建議直接訪問消息服務(wù)锡凝,上層應(yīng)用應(yīng)保持靈活粘昨。
完整的實(shí)踐代碼:https://github.com/luaxlou/micro-service-practice.git
1 前期準(zhǔn)備
使用docker-machine創(chuàng)建虛擬機(jī)。
關(guān)于docker的一些基本用法窜锯,可以讀上一篇文章:docker+consul基于服務(wù)發(fā)現(xiàn)的極簡web架構(gòu)實(shí)踐张肾,這里就不再贅述。
依次創(chuàng)建3臺(tái)虛擬機(jī):
$ dm create -d "virtualbox” node1
$ dm create -d "virtualbox” node2
$ dm create -d "virtualbox" node3
2 開始構(gòu)建
搭建Rabbitmq消息服務(wù)
消息隊(duì)列服務(wù)锚扎,已經(jīng)成為高并發(fā)應(yīng)用的必備基礎(chǔ)服務(wù)吞瞪。我們選用Rabbitmq,你可以換成任意的驾孔,遵循amqp協(xié)議即可芍秆。
使用docker安裝很方便惯疙,但是生產(chǎn)環(huán)境不建議使用docker安裝。更推薦的是使用云服務(wù)妖啥,這樣能保證足夠高的高可用和擴(kuò)展性霉颠。雖然價(jià)格貴點(diǎn),但是這是唯一的單點(diǎn)荆虱,花點(diǎn)錢還是值得的蒿偎。
直接安裝在宿主機(jī)上:
$ docker search rabbitmq
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
rabbitmq RabbitMQ is an open source multi-protocol ... 1466 [OK]
tutum/rabbitmq Base docker image to run a RabbitMQ server 11
frodenas/rabbitmq A Docker Image for RabbitMQ 11 [OK]
sysrun/rpi-rabbitmq RabbitMQ Container for the Raspberry Pi 2 ... 6
aweber/rabbitmq-autocluster RabbitMQ with the Autocluster Plugin 5
gonkulatorlabs/rabbitmq DEPRECATED: See maryville/rabbitmq 5 [OK]
letsxo/rabbitmq RabbitMQ with Management and MQTT plugins. 4 [OK]
bitnami/rabbitmq Bitnami Docker Image for RabbitMQ 3 [OK]
$ docker run -d --name rabbit -p 5672:5672 rabbitmq
這樣就啟動(dòng)了一個(gè)消息隊(duì)里服務(wù),并且開放5672端口
安裝jenkins
jenkins用于自動(dòng)集成怀读,不然每次構(gòu)建是個(gè)很麻煩的事诉位。
下面的實(shí)踐是筆者掉了不少坑之后完成的,jenkins在安裝過程中會(huì)有不少麻煩菜枷,而且在mac下安裝也會(huì)遇到麻煩不从。
將jenkins 安裝到 node1
$ dm ssh node1
$ mkdir /mnt/sda1/var/jenkins_home
$ sudo chown 1000 /mnt/sda1/var/jenkins_home
$ sudo chown 1000 /var/run/docker.sock
$ docker run -d -v /var/run/docker.sock:/var/run/docker.sock \
-v /mnt/sda1/var/jenkins_home:/var/jenkins_home \
-v $(which docker):/usr/bin/docker -p 8080:8080 jenkins
查看初始密碼:
$ cat /mnt/sda1/var/jenkins_home/secrets/initialAdminPassword
安裝私有的Registry
在mac上安裝即可
$ docker run -d -p 5000:5000 registry
文檔參考:https://docs.docker.com/registry/spec/api/
準(zhǔn)備代碼
代碼使用的是seneca官方的例子,完整的Dockerfile也已經(jīng)寫好犁跪。
FROM node:alpine
RUN npm install pm2 -g
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY . .
CMD ["pm2-docker","process.yml"]
為了讓nodejs能使用到多核cpu椿息,Dockerfile 集成了pm2,使用pm2來管理node進(jìn)程坷衍。
完整代碼:
https://github.com/luaxlou/micro-service-practice.git
配置自動(dòng)集成
這里使用了最新版的jenkins寝优,新版的jenkins使用了pipline。一種新的構(gòu)建方式,使用groovy語法枫耳。
寫起來是挺優(yōu)雅的乏矾,但是學(xué)習(xí)成本頗高。因?yàn)槲臋n不全及有些文檔失效迁杨,筆者不得已反編譯了pipeline插件钻心,才得以調(diào)通。
使用pipeline script
node {
stage('Preparation') {
def r = git('https://github.com/luaxlou/micro-service-practice.git')
}
stage('Build') {
dir('seneca-listener') {
withEnv(["DOCKER_REGISTRY_URL=http://192.168.99.1:5000"]) {
docker.build("seneca-listener").push("latest")
}
}
}
}
開始構(gòu)建铅协,順利的話捷沸,會(huì)看到如下的結(jié)果:
這是pipeline的特性,可以可視化看到各個(gè)階段的執(zhí)行情況狐史,算是不小的進(jìn)步吧痒给。
訪問私有Registy的API,就可以看到生成的tag骏全。
curl http://192.168.99.1:5000/v2/seneca-listener/tags/list
最后一步苍柏,試試我們的程序
在宿主機(jī)發(fā)布消息:
$ git clone https://github.com/luaxlou/micro-service-practice.git
seneca-clinet 代碼是接口層代碼的示意,可以根據(jù)自己的喜好封裝姜贡。
同時(shí)直接發(fā)送了命令代碼用于測試试吁。
進(jìn)入seneca-clinet 目錄
$ AMQP_URL=192.168.99.1:5672 node index.js
這個(gè)程序會(huì)每隔兩秒發(fā)送一個(gè)命令:
#!/usr/bin/env node
'use strict';
const client = require('seneca')()
.use('seneca-amqp-transport')
.client({
type: 'amqp',
pin: 'cmd:salute',
url: process.env.AMQP_URL
});
setInterval(function() {
client.act('cmd:salute', {
name: 'World',
max: 100,
min: 25
}, (err, res) => {
if (err) {
throw err;
}
console.log(res);
});
}, 2000);
雖然一直在發(fā)命令,你很快就會(huì)發(fā)現(xiàn)命令全部超時(shí)了楼咳。這是因?yàn)檫€沒有消費(fèi)者熄捍,當(dāng)然這些命令也沒有丟失律秃,只不過接口層沒有得到及時(shí)返回。如果應(yīng)用層支持異步的模式治唤,每個(gè)command都有獨(dú)立的id棒动,可以保留id后,以后再過來取宾添。這就很靈活了船惨,一切看需求去封裝接口層即可。
進(jìn)入node2
$ docker run 192.168.99.1:5000/seneca-listener:latest
0|seneca-l | {"kind":"notice","notice":"hello seneca fwunhukrcmzn/1507605332382/16/3.4.2/-","level":"info","seneca":"fwunhukrcmzn/1507605332382/16/3.4.2/-","when":1507605332661}
啟動(dòng)后缕陕,回到seneca-clinet粱锐,發(fā)現(xiàn)之前超時(shí)的命令洒疚,全部接收到了绕德。
{ id: 86,
message: 'Hello World!',
from: { pid: 16, file: 'index.js' },
now: 1507605332699 }
{ id: 44,
message: 'Hello World!',
from: { pid: 16, file: 'index.js' },
now: 1507605332701 }
{ id: 56,
message: 'Hello World!',
from: { pid: 16, file: 'index.js' },
now: 1507605332703 }
{ id: 57,
message: 'Hello World!',
from: { pid: 16, file: 'index.js' },
now: 1507605332706 }
{ id: 58,
message: 'Hello World!',
from: { pid: 16, file: 'index.js' },
now: 1507605332707 }
至此,完整架構(gòu)已經(jīng)構(gòu)建完畢爽醋。
一些未完的事項(xiàng)
1.自動(dòng)集成蔬崩,只需要配置webhook即可恶座。
2.自動(dòng)部署,因?yàn)閐ocker運(yùn)轉(zhuǎn)的方式沥阳,當(dāng)服務(wù)升級時(shí)需要重啟docker進(jìn)程跨琳。方式有很多,比較粗暴的是直接控制宿主機(jī)桐罕,或者類似salt這樣的工具脉让。
目前來說,沒有找到太好的開源方案功炮。個(gè)人傾向于自己開發(fā)agent溅潜,發(fā)布有限的API,用于常規(guī)的部署或者其他任務(wù)薪伏,以及可以定時(shí)收集服務(wù)器的信息滚澜,用于監(jiān)控。這可能會(huì)是筆者的下一個(gè)開源項(xiàng)目毅该。
總結(jié)
這篇文章算是一個(gè)新的里程碑博秫,實(shí)踐的成果將用于后期的架構(gòu)潦牛。docker讓我從傳統(tǒng)的架構(gòu)模式中脫離出來眶掌,同時(shí)也讓我吃了不少苦頭。但這一切都是值得的巴碗。
同時(shí)也是一個(gè)新的開始朴爬,終于從之前的公司出來。未來何去何從橡淆,有很多的未知召噩,但我相信都是美好的母赵。
這也許就是人生的魅力。
Hello World>叩巍凹嘲!