postgres+socket.io+nodejs实时地图应用实践
2019獨角獸企業重金招聘Python工程師標準>>>
nodejs一直以異步io著稱,其語言特性尤其擅長于在realtime應用中,如聊天室等。在進行實時應用開發時,必不可少的需要用到 socket.io庫,可以說,nodejs+socket.io在實時應用中具有較好的表現能力。
??本文既然選擇以實時地圖應用做個小例子,那么選擇經典的PostgreSQL/PostGIS作為地圖的數據庫。希望實現的是模擬數據庫數據插入了新的GPS坐標,而一旦數據發生改變,立刻將插入的GPS坐標廣播到服務端,服務端廣播到所有的客戶端地圖上,進行定位展示。早期作者使用的是redis的廣播/訂閱機制,最近發現Pg數據庫的listen/notify也具備這種消息傳遞機制。
??本文主要的socke.io廣播/訂閱參考官網,Pg的listen/notify自行谷歌,作者僅簡述一下自己如何考慮應用的。
一 服務器端
var fs = require('fs');var http = require('http');var socket = require('socket.io');var pg = require('pg');var util=require('util');var constr=util.format('%s://%s:%s@%s:%s/%s', 'postgres','postgres','123456','192.168.43.125',5432,'Test');var server = http.createServer(function(req, res) {res.writeHead(200, { 'Content-type': 'text/html'});res.end(fs.readFileSync(__dirname + '/index.html'));}).listen(8081, function() {console.log('Listening at: http://localhost:8081');});var pgClient = new pg.Client(constr);//數據庫連接var socketio=socket.listen(server);//socketiosocketio.on('connection', function (socketclient) {console.log('已連接socket:');//socketclient.broadcast.emit('GPSCoor', data.payload);//廣播給別人//socketclient.emit('GPSCoor', data.payload);//廣播給自己});var sql = 'LISTEN gps'; //監聽數據庫的gps消息var query = pgClient.query(sql);//開始數據庫消息監聽//數據庫一旦獲取通知,將通知消息通過socket.io發送到各個客戶端展示。pgClient.on('notification', function (data) {console.log(data.payload);//socketio.sockets.emit('GPSCoor', data.payload); //與下面的等價socketio.emit('GPSCoor', data.payload);//廣播給所有的客戶端});pgClient.connect();二 數據庫端
建立一個測試表如下:
create table t_gps(id serial not null,geom geometry(Point,4326),constraint t_gps_pkey primary key (id) ); --建立索引 create index t_gps_geom_idx on t_gps using gist(geom);對表的增刪改建立一個觸發器,觸發器中發送變化數據出去:
CREATE OR REPLACE FUNCTION process_t_gps() RETURNS TRIGGER AS $body$DECLARErec record;BEGINIF (TG_OP = 'DELETE') THEN--插入的GPS都是4326的經緯度,我們將在3857的谷歌底圖上顯示數據,發送轉換后的3857出去select TG_OP TG_OP,OLD.id,ST_AsText(ST_Transform(OLD.geom,3857)) geom into rec;perform pg_notify('gps',row_to_json(rec)::text);RETURN OLD;ELSIF (TG_OP = 'UPDATE') THEN select TG_OP TG_OP,NEW.id,ST_AsText(ST_Transform(NEW.geom,3857)) geom into rec;perform pg_notify('gps',row_to_json(rec)::text);RETURN NEW;ELSIF (TG_OP = 'INSERT') THENselect TG_OP TG_OP,NEW.id,ST_AsText(ST_Transform(NEW.geom,3857)) geom into rec;perform pg_notify('gps',row_to_json(rec)::text);RETURN NEW;END IF;RETURN NULL;END; $body$ LANGUAGE plpgsql;CREATE TRIGGER T_GPS_TRIGGER AFTER INSERT OR UPDATE OR DELETE ON T_GPSFOR EACH ROW EXECUTE PROCEDURE process_t_gps();三 客戶端
<html> <head><meta charset='utf-8'><title>實時地圖應用</title><link rel="stylesheet" href="http://openlayers.org/en/v3.18.2/css/ol.css" type="text/css"><script src="http://openlayers.org/en/v3.18.2/build/ol.js"></script><script src="/socket.io/socket.io.js"></script><script>var wktform=new ol.format.WKT();//wkt解析var gpsSource=new ol.source.Vector();function init(){var gpsLayer=new ol.layer.Vector({source:gpsSource,style:new ol.style.Style({image: new ol.style.Icon(({anchor: [0.5, 1],src: 'http://openlayers.org/en/v3.18.2/examples/data/icon.png'}))})});var map = new ol.Map({layers : [new ol.layer.Tile({title : '街道圖',visible : true,source : new ol.source.XYZ({url : 'http://www.google.cn/maps/vt?pb=!1m5!1m4!1i{z}!2i{x}!3i{y}!4i256!2m3!1e0!2sm!3i342009817!3m9!2szh-CN!3sCN!5e18!12m1!1e47!12m3!1e37!2m1!1ssmartmaps!4e0&token=32965'})}),gpsLayer],target : 'map',controls : ol.control.defaults({attributionOptions : ({collapsible : false})}),view : new ol.View({center : [0, 0],zoom : 2})});var iosocket = io.connect();//接受服務端消息iosocket.on('GPSCoor', function(data) {data=JSON.parse(data);switch(data.tg_op){case 'INSERT':var feature=new ol.Feature({geometry:wktform.readGeometry(data.geom)});feature.setId(data.id);gpsSource.addFeature(feature);//地圖新增點break;case 'UPDATE':var geom=wktform.readGeometry(data.geom);var feature=gpsSource.getFeatureById(data.id);if(feature)feature.setGeometry(geom);//修改已有點break;case 'DELETE':var feature=gpsSource.getFeatureById(data.id);if(feature)gpsSource.removeFeature(feature);//刪除點break;}});}</script> </head> <body onload="init()"><div id="map"></div> </body> </html>客戶端接收到消息后,改變當前地圖上的圖標gps坐標位置。
四 測試與結果
連開三個客戶端連接如下:
?
初始化三個客戶端.png
服務器端socket連接成功.png
4.1 數據庫新增GPS坐標
insert into t_gps(geom) values (st_geomfromtext('Point(0 0)',4326)); insert into t_gps(geom) values (st_geomfromtext('Point(118 32)',4326)); insert into t_gps(geom) values (st_geomfromtext('Point(-118 -32)',4326));頁面自動響應效果如下:
服務器端監聽到的數據庫消息.png
服務器端socket到客戶端的效果.png
4.2 數據庫修改GPS坐標
查看下當前的數據如下:
Test=# select id,st_astext(geom) from t_gps;id | st_astext ----+-----------------24 | POINT(0 0)25 | POINT(118 32)26 | POINT(-118 -32) (3 rows)將id=25的坐標改成 150,40:
Test=# update t_gps set geom=st_geomfromtext('Point(150 40)',4326) where id=25; UPDATE 1服務器端打印如下:
顯示一條更新語句.png
更新效果.png
4.3 數據庫刪除GPS坐標
Test=# delete from t_gps where id=25; DELETE 1顯示一條刪除.png
刪除效果.png
所有以上操作,只是數據的增刪改指令,服務器和客戶端都是自動響應的。
結論:本文實現了,數據庫一旦廣播了消息,服務器端監聽,并繼續以sockeio廣播到客戶端。全部過程,只是數據庫發送了一個坐標消息無任何其他操作。pg的notify和listen消息機制,真實應用一般比如寫在觸發器中,觸發器監聽是否有數據采集終端將新坐標寫入或者更新,然后在觸發器中notify消息,這樣,前端實時響應。可以做到將終端應用位置無任何操作的一波流發送到全部客戶端實時展示。
?
轉載于:https://my.oschina.net/u/3371661/blog/3041232
新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!總結
以上是生活随笔為你收集整理的postgres+socket.io+nodejs实时地图应用实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux基础书籍推荐
- 下一篇: hadoop--Shuffle机制