SRS 4.0 HTTP回调实战SpringBoot 5个接口实现推拉流鉴权与日志记录流媒体服务在现代互联网应用中扮演着越来越重要的角色而SRS(Simple Realtime Server)作为一款高性能的开源流媒体服务器凭借其轻量级、高并发的特性已经成为众多开发者的首选。本文将深入探讨如何利用SRS 4.0的HTTP回调机制结合SpringBoot后端实现一个完整的流媒体业务系统涵盖推流/拉流鉴权、用户行为日志记录等核心功能。1. SRS HTTP回调机制解析HTTP回调是SRS提供的一种灵活的事件通知机制它允许开发者在特定事件发生时如客户端连接、推流、拉流等通过HTTP请求将相关数据发送到开发者自定义的服务端接口。这种机制为业务逻辑的扩展提供了极大的便利。SRS 4.0支持的主要回调事件包括on_connect客户端连接到指定vhost和app时触发on_publish客户端开始推流时触发on_play客户端开始拉流播放时触发on_stop客户端停止播放时触发on_close客户端断开连接时触发每个回调事件都会携带丰富的上下文信息例如{ action: on_publish, client_id: 1985, ip: 192.168.1.10, vhost: video.test.com, app: live, stream: livestream, param: ?tokenxxxsaltyyy }2. SpringBoot项目架构设计为了实现一个完整的流媒体业务系统我们需要设计合理的项目结构。以下是推荐的SpringBoot项目模块划分src/main/java ├── com.example.live │ ├── config # 配置类 │ ├── constant # 常量定义 │ ├── controller # 控制器 │ ├── callbackbean # 回调数据结构 │ ├── service # 业务服务 │ └── utils # 工具类 src/main/resources ├── static # 静态资源 └── templates # 模板文件2.1 核心依赖配置在pom.xml中需要添加以下关键依赖dependencies !-- SpringBoot Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- JWT支持 -- dependency groupIdcom.auth0/groupId artifactIdjava-jwt/artifactId version3.18.2/version /dependency !-- 日志记录 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-log4j2/artifactId /dependency /dependencies3. JWT鉴权方案实现在流媒体系统中安全认证是必不可少的环节。我们采用JWT(JSON Web Token)作为鉴权方案它具有无状态、易扩展的特点非常适合分布式系统。3.1 Token工具类实现public class TokenUtils { private static final long EXPIRE_TIME 60 * 60 * 1000; // 1小时有效 private static final String SECRET your-secret-key; // 加密密钥 /** * 生成签名 */ public static String sign(String username) { Date expireDate new Date(System.currentTimeMillis() EXPIRE_TIME); Algorithm algorithm Algorithm.HMAC256(SECRET); return JWT.create() .withIssuer(srs-auth) // 签发者 .withClaim(username, username) // 自定义声明 .withExpiresAt(expireDate) // 过期时间 .sign(algorithm); } /** * 验证Token */ public static boolean verify(String token, String username) { try { Algorithm algorithm Algorithm.HMAC256(SECRET); JWTVerifier verifier JWT.require(algorithm) .withClaim(username, username) .build(); verifier.verify(token); return true; } catch (Exception e) { return false; } } }3.2 推流URL生成推流URL需要包含Token参数供后续鉴权使用Service public class StreamServiceImpl implements StreamService { Override public String generateURL(String username) { String token TokenUtils.sign(username); String stream UUID.randomUUID().toString(); return rtmp://your-srs-server/live/ stream ?token token; } }4. 核心回调接口实现4.1 连接回调(on_connect)PostMapping(/connect) public int onConnect(RequestBody CallBackDataOnConnect data) { // 验证app名称是否正确 if(!live.equals(data.getApp())) { log.warn(Invalid app name: {}, data.getApp()); return 1; // 拒绝连接 } log.info(Client connected - IP: {}, Vhost: {}, App: {}, data.getIp(), data.getVhost(), data.getApp()); return 0; // 允许连接 }4.2 推流回调(on_publish)PostMapping(/publish) public int onPublish(RequestBody CallBackDataOnPublish data) { // 1. 验证app名称 if(!live.equals(data.getApp())) { log.warn(Publish rejected - invalid app: {}, data.getApp()); return 1; } // 2. 提取并验证Token String token extractToken(data.getParam()); if(token null || !TokenUtils.verify(token, expected-username)) { log.warn(Publish rejected - invalid token); return 1; } log.info(Stream published - Stream: {}, Client: {}, data.getStream(), data.getIp()); return 0; } private String extractToken(String param) { if(param null || !param.startsWith(?token)) { return null; } return param.substring(7); // 截取?token后面的部分 }4.3 播放回调(on_play)PostMapping(/play) public int onPlay(RequestBody CallBackDataOnPlay data) { // 1. 基础验证 if(!live.equals(data.getApp())) { log.warn(Play rejected - invalid app: {}, data.getApp()); return 1; } // 2. Token验证 String token extractToken(data.getParam()); if(token null || !TokenUtils.verify(token, expected-username)) { log.warn(Play rejected - invalid token); return 1; } // 3. 记录播放行为 log.info(Stream playing - Stream: {}, Client: {}, Page: {}, data.getStream(), data.getIp(), data.getPageUrl()); return 0; }4.4 停止播放回调(on_stop)PostMapping(/stop) public int onStop(RequestBody String rawData) { // 解析JSON数据 CallBackDataOnStop data parseStopData(rawData); log.info(Play stopped - Stream: {}, Duration: {}, data.getStream(), data.getDuration()); return 0; }4.5 断开连接回调(on_close)PostMapping(/close) public int onClose(RequestBody CallBackDataOnClose data) { log.info(Client disconnected - IP: {}, Bytes sent: {}, Bytes received: {}, data.getIp(), data.getSendBytes(), data.getRecvBytes()); return 0; }5. SRS服务配置要让SRS能够正确调用我们的SpringBoot接口需要在SRS配置文件中进行相应设置vhost __defaultVhost__ { http_hooks { enabled on; on_connect http://your-springboot-server:8080/connect; on_close http://your-springboot-server:8080/close; on_publish http://your-springboot-server:8080/publish; on_unpublish http://your-springboot-server:8080/unpublish; on_play http://your-springboot-server:8080/play; on_stop http://your-springboot-server:8080/stop; } }6. 日志记录与监控完善的日志系统对于流媒体服务至关重要。我们采用Log4j2进行日志记录并设计了以下日志规范访问日志记录所有客户端连接、断开事件推流日志记录推流开始、结束事件及推流质量播放日志记录播放开始、结束事件及播放质量异常日志记录所有鉴权失败、异常情况日志配置示例log4j2.xmlConfiguration Appenders RollingFile nameFileAppender fileNamelogs/app.log filePatternlogs/app-%d{yyyy-MM-dd}.log.gz PatternLayout Pattern%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n/Pattern /PatternLayout Policies TimeBasedTriggeringPolicy interval1 modulatetrue/ /Policies /RollingFile /Appenders Loggers Logger namecom.example.live.controller levelinfo additivityfalse AppenderRef refFileAppender/ /Logger Root levelerror AppenderRef refFileAppender/ /Root /Loggers /Configuration7. 性能优化与安全建议在实际部署时还需要考虑以下优化和安全措施接口性能优化使用缓存减少数据库查询异步处理非关键日志记录合理设置HTTP超时时间安全加固启用HTTPS加密通信实现IP白名单机制定期轮换JWT密钥高可用设计部署多个SRS边缘节点实现回调服务的负载均衡建立完善的监控告警系统通过以上实现我们构建了一个完整的流媒体业务系统具备完善的鉴权机制和操作日志记录功能。这套方案已经在多个实际项目中得到验证能够稳定支持高并发的流媒体服务需求。
SRS 4.0 HTTP回调实战:SpringBoot 5个接口实现推拉流鉴权与日志记录
SRS 4.0 HTTP回调实战SpringBoot 5个接口实现推拉流鉴权与日志记录流媒体服务在现代互联网应用中扮演着越来越重要的角色而SRS(Simple Realtime Server)作为一款高性能的开源流媒体服务器凭借其轻量级、高并发的特性已经成为众多开发者的首选。本文将深入探讨如何利用SRS 4.0的HTTP回调机制结合SpringBoot后端实现一个完整的流媒体业务系统涵盖推流/拉流鉴权、用户行为日志记录等核心功能。1. SRS HTTP回调机制解析HTTP回调是SRS提供的一种灵活的事件通知机制它允许开发者在特定事件发生时如客户端连接、推流、拉流等通过HTTP请求将相关数据发送到开发者自定义的服务端接口。这种机制为业务逻辑的扩展提供了极大的便利。SRS 4.0支持的主要回调事件包括on_connect客户端连接到指定vhost和app时触发on_publish客户端开始推流时触发on_play客户端开始拉流播放时触发on_stop客户端停止播放时触发on_close客户端断开连接时触发每个回调事件都会携带丰富的上下文信息例如{ action: on_publish, client_id: 1985, ip: 192.168.1.10, vhost: video.test.com, app: live, stream: livestream, param: ?tokenxxxsaltyyy }2. SpringBoot项目架构设计为了实现一个完整的流媒体业务系统我们需要设计合理的项目结构。以下是推荐的SpringBoot项目模块划分src/main/java ├── com.example.live │ ├── config # 配置类 │ ├── constant # 常量定义 │ ├── controller # 控制器 │ ├── callbackbean # 回调数据结构 │ ├── service # 业务服务 │ └── utils # 工具类 src/main/resources ├── static # 静态资源 └── templates # 模板文件2.1 核心依赖配置在pom.xml中需要添加以下关键依赖dependencies !-- SpringBoot Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- JWT支持 -- dependency groupIdcom.auth0/groupId artifactIdjava-jwt/artifactId version3.18.2/version /dependency !-- 日志记录 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-log4j2/artifactId /dependency /dependencies3. JWT鉴权方案实现在流媒体系统中安全认证是必不可少的环节。我们采用JWT(JSON Web Token)作为鉴权方案它具有无状态、易扩展的特点非常适合分布式系统。3.1 Token工具类实现public class TokenUtils { private static final long EXPIRE_TIME 60 * 60 * 1000; // 1小时有效 private static final String SECRET your-secret-key; // 加密密钥 /** * 生成签名 */ public static String sign(String username) { Date expireDate new Date(System.currentTimeMillis() EXPIRE_TIME); Algorithm algorithm Algorithm.HMAC256(SECRET); return JWT.create() .withIssuer(srs-auth) // 签发者 .withClaim(username, username) // 自定义声明 .withExpiresAt(expireDate) // 过期时间 .sign(algorithm); } /** * 验证Token */ public static boolean verify(String token, String username) { try { Algorithm algorithm Algorithm.HMAC256(SECRET); JWTVerifier verifier JWT.require(algorithm) .withClaim(username, username) .build(); verifier.verify(token); return true; } catch (Exception e) { return false; } } }3.2 推流URL生成推流URL需要包含Token参数供后续鉴权使用Service public class StreamServiceImpl implements StreamService { Override public String generateURL(String username) { String token TokenUtils.sign(username); String stream UUID.randomUUID().toString(); return rtmp://your-srs-server/live/ stream ?token token; } }4. 核心回调接口实现4.1 连接回调(on_connect)PostMapping(/connect) public int onConnect(RequestBody CallBackDataOnConnect data) { // 验证app名称是否正确 if(!live.equals(data.getApp())) { log.warn(Invalid app name: {}, data.getApp()); return 1; // 拒绝连接 } log.info(Client connected - IP: {}, Vhost: {}, App: {}, data.getIp(), data.getVhost(), data.getApp()); return 0; // 允许连接 }4.2 推流回调(on_publish)PostMapping(/publish) public int onPublish(RequestBody CallBackDataOnPublish data) { // 1. 验证app名称 if(!live.equals(data.getApp())) { log.warn(Publish rejected - invalid app: {}, data.getApp()); return 1; } // 2. 提取并验证Token String token extractToken(data.getParam()); if(token null || !TokenUtils.verify(token, expected-username)) { log.warn(Publish rejected - invalid token); return 1; } log.info(Stream published - Stream: {}, Client: {}, data.getStream(), data.getIp()); return 0; } private String extractToken(String param) { if(param null || !param.startsWith(?token)) { return null; } return param.substring(7); // 截取?token后面的部分 }4.3 播放回调(on_play)PostMapping(/play) public int onPlay(RequestBody CallBackDataOnPlay data) { // 1. 基础验证 if(!live.equals(data.getApp())) { log.warn(Play rejected - invalid app: {}, data.getApp()); return 1; } // 2. Token验证 String token extractToken(data.getParam()); if(token null || !TokenUtils.verify(token, expected-username)) { log.warn(Play rejected - invalid token); return 1; } // 3. 记录播放行为 log.info(Stream playing - Stream: {}, Client: {}, Page: {}, data.getStream(), data.getIp(), data.getPageUrl()); return 0; }4.4 停止播放回调(on_stop)PostMapping(/stop) public int onStop(RequestBody String rawData) { // 解析JSON数据 CallBackDataOnStop data parseStopData(rawData); log.info(Play stopped - Stream: {}, Duration: {}, data.getStream(), data.getDuration()); return 0; }4.5 断开连接回调(on_close)PostMapping(/close) public int onClose(RequestBody CallBackDataOnClose data) { log.info(Client disconnected - IP: {}, Bytes sent: {}, Bytes received: {}, data.getIp(), data.getSendBytes(), data.getRecvBytes()); return 0; }5. SRS服务配置要让SRS能够正确调用我们的SpringBoot接口需要在SRS配置文件中进行相应设置vhost __defaultVhost__ { http_hooks { enabled on; on_connect http://your-springboot-server:8080/connect; on_close http://your-springboot-server:8080/close; on_publish http://your-springboot-server:8080/publish; on_unpublish http://your-springboot-server:8080/unpublish; on_play http://your-springboot-server:8080/play; on_stop http://your-springboot-server:8080/stop; } }6. 日志记录与监控完善的日志系统对于流媒体服务至关重要。我们采用Log4j2进行日志记录并设计了以下日志规范访问日志记录所有客户端连接、断开事件推流日志记录推流开始、结束事件及推流质量播放日志记录播放开始、结束事件及播放质量异常日志记录所有鉴权失败、异常情况日志配置示例log4j2.xmlConfiguration Appenders RollingFile nameFileAppender fileNamelogs/app.log filePatternlogs/app-%d{yyyy-MM-dd}.log.gz PatternLayout Pattern%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n/Pattern /PatternLayout Policies TimeBasedTriggeringPolicy interval1 modulatetrue/ /Policies /RollingFile /Appenders Loggers Logger namecom.example.live.controller levelinfo additivityfalse AppenderRef refFileAppender/ /Logger Root levelerror AppenderRef refFileAppender/ /Root /Loggers /Configuration7. 性能优化与安全建议在实际部署时还需要考虑以下优化和安全措施接口性能优化使用缓存减少数据库查询异步处理非关键日志记录合理设置HTTP超时时间安全加固启用HTTPS加密通信实现IP白名单机制定期轮换JWT密钥高可用设计部署多个SRS边缘节点实现回调服务的负载均衡建立完善的监控告警系统通过以上实现我们构建了一个完整的流媒体业务系统具备完善的鉴权机制和操作日志记录功能。这套方案已经在多个实际项目中得到验证能够稳定支持高并发的流媒体服务需求。