Easymall项目分布式拆分整合(五)

                Easymall项目分布式拆分整合(五)

目录

                Easymall项目分布式拆分整合(五)

一.搭建后台商品系统

1.1搭建步骤

1.maven quickstart(springboot)

2.继承parent,依赖common

3.product工程还需要访问持久层,redis(common依赖的传递)

4.application.properties

5.添加一个redis底层连接池的配置类

1.代码模板

2.具体位置

6.nginx配置文件和hosts映射

1.nginx配置文件

2.hosts配置文件

7.商品后台系统的启动类StarterProduct

二.根据接口文件实现后台查询分页逻辑

1.接口文件

2.准备好所有内容(红色内容从以前项目粘贴过来)

1.ProductController(之前的Colltroller被前端使用了,需要重写)

2.ProductService

3.ProductMapper

4.ProductMapper.xml

5.运行代码

二.首页单个商品根据id查询

   1.接口文件

    2.ProductController

    3.ProductService

1.缓存逻辑的使用

2.判断Key是否早redis存在,

3.redis缓存数据需要和数据库保持一致;

三.商品数据的查询后台分页

1.ProductController

2.ProductService

3.分页数据的缓存考虑问题;

四.商品数据的新增

1.ProductController

2.ProductService

五.商品数据的修改(更新锁)

1.ProductController

2.ProductService 添加更新锁


一.搭建后台商品系统

1.1搭建步骤

1.maven quickstart(springboot)

2.继承parent,依赖common

3.product工程还需要访问持久层,redis(common依赖的传递)

 <dependencies>
  	<dependency>
  		<groupId>cn..tedu</groupId>
  		<artifactId>springboot-common-easymall</artifactId>
  		<version>0.0.1-SNAPSHOT</version>
  	</dependency>
  	<dependency>
  		<groupId>org.springframework.boot</groupId>
  		<artifactId>spring-boot-starter-jdbc</artifactId>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  	</dependency>
  	<dependency>
  		<groupId>org.mybatis.spring.boot</groupId>
       	<artifactId>mybatis-spring-boot-starter</artifactId>	
  	</dependency>
  </dependencies>

4.application.properties

server.port=8092

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///easydb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.password=root
spring.datasource.username=root

mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.typeAliasesPackage=com.jt.common.pojo

redis.nodes=10.9.151.60:6379
redis.maxTotal=200
redis.maxIdle=8
redis.minIdle=3

5.添加一个redis底层连接池的配置类

1.代码模板

/**
 * 在当前工程扫描范围之内的configuration配置类
 * 读取前缀是redis的所有key值,根据私有属性名称的getter/stter
 * 赋值给属性
 * @author TEDU
 *
 */
@Configuration
@ConfigurationProperties(prefix="redis")
public class RedisCumConfiguration {
	//根据前缀读取数据,私有属性名称,必须和
	//properties中的值相同
	private String nodes;
	private Integer maxTotal;
	private Integer maxIdle;
	private Integer minIdle;
	
	//编写初始化JedisShardPool对象的方法,@Bean将返回对象
	//作为框架管理的bean
	@Bean 
	public ShardedJedisPool initJedisPool(){
		//利用本类中读取的属性,创建连接池对象
		//先做一个config对象
		GenericObjectPoolConfig config=new GenericObjectPoolConfig();
		config.setMaxIdle(maxIdle);
		config.setMaxTotal(maxTotal);
		//config.setMinIdle(minIdle);
		//解析nodes,生成一个list对象
		//准备一个空内容
		List<JedisShardInfo> infoList=new ArrayList<JedisShardInfo>();
		String[] node = nodes.split(",");//{"10.9.9.9:6379","10l.9.9.9:6380",""}
		for (String hostAndPort : node) {
			String host=hostAndPort.split(":")[0];
			int port=Integer.parseInt(hostAndPort.split(":")[1]);
			infoList.add(new JedisShardInfo(host, port));
		}
		//list,config,构造连接池对象返回
		return new ShardedJedisPool(config,infoList);
	}
	
	public String getNodes() {
		return nodes;
	}
	public void setNodes(String nodes) {
		this.nodes = nodes;
	}
	public Integer getMaxTotal() {
		return maxTotal;
	}
	public void setMaxTotal(Integer maxTotal) {
		this.maxTotal = maxTotal;
	}
	public Integer getMaxIdle() {
		return maxIdle;
	}
	public void setMaxIdle(Integer maxIdle) {
		this.maxIdle = maxIdle;
	}
	public Integer getMinIdle() {
		return minIdle;
	}
	public void setMinIdle(Integer minIdle) {
		this.minIdle = minIdle;
	}

}

