Nginx->NodeJS->Redis开发测试(4)接收/解析/验证

很多东西,仅仅只是为了用而用,不去讨论这个软件组件存在的必要性和可替代性。

接下来,我们将YY一下,数据从车载的TBOX/OBD等设备发送出来之后的流程,短信网关通常采用中国移动、联通的服务。这部分对于SMS短信进行规则性解析暂时还不是很了解,所以暂时先不无法覆盖这部分的内容。

1/ 车辆TBOX/OBD上报SMS消息给应用网关

从车载TBOX中发出的短信长什么样?

我们假设一把先,它就是来自一个汽车里面的盒子,里面插了个SIM卡,然后每隔10秒就发个短消息给某个专用的特定号码而已。对于整车厂来说,这个盒子可能是固定在车内的,拿不出来的,并非是后来找个地方安装上去的。

在我们的例子中,则没有这样的外置的盒子,都是默认盒子是整车厂直接默认就安装在车内的。

这里找个例子,中国移动出的路尚OBD盒子,直接插在汽车的OBD接口上,数据就直接上载到云了,下个应用App就可以直接看到自己车辆的所有的数据。

OBD盒子的汽车上的接口,汽车启动就开始传输数据了到中国移动OneNET网关了。(大众的车貌似都是在A的这个位置

如果发过来短信的号码不在自己的SIM卡管理范围中,可能是别人发错了,或者是一个欺诈或者虚假消息,可以直接删除这个消息。还有其他的一些规则校验,都需要实现,这些内容其实属于SIM管理的一些功能。

假设下面这是我们的收到的消息(这个短信在传输前被TBOX/OBD加过偏移量、传输过程经过几道加密、进制转换等,因此这个消息我们是没法直接看懂的,所以我们需要一个解析规则表,因为每个消息ID都有一个实现规定的解析规则和编码表,这应该算是车厂或者零部件厂商的内部的机密了,否则即使你拿到原始消息,你也不知道如何去解析出真实的消息

EES0066654E91A204E64646654E94A373B2456D789D4….省略….

还有,这个报文是很长的,有的报文可能超过几百字节,收到这个短消息之后如何进行解析?还需要做什么操作呢?

1.1 解析规则表

按照规定好的消息格式进行短息的字符串分割,前面8位置是MessageID,因为每个MessageID所带的内容都不同,有长有段,所以必须根据MessageID来判断怎么截图数据。无论怎样,大概是这个样子:

MessageIDEES006消息的内容的的编码规则

属性(不在消息中)开始长度偏移量单位其他
device_id7100车架号
time_stamp17 140发送消息的时间戳
highest_temp 315-20c
lowest_temp365-20c
high_temp_module4150
highest_volt465-100mV
lowest_volt515-100mV
high_volt_module5650

1.2 解析短信数据的流程

  1. 加上所需的时间戳(如果报文里面不带时间)
  2. 解析短信,将其结构化为JSON、CSV、String的格式。
    然后准给发送给Nginx服务器(它会帮忙做路由,路由到实际的某个企业内网中的Node服务器)
  3. 发出http请求,结束自己的使命
    发送http请求(POST协议)给Nginx服务器

2/ NodeJS on Nginx负债均衡、反向代理

尽管Node的高性能很好,其实也可以不需要使用Nginx作为,而且Node里面的http模块实现了完整HTTP1.1的全部功能(差不多代码都是参考Nginx来写的)。孰强孰弱,可以参考《巨头终极对决,Apache、Nginx 与 Node.js 之争》一文,里面提供非常详细的测试结果作为论证。

这里不做任何的讨论。因为我们就是要用Nginx来做为前端的服务器,提供对外的IP地址,而Node服务器不对外提供服务,也不会暴露出去。

2.1 安装Nginx服务器

http://nginx.org/en/download.html

下载后、直接解压后在我们的c:\bigdata目录下,进入其目录,双击nginx.exe,就启动了,会在windows的后台运行。如果你在浏览器中直接运行:http://localhost。

浏览器就会返回Nginx服务器的欢迎页面,这表明Nginx服务器正在运行中。

2.2 配置Nginx服务器 – 反向代理

Nginx的功能有静态页面路由、反向代理、负载均衡,有一些我们可能需要用到几台服务器才能模拟出来。例如,负载均衡。

刚安装好的Nginx服务器是一个空的服务器,不干任何事情,所以我们将其停掉。然后让它担任第一件事事情。这里,我们先实现它的反向代理功能。

 

Nginx下面的conf目录中的nginx.conf文件的内容,我们在http{    }的区域中删除了默认的一些参考和示例的内容,只放了我们的一个代理服务的server的内容。

  • server_name,填写实际对外可以访问的IP或者服务器域名
    www.host123.com
  • listen
    80端口
    其实可以设置一个特别的端口,例如3333或者8888,这样就可以把80的默认端口给空出来
  • proxy_pass
    填写需要转发的目的的服务器地址和端口(通常是内网的IP)

重新启动nginx服务(Windows下需要去服务里面手动的去结束这个进程,再启动,Linux下里面只需要nginx restart就可以了)。此时在浏览器中输入:http://www.host123.com:3333

此时,GET/POST的请求将会被内部转发到http://localhost:8125这个端口上。这个后台的转发对于调用这个请求的用户或者外部系统来说,是透明不可见的,也是不需要关心的。

在单机环境下,用Nginx和Node在同一个Windows操作系统下,为了模拟请求从Nignx转发到Node中,我们特意在Windows 的host文件中,配置了一个www.host123.com的假的域名。

如果你有两台电脑或者采用虚拟机,则可以考虑一台上安装Nginx,另外一台安装Node,使用真实的IP来进行配置,而非总是在一台电脑中进行模拟。

2.3 配置Nginx服务器 – 负载均衡

修改conf/nginx.conf的文件的内容。

  • 将服务的入口服务器修改为apple.local
  • 访问端口修改为8888
  • 使用Upstream来创建Nginx对后台服务器的负载均衡

拷贝server.js的内容到server2.js中,将其监听的端口以及输出的消息全部变成8126.

确保

  • http://127.0.0.1: 8125,监听8125端口(模拟服务器A) ,由server.js来模拟
  • http://127.0.0.1: 8126,监听8126端口(模拟服务器B) ,由server2.js来模拟

然后,在命令行启动运行这个两个nodejs的服务器端程序。

重新启动Nginx服务器,在浏览器中输入http://www.host123.com:8888, 浏览器会出现:

再次刷新请求,服务会变成另外一个提供者。

这说明Nginx服务器,已经在动态的转发我们的http请求到后面的两个不同端口的服务了。

2.4 管理NodeJS应用

在服务器环境下,通常会使用pm2,forever,nohup等方式来后台运行nodejs的应用,守护其进程。因为NodeJS提供了pm2,forever这两个比较好的进程管理包,所以我们就直接使用pm2或者forever来管理我们两台服务器上的http服务。但是pm2目前貌似比forever应用的更广,所以我们就使用pm2来管理应用。

步骤:登录到服务器A中,进入到部署server.js的目录下。(然后,再登录到服务器B中,进入到部署server.js的目录)

命令行中,使用npm安装pm2组件

然后使用命令,这里-i意思是,启动4个进程,有多少个cpu核,就用多少个进程。

如果只是应用pm2来进行管理nodejs应用的话,还是比较方便的,所以这里就不多阐述。

在postman中,继续对http://www.host123.com:8888进行POST的请求,传入数据。

  • pm2 show 1
    查看进程1的server.js的运行情况
  • pm2 log server
    查看server它所输出的所有的日志, 这里可以看到server.js启动了4个进程,来处理传入的POST消息,并且将console.log的输出到各自所在的.log文件。

对pm2这个包就不在再多的深入了,毕竟每个东西都可以学的很细致,也可以探的很浅,关键是看你有没有这个时间和你的应用场景有多复杂和真正需要什么。

到这里为止,Nginx的部分就不再继续说明了,毕竟我们每挑每样东西最关键和我们最需要的部分来使用。

3/ 解析JSON消息,校验消息(搜索Redis缓存库)

在server.js这个后台服务收到消息之后,第一件事就是要解析这个消息,从数据从JSON数组里面拿出来,将其变成可以识别的数据。然后对其进行基本的完整性的校验,和数据有效性的校验。

由于这种校验的是非常简单的(看数据是否在某个表里面找到即可),但是这种查询的频率是相当高的,所以,我们要对之前的架构做一个小小的调整,变成下面的这种方式。在数据被写入到HBase之前,先从Redis中查询车辆的车架号是否存在,存在的话才保存起来。

 

从SMS应用网关发来的消息是JSON格式,并且包含了设备的ID。在server.js收到这个请求之后,要对每个消息进行验证,验证的过程如下:

  • 转换JSON消息成为一个JSON对象
  • 提取DEVICE_ID,验证消息是否完整(关键性消息是否缺失,例如车架号是否有丢失等)

数据的有效性验证

  • 验证消息的来源是否是正常的,从Redis里面获得车辆的匹配信息(主要是Device_ID,车架号\发动机号这样的)
  • 如果数据可靠有效,则继续后续步骤。否则,不做任何处理,直接结束这个请求。

3.1 为什么需要使用Redis

呵呵,这是一个好问题。

因为NodeJS面对的一个高并发(上万并发)的请求场景,每个消息都需要进行验证、这样势必有大量的数据库读请求,验证这个消息的是否可靠。如果使用传统关系型数据库(例如,MySQL)直接暴露出去,数据库则会面临很大压力,被大量请求直接给弄崩溃是大概率的事件。所以,通常很多互联网公司会将Redis+RDBMS搭配起来使用。

所以使用Redis这样的Key-Value的键值的缓存库方案,可以直接非常高效的应对NodeJS的请求,请求一个Key(车架号)给你,你直接告诉返回的值是否有,找不到,那么这个消息是虚假消息。

Linux、MacOS安装Redis都很简单,2~3MB的包,打开就可以运行了。因为又切换到Windows环境了,所以我们这里直接下个Windows版本的Redis,下载latest版本。

https://github.com/ServiceStack/redis-windows 

打开命令行窗口,启动Redis,不要关闭窗口。

新开一个CMD命令行窗口,启动Redis客户端(测试是否可用),下面就用SET/GET来操作数据的写入

Redis是目前公认的最高效率的一个缓存库(不能看成是一个独立的数据库),但是Redis是没有Table、Schema的概念的,通常搭配着主数据库一起使用。在我们的场景中,Redis主要用来存储:
1)我厂生产所有车辆(车架号、车型、品牌),使用JSON格式存储
2)TBOX的消息和对应关系(消息ID,消息类型,隐私),使用JSON格式存储

