一、库存服务项目搭建
1、配置Maven环境,创建Maven工程
2、pom.xml中导入jar包坐标
4.0.0
com.wingzhe.cache
es_cache
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
1.2.5.RELEASE
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-starter-actuator
org.mybatis
mybatis-spring
1.2.2
org.mybatis
mybatis
3.2.8
org.apache.tomcat
tomcat-jdbc
mysql
mysql-connector-java
com.alibaba
fastjson
1.1.43
redis.clients
jedis
junit
junit
4.7
test
3、编写Springboot启动引导类
package com.wingzhe.cache;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
/**
* 库存服务启动引导类
*/
@EnableAutoConfiguration // 开启自动配置
@SpringBootApplication // 标记为springboot启动引导类
@ComponentScan // 开启组件扫描
@MapperScan("com.wingzhe.cache.mapper") // 配置mapper配置接口扫描路径
public class MainApp {
/**
* 启动引导类
*/
public static void main(String[] args) {
SpringApplication.run(MainApp.class, args);
}
/**
* 创建数据源
*
* @return
*/
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource dataSource() {
return new DataSource();
}
/**
* 创建Mybatis的sessionFactory
*
* @return
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
// 创建对象用来解析资源文件,需要在classpath下创建templates文件夹,其中放置任意ftl结尾文件即可
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mybatis/*.xml"));
return sqlSessionFactoryBean.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
/**
* 配置JedisCluster
*/
@Bean
public JedisCluster jedisClusterFactory() {
Set jedisClusterNodes = new HashSet<>();
jedisClusterNodes.add(new HostAndPort("192.168.20.128", 7001));
jedisClusterNodes.add(new HostAndPort("192.168.20.128", 7002));
jedisClusterNodes.add(new HostAndPort("192.168.20.129", 7003));
JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes);
return jedisCluster;
}
}
4、创建数据库、用户、表
5、编写springboot主配置文件
spring.datasource.url=jdbc:mysql://192.168.20.131:3306/cache
spring.datasource.username=wingzhe
spring.datasource.password=wingzhe
spring.datasource.driverClassName=com.mysql.jdbc.Driver
6、整合mybatis|springmvc|springboot,并编写测试类进行测试数据库以及redisCluster功能
User.java
package com.wingzhe.cache.model;
public class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
UserMapper.xml
UserMapper.java
package com.wingzhe.cache.mapper;
import com.wingzhe.cache.model.User;
public interface UserMapper {
public User findUserInfo();
}
RedisDao.java
package com.wingzhe.cache.dao;
/**
* RedisDao
*
*/
public interface RedisDao {
public void setKey(String key, String value);
public String getKey(String key);
}
RedisDaoImpl.java
package com.wingzhe.cache.dao.impl;
import com.wingzhe.cache.dao.RedisDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import redis.clients.jedis.JedisCluster;
@Repository
public class RedisDaoImpl implements RedisDao {
@Autowired
private JedisCluster jedisCluster;
@Override
public void setKey(String key, String value) {
jedisCluster.set(key, value);
}
@Override
public String getKey(String key) {
return jedisCluster.get(key);
}
}
UserService.java
package com.wingzhe.cache.service;
import com.wingzhe.cache.model.User;
public interface UserService {
public User findUserInfo();
public User findCacheUserInfoFromRedis();
}
UserServiceImpl.java
package com.wingzhe.cache.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.wingzhe.cache.dao.RedisDao;
import com.wingzhe.cache.mapper.UserMapper;
import com.wingzhe.cache.model.User;
import com.wingzhe.cache.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RedisDao redisDao;
public User findUserInfo() {
return userMapper.findUserInfo();
}
@Override
public User findCacheUserInfoFromRedis() {
redisDao.setKey("user_cache", "{name:\"李四\",age:\"23\"}");
String jsonStr = redisDao.getKey("user_cache");
JSONObject object = JSONObject.parseObject(jsonStr);
User user = new User(object.getString("name"), object.getInteger("age"));
return user;
}
}
UserController.java
package com.wingzhe.cache.controller;
import com.wingzhe.cache.model.User;
import com.wingzhe.cache.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/getUserInfo")
@ResponseBody
public User getUserInfo(){
return userService.findUserInfo();
}
@RequestMapping("/getCacheUserInfoFromRedis")
@ResponseBody
public User getCacheUserInfoFromRedis(){
return userService.findCacheUserInfoFromRedis();
}
}
二、在库存服务中实现缓存与数据的双写一致保障方案
1、线程池+内存队列初始化操作
(1)、创建单例请求处理的线程池以及执行工作请求的线程类
RequestProcessorThread.java
package com.wingzhe.cache.thread;
import com.wingzhe.cache.request.Request;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
/**
* 执行请求的工作线程
*/
public class RequestProcessorThread implements Callable {
/**
* 内存队列
*/
private ArrayBlockingQueue queue;
public RequestProcessorThread(ArrayBlockingQueue queue) {
this.queue = queue;
}
@Override
public Boolean call() throws Exception {
while(true){
break;
}
return true;
}
}
RequestProcessorThreadPool.java
package com.wingzhe.cache.thread;
import com.wingzhe.cache.request.Request;
import com.wingzhe.cache.request.RequestQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单例模式的请求处理线程池
*/
public class RequestProcessorThreadPool {
// 初始化长度为10的自定义线程池
private ExecutorService threadPool = Executors.newFixedThreadPool(10);
public RequestProcessorThreadPool() {
RequestQueue requestQueue = RequestQueue.getInstance();
for (int i = 0; i < 10; i++) {
ArrayBlockingQueue queue = new ArrayBlockingQueue<>(100);
requestQueue.addQueue(queue);
threadPool.submit(new RequestProcessorThread(queue));
}
}
/**
* 使用静态内部类的方式实现初始化单例
* 优点:绝对安全
*/
private static class Singleton {
private static RequestProcessorThreadPool instance;
static {
instance = new RequestProcessorThreadPool();
}
public static RequestProcessorThreadPool getInstance() {
return instance;
}
}
/**
* 静态方法能够通过JVM的机制去保证多线程下的并发安全
* 不管多少个线程去并发初始化当前的内存队列,内部类初始化只会发生一次,因此只会创建一个内存队列
* 从而保证所有的线程用到的都是同一个内存队列
*
* @return
*/
public static RequestProcessorThreadPool getInstance() {
return Singleton.getInstance();
}
/**
* 初始化入口方法
*/
public static void init() {
getInstance();
}
}
(2)、编写自定义请求队列以及请求接口类
Request.java
package com.wingzhe.cache.request;
/**
* 自定义封装请求的接口
*/
public interface Request {
}
RequestQueue.java
package com.wingzhe.cache.request;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 使用单例方式自定义请求内存队列
*/
public class RequestQueue {
/**
* 初始化内存队列
*/
private List> queues = new ArrayList>();
/**
* 使用静态内部类的方式实现初始化单例
* 优点:绝对安全
*/
private static class Singleton {
private static RequestQueue instance;
static {
instance = new RequestQueue();
}
public static RequestQueue getInstance() {
return instance;
}
}
/**
* 静态方法能够通过JVM的机制去保证多线程下的并发安全
* 不管多少个线程去并发初始化当前的内存队列,内部类初始化只会发生一次,因此只会创建一个内存队列
* 从而保证所有的线程用到的都是同一个内存队列
*
* @return
*/
public static RequestQueue getInstance() {
return Singleton.getInstance();
}
/**
* 添加一个内存队列
*
* @param queue
*/
public void addQueue(ArrayBlockingQueue queue) {
this.queues.add(queue);
}
}
(3)、编写自定义的初始化监听类,并在启动引导类中加入线程池监听
2、两种请求对象的封装
3、请求异步执行Service封装
4、两种请求Controller接口封装
5、读请求去重优化
6、空数据读请求过滤优化