【《基于微信小程序的数据采集可视化平台设计》14000字】_第1页
【《基于微信小程序的数据采集可视化平台设计》14000字】_第2页
【《基于微信小程序的数据采集可视化平台设计》14000字】_第3页
【《基于微信小程序的数据采集可视化平台设计》14000字】_第4页
【《基于微信小程序的数据采集可视化平台设计》14000字】_第5页
已阅读5页,还剩25页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第1章绪论PAGE3第2章数据采集端的基本结构和组件PAGEIIPAGEII基于微信小程序的数据采集可视化平台设计目录TOC\o"1-3"\h\u15638第1章绪论 213671.1研究背景 225491.2国内外研究现状 328601.3论文章节安排 39414第2章数据采集端的基本结构和组件 4248082.1基于JavaSpringboot框架的架构设计 4121982.1.1SpringBoot框架介绍 482412.1.2数据采集端后台架构 6178412.2基于微信小程序框架的架构设计 1519132.2.1微信小程序框架介绍 1592702.2.2微信小程序架构介绍 1531988第3章数据可视化部分的基本结构和组件 2198023.1基于PythonDjangoRestFrameWork框架的架构设计 2136563.1.1Django框架介绍 21282223.1.2数据可视化部分后台架构 22314223.2基于VUE框架的架构设计 25131923.2.1Vue框架介绍 2510203.2.2基于Vue的可视化界面架构介绍 258471第4章高并发问题的处理和思考 32249784.1可能出现的高并发问题 32202704.2问题的思考和处理 3221980第5章总结与展望 34320625.1总结 3458945.2展望 344944参考文献 34摘要随着计算机算力的大幅度提升,人工智能行业正在逐步的走进人们的视线,深度学习,机器学习各种算法概念层出不穷。但是,正如一切事物的发展过程一样,由数据支撑的人工智能行业逐渐显示出了他的瓶颈——数据不足,数据预处理复杂,大量数据获取对个人与中小企业开发者十分具有难度,由于这种局限,由数据支撑的人工智能行业更多的由大型企业和实验室把控,因此需要一个由更多人参与的数据采集平台来方便更多的小微企业与开发者们。数据采集平台是一种统一的的数据采集和处理分发的集成服务提供者,它通过其本身提供数据采集服务和数据处理服务,同时整合互联网环境下的参与用户,向客户提供一套完整的数据采集以及处理的方案。平台数据可视化同时为数据采集平台提供了更多元化的用户群体,通过将运营的数据可视化可以更加方便对平台的采集群体倾向进行分析,是的任务发布方的运营者可以更好地针对相应的用户群体发布更加合适的任务,同时可以使得运营数据更加的具象,方便提取数据重点。论文引入数据采集平台相关构建方法和使用组件,利用Java语言的SpringBoot框架作为后台服务的主要模块框架,同时使用Mysql和Redis作为存储和缓存数据库,加强后台服务模块的健壮性。使用微信小程序作为数据采集的前台模块,是的用户更好上手使用,提高前台模块的易用性。引入可视化平台的相关构建方法和使用组件,利用Python语言的Django框架作为后台服务的主要模块框架,同时使用Mysql作为存储数据库,使用Python的第三方库可以极大提高处理数据的能力。使用Vue框架作为可视化平台的前端展示框架,JavaScript的第三方库对数据可视化做了很好的支持。针对后台服务中可能出现的并发问题,本文结合Java中的线程池和锁以及Redis数据库的缓存机制进行优化,在并发数量支持得到了极大提升。关键词:数据采集可视化平台;微信小程序;Springboot;Mysql;Redis;Dajngo;Vue绪论1.1研究背景所谓的数据就是常说的数值,也就是某种可以通过我们实验,观察或计算得出的结果。数据有很多种类,数字是其中最简单的。文字、图像、声音等也都是数据的一种。科学研究、设计、查证、数学等都有数据的身影。人工智能(ArtificialIntelligence),英文缩写为AI。它是研究、开发理论、方法、技术及应用系统以用于模拟、延伸和扩展人的智能,是一门新的技术科学。它了解智能的实质,企图生产出一种以人类智能相似的方式做出反应的新的能智能机器,包括机器人、语言识别、图像识别、自然语言处理和专家系统等都属于该领域的研究。理论和技术日益成熟的人工智能从自诞生以来,应用领域也不断扩大,未来人工智能带来的科技产品,可以设想,将会是一个“容器”承载人类智慧。人工智能可以模拟人的意识、思维的信息过程。人的智能不是人工智能,但AI能思考的像人那样、甚至人的智能可能不如人工智能。一门涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科的多领域交叉学科-机器学习。它专门探讨怎样使用计算机模拟或实现一些属于人类的行为,通过这样来得到新的知识,对已有的知识结构重新组织令其至深的性能不断得到改善。它是人工智能主要,是最根本的令计算机具有智能的途径。近些年来计算机算力得到了大幅度提升,人们的视线中慢慢出现了人工智能这个行业,各种算法概念:深度学习,机器学习层出不穷。中国AI数据服务行业已经到了需要规范化发展的阶段,《2020年中国AI数据服务行业研究报告》中的数据显示,2020年为止人工智能的总体技术和应用达到了世界水平同步先进,超过1500亿元的核心产业规模,到2025年人工智能核心产业规模可以超过4000亿元,人工智能理论、技术与应用到2030年总体达到世界领先水平,核心产业规模将超过1万亿元。但是,一切事物的发展过程相似,人工智能行业由于大量数据依赖正在逐渐显示出了他的瓶颈——数据预处理复杂,数据不足,大量数据获取对个人与中小企业开发者十分具有难度,由于这种局限,由数据支撑的人工智能行业更多的由大型企业和实验室把控,因此需要一个由更多人参与的数据采集平台来方便更多的小微企业与开发者们。1.2国内外研究现状数据采集服务在全球已经有了许多的服务商以及供应商。国内诸如:云测数据,山西数据交易平台,数聚蜂巢等国外诸如:Caspio,BrightData等可以说由大型企业提供的数据采集业务已经日见成效,但因为这种服务往往是由一到两个大型的数据提供商提供的服务,往往由服务商自己雇佣或者使用公司内部员工采集或者处理数据,这样会导致问题,数据往往会集中在供应商手中,并且采集到的数据具有一定的倾向性,并且因为占用了较多的公司人力成本,所以最后的数据价格往往是小微企业以及个人开发者无法承担的,同时将采集工作更多的集中在服务商手中,失去了发挥互联网大量参与者的特性。1.3论文章节安排全文共5个大章节,各个章节安排如下:第一章,绪论。讨论了课题的研究背景,国内外发展方向以及本课题的研究方向。第二章,数据采集平台端基本结构和组件。介绍了数据采集平台部分的基本代码架构和使用到的开源组件。第三章,数据平台可视化部分的基本结构和组件。介绍了数据平台可视化部分的基本代码结构和使用的开源组件。第四章,对可能得高并发问题的解决和思考。对平台可能遇到的高并发问题的优化设计以及对未来可能出现的更多问题的解决方案的思考。第五章,总结和展望。第2章数据采集端的基本结构和组件2.1基于JavaSpringboot框架的架构设计2.1.1SpringBoot框架介绍Spring:Spring框架是使用java语言开发的的一种开源应用框架,提供主要以反转控制为核心的特性的容器。同时为开发者提供了成套的解决方案,例如通过依赖注入实现控制翻转功能调用反转控制容器来对管理对象生命周期容器化,对事务管理进行面向切面式编程,同时集成多种管理数据访问的持久化技术,提供大量优秀的Web框架给开发者等等[1]。Spring框架具有控制反转(IOC)特性,IOC主要目的在于方便项目开发后维护测试,IOC提供了一种机制通过Java的反射对JavaBean进行统一的配置和管理。Spring框架主要通过容器来管理Bean的生命周期,通过扫描XML文件或类上特定Java注解容器可以来配置对象,通过依赖查找或依赖注入开发者可以来获得对象。Spring框架具有面向切面编程(AOP)框架,SpringAOP框架基于设计模式中的代理模式,主要特点是运行时可进行注入。SpringMVC:WEB项目开发以MVC作为的核心设计,正如三个字母的构成那样,C(控制器)与V(视图、用户客户端)与M(javaBean:封装数据)分开构成了MVC。SpringMVC是SpringFrameWork的后续产品,已经融合在SpringWeb的框架里面。SpringMVC框架提供了全功能MVC模块构建

