初始化
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
101
gateway2/src/main/resources/application-dev.yml
Normal file
101
gateway2/src/main/resources/application-dev.yml
Normal 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
|
||||
101
gateway2/src/main/resources/application-local.yml
Normal file
101
gateway2/src/main/resources/application-local.yml
Normal 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
|
||||
101
gateway2/src/main/resources/application-prod.yml
Normal file
101
gateway2/src/main/resources/application-prod.yml
Normal 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
|
||||
8
gateway2/src/main/resources/application.yml
Normal file
8
gateway2/src/main/resources/application.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: local
|
||||
main:
|
||||
web-application-type: reactive
|
||||
cloud:
|
||||
gateway:
|
||||
enabled: true
|
||||
12
gateway2/src/main/resources/bin/log.sh
Normal file
12
gateway2/src/main/resources/bin/log.sh
Normal 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}"
|
||||
13
gateway2/src/main/resources/bin/restart.bat
Normal file
13
gateway2/src/main/resources/bin/restart.bat
Normal 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.
|
||||
7
gateway2/src/main/resources/bin/restart.sh
Normal file
7
gateway2/src/main/resources/bin/restart.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Spring Boot 网关项目重启脚本
|
||||
|
||||
echo "=== 开始重启项目 ==="
|
||||
./stop.sh
|
||||
./start.sh
|
||||
echo "=== 重启操作完成 ==="
|
||||
13
gateway2/src/main/resources/bin/start.bat
Normal file
13
gateway2/src/main/resources/bin/start.bat
Normal 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.
|
||||
74
gateway2/src/main/resources/bin/start.sh
Normal file
74
gateway2/src/main/resources/bin/start.sh
Normal 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
|
||||
19
gateway2/src/main/resources/bin/status.sh
Normal file
19
gateway2/src/main/resources/bin/status.sh
Normal 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
|
||||
8
gateway2/src/main/resources/bin/stop.bat
Normal file
8
gateway2/src/main/resources/bin/stop.bat
Normal 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.
|
||||
31
gateway2/src/main/resources/bin/stop.sh
Normal file
31
gateway2/src/main/resources/bin/stop.sh
Normal 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
|
||||
57
gateway2/src/main/resources/logback.xml
Normal file
57
gateway2/src/main/resources/logback.xml
Normal 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>
|
||||
48
gateway2/src/test/java/com/sdm/gateway2/ActuatorTest.java
Normal file
48
gateway2/src/test/java/com/sdm/gateway2/ActuatorTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user