高中肮脏的秘密免安装绿色版
5.12G · 2025-11-18
掌握Spring Boot高级特性,了解微服务架构设计理念,学习Spring Cloud基础组件,为构建分布式系统打下基础。
核心概念:
@EnableAutoConfiguration 注解的工作原理spring.factories 文件的作用机制代码示例:
// 自定义自动配置类
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(prefix = "custom.datasource", name = "enabled", havingValue = "true")
@EnableConfigurationProperties(CustomDataSourceProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class CustomDataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@Primary
public DataSource customDataSource(CustomDataSourceProperties properties) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(properties.getUrl());
config.setUsername(properties.getUsername());
config.setPassword(properties.getPassword());
config.setMaximumPoolSize(properties.getMaxPoolSize());
config.setMinimumIdle(properties.getMinIdle());
config.setConnectionTimeout(properties.getConnectionTimeout());
config.setIdleTimeout(properties.getIdleTimeout());
config.setMaxLifetime(properties.getMaxLifetime());
config.setLeakDetectionThreshold(properties.getLeakDetectionThreshold());
return new HikariDataSource(config);
}
@Bean
@ConditionalOnMissingBean
public DataSourceTransactionManager customTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// 配置属性类
@ConfigurationProperties(prefix = "custom.datasource")
@Validated
public class CustomDataSourceProperties {
@NotBlank
private String url;
@NotBlank
private String username;
@NotBlank
private String password;
@Min(1)
@Max(100)
private int maxPoolSize = 10;
@Min(1)
@Max(50)
private int minIdle = 5;
@Min(1000)
private long connectionTimeout = 30000;
@Min(1000)
private long idleTimeout = 600000;
@Min(1000)
private long maxLifetime = 1800000;
@Min(0)
private long leakDetectionThreshold = 60000;
// getter和setter方法
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public int getMaxPoolSize() { return maxPoolSize; }
public void setMaxPoolSize(int maxPoolSize) { this.maxPoolSize = maxPoolSize; }
public int getMinIdle() { return minIdle; }
public void setMinIdle(int minIdle) { this.minIdle = minIdle; }
public long getConnectionTimeout() { return connectionTimeout; }
public void setConnectionTimeout(long connectionTimeout) { this.connectionTimeout = connectionTimeout; }
public long getIdleTimeout() { return idleTimeout; }
public void setIdleTimeout(long idleTimeout) { this.idleTimeout = idleTimeout; }
public long getMaxLifetime() { return maxLifetime; }
public void setMaxLifetime(long maxLifetime) { this.maxLifetime = maxLifetime; }
public long getLeakDetectionThreshold() { return leakDetectionThreshold; }
public void setLeakDetectionThreshold(long leakDetectionThreshold) { this.leakDetectionThreshold = leakDetectionThreshold; }
}
常用条件注解使用:
@Configuration
@EnableConfigurationProperties(ApplicationProperties.class)
public class ConditionalConfig {
// 类路径存在时生效
@Bean
@ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper")
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.registerModule(new JavaTimeModule());
return mapper;
}
// 属性值匹配时生效
@Bean
@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true", matchIfMissing = false)
@ConditionalOnClass(CacheManager.class)
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10))
.recordStats());
return cacheManager;
}
// 缺少Bean时生效
@Bean
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.url", matchIfMissing = true)
public DataSource defaultDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName("testdb")
.addScript("classpath:schema.sql")
.addScript("classpath:data.sql")
.build();
}
// 表达式条件
@Bean
@ConditionalOnExpression("'${app.profile}' == 'dev' and '${app.debug}' == 'true'")
public DevTools devTools() {
DevTools devTools = new DevTools();
devTools.setRestartEnabled(true);
devTools.setLiveReloadEnabled(true);
return devTools;
}
// 自定义条件
@Bean
@ConditionalOnCustomProperty(prefix = "app.redis", name = "enabled", havingValue = "true")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 设置序列化器
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
// 自定义条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(CustomPropertyCondition.class)
public @interface ConditionalOnCustomProperty {
String prefix() default "";
String name() default "";
String havingValue() default "";
boolean matchIfMissing() default false;
}
// 自定义条件实现
public class CustomPropertyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attrs = metadata.getAnnotationAttributes(
ConditionalOnCustomProperty.class.getName());
String prefix = (String) attrs.get("prefix");
String name = (String) attrs.get("name");
String havingValue = (String) attrs.get("havingValue");
boolean matchIfMissing = (Boolean) attrs.get("matchIfMissing");
String propertyName = prefix.isEmpty() ? name : prefix + "." + name;
String actualValue = context.getEnvironment().getProperty(propertyName);
if (actualValue == null) {
return matchIfMissing;
}
return havingValue.equals(actualValue);
}
}
创建自定义Starter步骤:
// 1. 核心功能类
public class CustomService {
private final CustomProperties properties;
private final RedisTemplate<String, Object> redisTemplate;
public CustomService(CustomProperties properties, RedisTemplate<String, Object> redisTemplate) {
this.properties = properties;
this.redisTemplate = redisTemplate;
}
public String process(String input) {
String result = properties.getPrefix() + input + properties.getSuffix();
// 如果启用了缓存
if (properties.isCacheEnabled()) {
String cacheKey = "custom:process:" + input.hashCode();
Object cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return (String) cached;
}
redisTemplate.opsForValue().set(cacheKey, result,
Duration.ofSeconds(properties.getCacheTtl()));
}
return result;
}
public void clearCache() {
if (properties.isCacheEnabled()) {
redisTemplate.delete("custom:process:*");
}
}
}
// 2. 配置属性
@ConfigurationProperties(prefix = "custom.service")
@Validated
public class CustomProperties {
@NotBlank
private String prefix = "[";
@NotBlank
private String suffix = "]";
private boolean enabled = true;
private boolean cacheEnabled = false;
@Min(1)
@Max(3600)
private long cacheTtl = 300;
// getter和setter方法
public String getPrefix() { return prefix; }
public void setPrefix(String prefix) { this.prefix = prefix; }
public String getSuffix() { return suffix; }
public void setSuffix(String suffix) { this.suffix = suffix; }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public boolean isCacheEnabled() { return cacheEnabled; }
public void setCacheEnabled(boolean cacheEnabled) { this.cacheEnabled = cacheEnabled; }
public long getCacheTtl() { return cacheTtl; }
public void setCacheTtl(long cacheTtl) { this.cacheTtl = cacheTtl; }
}
// 3. 自动配置类
@Configuration
@ConditionalOnClass(CustomService.class)
@EnableConfigurationProperties(CustomProperties.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class CustomServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "custom.service", name = "enabled", havingValue = "true")
public CustomService customService(CustomProperties properties,
RedisTemplate<String, Object> redisTemplate) {
return new CustomService(properties, redisTemplate);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "custom.service", name = "enabled", havingValue = "true")
public CustomServiceController customServiceController(CustomService customService) {
return new CustomServiceController(customService);
}
}
// 4. 控制器
@RestController
@RequestMapping("/api/custom")
public class CustomServiceController {
private final CustomService customService;
public CustomServiceController(CustomService customService) {
this.customService = customService;
}
@PostMapping("/process")
public ResponseEntity<String> process(@RequestBody ProcessRequest request) {
String result = customService.process(request.getInput());
return ResponseEntity.ok(result);
}
@DeleteMapping("/cache")
public ResponseEntity<Void> clearCache() {
customService.clearCache();
return ResponseEntity.ok().build();
}
}
// 5. spring.factories文件内容
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=
// com.example.custom.CustomServiceAutoConfiguration
核心原则:
// 1. 单一职责原则 - 用户服务
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable @Min(1) Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(UserResponse.from(user));
}
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
User user = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED)
.body(UserResponse.from(user));
}
@PutMapping("/{id}")
public ResponseEntity<UserResponse> updateUser(@PathVariable @Min(1) Long id,
@Valid @RequestBody UpdateUserRequest request) {
User user = userService.update(id, request);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(UserResponse.from(user));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable @Min(1) Long id) {
boolean deleted = userService.delete(id);
if (!deleted) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
@GetMapping
public ResponseEntity<Page<UserResponse>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String sortDir) {
Page<User> users = userService.findAll(page, size, sortBy, sortDir);
Page<UserResponse> response = users.map(UserResponse::from);
return ResponseEntity.ok(response);
}
}
// 2. 数据独立原则 - 用户实体
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false, length = 50)
@NotBlank
@Size(min = 3, max = 50)
private String username;
@Column(nullable = false, length = 100)
@NotBlank
@Email
@Size(max = 100)
private String email;
@Column(name = "first_name", length = 50)
@Size(max = 50)
private String firstName;
@Column(name = "last_name", length = 50)
@Size(max = 50)
private String lastName;
@Column(name = "phone", length = 20)
@Pattern(regexp = "^[0-9+\-\s()]+$")
private String phone;
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private UserStatus status = UserStatus.ACTIVE;
@Column(name = "created_at", nullable = false, updatable = false)
@CreatedDate
private LocalDateTime createdAt;
@Column(name = "updated_at")
@LastModifiedDate
private LocalDateTime updatedAt;
@Column(name = "created_by", length = 50)
@CreatedBy
private String createdBy;
@Column(name = "updated_by", length = 50)
@LastModifiedBy
private String updatedBy;
// 构造函数
public User() {}
public User(String username, String email, String firstName, String lastName) {
this.username = username;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
this.status = UserStatus.ACTIVE;
}
// getter和setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public UserStatus getStatus() { return status; }
public void setStatus(UserStatus status) { this.status = status; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
public String getCreatedBy() { return createdBy; }
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
public String getUpdatedBy() { return updatedBy; }
public void setUpdatedBy(String updatedBy) { this.updatedBy = updatedBy; }
}
// 3. 用户状态枚举
public enum UserStatus {
ACTIVE("激活"),
INACTIVE("未激活"),
SUSPENDED("暂停"),
DELETED("已删除");
private final String description;
UserStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
RESTful API调用:
// 订单服务调用用户服务
@Service
@Slf4j
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private CircuitBreakerFactory circuitBreakerFactory;
@Value("${user.service.url}")
private String userServiceUrl;
@Value("${user.service.timeout:5000}")
private int timeout;
public Order createOrder(Long userId, OrderRequest request) {
// 使用熔断器调用用户服务
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("user-service");
User user = circuitBreaker.run(() -> {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-Service-Name", "order-service");
headers.set("X-Request-ID", UUID.randomUUID().toString());
HttpEntity<Void> entity = new HttpEntity<>(headers);
ResponseEntity<User> response = restTemplate.exchange(
userServiceUrl + "/api/users/" + userId,
HttpMethod.GET,
entity,
User.class
);
if (response.getStatusCode() == HttpStatus.OK) {
return response.getBody();
} else {
throw new ServiceException("用户服务返回错误状态: " + response.getStatusCode());
}
} catch (ResourceAccessException e) {
log.error("调用用户服务超时", e);
throw new ServiceTimeoutException("用户服务调用超时", e);
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new UserNotFoundException("用户不存在: " + userId);
}
log.error("调用用户服务失败", e);
throw new ServiceException("用户服务调用失败: " + e.getMessage(), e);
}
}, throwable -> {
log.error("用户服务熔断器触发", throwable);
throw new ServiceUnavailableException("用户服务暂时不可用");
});
if (user == null) {
throw new UserNotFoundException("用户不存在");
}
// 验证用户状态
if (user.getStatus() != UserStatus.ACTIVE) {
throw new UserInactiveException("用户状态异常,无法创建订单");
}
// 创建订单
Order order = new Order();
order.setUserId(userId);
order.setAmount(request.getAmount());
order.setStatus(OrderStatus.PENDING);
order.setCreatedAt(LocalDateTime.now());
order.setOrderNumber(generateOrderNumber());
return orderRepository.save(order);
}
private String generateOrderNumber() {
return "ORD" + System.currentTimeMillis() + RandomStringUtils.randomNumeric(4);
}
}
// 异常定义
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
public class UserInactiveException extends RuntimeException {
public UserInactiveException(String message) {
super(message);
}
}
public class ServiceTimeoutException extends RuntimeException {
public ServiceTimeoutException(String message, Throwable cause) {
super(message, cause);
}
}
public class ServiceUnavailableException extends RuntimeException {
public ServiceUnavailableException(String message) {
super(message);
}
}
网关配置示例:
// 网关配置类
@Configuration
@EnableWebFlux
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r.path("/api/users/**")
.filters(f -> f.stripPrefix(2)
.addRequestHeader("X-Gateway", "true")
.addRequestHeader("X-Request-ID", generateRequestId())
.circuitBreaker(config -> config
.setName("user-service")
.setFallbackUri("forward:/fallback/user-service"))
.retry(config -> config
.setRetries(3)
.setMethods(HttpMethod.GET, HttpMethod.POST)
.setStatuses(HttpStatus.INTERNAL_SERVER_ERROR)
.setBackoff(Duration.ofMillis(100), Duration.ofMillis(1000), 2, true))
.requestRateLimiter(config -> config
.setRateLimiter(redisRateLimiter())
.setKeyResolver(ipKeyResolver()))
.modifyResponseBody(String.class, String.class,
(exchange, response) -> {
// 添加响应头
ServerHttpResponse httpResponse = exchange.getResponse();
httpResponse.getHeaders().add("X-Response-Time",
String.valueOf(System.currentTimeMillis()));
return Mono.just(response);
}))
.uri("lb://user-service"))
.route("order-service", r -> r.path("/api/orders/**")
.filters(f -> f.stripPrefix(2)
.addRequestHeader("X-Gateway", "true")
.addRequestHeader("X-Request-ID", generateRequestId())
.circuitBreaker(config -> config
.setName("order-service")
.setFallbackUri("forward:/fallback/order-service"))
.retry(config -> config
.setRetries(2)
.setMethods(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT)
.setStatuses(HttpStatus.INTERNAL_SERVER_ERROR)
.setBackoff(Duration.ofMillis(200), Duration.ofMillis(2000), 2, true)))
.uri("lb://order-service"))
.route("notification-service", r -> r.path("/api/notifications/**")
.filters(f -> f.stripPrefix(2)
.addRequestHeader("X-Gateway", "true")
.addRequestHeader("X-Request-ID", generateRequestId())
.circuitBreaker(config -> config
.setName("notification-service")
.setFallbackUri("forward:/fallback/notification-service")))
.uri("lb://notification-service"))
.build();
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20, 1);
}
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
private String generateRequestId() {
return UUID.randomUUID().toString();
}
@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 添加请求ID
String requestId = UUID.randomUUID().toString();
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-Request-ID", requestId)
.header("X-Forwarded-For", getClientIp(request))
.build();
// 记录请求日志
log.info("Gateway Request: {} {} from {}",
request.getMethod(), path, getClientIp(request));
return chain.filter(exchange.mutate().request(mutatedRequest).build())
.doOnSuccess(response -> {
log.info("Gateway Response: {} {} - {}",
request.getMethod(), path, response.getStatusCode());
})
.doOnError(error -> {
log.error("Gateway Error: {} {} - {}",
request.getMethod(), path, error.getMessage());
});
};
}
private String getClientIp(ServerHttpRequest request) {
String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddress() != null ?
request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
}
}
Eureka Server配置:
// Eureka服务端
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
// application.yml配置
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 10000
response-cache-update-interval-ms: 3000
response-cache-auto-expiration-in-seconds: 180
renewal-percent-threshold: 0.85
Eureka Client配置:
// 服务提供者
@SpringBootApplication
@EnableEurekaClient
@RestController
@Slf4j
public class UserServiceApplication {
@Value("${server.port}")
private String port;
@Autowired
private UserService userService;
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> health() {
Map<String, Object> health = new HashMap<>();
health.put("status", "UP");
health.put("service", "user-service");
health.put("port", port);
health.put("timestamp", LocalDateTime.now());
health.put("database", checkDatabaseHealth());
return ResponseEntity.ok(health);
}
@GetMapping("/info")
public ResponseEntity<Map<String, Object>> info() {
Map<String, Object> info = new HashMap<>();
info.put("service", "user-service");
info.put("version", "1.0.0");
info.put("description", "用户管理服务");
info.put("port", port);
info.put("uptime", getUptime());
return ResponseEntity.ok(info);
}
private String checkDatabaseHealth() {
try {
// 检查数据库连接
return "UP";
} catch (Exception e) {
return "DOWN";
}
}
private String getUptime() {
// 计算服务运行时间
return "运行中";
}
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
// application.yml配置
spring:
application:
name: user-service
profiles:
active: dev
server:
port: 8081
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
register-with-eureka: true
fetch-registry: true
registry-fetch-interval-seconds: 30
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.profiles.active}:${server.port}
lease-renewal-interval-in-seconds: 30
lease-expiration-duration-in-seconds: 90
status-page-url-path: /actuator/info
health-check-url-path: /actuator/health
Config Server配置:
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
// application.yml
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/your-repo/config-repo
search-paths: configs
clone-on-start: true
force-pull: true
timeout: 10
native:
search-locations: classpath:/config
label: main
profiles:
active: git
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Config Client使用:
@RestController
@RefreshScope
@Slf4j
public class ConfigController {
@Value("${app.name:default}")
private String appName;
@Value("${app.version:1.0}")
private String appVersion;
@Value("${app.description:默认描述}")
private String appDescription;
@Value("${app.features:[]}")
private List<String> features;
@Value("${app.database.url:jdbc:h2:mem:testdb}")
private String databaseUrl;
@Value("${app.redis.host:localhost}")
private String redisHost;
@Value("${app.redis.port:6379}")
private int redisPort;
@GetMapping("/config")
public ResponseEntity<Map<String, Object>> getConfig() {
Map<String, Object> config = new HashMap<>();
config.put("name", appName);
config.put("version", appVersion);
config.put("description", appDescription);
config.put("features", features);
config.put("database", Map.of("url", databaseUrl));
config.put("redis", Map.of("host", redisHost, "port", redisPort));
config.put("timestamp", LocalDateTime.now());
return ResponseEntity.ok(config);
}
@PostMapping("/refresh")
public ResponseEntity<Map<String, String>> refresh() {
// 手动刷新配置
log.info("手动刷新配置");
return ResponseEntity.ok(Map.of("status", "success", "message", "配置已刷新"));
}
}
// bootstrap.yml配置
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8888
fail-fast: true
retry:
initial-interval: 1000
max-attempts: 6
max-interval: 2000
multiplier: 1.1
discovery:
enabled: true
service-id: config-server
Feign客户端配置:
// 用户服务Feign客户端
@FeignClient(name = "user-service",
fallback = UserServiceFallback.class,
configuration = FeignConfig.class)
public interface UserServiceClient {
@GetMapping("/api/users/{id}")
User getUserById(@PathVariable("id") Long id);
@PostMapping("/api/users")
User createUser(@RequestBody User user);
@GetMapping("/api/users")
List<User> getAllUsers(@RequestParam("page") int page,
@RequestParam("size") int size);
@PutMapping("/api/users/{id}")
User updateUser(@PathVariable("id") Long id, @RequestBody User user);
@DeleteMapping("/api/users/{id}")
void deleteUser(@PathVariable("id") Long id);
}
// Feign配置
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("X-Service-Name", "order-service");
requestTemplate.header("X-Request-ID", UUID.randomUUID().toString());
requestTemplate.header("X-Request-Time", String.valueOf(System.currentTimeMillis()));
};
}
@Bean
public Retryer retryer() {
return new Retryer.Default(1000, 2000, 3);
}
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}
// 自定义错误解码器
public class CustomErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()) {
case 400:
return new BadRequestException("请求参数错误");
case 401:
return new UnauthorizedException("未授权访问");
case 403:
return new ForbiddenException("禁止访问");
case 404:
return new UserNotFoundException("用户不存在");
case 500:
return new ServiceException("服务内部错误");
default:
return defaultErrorDecoder.decode(methodKey, response);
}
}
}
// 降级处理
@Component
@Slf4j
public class UserServiceFallback implements UserServiceClient {
@Override
public User getUserById(Long id) {
log.warn("用户服务降级处理: getUserById, id={}", id);
User user = new User();
user.setId(id);
user.setUsername("用户服务暂时不可用");
user.setEmail("[email protected]");
user.setStatus(UserStatus.INACTIVE);
return user;
}
@Override
public User createUser(User user) {
log.warn("用户服务降级处理: createUser");
throw new ServiceUnavailableException("用户服务暂时不可用,无法创建用户");
}
@Override
public List<User> getAllUsers(int page, int size) {
log.warn("用户服务降级处理: getAllUsers");
return Collections.emptyList();
}
@Override
public User updateUser(Long id, User user) {
log.warn("用户服务降级处理: updateUser, id={}", id);
throw new ServiceUnavailableException("用户服务暂时不可用,无法更新用户");
}
@Override
public void deleteUser(Long id) {
log.warn("用户服务降级处理: deleteUser, id={}", id);
throw new ServiceUnavailableException("用户服务暂时不可用,无法删除用户");
}
}
熔断器配置:
@Service
@Slf4j
public class PaymentService {
@HystrixCommand(fallbackMethod = "processPaymentFallback",
commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "10"),
@HystrixProperty(name = "maxQueueSize", value = "100"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "80")
})
public PaymentResult processPayment(PaymentRequest request) {
log.info("开始处理支付请求: {}", request.getOrderId());
// 模拟支付处理
if (Math.random() > 0.7) {
throw new RuntimeException("支付服务异常");
}
// 模拟网络延迟
try {
Thread.sleep(1000 + (long)(Math.random() * 2000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("支付处理被中断", e);
}
PaymentResult result = new PaymentResult();
result.setSuccess(true);
result.setTransactionId(UUID.randomUUID().toString());
result.setAmount(request.getAmount());
result.setOrderId(request.getOrderId());
result.setProcessedAt(LocalDateTime.now());
result.setPaymentMethod(request.getPaymentMethod());
log.info("支付处理成功: {}", result.getTransactionId());
return result;
}
public PaymentResult processPaymentFallback(PaymentRequest request, Throwable throwable) {
log.error("支付服务降级处理: {}", request.getOrderId(), throwable);
PaymentResult result = new PaymentResult();
result.setSuccess(false);
result.setMessage("支付服务暂时不可用,请稍后重试");
result.setOrderId(request.getOrderId());
result.setProcessedAt(LocalDateTime.now());
result.setErrorCode("SERVICE_UNAVAILABLE");
result.setErrorDetails(throwable != null ? throwable.getMessage() : "未知错误");
return result;
}
@HystrixCommand(fallbackMethod = "refundPaymentFallback")
public RefundResult refundPayment(String transactionId, BigDecimal amount) {
log.info("开始处理退款请求: {}", transactionId);
// 模拟退款处理
if (Math.random() > 0.8) {
throw new RuntimeException("退款服务异常");
}
RefundResult result = new RefundResult();
result.setSuccess(true);
result.setRefundId(UUID.randomUUID().toString());
result.setTransactionId(transactionId);
result.setAmount(amount);
result.setProcessedAt(LocalDateTime.now());
log.info("退款处理成功: {}", result.getRefundId());
return result;
}
public RefundResult refundPaymentFallback(String transactionId, BigDecimal amount, Throwable throwable) {
log.error("退款服务降级处理: {}", transactionId, throwable);
RefundResult result = new RefundResult();
result.setSuccess(false);
result.setMessage("退款服务暂时不可用,请稍后重试");
result.setTransactionId(transactionId);
result.setAmount(amount);
result.setProcessedAt(LocalDateTime.now());
result.setErrorCode("SERVICE_UNAVAILABLE");
return result;
}
}
microservice-demo/
├── eureka-server/ # 注册中心
│ ├── src/main/java/
│ ├── src/main/resources/
│ └── pom.xml
├── config-server/ # 配置中心
│ ├── src/main/java/
│ ├── src/main/resources/
│ └── pom.xml
├── api-gateway/ # API网关
│ ├── src/main/java/
│ ├── src/main/resources/
│ └── pom.xml
├── user-service/ # 用户服务
│ ├── src/main/java/
│ ├── src/main/resources/
│ └── pom.xml
├── order-service/ # 订单服务
│ ├── src/main/java/
│ ├── src/main/resources/
│ └── pom.xml
├── notification-service/ # 通知服务
│ ├── src/main/java/
│ ├── src/main/resources/
│ └── pom.xml
└── common/ # 公共模块
├── model/ # 实体类
├── dto/ # 数据传输对象
├── util/ # 工具类
└── pom.xml
异步消息处理:
// 消息发布者
@Component
@Slf4j
public class OrderEventPublisher {
@Autowired
private RabbitTemplate rabbitTemplate;
@Value("${app.rabbitmq.exchange.order}")
private String orderExchange;
@Value("${app.rabbitmq.routing-key.order.created}")
private String orderCreatedRoutingKey;
public void publishOrderCreated(Order order) {
try {
OrderEvent event = new OrderEvent();
event.setOrderId(order.getId());
event.setUserId(order.getUserId());
event.setAmount(order.getAmount());
event.setEventType("ORDER_CREATED");
event.setTimestamp(LocalDateTime.now());
event.setOrderNumber(order.getOrderNumber());
// 设置消息属性
MessageProperties properties = new MessageProperties();
properties.setContentType("application/json");
properties.setMessageId(UUID.randomUUID().toString());
properties.setTimestamp(new Date());
properties.setHeader("eventType", "ORDER_CREATED");
properties.setHeader("serviceName", "order-service");
Message message = new Message(JSON.toJSONBytes(event), properties);
rabbitTemplate.convertAndSend(orderExchange, orderCreatedRoutingKey, message);
log.info("订单创建事件发布成功: {}", order.getOrderNumber());
} catch (Exception e) {
log.error("订单创建事件发布失败: {}", order.getOrderNumber(), e);
throw new EventPublishException("事件发布失败", e);
}
}
public void publishOrderPaid(Order order, PaymentResult payment) {
try {
OrderEvent event = new OrderEvent();
event.setOrderId(order.getId());
event.setUserId(order.getUserId());
event.setAmount(order.getAmount());
event.setEventType("ORDER_PAID");
event.setTimestamp(LocalDateTime.now());
event.setOrderNumber(order.getOrderNumber());
event.setTransactionId(payment.getTransactionId());
MessageProperties properties = new MessageProperties();
properties.setContentType("application/json");
properties.setMessageId(UUID.randomUUID().toString());
properties.setTimestamp(new Date());
properties.setHeader("eventType", "ORDER_PAID");
properties.setHeader("serviceName", "order-service");
Message message = new Message(JSON.toJSONBytes(event), properties);
rabbitTemplate.convertAndSend(orderExchange, "order.paid", message);
log.info("订单支付事件发布成功: {}", order.getOrderNumber());
} catch (Exception e) {
log.error("订单支付事件发布失败: {}", order.getOrderNumber(), e);
throw new EventPublishException("事件发布失败", e);
}
}
}
// 消息消费者
@Component
@RabbitListener(queues = "order.notification.queue")
@Slf4j
public class OrderNotificationConsumer {
@Autowired
private NotificationService notificationService;
@RabbitHandler
public void handleOrderCreated(OrderEvent event) {
try {
log.info("接收到订单创建事件: {}", event.getOrderId());
// 发送订单创建通知
notificationService.sendOrderCreatedNotification(event);
log.info("订单创建通知发送成功: {}", event.getOrderId());
} catch (Exception e) {
log.error("订单创建通知发送失败: {}", event.getOrderId(), e);
// 可以在这里实现重试机制或死信队列处理
}
}
@RabbitHandler
public void handleOrderPaid(OrderEvent event) {
try {
log.info("接收到订单支付事件: {}", event.getOrderId());
// 发送订单支付通知
notificationService.sendOrderPaidNotification(event);
log.info("订单支付通知发送成功: {}", event.getOrderId());
} catch (Exception e) {
log.error("订单支付通知发送失败: {}", event.getOrderId(), e);
}
}
}
// 订单事件实体
public class OrderEvent {
private Long orderId;
private Long userId;
private BigDecimal amount;
private String eventType;
private LocalDateTime timestamp;
private String orderNumber;
private String transactionId;
// 构造函数
public OrderEvent() {}
// getter和setter方法
public Long getOrderId() { return orderId; }
public void setOrderId(Long orderId) { this.orderId = orderId; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getEventType() { return eventType; }
public void setEventType(String eventType) { this.eventType = eventType; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
public String getOrderNumber() { return orderNumber; }
public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
public String getTransactionId() { return transactionId; }
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
}
Saga模式实现:
// Saga编排器
@Component
@Slf4j
public class OrderSagaOrchestrator {
@Autowired
private UserServiceClient userServiceClient;
@Autowired
private PaymentServiceClient paymentServiceClient;
@Autowired
private InventoryServiceClient inventoryServiceClient;
@Autowired
private OrderEventPublisher eventPublisher;
public OrderResult processOrder(OrderRequest request) {
log.info("开始处理订单Saga: {}", request.getOrderNumber());
List<SagaStep> steps = new ArrayList<>();
// 步骤1:验证用户
steps.add(new SagaStep("validateUser",
() -> {
log.info("执行用户验证步骤");
User user = userServiceClient.getUserById(request.getUserId());
if (user == null || user.getStatus() != UserStatus.ACTIVE) {
throw new UserValidationException("用户验证失败");
}
return user;
},
() -> {
log.info("补偿用户验证步骤");
// 用户验证步骤通常不需要补偿
}));
// 步骤2:扣减库存
steps.add(new SagaStep("reserveInventory",
() -> {
log.info("执行库存扣减步骤");
InventoryReservation reservation = inventoryServiceClient.reserve(
request.getProductId(), request.getQuantity());
if (reservation == null || !reservation.isSuccess()) {
throw new InventoryException("库存扣减失败");
}
return reservation;
},
() -> {
log.info("补偿库存扣减步骤");
inventoryServiceClient.release(request.getProductId(), request.getQuantity());
}));
// 步骤3:处理支付
steps.add(new SagaStep("processPayment",
() -> {
log.info("执行支付处理步骤");
PaymentRequest paymentRequest = new PaymentRequest();
paymentRequest.setOrderId(request.getOrderId());
paymentRequest.setAmount(request.getAmount());
paymentRequest.setPaymentMethod(request.getPaymentMethod());
PaymentResult payment = paymentServiceClient.charge(paymentRequest);
if (payment == null || !payment.isSuccess()) {
throw new PaymentException("支付处理失败");
}
return payment;
},
() -> {
log.info("补偿支付处理步骤");
paymentServiceClient.refund(request.getTransactionId(), request.getAmount());
}));
// 步骤4:创建订单
steps.add(new SagaStep("createOrder",
() -> {
log.info("执行订单创建步骤");
Order order = createOrder(request);
eventPublisher.publishOrderCreated(order);
return order;
},
() -> {
log.info("补偿订单创建步骤");
// 删除已创建的订单
deleteOrder(request.getOrderId());
}));
return executeSaga(steps);
}
private OrderResult executeSaga(List<SagaStep> steps) {
List<SagaStep> executedSteps = new ArrayList<>();
try {
for (SagaStep step : steps) {
log.info("执行Saga步骤: {}", step.getName());
step.execute();
executedSteps.add(step);
log.info("Saga步骤执行成功: {}", step.getName());
}
log.info("Saga执行成功");
return OrderResult.success("订单处理成功");
} catch (Exception e) {
log.error("Saga执行失败,开始补偿操作", e);
// 补偿执行
Collections.reverse(executedSteps);
for (SagaStep step : executedSteps) {
try {
log.info("执行补偿步骤: {}", step.getName());
step.compensate();
log.info("补偿步骤执行成功: {}", step.getName());
} catch (Exception ex) {
log.error("补偿操作失败: {}", step.getName(), ex);
// 记录补偿失败,可能需要人工干预
}
}
return OrderResult.failure("订单处理失败: " + e.getMessage());
}
}
private Order createOrder(OrderRequest request) {
Order order = new Order();
order.setId(request.getOrderId());
order.setUserId(request.getUserId());
order.setAmount(request.getAmount());
order.setStatus(OrderStatus.PENDING);
order.setCreatedAt(LocalDateTime.now());
order.setOrderNumber(request.getOrderNumber());
return orderRepository.save(order);
}
private void deleteOrder(Long orderId) {
orderRepository.deleteById(orderId);
}
}
// Saga步骤类
public class SagaStep {
private final String name;
private final Runnable executeAction;
private final Runnable compensateAction;
public SagaStep(String name, Runnable executeAction, Runnable compensateAction) {
this.name = name;
this.executeAction = executeAction;
this.compensateAction = compensateAction;
}
public void execute() {
executeAction.run();
}
public void compensate() {
compensateAction.run();
}
public String getName() {
return name;
}
}
// 订单结果类
public class OrderResult {
private boolean success;
private String message;
private String orderId;
private LocalDateTime timestamp;
public OrderResult(boolean success, String message) {
this.success = success;
this.message = message;
this.timestamp = LocalDateTime.now();
}
public static OrderResult success(String message) {
return new OrderResult(true, message);
}
public static OrderResult failure(String message) {
return new OrderResult(false, message);
}
// getter和setter方法
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
}
健康检查配置:
// 自定义健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public Health health() {
Health.Builder builder = new Health.Builder();
// 检查数据库连接
try {
Connection connection = dataSource.getConnection();
if (connection.isValid(5)) {
builder.withDetail("database", "UP");
connection.close();
} else {
builder.withDetail("database", "DOWN");
builder.down();
}
} catch (Exception e) {
builder.withDetail("database", "DOWN");
builder.withDetail("database.error", e.getMessage());
builder.down();
}
// 检查Redis连接
try {
redisTemplate.opsForValue().get("health-check");
builder.withDetail("redis", "UP");
} catch (Exception e) {
builder.withDetail("redis", "DOWN");
builder.withDetail("redis.error", e.getMessage());
builder.down();
}
// 检查内存使用情况
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
builder.withDetail("memory.used", usedMemory);
builder.withDetail("memory.max", maxMemory);
builder.withDetail("memory.usage", (double) usedMemory / maxMemory * 100);
// 检查磁盘空间
File root = new File("/");
long totalSpace = root.getTotalSpace();
long freeSpace = root.getFreeSpace();
long usedSpace = totalSpace - freeSpace;
builder.withDetail("disk.used", usedSpace);
builder.withDetail("disk.total", totalSpace);
builder.withDetail("disk.usage", (double) usedSpace / totalSpace * 100);
return builder.build();
}
}
// 服务信息端点
@Component
@Endpoint(id = "service-info")
public class ServiceInfoEndpoint {
@Value("${spring.application.name}")
private String applicationName;
@Value("${spring.application.version:1.0.0}")
private String version;
@Value("${server.port}")
private String port;
@ReadOperation
public Map<String, Object> serviceInfo() {
Map<String, Object> info = new HashMap<>();
info.put("name", applicationName);
info.put("version", version);
info.put("port", port);
info.put("uptime", getUptime());
info.put("timestamp", LocalDateTime.now());
info.put("jvm", getJvmInfo());
return info;
}
private String getUptime() {
long uptime = ManagementFactory.getRuntimeMXBean().getUptime();
return Duration.ofMillis(uptime).toString();
}
private Map<String, Object> getJvmInfo() {
Map<String, Object> jvmInfo = new HashMap<>();
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
jvmInfo.put("name", runtimeBean.getVmName());
jvmInfo.put("version", runtimeBean.getVmVersion());
jvmInfo.put("vendor", runtimeBean.getVmVendor());
return jvmInfo;
}
}
Spring Boot高级特性
微服务架构设计
Spring Cloud基础组件
分布式系统实践
练习1:自定义Starter开发
练习2:微服务拆分实战
练习3:分布式事务处理
练习4:服务监控和治理
问题1:服务注册失败
# 解决方案:检查Eureka配置
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${server.port}
问题2:配置刷新不生效
// 解决方案:添加@RefreshScope注解
@RestController
@RefreshScope
public class ConfigController {
@Value("${app.name}")
private String appName;
}
问题3:Feign调用超时
# 解决方案:配置超时时间
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 10000
问题4:熔断器不生效
// 解决方案:检查Hystrix配置
@HystrixCommand(fallbackMethod = "fallbackMethod",
commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10")
})
public String serviceMethod() {
// 服务调用逻辑
}
第24天将学习:
继续加油,您已经掌握了微服务架构的核心概念!