3.2 要缓存的数据怎么放入到Redis

因为Redis是K-V的缓存库,那么Key是键值(String类型的,区分大小写),Value里面能够放什么呢?在Redis中,Value支持String(上面的set,get的例子),hashes(哈希), lists(列表), sets(集合), sorted sets(序列集合)等数据集作为Value。

对于我们需要的数据,肯定是一行数据,而不是一个简单的string作为Value的全部类型。我们需要将这个JSON格式的数据放入到Redis中,Key就是ID0000001,这种类型的数据适用于哈希类型。

所以,我们可以将这样的数据放入到Redis中,我们使用Redis客户端工具来测试一下。

  • hmset ,是写HASH的命令,通常用于处理一组数据
  • hgetall,是取HASH的命令

类似的Redis的命令还有:

  • hset
    执行hset多次,相当于执行hmset一次
  • hget
    从key中取出值,但是只取出其中某个字段的值

类似的其他的命令还有很多,但是在我们的应用场中,就是应用读数据(校验消息),更新数据(初始化的时候,将所有所需的数据全部写入到Redis中)。

3.3 NodeJS中从Redis中读取数据

继续打开server.js文件,然后加入从Redis从读取数据的功能。NodeJS访问Redis的话,基本上都会使用redis这个npm包,所以这里需要:

