练手项目1笔记 day20

目标

  • 运用SpringTask实现任务调度
  • 运用MavenProfile实现并发和生产环境切换
  • 了解MongoDB数据库的应用场景
  • 说出其他业务功能的需求和实现思路

1. 任务调度SpringTask

1. 什么是任务调度

在企业级应用中,经常会制定一些“计划任务”,即在某个时间点做某件事情,核心是以时间为关注点,即在一个特定的时间点,系统执行指定的一个操作。常见的任务调度框架有Quartz和SpringTask等。

2. SpringTask入门小demo

创建模块pinyougou-task-service,引入spring相关依赖 dao 和common工程,tomcat7端口为9108

添加web.xml

添加配置文件applicationContext-task.xml,内容如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.2.xsd">

  <context:component-scan base-package="com.pinyougou.task"/>
  <task:annotation-driven/>
</beans>

创建包com.pinyougou.task

编写类

@Component
public class SeckillTask {

  @Scheduled(cron = "* * * * * ?")
  public void refreshSeckillGoods(){
    System.out.println("执行了任务调度:"+new Date());
  }
}

执行后控制台每秒都输出当前时间,cron设置的是表达式,执行时间规则

3. Cron表达式

1. 表达式格式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:

(1)Seconds Minutes Hours DayofMonth Month DayofWeek Year

(2)Seconds Minutes Hours DayofMonth Month DayofWeek

每一个域可出现的字符如下:

Seconds:可出现", - * /"四个字符,有效范围为0-59的整数

Minutes:可出现", - * /"四个字符,有效范围为0-59的整数

Hours:可出现", - * /"四个字符,有效范围为0-23的整数

DayofMonth:可出现", - * / ? L W C"八个字符,有效范围为1-31的整数

Month:可出现", - * /"四个字符,有效范围为1-12的整数或JAN-DEc

DayofWeek:可出现", - * / ? L C #"四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一,依次类推

Year:可出现", - * /"四个字符,有效范围为1970-2099年

每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:

(1)*:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件。