2.具体位置

6.nginx配置文件和hosts映射

1.nginx配置文件

2.hosts配置文件

7.商品后台系统的启动类StarterProduct

@SpringBootApplication
@MapperScan("com.jt.product.mapper")
public class StarterProduct {
	public static void main(String[] args) {
		SpringApplication.run(StarterProduct.class, args);
	}
}

二.根据接口文件实现后台查询分页逻辑

1.接口文件

请求地址

http://product.jt.com/product/queryByPage?page=1&rows=5

请求参数

Integer page,Integer rows

请求方式

get提交

相应数据

Page对象,totalPage总页数,currentPage当前页数,products分页查询结果;

{"total":56,"currentPage":2;"products":[{},{},{},{},{}]}

2.准备好所有内容(红色内容从以前项目粘贴过来)

1.ProductController(之前的Colltroller被前端使用了,需要重写)

@RestController
public class ProductController {
	@Autowired
	private ProductService productService;
	@RequestMapping("product/queryByPage")
	public Page queryByPage(Integer page,Integer rows){
		return productService.queryByPage(page, rows);
	}
	//根据商品id查询某个商品对象
	@RequestMapping("product/queryById/{productId}")
	public Product queryById(@PathVariable String productId){
		return productService.queryById(productId);
	}
	//查询管理商品页面的分页数据
	@RequestMapping("product/manage/queryByPage")
	public EasyUIResult queryManageByPage(Integer page,Integer rows){
		return productService.queryByPageManage(page, rows);
	}
	//新增商品数据到数据库
	@RequestMapping("product/saveProduct")
	//productName=**&productPrice=**&productCategory=**
	public Integer saveProduct(Product product){
		try{
			productService.saveProduct(product);
			return 1;
		}catch(Exception e){
			e.printStackTrace();
			return 0;
		}
	}
	@RequestMapping("product/updateProduct")
	public Integer updateProduct(Product product){
		try{
			productService.updateProductById(product);
			return 1;
		}catch(Exception e){
			e.printStackTrace();
			return 0;
		}
	}		
}

2.ProductService

  • 修改包com.jt.product.service;
  • 重新导入Page的包com.jt.common.vo
  • 重新导入ProductMapper的包

3.ProductMapper

  • 修改包路径com.jt.product.mapper
  • 重新导入所有的新包Product com.jt.common.product

4.ProductMapper.xml

  • namespace="com.jt.product.mapper.ProductMapper"

5.运行代码

二.首页单个商品根据id查询

   1.接口文件

请求地址

http://product.easymall.com/product/queryById/{productId}

请求参数

路径传参String productId

请求方式

get提交

相应数据

Product对象的json字符串

    2.ProductController

根据接口文件内容编写方法和返回值​​​​​​​

	//根据商品id查询某个商品对象
	@RequestMapping("product/queryById/{productId}")
	public Product queryById(@PathVariable String productId){
		return productService.queryById(productId);
	}

    3.ProductService

1.缓存逻辑的使用

引入缓存技术redis,生成productKey

2.判断Key是否早redis存在,

  • 存在:直接返回redis数据解析的对象
  • 不存:到数据库查询,存放到redis中,供后续使用返回对象

3.redis缓存数据需要和数据库保持一致;

查询缓存高并发下,会发生在更新删除缓存之后,由于高并发的查询导致还没有更新数据之前,就由别的用户将缓存个重新添加了旧数据,最终执行更新导致缓存和数据库数据不一致

 

  • 基本思路:在系统对数据库做改操作时,一并将缓存数据修改,覆盖删除
  • 可以在更新数据时,删除缓存之前添加一个更新的锁product_productId.update.lock删除缓存完毕,更新数据库完毕,lock释放(超时时间)
  • 查询的逻辑:
    • 判断是否锁:有锁,不使用缓存,查数据库
    • 没有锁:说明没有更新操作,执行缓存逻辑
	@Autowired
	private RedisCumUtils jedis;
	public Product queryById(String productId) {
		//定义更新锁的key值
		String productLockKey="product_"+productId+".lock";
		//定义缓存的key
		String productKey="product_"+productId;
		//先判断锁的存在
		try{
			if(jedis.isExist(productLockKey)){
				//说明有人正在更新,不能操作缓存
				return productMapper.queryById(productId);
			}else{//正常执行缓存逻辑
				if(jedis.isExist(productKey)){//缓存有数据
					String productJson=jedis.query(productKey);
					return MapperUtils.MP.readValue(productJson,
							Product.class);
				}else{
					//查询数据库,并且再缓存中添加数据
					Product product=productMapper.queryById(productId);
					String json=MapperUtils.MP.writeValueAsString(product);
					jedis.addOrUpdate(productKey, json);
					return product;
				}
			}	
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}	
	}

