1、项目简介项目规模15人项目周期2021.5-至今开发环境Intellij IDEA、Maven、JDK1.8、GIT所用框架Spring、SpringBoot、Mybatis-Plus、SpringCloud、Shiro、XXL-Job、其他技术RocketMQ、Redis、Mysql、MongoDB、ElasticSearch、JWT、Nginx、Nacos、Gateway、Nginx、EasyExcel、Sentinel项目描述XX在线教育是垂直的小学、初中、高中知识学习网站课程紧跟前沿理念帮助学生在当今课程体系体量压力较大的环境下保证学生学习成绩提高课程设置科学满足不同基础用户的学习需求快速匹配学习路线使教育资源共享化降低了学习门槛课程主要分为免费课VIP课付费课三种平台主要通过课程抽成VIP会费及广告收费收取利益平台主要分为网站前台运营商后台讲师后台等三方面组成技术描述1、SpringCloud来建立各微服务之间的实时/异步的业务调用2、通过Redis来完成首页轮播图缓存展示分布式幂等校验以及分布式锁的应用课程排行等3、RocketMQ来解耦服务间信息的同步分布式事务确保最终一致性并对晚上起到削峰的作用4、GateWay用来统一路由、对各业务线请求进行鉴权、限流处理提高系统健壮性5、借助MongoDB完成评论反馈留言等非核心业务数据的存储使用EasyExcel导入导出6、XXL-Job以及JavaMail实现定时发送邮件给老师统计本月课程售卖情况以及购买人群相关信息7、通过阿里云第三方技术实现OSS图片上传Alidayu短信发送视频点播以及AliPay支付功能8、借助ElasticSearch完成全文检索以及根据用户喜好定向推送课程(Redis)等9、Nginx进行请求分发负载均衡Sentienl对高压接口进行限流和降级项目职责1、配合产品讨论业务方案并持续跟进参与项目的需求探讨2、负责后台广告模块、智能推荐的日常开发3、对核心业务的攻关并提供相关解决方案对其他需求开发的补位4、修改测试人员提出的BUG编写相关接口文档配合前端人员完成接口联调5、完成上级交代的其他任务根据需求文档完成所分模块进行日常开发工作二、项目介绍我最近参与的这个项目是服务于在职提升人员为主的在线教育平台通过和.个人老师入驻形式向学习者提供在线和点播的网络授课资源。对于上架的课程我们分成了免费与付费两种课程所以平台是通过抽取佣金的方式来进行盈利的。包括像首页我们也是有广告投放功能这也是平台盈利的方式之一。整个平台主要分为网站前台、运营商后台、讲师管理后台三个子系统,我就从整体项目的架构方面来给您介绍吧。三、项目开发流程在我们项目立项以后项目经理、研发部和产品部一起针对项目进行了需求分析会议。会议结束以后产品部门会给我们研发部提供需求文档然后我们研发部再进行开会分析这些需求根据需求进行分组后分组后再进行讨论、创建表、写排期等开发文档我们使用是这个swagger自动生成API文档。然后就是和我们项目老大一块去搭建框架然后提交到远程仓库master上由组员进行拉取分支接下来就是根据需求进行开发。四、项目架构图五、架构介绍1、微服务架构在设计的时候考虑到项目是2C的面向学员个人的随着项目的运营访问量也会不断增加的所以我们这个项目选用的是SpringCloudNacos的微服务架构模式其实当时也有考虑过dubbozookeeper使用DubbozooKeeper的话服务之间的依赖性太强综合考虑还是选用了SpringCloudNacos的微服务架构您也知道这个SpringCloud提供了很多成熟的服务组件能为微服务架构提供一站式的解决方案, 而且调用方式使用的是更加轻量的RestFul API服务与服务之间不存在耦合和依赖只需要遵守规范和约定就能互相调用。并且每个微服务是使用Spring Boot来开发的,开发速度也很快而且呢也很容易和其他技术进行整合。2、入口层首先是入口层我们在这里用Nginx做了负载均衡和反向代理对用户请求进行了转发。它的响应速度、抗并发、限流这些功能性能上都比较高所以咱们业内用的也比较多。为了防止单点故障我还给Nginx做了主备。像静态资源、详情页面也都是用它来做处理的实现了资源的动静分离3、网关层接下来是网关层为了保证服务的安全性用zuul网关设置了微服务的统一请求入口帮我们完成请求路由、校验过滤这些功能对zuul网关也配置了集群保证网关服务的一个高可用嘛。4、服务层再往下是服务层我们是将服务统一注册到Nacos上面的, Nacos作为整个微服务的注册中心同时也是配置中心所有的配置都能传到Nacos上Nacos的概念加上个RefreshScope注解就可以实现配置的实时刷新功能服务之间通过Feign接口调用。因为Feign集成了Ribbon和熔断器(Hystrix)所以请求过来之后会通过Ribbon的负载均衡分配到一台机器并进行调用。也有效防止了服务扇出调用导致的雪崩效应,让系统更加稳定。由于咱们这个是分布式系统许多依赖难免会调用失败比如出现超时、异常这些那熔断器就能帮我们避免因为依赖调用问题而导致的系统级联故障从而提高系统弹性。链路调用出现问题的话定位起来也方便用zipkin就行。5、数据层再往下就到了数据层使用的是MySQL数据库数据库的重要性就不用多说了“非必要请求肯定是不让它不走数据库的”。像一些“热数据”呢是交给Redis处理的对于项目中用户评论这样的数据数据量特别大访问也很频繁但是结构比较松散的数据是用MongoDB数据库存储的搜索这方面使用的是ElasticSearch搜索引擎提高搜索响应速度因为ES相对于其他搜索引擎它还有一个文件系统缓存ES的近实时 或 文件系统缓存的作用所以实时性比较高ES还支持分布式,加入节点自动均衡等特点。当然对他们也都做了集群配置保证高可用嘛。除了借助中间件的方式我们也对MySQL也做了主从同步提高数据的安全性。另外对于一些大表比如订单表就用mycat做了拆分减轻了单表的压力。6、消息队列除此之外呢还使用了一些其他的中间件作为架构的补充比如一些需要异步处理的场景我是借助了RocketMQ的消息队列来处理的对于项目中订单支付成功增加积分的业务为了保证原子性使用的RocketMQ的分布式事务消息来解决的。当然对它也做了集群配置保证它的一个高可用。六、项目模块-课程搜索1、当学员点击分类查询或者是在搜索框中搜索进行展示的时候,需要到数据库进行联查多张表比如:(课程标签表...),然后再到前台进行展示;但是在数据库中进行联查多张表的话我考虑这样几个问题:会增加数据库连接数,给数据库造成压力;联查数据量大的话查询效率会很低;Mysql做like这样的模糊查询,效率和结果都不尽人意;所以我就考虑使用索引库去解决这个问题原因就在于mysql采用的是正排索引,所谓的正排索引就是我们得从头到尾把数据查询一遍,查看哪些数据里面包含我们要查询的关键字,效率非常低,其次就是mysql也没有分词,而且mysql索引存储的是字段的内容如果字段内容过长就只存前几位的内容为索引-----------------------------2、但是倒排索引正好相反,他维护了一个字典表,key就是我们要搜索的关键词,value是包含这些关键词的数据的id,我们只要对比字典表就能知道哪几条数据有我们要查询的关键字,然后拿到这些数据的id,马上就能找到我们要找的数据了。---------------------------------3、当前主流的索引库有solr和elasticSearch考虑到solr虽然搜索方面相对强大一些但是他的实时性并不高问题就在于solr在建立索引时搜索效率会下降相比起来es的实时搜索效率会更高一些。毕竟es的搜索被称为近实时,而且天然支持分布式也是我们当时选择的一个原因。----------------------------------------4、这里说一下ES的近实时Es设计的理念就是分布式搜索引擎底层其实还是基于lucene的。核心思想就是在多台机器上启动多个es进程实例组成了一个es集群。es中存储数据的基本单位是索引一个索引差不多就是相当于是mysql里的一个库。index - type - mapping - document - field。简单的说一下就是一个index包含多个typetype由多个mapping组成type中有多条document每条document有多个field。接下来咱们再说一下而是其他比较好的地方使用ES可以去实现模糊搜索、分词查询、高亮显示、聚合。IK分词器ES默认是自带分词的。但是ES是国外的产品他们只对英文分词效果好而对我们汉字它会将每一个汉字分成一个词语很显然这是不行的。因此我们当时选择了国内比较好用的分词器IK分词器。如果问到其他的分词器可以说庖丁分词器ICU 分词器。thulac分词清华大学分词器查询高亮ES在查询的时候我们可以设置查询关键字的高亮显示指定高亮的样式其实也就是拼接上html的样式标签。索引库的更新索引库的更新我们使用的是logstash进行数据库和ES的数据同步他的工作原理很简单就是定时执行我们配置文件中所定义的sql语句他需要两个插件 一个是读取msql数据的插件一个是同步ES的插件。七、项目模块-广告模块1、广告页由于广告页访问量比较大而且更新较少所以我们用了redis缓存redis 对于1000万条以内的数据性能是比较高的并且支持持久化和冷热交互。另外它还支持集群模式、哨兵监控和主从复制我们当时使用了集群模式。redis和spring整合后有一个模板工具叫redistemplet 通过redistemplete可以存储多种数据类型。2、广告投放广告的收费设计有很多种比如说CPC点击次数、CPA转化效果、CPM千次曝光等收费方式我们采用的是CPM的计费方式。首先我们用Quartz定时器做了一个定时任务就是定时获取广告缓存与数据库进行同步然后对缓存中所有广告的曝光量进行判断如果曝光量为0则无需任何操作如果曝光量不为0则对该用户的账户余额进行相应扣费(10元/1000次)扣费完成后将曝光量重置为0。然后判断账户余额是否小于100如果小于100就将该用户正在投放的所有广告进行数据同步,然后删除广告缓存,并将广告状态修改为待投放同时调用短信接口发送短信提醒用户余额不足。由于首页投放的广告会出现多个用户同时点击而被扣费的情况为了避免数据不一致我们使用了分布式锁目前实现分布式锁的技术有很多, 我们在项目中是使用Redis来解决的。redis分布式锁3、限时秒杀我们每天会在定时发送一定数量的优惠券用于给用户抢券为了严格控制优惠券的发放数量, 我是借助redis的decr来做的, 首先他们是可以保证原子性的, 我们提前将优惠券数量放到redis中, 当用户点击抢券的时候发送请求到服务端, 首先进行抢券开始时间的查询,如果当前时间小于抢券开始时间,直接返回错误,防止用户拿到接口直接调用的问题然后判断优惠券的数量,如过数量小于等于0直接返回失败这样的话后面的大量请求无需给系统带来压力。如果当前数量还有的话,那么会根据当前的用户id “counts”进行redis查询,我们是在抢购成功时通过redis给当前用户做了一个标记,目的是避免一个账户重复抢券的情况然后如果数量充足且无重复秒杀情况,执行decr操作,decr操作的话会执行减一并且返回当前的值,然后再次进行判断当前值是否小于0,如果是则返回失败。否则调用redis通过用户id “counts”做key,值是默认给的一个success代表抢券成功(就是刚刚所说的重复抢券问题),然后mq异步的方式给用户-优惠券表中增加数据。4、购物车在详情页点击“加入购物车”或者“查看购物车”的时候我们做了一个判断假如学生没有登录我们会将学生加入到购物车中的课程信息存入到cookie中然后在学生点击“去结算”时提醒学生进行登陆登录成功后将购物车中的信息和学生对应的购物车中的信息关联起来存入到redis中如果学生已经登录那么学生将课程加入购物车时直接将课程信息存入redis中之所以将信息放redis中的目的就是为了防止学生频繁操作购物车增加系统压力而redis可以看做是一个持久化的缓存。之后学生填写完收获地址信息以及选择支付方式点击“提交订单”时会将redis中的信息取出来插入到数据库的订单表中并清除redis中该学生购物车的信息同时将订单表中该条记录的状态设置为1。 接下来学生可以选择进行支付支付成功后会将订单的状态改为2并将支付宝返回的交易信息存入数据库的交易记录表中同时调用阿里接口发送短信通知学生支付成功此时的订单已进入后台去处理前台再查看订单时我们会根据状态显示不同的信息比如状态1是未支付订单状态为2的是支付成功等待审核状态。八、项目模块-热门推荐1、我们首页有一个课程推荐栏,当学员第一次访问时直接推荐当前最热门,评分最高的课程,之后随着学员浏览的课程我们会直接在课程推荐中推荐学员最近经常访问的课程进行推荐.2、具体的实现是我们定义的标签,在讲师上传课程的时候需要指定添加的标签,我们并没有直接通过课程的分类来进行推荐因为课程分类太广泛,局限性很大而我们使用标签可以很好的扩展课程的维度。3、而且考虑到首页访问量巨大,课程数据很多的情况,所以我当时采用的是redis缓存技术ES搜索引擎实现的.来提高抗并发的能力以及记录课程标签的检索速度.4、该模块分为两种情况,第一种就是用户不登录的情况如果用户不登录的话我们就展示本站所有课程访问量前十的课程进行展示。4.1.首先每当用户点击课程的时候我们都会将该课程的id作为key然后判断redis中是否存在该key如果不存在那就score为1并存入redis的zset数据结构中。如果存在就进行incr操作。4.2.然后我们会从redis中取出排名前十的课程ID在ES索引库中使用terms查询课程数据并返回页面进行展示。这里我们只是将课程ID存入Redis并没有将整个课程数据存入因为内存非常的珍贵我们不可能将那么多数据存入缓存而且拿到课程ID之后是走的ES而没有去给数据库增加压力.5、第二种在学员登录的情况下,当学员每点击一次课程,会先判断redis中是否存在该课程标签.如果没有就直接将学员id加浏览量标识(”-num”)作为key,课程标签作为value存入redis缓存中,并将zset的分数设为1.如果redis中已存在该课程标签,就使用increment方法将分数1.并且同时会将学员id加时间标识(“-time”)作为key,课程标签作为value,当前时间的时间戳作为分数存入缓存.目的是为了记录各个标签的访问时间.6、此外我们使用quartz定时器每隔一段时间就查询这两个zset.将访问时间最近的三个标签删掉,目的是排除学员误触的情况.然后将访问量前三的课程标签以zset类型存入定向推荐缓存中.分数分别设为3,2,1.然后再获取学员最近访问的三个标签,也放入该缓存中,分数统一设为3,如果标签已存在直接使用incr3.(这个缓存用来做定向推荐的,每一小时更新一次).7、当学员再次访问首页时获取用来定向推荐的缓存中的标签然后进行多字段查询elasticsearch拿到课程然后在课程推荐中展示。