Node.js垂直水平擴充套件與訊息整合方案
這是一篇討論Node.js在無需修改任何程式碼從單核垂直擴充套件到多核,再水平擴充套件到多臺叢集和訊息整合的分散式系統,展示了Node.JS在無縫擴充套件性方面要強於Java。其主要架構是Node.js微服務 + 訊息Messaging + 叢集Clustering 。翻譯如下:
當使用微服務建立一個複雜的分散式系統時,關鍵問題是提供服務之間的通訊,微服務一般使用REST API,你一般會快取遠方的狀態,但系統是在不斷地變化,也許你狀態已經過期了,你可以透過輪詢重新整理你的複製,但這是不可擴充套件的,增加系統壓力,很多重複的請求通常反而得不到最新的狀態( ‘不,還沒有新的訊息。別再重複問了。”)
更好的方式是反轉資訊流,讓服務擁有資料當發生變化時主動告訴你。這是來自微服務為基礎的訊息系統,這需要某種訊息代理。
另一個重要的特點是可擴充套件性Scalable。你可能需要迅速提高你的處理能力,垂直或水平伸縮擴充套件微服務能力是關鍵。
水平和垂直擴充套件
通常認為從Java遷移到Node.js的開發人員其實並不真正理解水平擴充套件的含義,因為在JEE容器中執行緒池實現了魔術的處理,增加真實或虛擬的伺服器其實不需要軟體這邊做出什麼修改,舉例,如果你為你的JVM增加CPU核數,JEE容器會擴充套件利用它們,如果你增加更多記憶體,你就需要微調JVM引數才能充分利用擴大的記憶體,否則它像以往一樣工作,這時你可能需要重新啟動多個JVM, 在這點上你發現你的JEE應用實際並不是以叢集方式編寫的。
在Node.js這裡,增加更多CPU核數其實也不難做到,使用PM2如下命令:
pm2 start app.js -i max
無論如何,對於Node.js水平或垂直擴充套件是不管你編寫程式碼的方式,你只需要透過叢集利用在同一臺機器上所有CPU核心,不用像Java那樣需要在多個獨立的伺服器或虛擬機器實現負載均衡。
我真的喜歡Node.js這個特性–它迫使你從一開始就要考慮叢集,阻止你在請求之間持有資料,迫使你儲存狀態到一個共享的資料庫中,這個資料庫能夠被所有正在執行的例項訪問。這使得從垂直到水平的擴充套件性切換,根本不影響你。這裡沒什麼新的東西,只是基本的share-nothing的好處。
然而,使用PM2載入多個Node.js或使用Node的叢集模組與使用Nginx代理作為負載平衡器之間是有重要區別的:使用Nginx 作為代理伺服器時,我們有一個獨立的繫結到一個機器上的一個埠伺服器,負載平衡和URL代理同時已經完成。在Nginx中是這樣配置:
http { upstream myapp1 { server srv1.example.com; server srv2.example.com; server srv3.example.com; } server { listen 80; location / { proxy_pass http://myapp1; } } } <p class="indent"> |
如果你試圖在單臺機器上啟動多個Node伺服器時,除了第一個都將失敗,因為它們(srv1. srv2. srv3.)不能繫結到同一個80埠上,然而,如果你使用Node的叢集模組或使用PM2,會有一點魔法發生,主程式有一段程式碼會啟用Socket在多個工作程式之間共享,使用了一種共享策略(或者使用作業系統定義 或者Node 0.12的’round-robin’),這非常類似於Nginx在不同伺服器之間為你做的事情,Nginx也有負載平衡的選項(round-robin, 最少連線, IP-hash, weight-directed重導向等).
訊息
下面我們將訊息和叢集兩個概念放在一起。
為了讓事情變得更具體生動,讓我們看一個真實世界的例子。我們已經編寫的活動的流微服務。它的工作是收集活動流規範2草案中的活動,並將它們儲存在cloudant資料庫,這樣它們以後可以作為一種活動流檢索。這項微服務做一件事情,並且做得很好–它聚集來自系統任何地方的活動–然後發射一個活動到一個專用的MQTT主題。
我們使用mqtt作為MQTT客戶端,RabbitMQ作為我們的多語種訊息代理,Node.js作為我們的活動微服務,這已經是我們不止第一次這麼架構了。
當前面談的叢集加入其中時,MQTT是一個pub/sub協議.為了讓每個訂閱者從佇列中讀取訊息, RabbitMQ為叢集中每個Node例項開設一個單獨佇列例項。
[img index=1]
但這並不是我們需要的,每個例項都將收到一個“新活動”訊息,並試圖將它寫入資料庫,資料庫這裡需要避免競爭。即使資料庫可以阻止其他Node保證只有一個Node節點成功寫入記錄,這也是一種浪費,因為所有的Node節點都在執行同一個任務。
這裡的問題是,用於叢集模組“白魔法”來處理HTTP / HTTPS伺服器請求但並沒有延伸到MQTT模組。
我們解決這個問題原始想法是,如果我們遷移訊息客戶端到主例項,它會對進來的訊息響應,然後傳遞他們到後面的從工作例項,以round- robin方式,這似乎是合理的,但需要調整一個ICK引數,因為需要我們自己實施自己的負載平衡,它會阻止我們使用PM2(因為我們必須對從工作例項進行控制),如果我們使用多個虛擬機器和Nginx的負載平衡,我們將回到原點(Nginx並不支援單機同一個埠的負載平衡)。
幸運的是,我們發現RabbitMQ已經可以處理這部分,如果我們放棄持久,並且確認我們是MQTT抽象下執行AMQP。RabbitMQ的pub / sub拓撲方式是:釋出者提交到 “主題”交流,然後被繫結到一個使用路由作為Key的佇列上(事實上,在AMQP路由key和MQTT主題topic有直接對映)。
原來使用MQTT客戶端使得每個群集例項接收自己的佇列。現在透過遷移到AMQP客戶端和將所有例項繫結到同一個佇列,我們讓RabbitMQ對客戶端使用round-robin實現基本負載平衡。
[img index=2]
下面是使用Eclipse PAHO Java客戶端釋出MQTT訊息給主題Topic程式碼,使用Node.js或•Ruby等其他客戶端幾乎一樣:
public static final String TOPIC = "activities"; MqttClient client; try { client = new MqttClient("tcp://"+host+":1883", "mqtt-client1"); client.connect(); MqttMessage message = new MqttMessage(messageText.getBytes()); client.publish(TOPIC, message); System.out.println (" [x] Sent to MQTT topic '" +TOPIC+"': "+ message + "'"); client.disconnect(); } catch (MqttException e) { // TODO Auto-generated catch block e.printStackTrace(); } <p class="indent"> |
上面是發出訊息到‘activities’主題,下面是接受訊息,然後將佇列使用匹配的路由key(還是‘activities’)和預設的AMQP主題交換(topic exchange (“amq.topic”))繫結,佇列的名稱不重要,只要所有Node的伺服器都使用同一個即可,實際上名稱在它們之間會相互複製。
var amqp = require('amqp'); var connection = amqp.createConnection({ host: 'localhost' }); // Wait for connection to become established. connection.on('ready', function () { // Use the default 'amq.topic' exchange connection.queue('worker-queue', { durable: true}, function (q) { // Route key identical to the MQTT topic q.bind('activities'); // Receive messages q.subscribe(function (message, headers, deliveryInfo, messageObject) { // Print messages to stdout console.log('Node AMQP('+process.pid+'): received topic "'+ deliveryInfo.routingKey+ '", message: "'+ message.data.toString ()+'"'); }); }); }); <p class="indent"> |
[該貼被banq於2014-04-24 12:27修改過]
相關文章
- 乾貨丨如何水平擴充套件和垂直擴充套件DolphinDB叢集?套件
- 負載均衡的原理(垂直擴充套件 Scale Up、橫向擴充套件 Scale Out)負載套件
- Android Handler機制之訊息池的擴充套件 SimplePool與SynchronizedPoolAndroid套件synchronized
- Mybatis外掛擴充套件以及與Spring整合原理MyBatis套件Spring
- Docker compose 水平擴充套件 和負載均衡Docker套件負載
- 訊息同步 —— 在 Airbnb 我們是怎樣擴充套件移動訊息的AI套件
- Lambda擴充套件與用途套件
- BSGS與擴充套件BSGS套件
- kotlin 擴充套件(擴充套件函式和擴充套件屬性)Kotlin套件函式
- 進行Windows Azure SQL Database的水平擴充套件GVWindowsSQLDatabase套件
- pika叢集水平擴充套件——讓效能容量不再受限套件
- Django訊號與擴充套件:深入理解與實踐Django套件
- 如何使用訊息佇列、Spring Boot和Kubernetes擴充套件微服務佇列Spring Boot套件微服務
- 釘釘推送機器人訊息傳送 Laravel 擴充套件包機器人Laravel套件
- SpringCloudGateway資料庫儲存路由資訊的擴充套件方案SpringGCCloudGateway資料庫路由套件
- gochat - 純go實現的im即時通訊系統(支援水平擴充套件)Go套件
- Solon詳解(六)- Solon的校驗擴充套件框架使用與擴充套件套件框架
- 【Kotlin】擴充套件屬性、擴充套件函式Kotlin套件函式
- SpringMVC 擴充套件SpringMVC套件
- Mybatis擴充套件MyBatis套件
- 擴充套件工具套件
- Sanic 擴充套件套件
- ORACLE 擴充套件Oracle套件
- 讀構建可擴充套件分散式系統:方法與實踐06非同步訊息傳遞套件分散式非同步
- JMeter 擴充套件開發:擴充套件 TCP 取樣器JMeter套件TCP
- 使用Kotlin擴充套件函式擴充套件Spring Data案例Kotlin套件函式Spring
- Spring Cloud Gateway 資料庫儲存路由資訊的擴充套件方案SpringCloudGateway資料庫路由套件
- 以太坊擴充套件解決方案Nahmii簡介套件
- ASP.NET Core擴充套件庫之Http通用擴充套件ASP.NET套件HTTP
- ?用Chrome擴充套件管理器, 管理你的擴充套件Chrome套件
- [外掛擴充套件]騰訊分析外掛套件
- 提高擴充套件性套件
- LINQ擴充套件方法套件
- HttpContext擴充套件類HTTPContext套件
- Json擴充套件方法JSON套件
- 擴充套件BSGS/exBSGS套件
- 擴充套件包上傳套件
- Flask 自建擴充套件Flask套件
- Nmap 擴充套件(四)套件