(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。

(3)-:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

(4)/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.

(5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

(6)L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

(7)W: 表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一 到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份

(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五

(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

2. 例子

eg. 每分钟:第3秒到第10秒 再从第15秒 到第20秒 第45秒执行一次 `15-20, 3-10, 45

eg. 公司 每月第10天计算(发放)工资 10W

eg. 每月最后一个工作日结账 LW

eg. 母亲节、父亲节发送短信 4#2 表示某月的第二个星期三

4. 秒杀商品列表的增量更新

每分钟执行查询秒杀商品表,将符合条件的记录并且缓存中不存在的秒杀商品存入缓存

@Scheduled(cron = "0 * * * * ?")
public void refreshSeckillGoods(){
  System.out.println("执行了秒杀商品增量更新 任务调度:"+new Date());

  // 查询缓存中的秒杀商品id集合
  List goodsIdList = new ArrayList(redisTemplate.boundHashOps("seckillGoods").keys());

  TbSeckillGoodsExample example = new TbSeckillGoodsExample();
  TbSeckillGoodsExample.Criteria criteria = example.createCriteria();
  criteria.andStatusEqualTo("1");//审核通过的商品
  criteria.andStockCountGreaterThan(0);//库存数大于0
  criteria.andStartTimeLessThanOrEqualTo(new Date());//开始日期小于等于当前日期
  criteria.andEndTimeGreaterThanOrEqualTo(new Date());//截止日期大于等于当前日期

  if(goodsIdList.size()>0){
    criteria.andIdNotIn(goodsIdList);// 排除缓存中已经存在的商品id集合
  }

  List<TbSeckillGoods> seckillGoodsList = seckillGoodsMapper.selectByExample(example);
  // 将列表数据装入缓存
  for (TbSeckillGoods seckillGoods : seckillGoodsList) {
    redisTemplate.boundHashOps("seckillGoods").put(seckillGoods.getId(),seckillGoods);
    System.out.println("增量更新秒杀商品id:"+seckillGoods.getId());
  }
}

5. 过期秒杀商品的移除

每秒钟在缓存的秒杀商品列表查询过期的商品,发现过期同步到数据库,并在缓存中移除该秒杀商品

@Scheduled(cron = "* * * * * ?")
public void removeSeckillGoods(){
  // 查询出缓存中的数据,扫描每条记录,判断时间,如果当前时间超过截止时间,移除此记录
  List<TbSeckillGoods> seckillGoodsList = redisTemplate.boundHashOps("seckillGoods").values();
  System.out.println("执行了清除秒杀商品的任务");
  for (TbSeckillGoods seckillGoods : seckillGoodsList) {
    if(seckillGoods.getEndTime().getTime()<new Date().getTime()){
      // 同步到数据库
      seckillGoodsMapper.updateByPrimaryKey(seckillGoods);
      // 清除缓存
      redisTemplate.boundHashOps("seckillGoods").delete(seckillGoods.getId());
      System.out.println("秒杀商品"+seckillGoods.getId()+"已过期");
    }
  }
  System.out.println("执行了清除秒杀商品的任务...end...");
}

2. Maven Profile

1. 什么是MavenProfile

在我们平常的java开发中,会经常使用到很多配制文件(xxx.properties,xxx.xml),而当我们在本地开发(dev),测试环境测试(test),线上生产使用(product)时,需要不停的去修改这些配制文件,次数一多,相当麻烦。现在,利用maven的filter和profile功能,我们可实现在编译阶段简单的指定一个参数就能切换配制,提高效率,还不容易出错.。

profile可以让我们定义一系列的配置信息,然后指定其激活条件。这样我们就可以定义多个profile,然后每个profile对应不同的激活条件和配置信息,从而达到不同环境使用不同配置信息的效果。

2. 入门

修改page-web的pom.xml

<properties>
  <port>9105</port>
</properties>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.2</version>
      <configuration>
        <!-- 指定端口 -->
        <port>${port}</port>
        <!-- 请求路径 -->
        <path>/</path>
      </configuration>
    </plugin>
  </plugins>
</build>

运行发现结果一样,其实就是之前学习的maven的变量

如何切换为生产环境的9205呢?

修改pom文件,添加profile定义

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <port>9105</port>
    </properties>
  </profile>
  <profile>
    <id>pro</id>
    <properties>
      <port>9205</port>
    </properties>
  </profile>
</profiles>

在这里插入图片描述

执行后发现运行的端口改变为9205了

3. 切换数据库连接配置

1. 编写不同环境的配置文件
  1. 在pinyougou-dao的src/main/resources下创建filter文件夹
  2. filter文件夹下创建db_dev.properties,用于配置开发环境用到的数据库
env.jdbc.driver=com.mysql.jdbc.Driver
env.jdbc.url=jdbc:mysql://localhost:3306/pinyougoudb?characterEncoding=utf-8
env.jdbc.username=root
env.jdbc.password=root
  1. filter文件夹下创建db_pro.properties
env.jdbc.driver=com.mysql.jdbc.Driver
env.jdbc.url=jdbc:mysql://localhost:3306/pinyougoudb_pro?characterEncoding=utf-8
env.jdbc.username=root
env.jdbc.password=root
  1. 修改properties下的db.properties
jdbc.driver=${env.jdbc.driver}
jdbc.url=${env.jdbc.url}
jdbc.username=${env.jdbc.username}
jdbc.password=${env.jdbc.password}
2. 定义profile

修改pom.xml

<properties>
  <env>dev</env>
</properties>

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <env>dev</env>
    </properties>
  </profile>
  <profile>
    <id>pro</id>
    <properties>
      <env>pro</env>
    </properties>
  </profile>
</profiles>

定义了2个profile,分别是开发环境和生产环境

3. 资源过滤和变量替换

修改pom.xml,在build节点中添加如下配置

<build>
  <filters>
    <filter>src\main\resources\filters\db_${env}.properties</filter>
  </filters>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>

利用filter实现对资源文件(resources)过滤

maven filter可利用指定的xxx.properties中对应的key=value对资源文件中的${key}进行替换,最终把你的资源文件中username=value

4. 打包

在pinyougou-dao工程执行命令:package -P pro 解压生成的jar包,观察db.properties配置文件内容,替换为生产环境的值

在这里插入图片描述

解压后发现db已经变成了pinyougoudb_pro

5. 测试运行

4. 切换注册中心连接配置

1. 集中配置注册中心地址
  1. 在pinyougou-common工程properties下创建dubbox.properties
address=192.168.25.128:2181
  1. spring目录下创建spring配置文件applicationContext-dubbox.xml配置如下
<context:property-placeholder location="classpath*:properties/*.properties" />
<dubbo:registry protocol="zookeeper" address="${address}"/>
  1. 所有服务工程和web工程都要依赖pinyougou-common,并删除每个工程中关于注册中心地址的配置
  2. 测试运行 正常
2. MavenProfile配置
  1. 在pinyougou-common工程创建filters目录,目录下建立dubbox_dev.properties和dubbox_pro.properties
env.address=192.168.25.128:2181
env.address=192.168.25.122:2181
  1. 修改dubbox.properties
address=${env.address}
  1. 修改pinyougou-common的pom.xml
<properties>
  <env>dev</env>
</properties>

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <env>dev</env>
    </properties>
  </profile>

  <profile>
    <id>pro</id>
    <properties>
      <env>pro</env>
    </properties>
  </profile>
</profiles>

...
<build>
  <filters>
    <filter>src/main/resources/filters/dubbox_${env}.properties</filter>
  </filters>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>

3. MongoDB简介

1. 什么是mongodb

MongoDB 是一个跨平台的,面向文档的数据库,是当前 NoSQL 数据库产品中最热门的一种。它介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富,最像关系数据库的产品。它支持的数据结构非常松散,是类似JSON 的 BSON 格式,因此可以存储比较复杂的数据类型

2. 特点

MongoDB 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它是一个面向集合的,模式自由的文档型数据库。

具体特点总结如下:

(1)面向集合存储,易于存储对象类型的数据

(2)模式自由

(3)支持动态查询

(4)支持完全索引,包含内部对象

(5)支持复制和故障恢复

(6)使用高效的二进制数据存储,包括大型对象(如视频等)

(7)自动处理碎片,以支持云计算层次的扩展性

(8)支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++语言的驱动程序,社区中也提供了对 Erlang 及.NET 等平台的驱动程序

(9)文件存储格式为 BSON(一种 JSON 的扩展)

3. 体系结构

MongoDB 的逻辑结构是一种层次结构。主要由:

文档(document)、集合(collection)、数据库(database)这三部分组成的。逻辑结构是面向用户

的,用户使用 MongoDB 开发应用程序使用的就是逻辑结构。

(1)MongoDB 的文档(document),相当于关系数据库中的一行记录。

(2)多个文档组成一个集合(collection),相当于关系数据库的表。

(3)多个集合(collection),逻辑上组织在一起,就是数据库(database)。

(4)一个 MongoDB 实例支持多个数据库(database)。

4. 在品优购中的应用

品优购的评价系统、收藏系统采用等信息存储在mongodb

4. 品优购-其他业务功能分析

1. 用户中心(WEB)

用户在首页登录系统后会进入到用户中心首页

1. 订单中心

功能需求:

(1)实现对订单的查询功能

(2)未付款订单的付款功能

(3)未付款订单的取消功能

(4)已付款提醒订单发货功能

(5)确认收货

(6)退货

(7)用户评价

(8)物流信息跟踪

2. 秒杀订单中心

同上

3. 我的收藏

购物车中有将我的购物车商品移到我的收藏功能,在用户中心中可以查看我收藏的商品

对于这样的用户收藏数据,我们可以使用mongoDB来实现

(1)我的收藏列表

(2)删除收藏

4. 我的足迹

(1)查看足迹列表

(2)删除我的足迹

5. 个人信息设置

(1)个人信息

(2)地址信息

(3)密码重置

(4)绑定手机

2. 商家后台-订单管理(WEB)

1. 订单管理

(1)订单查询

(2)订单发货

(3)订单退货

2. 秒杀订单管理

(1)秒杀中订单查询(查询redis )

(2)已完成秒杀订单查询(查询数据库)

(3)秒杀订单发货

(4)秒杀订单退货查询

3. 运营商后台-订单管理(WEB)

1. 订单管理

根据商家、订单号、用户ID等信息查询订单列表

2. 秒杀订单管理

(1)查询秒杀中订单

(2)查询已付款订单

4. 评价系统

针对评论这样数据量大并且价值不高的数据,我们通常采用MongoDB来实现存储

1. 评价系统-数据访问层

评价数据访问层-操作mongoDB

2. 评价系统-服务层
3. web工程调用评价系统

(1)在商品详细页显示该商品的所有评论信息(CORS跨域)

(2)用户中心web工程引用评价服务 可以对已收货的订单追加评价。

(3)商家后台web工程引用评价服务 可以查看订单的评价

(4)运营商后台web工程引用评价服务 可以查看订单的评价

(5)任务服务pinyougou-task-service引用评价服务和搜索服务,统计每个商品的评价更新到solr索引库中。

5. 商家首页

构建商家首页工程,引用搜索服务,显示该商家的商品列表

6. 资金结算

用户购买商品是直接付款给平台的,而发货的是商家,那商家如何获得货款呢?这就需要运营商定期将货款转账给商家

1. 佣金和佣金比例

说到平台与商家之间的资金结算,我们必须要提一下佣金。佣金就是运营商以销售额为基础抽取的销售提成。 商品类型不同,设定相应的佣金比例也不同。例如食品类佣金比例为0.5% ,那么商家每产生100元的销售额就需要支付给运营商平台相应比例的佣金

2. 结算流程图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.2. 结构化一下 1.3. 图形化一下 1.3.1. 运营商后台 1.3.2. 商家后台 1.3.3. 网页前台 参考京东 2. 技术选型 前端:angularJS + Bootstrap 后台:SSM( springmvc+spring+mybatis) 数据库:mysql,使用mycat读写分离 开发模式:SOA 服务中间件:dubbox,需要和zookeeper配合使用 注册中心:zookeeper 消息中间件:Activemq,使用spring-jms 负载均衡:nginx 搜索:solr集群(solrCloud),配合zookeeper搭建, 使用spring-data-solor 缓存:redis集群,使用spring-data-redis 图片存储:fastDFS集群 网页静态化:freemarker 单点登录:cas 权限管理:SpringSecurity, 跨域:cros 支付:微信扫描 短信验证:阿里大于 密码加密:BCrypt 富文本:KindEditor 事务:声明式事务 任务调度:spring task 所有的技术,都可能涉及到为什么用?怎么用?用的过程中有什么问题? 3. 框架搭建 3.1. 前端 理解baseControler.js、base.js、base_pagination.js,以及每一个xxxController.js里面都公共的做了些什么。 baseControler.js 分页配置 列表刷新 处理checkBox勾选 xxxControler.js 自动生成增删改查 base_pagination.js 带分页 base.js 不带分页 3.2. dao 使用了mybatis逆向工程 4. 模块开发 逐个模块开发就好 4.1. 学会评估模块难不难 一个模块难不难从几方面考虑。 涉及几张表? 1,2张表的操作还是没有什么难度的。 涉及哪些功能? 增删改查,批量删除。 前端展示? 分页列表、树形、面包屑、三级联动、内容格式化。 4.2. 举几个简单模块的例子 4.2.1. 品牌管理 单表 分页、新增、删除、修改 4.2.2. 规格管理 2张表 分页、新增、删除、修改、显示优化(显示列表内容的一部分) 4.2.3. 模板管理 2张表 分页、新增、删除、修改、显示优化(显示列表内容的一部分) 4.2.4. 分类管理 单表 4.2.5. 商家审核 单表 4.3. 举一个复杂模块 4.3.1. 商品新增 需要插入3张表,tb_goods、tb_goods_desc、tb_item 前端:三级联动、富文本、图片上传、动态生成内容 4.3.2. 商品修改 需要从3张表获取数据,然后进行回显。 4.4. 典型模块设计 4.4.1. 管理后台 商品新增、商品修改 4.4.2. 前台页面 搜索模块实现 购物车模块实现 支付模块实现 秒杀模块实现 5. 开发过程中问题&优化 1.1. 登录 单点登录怎么实现 session怎么共享 1.2. 缓存 哪些场景需要用到redis redis存储格式的选择 怎么提高redis缓存利用率 缓存如何同步 1.3. 图片上传 图片怎么存储 图片怎么上传 1.4. 搜索 ​ 怎么实现 数据量大、 并发量高的搜索 怎么分词 1.5. 消息通知 ​ 哪些情况用到activeMq 1.6. 优化 seo怎么优化 怎么加快访问速度 1.7. 秒杀 ​ 怎么处理高并发 ​ 秒杀过程中怎么控制库存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值