用插件的方式解决复杂大屏的运算需求

        前边已经聊过组件和基类封装的问题,这里主要的核心问题还是边界和抽象,正好昨天客户要求改一下大屏的数据展示要求,看了看,还好在可控范围内,随手就改了并直接上线。

        也曾经了解过其他公司或者个人做的大屏,大致都是使用webapi作为接口,后端获取数据有直接查数据库的,这种方式的比较多;也有放到redis中或者其他中间件的。在我看来,做大屏无非就是牺牲空间还是牺牲时间的问题,我是选择牺牲空间,主要是内存和硬盘都不值钱。

        整理了一下我自己做的大屏微服务,说简单也简单,主要分以下几个组成部分:

        第一,数据接入层。大屏的数据来源,这个接入层用的是插件模式。例如数据来源可以是MQTT、MQ、DB等,在大屏主程序上定义一个接口,例如IReceiver,这个插件要负责把数据接收到大屏的内置数据处理池中,数据是带有数据的编号和类型的,方便后边处理数据。MQTT和MQ协议都是从外部主动推送过来的,只要开启订阅就可以了;DB需要开发轮询,且要过滤掉已经读取过的数据,这里需要和DB那边协商怎么解决,一般来说建立一个中间变,在记录中标记已读取,如果更改过的记录,就抹掉这个标记,或者直接删除掉读取过的记录,下次就没有对比了。DB操作总的来讲性能是比较低的。

        第二,数据处理层。一般来说,相同类型的的数据处理的方法是一样的,如果出现不一样,那么就另外定义一种类型。要把每次采集到的数据进入这个数据处理层加工,加工的目标就是大屏要显示的格式数据。例如大屏要做一个图表,每月的销售量曲线,每个月就一条记录,处理程序把数据即时运算,不断按数据的日期合并到对应的记录中。这种方法可以分时处理掉大量的明细数据,且是单独的线程中进行,不会影响到主线程响应前端界面的时间。这里设计一个加载接口,IDataProcess,实现类是继承DataProcess基类,每个实现类针对一种数据类型开发,即只在数据池中获取对应数据类型的元素。这个类是和数据接入层通过一个消息订阅的方式实现的,只要有新的数据进入池中,就会通知到对应数据类型的处理类,处理类就去池中获取数据,直到取完为止就停止工作。

        第三,持久化处理。分时运算的结果都是保存在内存的,为的是加快处理数据的速度。持久化处理的方式可以参考redis,不需要每次有数据变更都做一次持久化,可以定义一个时间间隔把内存中的数据保存成文件,这个过程也需要一个独立的线程,且每个文件,只有一个线程在做持久化。另外,当涉及这部分数据的程序重启,持久化处理模块要重新加载文件到内存。我比较懒,直接写成json文件就完事了。

        第四,API响应容器。分时处理好的数据,根据大屏的需求,重新组合一下放到容器中,也可能是数据处理层加工好的数据结构,这个看具体需求了。这里需要注意的是,每个数据ID必须单独建立一个容器,这样API请求的时候,直接把容器内的数据打包丢出去即可,中间无需进行任何加工,所以响应时间是杠杠的。每个容器需要实现IContainer接口,负责保存和获取数据。

        第五,插件加载器。任何程序都免不了要修改重启等,不能为了改一点点需求,把整套大屏程序重启,这也不是我的作风。加载器把上述4个组件打包成一个整体的应用,其实就是以数据处理层为切入点,在内存中停止数据处理层工作并释放所有占用的资源。然后重新加载更新好的程序包,并启动。加载器是一个单独运行的模块,内置处理对象列表,每加载一个,列表增加,也防止重复加载;关停一个,就从列表中移除并释放资源。

        大屏的主程序,只负责两部分功能,一个是提供标准的API接口,例如响应前端的请求

        /api/Screen/GetList?id=00001

        表示获取设备编号为00001的数据,主程序会向加载器找到处理程序,然后处理程序会找到自己加载的容器列表,找到00001的容器,把数据取出。主程序获取到数据后就丢给前端。

        /api/Screen/GetProcessList                                 获取当前正在运行的处理程序

        /api/Screen/Load?name="processABC"              重新加载名称为processABC的处理程序

        这样的程序结构,最小化影响整体数据,也最大程度提高了大屏的响应速度。

        由于牺牲了空间,特别是容器这一块,随着接入的设备增加,内存的消耗还是很厉害的。解决这个问题,可以使用分布式。增加一个负载均衡的入口,配置不同类型的设备请求不同的大屏微服务。如果不同的微服务处理同一个设备的数据,这样就没有必要了,毕竟是为了解决空间问题,让多台服务器来共同完成大屏服务。不过一般企业都不需要这么做,毕竟大屏展示的内容还是非常有限的,不是把系统中所有设备的数据都放到大屏上的。

        注意,大屏设计的核心不是依赖注入,这里是一个接口可以有多个实体类同时存在并加载的,所以不能使用平时的类注入方式,用反射方式加载即可。

       

你可能感兴趣的:(系统架构,c#,数据结构)