目录
---------------------------------------------------------------------------------------------
一、线程池配置
直接上代码,我直接用的ruoyi系统里的线程池配置,个别依赖请自行查找导入
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;
/**
* 线程相关工具类.
*
*
*/
public class Threads
{
private static final Logger logger = LoggerFactory.getLogger(Threads.class);
/**
* sleep等待,单位为毫秒
*/
public static void sleep(long milliseconds)
{
try
{
Thread.sleep(milliseconds);
}
catch (InterruptedException e)
{
return;
}
}
/**
* 停止线程池
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
* 如果仍然超時,則強制退出.
* 另对在shutdown时线程本身被调用中断做了处理.
*/
public static void shutdownAndAwaitTermination(ExecutorService pool)
{
if (pool != null && !pool.isShutdown())
{
pool.shutdown();
try
{
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
{
pool.shutdownNow();
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
{
logger.info("Pool did not terminate");
}
}
}
catch (InterruptedException ie)
{
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
/**
* 打印线程异常信息
*/
public static void printException(Runnable r, Throwable t)
{
if (t == null && r instanceof Future<?>)
{
try
{
Future<?> future = (Future<?>) r;
if (future.isDone())
{
future.get();
}
}
catch (CancellationException ce)
{
t = ce;
}
catch (ExecutionException ee)
{
t = ee.getCause();
}
catch (InterruptedException ie)
{
Thread.currentThread().interrupt();
}
}
if (t != null)
{
logger.error(t.getMessage(), t);
}
}
}
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置
*
*
**/
@Configuration
public class ThreadPoolConfig
{
// 核心线程池大小
private int corePoolSize = 50;
// 最大可创建的线程数
private int maxPoolSize = 200;
// 队列最大长度
private int queueCapacity = 1000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 300;
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
/**
* 执行周期性或定时任务
*/
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
{
@Override
protected void afterExecute(Runnable r, Throwable t)
{
super.afterExecute(r, t);
Threads.printException(r, t);
}
};
}
}
二、代码主体
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.*;
@Component
public class SocketServer implements CommandLineRunner {
private Integer port = 监听的端口;
@Autowired
private WebSocket webSocket;
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
//程序启动自动执行
@Override
public void run(String... args) throws Exception {
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
while (true) {//持续监听端口
Socket accept = ss.accept();
//socket连接交由线程池管理
threadPoolTaskExecutor.execute(new ClientHandler(accept));
}
} catch (IOException e0) {
e0.printStackTrace();
} finally {
if (ss != null) {
try {
ss.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// 客户端处理类,实现Runnable接口
private class ClientHandler implements Runnable {
private final Socket accept;
ClientHandler(Socket accept) {
this.accept = accept;
}
//分配线程后执行的方法
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = accept.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
BufferedInputStream bis = new BufferedInputStream(inputStream);
//处理bis流部分
根据需求处理流
//发送websocket(注意发送代码的位置,如果一次连接发送数据持续时间较久可能需要放到处理流的过程中)
webSocket.sendAllMessage(发送内容);
System.out.println("读取结束");
//关闭
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception exception) {
exception.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (Exception exception) {
exception.printStackTrace();
}
}
if (accept != null) {
try {
accept.close();
} catch (Exception exception) {
exception.printStackTrace();
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
三、WebSocket配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 注入ServerEndpointExporter,
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@ServerEndpoint("/websocket/{clientId}")
//此注解相当于设置访问URL
public class WebSocket {
private static final Logger log = LoggerFactory.getLogger(WebSocket.class);
private Session session;
private String clientId;
private static Map<String,WebSocket> webSockets = new ConcurrentHashMap<>();
private static int onlineCount = 0;
@OnOpen
public void onOpen(Session session, @PathParam(value = "clientId") String clientId) {
this.session = session;
this.clientId = clientId;
webSockets.put(clientId,this);
onlineCount++;
log.info(clientId+"【websocket消息】有新的连接,总数为:"+onlineCount);
}
@OnClose
public void onClose() {
webSockets.remove(clientId);
onlineCount--;
log.info("【websocket消息】连接断开,总数为:"+onlineCount);
}
@OnMessage
public void onMessage(String message,Session session) throws IOException {
if (session != null) {
this.session.getAsyncRemote().sendText(message);
log.info("【websocket消息】客户端发来消息:"+message);
}
}
@OnError
public void onError(Session session, Throwable throwable) {
log.error("WebSocket发生错误:" + throwable.getMessage());
}
// 此为广播消息,synchronized 防止多线程同时使用同一个session进行写操作
public synchronized void sendAllMessage(String message) {
for (WebSocket item : webSockets.values()) {
if (item.session.isOpen()) {
try {
item.session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
log.info("【websocket消息】广播消息:" + message);
}
// 此为单点消息(目前不能用,不知道为什么clientId获取不到,用之前可以先试一下是否可行)
public Boolean sendOneMessage(String message) {
WebSocket webSocket = webSockets.get(clientId);
if (session != null) {
try {
if (session.isOpen()) {
System.out.println("【websocket消息】单点消息:"+message);
webSocket.session.getAsyncRemote().sendText(message);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
}
sendAllMessage实现广播发送给每一个客户端,具体发送需求可以自己调整