初始化

This commit is contained in:
2025-10-15 16:16:37 +08:00
commit 1c6412bb05
797 changed files with 51693 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
package com.sdm.gateway2;
import com.sdm.gateway2.route.CustomLoadBalancerClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
/**
* gateway 网关部署到测试环境请求一直无响应,已废弃使用 gateway2作为部署
* http://localhost:7100/actuator/health
* http://localhost:7100/actuator/info
* http://localhost:7100/actuator/metrics
*
* GatewayHealthIndicator检查网关核心组件状态
* RouteHealthIndicator检查路由配置加载状态
* NacosDiscoveryHealthIndicator检查 Nacos 服务发现状态
* DownstreamServicesHealthIndicator检查关键下游服务的可达性
*/
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class,
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.class
})
@EnableDiscoveryClient
@LoadBalancerClients(defaultConfiguration = CustomLoadBalancerClient.class)
public class Gateway2Application {
public static void main(String[] args) {
SpringApplication.run(Gateway2Application.class, args);
}
}

View File

@@ -0,0 +1,68 @@
package com.sdm.gateway2.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.InetSocketAddress;
import java.util.UUID;
@Slf4j
@Component
public class RequestLoggingFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 生成请求追踪ID
String traceId = UUID.randomUUID().toString().replace("-", "");
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethod().toString();
String path = request.getURI().getPath();
String query = request.getURI().getQuery();
String clientIp = getClientIp(request);
// 记录请求开始日志
log.info("[{}] {} {}?{} from {}", traceId, method, path, query != null ? query : "", clientIp);
long startTime = System.currentTimeMillis();
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
// 记录响应日志
log.info("[{}] {} {} returned status {} in {} ms",
traceId, method, path, response.getStatusCode(), duration);
})
);
}
private String getClientIp(ServerHttpRequest request) {
String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
String xRealIp = request.getHeaders().getFirst("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty()) {
return xRealIp;
}
InetSocketAddress remoteAddress = request.getRemoteAddress();
return remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : "unknown";
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

View File

@@ -0,0 +1,57 @@
package com.sdm.gateway2.health;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
/**
* Nacos 服务发现健康检查指示器
*/
@Component
public class CustomNacosDiscoveryHealthIndicator implements HealthIndicator {
@Autowired(required = false)
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public Health health() {
if (nacosDiscoveryProperties == null) {
return Health.unknown()
.withDetail("nacosDiscovery", "Nacos discovery not configured")
.withDetail("description", "Nacos discovery properties not available")
.build();
}
try {
// 检查 Nacos 服务是否可用
String serverAddr = nacosDiscoveryProperties.getServerAddr();
boolean enabled = nacosDiscoveryProperties.isRegisterEnabled();
String serviceId = nacosDiscoveryProperties.getService();
if (enabled) {
return Health.up()
.withDetail("nacosDiscovery", "Nacos discovery is enabled")
.withDetail("serverAddr", serverAddr)
.withDetail("serviceId", serviceId)
.withDetail("groupName", nacosDiscoveryProperties.getGroup())
.withDetail("clusterName", nacosDiscoveryProperties.getClusterName())
.withDetail("description", "Successfully connected to Nacos discovery server")
.build();
} else {
return Health.down()
.withDetail("nacosDiscovery", "Nacos discovery is disabled")
.withDetail("serviceId", serviceId)
.withDetail("description", "Nacos service discovery is not enabled")
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("nacosDiscovery", "Nacos discovery check failed")
.withDetail("error", e.getMessage())
.withDetail("description", "Failed to connect to Nacos discovery server")
.build();
}
}
}

View File

@@ -0,0 +1,91 @@
/*
package com.sdm.gateway2.health;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
*/
/**
* 下游服务健康检查指示器
* 检查关键下游服务的可达性
*//*
@Component
public class DownstreamServicesHealthIndicator implements HealthIndicator {
// 服务名称列表
private static final String[] SERVICE_NAMES = {
"approve", "capability", "data", "pbs",
"performance", "project", "system", "task"
};
@Override
public Health health() {
Map<String, Object> servicesStatus = new HashMap<>();
boolean allServicesUp = true;
// 检查各个关键服务的状态
for (String serviceName : SERVICE_NAMES) {
boolean serviceUp = checkServiceStatus(serviceName);
servicesStatus.put(serviceName, serviceUp ? "UP" : "DOWN");
if (!serviceUp) {
allServicesUp = false;
}
}
if (allServicesUp) {
return Health.up()
.withDetail("downstreamServices", servicesStatus)
.withDetail("description", "All downstream services are reachable")
.build();
} else {
return Health.down()
.withDetail("downstreamServices", servicesStatus)
.withDetail("description", "Some downstream services are not reachable")
.build();
}
}
*/
/**
* 检查特定服务的状态
* 通过调用服务的 /actuator/health 端点来检查服务的健康状况
*//*
private boolean checkServiceStatus(String serviceName) {
try {
WebClient webClient = WebClient.builder()
.baseUrl("http://" + serviceName)
.build();
// 设置较短的超时时间,避免健康检查阻塞太久
String response = webClient.get()
.uri("/actuator/health")
.retrieve()
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(5)) // 5秒超时
.block();
// 如果能成功获取响应,则认为服务是可达的
return response != null && !response.isEmpty();
} catch (WebClientResponseException e) {
// 服务返回错误响应4xx, 5xx等但服务是可达的
return true;
} catch (WebClientRequestException e) {
// 网络错误,服务不可达
return false;
} catch (Exception e) {
// 其他异常,认为服务不可达
return false;
}
}
}*/

View File

@@ -0,0 +1,45 @@
package com.sdm.gateway2.health;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
/**
* 自定义网关健康检查指示器
*/
@Component
public class GatewayHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 检查网关核心组件是否正常工作
int gatewayStatus = checkGatewayBasicStatus();
if (gatewayStatus == 0) {
return Health.up()
.withDetail("gateway", "Gateway core components are running")
.withDetail("status", "Available")
.build();
} else {
return Health.down()
.withDetail("gateway", "Gateway core components error")
.withDetail("status", "Unavailable")
.withDetail("errorCode", gatewayStatus)
.build();
}
}
/**
* 检查网关基本状态
* 返回值:
* 0 - 正常
* 1 - 网关未正确初始化
* 2 - 网关核心组件异常
*/
private int checkGatewayBasicStatus() {
// 在实际应用中,这里应该包含对网关核心组件的检查
// 例如检查必要的Bean是否存在、配置是否正确等
// 目前简化实现直接返回0表示正常
return 0;
}
}

