一、项目初始化与服务拆分
1.1 父工程依赖配置 (pom.xml)
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>
1.2 用户服务实现 (UserServiceApplication.java)
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class UserServiceApplication {public static void main(String[] args) {SpringApplication.run(UserServiceApplication.class, args);}
}@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;private String email;
}@Repository
public interface UserRepository extends JpaRepository<User, Long> {User findByUsername(String username);
}@RestController
@RequestMapping("/api/users")
public class UserController {@Autowiredprivate UserRepository userRepository;@GetMapping("/{id}")public ResponseEntity<User> getUser(@PathVariable Long id) {return userRepository.findById(id).map(user -> ResponseEntity.ok().body(user)).orElse(ResponseEntity.notFound().build());}@PostMappingpublic User createUser(@RequestBody User user) {return userRepository.save(user);}
}
二、服务间通信实现
2.1 Feign 客户端定义 (OrderClient.java)
@FeignClient(name = "order-service", url = "${order.service.url}")
public interface OrderClient {@GetMapping("/api/orders/user/{userId}")List<Order> getOrdersByUserId(@PathVariable Long userId);@PostMapping("/api/orders")Order createOrder(@RequestBody OrderRequest request);
}@Data
class OrderRequest {private Long productId;private Integer quantity;private BigDecimal totalAmount;
}@Data
class Order {private Long id;private Long userId;private LocalDateTime createTime;private BigDecimal amount;
}
2.2 业务服务调用 (UserServiceImpl.java)
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {private final UserRepository userRepository;private final OrderClient orderClient;@Overridepublic UserProfile getUserWithOrders(Long userId) {User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("User not found"));List<Order> orders = orderClient.getOrdersByUserId(userId);return new UserProfile(user, orders);}
}
三、服务注册与发现配置
3.1 application.yml 核心配置
spring:application:name: user-servicecloud:config:uri: http://config-server:8888fail-fast: trueenabled: trueloadbalancer:retry: falseinstance-list: true # 启用实例列表缓存机制eureka:client:serviceUrl:defaultZone: http://eureka-server:8761/eureka/apps/healthcheck:enabled: trueleaseRenewalIntervalInSeconds: 10 # 心跳间隔优化为10秒高频次保活,实际生产环境建议设置为30~60秒。这种高频心跳机制虽然能更快检测服务状态,但会增加网络负载,需根据集群规模权衡配置。对于大型微服务集群,建议采用自适应心跳策略,结合服务重要性分级设置不同频率,既能保证关键服务的高可用性,又能降低非核心服务的心跳开销。
instance:preferIpAddress: true # 优先使用IP地址注册而非主机名,避免DNS解析问题,尤其适用于容器化部署场景。在K8s等容器编排系统中,Pod IP动态变化时,该配置可确保服务发现准确性。建议配合Kubernetes Service资源一起使用,通过Headless Service实现稳定的服务注册。
nonSecurePortEnabled: false # 禁用非安全端口注册,强制使用HTTPS进行服务间通信,提升安全性。生产环境应始终启用此配置,并配置TLS证书验证机制。
metadataMap: # 自定义元数据标签,可用于灰度发布、金丝雀部署等场景的流量路由控制。例如根据环境标记区分dev/test/prod环境实例,实现按环境分流。environment: ${ENVIRONMENT:dev}version: ${BUILD_VERSION:v1.0}
四、动态配置管理
4.1 配置中心客户端配置 (bootstrap.yml)
spring:cloud:config:profile: ${ENVIRONMENT:dev} # 多环境配置切换label: main # Git分支/标签对应配置版本discovery:enabled: true # 从注册中心获取配置信息service-id: config-server # 配置服务中心名称
4.2 配置变更监听实现 (ConfigRefreshListener.java)
@Component
public class ConfigRefreshListener implements ApplicationListener<EnvironmentChangeEvent> {private static final Logger log = LoggerFactory.getLogger(ConfigRefreshListener.class);@Overridepublic void onApplicationEvent(EnvironmentChangeEvent event) {Set<String> changedKeys = event.getKeys();if (!changedKeys.isEmpty()) {log.info("Configuration changed: {}", changedKeys);// 执行配置变更后的处理逻辑changedKeys.forEach(key -> {String newValue = event.getProperty(key);log.debug("{} = {}", key, newValue);});}}
}