基于BottledWater-PG+nodejs实时地图应用实践

简介: 前文作者讲述了BottledWater-PG安装部署,并在pg中实现了数据改变,向kafka发送消息的案例,详细参考《BottledWater-PG:PostgreSQL集成Kafka的实时数据交换平台》。

前文作者讲述了BottledWater-PG安装部署,并在pg中实现了数据改变,向kafka发送消息的案例,详细参考《BottledWater-PG:PostgreSQL集成Kafka的实时数据交换平台》。此前作者写过一篇pg的异步消息实现的实时地图应用案例《postgres+socket.io+nodejs实时地图应用实践》,本文将改用BottledWater-PG实现一遍。

一 服务器端

var fs = require('fs');
var http = require('http');
var socket = require('socket.io');
var Kafka = require('node-rdkafka');
    
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');
});
//注册socket.io
var socketio=socket.listen(server);
socketio.on('connection', function (socketclient) {
    console.log('已连接socket:');
    //socketclient.broadcast.emit('GPSCoor', data.payload);//广播给别人
    //socketclient.emit('GPSCoor', data.payload);//广播给自己
    
});

var consumer = new Kafka.KafkaConsumer({
  //'debug': 'all',
  'metadata.broker.list': '192.168.43.27:9092',
  'group.id': 'node-rdkafka-consumer-flow-example',
  'enable.auto.commit': false
});

var topicName = 'gps';

//logging debug messages, if debug is enabled 
consumer.on('event.log', function(log) {
  console.log(log);
});

//打印错误
consumer.on('error', function(err) {
  console.error('Error from consumer');
  console.error(err);
});

consumer.on('ready', function(arg) {
  console.log('consumer ready.' + JSON.stringify(arg));
  consumer.subscribe([topicName]);
  //准备消费消息
  consumer.consume();
});

consumer.on('data', function(m) {
    console.log(m);
    let _data;
    if(m.value==null)//delete操作发送来的消息
    {
        _data=JSON.parse(m.key);
        _data.tg_op='delete';
    }
    else{
        _data=m.value.toString();
        _data=JSON.parse(_data);
    }   
    console.log(_data);
    socketio.emit('GPSCoor', _data);//广播给所有的客户端
});

consumer.on('disconnected', function(arg) {
  console.log('consumer disconnected. ' + JSON.stringify(arg));
});

//启动
consumer.connect();

二 客户端

<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) {
                    console.log(data);
                    var id=data.id.int;
                    var feature;
                    if(data.tg_op=='delete'){
                        feature=gpsSource.getFeatureById(id);
                        if(feature)
                            gpsSource.removeFeature(feature);//删除点
                    }
                    else{
                        var geom=data.geom.string;
                        geom=wktform.readGeometry(geom);
                        geom.transform('EPSG:4326','EPSG:3857');
                        feature=gpsSource.getFeatureById(id);
                        if(feature)
                            feature.setGeometry(geom);//修改已有点
                        else{
                            feature=new ol.Feature({
                                geometry:geom
                            });
                            feature.setId(id);
                            gpsSource.addFeature(feature);//地图新增点
                        }
                    }
                });
            }
            
           
    </script>
</head>
<body onload="init()">
    <div id="map"></div>
</body>
</html>

三 测试成果

3.1 新增

mcsas=# insert into gps(name,geom) values ('opy','Point(118 31.5)');
INSERT 0 1
mcsas=# insert into gps(name,geom) values ('ty','Point(117 30.5)');
INSERT 0 1
img_fa5e408b18ab9db86a5d879c20f7dcc9.png
新增成果.png

3.2 修改

mcsas=# update gps set geom='Point(115 40)' where name='opy';
UPDATE 1
img_ab074c2cf274e1356c1df543d1e3acd1.png
修改成果.png

3.3 删除

mcsas=# delete from gps where name='opy';
DELETE 1

img_394f23ea6c9875f096f2a9012467a6bb.png
删除结果.png

四 总结

BottledWater-PG主要作用是将pg库中的表的增删改的消息都发往了kafka,应用程序并没有直接连接数据库,而是直接去消费kafka的消息。在表发生insert,update,delete能获取消息,但是truncate table并未向kafka生成消息,不知是否是我哪里遗漏。
  作者之前曾使用pg自带的notify与listen实现异步消息发送,该方法借助了表的触发器实现。应用程序是直连数据库且数据增删改都会走触发器。
  匆忙中,作者并未对比两者之间孰优孰劣,但一个直连库,一个间接消费,在不同需求中可选择一个比较符合要求的方案而加以应用。