View File

@@ -0,0 +1,53 @@
package com.sdm.gateway2.health;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.stream.Collectors;
/**
* 路由配置健康检查指示器
*/
@Component
public class RouteHealthIndicator implements HealthIndicator {
@Autowired
private RouteLocator routeLocator;
@Override
public Health health() {
try {
// 获取所有路由配置
Flux<Route> routes = routeLocator.getRoutes();
List<Route> routeList = routes.collectList().block();
if (routeList != null && !routeList.isEmpty()) {
List<String> routeIds = routeList.stream()
.map(Route::getId)
.collect(Collectors.toList());
return Health.up()
.withDetail("routes", routeIds)
.withDetail("routeCount", routeList.size())
.withDetail("description", "All routes loaded successfully")
.build();
} else {
return Health.down()
.withDetail("reason", "No routes configured")
.withDetail("description", "Gateway has no route definitions")
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.withDetail("description", "Failed to load routes")
.build();
}
}
}

View File

@@ -0,0 +1,34 @@
package com.sdm.gateway2.info;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 网关信息贡献器
*/
@Component
public class GatewayInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
Map<String, Object> gatewayInfo = new HashMap<>();
gatewayInfo.put("version", "2.0.0");
gatewayInfo.put("description", "SDM Gateway Service");
gatewayInfo.put("routes", new String[]{
"approve-service",
"capability-service",
"data-service",
"pbs-service",
"performance-service",
"project-service",
"system-service",
"task-service"
});
builder.withDetail("gateway", gatewayInfo);
}
}