然后,引入redis的包,并且创建一个redis的客户端。

开始校验数据是否在Redis中存在

通过redis这个npm包,可以从redis中快速读取到消息(车架号)是否存在。

如果存在,就继续把数据保存到HBase中,然后将数据发给Kafka。

3.2 车辆主数据怎么放入到Redis

NodeJS每次接受到消息之后,它都会验证这个消息是否是我厂的车辆发过来的,它需要从Redis中读数据,那么存储在Redis中数据是什么时候放进去的呢?这是一个单独的功能,肯定是需要单独在某个批处理程序中去实现的,我们来看看如何做:

其实有很多种方法,我们来假设一下:

  • 整车厂每次有新车下线,移交给4S店,就会将新车的主数据放入到某个主数据数据库中(MySQL),
    用来保存所有车辆的信息
    车架号、发动机机号、出厂日期、生产地址、型号等等
  • 假设这个数据库是MySql ,主数据存储在car_master_table表中。

我们需要经常从这个MySQL数据库的表中读取最新,最全的信息放入到Redis中(直接覆盖,不检查是否已经存在),可以考虑直接做一个定期的作业,直接将数据复制过来。这个需求涉及到两个功能:

  • 从MySQL复制数据:使用NodeJS的mysql的NPM包
  • 每天都复制一次:使用NodeJS的node-schedule的NPM包

接下来,我们需要新创建一个js文件,名为:mysql_2_redis.js

首先,需要安装两个所依赖的包(本地,非全局),当然redis的包也需要安装

然后,在mysql_2_redis.js文件中键入以下的代码。

执行这个js文件,可以看到输出(只要是到了第40秒了,就开始运行了)。

然后,在命令里面执行(redis-cli,如果是windows下,则是redis-cli.exe),执行以下的命令行,应该可以看到从MySQL数据库中取出来的数据,被缓存到Redis里面去了。

3.3 Redis的缓存策略怎么设(占坑)

到这里为止,如何将数据定期的复制到Redis的部分已经结束了。 Redis在我们的例子中被用来做一个缓存库(MySQL中车辆主数据表的在内存中的分身)。除此之外,我们也会考虑放入一些其他的数据在Redis中:

  • 车辆主数据
    估计在几千万的级别
  • 消息规则
    消息ID,可以对外开放,直接触发告警-如果出现就直接告警,能够发给谁)

理论上Redis可以处理多达2的32次方(约合42亿9496万7296)的keys,根据一些其他的网络上的测试,有的Redis实例(单机)存放了2亿5千万的keys。
目前看下来没问题,只要你内存够大,即使超过单机的内存,Redis依然放得下,最多是SWAP交换分区会大,响应速度会慢。

默认情况下,Redis启动,是不需要对它做任何的配置的,默认的配置可以满足所有的需求。但是还有一些情况下,我们想做一些不一样的东西,例如:

有空再补充!

 

下一篇,将来实践看看到如何将数据放入HBase中。

Leave a Reply

Your email address will not be published.