[+] 结构调整
This commit is contained in:
72
README.md
72
README.md
@@ -10,15 +10,16 @@
|
||||
|taoyao-nat|内网穿透|STUN/TURN|
|
||||
|taoyao-boot|基础|启动模块|
|
||||
|taoyao-live|直播|直播、连麦|
|
||||
|taoyao-media|媒体|录制、视频(美颜、AI识别)、音频(混音、变声)|
|
||||
|taoyao-test|测试|测试工具|
|
||||
|taoyao-media|媒体|录制、视频(美颜、AI识别)、音频(混音、变声、降噪)|
|
||||
|taoyao-signal|信令|信令服务|
|
||||
|taoyao-server|服务|启动服务|
|
||||
|taoyao-meeting|会议|会议模式、广播模式、单人对讲|
|
||||
|taoyao-webrtc|WebRTC模块||
|
||||
|taoyao-webrtc|WebRTC模块|WebRTC模块|
|
||||
|taoyao-webrtc-jni|WebRTC JNI|WebRTC本地接口|
|
||||
|taoyao-webrtc-sfu|WebRTC SFU架构实现||
|
||||
|taoyao-webrtc-mcu|WebRTC MCU架构实现||
|
||||
|taoyao-webrtc-mesh|WebRTC MESH架构实现||
|
||||
|taoyao-webrtc-sfu|WebRTC SFU架构|SFU架构|
|
||||
|taoyao-webrtc-mcu|WebRTC MCU架构|MCU架构|
|
||||
|taoyao-webrtc-mesh|WebRTC MESH架构|MESH架构|
|
||||
|
||||
## STUN/TURN公共服务
|
||||
|
||||
@@ -30,46 +31,41 @@ stun:stun4.l.google.com:19302
|
||||
stun:stun.stunprotocol.org:3478
|
||||
```
|
||||
|
||||
## 终端
|
||||
|
||||
帐号(移动端|浏览器)
|
||||
摄像头
|
||||
|
||||
### 功能
|
||||
|
||||
|功能|场景|描述|帐号|摄像头|
|
||||
注册
|
||||
注销
|
||||
心跳
|
||||
推流
|
||||
拉流
|
||||
邀请
|
||||
踢出
|
||||
绑定设备
|
||||
解绑设备
|
||||
进入会议:没有自动创建
|
||||
关闭会议:
|
||||
订阅
|
||||
取消订阅
|
||||
暂停推流
|
||||
恢复推流
|
||||
掉线重连
|
||||
|
||||
## 信令
|
||||
|
||||
### 信息
|
||||
|
||||
IP
|
||||
MAC
|
||||
信号
|
||||
电量
|
||||
通话状态
|
||||
录制状态
|
||||
|功能|描述|
|
||||
|:--|:--|
|
||||
|注册|终端注册(同步信息)|
|
||||
|关闭|终端关闭(注销)|
|
||||
|心跳|终端心跳|
|
||||
|进入会议|没有会议自动创建|
|
||||
|离开会议|离开会议|
|
||||
|关闭会议|关闭会议(所有人员离开)|
|
||||
|邀请终端|会议邀请终端|
|
||||
|踢出终端|会议踢出终端|
|
||||
|推流|控制终端推流|
|
||||
|暂停推流|控制终端暂停推流|
|
||||
|订阅(分流)|控制终端暂停推流|
|
||||
|暂停订阅(分流)|控制终端暂停推流|
|
||||
|
||||
## 直播
|
||||
|
||||
终端推流到服务端,由服务端分流。
|
||||
|
||||
## 会议
|
||||
|
||||
### Mesh
|
||||
|
||||
流媒体点对点连接,不经过服务端。
|
||||
|
||||
### MCU
|
||||
|
||||
终端推流到服务端,由服务端分流并且混音。
|
||||
|
||||
### SFU
|
||||
|
||||
终端推流到服务端,由服务端分流没有混音。
|
||||
|
||||
## 证书
|
||||
|
||||
```
|
||||
|
||||
6
pom.xml
6
pom.xml
@@ -38,6 +38,7 @@
|
||||
<module>taoyao-nat</module>
|
||||
<module>taoyao-boot</module>
|
||||
<module>taoyao-live</module>
|
||||
<module>taoyao-test</module>
|
||||
<module>taoyao-media</module>
|
||||
<module>taoyao-signal</module>
|
||||
<module>taoyao-webrtc</module>
|
||||
@@ -117,6 +118,11 @@
|
||||
<artifactId>taoyao-live</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-media</artifactId>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.acgist.taoyao.boot.config;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -50,6 +51,7 @@ import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import com.acgist.taoyao.boot.controller.TaoyaoControllerAdvice;
|
||||
import com.acgist.taoyao.boot.controller.TaoyaoErrorController;
|
||||
import com.acgist.taoyao.boot.interceptor.SecurityInterceptor;
|
||||
import com.acgist.taoyao.boot.interceptor.SlowInterceptor;
|
||||
import com.acgist.taoyao.boot.model.MessageCode;
|
||||
import com.acgist.taoyao.boot.service.IdService;
|
||||
import com.acgist.taoyao.boot.service.impl.IdServiceImpl;
|
||||
@@ -73,7 +75,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
@EnableConfigurationProperties({ TaoyaoProperties.class, WebrtcProperties.class, SecurityProperties.class })
|
||||
@EnableConfigurationProperties({ IdProperties.class, TaoyaoProperties.class, WebrtcProperties.class, SecurityProperties.class })
|
||||
public class BootAutoConfiguration {
|
||||
|
||||
@Value("${spring.application.name:taoyao}")
|
||||
@@ -119,6 +121,12 @@ public class BootAutoConfiguration {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SlowInterceptor slowInterceptor() {
|
||||
return new SlowInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityInterceptor securityInterceptor() {
|
||||
@@ -139,15 +147,15 @@ public class BootAutoConfiguration {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
final var runtime = Runtime.getRuntime();
|
||||
final var runtimeMXBean = ManagementFactory.getRuntimeMXBean();
|
||||
final Runtime runtime = Runtime.getRuntime();
|
||||
final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
|
||||
final String freeMemory = FileUtils.formatSize(runtime.freeMemory());
|
||||
final String totalMemory = FileUtils.formatSize(runtime.totalMemory());
|
||||
final String maxMemory = FileUtils.formatSize(runtime.maxMemory());
|
||||
log.info("操作系统名称:{}", System.getProperty("os.name"));
|
||||
log.info("操作系统架构:{}", System.getProperty("os.arch"));
|
||||
log.info("操作系统版本:{}", System.getProperty("os.version"));
|
||||
log.info("操作系统可用处理器数量:{}", runtime.availableProcessors());
|
||||
log.info("可用的处理器数量:{}", runtime.availableProcessors());
|
||||
log.info("Java版本:{}", System.getProperty("java.version"));
|
||||
log.info("Java主目录:{}", System.getProperty("java.home"));
|
||||
log.info("Java库目录:{}", System.getProperty("java.library.path"));
|
||||
@@ -184,8 +192,8 @@ public class BootAutoConfiguration {
|
||||
ErrorUtils.register(MessageCode.CODE_3400, MethodArgumentNotValidException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3500, ConversionNotSupportedException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3500, HttpMessageNotWritableException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3415, HttpMediaTypeNotSupportedException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3400, MissingServletRequestPartException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3415, HttpMediaTypeNotSupportedException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3406, HttpMediaTypeNotAcceptableException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3405, HttpRequestMethodNotSupportedException.class);
|
||||
ErrorUtils.register(MessageCode.CODE_3400, MissingServletRequestParameterException.class);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.acgist.taoyao.boot.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* ID配置
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "taoyao.id")
|
||||
public class IdProperties {
|
||||
|
||||
/**
|
||||
* 机器序号
|
||||
*/
|
||||
private Integer sn;
|
||||
/**
|
||||
* 最大序号
|
||||
*/
|
||||
private Integer maxIndex;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.acgist.taoyao.boot.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springdoc.core.GroupedOpenApi;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -9,8 +11,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.ExternalDocumentation;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
@@ -61,24 +63,33 @@ public class OpenApiAutoConfiguration {
|
||||
public OpenAPI openAPI() {
|
||||
return new OpenAPI()
|
||||
.info(this.buildInfo())
|
||||
.externalDocs(this.buildExternalDocumentation())
|
||||
.addSecurityItem(this.buildSecurityRequirement())
|
||||
.security(this.buildSecurity())
|
||||
.components(this.buildComponents());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 文档基本信息
|
||||
* @return 基本信息
|
||||
*/
|
||||
private Info buildInfo() {
|
||||
return new Info()
|
||||
.contact(this.buildContact())
|
||||
.license(this.buildLicense())
|
||||
.title(this.taoyaoProperties.getName())
|
||||
.version(this.taoyaoProperties.getVersion())
|
||||
.description(this.taoyaoProperties.getDescription())
|
||||
.license(this.buildLicense());
|
||||
.description(this.taoyaoProperties.getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 授权协议信息
|
||||
* @return 联系方式
|
||||
*/
|
||||
private Contact buildContact() {
|
||||
return new Contact()
|
||||
.url(this.taoyaoProperties.getUrl())
|
||||
.name(this.taoyaoProperties.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 开源信息
|
||||
*/
|
||||
private License buildLicense() {
|
||||
return new License()
|
||||
@@ -86,21 +97,11 @@ public class OpenApiAutoConfiguration {
|
||||
.url("https://www.apache.org/licenses/LICENSE-2.0.html");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 外部文档信息
|
||||
*/
|
||||
private ExternalDocumentation buildExternalDocumentation() {
|
||||
return new ExternalDocumentation()
|
||||
.description(this.taoyaoProperties.getDescription())
|
||||
.url(this.taoyaoProperties.getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 授权
|
||||
*/
|
||||
private SecurityRequirement buildSecurityRequirement() {
|
||||
return new SecurityRequirement()
|
||||
.addList(SecurityProperties.BASIC);
|
||||
private List<SecurityRequirement> buildSecurity() {
|
||||
return List.of(new SecurityRequirement().addList(SecurityProperties.BASIC));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,5 +36,4 @@ public class TaoyaoProperties {
|
||||
*/
|
||||
private String description;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package com.acgist.taoyao.boot.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import com.acgist.taoyao.boot.interceptor.SecurityInterceptor;
|
||||
import com.acgist.taoyao.boot.interceptor.InterceptorAdapter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -19,12 +20,17 @@ import lombok.extern.slf4j.Slf4j;
|
||||
public class WebMvcConfigurerAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private SecurityInterceptor securityInterceptor;
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
log.info("加载拦截器:securityInterceptor");
|
||||
registry.addInterceptor(this.securityInterceptor).addPathPatterns("/**");
|
||||
this.context.getBeansOfType(InterceptorAdapter.class).entrySet().stream()
|
||||
.sorted((a, z) -> a.getValue().compareTo(z.getValue()))
|
||||
.forEach(entry -> {
|
||||
final InterceptorAdapter value = entry.getValue();
|
||||
log.info("加载拦截器:{}-{}", entry.getKey(), value.name());
|
||||
registry.addInterceptor(value).addPathPatterns(value.pathPattern());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import lombok.Setter;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(name = "WebRTC配置")
|
||||
@Schema(title = "WebRTC配置", description = "WebRTC配置")
|
||||
@ConfigurationProperties(prefix = "taoyao.webrtc")
|
||||
public class WebrtcProperties {
|
||||
|
||||
@@ -42,15 +42,17 @@ public class WebrtcProperties {
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
@Schema(name = "架构类型", description = "WebRTC架构类型")
|
||||
@Schema(title = "架构类型", description = "WebRTC架构类型")
|
||||
private Type type;
|
||||
/**
|
||||
* stun服务器
|
||||
*/
|
||||
@Schema(title = "stun服务器", description = "stun服务器")
|
||||
private String[] stun;
|
||||
/**
|
||||
* turn服务器
|
||||
*/
|
||||
@Schema(title = "turn服务器", description = "turn服务器")
|
||||
private String[] turn;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.acgist.taoyao.boot.interceptor;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* 拦截器适配器
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public abstract class InterceptorAdapter implements Ordered, HandlerInterceptor, Comparable<InterceptorAdapter> {
|
||||
|
||||
/**
|
||||
* @return 名称
|
||||
*/
|
||||
public abstract String name();
|
||||
|
||||
/**
|
||||
* @return 拦截地址
|
||||
*/
|
||||
public abstract String[] pathPattern();
|
||||
|
||||
@Override
|
||||
public int compareTo(InterceptorAdapter o) {
|
||||
return Integer.compare(this.getOrder(), o.getOrder());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import com.acgist.taoyao.boot.config.SecurityProperties;
|
||||
|
||||
@@ -22,20 +21,29 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
public class SecurityInterceptor implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private ThreadLocal<Long> local = new ThreadLocal<>();
|
||||
public class SecurityInterceptor extends InterceptorAdapter {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "安全拦截";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] pathPattern() {
|
||||
return new String[] { "/**" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Integer.MIN_VALUE + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
if(this.permit(request) || this.authorization(request)) {
|
||||
this.local.set(System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
@@ -85,14 +93,4 @@ public class SecurityInterceptor implements HandlerInterceptor {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
final long duration;
|
||||
final Long last = this.local.get();
|
||||
if(last != null && (duration = System.currentTimeMillis() - last) > 1000) {
|
||||
log.info("执行时间过慢:{}-{}", request.getRequestURI(), duration);
|
||||
}
|
||||
this.local.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.acgist.taoyao.boot.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.acgist.taoyao.boot.config.TaoyaoProperties;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 过慢请求统计拦截
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
public class SlowInterceptor extends InterceptorAdapter {
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private ThreadLocal<Long> local = new ThreadLocal<>();
|
||||
|
||||
@Autowired
|
||||
private TaoyaoProperties taoyaoProperties;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "过慢请求统计拦截";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] pathPattern() {
|
||||
return new String[] { "/**" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
this.local.set(System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {
|
||||
final long duration;
|
||||
final Long last = this.local.get();
|
||||
if(last != null && (duration = System.currentTimeMillis() - last) > this.taoyaoProperties.getTimeout()) {
|
||||
log.info("请求执行时间过慢:{}-{}", request.getRequestURI(), duration);
|
||||
}
|
||||
this.local.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.acgist.taoyao.boot.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
@@ -9,12 +10,13 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 信令头部
|
||||
* 请求响应头部
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema( title = "请求响应头部", description = "请求响应头部")
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -23,20 +25,24 @@ public class Header implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 信令版本
|
||||
* 请求响应版本
|
||||
*/
|
||||
@Schema(title = "请求响应版本", description = "请求响应版本")
|
||||
private String v;
|
||||
/**
|
||||
* 请求标识
|
||||
* 请求响应标识
|
||||
*/
|
||||
@Schema(title = "请求响应标识", description = "请求响应标识")
|
||||
private Long id;
|
||||
/**
|
||||
* 终端标识
|
||||
*/
|
||||
@Schema(title = "终端标识", description = "终端标识")
|
||||
private String sn;
|
||||
/**
|
||||
* 协议标识
|
||||
*/
|
||||
@Schema(title = "协议标识", description = "协议标识")
|
||||
private Integer pid;
|
||||
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
* 请求响应消息
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(name = "消息", description = "请求响应消息")
|
||||
@Schema(title = "请求响应消息", description = "请求响应消息")
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -31,22 +31,22 @@ public class Message implements Serializable {
|
||||
/**
|
||||
* 响应编码
|
||||
*/
|
||||
@Schema(name = "响应编码", description = "响应消息标识响应状态")
|
||||
@Schema(title = "响应编码", description = "响应消息标识响应状态")
|
||||
private String code;
|
||||
/**
|
||||
* 响应描述
|
||||
*/
|
||||
@Schema(name = "响应描述", description = "响应消息描述响应编码")
|
||||
@Schema(title = "响应描述", description = "响应消息描述响应编码")
|
||||
private String message;
|
||||
/**
|
||||
* 请求响应头部
|
||||
*/
|
||||
@Schema(name = "请求响应头部", description = "请求响应头部")
|
||||
@Schema(title = "请求响应头部", description = "请求响应头部")
|
||||
private Header header;
|
||||
/**
|
||||
* 请求响应主体
|
||||
*/
|
||||
@Schema(name = "请求响应主体", description = "请求响应主体")
|
||||
@Schema(title = "请求响应主体", description = "请求响应主体")
|
||||
private Object body;
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,10 +5,10 @@ import lombok.Getter;
|
||||
/**
|
||||
* 状态编码
|
||||
*
|
||||
* 1xxx=前置错误:前置校验错误(数据校验)
|
||||
* 2xxx=内部错误:服务内部错误
|
||||
* 1xxx=前置错误:数据校验
|
||||
* 2xxx=内部错误
|
||||
* 3xxx=请求错误:HTTP错误
|
||||
* 9999=未知错误:没有适配异常
|
||||
* 9999=未知错误
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@@ -41,7 +41,7 @@ public enum MessageCode {
|
||||
CODE_9999("9999", 500, "未知错误");
|
||||
|
||||
/**
|
||||
* HTTP状态编码头部
|
||||
* HTTP状态编码前缀
|
||||
*/
|
||||
public static final String HTTP_STATUS = "3";
|
||||
|
||||
@@ -54,7 +54,7 @@ public enum MessageCode {
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态信息
|
||||
* 状态描述
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
@@ -85,14 +85,7 @@ public enum MessageCode {
|
||||
* @return 状态编码
|
||||
*/
|
||||
public static final MessageCode of(Integer status) {
|
||||
final String code = HTTP_STATUS + status;
|
||||
final MessageCode[] values = MessageCode.values();
|
||||
for (MessageCode value : values) {
|
||||
if (value.code.equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return of(String.valueOf(status));
|
||||
return of(HTTP_STATUS + status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package com.acgist.taoyao.boot.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 状态编码异常
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
public class MessageCodeException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -20,104 +22,59 @@ public class MessageCodeException extends RuntimeException {
|
||||
private final MessageCode code;
|
||||
|
||||
/**
|
||||
* @param messages 错误消息
|
||||
* @param message 错误消息
|
||||
*
|
||||
* @return 状态编码异常
|
||||
*/
|
||||
public static final MessageCodeException of(Object ... messages) {
|
||||
return of(null, MessageCode.CODE_9999, messages);
|
||||
public static final MessageCodeException of(String message) {
|
||||
return of(null, null, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param t 异常
|
||||
* @param messages 错误消息
|
||||
* @param message 错误消息
|
||||
*
|
||||
* @return 状态编码异常
|
||||
*/
|
||||
public static final MessageCodeException of(Throwable t, Object ... messages) {
|
||||
return of(t, MessageCode.CODE_9999, messages);
|
||||
public static final MessageCodeException of(Throwable t, String message) {
|
||||
return of(t, null, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code 状态编码
|
||||
* @param messages 错误消息
|
||||
* @param message 错误消息
|
||||
*
|
||||
* @return 状态编码异常
|
||||
*/
|
||||
public static final MessageCodeException of(MessageCode code, Object ... messages) {
|
||||
return of(null, code, messages);
|
||||
public static final MessageCodeException of(MessageCode code, String message) {
|
||||
return of(null, code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param t 异常
|
||||
* @param code 状态编码
|
||||
* @param messages 错误消息
|
||||
* @param message 错误消息
|
||||
*
|
||||
* @return 状态编码异常
|
||||
*/
|
||||
public static final MessageCodeException of(Throwable t, MessageCode code, Object ... messages) {
|
||||
final String message;
|
||||
if(ArrayUtils.isEmpty(messages)) {
|
||||
message = Objects.isNull(t) ? code.getMessage() : t.getMessage();
|
||||
} else {
|
||||
// 拼接错误描述
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (Object value : messages) {
|
||||
builder.append(value);
|
||||
}
|
||||
message = builder.toString();
|
||||
public static final MessageCodeException of(Throwable t, MessageCode code, String message) {
|
||||
if(code == null) {
|
||||
code = MessageCode.CODE_9999;
|
||||
}
|
||||
return new MessageCodeException(code, message, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code 状态编码
|
||||
*/
|
||||
public MessageCodeException(MessageCode code) {
|
||||
this(code, code.getMessage());
|
||||
if(StringUtils.isEmpty(message)) {
|
||||
message = Objects.isNull(t) ? code.getMessage() : t.getMessage();
|
||||
}
|
||||
return new MessageCodeException(t, code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param t 异常
|
||||
* @param code 状态编码
|
||||
* @param message 错误消息
|
||||
*/
|
||||
public MessageCodeException(MessageCode code, String message) {
|
||||
this(code, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code 状态编码
|
||||
* @param t 异常
|
||||
*/
|
||||
public MessageCodeException(MessageCode code, Throwable t) {
|
||||
this(code, Objects.isNull(t) ? code.getMessage() : t.getMessage(), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code 状态编码
|
||||
* @param message 错误消息
|
||||
* @param t 异常
|
||||
*/
|
||||
public MessageCodeException(MessageCode code, String message, Throwable t) {
|
||||
public MessageCodeException(Throwable t, MessageCode code, String message) {
|
||||
super(message, t);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 状态编码
|
||||
*/
|
||||
public MessageCode getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
final String message = super.getMessage();
|
||||
if (StringUtils.isEmpty(message)) {
|
||||
return this.code.getMessage();
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
import com.acgist.taoyao.boot.utils.JSONUtils;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -16,6 +17,7 @@ import lombok.Setter;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(title = "模型", description = "模型")
|
||||
@EqualsAndHashCode(callSuper = false, of = "id")
|
||||
public abstract class Model implements Cloneable, Serializable {
|
||||
|
||||
@@ -24,14 +26,17 @@ public abstract class Model implements Cloneable, Serializable {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Schema(title = "标识", description = "标识")
|
||||
private Long id;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(title = "创建时间", description = "创建时间")
|
||||
private LocalDateTime createDate;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@Schema(title = "修改时间", description = "修改时间")
|
||||
private LocalDateTime modifyDate;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.acgist.taoyao.boot.service;
|
||||
|
||||
/**
|
||||
* ID
|
||||
* ID生成器
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
|
||||
@@ -2,30 +2,25 @@ package com.acgist.taoyao.boot.service.impl;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.acgist.taoyao.boot.config.IdProperties;
|
||||
import com.acgist.taoyao.boot.service.IdService;
|
||||
|
||||
public class IdServiceImpl implements IdService {
|
||||
|
||||
/**
|
||||
* 机器序号
|
||||
*/
|
||||
@Value("${taoyao.sn:0}")
|
||||
private int sn = 9;
|
||||
/**
|
||||
* 当前索引
|
||||
*/
|
||||
private int index;
|
||||
/**
|
||||
* 最大索引
|
||||
*/
|
||||
private static final int MAX_INDEX = 999999;
|
||||
|
||||
@Autowired
|
||||
private IdProperties idProperties;
|
||||
|
||||
@Override
|
||||
public long id() {
|
||||
synchronized (this) {
|
||||
if (++this.index > MAX_INDEX) {
|
||||
if (++this.index > this.idProperties.getMaxIndex()) {
|
||||
this.index = 0;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +33,7 @@ public class IdServiceImpl implements IdService {
|
||||
1000000000L * time.getMinute() +
|
||||
10000000L * time.getSecond() +
|
||||
// 机器序号一位
|
||||
1000000L * this.sn +
|
||||
1000000L * this.idProperties.getSn() +
|
||||
// 每秒并发数量
|
||||
this.index;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,8 @@ public final class ErrorUtils {
|
||||
请求参数:{}
|
||||
请求方法:{}
|
||||
错误信息:{}
|
||||
""", path, query, method, message, globalError);
|
||||
响应状态:{}
|
||||
""", path, query, method, message, status, globalError);
|
||||
} else {
|
||||
log.warn("""
|
||||
请求错误
|
||||
@@ -133,8 +134,9 @@ public final class ErrorUtils {
|
||||
请求参数:{}
|
||||
请求方法:{}
|
||||
错误信息:{}
|
||||
响应状态:{}
|
||||
原始信息:{}
|
||||
""", path, query, method, message, globalError);
|
||||
""", path, query, method, message, status, globalError);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public final class JSONUtils {
|
||||
try {
|
||||
return MAPPER.writeValueAsString(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw MessageCodeException.of(e, "Java转JSON失败:", object);
|
||||
throw MessageCodeException.of(e, "Java转JSON失败:" + object);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public final class JSONUtils {
|
||||
return MAPPER.readValue(json, new TypeReference<T>() {
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw MessageCodeException.of(e, "JSON转Java失败:", json);
|
||||
throw MessageCodeException.of(e, "JSON转Java失败:" + json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public final class JSONUtils {
|
||||
try {
|
||||
return MAPPER.readValue(json, clazz);
|
||||
} catch (IOException e) {
|
||||
throw MessageCodeException.of(e, "JSON转Java失败:", json);
|
||||
throw MessageCodeException.of(e, "JSON转Java失败:" + json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public final class JSONUtils {
|
||||
return MAPPER.readValue(json, new TypeReference<Map<K, V>>() {
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw MessageCodeException.of(e, "JSON转Map失败:", json);
|
||||
throw MessageCodeException.of(e, "JSON转Map失败:" + json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ public final class JSONUtils {
|
||||
return MAPPER.readValue(json, new TypeReference<List<T>>() {
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw MessageCodeException.of(e, "JSON转List失败:", json);
|
||||
throw MessageCodeException.of(e, "JSON转List失败:" + json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ public final class JSONUtils {
|
||||
return MAPPER.readValue(json, new TypeReference<List<T>>() {
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw MessageCodeException.of(e, "JSON转List失败:", json);
|
||||
throw MessageCodeException.of(e, "JSON转List失败:" + json);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,12 @@
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
taoyao:
|
||||
security:
|
||||
permit: /favicon.ico,/error
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
taoyao:
|
||||
security:
|
||||
permit: /favicon.ico,/error
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
server:
|
||||
port: 8888
|
||||
http2:
|
||||
enabled: true
|
||||
ssl:
|
||||
key-alias: taoyao
|
||||
key-store: classpath:taoyao.jks
|
||||
key-store-password: 123456
|
||||
key-password: 123456
|
||||
tomcat:
|
||||
thread:
|
||||
max: 128
|
||||
min-spare: 4
|
||||
remoteip:
|
||||
host-header: X-Forwarded-Host
|
||||
port-header: X-Forwarded-Port
|
||||
@@ -16,8 +21,6 @@ spring:
|
||||
active: dev
|
||||
application:
|
||||
name: taoyao-server
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 256MB
|
||||
@@ -47,6 +50,9 @@ taoyao:
|
||||
timeout: 5000
|
||||
version: 1.0.0
|
||||
description: WebRTC信令服务
|
||||
id:
|
||||
sn: 0
|
||||
max-index: 999999
|
||||
webrtc:
|
||||
type: SFU
|
||||
stun:
|
||||
@@ -56,10 +62,12 @@ taoyao:
|
||||
- stun:stun4.l.google.com:19302
|
||||
- stun:stun.stunprotocol.org:3478
|
||||
turn:
|
||||
record:
|
||||
storage: /data/record
|
||||
security:
|
||||
realm: taoyao
|
||||
permit: /v3/api-docs/,/swagger-ui/,/error
|
||||
username:
|
||||
password:
|
||||
permit: /v3/api-docs/,/swagger-ui/,/favicon.ico,/error
|
||||
username: taoyao
|
||||
password: taoyao
|
||||
scheduled:
|
||||
session: 0 * * * * ?
|
||||
|
||||
BIN
taoyao-server/src/main/resources/static/favicon.ico
Normal file
BIN
taoyao-server/src/main/resources/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,35 @@
|
||||
package com.acgist.taoyao.boot.service;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.acgist.taoyao.main.TaoyaoApplication;
|
||||
import com.acgist.taoyao.test.annotation.TaoyaoTest;
|
||||
import com.acgist.taoyao.test.annotation.CostedTest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@TaoyaoTest(classes = TaoyaoApplication.class)
|
||||
//@SpringBootTest(classes = TaoyaoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
class IdServiceTest {
|
||||
|
||||
@Autowired
|
||||
private IdService idService;
|
||||
|
||||
@Test
|
||||
// @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
|
||||
// @Rollback()
|
||||
// @RepeatedTest(10)
|
||||
void testId() {
|
||||
final long id = this.idService.id();
|
||||
log.info("生成ID:{}", id);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CostedTest(count = 100000, thread = 10)
|
||||
void testIdCosted() {
|
||||
this.idService.id();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,12 +27,14 @@ public class RegisterListener extends ApplicationListenerAdapter<RegisterEvent>
|
||||
@Override
|
||||
public void onApplicationEvent(RegisterEvent event) {
|
||||
final ClientSession session = event.getSession();
|
||||
if(!session.authorized()) {
|
||||
if (!session.authorized()) {
|
||||
return;
|
||||
}
|
||||
final Message message = this.onlineProtocol.build();
|
||||
message.setBody(Map.of("sn", session.sn()));
|
||||
this.clientSessionManager.broadcast(message);
|
||||
this.clientSessionManager.broadcast(session.sn(), message);
|
||||
// TODO:ip等等
|
||||
// TODO:重新注册上来需要掉线重连
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.acgist.taoyao.signal.media;
|
||||
|
||||
/**
|
||||
* 终端媒体操作
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public interface ClientMediaHandler {
|
||||
|
||||
/**
|
||||
* 打开
|
||||
*/
|
||||
void open(String id);
|
||||
|
||||
/**
|
||||
* 暂停
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* 恢复
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* 关闭
|
||||
*/
|
||||
void close();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.acgist.taoyao.signal.media;
|
||||
|
||||
/**
|
||||
* 终端推流
|
||||
*/
|
||||
public class ClientMediaPublisher {
|
||||
|
||||
public void publish(String id) {
|
||||
}
|
||||
|
||||
public void unpublish(String id) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.acgist.taoyao.signal.media;
|
||||
|
||||
/**
|
||||
* 终端媒体订阅者(终端拉流)
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class ClientMediaSubscriber implements ClientMediaHandler {
|
||||
|
||||
public void subscribe(String id) {
|
||||
}
|
||||
|
||||
public void unsubscribe(String id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(String id) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.acgist.taoyao.signal.media.router;
|
||||
|
||||
/**
|
||||
* 终端媒体流路由(推流->拉流)
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public interface ClientMediaStreamRouter {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.acgist.taoyao.signal.media.router;
|
||||
|
||||
import com.acgist.taoyao.signal.media.stream.ClientMediaStream;
|
||||
|
||||
public class ClientMediaStreamRouterAdapter {
|
||||
|
||||
ClientMediaStream source;
|
||||
ClientMediaStream target;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.acgist.taoyao.signal.media.stream;
|
||||
|
||||
/**
|
||||
* 终端媒体流
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public interface ClientMediaStream {
|
||||
|
||||
/**
|
||||
* 终端媒体类型
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public enum Type {
|
||||
|
||||
/**
|
||||
* 音频
|
||||
*/
|
||||
AUDIO,
|
||||
/**
|
||||
* 视频
|
||||
*/
|
||||
VIDEO;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 终端媒体流状态
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public enum Status {
|
||||
|
||||
/**
|
||||
* 没有激活
|
||||
*/
|
||||
IDLE,
|
||||
/**
|
||||
* 已经激活
|
||||
*/
|
||||
BUSY,
|
||||
/**
|
||||
* 已经暂停
|
||||
*/
|
||||
PAUSE,
|
||||
/**
|
||||
* 已经关闭
|
||||
*/
|
||||
CLOSE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 终端媒体流ID
|
||||
*/
|
||||
String id();
|
||||
|
||||
/**
|
||||
* 打开终端媒体流
|
||||
*/
|
||||
void open();
|
||||
|
||||
/**
|
||||
* 暂停终端媒体流
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* 恢复终端媒体流
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* 关闭终端媒体流
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @return 终端媒体流类型
|
||||
*/
|
||||
Type type();
|
||||
|
||||
/**
|
||||
* @return 终端媒体流状态
|
||||
*/
|
||||
Status status();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.acgist.taoyao.signal.media.stream;
|
||||
|
||||
/**
|
||||
* 终端媒体流适配器
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public abstract class ClientMediaStreamAdapter<T> implements ClientMediaStream {
|
||||
|
||||
/**
|
||||
* 媒体标识
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 真实流
|
||||
*/
|
||||
protected T stream;
|
||||
|
||||
}
|
||||
@@ -60,6 +60,7 @@ public class ProtocolManager {
|
||||
* @param instance 会话实例
|
||||
*/
|
||||
public void execute(String message, AutoCloseable instance) {
|
||||
log.debug("执行信令消息:{}", message);
|
||||
if(StringUtils.isEmpty(message)) {
|
||||
log.warn("消息为空:{}", message);
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.acgist.taoyao.signal.session;
|
||||
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.media.ClientMediaPublisher;
|
||||
import com.acgist.taoyao.signal.media.ClientMediaSubscriber;
|
||||
|
||||
/**
|
||||
* 会话
|
||||
@@ -16,6 +18,21 @@ public interface ClientSession extends AutoCloseable {
|
||||
*/
|
||||
String sn();
|
||||
|
||||
/**
|
||||
* @return 终端状态
|
||||
*/
|
||||
ClientSessionStatus status();
|
||||
|
||||
/**
|
||||
* @return 终端媒体发布者
|
||||
*/
|
||||
ClientMediaPublisher publisher();
|
||||
|
||||
/**
|
||||
* @return 终端媒体订阅者
|
||||
*/
|
||||
ClientMediaSubscriber subscriber();
|
||||
|
||||
/**
|
||||
* 推送消息
|
||||
*
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.acgist.taoyao.signal.session;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.acgist.taoyao.signal.media.ClientMediaPublisher;
|
||||
import com.acgist.taoyao.signal.media.ClientMediaSubscriber;
|
||||
|
||||
/**
|
||||
* 会话适配器
|
||||
*
|
||||
@@ -25,11 +28,26 @@ public abstract class ClientSessionAdapter<T extends AutoCloseable> implements C
|
||||
* 是否授权
|
||||
*/
|
||||
protected boolean authorized;
|
||||
/**
|
||||
* 终端状态
|
||||
*/
|
||||
protected ClientSessionStatus status;
|
||||
/**
|
||||
* 终端媒体发布者
|
||||
*/
|
||||
protected ClientMediaPublisher publisher;
|
||||
/**
|
||||
* 终端媒体订阅者
|
||||
*/
|
||||
protected ClientMediaSubscriber subscriber;
|
||||
|
||||
protected ClientSessionAdapter(T instance) {
|
||||
this.time = System.currentTimeMillis();
|
||||
this.instance = instance;
|
||||
this.authorized = false;
|
||||
this.status = new ClientSessionStatus();
|
||||
this.publisher = new ClientMediaPublisher();
|
||||
this.subscriber = new ClientMediaSubscriber();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,6 +55,21 @@ public abstract class ClientSessionAdapter<T extends AutoCloseable> implements C
|
||||
return this.sn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSessionStatus status() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientMediaPublisher publisher() {
|
||||
return this.publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientMediaSubscriber subscriber() {
|
||||
return this.subscriber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean timeout(long timeout) {
|
||||
return !(this.authorized && System.currentTimeMillis() - this.time <= timeout);
|
||||
|
||||
@@ -101,9 +101,8 @@ public class ClientSessionManager {
|
||||
try {
|
||||
if(session != null) {
|
||||
session.close();
|
||||
} else {
|
||||
instance.close();
|
||||
}
|
||||
instance.close();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭会话异常", e);
|
||||
} finally {
|
||||
|
||||
@@ -1,12 +1,36 @@
|
||||
package com.acgist.taoyao.signal.session;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 终端状态
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ClientSessionStatus {
|
||||
|
||||
|
||||
/**
|
||||
* 终端标识
|
||||
*/
|
||||
private String sn;
|
||||
/**
|
||||
* IP
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* MAC
|
||||
*/
|
||||
private String mac;
|
||||
/**
|
||||
* 信号强度(0~100)
|
||||
*/
|
||||
private Integer signal = 0;
|
||||
/**
|
||||
* 电量(0~100)
|
||||
*/
|
||||
private Integer battery = 0;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.acgist.taoyao.signal.session.socket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.session.ClientSessionAdapter;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Socket会话
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Setter
|
||||
public class SocketSession extends ClientSessionAdapter<Socket> {
|
||||
|
||||
public SocketSession(Socket instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(Message message) {
|
||||
try {
|
||||
this.instance.getOutputStream().write(message.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
log.error("Socket发送消息异常:{}", message, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.acgist.taoyao.signal.session.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.websocket.Session;
|
||||
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
@@ -28,8 +26,12 @@ public class WebSocketSession extends ClientSessionAdapter<Session> {
|
||||
@Override
|
||||
public void push(Message message) {
|
||||
try {
|
||||
this.instance.getBasicRemote().sendText(message.toString());
|
||||
} catch (IOException e) {
|
||||
if(this.instance.isOpen()) {
|
||||
this.instance.getBasicRemote().sendText(message.toString());
|
||||
} else {
|
||||
log.error("会话已经关闭:{}", this.instance);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("WebSocket发送消息异常:{}", message, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.acgist.taoyao.signal.session.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.websocket.OnClose;
|
||||
import javax.websocket.OnError;
|
||||
import javax.websocket.OnMessage;
|
||||
@@ -70,8 +68,12 @@ public class WebSocketSignal {
|
||||
*/
|
||||
private void push(Session session, Message message) {
|
||||
try {
|
||||
session.getBasicRemote().sendText(message.toString());
|
||||
} catch (IOException e) {
|
||||
if(session.isOpen()) {
|
||||
session.getBasicRemote().sendText(message.toString());
|
||||
} else {
|
||||
log.error("会话已经关闭:{}", session);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("推送消息异常:{}", message, e);
|
||||
}
|
||||
}
|
||||
|
||||
27
taoyao-test/pom.xml
Normal file
27
taoyao-test/pom.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>taoyao-test</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>taoyao-test</name>
|
||||
<description>测试:测试工具</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.acgist.taoyao.test.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 多线程测试
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface CostedTest {
|
||||
|
||||
/**
|
||||
* @return 执行次数
|
||||
*/
|
||||
int count() default 1;
|
||||
|
||||
/**
|
||||
* @return 线程数量
|
||||
*/
|
||||
int thread() default 1;
|
||||
|
||||
/**
|
||||
* @return 超时时间
|
||||
*/
|
||||
long timeout() default 1000;
|
||||
|
||||
/**
|
||||
* @return 超时时间单位
|
||||
*/
|
||||
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.acgist.taoyao.test.annotation;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 多线程测试监听
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
public class CostedTestTestExecutionListener implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
final CostedTest costedTest = testContext.getTestMethod().getDeclaredAnnotation(CostedTest.class);
|
||||
final int count = costedTest.count();
|
||||
final int thread = costedTest.thread();
|
||||
final long timeout = costedTest.timeout();
|
||||
final TimeUnit timeUnit = costedTest.timeUnit();
|
||||
final long aTime = System.currentTimeMillis();
|
||||
if(thread == 1) {
|
||||
for (int index = 0; index < count; index++) {
|
||||
testContext.getTestMethod().invoke(testContext.getTestInstance());
|
||||
}
|
||||
} else {
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(count);
|
||||
final ExecutorService executor = Executors.newFixedThreadPool(thread);
|
||||
for (int index = 0; index < count; index++) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
testContext.getTestMethod().invoke(testContext.getTestInstance());
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
log.error("多线程测试异常", e);
|
||||
} finally {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
countDownLatch.await(timeout, timeUnit);
|
||||
}
|
||||
final long zTime = System.currentTimeMillis();
|
||||
final long costed = zTime - aTime;
|
||||
log.info("多线程测试消耗时间:{}", costed);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.acgist.taoyao.test.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.TestExecutionListeners.MergeMode;
|
||||
|
||||
/**
|
||||
* 测试启动
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
@TestExecutionListeners(listeners = CostedTestTestExecutionListener.class, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
|
||||
public @interface TaoyaoTest {
|
||||
|
||||
@AliasFor(annotation = SpringBootTest.class)
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user