QuartZ
概述
任务调度就是控制定时任务的执行,比如订单超时、注册验证码等等都可以使用任务调度实现
Quartz入门案例
- 导入依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
- 创建一个类MyJob实现接口Job,这个类编写我们要实现的任务
public class MyJob implements Job {
/**
* 在这个方法里面编写任务逻辑
* @param context 这个对象可以获取调用本任务的JobDetail和Trigger,可通过这两个对象来从外界获取参数
* @throws JobExecutionException
*/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//获取JobDetail传入的参数
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
System.out.println(jobDataMap.get("name"));
System.out.println("hello");
}
}
- 创建一个类MyScheduler,这个类来对我们要实现的任务进行调度
public class MyScheduler {
public static void main(String[] args) throws SchedulerException {
//构造JobDetail对象
JobDetail jobDetail = JobBuilder
.newJob(MyJob.class)//JobDetail绑定的任务类
.withIdentity("myJob")//任务的标识符
.usingJobData("name","张三")//传入参数
.build();
//构造一个触发器,该触发器将立即执行,间隔两秒执行一次,一直重复
SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("myJob")
.startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever())
.build();
//使用调度工厂构造一个调度对象,该调度对象将JobDetail和trigger绑定并开启任务调度
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
cron表达式
调度的构建有多种,比上方使用的SimpleScheduler更加灵活的的cron表达式的方式,这种方式可以灵活的实现个性化的任务调度
分布式任务调度
在分布式系统中,为了提高性能,任务调度在多台服务器上进行,存在内存中的任务调度就不好用了;于是要对调度规则进行持久化,在官网的源码压缩包中src\org\quartz\impl\jdbcjobstore目录下就有对应各种数据库的建表sql脚本文件,这里使用oracle进行示例
代码实现
- 建表语句
--
-- A hint submitted by a user: Oracle DB MUST be created as "shared" and the
-- job_queue_processes parameter must be greater than 2
-- However, these settings are pretty much standard after any
-- Oracle install, so most users need not worry about this.
--
-- Many other users (including the primary author of Quartz) have had success
-- runing in dedicated mode, so only consider the above as a hint ;-)
--
delete from qrtz_fired_triggers;
delete from qrtz_simple_triggers;
delete from qrtz_simprop_triggers;
delete from qrtz_cron_triggers;
delete from qrtz_blob_triggers;
delete from qrtz_triggers;
delete from qrtz_job_details;
delete from qrtz_calendars;
delete from qrtz_paused_trigger_grps;
delete from qrtz_locks;
delete from qrtz_scheduler_state;
drop table qrtz_calendars;
drop table qrtz_fired_triggers;
drop table qrtz_blob_triggers;
drop table qrtz_cron_triggers;
drop table qrtz_simple_triggers;
drop table qrtz_simprop_triggers;
drop table qrtz_triggers;
drop table qrtz_job_details;
drop table qrtz_paused_trigger_grps;
drop table qrtz_locks;
drop table qrtz_scheduler_state;
CREATE TABLE qrtz_job_details
(
SCHED_NAME VARCHAR2(120) NOT NULL,
JOB_NAME VARCHAR2(200) NOT NULL,
JOB_GROUP VARCHAR2(200) NOT NULL,
DESCRIPTION VARCHAR2(250) NULL,
JOB_CLASS_NAME VARCHAR2(250) NOT NULL,
IS_DURABLE VARCHAR2(1) NOT NULL,
IS_NONCONCURRENT VARCHAR2(1) NOT NULL,
IS_UPDATE_DATA VARCHAR2(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR2(1) NOT NULL,
JOB_DATA BLOB NULL,
CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
JOB_NAME VARCHAR2(200) NOT NULL,
JOB_GROUP VARCHAR2(200) NOT NULL,
DESCRIPTION VARCHAR2(250) NULL,
NEXT_FIRE_TIME NUMBER(13) NULL,
PREV_FIRE_TIME NUMBER(13) NULL,
PRIORITY NUMBER(13) NULL,
TRIGGER_STATE VARCHAR2(16) NOT NULL,
TRIGGER_TYPE VARCHAR2(8) NOT NULL,
START_TIME NUMBER(13) NOT NULL,
END_TIME NUMBER(13) NULL,
CALENDAR_NAME VARCHAR2(200) NULL,
MISFIRE_INSTR NUMBER(2) NULL,
JOB_DATA BLOB NULL,
CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_simple_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
REPEAT_COUNT NUMBER(7) NOT NULL,
REPEAT_INTERVAL NUMBER(12) NOT NULL,
TIMES_TRIGGERED NUMBER(10) NOT NULL,
CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_cron_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
CRON_EXPRESSION VARCHAR2(120) NOT NULL,
TIME_ZONE_ID VARCHAR2(80),
CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_simprop_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
STR_PROP_1 VARCHAR2(512) NULL,
STR_PROP_2 VARCHAR2(512) NULL,
STR_PROP_3 VARCHAR2(512) NULL,
INT_PROP_1 NUMBER(10) NULL,
INT_PROP_2 NUMBER(10) NULL,
LONG_PROP_1 NUMBER(13) NULL,
LONG_PROP_2 NUMBER(13) NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR2(1) NULL,
BOOL_PROP_2 VARCHAR2(1) NULL,
CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_blob_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
BLOB_DATA BLOB NULL,
CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_calendars
(
SCHED_NAME VARCHAR2(120) NOT NULL,
CALENDAR_NAME VARCHAR2(200) NOT NULL,
CALENDAR BLOB NOT NULL,
CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);
CREATE TABLE qrtz_paused_trigger_grps
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_fired_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
ENTRY_ID VARCHAR2(95) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
INSTANCE_NAME VARCHAR2(200) NOT NULL,
FIRED_TIME NUMBER(13) NOT NULL,
SCHED_TIME NUMBER(13) NOT NULL,
PRIORITY NUMBER(13) NOT NULL,
STATE VARCHAR2(16) NOT NULL,
JOB_NAME VARCHAR2(200) NULL,
JOB_GROUP VARCHAR2(200) NULL,
IS_NONCONCURRENT VARCHAR2(1) NULL,
REQUESTS_RECOVERY VARCHAR2(1) NULL,
CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);
CREATE TABLE qrtz_scheduler_state
(
SCHED_NAME VARCHAR2(120) NOT NULL,
INSTANCE_NAME VARCHAR2(200) NOT NULL,
LAST_CHECKIN_TIME NUMBER(13) NOT NULL,
CHECKIN_INTERVAL NUMBER(13) NOT NULL,
CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);
CREATE TABLE qrtz_locks
(
SCHED_NAME VARCHAR2(120) NOT NULL,
LOCK_NAME VARCHAR2(40) NOT NULL,
CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY);
create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME);
create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP);
create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE);
create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME);
create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP);
- 导入ojdbc与c3p0连接池依赖,配置quartz属性文件
# 修改存储方式,默认是内存,改为JobStoreTX改为独立环境
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
# 自定义数据源名称
org.quartz.jobStore.dataSource=myDS
# 数据库相关配置
org.quartz.dataSource.myDS.driver=oracle.jdbc.OracleDriver
org.quartz.dataSource.myDS.URL=jdbc:oracle:thin:@localhost:1521:XE
org.quartz.dataSource.myDS.user=c##laowa
org.quartz.dataSource.myDS.password=laowa
- 修改MyScheduler,因为持久化后若重启调度,由于数据库已有该Job,会报错,于是可以先查看数据库是否有该Job的调度,如果有则继承该Job继续运行,没有则创建(以标识符JobKey来判定)
public class MyScheduler2 {
public static void main(String[] args) throws SchedulerException {
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
//通过标识符查看数据库是否已经存在改调度
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(new JobKey("myJob"));
if(triggers.size()>0){
for (Trigger trigger:triggers){
//判断触发器类型,符合则恢复原Job
if(trigger instanceof CronTrigger||trigger instanceof SimpleTrigger){
scheduler.resumeJob(new JobKey("myJob"));
}
}
}else{
//如果不存在则新建一个JobDetail和Trigger
JobDetail jobDetail = JobBuilder
.newJob(MyJob.class)
.withIdentity("myJob")
.usingJobData("name","张三")
.build();
SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
.withIdentity("myJob")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.build();
scheduler.scheduleJob(jobDetail,simpleTrigger);
}
scheduler.start();
}
}
做了持久化之后,只要在数据库中有对应的数据存在,另外任务执行也会带着这个任务执行,要停止Job需要对数据库的数据删除
SpringBoot整合Quartz
- 添加依赖spring-boot-starter-quartz
- 入口类添加
@EnableScheduling
注解,启用调度 - 自定义任务方法,使用
@Scheduled
修饰方法进行调度,对简单的任务可以使用这个注解的方式,复杂的任务调度需要配置JobDetail和Trigger的Bean
@Component
public class MyJob2 {
@Scheduled(cron = "*/5 * * * * *")
public void test1(){
System.out.println("hello");
}
}
集群
- 集群当前使用JDBC-Jobstore (JobStoreTX或JobStoreCMT(容器事务))和TerracottaJobStore。特性包括负载平衡和作业故障转移,通过设置isClustered为true来启用集群
- 集群中的每个节点必须有一个唯一的instanceId,这个类似于分布式事务中的事务组,只需将“AUTO”作为该属性的值,会自动分配Id
- 每个集群每次任务开始中只有一个节点会执行,如果任务有一个重复的触发器,它会随机在一个节点执行,负载均衡是随机分配的,但会倾向于一个比较空闲的节点
配置文件
spring:
quartz:
#持久化到数据库方式
job-store-type: jdbc
initialize-schema: embedded
properties:
org:
quartz:
dataSource:
#数据库连接信息
quartzDS:
driver: oracle.jdbc.OracleDriver
URL: jdbc:oracle:thin:@192.168.206.1:1521:XE
user: c##laowa
password: laowa
scheduler:
instanceName: MyScheduler #实例的名称
instanceId: AUTO #实例的Id,自动分配
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_ #持久化表的前缀
isClustered: true #是否开启集群
clusterCheckinInterval: 3000 #检查节点的时间间隔,检查节点是否挂掉,如果挂掉需要接管该节点的任务,这里表示3秒钟检查一次
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
Swagger
概述
OpenAPI
OpenAPI规范是针对于REST API的接口描述格式,一个OpenAPI文件包括:
- 可用端点(/用户)和每个端点上的操作(GET /用户、POST /用户)
- 操作参数每个操作的输入和输出
- 身份验证方法
- 联系方式,许可,使用条款和其他信息
Swagger
- Swagger是一套围绕OpenAPI规范构建的开源工具,可以帮助设计、构建、记录和使用REST API
- Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口,可以让人和计算机拥有无须访问源码、文档或网络流量监测就可以发现和理解服务的能力。边写代码边写文档
使用
- 导入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
- 配置Bean
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.pathMapping("/")
.select()
.apis(RequestHandlerSelectors.basePackage("com.demo.user.web"))
.paths(PathSelectors.any())
.build().apiInfo(new ApiInfoBuilder()
.title("spring整合swagger")
.description("spring整合swagger详细信息")
.version("9.0")
.contact(new Contact("laowa","blog.csdn.net","123@123.com"))
.build());
}
}
- 在Controller使用注解为接口添加描述
@Controller
@RequestMapping("user")
@ResponseBody
@Api(tags = "用户管理相关接口")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("get/{id}")
@ApiOperation("通过id获取用户信息")
@ApiImplicitParam(name = "id",value = "用户ID",defaultValue = "0")
public ResponseVO getById(@PathVariable Integer id){
return ResponseVO.FORBIDDEN;
}
@Transactional
@PostMapping("addOrder/{id}")
@ApiOperation("添加用户拥有的订单数")
@ApiImplicitParam(name = "id",value = "用户ID",defaultValue = "0")
public ResponseVO addOrder(@PathVariable Integer id) throws Exception {
userService.addOrder(id);
if(id.equals(1)){
throw new Exception();
}
return ResponseVO.SUCCESS;
}
}
WebSocket
概述
WebSocket和HTTP一样,是一个网络通信协议,而且webSocket是基于HTTP的,相对于HTTP具有长连接、双向交互的特点;HTTP协议最大的特点是无状态,基于典型的请求响应模式,所以造成了一个缺陷:请求只能由客户端发起,针对这个缺陷,WebSocket就有了用武之地
应用场景
- 网络聊天
- 应用更新消息推送
网络聊天的简单实现(原生写法)
服务器端
- 导入依赖(springboot版本2.5.4)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
- 配置Bean,注意注解
@EnableWebSocket
开启websocket
@SpringBootApplication
@EnableWebSocket
public class DemoWebsocketApplication {
public static void main(String[] args) {
SpringApplication.run(DemoWebsocketApplication.class, args);
}
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
- 服务端推送消息编码
public class MessageEncoder implements Encoder.Text<Message>{
@Override
public String encode(Message message) throws EncodeException {
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(message);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return "";
}
@Override
public void init(EndpointConfig endpointConfig) {
}
@Override
public void destroy() {
}
}
- WebSocket服务端
@ServerEndpoint(value = "/chat/{userId}",encoders = MessageEncoder.class) //服务器连接点,客户端连接地址
@Component
public class WSServer {
//使用ConcurrentHashMap保证线程安全,这个map存储所有当前连接上的客户端的session,通过session向客户端发送消息
private static ConcurrentHashMap<String,Session> sessions = new ConcurrentHashMap<>();
//连接上的回调
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId){
System.out.println("客户端上线");
sessions.put(userId,session);
}
//服务端接收到消息的回调
@OnMessage
public void onMessage(String message) throws IOException, EncodeException {
ObjectMapper objectMapper = new ObjectMapper();
Message message1 = objectMapper.readValue(message,Message.class);
Session receiver = sessions.get(message1.getReceiverId());
receiver.getBasicRemote().sendObject(message1);//向客户端发送消息
}
//发生错误的回调
@OnError
public void onError(Throwable throwable){
throwable.printStackTrace();
}
//连接关闭的回调
@OnClose
public void onClose(@PathParam("userId") String id){
sessions.remove(id);
}
}
客户端
客户端的回调和服务端的回调是一样的,可以在onerror回调上进行websocket的重连
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="jquery.js"></script>
<script type="text/javascript">
$(function (){
let ws;
$("#connect").click(function (){
//判断websocket是否支持
if("WebSocket" in window){
ws = new WebSocket("ws://localhost:8088/chat/"+$("#senderId").val());
//连接上服务器的回调
ws.onopen = function (){
alert("已连接");
}
//收到消息的回调
ws.onmessage = function (message){
$("#showMsg").innerHTML=JSON.parse(message.data).content;
}
}else{
alert("请更新浏览器");
}
$("#send").click(function (){
let msg={
"senderId":$("#senderId").val(),
"content":$("#editMsg").val(),
"receiverId":$("#receiverId").val()
}
if(ws==null){
alert("请连接");
}else{
ws.send(JSON.stringify(msg));
}
})
})
})
</script>
</head>
<body>
<div id="showMsg" style="width: 500px;height: 200px;background-color: aqua"></div>
<br>
<textarea id="editMsg" style="width: 500px;height: 100px"></textarea>
<br>
<button id="connect">连接</button>
<br>
<button id="send">发送</button> 发送方id:<input type="text" id="senderId"/> 接收方id:<input type="text" id="receiverId"/>
</body>
</html>
服务端SpringBoot整合WebSocket
- 握手拦截器
/***
* @author shaofan
* @Description 该类用来处理连接前后的事情,类似onOpen和onClose
* @Date 2021-9-14
* @Time 16:55
*/
public class MyHandShakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
return false;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
}
- websocket处理器
public class MyHandler extends TextWebSocketHandler {
private static ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
}
}
- 配置
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyHandler(), "/myHandler")
.addInterceptors(new MyHandShakeInterceptor());
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
对于上方的集合sessions,可以存在redis中