View File

@@ -0,0 +1,85 @@
package com.sdm.gateway2.route;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Component
public class CustomLoadBalancerClient implements ReactorServiceInstanceLoadBalancer {
@Value("${serverType}")
private int serverType;
@Value("${serverIp}")
private String serverIp;
private final AtomicInteger atomicInteger = new AtomicInteger(0);
// 服务列表
private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
public CustomLoadBalancerClient(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable();
assert supplier != null;
return supplier.get().next().map(serviceInstances -> getInstanceResponse(serviceInstances, request));
}
/**
* 负载均衡获取服务
*
* @param instances instances
* @return Response<ServiceInstance>
*/
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {
if (instances.isEmpty()) {
log.error("服务器列表为空");
return new EmptyResponse();
}
// log.info("request url:{}" + url);
// return new DefaultResponse(instances.get(0));
ServiceInstance serviceInstance = null;
// 0单机处理1负载均衡轮询
if (serverType == 0) {
// DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
// RequestData clientRequest = (RequestData) requestContext.getClientRequest();
// String url = clientRequest.getUrl().toString();
for (ServiceInstance instance : instances) {
if (instance.getHost().equals(serverIp)) {
serviceInstance = instance;
break;
}
}
//serviceInstance = instances.get(0);
log.info("转发到服务器:" + serviceInstance.getHost());
} else {
// 获取当前的调用编号(每来一次请求则累加1)
int sequence = atomicInteger.getAndIncrement();
// 达到1000清零
if (sequence == 1000) {
atomicInteger.set(0);
}
// 调用编号与服务器个数取余
int index = sequence % instances.size();
log.info("负载均衡到服务器:" + instances.get(index).getHost());
serviceInstance = instances.get(index);
}
return new DefaultResponse(serviceInstance);
}
}

View File

