springboot工程中使用tcp协议
//goto docker\docker-compose.yml
version: '3.7'
services:
springboot-tcp:
image: registry.cn-shanghai.aliyuncs.com/00fly/springboot-tcp:1.0.0
container_name: springboot-tcp
deploy:
resources:
limits:
cpus: '1.0'
memory: 200M
reservations:
cpus: '0.05'
memory: 200M
ports:
- 8080:8081
restart: on-failure
logging:
driver: json-file
options:
max-size: '5m'
max-file: '1'
//goto docker\restart.sh
#!/bin/bash
docker-compose down && docker-compose up -d && docker stats
//goto docker\stop.sh
#!/bin/bash
docker-compose down
//goto docker\wait-for.sh
#!/bin/sh
TIMEOUT=15
QUIET=0
echoerr() {
if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
}
usage() {
exitcode="$1"
cat << USAGE >&2
Usage:
$cmdname host:port [-t timeout] [-- command args]
-q | --quiet Do not output any status messages
-t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit "$exitcode"
}
wait_for() {
for i in `seq $TIMEOUT` ; do
nc -z "$HOST" "$PORT" > /dev/null 2>&1
result=$?
if [ $result -eq 0 ] ; then
if [ $# -gt 0 ] ; then
exec "$@"
fi
exit 0
fi
sleep 1
done
echo "Operation timed out" >&2
exit 1
}
while [ $# -gt 0 ]
do
case "$1" in
*:* )
HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
shift 1
;;
-q | --quiet)
QUIET=1
shift 1
;;
-t)
TIMEOUT="$2"
if [ "$TIMEOUT" = "" ]; then break; fi
shift 2
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
break
;;
--help)
usage 0
;;
*)
echoerr "Unknown argument: $1"
usage 1
;;
esac
done
if [ "$HOST" = "" -o "$PORT" = "" ]; then
echoerr "Error: you need to provide a host and port to test."
usage 2
fi
wait_for "$@"
//goto Dockerfile
#基础镜像
FROM adoptopenjdk/openjdk8-openj9:alpine-slim
COPY docker/wait-for.sh /
RUN chmod +x /wait-for.sh && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo 'Asia/Shanghai' >/etc/timezone
#引入运行包
COPY target/*.jar /app.jar
#指定交互端口
EXPOSE 8081
CMD ["--server.port=8081"]
#项目的启动方式
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Xshareclasses", "-Xquickstart", "-jar", "/app.jar"]
//goto pom.xml
<?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>
<groupId>com.fly</groupId>
<artifactId>springboot-tcp</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath />
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<docker.hub>registry.cn-shanghai.aliyuncs.com</docker.hub>
<java.version>1.8</java.version>
<skipTests>true</skipTests>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.8</version>
</dependency>
<!-- 异步日志,需要加入disruptor依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!-- 阿里云maven仓库 -->
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 添加docker-maven插件 -->
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.40.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
<!--<goal>push</goal>-->
<!--<goal>remove</goal>-->
</goals>
</execution>
</executions>
<configuration>
<!-- 连接到带docker环境的linux服务器编译image -->
<!-- <dockerHost>http://192.168.182.10:2375</dockerHost> -->
<!-- Docker 推送镜像仓库地址 -->
<pushRegistry>${docker.hub}</pushRegistry>
<images>
<image>
<name>
${docker.hub}/00fly/${project.artifactId}:${project.version}</name>
<build>
<dockerFileDir>${project.basedir}</dockerFileDir>
</build>
</image>
</images>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
//goto src\main\java\com\fly\BootApplication.java
package com.fly;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BootApplication
{
public static void main(String[] args)
{
SpringApplication.run(BootApplication.class, args);
}
}
//goto src\main\java\com\fly\core\config\AsyncThreadPoolConfig.java
package com.fly.core.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import lombok.extern.slf4j.Slf4j;
/**
*
* 异步线程池配置
*
* @author 00fly
* @version [版本号, 2023年10月22日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@Configuration
@EnableAsync
public class AsyncThreadPoolConfig implements AsyncConfigurer
{
@Bean
ThreadPoolTaskExecutor taskExecutor()
{
int processors = Runtime.getRuntime().availableProcessors();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Math.max(processors, 5));
executor.setMaxPoolSize(Math.max(processors, 5) * 2);
executor.setQueueCapacity(10000);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("asyncTask-");
// ThreadPoolExecutor类有几个内部实现类来处理这类情况:
// AbortPolicy 丢弃任务,抛运行时异常
// CallerRunsPolicy 执行任务
// DiscardPolicy 忽视,什么都不会发生
// DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
return executor;
}
@Override
public Executor getAsyncExecutor()
{
return taskExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler()
{
return (ex, method, params) -> {
log.info("Exception message - {}", ex.getMessage());
log.info("Method name - {}", method.getName());
for (Object param : params)
{
log.info("Parameter value - {}", param);
}
};
}
}
//goto src\main\java\com\fly\core\config\Knife4jConfig.java
package com.fly.core.config;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.ApiOperation;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
/**
* Knife4jConfig
*
*/
@EnableKnife4j
@Configuration
@EnableSwagger2WebMvc
@ConditionalOnWebApplication
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfig
{
/**
* 开发、测试环境接口文档打开
*
* @return
* @see [类、类#方法、类#成员]
*/
@Bean
Docket createRestApi()
{
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.enable(true)
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any()) // 包下的类,生成接口文档
.build()
.securitySchemes(security());
}
private ApiInfo apiInfo()
{
return new ApiInfoBuilder().title("数据接口API").description("接口文档").termsOfServiceUrl("http://00fly.online/").version("1.0.0").build();
}
private List<ApiKey> security()
{
return Collections.singletonList(new ApiKey("token", "token", "header"));
}
}
//goto src\main\java\com\fly\core\config\WebClientConfig.java
package com.fly.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
/**
* 配置WebClient
*/
@Configuration
public class WebClientConfig
{
@Bean
WebClient webClient()
{
return WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
}
}
//goto src\main\java\com\fly\core\config\WebMvcConfig.java
package com.fly.core.config;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*
* mvc配置
*
* @author 00fly
* @version [版本号, 2021年4月23日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Configuration
@ConditionalOnWebApplication
public class WebMvcConfig implements WebMvcConfigurer
{
@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters)
{
converters.add(stringHttpMessageConverter());
converters.add(mappingJackson2HttpMessageConverter());
}
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer)
{
configurer.defaultContentType(MediaType.APPLICATION_JSON);
configurer.ignoreUnknownPathExtensions(false);
configurer.favorPathExtension(true);
configurer.favorParameter(false);
final Map<String, MediaType> mediaTypes = new ConcurrentHashMap<>(3);
mediaTypes.put("atom", MediaType.APPLICATION_ATOM_XML);
mediaTypes.put("html", MediaType.TEXT_HTML);
mediaTypes.put("json", MediaType.APPLICATION_JSON);
configurer.mediaTypes(mediaTypes);
}
@Bean
StringHttpMessageConverter stringHttpMessageConverter()
{
return new StringHttpMessageConverter();
}
@Bean
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter()
{
final MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
final List<MediaType> list = new ArrayList<>();
list.add(MediaType.APPLICATION_JSON);
list.add(MediaType.APPLICATION_XML);
list.add(MediaType.TEXT_PLAIN);
list.add(MediaType.TEXT_HTML);
list.add(MediaType.TEXT_XML);
messageConverter.setSupportedMediaTypes(list);
return messageConverter;
}
/**
* 等价于mvc中<mvc:view-controller path="/" view-name="redirect:index" /><br>
* 等价于mvc中<mvc:view-controller path="/index" view-name="index.html" />
*
* @param registry
*/
@Override
public void addViewControllers(final ViewControllerRegistry registry)
{
registry.addViewController("/").setViewName("redirect:index");
registry.addViewController("/index").setViewName("index.html");
}
}
//goto src\main\java\com\fly\core\exception\GlobalExceptionHandler.java
package com.fly.core.exception;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
imp