目录
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 添加更新锁
cn..tedu
springboot-common-easymall
0.0.1-SNAPSHOT
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
org.mybatis.spring.boot
mybatis-spring-boot-starter
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
/**
* 在当前工程扫描范围之内的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 infoList=new ArrayList();
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;
}
}
@SpringBootApplication
@MapperScan("com.jt.product.mapper")
public class StarterProduct {
public static void main(String[] args) {
SpringApplication.run(StarterProduct.class, args);
}
}
请求地址 |
http://product.jt.com/product/queryByPage?page=1&rows=5 |
请求参数 |
Integer page,Integer rows |
请求方式 |
get提交 |
相应数据 |
Page对象,totalPage总页数,currentPage当前页数,products分页查询结果; {"total":56,"currentPage":2;"products":[{},{},{},{},{}]} |
@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;
}
}
}
请求地址 |
http://product.easymall.com/product/queryById/{productId} |
请求参数 |
路径传参String productId |
请求方式 |
get提交 |
相应数据 |
Product对象的json字符串 |
根据接口文件内容编写方法和返回值
//根据商品id查询某个商品对象
@RequestMapping("product/queryById/{productId}")
public Product queryById(@PathVariable String productId){
return productService.queryById(productId);
}
引入缓存技术redis,生成productKey
查询缓存高并发下,会发生在更新删除缓存之后,由于高并发的查询导致还没有更新数据之前,就由别的用户将缓存个重新添加了旧数据,最终执行更新导致缓存和数据库数据不一致
@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:总数量
|
//查询管理商品页面的分页数据
@RequestMapping("product/manage/queryByPage")
public EasyUIResult queryManageByPage(Integer page,Integer rows){
return productService.queryByPageManage(page, rows);
}
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 pList = productMapper.queryByPage(start, rows);
result.setpList(pList);
return result;
}
请求地址 |
http://product.easymall.com/product/saveProduct |
请求参数 |
Product product(productName=**&productPrice=**&) |
请求方式 |
post |
相应数据 |
如果两个系统对接需要传递详细信息 1返回一个SysResult status代表各种状态数字,data,msg传递复杂的信息 2 返回一个int字符串1成功,0表示失败 |
//新增商品数据到数据库
@RequestMapping("product/saveProduct")
//productName=**&productPrice=**&productCategory=**
public Integer saveProduct(Product product){
try{
productService.saveProduct(product);
return 1;
}catch(Exception e){
e.printStackTrace();
return 0;
}
}
可以在新增数据商品到数据库时,直接添加缓存逻辑,但是由于重要的步骤是新增数据库数据,可以将缓存新增异步执行;
public void saveProduct(Product product) {
//新增调用持久层之前,将数据补充完整
product.setProductId(UUID.randomUUID().toString());
productMapper.saveProduct(product);
/*
rabbit.convertAndSend("dir","saveKeyRedis",product.getProductId())
*/
}
请求地址 |
http://product.easymall.com/product/updateProduct |
请求参数 |
Product product(productName=**&productPrice=**&) |
请求方式 |
post |
相应数据 |
如果两个系统对接需要传递详细信息 1返回一个SysResult status代表各种状态数字,data,msg传递复杂的信息 2 返回一个int字符串1成功,0表示失败 |
@RequestMapping("product/updateProduct")
public Integer updateProduct(Product product){
try{
productService.updateProductById(product);
return 1;
}catch(Exception e){
e.printStackTrace();
return 0;
}
}
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);
}