@@ -0,0 +1,101 @@
server:
port: 7100
spring:
application:
name: gateway2
cloud:
nacos:
config:
server-addr: 192.168.65.161:8848
file-extension: yaml
import-check:
enabled: false
discovery:
server-addr: 192.168.65.161:8848
enabled: true
gateway:
httpclient:
connect-timeout: 5000
response-timeout: 5000
routes:
- id: approve-service
uri: lb://approve
predicates:
- Path=/simulation/approve/**
filters:
- StripPrefix=2
- id: capability-service
uri: lb://capability
predicates:
- Path=/simulation/capability/**
filters:
- StripPrefix=2
- id: data-service
uri: lb://data
predicates:
- Path=/simulation/data/**
filters:
- StripPrefix=2
- id: pbs-service
uri: lb://pbs
predicates:
- Path=/simulation/pbs/**
filters:
- StripPrefix=2
- id: performance-service
uri: lb://performance
predicates:
- Path=/simulation/performance/**
filters:
- StripPrefix=2
- id: project-service
uri: lb://project
predicates:
- Path=/simulation/project/**
filters:
- StripPrefix=2
- id: system-service
uri: lb://system
predicates:
- Path=/simulation/system/**
filters:
- StripPrefix=2
- id: task-service
uri: lb://task
predicates:
- Path=/simulation/task/**
filters:
- StripPrefix=2
discovery:
locator:
enabled: true
lower-case-service-id: true
management:
endpoints:
web:
exposure:
include: health,info,metrics,logfile
endpoint:
health:
show-details: always
enabled: true
probes:
enabled: true
info:
enabled: true
info:
env:
enabled: true
logging:
level:
com.sdm.gateway2: DEBUG
org.springframework.cloud.gateway: DEBUG
reactor.netty: DEBUG
# 0单机处理1负载均衡轮询
serverType: 0
serverIp: 192.168.65.161
#serverIp: 192.168.65.73

View File

@@ -0,0 +1,101 @@
server:
port: 7100
spring:
application:
name: gateway2
cloud:
nacos:
config:
server-addr: 192.168.65.161:8848
file-extension: yaml
import-check:
enabled: false
discovery:
server-addr: 192.168.65.161:8848
enabled: true
gateway:
httpclient:
connect-timeout: 5000
response-timeout: 5000
routes:
- id: approve-service
uri: lb://approve
predicates:
- Path=/simulation/approve/**
filters:
- StripPrefix=2
- id: capability-service
uri: lb://capability
predicates:
- Path=/simulation/capability/**
filters:
- StripPrefix=2
- id: data-service
uri: lb://data
predicates:
- Path=/simulation/data/**
filters:
- StripPrefix=2
- id: pbs-service
uri: lb://pbs
predicates:
- Path=/simulation/pbs/**
filters:
- StripPrefix=2
- id: performance-service
uri: lb://performance
predicates:
- Path=/simulation/performance/**
filters:
- StripPrefix=2
- id: project-service
uri: lb://project
predicates:
- Path=/simulation/project/**
filters:
- StripPrefix=2
- id: system-service
uri: lb://system
predicates:
- Path=/simulation/system/**
filters:
- StripPrefix=2
- id: task-service
uri: lb://task
predicates:
- Path=/simulation/task/**
filters:
- StripPrefix=2
discovery:
locator:
enabled: true
lower-case-service-id: true
management:
endpoints:
web:
exposure:
include: health,info,metrics,logfile
endpoint:
health:
show-details: always
enabled: true
probes:
enabled: true
info:
enabled: true
info:
env:
enabled: true
logging:
level:
com.sdm.gateway2: DEBUG
org.springframework.cloud.gateway: DEBUG
reactor.netty: DEBUG
# 0单机处理1负载均衡轮询
serverType: 0
#serverIp: 192.168.65.161
serverIp: 192.168.65.73

View File

@@ -0,0 +1,101 @@
server:
port: 7100
spring:
application:
name: gateway2
cloud:
nacos:
config:
server-addr: 192.168.65.161:8848
file-extension: yaml
import-check:
enabled: false
discovery:
server-addr: 192.168.65.161:8848
enabled: true
gateway:
httpclient:
connect-timeout: 5000
response-timeout: 5000
routes:
- id: approve-service
uri: lb://approve
predicates:
- Path=/simulation/approve/**
filters:
- StripPrefix=2
- id: capability-service
uri: lb://capability
predicates:
- Path=/simulation/capability/**
filters:
- StripPrefix=2
- id: data-service
uri: lb://data
predicates:
- Path=/simulation/data/**
filters:
- StripPrefix=2
- id: pbs-service
uri: lb://pbs
predicates:
- Path=/simulation/pbs/**
filters:
- StripPrefix=2
- id: performance-service
uri: lb://performance
predicates:
- Path=/simulation/performance/**
filters:
- StripPrefix=2
- id: project-service
uri: lb://project
predicates:
- Path=/simulation/project/**
filters:
- StripPrefix=2
- id: system-service
uri: lb://system
predicates:
- Path=/simulation/system/**
filters:
- StripPrefix=2
- id: task-service
uri: lb://task
predicates:
- Path=/simulation/task/**
filters:
- StripPrefix=2
discovery:
locator:
enabled: true
lower-case-service-id: true
management:
endpoints:
web:
exposure:
include: health,info,metrics,logfile
endpoint:
health:
show-details: always
enabled: true
probes:
enabled: true
info:
enabled: true
info:
env:
enabled: true
logging:
level:
com.sdm.gateway2: DEBUG
org.springframework.cloud.gateway: DEBUG
reactor.netty: DEBUG
# 0单机处理1负载均衡轮询
serverType: 0
serverIp: 192.168.65.161
#serverIp: 192.168.65.73

View File

@@ -0,0 +1,8 @@
spring:
profiles:
active: local
main:
web-application-type: reactive
cloud:
gateway:
enabled: true

View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Spring Boot 网关项目日志查看脚本
LOG_HOME="/home/app/gateway2/logs"
LOG_FILE="${LOG_HOME}/running.log"
# 查看实时日志
if [ ! -f "${LOG_FILE}" ]; then
echo "日志文件不存在:${LOG_FILE}(可能项目未启动)"
exit 1
fi
echo "正在查看实时运行日志(按 Ctrl+C 退出)... 日志路径:${LOG_FILE}"
tail -f "${LOG_FILE}"

View File

@@ -0,0 +1,13 @@
@echo off
echo Restarting gateway2 service...
REM 调用停止脚本
call stop.bat
REM 等待3秒
timeout /t 3 /nobreak >nul
REM 调用启动脚本
call start.bat
echo gateway2 service restarted.

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Spring Boot 网关项目重启脚本
echo "=== 开始重启项目 ==="
./stop.sh
./start.sh
echo "=== 重启操作完成 ==="

View File

@@ -0,0 +1,13 @@
@echo off
echo Starting gateway2 service...
REM 设置日志路径
set LOG_PATH=C:\Users\admin\Desktop\gateway\logs
REM 创建日志目录
if not exist "%LOG_PATH%" mkdir "%LOG_PATH%"
REM 后台启动应用,退出命令窗口后继续运行
start /b javaw -DLOG_PATH=%LOG_PATH% -jar gateway2-0.0.1-SNAPSHOT.jar
echo gateway2 service started in background.

View File

@@ -0,0 +1,74 @@
#!/bin/bash
# Spring Boot 网关项目启动脚本
JAR_PATH="/home/app/gateway2"
JAR_NAME="gateway2-0.0.1-SNAPSHOT.jar"
FULL_JAR_PATH="${JAR_PATH}/${JAR_NAME}"
# 与logback.xml保持一致的日志路径
LOG_HOME="/home/app/gateway2/logs"
LOG_FILE="${LOG_HOME}/running.log"
# JVM参数
JVM_OPTS="-Xms512m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_HOME}/heapdump.hprof"
# 默认为交互模式(显示实时日志)
silent_mode=0
# 处理参数:如果传入 --silent 则启用静默模式
if [ "$1" = "--silent" ]; then
silent_mode=1
fi
# 函数定义
check_jar_exists() {
if [ ! -f "${FULL_JAR_PATH}" ]; then
echo "ERROR: Jar包不存在路径${FULL_JAR_PATH}"
exit 1
fi
}
get_running_pid() {
ps -ef | grep "${JAR_NAME}" | grep -v "grep" | awk '{print $2}'
}
# 启动服务
check_jar_exists
PID=$(get_running_pid)
if [ -n "${PID}" ]; then
echo "项目已在运行中PID: ${PID}"
exit 0
fi
# 确保日志目录存在
if [ ! -d "${LOG_HOME}" ]; then
mkdir -p "${LOG_HOME}"
echo "日志目录不存在,已自动创建:${LOG_HOME}"
fi
echo "正在启动项目... 实时日志如下(按 Ctrl+C 可退出查看,进程会继续运行)"
echo "======================================================================"
# 启动项目,保留控制台输出
nohup java ${JVM_OPTS} -jar "${FULL_JAR_PATH}" > "${LOG_FILE}" 2>&1 &
# 获取刚启动的进程PID
NEW_PID=$(get_running_pid)
# 实时显示日志
if [ -n "${NEW_PID}" ]; then
# 根据模式决定是否显示实时日志
if [ ${silent_mode} -eq 0 ]; then
echo "实时日志如下(按 Ctrl+C 可退出查看,进程会继续运行)"
tail -f "${LOG_FILE}"
fi
# 无论 tail 命令如何退出,都认为启动成功
echo "======================================================================"
echo "项目启动成功PID: ${NEW_PID}"
echo "日志文件路径:${LOG_FILE}"
exit 0 # 强制返回成功状态
else
echo "======================================================================"
echo "项目启动失败!请查看日志:${LOG_FILE}"
exit 1
fi

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Spring Boot 网关项目状态查询脚本
JAR_NAME="gateway2-0.0.1-SNAPSHOT.jar"
LOG_HOME="/home/app/gateway2/logs"
LOG_FILE="${LOG_HOME}/running.log"
# 函数定义
get_running_pid() {
ps -ef | grep "${JAR_NAME}" | grep -v "grep" | awk '{print $2}'
}
# 查看服务状态
local PID=$(get_running_pid)
if [ -n "${PID}" ]; then
echo "项目运行中PID: ${PID}"
echo "日志文件路径:${LOG_FILE}"
else
echo "项目未在运行中"
fi

View File

@@ -0,0 +1,8 @@
@echo off
echo Stopping gateway2 service...
REM 强制终止匹配的Java进程
taskkill /f /im java.exe /fi "WINDOWTITLE eq *gateway2-0.0.1-SNAPSHOT.jar*" >nul 2>&1
taskkill /f /im javaw.exe /fi "WINDOWTITLE eq *gateway2-0.0.1-SNAPSHOT.jar*" >nul 2>&1
echo gateway2 service stopped.

View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Spring Boot 项目强制停止脚本
JAR_NAME="gateway2-0.0.1-SNAPSHOT.jar"
# 函数定义获取运行中的进程ID
get_running_pid() {
ps -ef | grep "${JAR_NAME}" | grep -v "grep" | awk '{print $2}'
}
# 获取进程ID
PID=$(get_running_pid)
# 检查进程是否存在
if [ -z "${PID}" ]; then
echo "项目未在运行中,无需停止"
exit 0
fi
# 强制停止进程
echo "正在强制停止项目... PID: ${PID}"
kill -9 "${PID}"
# 检查是否成功停止
sleep 5
if [ -z "$(get_running_pid)" ]; then
echo "项目已强制停止成功"
exit 0
else
echo "ERROR: 进程终止失败请手动检查ps -ef | grep ${JAR_NAME}"
exit 1
fi

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr([%15.15t]){faint} %clr(%logger){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 日志文件存储地址 -->
<property name="LOG_HOME" value="${LOG_PATH:-/home/app/gateway2/logs}" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 文件输出当前日志为running.log按大小和时间滚动 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 当前活动日志文件名称始终为running.log -->
<file>${LOG_HOME}/running.log</file>
<!-- 滚动策略:结合时间和大小 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档文件命名格式running.log.年--日.序号 -->
<FileNamePattern>${LOG_HOME}/running.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!-- 日志保留天数 -->
<MaxHistory>30</MaxHistory>
<!-- 所有归档文件的总大小上限 -->
<TotalSizeCap>1GB</TotalSizeCap>
<!-- 单个./文件大小上限(达到后触发滚动) -->
<maxFileSize>20MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId}] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
<!-- 特定包的日志级别 -->
<logger name="com.sdm.gateway2" level="DEBUG" />
<logger name="org.springframework.cloud.gateway" level="DEBUG" />
<logger name="reactor.netty" level="DEBUG" />
<logger name="org.springframework.web.reactive" level="DEBUG" />
</configuration>

View File

@@ -0,0 +1,48 @@
package com.sdm.gateway2;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ActuatorTest {
@LocalServerPort
private int port;
private TestRestTemplate restTemplate = new TestRestTemplate();
@Test
public void healthCheckShouldReturnOk() {
ResponseEntity<String> entity = restTemplate.getForEntity(
"http://localhost:" + port + "/actuator/health", String.class);
assertThat(entity.getStatusCode().is2xxSuccessful()).isTrue();
// 健康检查应该返回包含"status":"UP"的JSON
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}
@Test
public void infoEndpointShouldReturnInfo() {
ResponseEntity<String> entity = restTemplate.getForEntity(
"http://localhost:" + port + "/actuator/info", String.class);
assertThat(entity.getStatusCode().is2xxSuccessful()).isTrue();
}
@Test
public void healthDetailsShouldBeAvailable() {
ResponseEntity<String> entity = restTemplate.getForEntity(
"http://localhost:" + port + "/actuator/health", String.class);
// 详细健康信息应该包含我们自定义的健康指标
assertThat(entity.getBody()).contains("gateway");
assertThat(entity.getBody()).contains("nacosDiscovery");
assertThat(entity.getBody()).contains("routes");
assertThat(entity.getBody()).contains("downstreamServices");
}
}

View File

@@ -0,0 +1,13 @@
package com.sdm.gateway2;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class Gateway2ApplicationTests {
@Test
void contextLoads() {
}
}