相关文章
|
3月前
|
Web App开发 监控 JavaScript
【Node系列】创建第一个服务器应用
Node.js是一个基于Chrome V8引擎的JavaScript运行环境,可以用于构建高性能的网络应用程序。它采用事件驱动、非阻塞I/O模型,使得程序可以以高效地方式处理并发请求。
43 4
|
3月前
|
开发框架 JavaScript 中间件
中间件应用Koa.js(Node.js)
【5月更文挑战第3天】我们添加了两个中间件。第一个中间件记录请求的开始时间,并在下一个中间件执行完毕后计算并打印出请求的总时间。第二个中间件与之前的示例相同,它设置响应体为 "Hello World"
36 6
中间件应用Koa.js(Node.js)
|
3月前
|
JavaScript 前端开发 持续交付
【专栏】Vue.js和Node.js如何结合构建现代Web应用
【4月更文挑战第27天】本文探讨了Vue.js和Node.js如何结合构建现代Web应用。Vue.js作为轻量级前端框架,以其简洁易懂、组件化开发、双向数据绑定和虚拟DOM等特点受到青睐;而Node.js是高性能后端平台,具备事件驱动、非阻塞I/O、丰富生态系统和跨平台优势。两者结合实现前后端分离,高效通信,并支持热更新、持续集成、跨平台和多端适配,为开发高性能、易维护的Web应用提供强有力的支持。
191 2
|
3月前
|
存储 JavaScript 前端开发
Angular 应用 node_modules 子文件夹 @types 的作用介绍
Angular 应用 node_modules 子文件夹 @types 的作用介绍
|
19天前
|
存储 缓存 JavaScript
构建高效后端服务:Node.js与Express框架的实战应用
【8月更文挑战第2天】在数字化时代的浪潮中,后端服务的构建成为了软件开发的核心。本文将深入探讨如何利用Node.js和Express框架搭建一个高效、可扩展的后端服务。我们将通过实际代码示例,展示从零开始创建一个RESTful API的全过程,包括路由设置、中间件使用以及数据库连接等关键步骤。此外,文章还将触及性能优化和安全性考量,旨在为读者提供一套完整的后端开发解决方案。让我们一同走进Node.js和Express的世界,探索它们如何助力现代Web应用的开发。
|
16天前
|
数据采集 资源调度 JavaScript
Node.js 适合做高并发、I/O密集型项目、轻量级实时应用、前端构建工具、命令行工具以及网络爬虫和数据处理等项目
【8月更文挑战第4天】Node.js 适合做高并发、I/O密集型项目、轻量级实时应用、前端构建工具、命令行工具以及网络爬虫和数据处理等项目
30 5
|
21天前
|
JavaScript API 调度
深入理解Node.js事件循环及其在后端开发中的应用
【7月更文挑战第30天】本文旨在通过深入浅出的方式,解析Node.js事件循环机制的工作原理及其在后端开发中的实际应用。我们将从事件循环的基本概念出发,逐步探讨其与异步I/O操作的关系,以及如何利用事件循环优化后端性能和处理高并发请求。文章将结合实际案例,为读者提供清晰的认识和应用策略。
|
5天前
|
JavaScript 网络协议 前端开发
如何在单个VPS上使用nginx、forever和crontab托管多个Node.js应用
如何在单个VPS上使用nginx、forever和crontab托管多个Node.js应用
7 0
|
8天前
|
JavaScript Windows
NodeJs——如何获取Windows电脑指定应用进程信息
NodeJs——如何获取Windows电脑指定应用进程信息
14 0
|
2月前
|
JSON JavaScript 中间件
Express.js:构建轻量级Node.js应用的基石
**Express.js 概览**:作为Node.js首选Web框架,Express以其轻量、灵活和强大的特性深受喜爱。自2009年以来,其简洁设计和丰富中间件支持引领开发者构建定制化应用。快速开始:使用`express-generator`创建项目,安装依赖,启动应用。示例展示如何添加返回JSON消息的GET路由。Express适用于RESTful API、实时应用等多种场景,社区支持广泛,助力高效开发。
35 1