1.Main 注入Bean 注册监听器
@EnableAutoConfiguration
@SpringBootApplication
@ComponentScan
@MapperScan("com.jmm.mapper")
public class Application {
@Bean
@ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return new DataSource();
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mybatis/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
/**
* 注册监听器
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public ServletListenerRegistrationBean servletListenerRegistrationBean() {
ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
servletListenerRegistrationBean.setListener(new InitListener());
return servletListenerRegistrationBean;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.InitListener
import com.jmm.thread.RequestProcessorThreadPool;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class InitListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContextListener --- context init");
// 初始化工作线程池和内存队列
RequestProcessorThreadPool.init();
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContextListener --- context Destroyed");
}
}
3.RequestProcessorThreadPool
import com.jmm.service.Request;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 请求处理线程池:单例
*/
public class RequestProcessorThreadPool {
// 在实际项目中,你设置线程池大小是多少,每个线程监控的那个内存队列的大小是多少
// 都可以做到一个外部的配置文件中
// 我们这了就给简化了,直接写死了,好吧
/**
* 线程池
*/
private ExecutorService threadPool = Executors.newFixedThreadPool(10);
public RequestProcessorThreadPool() {
RequestQueue requestQueue = RequestQueue.getInstance();
for(int i = 0; i < 10; i++) {
//数组阻塞队列
ArrayBlockingQueue<Request> queue = new ArrayBlockingQueue<Request>(100);//队列容量 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();
}
}
4.RequestProcessorThread
import com.jmm.service.Request;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
/**
* 执行请求的工作线程
*/
public class RequestProcessorThread implements Callable<Boolean> {
/**
* 自己监控的内存队列
*/
private ArrayBlockingQueue<Request> queue;
public RequestProcessorThread(ArrayBlockingQueue<Request> queue) {
this.queue = queue;
}
@Override
public Boolean call() throws Exception {
try {
while(true) {
// ArrayBlockingQueue
// Blocking就是说明,如果队列满了,或者是空的,那么都会在执行操作的时候,阻塞住
Request request = queue.take();
// 执行这个request操作
request.process();
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
5.RequestQueue
import com.jmm.service.Request;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 请求内存队列
*/
public class RequestQueue {
/**
* 内存队列
*/
private List<ArrayBlockingQueue<Request>> queues = new ArrayList<ArrayBlockingQueue<Request>>();
/**
* 单例有很多种方式去实现:我采取绝对线程安全的一种方式
* 静态内部类的方式,去初始化单例
*/
private static class Singleton {
private static RequestQueue instance;
static {
instance = new RequestQueue();
}
public static RequestQueue getInstance() {
return instance;
}
}
/**
* jvm的机制去保证多线程并发安全
*
* 内部类的初始化,一定只会发生一次,不管多少个线程并发去初始化
*/
public static RequestQueue getInstance() {
return Singleton.getInstance();
}
/**
* 添加一个内存队列
* @param queue
*/
public void addQueue(ArrayBlockingQueue<Request> queue) {
this.queues.add(queue);
}
/**
* 获取内存队列的数量
* @return
*/
public int queueSize() {
return queues.size();
}
/**
* 获取内存队列
* @param index
* @return
*/
public ArrayBlockingQueue<Request> getQueue(int index) {
return queues.get(index);
}
}
6.Request
/**
* 请求接口
*/
public interface Request {
ModelInfo getInfo();
void process();
}
7.UpdateDataRequest
import com.jmm.model.ModelInfo;
import com.jmm.service.Request;
import java.util.concurrent.TimeUnit;
/**
* 更新数据的请求
*/
public class UpdateDataRequest implements Request {
private ModelInfo info;
public UpdateDataRequest(ModelInfo info){
this.info = info;
}
@Override
public ModelInfo getInfo() {
return this.info;
}
@Override
public void process() {
try {
TimeUnit.MILLISECONDS.sleep(3000);//模拟数据处理
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接收到请求["+info.getId()+"]~["+info.getMsg()+"]:更新数据,刷新缓存等操作 按照顺序依次执行 SUCCESS !");
}
}
8.RequestAsyncProcessService
/**
* 请求异步执行的service
*/
public interface RequestAsyncProcessService {
void process(Request request);
}
9. RequestAsyncProcessServiceImpl
import com.jmm.service.RequestAsyncProcessService;
import com.jmm.thread.RequestQueue;
import com.jmm.service.Request;
import org.springframework.stereotype.Service;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 请求异步处理的service实现
*/
@Service("requestAsyncProcessService")
public class RequestAsyncProcessServiceImpl implements RequestAsyncProcessService {
@Override
public void process(Request request) {
try {
// 做请求的路由,根据每个请求的商品id,路由到对应的内存队列中去
ArrayBlockingQueue<Request> queue = getRoutingQueue(request.getInfo().getId());
// 将请求放入对应的队列中,完成路由操作
queue.put(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 获取路由到的内存队列
* @param id 数据ID
* @return 内存队列
*/
private ArrayBlockingQueue<Request> getRoutingQueue(Long id) {
RequestQueue requestQueue = RequestQueue.getInstance();
// 先获取productId的hash值
String key = String.valueOf(id);
int h;
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
// 对hash值取模,将hash值路由到指定的内存队列中,比如内存队列大小8
// 用内存队列的数量对hash值取模之后,结果一定是在0~7之间
// 所以任何一个商品id都会被固定路由到同样的一个内存队列中去的
int index = (requestQueue.queueSize() - 1) & hash;
System.out.println("===========日志===========: 路由内存队列,id=" + id + ", 队列索引=" + index);
return requestQueue.getQueue(index);
}
}
10.ModelInfo
public class ModelInfo {
private Long id;
private String msg;
public ModelInfo(Long id, String msg) {
this.id = id;
this.msg = msg;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
11.Controller 调用
import com.jmm.model.ModelInfo;
import com.jmm.service.RequestAsyncProcessService;
import com.jmm.service.Request;
import com.jmm.service.impl.UpdateDataRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
@Controller
public class TestController {
@Resource
private RequestAsyncProcessService service;
private static int countNumber = 1;
/**
* 更新数据
*/
@RequestMapping("/update")
@ResponseBody
public String updateInfo(Long id) {
int number = countNumber++;
System.out.println("===========日志===========: 接收到更新的请求,数据id=" + id + " count:"+number);
String response = "操作失败";
try {
ModelInfo info = new ModelInfo(id,String.valueOf(number));
Request request = new UpdateDataRequest(info);
service.process(request);
response = "操作成功";
} catch (Exception e) {
e.printStackTrace();
response = "操作失败";
}
return response;
}
}
打印日志分析(内存队列 - 排队顺序执行):
===========日志===========: 接收到更新的请求,数据id=666 count:1
===========日志===========: 路由内存队列,id=666, 队列索引=0
===========日志===========: 接收到更新的请求,数据id=666 count:2
===========日志===========: 路由内存队列,id=666, 队列索引=0
===========日志===========: 接收到更新的请求,数据id=666 count:3
===========日志===========: 路由内存队列,id=666, 队列索引=0
===========日志===========: 接收到更新的请求,数据id=666 count:4
===========日志===========: 路由内存队列,id=666, 队列索引=0
===========日志===========: 接收到更新的请求,数据id=666 count:5
===========日志===========: 路由内存队列,id=666, 队列索引=0
===========日志===========: 接收到更新的请求,数据id=666 count:6
===========日志===========: 路由内存队列,id=666, 队列索引=0
接收到请求[666]~[1]:更新数据,刷新缓存等操作 按照顺序依次执行 SUCCESS !
接收到请求[666]~[2]:更新数据,刷新缓存等操作 按照顺序依次执行 SUCCESS !
接收到请求[666]~[3]:更新数据,刷新缓存等操作 按照顺序依次执行 SUCCESS !
接收到请求[666]~[4]:更新数据,刷新缓存等操作 按照顺序依次执行 SUCCESS !
接收到请求[666]~[5]:更新数据,刷新缓存等操作 按照顺序依次执行 SUCCESS !
接收到请求[666]~[6]:更新数据,刷新缓存等操作 按照顺序依次执行 SUCCESS !
Maven依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>threadPol_memoryQuery</groupId>
<artifactId>SpringBoot_ThreadPol_MemoryQuery</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
</dependencies>
</project>
大概流程:
内存队列中处理业务:
1.Application 注册了监听器:InitListener
2.监听器中初始化了线程迟跟内存队列:RequestProcessorThreadPool.init();
3.RequestProcessorThreadPool线程池中有内存队列(固定的-也可以写入配置文件)
4.RequestProcessorThread 内存队列中 while(true) 时刻检查队列中是否有需要执行的业务
往内存队列中加入任务(异步操作)
1.ProductInventoryController 中构建一个任务(Request)
2.ProductInventoryController 中将上述构建的任务通过ProductInventoryService.process加入到内存队列中
3.RequestAsyncProcessServiceImpl 会将具体的任务 put 到内存队列中,等待队列自行处理