[*] 日常优化
This commit is contained in:
@@ -2,52 +2,52 @@
|
||||
|
||||
<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>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>taoyao-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>taoyao-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>taoyao-server</name>
|
||||
<description>启动服务</description>
|
||||
<name>taoyao-server</name>
|
||||
<description>启动服务</description>
|
||||
|
||||
<properties>
|
||||
<taoyao.maven.basedir>${project.parent.basedir}</taoyao.maven.basedir>
|
||||
<taoyao.maven.skip.assembly>false</taoyao.maven.skip.assembly>
|
||||
</properties>
|
||||
<properties>
|
||||
<taoyao.maven.basedir>${project.parent.basedir}</taoyao.maven.basedir>
|
||||
<taoyao.maven.skip.assembly>false</taoyao.maven.skip.assembly>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-signal</artifactId>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-signal</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.acgist.taoyao.main.TaoyaoApplication</mainClass>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>./</classpathPrefix>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.acgist.taoyao.main.TaoyaoApplication</mainClass>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>./</classpathPrefix>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -22,29 +22,29 @@ import com.acgist.taoyao.signal.service.impl.SecurityServiceImpl;
|
||||
@AutoConfiguration
|
||||
public class TaoyaoAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SlowInterceptor slowInterceptor(TaoyaoProperties taoyaoProperties) {
|
||||
return new SlowInterceptor(taoyaoProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SlowInterceptor slowInterceptor(TaoyaoProperties taoyaoProperties) {
|
||||
return new SlowInterceptor(taoyaoProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityService securityService(
|
||||
SecurityProperties securityProperties,
|
||||
@Autowired(required = true) SecurityProperties securityProperties,
|
||||
@Autowired(required = false) UsernamePasswordService usernamePasswordService
|
||||
) {
|
||||
return new SecurityServiceImpl(securityProperties, usernamePasswordService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "taoyao.security", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityInterceptor securityInterceptor(
|
||||
SecurityService securityService,
|
||||
SecurityProperties securityProperties
|
||||
) {
|
||||
return new SecurityInterceptor(securityService, securityProperties);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "taoyao.security", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityInterceptor securityInterceptor(
|
||||
SecurityService securityService,
|
||||
SecurityProperties securityProperties
|
||||
) {
|
||||
return new SecurityInterceptor(securityService, securityProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.acgist.taoyao.boot.config.FfmpegProperties;
|
||||
import com.acgist.taoyao.boot.config.RewriteProperties;
|
||||
import com.acgist.taoyao.boot.config.MediaProperties;
|
||||
import com.acgist.taoyao.boot.config.RewriteProperties;
|
||||
import com.acgist.taoyao.boot.config.SocketProperties;
|
||||
import com.acgist.taoyao.boot.config.WebrtcProperties;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
@@ -29,18 +29,18 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor
|
||||
public class ConfigController {
|
||||
|
||||
private final MediaProperties mediaProperties;
|
||||
private final FfmpegProperties ffmpegProperties;
|
||||
private final SocketProperties socketProperties;
|
||||
private final WebrtcProperties webrtcProperties;
|
||||
private final RewriteProperties rewriteProperties;
|
||||
|
||||
private final MediaProperties mediaProperties;
|
||||
private final FfmpegProperties ffmpegProperties;
|
||||
private final SocketProperties socketProperties;
|
||||
private final WebrtcProperties webrtcProperties;
|
||||
private final RewriteProperties rewriteProperties;
|
||||
|
||||
@Operation(summary = "媒体配置", description = "媒体配置")
|
||||
@GetMapping("/media")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = MediaProperties.class)))
|
||||
public Message media() {
|
||||
return Message.success(this.mediaProperties);
|
||||
}
|
||||
@GetMapping("/media")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = MediaProperties.class)))
|
||||
public Message media() {
|
||||
return Message.success(this.mediaProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "FFmpeg配置", description = "FFmpeg配置")
|
||||
@GetMapping("/ffmpeg")
|
||||
@@ -55,19 +55,19 @@ public class ConfigController {
|
||||
public Message socket() {
|
||||
return Message.success(this.socketProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "WebRTC配置", description = "WebRTC配置")
|
||||
@GetMapping("/webrtc")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = WebrtcProperties.class)))
|
||||
public Message webrtc() {
|
||||
return Message.success(this.webrtcProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "地址重写配置", description = "地址重写配置")
|
||||
@GetMapping("/rewrite")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = WebrtcProperties.class)))
|
||||
public Message rewrite() {
|
||||
return Message.success(this.rewriteProperties);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "WebRTC配置", description = "WebRTC配置")
|
||||
@GetMapping("/webrtc")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = WebrtcProperties.class)))
|
||||
public Message webrtc() {
|
||||
return Message.success(this.webrtcProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "地址重写配置", description = "地址重写配置")
|
||||
@GetMapping("/rewrite")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = WebrtcProperties.class)))
|
||||
public Message rewrite() {
|
||||
return Message.success(this.rewriteProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.acgist.taoyao.signal.protocol.control.IControlPhotographProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.IControlServerRecordProtocol;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@@ -34,31 +35,22 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor
|
||||
public class ControlController {
|
||||
|
||||
private final IControlBellProtocol controlBellProtocol;
|
||||
private final IControlPhotographProtocol controlPhotographProtocol;
|
||||
private final IControlConfigAudioProtocol controlConfigAudioProtocol;
|
||||
private final IControlConfigVideoProtocol controlConfigVideoProtocol;
|
||||
private final IControlBellProtocol controlBellProtocol;
|
||||
private final IControlPhotographProtocol controlPhotographProtocol;
|
||||
private final IControlConfigAudioProtocol controlConfigAudioProtocol;
|
||||
private final IControlConfigVideoProtocol controlConfigVideoProtocol;
|
||||
private final IControlClientRecordProtocol controlClientRecordProtocol;
|
||||
private final IControlServerRecordProtocol controlServerRecordProtocol;
|
||||
|
||||
@Operation(summary = "响铃", description = "响铃控制")
|
||||
@GetMapping("/bell/{clientId}")
|
||||
public Message bell(@PathVariable String clientId, @NotNull(message = "没有指定操作状态") Boolean enabled) {
|
||||
public Message bell(
|
||||
@PathVariable String clientId,
|
||||
@NotNull(message = "没有指定操作状态") Boolean enabled
|
||||
) {
|
||||
return this.controlBellProtocol.execute(clientId, enabled);
|
||||
}
|
||||
|
||||
@Operation(summary = "录像", description = "终端录像控制")
|
||||
@GetMapping("/client/record/{clientId}")
|
||||
public Message record(@PathVariable String clientId, @NotNull(message = "没有指定操作状态") Boolean enabled) {
|
||||
return this.controlClientRecordProtocol.execute(clientId, enabled);
|
||||
}
|
||||
|
||||
@Operation(summary = "录像", description = "服务端录像控制")
|
||||
@GetMapping("/server/record/{roomId}/{clientId}")
|
||||
public Message record(@PathVariable String roomId, @PathVariable String clientId, @NotNull(message = "没有指定操作状态") Boolean enabled) {
|
||||
return this.controlServerRecordProtocol.execute(roomId, clientId, enabled);
|
||||
}
|
||||
|
||||
@Operation(summary = "拍照", description = "拍照控制")
|
||||
@GetMapping("/photograph/{clientId}")
|
||||
public Message photograph(@PathVariable String clientId) {
|
||||
@@ -67,14 +59,39 @@ public class ControlController {
|
||||
|
||||
@Operation(summary = "配置音频", description = "配置音频")
|
||||
@GetMapping("/config/audio/{clientId}")
|
||||
public Message configAudio(@PathVariable String clientId, @Valid MediaAudioProperties mediaAudioProperties) {
|
||||
public Message configAudio(
|
||||
@PathVariable String clientId,
|
||||
@Valid @RequestBody MediaAudioProperties mediaAudioProperties
|
||||
) {
|
||||
return this.controlConfigAudioProtocol.execute(clientId, mediaAudioProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "配置视频", description = "配置视频")
|
||||
@GetMapping("/config/video/{clientId}")
|
||||
public Message configVideo(@PathVariable String clientId, @Valid MediaVideoProperties mediaVideoProperties) {
|
||||
public Message configVideo(
|
||||
@PathVariable String clientId,
|
||||
@Valid @RequestBody MediaVideoProperties mediaVideoProperties
|
||||
) {
|
||||
return this.controlConfigVideoProtocol.execute(clientId, mediaVideoProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "录像", description = "终端录像控制")
|
||||
@GetMapping("/client/record/{clientId}")
|
||||
public Message record(
|
||||
@PathVariable String clientId,
|
||||
@NotNull(message = "没有指定操作状态") Boolean enabled
|
||||
) {
|
||||
return this.controlClientRecordProtocol.execute(clientId, enabled);
|
||||
}
|
||||
|
||||
@Operation(summary = "录像", description = "服务端录像控制")
|
||||
@GetMapping("/server/record/{roomId}/{clientId}")
|
||||
public Message record(
|
||||
@PathVariable String roomId,
|
||||
@PathVariable String clientId,
|
||||
@NotNull(message = "没有指定操作状态") Boolean enabled
|
||||
) {
|
||||
return this.controlServerRecordProtocol.execute(roomId, clientId, enabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,14 +25,14 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@RequestMapping("/platform")
|
||||
public class PlatformController {
|
||||
|
||||
private final PlatformRebootProtocol platformRebootProtocol;
|
||||
private final PlatformRebootProtocol platformRebootProtocol;
|
||||
private final PlatformShutdownProtocol platformShutdownProtocol;
|
||||
|
||||
public PlatformController(
|
||||
@Autowired(required = false) PlatformRebootProtocol platformRebootProtocol,
|
||||
@Autowired(required = false) PlatformRebootProtocol platformRebootProtocol,
|
||||
@Autowired(required = false) PlatformShutdownProtocol platformShutdownProtocol
|
||||
) {
|
||||
this.platformRebootProtocol = platformRebootProtocol;
|
||||
this.platformRebootProtocol = platformRebootProtocol;
|
||||
this.platformShutdownProtocol = platformShutdownProtocol;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@RequestMapping("/system")
|
||||
public class SystemController {
|
||||
|
||||
private final SystemRebootProtocol systemRebootProtocol;
|
||||
private final SystemRebootProtocol systemRebootProtocol;
|
||||
private final SystemShutdownProtocol systemShutdownProtocol;
|
||||
|
||||
public SystemController(
|
||||
@Autowired(required = false) SystemRebootProtocol systemRebootProtocol,
|
||||
@Autowired(required = false) SystemRebootProtocol systemRebootProtocol,
|
||||
@Autowired(required = false) SystemShutdownProtocol systemShutdownProtocol
|
||||
) {
|
||||
this.systemRebootProtocol = systemRebootProtocol;
|
||||
this.systemRebootProtocol = systemRebootProtocol;
|
||||
this.systemShutdownProtocol = systemShutdownProtocol;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,96 +24,99 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class SecurityInterceptor extends InterceptorAdapter {
|
||||
|
||||
private final SecurityService securityService;
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 鉴权信息
|
||||
*/
|
||||
private final String authenticate;
|
||||
/**
|
||||
* 地址匹配
|
||||
*/
|
||||
private final AntPathMatcher matcher;
|
||||
|
||||
public SecurityInterceptor(SecurityService securityService, SecurityProperties securityProperties) {
|
||||
private final SecurityService securityService;
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 鉴权信息
|
||||
*/
|
||||
private final String authenticate;
|
||||
/**
|
||||
* 地址匹配
|
||||
*/
|
||||
private final AntPathMatcher matcher;
|
||||
|
||||
public SecurityInterceptor(
|
||||
SecurityService securityService,
|
||||
SecurityProperties securityProperties
|
||||
) {
|
||||
this.securityService = securityService;
|
||||
this.securityProperties = securityProperties;
|
||||
this.authenticate = "Basic Realm=\"" + this.securityProperties.getRealm() + "\"";
|
||||
this.matcher = new AntPathMatcher();
|
||||
log.info("安全拦截密码:{}", securityProperties.getPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "安全拦截器";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] pathPattern() {
|
||||
return new String[] { "/**" };
|
||||
}
|
||||
|
||||
@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)) {
|
||||
return true;
|
||||
}
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("授权失败:{}", request.getRequestURL());
|
||||
}
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, this.authenticate);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request 请求
|
||||
*
|
||||
* @return 是否许可请求
|
||||
*/
|
||||
private boolean permit(HttpServletRequest request) {
|
||||
final String uri = request.getRequestURI();
|
||||
final String[] permit = this.securityProperties.getPermit();
|
||||
if(ArrayUtils.isEmpty(permit)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : permit) {
|
||||
if(this.matcher.match(pattern, uri)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request 请求
|
||||
*
|
||||
* @return 是否授权成功
|
||||
*/
|
||||
private boolean authorization(HttpServletRequest request) {
|
||||
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
if(StringUtils.isEmpty(authorization)) {
|
||||
return false;
|
||||
}
|
||||
int index = authorization.indexOf(' ');
|
||||
if(index < 0) {
|
||||
return false;
|
||||
}
|
||||
authorization = authorization.substring(index + 1).strip();
|
||||
authorization = new String(Base64.getMimeDecoder().decode(authorization));
|
||||
index = authorization.indexOf(':');
|
||||
if(index < 0) {
|
||||
return false;
|
||||
}
|
||||
final String username = authorization.substring(0, index);
|
||||
final String password = authorization.substring(index + 1);
|
||||
return this.securityService.authenticate(username, password);
|
||||
}
|
||||
|
||||
@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)) {
|
||||
return true;
|
||||
}
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("授权失败:{}", request.getRequestURL());
|
||||
}
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, this.authenticate);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request 请求
|
||||
*
|
||||
* @return 是否许可请求
|
||||
*/
|
||||
private boolean permit(HttpServletRequest request) {
|
||||
final String uri = request.getRequestURI();
|
||||
final String[] permit = this.securityProperties.getPermit();
|
||||
if(ArrayUtils.isEmpty(permit)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : permit) {
|
||||
if(this.matcher.match(pattern, uri)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request 请求
|
||||
*
|
||||
* @return 是否授权成功
|
||||
*/
|
||||
private boolean authorization(HttpServletRequest request) {
|
||||
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
if(StringUtils.isEmpty(authorization)) {
|
||||
return false;
|
||||
}
|
||||
int index = authorization.indexOf(' ');
|
||||
if(index < 0) {
|
||||
return false;
|
||||
}
|
||||
authorization = authorization.substring(index + 1).strip();
|
||||
authorization = new String(Base64.getMimeDecoder().decode(authorization));
|
||||
index = authorization.indexOf(':');
|
||||
if(index < 0) {
|
||||
return false;
|
||||
}
|
||||
final String username = authorization.substring(0, index);
|
||||
final String password = authorization.substring(index + 1);
|
||||
return this.securityService.authenticate(username, password);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,48 +14,47 @@ import lombok.extern.slf4j.Slf4j;
|
||||
*/
|
||||
@Slf4j
|
||||
public class SlowInterceptor extends InterceptorAdapter {
|
||||
|
||||
private final TaoyaoProperties taoyaoProperties;
|
||||
|
||||
/**
|
||||
* 请求开始时间
|
||||
*/
|
||||
private final ThreadLocal<Long> local;
|
||||
|
||||
public SlowInterceptor(TaoyaoProperties taoyaoProperties) {
|
||||
|
||||
private final TaoyaoProperties taoyaoProperties;
|
||||
|
||||
/**
|
||||
* 请求开始时间
|
||||
*/
|
||||
private final ThreadLocal<Long> local;
|
||||
|
||||
public SlowInterceptor(TaoyaoProperties taoyaoProperties) {
|
||||
this.taoyaoProperties = taoyaoProperties;
|
||||
this.local = new ThreadLocal<>();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
|
||||
@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 = System.currentTimeMillis() - this.local.get();
|
||||
if(duration > this.taoyaoProperties.getTimeout()) {
|
||||
log.info("请求执行时间过慢:{} - {}", request.getRequestURI(), duration);
|
||||
}
|
||||
this.local.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import org.springframework.context.annotation.ComponentScan;
|
||||
@SpringBootApplication
|
||||
public class TaoyaoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TaoyaoApplication.class, args);
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TaoyaoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>桃夭信令服务</title>
|
||||
<style type="text/css">
|
||||
p{text-align:center;}
|
||||
a{text-decoration:none;}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
<title>桃夭信令服务</title>
|
||||
<style type="text/css">
|
||||
p{text-align:center;}
|
||||
a{text-decoration:none;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p><a href="https://gitee.com/acgist/taoyao">taoyao-signal-server</a></p>
|
||||
<p><a href="https://www.acgist.com">acgist</a></p>
|
||||
<p><a href="https://gitee.com/acgist/taoyao">taoyao-signal-server</a></p>
|
||||
<p><a href="https://www.acgist.com">acgist</a></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -19,29 +19,29 @@ import java.util.concurrent.TimeUnit;
|
||||
@Documented
|
||||
public @interface CostedTest {
|
||||
|
||||
/**
|
||||
* @return 执行次数
|
||||
*/
|
||||
int count() default 1;
|
||||
/**
|
||||
* @return 执行次数
|
||||
*/
|
||||
int count() default 1;
|
||||
|
||||
/**
|
||||
* @return 线程数量
|
||||
*/
|
||||
int thread() default 1;
|
||||
|
||||
/**
|
||||
* @return 超时时间
|
||||
*/
|
||||
long timeout() default 1000L;
|
||||
|
||||
/**
|
||||
* @return 等待资源释放时间
|
||||
*/
|
||||
long waitRelease() default 0L;
|
||||
|
||||
/**
|
||||
* @return 超时时间单位
|
||||
*/
|
||||
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
|
||||
|
||||
/**
|
||||
* @return 线程数量
|
||||
*/
|
||||
int thread() default 1;
|
||||
|
||||
/**
|
||||
* @return 超时时间
|
||||
*/
|
||||
long timeout() default 1000L;
|
||||
|
||||
/**
|
||||
* @return 等待资源释放时间
|
||||
*/
|
||||
long waitRelease() default 0L;
|
||||
|
||||
/**
|
||||
* @return 超时时间单位
|
||||
*/
|
||||
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
|
||||
|
||||
}
|
||||
|
||||
@@ -18,46 +18,46 @@ import lombok.extern.slf4j.Slf4j;
|
||||
*/
|
||||
@Slf4j
|
||||
public class CostedTestTestExecutionListener implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
final CostedTest costedTest = testContext.getTestMethod().getDeclaredAnnotation(CostedTest.class);
|
||||
if(costedTest == null) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
executor.shutdown();
|
||||
}
|
||||
final long zTime = System.currentTimeMillis();
|
||||
final long costed = zTime - aTime;
|
||||
log.info("多线程测试消耗时间:{}", costed);
|
||||
final long waitRelease = costedTest.waitRelease();
|
||||
if(waitRelease > 0) {
|
||||
Thread.sleep(waitRelease);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
final CostedTest costedTest = testContext.getTestMethod().getDeclaredAnnotation(CostedTest.class);
|
||||
if(costedTest == null) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
executor.shutdown();
|
||||
}
|
||||
final long zTime = System.currentTimeMillis();
|
||||
final long costed = zTime - aTime;
|
||||
log.info("多线程测试消耗时间:{}", costed);
|
||||
final long waitRelease = costedTest.waitRelease();
|
||||
if(waitRelease > 0) {
|
||||
Thread.sleep(waitRelease);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.test.context.TestExecutionListeners.MergeMode;
|
||||
@TestExecutionListeners(listeners = CostedTestTestExecutionListener.class, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
|
||||
public @interface TaoyaoTest {
|
||||
|
||||
@AliasFor(annotation = SpringBootTest.class)
|
||||
Class<?>[] classes() default {};
|
||||
@AliasFor(annotation = SpringBootTest.class)
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -21,99 +21,99 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class RtpTest {
|
||||
|
||||
@Test
|
||||
void testSocket() throws Exception {
|
||||
final Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
|
||||
final InputStream inputStream = socket.getInputStream();
|
||||
final OutputStream outputStream = socket.getOutputStream();
|
||||
// 随机密码:https://localhost:8888/config/socket
|
||||
final String secret = "TSFXzB7hcfE=".strip();
|
||||
final Cipher encrypt = CipherUtils.buildCipher(Cipher.ENCRYPT_MODE, Encrypt.DES, secret);
|
||||
final Cipher decrypt = CipherUtils.buildCipher(Cipher.DECRYPT_MODE, Encrypt.DES, secret);
|
||||
// 接收
|
||||
new Thread(() -> {
|
||||
int length = 0;
|
||||
short messageLength = 0;
|
||||
final byte[] bytes = new byte[1024];
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
|
||||
try {
|
||||
while((length = inputStream.read(bytes)) >= 0) {
|
||||
buffer.put(bytes, 0, length);
|
||||
while(buffer.position() > 0) {
|
||||
if(messageLength <= 0) {
|
||||
if(buffer.position() < Short.BYTES) {
|
||||
// 不够消息长度
|
||||
break;
|
||||
} else {
|
||||
buffer.flip();
|
||||
messageLength = buffer.getShort();
|
||||
buffer.compact();
|
||||
if(messageLength > 16 * 1024) {
|
||||
throw MessageCodeException.of("超过最大数据大小:" + messageLength);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(buffer.position() < messageLength) {
|
||||
// 不够消息长度
|
||||
break;
|
||||
} else {
|
||||
final byte[] message = new byte[messageLength];
|
||||
messageLength = 0;
|
||||
buffer.flip();
|
||||
buffer.get(message);
|
||||
buffer.compact();
|
||||
log.debug("收到消息:{}", new String(decrypt.doFinal(message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("读取异常", e);
|
||||
}
|
||||
}).start();
|
||||
// 发送
|
||||
String line = """
|
||||
{
|
||||
"header":{"v":"1.0.0","id":1215293599999001,"signal":"client::register"},
|
||||
"body":{"clientId":"ffmpeg","name":"ffmpeg","clientType":"WEB","battery":100,"charging":true,"username":"taoyao","password":"taoyao"}
|
||||
}
|
||||
""";
|
||||
// {"header":{"v":"1.0.0","id":1215310510002009,"signal":"room::enter"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9"}}
|
||||
// {"header":{"v":"1.0.0","id":1215310510002010,"signal":"media::transport::plain"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","rtcpMux":false,"comedia":true}}
|
||||
// {"header":{"v":"1.0.0","id":1215375110006012,"signal":"media::produce"},"body":{"kind":"video","roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","transportId":"14dc9307-bf9c-4442-a9ad-ce6a97623ef4","appData":{},"rtpParameters":{"codecs":[{"mimeType":"video/vp8","clockRate":90000,"payloadType":102,"rtcpFeedback":[]}],"encodings":[{"ssrc":123123}]}}}
|
||||
// 音频转为PCM
|
||||
// ffmpeg.exe -i .\a.m4a -f s16le a.pcm
|
||||
// ffmpeg.exe -i .\a.m4a -f s16le -ac 2 -ar 8000 a.pcm
|
||||
// ffplay.exe -ar 48000 -ac 2 -f s16le -i a.pcm
|
||||
// ffmpeg不支持rtcpMux
|
||||
// ffmpeg -re -i video.mp4 -c:v vp8 -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||
// ffmpeg -re -i video.mp4 -c:v libvpx -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||
// 音频视频同时传输
|
||||
// ffmpeg -re -i video.mp4 -c:a libopus -vn -f rtp rtp://192.168.1.110:8888 -c:v libx264 -an -f rtp rtp://192.168.1.110:9999 -sdp_file taoyao.sdp
|
||||
// ffplay -protocol_whitelist "file,rtp,udp" -i taoyao.sdp
|
||||
// ffmpeg -protocol_whitelist "file,rtp,udp" -i taoyao.sdp taoyao.mp4
|
||||
final Scanner scanner = new Scanner(System.in);
|
||||
do {
|
||||
if(StringUtils.isEmpty(line)) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
final byte[] bytes = line.getBytes();
|
||||
final byte[] encryptBytes = encrypt.doFinal(bytes);
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(Short.BYTES + encryptBytes.length);
|
||||
buffer.putShort((short) encryptBytes.length);
|
||||
buffer.put(encryptBytes);
|
||||
buffer.flip();
|
||||
final byte[] message = new byte[buffer.capacity()];
|
||||
buffer.get(message);
|
||||
outputStream.write(message);
|
||||
} catch (Exception e) {
|
||||
log.error("发送异常", e);
|
||||
}
|
||||
} while((line = scanner.next()) != null);
|
||||
socket.close();
|
||||
scanner.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSocket() throws Exception {
|
||||
final Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
|
||||
final InputStream inputStream = socket.getInputStream();
|
||||
final OutputStream outputStream = socket.getOutputStream();
|
||||
// 随机密码:https://localhost:8888/config/socket
|
||||
final String secret = "TSFXzB7hcfE=".strip();
|
||||
final Cipher encrypt = CipherUtils.buildCipher(Cipher.ENCRYPT_MODE, Encrypt.DES, secret);
|
||||
final Cipher decrypt = CipherUtils.buildCipher(Cipher.DECRYPT_MODE, Encrypt.DES, secret);
|
||||
// 接收
|
||||
new Thread(() -> {
|
||||
int length = 0;
|
||||
short messageLength = 0;
|
||||
final byte[] bytes = new byte[1024];
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
|
||||
try {
|
||||
while((length = inputStream.read(bytes)) >= 0) {
|
||||
buffer.put(bytes, 0, length);
|
||||
while(buffer.position() > 0) {
|
||||
if(messageLength <= 0) {
|
||||
if(buffer.position() < Short.BYTES) {
|
||||
// 不够消息长度
|
||||
break;
|
||||
} else {
|
||||
buffer.flip();
|
||||
messageLength = buffer.getShort();
|
||||
buffer.compact();
|
||||
if(messageLength > 16 * 1024) {
|
||||
throw MessageCodeException.of("超过最大数据大小:" + messageLength);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(buffer.position() < messageLength) {
|
||||
// 不够消息长度
|
||||
break;
|
||||
} else {
|
||||
final byte[] message = new byte[messageLength];
|
||||
messageLength = 0;
|
||||
buffer.flip();
|
||||
buffer.get(message);
|
||||
buffer.compact();
|
||||
log.debug("收到消息:{}", new String(decrypt.doFinal(message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("读取异常", e);
|
||||
}
|
||||
}).start();
|
||||
// 发送
|
||||
String line = """
|
||||
{
|
||||
"header":{"v":"1.0.0","id":1215293599999001,"signal":"client::register"},
|
||||
"body":{"clientId":"ffmpeg","name":"ffmpeg","clientType":"WEB","battery":100,"charging":true,"username":"taoyao","password":"taoyao"}
|
||||
}
|
||||
""";
|
||||
// {"header":{"v":"1.0.0","id":1215310510002009,"signal":"room::enter"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9"}}
|
||||
// {"header":{"v":"1.0.0","id":1215310510002010,"signal":"media::transport::plain"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","rtcpMux":false,"comedia":true}}
|
||||
// {"header":{"v":"1.0.0","id":1215375110006012,"signal":"media::produce"},"body":{"kind":"video","roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","transportId":"14dc9307-bf9c-4442-a9ad-ce6a97623ef4","appData":{},"rtpParameters":{"codecs":[{"mimeType":"video/vp8","clockRate":90000,"payloadType":102,"rtcpFeedback":[]}],"encodings":[{"ssrc":123123}]}}}
|
||||
// 音频转为PCM
|
||||
// ffmpeg.exe -i .\a.m4a -f s16le a.pcm
|
||||
// ffmpeg.exe -i .\a.m4a -f s16le -ac 2 -ar 8000 a.pcm
|
||||
// ffplay.exe -ar 48000 -ac 2 -f s16le -i a.pcm
|
||||
// ffmpeg不支持rtcpMux
|
||||
// ffmpeg -re -i video.mp4 -c:v vp8 -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||
// ffmpeg -re -i video.mp4 -c:v libvpx -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||
// 音频视频同时传输
|
||||
// ffmpeg -re -i video.mp4 -c:a libopus -vn -f rtp rtp://192.168.1.110:8888 -c:v libx264 -an -f rtp rtp://192.168.1.110:9999 -sdp_file taoyao.sdp
|
||||
// ffplay -protocol_whitelist "file,rtp,udp" -i taoyao.sdp
|
||||
// ffmpeg -protocol_whitelist "file,rtp,udp" -i taoyao.sdp taoyao.mp4
|
||||
final Scanner scanner = new Scanner(System.in);
|
||||
do {
|
||||
if(StringUtils.isEmpty(line)) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
final byte[] bytes = line.getBytes();
|
||||
final byte[] encryptBytes = encrypt.doFinal(bytes);
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(Short.BYTES + encryptBytes.length);
|
||||
buffer.putShort((short) encryptBytes.length);
|
||||
buffer.put(encryptBytes);
|
||||
buffer.flip();
|
||||
final byte[] message = new byte[buffer.capacity()];
|
||||
buffer.get(message);
|
||||
outputStream.write(message);
|
||||
} catch (Exception e) {
|
||||
log.error("发送异常", e);
|
||||
}
|
||||
} while((line = scanner.next()) != null);
|
||||
socket.close();
|
||||
scanner.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,29 +15,29 @@ import lombok.extern.slf4j.Slf4j;
|
||||
//@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.buildId();
|
||||
log.info("生成ID:{}", id);
|
||||
log.info("生成ID:{}", String.valueOf(id).length());
|
||||
}
|
||||
|
||||
@Test
|
||||
@CostedTest(count = 100000, thread = 10)
|
||||
void testIdCosted() {
|
||||
this.idService.buildId();
|
||||
}
|
||||
|
||||
@Test
|
||||
@CostedTest(count = 100000, thread = 10)
|
||||
void testUuidCosted() {
|
||||
this.idService.buildUuid();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private IdService idService;
|
||||
|
||||
@Test
|
||||
// @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
|
||||
// @Rollback()
|
||||
// @RepeatedTest(10)
|
||||
void testId() {
|
||||
final long id = this.idService.buildId();
|
||||
log.info("生成ID:{}", id);
|
||||
log.info("生成ID:{}", String.valueOf(id).length());
|
||||
}
|
||||
|
||||
@Test
|
||||
@CostedTest(count = 100000, thread = 10)
|
||||
void testIdCosted() {
|
||||
this.idService.buildId();
|
||||
}
|
||||
|
||||
@Test
|
||||
@CostedTest(count = 100000, thread = 10)
|
||||
void testUuidCosted() {
|
||||
this.idService.buildUuid();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user