三.商品数据的查询后台分页

请求地址

http://product.easymall.com/product/manage/queryByPage?page=1&rows=5

请求参数

Integer page,Integer rows

请求方式

get提交

相应数据

EasyUIResult

rows:分查询结果

total:总数量

 

1.ProductController

//查询管理商品页面的分页数据
	@RequestMapping("product/manage/queryByPage")
	public EasyUIResult queryManageByPage(Integer page,Integer rows){
		return productService.queryByPageManage(page, rows);
	}

2.ProductService

	public EasyUIResult queryByPageManage(Integer page, Integer rows) {
		EasyUIResult result=new EasyUIResult();
		//总条数,count查询
		int total=productMapper.queryTotal();
		result.setTotal(total);
		//查询的分页结果,计算start
		int start=(page-1)*rows;
		List<Product> pList = productMapper.queryByPage(start, rows);
		result.setpList(pList);
		return result;
	}

3.分页数据的缓存考虑问题;

  • 也可以做缓存,缓存逻辑和商品id查询逻辑一样
  • key设计根据page和rows的值定义分页的缓存数据
  • 分页数据太庞大了,直接使用String类型的数据无法高效实现
  • 一旦出现了新增商品,删除商品,整体的所有分页数据都错误

四.商品数据的新增

请求地址

http://product.easymall.com/product/saveProduct

请求参数

Product product(productName=**&productPrice=**&)

请求方式

post

相应数据

如果两个系统对接需要传递详细信息

1返回一个SysResult status代表各种状态数字,data,msg传递复杂的信息

2 返回一个int字符串1成功,0表示失败

1.ProductController

//新增商品数据到数据库
	@RequestMapping("product/saveProduct")
	//productName=**&productPrice=**&productCategory=**
	public Integer saveProduct(Product product){
		try{
			productService.saveProduct(product);
			return 1;
		}catch(Exception e){
			e.printStackTrace();
			return 0;
		}
	}

2.ProductService

可以在新增数据商品到数据库时,直接添加缓存逻辑,但是由于重要的步骤是新增数据库数据,可以将缓存新增异步执行;

	public void saveProduct(Product product) {
		//新增调用持久层之前,将数据补充完整
		product.setProductId(UUID.randomUUID().toString());
		productMapper.saveProduct(product);
		/*
		rabbit.convertAndSend("dir","saveKeyRedis",product.getProductId())
		*/
	}
  • rabbitmq,将本来应该同步执行的redis缓存添加逻辑
  • 封装成消息形式,异步获取消息执行缓存添加

五.商品数据的修改(更新锁)

请求地址

http://product.easymall.com/product/updateProduct

请求参数

Product product(productName=**&productPrice=**&)

请求方式

post

相应数据

如果两个系统对接需要传递详细信息

1返回一个SysResult status代表各种状态数字,data,msg传递复杂的信息

2 返回一个int字符串1成功,0表示失败

1.ProductController

@RequestMapping("product/updateProduct")
	public Integer updateProduct(Product product){
		try{
			productService.updateProductById(product);
			return 1;
		}catch(Exception e){
			e.printStackTrace();
			return 0;
		}
	}	

2.ProductService 添加更新锁

public void updateProductById(Product product) {
		//更新锁添加到redis
		String productLockKey="product_"+product.getProductId()+".lock";
		//定义缓存的key
		String productKey="product_"+product.getProductId();
		//添加锁//可以定义锁的超时
		jedis.addOrUpdate(productLockKey, "");
		//删除已有的缓存
		jedis.delete(productKey);
		productMapper.updateProduct(product);
		//释放锁
		jedis.delete(productLockKey);
	}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值