今日指数 1.项目介绍 【今日指数】是基于股票实时交易产生的数据分析产品,旨在为特定用户和机构提供定制化的股票数据分析和展示服务;
项目的核心功能以数据分析和展示为主,主要包含了沪深指数、沪深板块、沪深个股和K线的实时行情查询等功能,内容包含了国内实时指数、涨幅榜、个股涨跌、个股秒级行情、实时日K线行情等;
2.项目架构 2.1今日指数技术选型 【1】前端技术
名称
技术
场景
基本骨架
vue-cli+vue+element+axios
报表
echartsjs
前端支持
node webpack 等
【2】后端技术栈
名称
技术
场景
基础框架
springboot、mybatis-springboot、springMVC
项目基础骨架
安全框架
boot-security+jwt
认证与授权
缓存
redis
缓存
excel表格导出
easyexcel
小组件
jode-date 、gson 、guava 、httpClient \
restTemplate 、线程池
定时任务
xxljob
文档框架
swagger
分库分表
sharding-JDBC
部署
nginx
【3】整体概览
2.2 核心业务介绍
【2】业务功能简介
1 2 3 4 5 6 7 8 9 10 11 12 1.定时任务调度服务 XXL-JOB通过RestTemplate+多线程动态拉去股票接口数据,刷入数据库; 2.国内指数服务 3.板块指数服务 4.涨幅榜展示功能 5.涨停跌停数展示功能 6.成交量对比展示功能 7.个股涨停服务展示功能 8.个股详情展示功能 包含分时行情、日k线、周K线图等 9.个股描述服务; 10.报表导出服务
3.SQL相关 3.1SQL基础 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1. 时间函数date_formate(dateTime,'%Y%m%d' )2. 索引失效情况: select * from user where id = 20 (id为primary key) 2.1 索引字段使用函数 导致索引失效,导致全表查询 eg:select * from user where trim (id) = 20 2.2 对索引字段进行数学运算 eg:select * from user where id + 5 = 20 2.3 使用反向查询导致索引失效 (!= 丶 not like ,not in ) eg:select * from user where id != 20 2.4 索引字段频繁的修改 会造成b+ tree频繁的维护(16 kb页的分裂等) 3. 关于分组group by user :id,age,name,sexeg: select sex,* from group by sex;是错误的,一般group by 要结合聚合函数处理数据,此时* 仅显示第一行记录,无意义数据如果仅仅为了获取分组后的字段,eg: select sex from user group by sex;强烈建议改为:select distinct sex from user ; 4. 业务分析通透后再落地SQL 2 :8 定律;
3.2 业务分析 1 2 3 4 5 6 7 1.拿到需求,然后独立分析(第一次确认); 任务交接方会提供一些需求文档:需求说明书,产品原型 2.独立分析后,把潜在的问题(有歧义的问题) 整理集中找相关负责人对接,做开发需求的二次确认 这个过程会加深对需求的理解 甚至在探讨过程中负责人会给你一些建设性的开发建议; 3.拆解业务,SQL分步骤落地; sql先在客户端运行,然后复制到xml中;
3.3业务举例说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 SELECT stock_code, DATE_FORMAT(cur_time,'%Y-%u' ) AS week, MAX (cur_time) AS mxTime, / / 收盘时间点 对应价格 收盘价 MIN (cur_time) AS miTime,/ / 开盘时间点 对应价格 开盘价 MAX (cur_price) AS maxPrice, MIN (cur_price) AS minPrice, AVG (cur_price) AS avgPrice FROM stock_rt_info WHERE stock_code= #{stockCode} and cur_time between #{startTime} and #{endTime} GROUP BY week SELECT tmp.stock_code,tmp.maxPrice,tmp.minPrice,tmp.avgPrice,s1.cur_price AS openPrice,s2.cur_price AS closePrice, DATE_FORMAT(tmp.mxTime,'%Y%m%d' ) AS mxTime FROM ( SELECT stock_code, DATE_FORMAT(cur_time,'%Y-%u' ) AS week, MAX (cur_time) AS mxTime, MIN (cur_time) AS miTime, MAX (cur_price) AS maxPrice, MIN (cur_price) AS minPrice, AVG (cur_price) AS avgPrice FROM stock_rt_info WHERE stock_code= #{stockCode} and cur_time between #{startTime} and #{endTime} GROUP BY week ) AS tmp LEFT JOIN stock_rt_info AS s1 ON s1.stock_code= tmp.stock_code AND s1.cur_time= tmp.miTime LEFT JOIN stock_rt_info AS s2 ON s2.stock_code= tmp.stock_code AND s2.cur_time= tmp.mxTime ORDER BY mxTime ASC
4.数据采集 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1.Spring提供的模拟浏览器请求行为的对象RestTemplate 核心API: getForObject, getForEntity, PostForObject, PostForEntity 2.解析采集的数据的思路 2.1 如果采集的数据时标准的json数据 直接借助jackson\fastjson \gson 等工具解析 甚至RestTemplate本身也可以解析 2.2 如果是复杂的非标准的格式,先以String 接收,然后使用正则匹配 3.采集后的数据保存问题 采集数据IO开销比较大,且操作比较耗时 思路:将采集的数据partion分区分片,然后再逐次保存; 多线程+线程池异步并发采集数据,并操作;
5.多线程和线程池 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 1.构建线程对象的几种方式 1.1继承Thread类 1.2实现Runable接口 1.3实现Callable接口 1.4从线程池获取(底层也实现了Runable接口) 2.线程池专题 2.1线程池核心参数 核心线程数 最大线程数 任务队列 存活时间大小 存活时间单位 拒绝策略 线程工厂treadFactory 2.2线程池拒绝策略有哪些 AbordPolicy:抛出异常 中断执行 DiscardPolicy:丢掉任务,不抛异常 DiscardOldestPolicay:淘汰老的任务 CallerRunsPolicy:将处理不了的任务交给主线程处理,给主线程一个叫正向反馈 如果主线在执行任务时也阻塞了那就不会接受新的任务,这样任务线程就有时间缓慢处理当前的任务 自定义任务拒绝策略 实现RejectedExcuetionHandler接口下的rejectedExceution 2.3 线程池工作机制(工作流程) 0) 线程初始化时,核心线程数是多少? 零,线程是比较重的对象,构建对象时,需要与系统底层交互,所以尽量按需加载(懒加载) 1)当前线程数<核心线程数,此时如果有新的任务进来 会线程复用? 不会 2)当前线程数= 核心线程数,此时并发任务数小于等于核心线程数+任务队列长度,会构建新的线程吗? 不会,超过核心线程的任务会加入到任务队列中,异步处理 3) 并发任务数 > 核心线程数 + 工作队列长度,但是小于等于 最大线程数 + 工作队列长度,会构建新的线程(临时线程),但是这些临时线程一旦空闲,就会回收 4) 并发任务数 > 最大线程数 + 工作队列长度 线程池如何处理? 触发拒绝策略 3.线程池参数设置原则 3.1 先确定线程池使用场景 cpu密集型 如果是cpu密集型,那么会高频占用cpu,这样线程数尽量不要设置过大,因为线程设置过大,会导致多线程竞争cpu资源,这样会导致线程上下文的切换,带来开销相对较大,所以一般建议核心线程数=Cpu核心数+1 IO密集型(常见) 网络IO,磁盘IO等,比较耗时,单数cpu大多数时间都是处于空闲状态的,所以为了更加高效利用cpu 核心线程数=2Cpu核心数+1
6.分库分表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 1.sharding-jdbc框架 1.1 相关名词 物理表(数据库中真实存在的表) 逻辑表(逻辑上操作,是为了sharding-jdbc操作物理表而存在的表) 分片键(物理表中的某个字段) 节点(物理库.物理表) 广播表(公共表特点:数据量少,但是各个数据库都会用到) 动态表(随着某种规则而动态生成的表 比如:股票流水,我们是按照年分库,月分表,但是2023年的月中也有对应的表,表随着时间而动态生成) 1.2 sharding-配置 分库分表数据源 指定分库的片键,分表的片键 默认数据源 不知道片键的操作,默认就会操作指定的默认数据源 公共表配置 广播表:broadcast 2.如何根据业务分析项目组分库分表的策略? 根据数据量和维护性的角度,选择是否水平分库分表 根据冷热数据 垂直分表 eg:以股票相关数据为例,分析: 大盘,板块每年产生的数据不大,但是股票的数据是以时间维度动态生成,考虑维护成本,我们是按照年为单位维护; 但是对于股票流水表,每个月产生数据动辄800w左右,所以需要水平分表,同时按照年分库; 代码落地思路: 都是按照年分库,所以可以将年分库的算法公共提取, 其他的分片算法,可自定义 同时应该选择时间作为数据分片的片键,因为查询大量数据关系到时间
7.权限相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1.SpringSecurity 核心的类: UserDetailService方法:loadUserByName(name) UserDetail:封装了用户的名称,密码,权限集合等信息 UsernamePasswordAuthentionFilter:认证(登录)过滤器 权限定义的2种方式: 方式1:基于配置类配置资源对应的权限 eg:antMatcher("资源路径").hasAuthorty("xxxx") .permitAll() 方式2:基于注解 @PreAuthority("hasAuthority("xxx")") @PostAuthority(xxx) 2.自定义认证过滤器和授权过滤器 定义了2个过滤器: 认证过滤器:模拟UsernamePasswordAuthentionFilter自定义实现 授权过滤器:继承OnecePerRequestFilter,自定过滤器 实现UserDetailService接口方法 重写权限拒绝策略:AccessDenyHandler接口 3.RBAC模型 核心表结构:5张表 用户 用户-角色 角色 角色-权限 权限表 RBAC 两层意思? 基于角色的权限控制,问题:角色改动后,代码要做调整 维护性差 基于资源的权限控制,维护性较好 4.前端展示需要菜单栏 菜单栏是树状结构,逻辑层通过递归查询数据,并组装
8.部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1.nginx 核心命令: nginx 启动 nginx -s stop 停止 (-s作用:优雅关闭nginx,如果当前nginx正在处理请求,则暂时不关闭,如果有新的请求过来,则不处理,直到没有要处理的任务 则关闭) nginx -s reload 重新加载 nginx -t 测试nginx.conf 2.nginx的作用? 2.1 部署静态资源(前后端分离后,前端的静态资源) nginx优势在于静态资源的处理和反向代理,对于动态请求则交给tomcat处理(动静分离) 2.2 反向代理 与正向代理的本质区别: 正向代理是客户端知道要访问什么服务器,通过代理服务器代理执行 反向代理是客户端不知道代理服务器做了什么处理 (一个找黄牛,一个开分店) 2.3 负载均衡 1.轮询 2.权重 3.ip-hash 4.url-hash 5.least-connection 最小连接数 6.fair 相应时间 3.项目部署 理解基于脚本部署的意义和简单使用;