# zlm-spring-boot-starter
**Repository Path**: wenfengSAT/zlm-spring-boot-starter
## Basic Information
- **Project Name**: zlm-spring-boot-starter
- **Description**: No description available
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-10-16
- **Last Updated**: 2025-10-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# zlm-spring-boot-starter
[](https://mvnrepository.com/artifact/io.github.lunasaw/zlm-spring-boot-starter)
[](https://raw.githubusercontent.com/lunasaw/zlm-spring-boot-starter/master/LICENSE)
[www.isluna.ml](http://lunasaw.github.io)
## 项目简介
ZLMediaKit的Spring Boot Starter,是一个针对[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
流媒体服务器的Java集成组件。本项目对ZLMediaKit的REST
API进行了完整封装,并提供了Hook事件处理机制,支持集群化管理和多种负载均衡策略,让Java开发者能够轻松集成和管理ZLMediaKit流媒体服务器。
完整的视频平台实现:[voglander](https://github.com/lunasaw/voglander)
[文档链接](https://github.com/lunasaw/zlm-spring-boot-starter/blob/master/zlm-api.md) | [API文档](https://lunasaw.github.io/zlm-spring-boot-starter/)
## 功能特性
- 🚀 **简单易用**: 基于Spring Boot自动配置,开箱即用
- 🔄 **完整API封装**: 封装了ZLMediaKit的所有REST API接口
- 🎯 **Hook事件处理**: 支持ZLMediaKit的所有Hook事件回调
- ⚖️ **负载均衡**: 内置5种负载均衡算法,支持集群化部署
- 🔧 **灵活配置**: 支持多节点配置,可独立启用/禁用节点和Hook功能
- 🌐 **动态节点发现**: 支持NodeSupplier接口实现动态节点管理和服务发现
- 📊 **监控支持**: 提供流媒体状态监控和统计信息获取
- 🎬 **流媒体管理**: 支持流的推拉、录制、截图等完整功能
- 🔐 **安全认证**: 支持RTSP认证和HTTP访问控制
- 🌐 **REST API控制器**: 内置完整的HTTP REST API控制器,直接提供Web接口
- 📖 **API文档集成**: 集成OpenAPI/Swagger,自动生成API文档
- 🔄 **自动数据库类型检测**: 支持多种数据库的自动检测和适配
## 系统要求
- Java 17+
- Spring Boot 3.5.3+
- ZLMediaKit服务器
- 支持Jakarta EE规范(使用jakarta包而非javax包)
## 快速开始
### 1. 添加依赖
```xml
io.github.lunasaw
zlm-spring-boot-starter
1.0.6
```
### 2. 配置文件
在 `application.yml` 中添加ZLMediaKit配置:
```yaml
zlm:
enable: true # 是否启用,未启用不会加载
balance: round_robin # 节点负载均衡算法,默认round_robin
nodes: # zlm节点列表,每个节点配置如下
- server-id: zlm-node-1 # 节点ID,可自定义
host: "http://127.0.0.1:9092" # 节点地址
secret: zlm # 节点密钥
enabled: true # 节点是否启用
hook-enabled: true # 节点是否启用hook接口,启用hook会注入hook接口,默认true,需要注意拦截器放通
- server-id: zlm-node-2 # 可配置多个节点
host: "http://127.0.0.1:9093"
secret: zlm
enabled: true
hook-enabled: true
```
### 3. 使用REST API
#### 方式一:直接调用静态方法
```java
import io.github.lunasaw.zlm.api.ZlmRestService;
import io.github.lunasaw.zlm.entity.ServerResponse;
import io.github.lunasaw.zlm.entity.Version;
// 获取服务器版本信息
ServerResponse versionResponse = ZlmRestService.getVersion("http://127.0.0.1:9092", "zlm");
System.out.println("ZLMediaKit版本: " + versionResponse.getData().getVersion());
// 获取流列表
ServerResponse> mediaList = ZlmRestService.getMediaList("http://127.0.0.1:9092", "zlm", new HashMap<>());
mediaList.getData().forEach(media -> {
System.out.println("流ID: " + media.getApp() + "/" + media.getStream());
});
```
#### 方式二:使用内置API控制器
项目内置了完整的REST API控制器,可以直接通过HTTP接口访问:
```bash
# 获取服务器版本信息
GET http://localhost:8080/zlm/api/version
# 获取流列表
POST http://localhost:8080/zlm/api/media/list
Content-Type: application/json
{
"app": "live",
"stream": ""
}
# 获取API文档
GET http://localhost:8080/swagger-ui.html
```
支持的API接口路径前缀:`/zlm/api/`,包括:
- 服务器管理:`/zlm/api/version`、`/zlm/api/server/config`
- 流媒体管理:`/zlm/api/media/list`、`/zlm/api/media/close`
- 代理管理:`/zlm/api/proxy/add`、`/zlm/api/proxy/delete`
- 录制管理:`/zlm/api/record/start`、`/zlm/api/record/stop`
- RTP管理:`/zlm/api/rtp/open`、`/zlm/api/rtp/close`
### 4. 实现Hook服务
创建Hook服务实现类来处理ZLMediaKit的事件回调:
```java
import io.github.lunasaw.zlm.hook.service.AbstractZlmHookService;
import org.springframework.stereotype.Service;
@Service
public class CustomZlmHookService extends AbstractZlmHookService {
@Override
public HookResult onPlay(OnPlayHookParam param) {
// 播放鉴权逻辑
log.info("播放请求: {}:{}", param.getApp(), param.getStream());
if (isValidUser(param.getParams())) {
return HookResult.SUCCESS();
}
return HookResult.FAILED("无权限播放该流");
}
@Override
public HookResultForOnPublish onPublish(OnPublishHookParam param) {
// 推流鉴权逻辑
log.info("推流请求: {}:{}", param.getApp(), param.getStream());
return HookResultForOnPublish.SUCCESS();
}
@Override
public void onStreamChanged(OnStreamChangedHookParam param) {
// 流状态变化处理
log.info("流状态变化: {} - {}", param.getStream(), param.isRegist());
}
private boolean isValidUser(String params) {
// 实现用户验证逻辑
return true;
}
}
```
## 详细配置说明
### 负载均衡算法
支持以下5种负载均衡算法:
| 算法 | 配置值 | 说明 |
|-------|----------------------|------------|
| 随机 | `random` | 随机选择节点 |
| 轮询 | `round_robin` | 轮询选择节点(默认) |
| 一致性哈希 | `consistent_hashing` | 基于一致性哈希算法 |
| 加权轮询 | `weight_round_robin` | 基于权重的轮询 |
| 加权随机 | `weight_random` | 基于权重的随机选择 |
### 配置参数详解
```yaml
zlm:
enable: true # 是否启用ZLM功能
balance: round_robin # 负载均衡算法
nodes: # 节点配置列表
- server-id: unique-id # 节点唯一标识
host: "http://ip:port" # 节点地址
secret: "secret-key" # API密钥
enabled: true # 是否启用该节点
hook-enabled: true # 是否启用Hook功能
weight: 1 # 节点权重(仅加权算法有效)
```
## API功能详解
### 1. 服务器管理
```java
// 获取服务器版本
ServerResponse version = ZlmRestService.getVersion(host, secret);
// 获取服务器配置
ServerResponse config = ZlmRestService.getServerConfig(host, secret);
// 获取API列表
ServerResponse> apiList = ZlmRestService.getApiList(host, secret);
// 获取服务器统计信息
ServerResponse statistics = ZlmRestService.getStatistic(host, secret);
```
### 2. 流媒体管理
```java
// 获取流列表
MediaReq mediaReq = new MediaReq();
mediaReq.
setApp("live");
ServerResponse> mediaList = ZlmRestService.getMediaList(host, secret, mediaReq);
// 关闭指定流
ZlmRestService.
closeStream(host, secret, mediaReq);
// 检查流是否在线
MediaOnlineStatus status = ZlmRestService.isMediaOnline(host, secret, mediaReq);
// 获取流详细信息
ServerResponse mediaInfo = ZlmRestService.getMediaInfo(host, secret, mediaReq);
```
### 3. 代理拉流
```java
// 添加拉流代理
StreamProxyItem proxyItem = new StreamProxyItem();
proxyItem.
setVhost("__defaultVhost__");
proxyItem.
setApp("live");
proxyItem.
setStream("test");
proxyItem.
setUrl("rtmp://example.com/live/stream");
ServerResponse result = ZlmRestService.addStreamProxy(host, secret, proxyItem);
// 删除拉流代理
ZlmRestService.
delStreamProxy(host, secret, result.getData().
getKey());
```
### 4. 推流管理
```java
// 添加推流
StreamPusherItem pusherItem = new StreamPusherItem();
pusherItem.
setSchema("rtmp");
pusherItem.
setVhost("__defaultVhost__");
pusherItem.
setApp("live");
pusherItem.
setStream("test");
pusherItem.
setDst_url("rtmp://push.example.com/live/stream");
ServerResponse pushResult = ZlmRestService.addStreamPusherProxy(host, secret, pusherItem);
```
### 5. 录制功能
```java
// 开始录制
RecordReq recordReq = new RecordReq();
recordReq.
setType(0); // 0-hls, 1-mp4
recordReq.
setVhost("__defaultVhost__");
recordReq.
setApp("live");
recordReq.
setStream("test");
ZlmRestService.
startRecord(host, secret, recordReq);
// 停止录制
ZlmRestService.
stopRecord(host, secret, recordReq);
// 获取录制文件
ServerResponse recordFiles = ZlmRestService.getMp4RecordFile(host, secret, recordReq);
```
### 6. 截图功能
```java
// 获取流截图
SnapshotReq snapshotReq = new SnapshotReq();
snapshotReq.
setVhost("__defaultVhost__");
snapshotReq.
setApp("live");
snapshotReq.
setStream("test");
snapshotReq.
setSavePath("/path/to/snapshot.jpg");
String result = ZlmRestService.getSnap(host, secret, snapshotReq);
```
### 7. RTP服务
```java
// 创建RTP服务器
OpenRtpServerReq rtpReq = new OpenRtpServerReq();
rtpReq.
setStream_id("test_rtp");
rtpReq.
setPort(10000);
OpenRtpServerResult rtpResult = ZlmRestService.openRtpServer(host, secret, rtpReq);
// 关闭RTP服务器
ZlmRestService.
closeRtpServer(host, secret, "test_rtp");
```
## Hook事件详解
### Hook接口说明
实现 `ZlmHookService` 接口或继承 `AbstractZlmHookService` 类来处理各种Hook事件:
```java
public interface ZlmHookService {
// 服务器保活事件
void onServerKeepLive(OnServerKeepaliveHookParam param);
// 播放鉴权
HookResult onPlay(OnPlayHookParam param);
// 推流鉴权
HookResultForOnPublish onPublish(OnPublishHookParam param);
// 流状态变化
void onStreamChanged(OnStreamChangedHookParam param);
// 流无人观看
HookResultForStreamNoneReader onStreamNoneReader(OnStreamNoneReaderHookParam param);
// 流未找到
void onStreamNotFound(OnStreamNotFoundHookParam param);
// 服务器启动
void onServerStarted(ServerNodeConfig param);
// RTP推流停止
void onSendRtpStopped(OnSendRtpStoppedHookParam param);
// RTP服务器超时
void onRtpServerTimeout(OnRtpServerTimeoutHookParam param);
// HTTP访问鉴权
HookResultForOnHttpAccess onHttpAccess(OnHttpAccessParam param);
// RTSP Realm鉴权
HookResultForOnRtspRealm onRtspRealm(OnRtspRealmHookParam param);
// RTSP用户密码鉴权
HookResultForOnRtspAuth onRtspAuth(OnRtspAuthHookParam param);
// 流量统计
void onFlowReport(OnFlowReportHookParam param);
// 服务器退出
void onServerExited(HookParam param);
// MP4录制完成
void onRecordMp4(OnRecordMp4HookParam param);
}
```
### Hook返回值说明
不同的Hook事件需要返回不同的结果:
```java
// 基础Hook结果 - 用于播放鉴权等
HookResult.SUCCESS(); // 允许
HookResult.
FAILED("原因"); // 拒绝
// 推流鉴权结果
HookResultForOnPublish.
SUCCESS(); // 允许推流
HookResultForOnPublish.
FAILED("推流被拒绝"); // 拒绝推流
// HTTP访问鉴权结果
HookResultForOnHttpAccess.
SUCCESS(); // 允许访问
HookResultForOnHttpAccess.
FAILED(401,"未授权"); // 拒绝访问
```
## 高级用法
### 动态节点发现 (NodeSupplier)
本项目支持通过`NodeSupplier`接口实现动态节点发现和管理,支持从数据库、注册中心、配置中心等数据源动态获取节点列表。
#### 默认实现
系统默认提供`DefaultNodeSupplier`实现,从配置文件中获取节点列表:
```java
@Component
public class DefaultNodeSupplier implements NodeSupplier {
@Autowired
private ZlmProperties zlmProperties;
@Override
public String getName() {
return "DefaultNodeSupplier";
}
@Override
public List getNodes() {
return zlmProperties.getNodes();
}
@Override
public ZlmNode getNode(String serverId) {
return zlmProperties.getNodeMap().get(serverId);
}
}
```
#### 自定义NodeSupplier
可以实现自定义的NodeSupplier来支持动态节点发现:
```java
@Component
public class DatabaseNodeSupplier implements NodeSupplier {
@Autowired
private NodeRepository nodeRepository;
@Override
public String getName() {
return "DatabaseNodeSupplier";
}
@Override
public List getNodes() {
// 从数据库获取活跃节点列表
List activeNodes = nodeRepository.findByStatus("ACTIVE");
return activeNodes.stream()
.map(this::convertToZlmNode)
.collect(Collectors.toList());
}
@Override
public ZlmNode getNode(String serverId) {
NodeEntity entity = nodeRepository.findByServerId(serverId);
return entity != null ? convertToZlmNode(entity) : null;
}
private ZlmNode convertToZlmNode(NodeEntity entity) {
ZlmNode node = new ZlmNode();
node.setServerId(entity.getServerId());
node.setHost(entity.getHost());
node.setSecret(entity.getSecret());
node.setEnabled(entity.isEnabled());
node.setWeight(entity.getWeight());
return node;
}
}
```
#### 注册中心集成示例
与Spring Cloud集成,从注册中心动态发现节点:
```java
@Component
public class EurekaNodeSupplier implements NodeSupplier {
@Autowired
private DiscoveryClient discoveryClient;
@Override
public String getName() {
return "EurekaNodeSupplier";
}
@Override
public List getNodes() {
List instances = discoveryClient.getInstances("zlm-service");
return instances.stream()
.filter(ServiceInstance::isSecure)
.map(this::convertToZlmNode)
.collect(Collectors.toList());
}
@Override
public ZlmNode getNode(String serverId) {
List instances = discoveryClient.getInstances("zlm-service");
return instances.stream()
.filter(instance -> serverId.equals(instance.getInstanceId()))
.findFirst()
.map(this::convertToZlmNode)
.orElse(null);
}
private ZlmNode convertToZlmNode(ServiceInstance instance) {
ZlmNode node = new ZlmNode();
node.setServerId(instance.getInstanceId());
node.setHost(instance.getUri().toString());
node.setSecret(instance.getMetadata().get("secret"));
node.setEnabled(true);
node.setWeight(Integer.parseInt(instance.getMetadata().getOrDefault("weight", "1")));
return node;
}
}
```
#### Nacos配置中心集成
从Nacos配置中心动态获取节点配置:
```java
@Component
public class NacosNodeSupplier implements NodeSupplier {
@NacosValue("${zlm.nodes:[]}")
private String nodesConfig;
@Autowired
private ObjectMapper objectMapper;
@Override
public String getName() {
return "NacosNodeSupplier";
}
@Override
public List getNodes() {
try {
if (StringUtils.hasText(nodesConfig)) {
return objectMapper.readValue(nodesConfig,
new TypeReference>() {
});
}
return Collections.emptyList();
} catch (Exception e) {
log.error("解析Nacos节点配置失败", e);
return Collections.emptyList();
}
}
}
```
#### NodeSupplier优势
1. **实时性**: 每次负载均衡选择节点时都获取最新的节点列表
2. **动态性**: 支持节点的动态上下线,无需重启应用
3. **扩展性**: 可以集成任何数据源,如数据库、注册中心、配置中心等
4. **容错性**: 支持多种数据源的容错和降级策略
#### 使用建议
- **开发环境**: 使用默认的`DefaultNodeSupplier`,配置简单
- **测试环境**: 可以使用数据库或配置中心的NodeSupplier
- **生产环境**: 建议使用注册中心集成的NodeSupplier,支持自动故障转移
### 集群部署示例
```yaml
zlm:
enable: true
balance: consistent_hashing
nodes:
- server-id: zlm-beijing-1
host: "http://10.0.1.10:9092"
secret: "beijing-secret"
enabled: true
hook-enabled: true
weight: 3
- server-id: zlm-beijing-2
host: "http://10.0.1.11:9092"
secret: "beijing-secret"
enabled: true
hook-enabled: true
weight: 2
- server-id: zlm-shanghai-1
host: "http://10.0.2.10:9092"
secret: "shanghai-secret"
enabled: true
hook-enabled: false # 上海节点不处理Hook
weight: 1
```
### 自定义负载均衡器
```java
@Component
public class CustomLoadBalancer implements LoadBalancer {
private volatile NodeSupplier nodeSupplier;
@Override
public void setNodeSupplier(NodeSupplier nodeSupplier) {
this.nodeSupplier = nodeSupplier;
}
@Override
public ZlmNode selectNode(String key) {
List nodes = getCurrentNodes();
if (nodes == null || nodes.isEmpty()) {
return null;
}
// 实现自定义负载均衡逻辑,例如基于地理位置的选择
return selectByLocation(nodes, key);
}
@Override
public String getType() {
return "CustomLoadBalancer";
}
private List getCurrentNodes() {
if (nodeSupplier == null) {
return Collections.emptyList();
}
try {
return nodeSupplier.getNodes();
} catch (Exception e) {
log.error("获取节点列表失败", e);
return Collections.emptyList();
}
}
private ZlmNode selectByLocation(List nodes, String key) {
// 基于地理位置或其他业务逻辑的选择算法
// 例如:选择离用户最近的节点
return nodes.stream()
.filter(node -> isNearUser(node, key))
.findFirst()
.orElse(nodes.get(0));
}
private boolean isNearUser(ZlmNode node, String key) {
// 实现地理位置判断逻辑
return true;
}
}
```
### 条件化Hook处理
```java
@Service
public class ConditionalZlmHookService extends AbstractZlmHookService {
@Override
public HookResult onPlay(OnPlayHookParam param) {
// 根据不同应用进行不同处理
switch (param.getApp()) {
case "live":
return handleLivePlay(param);
case "vod":
return handleVodPlay(param);
default:
return HookResult.SUCCESS();
}
}
private HookResult handleLivePlay(OnPlayHookParam param) {
// 直播流播放逻辑
return HookResult.SUCCESS();
}
private HookResult handleVodPlay(OnPlayHookParam param) {
// 点播流播放逻辑
return HookResult.SUCCESS();
}
}
```
## 常见问题
### Q: Hook接口无法接收到回调?
A: 请检查以下几点:
1. 确保 `hook-enabled: true`
2. 检查Spring Boot的拦截器是否放通了Hook接口路径
3. 确认ZLMediaKit配置中Hook地址是否正确
4. 检查网络连通性
### Q: 多节点负载均衡不生效?
A: 请确认:
1. 多个节点的 `enabled: true`
2. 负载均衡算法配置正确
3. 节点权重配置(如使用加权算法)
### Q: 自定义NodeSupplier不生效?
A: 请检查:
1. 确保自定义NodeSupplier标注了`@Component`注解
2. 检查Spring扫描路径是否包含NodeSupplier实现类
3. 确认NodeSupplier的`getNodes()`方法返回非空且有效的节点列表
4. 查看日志确认NodeSupplier是否被正确注入到LoadBalancer
### Q: 动态节点发现不及时?
A: 解决方案:
1. NodeSupplier每次选择节点时都会被调用,确保实时性
2. 检查数据源(数据库/注册中心)的更新是否及时
3. 考虑在NodeSupplier中增加缓存和定时刷新机制
4. 查看NodeSupplier实现中的异常处理逻辑
### Q: API调用超时?
A: 建议:
1. 检查网络连接
2. 调整HTTP客户端超时时间
3. 确认ZLMediaKit服务状态
### Q: 录制文件找不到?
A: 请检查:
1. ZLMediaKit的录制路径配置
2. 文件系统权限
3. 磁盘空间
## 注意事项
1. **版本兼容性**: 请确保ZLMediaKit版本与starter版本兼容
2. **Hook接口安全**: 生产环境需要对Hook接口进行适当的安全防护
3. **性能考虑**: 大量并发时建议合理配置连接池和超时时间
4. **NodeSupplier性能**: 由于每次负载均衡选择节点时都会调用NodeSupplier,请确保`getNodes()`方法的性能,必要时添加缓存机制
5. **节点数据一致性**: 使用自定义NodeSupplier时,确保数据源的高可用性和数据一致性
6. **容错处理**: NodeSupplier应当具备良好的异常处理能力,避免因数据源异常导致整个负载均衡失效
7. **日志监控**: 建议开启详细日志以便问题排查,特别是NodeSupplier的执行情况
## 代码规范
- 后端使用同一份代码格式化模板ali-code-style.xml,eclipse直接导入使用,idea使用Eclipse Code Formatter插件配置xml后使用。
- 前端代码使用vs插件的Beautify格式化,缩进使用TAB
- 后端代码非特殊情况遵守P3C插件规范
- 注释要尽可能完整明晰,提交的代码必须要先格式化
- xml文件和前端一样,使用TAB缩进
## 贡献指南
欢迎提交Issue和Pull Request!
1. Fork本项目
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 开启Pull Request
## 许可证
本项目使用 [Apache 2.0](LICENSE) 许可证。
## 联系方式
- 作者: luna
- 邮箱: iszychen@gmail.com
- 项目主页: https://github.com/lunasaw/zlm-spring-boot-starter