Web应用程序。图2-1SpringBoot:SpringBoot设计核心是用来简化新的Spring应用的初始化过程以及开发难度[2]。SpringBoot框架中同时有两个非常重要的策略:开箱即用和约定优于配置。开箱即用,Outofbox,是指在开发过程中,提前通过MAVEN将一些开发者经常使用的java依赖提前打入项目中,这样在开始开发时,springboot项目就是一个独立的应用,可以直接使用。这个特点使得开发人员挣脱了繁琐的配置工作以及大量依赖的管理工作,更加专注于开发业务逻辑。约定优于配置,Conventionoverconfiguration,是指的在项目初始化时为需要多原本需要在soring中单独进行配置的参数使用默认值直接配置,这样可以使得开发者对一些已经有了经验的参数不需要去单独的进行配置,直接使用,这样同时也大大减少了各种配置文件在项目中耦合性。2.1.2数据采集端后台架构图2-2 数据采集端后台架构主要如上图2-2所示,从微信小程序端发送过来的Http请求被DispathcherServlet解析,根据其RestFul风格的路由进行匹配,之后进入到相关的Controller层(MVC中的C)中去查询业务逻辑,首先匹配到Redis中读取缓存,若是找不到相关的缓存,则通过Mybatis去Mysql中查询数据,写入Redis,之后再返回给微信小程序段,完成一次Http请求。下面会详细说明每一步所用到的结构设计或组件的功能和意义。 DispathcherServlet:前端控制器,由SpringMVC框架自带,通过解析请求所对应的路径去SpringBoot容器中寻找所有已经注册在容器中的ControllerBean,根据路由信息匹配到相应的Bean,并将解析好的http上下文根据Bean的属性配置解析为相应的参数以供Controller使用。 而所谓的cotroller则是用户自己实现的一个一个spring的bean,绑定注释为@Controller绑定到Spring的容器中,由Spring的容器通过DispathcherServlet对这些bean进行绑定的分发吗,并且传入上下文。通过这样的方式,用户就可以通过springmvc框架实现自己的逻辑,而完全不用管理框架对于过来的请求进行的处理同时亦可以做到将代码逻辑与框架最大的解耦。增加代码内聚。RestFul架构:Fielding将他对互联网软件的架构原则,定名为REST,即RepresentationalStateTransfer的缩写。我对这个词组的翻译是“表现层状态转化”[3]。如果一个架构符合REST原则,就称它为RESTful架构。即:(1)每一个URI代表一种资源;(2)客户端和服务器之间,传递这种资源的某种表现层;(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。通过RestFul的设计模式,我们可以使得Url表达出更多含义更容易让人理解并进行使用和解析。例如如下所示代码根据用户名获取微信的Openid,仅通过Url就可以明白,简单易懂。@ResponseBody

@GetMapping("/users/{username}/openid")

String

getOpenidweb(

@PathVariable("username")

String

username,

@RequestParam("code")

String

code

)

{

return

getOpenid(username,

code);

}Redis:redis是一个key-value基于内存的存储系统。它支持存储的value类型相对其他的key-value1来说有更多的种类,包括string(字符串)、list(链表)、set(集合)、zset(sortedset--有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。与上文Restful相关,通过用户的请求我们可以判断出用户的行为,而对于Get请求(即获取信息)来说,用户行为是幂等的,即在底层数据没有被改写的情况下,不论多少次的数据查询查询到的数据应当是完全一致的,这样才符合Restful的设计特性,而基于这样的一个特性,我们就可以使用redis对用户上一次请求的数据进行缓存,这样请求在dispatch分发路由之后会直接去读取redis中存储的数据,这样又基于redis是存储在内存中的数据库,所以检索速度是传统磁盘数据库的百倍以上,同时因为redis这里使用redis做单线程只读逻辑,也避免了多条线程抢占数据库资源引起的各种死锁问题。具体代码逻辑如下:

//

分页获取

PageHelper.startPage(pageNumb,

pageSize);

List<Job>

list;

//

redisId

String

jobId

=

"jobs_"

+

pageNumb

+

"_"

+

pageSize;

//查询缓存中是否存在

boolean

hasKey

=

redisUtils.exists(jobId);

String

str

=

"";

if

(hasKey)

{

//获取缓存

list

=

redisUtils.get(id);

("从缓存获取的数据"

+

list);

}

else

{

//从数据库中获取信息

("从数据库中获取数据");

list

=

jobService.job_select(pageNumb,

pageSize);

//数据插入缓存(set中的参数含义:key值,user对象,缓存存在时间10(long类型),时间单位)

redisUtils.set(id,

list,

100L,

TimeUnit.MINUTES);

("数据插入缓存"

+

str);

}通过代码我们不难看出主要逻辑分为三个部分,查找是否存在key->存在取出返回,不存在去数据库查找再存入,这样就避免了每次查找都要去mysql中读取数据,使得数据库负载过重的问题。 使用Redis同样也会存在一些问题,在本项目中主要遇到的问题有两个。1、缓存和主存信息不同步。在最开始开发时,我只是单纯的将数据存入,每次取不到数据时刷新,但是这时会遇到一个一个问题就是用户更新数据后,redis中的key还存在,所以直接数据并未被更新。这时就会导致主存不同步的问题,即在页面上展示的不是最新的数据。解决这个问题的方法也很简单,就是在写入数据库时同步的写入redis,这样我们在每次更新时都可以保证redis中的数据是最新的了。这里还要注意两点。1、在写入时我们可以使用spring框架中的AOP开始事务功能保证每一次写入redis和mysql的原子性,在出现问题时,同时回退,保证两者之间的数据统一。2、在极端情况下主要保证主存的可用性,所以每一次写入redis时,我们要删除原本的键,在新增新的键,这样就可以保证即使再删除时出现问题,下一次查询也会直接去mysql中查询最新的数据再写入,而非更新了错误的数据。2、redis存储过多。因为redis是内存数据库,而内存在计算机上一直是属于比较重要的资源,所以大量的写入数据会导致redis被爆掉。解决这个问题,一是在代码中,我只将redis写入到时间十分敏感的数据中做只读逻辑,比如上文代码中的首页逻辑,这些地方用户对于打开时间比较敏感,过长的时间会导致用户流失,而在一些地方例如个人信息,商城等就不使用redis,避免过多的占用内存。同时redis自己拥有一套存储不够的淘汰机制,我这边使用的是给key添加过期时间,在时间过期后自动删除相应的key和value,同时也开启了redis的策略,每过一段时间淘汰最久没有被访问到的key。双管齐下保证不会存在内存溢出的问题。 MyBatis和Mysql:在这一部分我将mybatis和mysql放到一起进行说明。Mysql:mysql是当前市面上最流行的关系型数据库之一,是Oracle旗下的一款产品,最为主要的是mysql提供了社区的开源版本,是的中小型开发者可以免费使用,所以本项目也使用了mysql作为主要数据库。关系型数据库主要通过一张张表结构来存储大量的数据,并对其进行分类。而在本项目中主要用到mysql的两个特性为事务和索引,将在下面详细介绍。MyBatis,是github上的一款开源的由java编写的持久层框架,主要负责将java代码映射为jdbc同时转换为相应的sql语句进行查询,并将查询后的结果再次转换为java对象供代码使用。Mybatis使用xml进行配置,使得其与java的业务逻辑代码解耦,同时防止了sql诸如等sql安全问题。图2-3上图2-3主要展示了mybatis的工作原理,通过图片我们可以清晰的看出,mybatis主要通过项目中配置的mybatis-config.xml来生成设计模式中工厂模式的SqlSessionFactory,生成出这个大工厂后,就监听每一次的代码中的sql调用,生成出sqlsession,同时去查找当前查找所映射的mapper.xml来生成相应的sql语句,从而进行sql查询。具体代码如下。

<!--

任务类型排查找

-->

<select

id="job_img_find"

resultType="com.trt.datacollect.model.Job">

select

*

from

job

where

job_type

=

'img'

and

audit_state

=

'accept'

and

overtime

=

0

and

complete

=

0

</select>如上代码所示,mybatis主要通过id映射到相关的java函数中,而resultType则代表着其映射到的java对象,mybatis将查询后的结果通过java中的反射技术生成相应的javabean进行注入从而生成对象。介绍了mybatis的使用后,我们来看mysql。(2-4)如上图2-4所示,项目的主要表结构,主要分为任务,任务用户关系表,任务提交关系表,任务规则关系表,用户表。其中因为任务和用户和提交记录都是多对多,所以使用了任务提交表,任务用户表,任务接取表做了中间表存储双方的主键和中间状态关系来做多对多映射。 Mysql在本项目中除了使用关系型结构做表关系映射外主要有两个方面的使用,第一个是mysql的索引。Mysql的底层主要是使用B+树来做存储,B+树的主要结构如下图所示:图2-5B+树作为mysql的底层的数据结构的特点主要有:非叶子节点的子树指针与关键字个数相同非叶子结点的子树指针指向的值都小于下一个关键字,都大于等于它本身非叶子节点只作为索引,叶子节点中存储数据所有叶子节点均有个链指针指向下一个叶子节点(支持范围统计,可以从叶子节点横向跨子树)这样的一些特点和可以保证B+树的磁盘读写代价低,查询效率更加稳定,更有利于对数据库的扫描。同样,因为有了B+树作为索引,mysql的Innodb引擎则针对B+树的特色对索引进行了升级。Innodb是用了以主键索引为主的聚集索引,主要如下图2-5所示。使用了聚集索引的mysql将当前字段的所有数据保存在以主键为主的树的叶子节点上,与传统的非聚集索引相比,通过主键检索的速度将会更加的快速,同时可以利用其B+树的底层为链表的数据结构,在主键之间的遍历更加的快速,十分符合本项目,通过任务ID和用户ID来做主要检索需求的特性。图2-6 第二个主要用到的特性为mysql的事务隔离机制,mysql的事务隔离机制主要由于事务的四种隔离级别,分为:图2-7而这四种个隔离级别主要由以下几种并发问题导致和解决:1、更新丢失:mysql所有事务隔离级别在数据库层面上可避免脏读:一个事务读取其他事务未提交的数据将事务隔离级别设置为READ-COMMIT(只能读取已提交数据)级别以上2、不可重复读:事务A多次读取某数据,事务B对数据修改,导致事务A多次读取不一致将事务隔离级别设置为REPEATABLE-READ(可重复读)级别以上InnoDB采用MVCC解决每条数据后面加了隐藏的两列(创建版本号和删除版本号),每个事务在开始的时候都会有一个递增的版本号1、查询时当前事务的版本号需要大于或等于创建版本号2、查询时当前事务的版本号需要小于删除的版本号3、幻读:事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读Recordlock:单个行记录上的锁若走主键外的索引,对当前索引以及主键索引上对应的记录都上锁Gaplock:间隙锁,锁定一个范围,不包括记录本身(RR以上)若where条件全部命中,则不会加Gap。部分命中与全不命中则加锁。会用在非唯一索引,或者不走索引的当前读中(全锁)。根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。间隙锁的目的是为了防止幻读,其主要通过两个方面实现这个目的:(1)防止间隙内有新数据被插入。(2)防止已存在的数据,更新成间隙内的数Next-keylock:record+gap锁定一个范围,包含记录本身通过mysql的innodb的隔离级别,我们可以很轻松的解决由于数据并发问题所导致的数据库错误,这样很好的帮助本项目解决了任务数据库层面的并发提交问题。2.2基于微信小程序框架的架构设计2.2.1微信小程序框架介绍 微信小程序是微信开发的一个用于在微信中嵌入web应用供用户使用的一款开发这框架,微信小程序的开发流程和前端的开发流程十分相似,并且使用微信小程序可以免去用户登录,鉴权,等各类web网站本来就有的问题,是的开发者更加的专注于业务逻辑的表达。同时微信小程序自带天生的微信流量,更加方便我们项目这种需要大量互联网用户参与的使用微信小程序再适合不过了。2.2.2微信小程序架构介绍 微信小程序的开发架构与Vue框架十分类似,但是又有所不同。微信小程序主要分为两个大的架构模块:视图View模块和服务Service模块,他们独立运行在不同的WebViewTag(网页视图)中,View模块主要要负责视图的展示,每个模块都独立对应于一个WebView组件中(网页的一个页面),由开发者编写wxml和wxss代码来展示页面的UI,小程序支持同时展示多个View,View模块和Service的数据交互主要通过微信小程序的服务WeixinJSBridge来实现。与View模块不同的是,Service模块,Service模块的设计与View中的Js设计相似,每个应用只有一个Service进程,主管整个微信小程序的生命周期,每个View发送过来的通讯消息都在Service的整个主进程中进行注册排序,之后依次对齐进行消费,Service模块的消息通信使用的也是WeixinJSBridge模块,但与View模块不同的是,Service的WeixinJSBridge模块可以更多的调用起微信底层能力,发起与后台服务的消息通讯,而View层的WeixinJSBridge只能用于与Service模块的通信,下面我们通过代码展示一次完整的通向java后台的http请求。发布任务界面,如下图(2-6)我们首先通过View界面渲染出来让用户填写的输入框,在最后放置按钮,用户单击后View层会通过WeixinJSBridge向Service层发送一条消息,类型为submit,Service层会通过submit找到这条消息并进行解析图2-8<view>

<image

src="../../img/job10.png"

style="margin-bottom:

2px;margin-right:

5px;vertical-align:

middle;width:30px;

height:

30px;"></image>

<text

class="weui-label"

style='font-size:800'>标题:</text>

</view>

<view

class="weui-cell__hd">

<view

class="weui-cell

weui-cell_input">

<view

class="weui-cell__bd">

<input

placeholder="请输入任务标题"

name="title"

value='{{form_info}}'

/>

</view>

</view></view>...<view

class="button-sp-area">

<button

class="weui-btn"

type="primary"

formType='submit'>发布任务</button></view>后台代码如下,通过formSubmit函数接受View层发送过来的消息,之后将View的数据获取并构成一个js对象,之后调用微信底层模块request方法,发送http请求,请求的对象由app.globalData(微信小程序的全局配置)决定,发送的请求因为有图片文件的存在,所以还用到了wx.upLoadFile进行图片的异步上传到后台,在最后通过解析后台返回的http状态码,如果是200则进入Success函数,通过showPop函数在View界面展示提交成功的弹窗,若是后台返回鹅状态码不是200,则进入Fail函数,跳转到设置好的错误界面引导用户根据提示进行下一步操作。formSubmit:

function

(e)

{

var

that

=

this

var

formData

=

e.detail.value

var

title

=

formData.title

var

content

=

formData.content

var

openid

=

app.globalData.openid

...

wx.request({

url:

app.globalData.URL

+

'/user/interface',

data:

{

openid:

app.globalData.openid

},

method:

'POST',

header:

{

'content-type':

'application/x-www-form-urlencoded'

},

success:

function

(res)

{

app.globalData.account

=

res.data.user_account_numb

if

(numb

*

reward

>=

res.data.user_account_numb)

{

that.setData({

account:

"flase",

form_info:

"",

photos:

"",

})

return

false;

}

else

{

var

account

=

(res.data.user_account_numb

-

numb

*

reward)

wx.request({

url:

app.globalData.URL

+

'/user/update_account',

data:

{

account:

account,

openid:

app.globalData.openid

},

header:

{

'content-type':

'application/x-www-form-urlencoded'

},

method:

'POST',

success:

function

(res)

{

//图片提交

if

(type

==

"img")

{

if

(!that.data.photos[0])

{

that.showPopup();

return

false

}

wx.uploadFile({

url:

app.globalData.URL

+

'/job/example_image',

filePath:

that.data.photos[0],

method:

'POST',

name:

'file',

formData:

{

job_publisher_openid:

openid,

job_title:

title,

job_type:

type,

job_content:

content,

job_join_numb:

numb,

job_reward:

reward,

job_end:

date,

job_tag_type:

tag_type,

job_tag_content:

tag_content,

job_audit_type:

audit_type,

job_audit_content:

audit_content,

email:

email

},

success:

function

(res)

{

that.showPostPopup()

},

fail:

function

(res)

{

wx.navigateTo({

url:

'../../pages/error/error'

})

},

})

}fail:

function

(res)

{

wx.navigateTo({

url:

'../../pages/error/error'

})

},

})

}

},

fail:

function

(res)

{

wx.navigateTo({

url:

'../../pages/error/error'

})

},

})通过上文中的方法,类似的我们很快可以构建出很多的View界面与Service进行消息交互来改变View界面的数据状态进行不同效果的展示,同时利用微信的底层模块与后台进行通讯,完成数据的更新和展示。

第4章高并发问题的处理和思考PAGE3第3章数据可视化部分的基本结构和组件3.1基于PythonDjangoRestFrameWork框架的架构设计3.1.1Django框架介绍 Django,一款使用Python编写的开源框架。与上文中介绍的Spring不同的是,Spring是一款软件开发框架,主要用来作用与软件开发的各个领域和方向。而Django不同,Django是专门面向WEB应用开发的一款MVT框架。MVT与上文中的MVC架构模式类似为Model(模型),View(视图),Template(模板),模型层又可以被称作数据层,主要用来将数据进行处理和转换,将其输入至视图层中,而视图层类似以MVC中的Controller用来处理网络请求的上下文,接受用户的请求参数并按照逻辑对其进行处理,将其利用模型层获取并整理数据,最后将其输出至请求的相应中返回给模板层进行数据渲染,最后由浏览器解析模板层生成的Html页面展示给用户[4]。 Django相对于上文中的Spring相比,更加的专注于WEB应用的开发,首先,因为是WEB应用的开发框架,所以与WEB框架无关的一些配置,在Django中是不会提供原生库依赖进行支持,这样就使得Dajngo的配置参数相对于Spring这种大型软件的开发框架来说更加的少,轻便,是的开发更加的快速,开箱即用。同时Django针对数据库专门开发出了ORM对象关系映射系统,方便更加优雅的进行数据库查询,并对底层进行了很好的优化,更有缓存,国际化,管理页面等每个WEB项目都会遇到的功能的开箱即用的组件,加上Python原生的对数据处理的支持,使用Django来做数据可视化部分的后台应用框架,在开发效率和其复用性上都远远优于spring。 DjangoRestFrameWork是一款基于Django的rest设计模式的框架,在Django框架的基础上,DjangoRestFrameWork原生的提供了对rest设计模式的支持,对于Rest设计模式中的“动作”这个概念做了很好的处理,使得开发的工作量极大地减少,同时因为数据可视部分的前端较为复杂,所以对前端的开发使用了前后端分离的开发模式,这样就可以使得数据更加灵活的进行展示,但是同时也会因为没有使用模板进行前端渲染,所以需要使用JSON数据格式进行数据交互,而使用DjangoRestFrameWork可以原生的支持对JSON的数据传输,减少开发难度。3.1.2数据可视化部分后台架构 数据可视化部分后台架构层面与数据采集部分类似,如下图(3-1)所示。与上文中的后台架构类似,主要由前端发送过来请求,之后由服务器端的逻辑进行处理,相较于上文中数据采集的后台架构,数据可视化的数据架构则更加的简单,去掉了使用redis的缓存,这个是因为数据可视化后台的请求量并不大,并且对于访问速度的要求不是很高,更多的部署数据库访问,而是后台的数据计算,所以使用缓存的意义不大,直接请求即可。同时去掉了Mybatis而是使用了Django支持的更加优雅的ORM系统进行数据库访问。图3-1对于上文中出现的技术架构不再进行赘述,本部分更多的介绍Django中独有的后台架构,首先,DajngoRestFrameWork的架构设计。...router

=

CustomRouter(trailing_slash=True)router.register(r'job_audit',

job_audit.JobAuditViewSet,

basename="job_audit")...class

JobAuditViewSet(ModelViewSet):

queryset

=

Job.objects.all()

serializer_class

=

JobSerializer

filter_backends

=

[DjangoFilterBackend,

filters.SearchFilter,

filters.OrderingFilter]

filterset_fields

=

['audit_state',

'job_type',

'job_id']

search_fields

=

["job_title"]

ordering_fields

=

["job_id"]

permission_classes

=

[IsAuthenticated,

AdminPermission]

authentication_classes

=

[JWTAuthentication]...class

Job(models.Model):

job_id

=

models.AutoField(primary_key=True)

job_title

=

models.CharField(max_length=255)

job_type

=

models.CharField(max_length=255)

job_content

=

models.CharField(max_length=255)

job_example

=

models.CharField(max_length=255)

job_publisher_openid

=

models.CharField(max_length=255)

job_reward

=

models.IntegerField()

job_start

=

models.CharField(max_length=255)

job_end

=

models.CharField(max_length=255)

job_receive_numb

=

models.IntegerField()

job_complete_numb

=

models.IntegerField()

job_join_numb

=

models.IntegerField()

audit_state

=

models.CharField(max_length=255)

overtime

=

models.IntegerField()

complete

=

models.IntegerField()

job_tag_type

=

models.CharField(max_length=255)

job_tag_content

=

models.CharField(max_length=255)

job_audit_type

=

models.CharField(max_length=255)

job_audit_content

=

models.CharField(max_length=255)

email

=

models.CharField(max_length=255)

class

Meta:

managed

=

False

db_table

=

'job'上文代码为最标准的DjangoRestFrameWork代码架构的一个实现,首先通过集成Django自己的路由系统CustomRouter(routers.DefaultRouter)创建出当前项目的自定义路由系统,并将我们需要的路由通过写定的Url关键字绑定在域名之后。之后再将当前的Url绑定到我们需要进行解析上下文的视图类JobAuditViewSet(ModelViewSet)中,当前视图类继承DjangoRestFrameWork自己的视图类ModelViewSet,这个视图类原生的提供RestFul的Action访问,GET(查看)POST(新增)PUT(修改)DELETE(删除),通过进行这四种HTTP请求时,DjangoRestFrameWork将自动的对模型层中的数据进行这四种类型的操作,代码中的QuerySet绑定的便是相关的数据模型,在下面的类定义继承自Django自带的Model类,其中的每个字段根据数据库一一对应,DjanoRestFrameWork将自己取数据库通过ORM进行对应的查询操作,filterset_fields,绑定的则为分界词,通过分界词,在GET查询时可以字段的对当前字段进行条件过滤,search_fields为搜索,配置后可对当前字段进行模糊搜索操作,ordering_fields为排序关键词,使用后查询的数据将根据这里配置的字段进行排序,permission_classes,authentication_classes两个关键字则为使用的权限认证手段和对权限认证的工具,本项目使用的JWT认证。接口的实现效果如下图3-2所示。图3-2可以看出通过简单的配置DjangoRestFrameWork可以实现对大多数简单数据类的增删改查查询,而无需编写过多的代码。 JWT:JSONWebToken是一个继续json对象的安全签名协议。主要用于授权和信息交换的场所,最出名的是各个网站间的单点登录的实现。JWT主要通过非对称加密的公匙和私匙进行信息保护,用户将JWT返回的私匙放到HTTP的请求头Authorization:Bearer,服务器收到后会器使用公式进行解密,获取对应的用户信息,同时根据用户信息进行用户的权限校验,判断用户是否具有使用当前接口的权限,通过这样就组成了DjangoRestFrameWork的权限校验系统。没有权限校验则无法获取数据,如下图(3-3)所示。图3-33.2基于VUE框架的架构设计3.2.1Vue框架介绍 Vue是一款现在的Web应用前端开发框架,Vue将开发聚焦在视图层的层面上,通过自底像上进行渲染。Vue会在核心的main.js文件中,先是通过创建一个总的贯穿上下文的JS-DOM对象渲染出根节点<div>之后将各种需要依赖javaScript库文件导入根节点中供项目使用。之后用户只用将每个视同编写完成绑定到根节点上即可。通过这样的设计,就可以使得每个视图的数据可以直接绑定到HTML上,直接进行渲染和映射。同时Vue提供了极其强大的功能复用,组件。组件相当于一个预输入的Vue视图,可以在Vue的视图中对其进行引用,并且向其中传入数据以及监听其中的函数回调,从而实现对组件的控制和复用。3.2.2基于Vue的可视化界面架构介绍 与上文中微信小程序不同的是,Vue采用的是基于视图的设计模式,所以Vue项目基于每个页面的视图进行编写,同时通过Router组件来对每个路由进行匹配,同时通过Store组件来进行视图间的数据共享。<template>

<div

class="title-container">

<h3

class="title">用户登录</h3>

</div>

...</template><script>import

{

validUsername

}

from

'@/utils/validate'export

default

{

name:

'Login',

data()

{

return

{

loginForm:

{

username:

'',

password:

''

}

...

}

},

watch:

{

$route:

{

handler:

function(route)

{

const

query

=

route.query

if

(query)

{

this.redirect

=

query.redirect

this.otherQuery

=

this.getOtherQuery(query)

}

},

immediate:

true

}

},

...

methods:

{

handleLogin()

{

this.$refs.loginForm.validate(valid

=>

{

if

(valid)

{

this.loading

=

true

this.$store.dispatch('user/login',

this.loginForm)

.then(()

=>

{

this.$router.push({

path:

this.redirect

||

'/',

query:

this.otherQuery

})

this.loading

=

false

})

.catch(()

=>

{

this.loading

=

false

})

}

else

{

console.log('error

submit!!')

return

false

}

})

}}</script><style

lang="scss"

scoped>.login-container

{

min-height:

100%;

width:

100%;

background-color:

$bg;

overflow:

hidden;

...}</style>如上文代码所示,每一个Vue视图可以分为三个部分,1、展示部分:这一部分主要由HTML编写,在这里编写的Html将被渲染到最后的主DOM上,进行页面展示2、样式模块:这一部分主要由CSS以及各种CSS的加强库如SCSS等,这里编写的样式模块主要对上面展示模块的HTML进行样式渲染3、逻辑部分:Vue视图中最主要的模块,主要负责当前视图的逻辑处理,逻辑部分主要分为,数据层由Vue库函数的data定义,在其中定义当前视图需要用到进行渲染或者交互的数据,这里定义的数据将被加入至Vue主进程中,所以在这里定义的数据可以直接被渲染到Vue的DOM上,并且可以通过v-bind函数进行双向渲染。预处理函数,这一部分由Vue提供函数,通过重写Vue的标准函数可以控制部分暴露出的Vue操作主Dom逻辑,例如上文代码中的watch函数,用来监听Router的路由跳转,将跳转过来的路由绑定到需要重点向的路由上,在登录结束后进行重定向跳转。函数层,这一部分由用户的自定义函数组成,这些函数不会被绑定到主进程中,但可以在预处理函数逻辑里引用以及视图层上直接进行绑定,例如上文中的handeLogin函数被watch函数绑定,对页面进行重定向处理,只要通过了登录验证之后,进行重定向跳转。最后渲染出的视图如下图3-4所示图3-4通过如上部分的代码编写,我们可以很快地编写出每个视图界面,编写视图结束之后,我们需要对其进行数据渲染,数据渲染主要通过与后台交互获得,后台交互使用ajax进行Http异步请求,本项目主要使用axios库,对每个类型的http请求进行封装,同时在逻辑层进行调用,并对请求之前进行token绑定,请求之后进行状态码解析处理,都确认无误后将其返回给逻辑层用来监听的回调函数进行相应对视图层进行数据渲染。如下代码所示:Object.defineProperty(http,

'request',

{

get

()

{

return

getRequest('request')

}})/**

*

获取

http

不同请求方式对应的函数

*

*

@param

{string}

http

method

axios

实例中的

method

保持一致

*

*

@return

{Function}

实际调用的请求函数

*/function

getRequest

(method)

{

return

(url,

data,

config)

=>

getPromise(method,

url,

data,

config)}/**

*

实际发起

http

请求的函数,根据配置调用缓存的

promise

或者发起新的请求

*

*

@param

{method}

http

method

axios

实例中的

method

保持一致

*

@param

{string}

请求地址

*

@param

{Object}

需要传递的数据,

post/put/patch

三种请求方式可用

*

@param

{Object}

用户配置,包含

axios

的配置与本系统自定义配置

*

*

@return

{Promise}

本次http请求的Promise

*/async

function

getPromise

(method,

url,

data,

userConfig

=

{})

{

const

config

=

initConfig(method,

url,

userConfig)

let

promise

if

(config.cancelPrevious)

{

await

http.cancel(config.requestId)

}

if

(config.clearCache)

{

http.cache.delete(config.requestId)

}

else

{

promise

=

http.cache.get(config.requestId)

}

if

(config.fromCache

&&

promise)

{

return

promise

}

promise

=

new

Promise(async

(resolve,

reject)

=>

{

const

axiosRequest

=

http.$request.request(url,

data)

try

{

const

response

=

await

axiosRequest

Object.assign(config,

response.config

||

{})

handleResponse({

config,

response,

resolve,

reject

})

}

catch

(error)

{

Object.assign(config,

error.config)

//

reject(error)

//

避免

cancel

request

时出现

error

message

handleReject(error,

config)

}

}).catch(error

=>

{

return

handleReject(error,

config)

}).finally(()

=>

{

//

console.log('finally',

config)

})

//

添加请求队列

http.queue.set(config)

//

添加请求缓存

http.cache.set(config.requestId,

promise)

return

promise}/**

*

处理

http

请求成功结果

*

*

@param

{Object}

请求配置

*

@param

{Object}

cgi

原始返回数据

*

@param

{Function}

promise

完成函数

*

@param

{Function}

promise

拒绝函数

*/function

handleResponse

({

config,

response,

resolve,

reject

})

{

const

code

=

response.code

if

(code

!==

0

&&

code

!==

'1500200'

&&

config.globalError)

{

reject({

message:

response.message,

code:

code,

data:

response.data

||

{}

})

}

else

{

resolve(config.originalResponse

?

response

:

response.data,

config)

}

http.queue.delete(config.requestId)}/**

*

处理

http

请求失败结果

*

*

@param

{Object}

Error

对象

*

@param

{config}

请求配置

*

*

@return

{Promise}

promise

对象

*/function

handleReject

(error,

config)

{

if

(axios.isCancel(error))

{

return

Promise.reject(error)

}

http.queue.delete(config.requestId)

//

捕获

http

status

错误

if

(config.globalError

&&

error.response)

{

//

status

httpStatus

const

{

status,

data

}

=

error.response

const

nextError

=

{

message:

error.message,

response:

error.response

}

//

弹出登录框不需要出

bkMessage

提示

if

(status

===

401)

{

bus.$emit('show-login-modal')

return

Promise.reject(nextError)

}

else

if

(status

===

500)

{

nextError.message

=

'system

error'

}

else

if

(data

&&

data.message)

{

nextError.message

=

data.message

}

messageError(nextError.message)

console.error(nextError.message)

return

Promise.reject(nextError)

}

const

message

=

error.message

||

'system

error'

messageError(message)

console.error(message)

return

Promise.reject(error)}上文中展示了,在请求前检查请求中是否包含token,没有则加入token,在请求结束后通过状态码判断,若是没有权限则跳转登录界面。之后封装后相关的get调用函数,配置后台的URL地址后就可以在逻辑层中调用并进行使用对视图层进行数据渲染。渲染后界面如下图所示:图3-5

第4章高并发问题的处理和思考4.1可能出现的高并发问题 高并发,一个所有的互联网项目都会遇到并且致力于解决的问题。所谓高并发就是在同一时间之内有大量的请求来访问同一个服务或者同一个接口,导致程序无法处理出现阻塞现象,体现在WEB上面就是网页的响应十分缓慢或者干脆挂掉不显示,给用户带来十分不好的使用体验。在本项目中高并发问题主要就会出现在访问数据采集的任务界面,提交任务数据时,以及数据可视化端的数据查询接口处。对于任务界面和查询接口导致的问题一般是访问的次数超过了服务可以处理的上限,无法分配多余的线程去监听过来的请求,造成页面无法访问,无法刷新任务数据,而对于提交任务数据问题则严重得多,可能导致数据错误或者重复提交造成数据错误的情况。4.2问题的思考和处理 针对上面的情况,本项目可以针对下面的五种方案做优化。1、扩容 扩容的方案十分容易理解,就是增加服务器的配置,带宽,数量。服务器很多时可以搭建服务集群,以集群为单位做服务请求的调配。扩容的方案是最快速的提高服务请可承载请求的方案,缺点是需要很多的成本。 2、减少并发量 减少并发量指的是通过一些公共组件来减少每次访问到某个接口的并发量,例如使用ngnix做方向代

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论