您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# SpringBoot + WebSocket + Redis 搭建实时通信系统
## 目录
1. [技术选型与架构设计](#技术选型与架构设计)
2. [环境准备与项目初始化](#环境准备与项目初始化)
3. [基础WebSocket实现](#基础websocket实现)
4. [Redis集成与分布式会话](#redis集成与分布式会话)
5. [消息广播与集群处理](#消息广播与集群处理)
6. [安全认证与性能优化](#安全认证与性能优化)
7. [完整代码示例](#完整代码示例)
8. [常见问题解决方案](#常见问题解决方案)
---
## 技术选型与架构设计
### 为什么选择这套技术栈?
- **SpringBoot**:简化配置的微服务框架
- **WebSocket**:全双工通信协议(对比HTTP轮询)
- **Redis**:高性能内存数据库,实现:
- 分布式会话存储
- 消息发布/订阅
- 在线用户管理
### 系统架构图
```mermaid
graph TD
A[客户端] -->|WebSocket| B(SpringBoot服务端)
B --> C[Redis Pub/Sub]
C --> D[集群节点1]
C --> E[集群节点2]
<dependencies>
<!-- SpringBoot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Redis集成 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
</dependencies>
spring:
redis:
host: 127.0.0.1
port: 6379
password:
database: 0
server:
port: 8080
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOrigins("*")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
@Controller
public class MessageController {
@MessageMapping("/chat")
@SendTo("/topic/messages")
public Message sendMessage(Message message) {
return new Message(message.getSender(),
"ECHO: " + message.getContent());
}
}
const socket = new SockJS('/ws');
const stompClient = Stomp.over(socket);
stompClient.connect({}, (frame) => {
stompClient.subscribe('/topic/messages', (response) => {
console.log(JSON.parse(response.body));
});
});
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
public class AuthInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) {
String token = extractToken(request);
// 从Redis验证token有效性
return redisTemplate.opsForValue().get(token) != null;
}
}
@Configuration
public class RedisPubSubConfig {
@Bean
public RedisMessageListenerContainer container(
RedisConnectionFactory factory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
container.addMessageListener(listenerAdapter,
new PatternTopic("websocket:*"));
return container;
}
}
@Service
public class RedisMessageSubscriber {
@Autowired
private SimpMessagingTemplate messagingTemplate;
public void handleMessage(String message) {
// 将Redis消息转发给WebSocket客户端
messagingTemplate.convertAndSend("/topic/cluster", message);
}
}
优化点 | 实施方法 | 预期提升 |
---|---|---|
消息压缩 | 启用GZIP压缩 | 带宽减少60% |
连接池 | Lettuce连接池配置 | QPS提升3倍 |
序列化 | 使用Protobuf替代JSON | 吞吐量提高40% |
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
private String sender;
private String content;
private LocalDateTime timestamp;
}
@Service
public class OnlineUserService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String ONLINE_USERS = "online:users";
public void addUser(String userId) {
redisTemplate.opsForSet().add(ONLINE_USERS, userId);
}
public Long getOnlineCount() {
return redisTemplate.opsForSet().size(ONLINE_USERS);
}
}
方案:实现心跳检测机制
// 服务端配置心跳
registry.setHeartbeatValue(new long[]{10000, 10000});
// 客户端响应心跳
stompClient.heartbeat.outgoing = 10000;
解决:使用Redis的原子操作
// Lua脚本保证原子性
String script = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then " +
"return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end";
方案:集成Actuator端点
management:
endpoints:
web:
exposure:
include: websocketstats
最佳实践建议: 1. 生产环境建议使用专业的消息中间件(如RabbitMQ)替代Redis Pub/Sub 2. 重要消息需要实现ACK确认机制 3. 客户端需实现自动重连逻辑 4. 使用Nginx进行WebSocket负载均衡时需添加特殊配置:
> proxy_set_header Upgrade $http_upgrade; > proxy_set_header Connection "upgrade"; > ```
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。