1. 微服务架构概述
微服务架构是一种将单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,并通过轻量级机制(通常是HTTP API)进行通信。本文将详细介绍用户服务、API网关、设备服务、订单服务、接入服务等核心微服务的设计与实现,以及微服务架构的最佳实践。
1.1 微服务架构的优势
- 技术多样性: 每个服务可以使用不同的技术栈
- 独立部署: 服务可以独立开发、测试和部署
- 可扩展性: 可以针对特定服务进行水平扩展
- 故障隔离: 单个服务的故障不会影响整个系统
- 团队自治: 不同团队可以独立开发不同的服务
1.2 微服务架构挑战
- 分布式复杂性: 网络延迟、故障处理、数据一致性
- 服务治理: 服务发现、负载均衡、熔断降级
- 数据管理: 分布式事务、数据一致性
- 监控运维: 分布式链路追踪、日志聚合
- 安全管控: 服务间认证、授权、数据加密
1.3 微服务架构设计原则
1 2 3 4 5 6 7 8 9 10 11 12 13
| ┌─────────────────────────────────────────┐ │ 客户端层 │ │ (Web应用、移动应用、第三方系统) │ ├─────────────────────────────────────────┤ │ API网关层 │ │ (路由、认证、限流、监控、日志) │ ├─────────────────────────────────────────┤ │ 业务服务层 │ │ (用户服务、设备服务、订单服务、接入服务) │ ├─────────────────────────────────────────┤ │ 基础设施层 │ │ (服务注册、配置中心、消息队列、数据库) │ └─────────────────────────────────────────┘
|
2. 用户服务设计与实现
2.1 用户服务架构设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(unique = true, nullable = false) private String email; @Column(nullable = false) private String password; @Enumerated(EnumType.STRING) private UserStatus status; @Enumerated(EnumType.STRING) private UserRole role; @CreationTimestamp private LocalDateTime createdAt; @UpdateTimestamp private LocalDateTime updatedAt; }
public enum UserStatus { ACTIVE, INACTIVE, SUSPENDED, PENDING_VERIFICATION }
public enum UserRole { ADMIN, USER, OPERATOR, GUEST }
|
2.2 用户服务API设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| @RestController @RequestMapping("/api/users") @Validated public class UserController { private final UserService userService; private final UserMapper userMapper; public UserController(UserService userService, UserMapper userMapper) { this.userService = userService; this.userMapper = userMapper; } @PostMapping("/register") public ResponseEntity<UserResponse> register(@Valid @RequestBody UserRegistrationRequest request) { User user = userService.register(request); UserResponse response = userMapper.toResponse(user); return ResponseEntity.status(HttpStatus.CREATED).body(response); } @PostMapping("/login") public ResponseEntity<LoginResponse> login(@Valid @RequestBody LoginRequest request) { LoginResponse response = userService.login(request); return ResponseEntity.ok(response); } @GetMapping("/{id}") public ResponseEntity<UserResponse> getUser(@PathVariable Long id) { User user = userService.findById(id); UserResponse response = userMapper.toResponse(user); return ResponseEntity.ok(response); } @PutMapping("/{id}") public ResponseEntity<UserResponse> updateUser( @PathVariable Long id, @Valid @RequestBody UserUpdateRequest request) { User user = userService.updateUser(id, request); UserResponse response = userMapper.toResponse(user); return ResponseEntity.ok(response); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable Long id) { userService.deleteUser(id); return ResponseEntity.noContent().build(); } @GetMapping public ResponseEntity<Page<UserResponse>> getUsers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String keyword) { Page<User> users = userService.findUsers(page, size, keyword); Page<UserResponse> responses = users.map(userMapper::toResponse); return ResponseEntity.ok(responses); } @PostMapping("/authenticate") public ResponseEntity<AuthenticationResponse> authenticate(@RequestBody AuthenticationRequest request) { AuthenticationResponse response = userService.authenticate(request); return ResponseEntity.ok(response); } @PostMapping("/refresh-token") public ResponseEntity<TokenResponse> refreshToken(@RequestBody RefreshTokenRequest request) { TokenResponse response = userService.refreshToken(request); return ResponseEntity.ok(response); } }
|
2.3 用户服务业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
| @Service @Transactional public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final JwtTokenProvider jwtTokenProvider; private final RedisTemplate<String, Object> redisTemplate; private final EmailService emailService; public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtTokenProvider jwtTokenProvider, RedisTemplate<String, Object> redisTemplate, EmailService emailService) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; this.jwtTokenProvider = jwtTokenProvider; this.redisTemplate = redisTemplate; this.emailService = emailService; } @Override public User register(UserRegistrationRequest request) { if (userRepository.existsByUsername(request.getUsername())) { throw new BusinessException("用户名已存在"); } if (userRepository.existsByEmail(request.getEmail())) { throw new BusinessException("邮箱已存在"); } User user = new User(); user.setUsername(request.getUsername()); user.setEmail(request.getEmail()); user.setPassword(passwordEncoder.encode(request.getPassword())); user.setStatus(UserStatus.PENDING_VERIFICATION); user.setRole(UserRole.USER); User savedUser = userRepository.save(user); emailService.sendVerificationEmail(savedUser); return savedUser; } @Override public LoginResponse login(LoginRequest request) { User user = userRepository.findByUsername(request.getUsername()) .orElseThrow(() -> new BusinessException("用户名或密码错误")); if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { throw new BusinessException("用户名或密码错误"); } if (user.getStatus() != UserStatus.ACTIVE) { throw new BusinessException("用户账户未激活或被禁用"); } String accessToken = jwtTokenProvider.generateAccessToken(user); String refreshToken = jwtTokenProvider.generateRefreshToken(user); String tokenKey = "user:token:" + user.getId(); redisTemplate.opsForValue().set(tokenKey, accessToken, Duration.ofHours(24)); user.setLastLoginAt(LocalDateTime.now()); userRepository.save(user); return LoginResponse.builder() .accessToken(accessToken) .refreshToken(refreshToken) .tokenType("Bearer") .expiresIn(3600) .user(UserResponse.builder() .id(user.getId()) .username(user.getUsername()) .email(user.getEmail()) .role(user.getRole()) .status(user.getStatus()) .build()) .build(); } @Override public User findById(Long id) { return userRepository.findById(id) .orElseThrow(() -> new BusinessException("用户不存在")); } @Override public User updateUser(Long id, UserUpdateRequest request) { User user = findById(id); if (request.getEmail() != null) { user.setEmail(request.getEmail()); } if (request.getPassword() != null) { user.setPassword(passwordEncoder.encode(request.getPassword())); } return userRepository.save(user); } @Override public void deleteUser(Long id) { User user = findById(id); userRepository.delete(user); String tokenKey = "user:token:" + id; redisTemplate.delete(tokenKey); } @Override public Page<User> findUsers(int page, int size, String keyword) { Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending()); if (StringUtils.hasText(keyword)) { return userRepository.findByUsernameContainingOrEmailContaining( keyword, keyword, pageable); } return userRepository.findAll(pageable); } @Override public AuthenticationResponse authenticate(AuthenticationRequest request) { String token = request.getToken(); if (!jwtTokenProvider.validateToken(token)) { throw new BusinessException("无效的Token"); } String username = jwtTokenProvider.getUsernameFromToken(token); User user = userRepository.findByUsername(username) .orElseThrow(() -> new BusinessException("用户不存在")); return AuthenticationResponse.builder() .authenticated(true) .user(UserResponse.builder() .id(user.getId()) .username(user.getUsername()) .email(user.getEmail()) .role(user.getRole()) .status(user.getStatus()) .build()) .build(); } @Override public TokenResponse refreshToken(RefreshTokenRequest request) { String refreshToken = request.getRefreshToken(); if (!jwtTokenProvider.validateRefreshToken(refreshToken)) { throw new BusinessException("无效的刷新Token"); } String username = jwtTokenProvider.getUsernameFromRefreshToken(refreshToken); User user = userRepository.findByUsername(username) .orElseThrow(() -> new BusinessException("用户不存在")); String newAccessToken = jwtTokenProvider.generateAccessToken(user); return TokenResponse.builder() .accessToken(newAccessToken) .tokenType("Bearer") .expiresIn(3600) .build(); } }
|
3. API网关设计与实现
3.1 Spring Cloud Gateway配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @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 .addRequestHeader("X-Service", "user-service") .addResponseHeader("X-Response-Time", LocalDateTime.now().toString()) .circuitBreaker(config -> config .setName("user-service-cb") .setFallbackUri("forward:/fallback/user-service"))) .uri("lb://user-service")) .route("device-service", r -> r.path("/api/devices/**") .filters(f -> f .addRequestHeader("X-Service", "device-service") .circuitBreaker(config -> config .setName("device-service-cb") .setFallbackUri("forward:/fallback/device-service"))) .uri("lb://device-service")) .route("order-service", r -> r.path("/api/orders/**") .filters(f -> f .addRequestHeader("X-Service", "order-service") .circuitBreaker(config -> config .setName("order-service-cb") .setFallbackUri("forward:/fallback/order-service"))) .uri("lb://order-service")) .route("access-service", r -> r.path("/api/access/**") .filters(f -> f .addRequestHeader("X-Service", "access-service") .circuitBreaker(config -> config .setName("access-service-cb") .setFallbackUri("forward:/fallback/access-service"))) .uri("lb://access-service")) .build(); } @Bean public GlobalFilter customGlobalFilter() { return new CustomGlobalFilter(); } @Bean public GatewayFilter customGatewayFilter() { return new CustomGatewayFilter(); } }
|
3.2 全局过滤器实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Component @Order(-1) public class CustomGlobalFilter implements GlobalFilter { private static final Logger logger = LoggerFactory.getLogger(CustomGlobalFilter.class); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); logger.info("Request: {} {}", request.getMethod(), request.getURI()); String requestId = UUID.randomUUID().toString(); ServerHttpRequest mutatedRequest = request.mutate() .header("X-Request-ID", requestId) .build(); ServerWebExchange mutatedExchange = exchange.mutate() .request(mutatedRequest) .build(); return chain.filter(mutatedExchange) .doOnSuccess(aVoid -> { ServerHttpResponse response = mutatedExchange.getResponse(); logger.info("Response: {} {}", response.getStatusCode(), requestId); }) .doOnError(throwable -> { logger.error("Error processing request: {} - {}", requestId, throwable.getMessage()); }); } }
|
3.3 认证授权过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| @Component public class AuthenticationFilter implements GlobalFilter { private final UserServiceClient userServiceClient; private final RedisTemplate<String, Object> redisTemplate; public AuthenticationFilter(UserServiceClient userServiceClient, RedisTemplate<String, Object> redisTemplate) { this.userServiceClient = userServiceClient; this.redisTemplate = redisTemplate; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); if (isPublicPath(path)) { return chain.filter(exchange); } String token = extractToken(request); if (token == null) { return unauthorizedResponse(exchange); } return validateToken(token) .flatMap(user -> { ServerHttpRequest mutatedRequest = request.mutate() .header("X-User-ID", user.getId().toString()) .header("X-User-Role", user.getRole().name()) .build(); ServerWebExchange mutatedExchange = exchange.mutate() .request(mutatedRequest) .build(); return chain.filter(mutatedExchange); }) .onErrorResume(throwable -> unauthorizedResponse(exchange)); } private boolean isPublicPath(String path) { List<String> publicPaths = Arrays.asList( "/api/users/register", "/api/users/login", "/api/users/refresh-token", "/actuator/health" ); return publicPaths.stream().anyMatch(path::startsWith); } private String extractToken(ServerHttpRequest request) { String authorization = request.getHeaders().getFirst("Authorization"); if (authorization != null && authorization.startsWith("Bearer ")) { return authorization.substring(7); } return null; } private Mono<UserResponse> validateToken(String token) { String cacheKey = "token:" + token; UserResponse cachedUser = (UserResponse) redisTemplate.opsForValue().get(cacheKey); if (cachedUser != null) { return Mono.just(cachedUser); } return userServiceClient.authenticate(AuthenticationRequest.builder() .token(token) .build()) .map(AuthenticationResponse::getUser) .doOnNext(user -> { redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30)); }); } private Mono<Void> unauthorizedResponse(ServerWebExchange exchange) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); response.getHeaders().add("Content-Type", "application/json"); String body = "{\"error\":\"Unauthorized\",\"message\":\"Invalid or missing token\"}"; DataBuffer buffer = response.bufferFactory().wrap(body.getBytes()); return response.writeWith(Mono.just(buffer)); } }
|
4. 设备服务设计与实现
4.1 设备服务实体设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| @Entity @Table(name = "devices") public class Device { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String deviceId; @Column(nullable = false) private String deviceName; @Column(nullable = false) private String deviceType; @Column(nullable = false) private String manufacturer; @Column(nullable = false) private String model; @Column(nullable = false) private String serialNumber; @Enumerated(EnumType.STRING) private DeviceStatus status; @Column(nullable = false) private Long userId; @Column private String location; @Column private String description; @Column private String firmwareVersion; @Column private String hardwareVersion; @CreationTimestamp private LocalDateTime createdAt; @UpdateTimestamp private LocalDateTime updatedAt; @Column private LocalDateTime lastOnlineAt; }
public enum DeviceStatus { ONLINE, OFFLINE, MAINTENANCE, ERROR, DISABLED }
public enum DeviceType { SENSOR, ACTUATOR, GATEWAY, CAMERA, CONTROLLER }
|
4.2 设备服务API设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| @RestController @RequestMapping("/api/devices") @Validated public class DeviceController { private final DeviceService deviceService; private final DeviceMapper deviceMapper; public DeviceController(DeviceService deviceService, DeviceMapper deviceMapper) { this.deviceService = deviceService; this.deviceMapper = deviceMapper; } @PostMapping("/register") public ResponseEntity<DeviceResponse> registerDevice(@Valid @RequestBody DeviceRegistrationRequest request) { Device device = deviceService.registerDevice(request); DeviceResponse response = deviceMapper.toResponse(device); return ResponseEntity.status(HttpStatus.CREATED).body(response); } @GetMapping("/{id}") public ResponseEntity<DeviceResponse> getDevice(@PathVariable Long id) { Device device = deviceService.findById(id); DeviceResponse response = deviceMapper.toResponse(device); return ResponseEntity.ok(response); } @PutMapping("/{id}") public ResponseEntity<DeviceResponse> updateDevice( @PathVariable Long id, @Valid @RequestBody DeviceUpdateRequest request) { Device device = deviceService.updateDevice(id, request); DeviceResponse response = deviceMapper.toResponse(device); return ResponseEntity.ok(response); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteDevice(@PathVariable Long id) { deviceService.deleteDevice(id); return ResponseEntity.noContent().build(); } @GetMapping public ResponseEntity<Page<DeviceResponse>> getUserDevices( @RequestParam Long userId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) DeviceStatus status) { Page<Device> devices = deviceService.findUserDevices(userId, page, size, status); Page<DeviceResponse> responses = devices.map(deviceMapper::toResponse); return ResponseEntity.ok(responses); } @PostMapping("/{id}/online") public ResponseEntity<DeviceResponse> deviceOnline(@PathVariable Long id) { Device device = deviceService.deviceOnline(id); DeviceResponse response = deviceMapper.toResponse(device); return ResponseEntity.ok(response); } @PostMapping("/{id}/offline") public ResponseEntity<DeviceResponse> deviceOffline(@PathVariable Long id) { Device device = deviceService.deviceOffline(id); DeviceResponse response = deviceMapper.toResponse(device); return ResponseEntity.ok(response); } @GetMapping("/{id}/status") public ResponseEntity<DeviceStatusResponse> getDeviceStatus(@PathVariable Long id) { DeviceStatusResponse response = deviceService.getDeviceStatus(id); return ResponseEntity.ok(response); } @PostMapping("/{id}/command") public ResponseEntity<CommandResponse> sendCommand( @PathVariable Long id, @Valid @RequestBody DeviceCommandRequest request) { CommandResponse response = deviceService.sendCommand(id, request); return ResponseEntity.ok(response); } @GetMapping("/{id}/data") public ResponseEntity<Page<DeviceDataResponse>> getDeviceData( @PathVariable Long id, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime, @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime) { Page<DeviceDataResponse> data = deviceService.getDeviceData(id, page, size, startTime, endTime); return ResponseEntity.ok(data); } }
|
4.3 设备服务业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
| @Service @Transactional public class DeviceServiceImpl implements DeviceService { private final DeviceRepository deviceRepository; private final DeviceDataRepository deviceDataRepository; private final MqttService mqttService; private final RedisTemplate<String, Object> redisTemplate; public DeviceServiceImpl(DeviceRepository deviceRepository, DeviceDataRepository deviceDataRepository, MqttService mqttService, RedisTemplate<String, Object> redisTemplate) { this.deviceRepository = deviceRepository; this.deviceDataRepository = deviceDataRepository; this.mqttService = mqttService; this.redisTemplate = redisTemplate; } @Override public Device registerDevice(DeviceRegistrationRequest request) { if (deviceRepository.existsByDeviceId(request.getDeviceId())) { throw new BusinessException("设备ID已存在"); } Device device = new Device(); device.setDeviceId(request.getDeviceId()); device.setDeviceName(request.getDeviceName()); device.setDeviceType(request.getDeviceType()); device.setManufacturer(request.getManufacturer()); device.setModel(request.getModel()); device.setSerialNumber(request.getSerialNumber()); device.setStatus(DeviceStatus.OFFLINE); device.setUserId(request.getUserId()); device.setLocation(request.getLocation()); device.setDescription(request.getDescription()); device.setFirmwareVersion(request.getFirmwareVersion()); device.setHardwareVersion(request.getHardwareVersion()); Device savedDevice = deviceRepository.save(device); cacheDeviceInfo(savedDevice); return savedDevice; } @Override public Device findById(Long id) { return deviceRepository.findById(id) .orElseThrow(() -> new BusinessException("设备不存在")); } @Override public Device updateDevice(Long id, DeviceUpdateRequest request) { Device device = findById(id); if (request.getDeviceName() != null) { device.setDeviceName(request.getDeviceName()); } if (request.getLocation() != null) { device.setLocation(request.getLocation()); } if (request.getDescription() != null) { device.setDescription(request.getDescription()); } Device savedDevice = deviceRepository.save(device); cacheDeviceInfo(savedDevice); return savedDevice; } @Override public void deleteDevice(Long id) { Device device = findById(id); deviceRepository.delete(device); String cacheKey = "device:" + id; redisTemplate.delete(cacheKey); } @Override public Page<Device> findUserDevices(Long userId, int page, int size, DeviceStatus status) { Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending()); if (status != null) { return deviceRepository.findByUserIdAndStatus(userId, status, pageable); } return deviceRepository.findByUserId(userId, pageable); } @Override public Device deviceOnline(Long id) { Device device = findById(id); device.setStatus(DeviceStatus.ONLINE); device.setLastOnlineAt(LocalDateTime.now()); Device savedDevice = deviceRepository.save(device); cacheDeviceInfo(savedDevice); publishDeviceEvent(savedDevice, "ONLINE"); return savedDevice; } @Override public Device deviceOffline(Long id) { Device device = findById(id); device.setStatus(DeviceStatus.OFFLINE); Device savedDevice = deviceRepository.save(device); cacheDeviceInfo(savedDevice); publishDeviceEvent(savedDevice, "OFFLINE"); return savedDevice; } @Override public DeviceStatusResponse getDeviceStatus(Long id) { Device device = findById(id); boolean isOnline = device.getLastOnlineAt() != null && device.getLastOnlineAt().isAfter(LocalDateTime.now().minusMinutes(5)); return DeviceStatusResponse.builder() .deviceId(device.getId()) .deviceName(device.getDeviceName()) .status(device.getStatus()) .isOnline(isOnline) .lastOnlineAt(device.getLastOnlineAt()) .build(); } @Override public CommandResponse sendCommand(Long id, DeviceCommandRequest request) { Device device = findById(id); if (device.getStatus() != DeviceStatus.ONLINE) { throw new BusinessException("设备离线,无法发送命令"); } DeviceCommand command = DeviceCommand.builder() .deviceId(device.getDeviceId()) .commandType(request.getCommandType()) .parameters(request.getParameters()) .timestamp(LocalDateTime.now()) .build(); String topic = "device/" + device.getDeviceId() + "/command"; mqttService.publish(topic, command); return CommandResponse.builder() .commandId(UUID.randomUUID().toString()) .deviceId(device.getDeviceId()) .status("SENT") .timestamp(LocalDateTime.now()) .build(); } @Override public Page<DeviceDataResponse> getDeviceData(Long id, int page, int size, LocalDateTime startTime, LocalDateTime endTime) { Device device = findById(id); Pageable pageable = PageRequest.of(page, size, Sort.by("timestamp").descending()); Page<DeviceData> data; if (startTime != null && endTime != null) { data = deviceDataRepository.findByDeviceIdAndTimestampBetween( device.getDeviceId(), startTime, endTime, pageable); } else { data = deviceDataRepository.findByDeviceId(device.getDeviceId(), pageable); } return data.map(this::convertToDeviceDataResponse); } private void cacheDeviceInfo(Device device) { String cacheKey = "device:" + device.getId(); redisTemplate.opsForValue().set(cacheKey, device, Duration.ofHours(1)); } private void publishDeviceEvent(Device device, String eventType) { DeviceEvent event = DeviceEvent.builder() .deviceId(device.getId()) .deviceName(device.getDeviceName()) .eventType(eventType) .timestamp(LocalDateTime.now()) .build(); } private DeviceDataResponse convertToDeviceDataResponse(DeviceData data) { return DeviceDataResponse.builder() .id(data.getId()) .deviceId(data.getDeviceId()) .dataType(data.getDataType()) .value(data.getValue()) .unit(data.getUnit()) .timestamp(data.getTimestamp()) .build(); } }
|
5. 订单服务设计与实现
5.1 订单服务实体设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| @Entity @Table(name = "orders") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String orderNumber; @Column(nullable = false) private Long userId; @Enumerated(EnumType.STRING) private OrderStatus status; @Enumerated(EnumType.STRING) private OrderType type; @Column(nullable = false) private BigDecimal totalAmount; @Column private BigDecimal discountAmount; @Column private BigDecimal finalAmount; @Column private String paymentMethod; @Column private String paymentStatus; @Column private String shippingAddress; @Column private String billingAddress; @Column private String notes; @CreationTimestamp private LocalDateTime createdAt; @UpdateTimestamp private LocalDateTime updatedAt; @Column private LocalDateTime paidAt; @Column private LocalDateTime shippedAt; @Column private LocalDateTime deliveredAt; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List<OrderItem> items = new ArrayList<>(); }
public enum OrderStatus { PENDING, CONFIRMED, PAID, SHIPPED, DELIVERED, CANCELLED, REFUNDED }
public enum OrderType { ONLINE, OFFLINE, SUBSCRIPTION, BULK }
@Entity @Table(name = "order_items") public class OrderItem { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "order_id") private Order order; @Column(nullable = false) private String productId; @Column(nullable = false) private String productName; @Column(nullable = false) private Integer quantity; @Column(nullable = false) private BigDecimal unitPrice; @Column(nullable = false) private BigDecimal totalPrice; }
|
5.2 订单服务API设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| @RestController @RequestMapping("/api/orders") @Validated public class OrderController { private final OrderService orderService; private final OrderMapper orderMapper; public OrderController(OrderService orderService, OrderMapper orderMapper) { this.orderService = orderService; this.orderMapper = orderMapper; } @PostMapping public ResponseEntity<OrderResponse> createOrder(@Valid @RequestBody OrderCreateRequest request) { Order order = orderService.createOrder(request); OrderResponse response = orderMapper.toResponse(order); return ResponseEntity.status(HttpStatus.CREATED).body(response); } @GetMapping("/{id}") public ResponseEntity<OrderResponse> getOrder(@PathVariable Long id) { Order order = orderService.findById(id); OrderResponse response = orderMapper.toResponse(order); return ResponseEntity.ok(response); } @PutMapping("/{id}/status") public ResponseEntity<OrderResponse> updateOrderStatus( @PathVariable Long id, @Valid @RequestBody OrderStatusUpdateRequest request) { Order order = orderService.updateOrderStatus(id, request); OrderResponse response = orderMapper.toResponse(order); return ResponseEntity.ok(response); } @PostMapping("/{id}/cancel") public ResponseEntity<OrderResponse> cancelOrder(@PathVariable Long id) { Order order = orderService.cancelOrder(id); OrderResponse response = orderMapper.toResponse(order); return ResponseEntity.ok(response); } @GetMapping public ResponseEntity<Page<OrderResponse>> getUserOrders( @RequestParam Long userId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) OrderStatus status) { Page<Order> orders = orderService.findUserOrders(userId, page, size, status); Page<OrderResponse> responses = orders.map(orderMapper::toResponse); return ResponseEntity.ok(responses); } @PostMapping("/{id}/pay") public ResponseEntity<PaymentResponse> payOrder( @PathVariable Long id, @Valid @RequestBody PaymentRequest request) { PaymentResponse response = orderService.payOrder(id, request); return ResponseEntity.ok(response); } @PostMapping("/{id}/ship") public ResponseEntity<OrderResponse> shipOrder( @PathVariable Long id, @Valid @RequestBody ShippingRequest request) { Order order = orderService.shipOrder(id, request); OrderResponse response = orderMapper.toResponse(order); return ResponseEntity.ok(response); } @PostMapping("/{id}/deliver") public ResponseEntity<OrderResponse> deliverOrder(@PathVariable Long id) { Order order = orderService.deliverOrder(id); OrderResponse response = orderMapper.toResponse(order); return ResponseEntity.ok(response); } @PostMapping("/{id}/refund") public ResponseEntity<RefundResponse> refundOrder( @PathVariable Long id, @Valid @RequestBody RefundRequest request) { RefundResponse response = orderService.refundOrder(id, request); return ResponseEntity.ok(response); } }
|
6. 接入服务设计与实现
6.1 接入服务实体设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| @Entity @Table(name = "access_logs") public class AccessLog { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String requestId; @Column(nullable = false) private String clientId; @Column(nullable = false) private String userId; @Column(nullable = false) private String serviceName; @Column(nullable = false) private String endpoint; @Column(nullable = false) private String method; @Column private String requestBody; @Column private String responseBody; @Column(nullable = false) private Integer statusCode; @Column(nullable = false) private Long responseTime; @Column private String userAgent; @Column private String clientIp; @CreationTimestamp private LocalDateTime createdAt; }
@Entity @Table(name = "access_configs") public class AccessConfig { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String clientId; @Column(nullable = false) private String clientSecret; @Column(nullable = false) private String clientName; @Enumerated(EnumType.STRING) private AccessType accessType; @Column private String allowedServices; @Column private String allowedEndpoints; @Column private Integer rateLimit; @Column private Boolean isActive; @CreationTimestamp private LocalDateTime createdAt; @UpdateTimestamp private LocalDateTime updatedAt; }
public enum AccessType { WEB, MOBILE, API, THIRD_PARTY }
|
6.2 接入服务API设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| @RestController @RequestMapping("/api/access") @Validated public class AccessController { private final AccessService accessService; private final AccessLogService accessLogService; public AccessController(AccessService accessService, AccessLogService accessLogService) { this.accessService = accessService; this.accessLogService = accessLogService; } @PostMapping("/authenticate") public ResponseEntity<AccessTokenResponse> authenticate(@Valid @RequestBody AccessRequest request) { AccessTokenResponse response = accessService.authenticate(request); return ResponseEntity.ok(response); } @PostMapping("/refresh") public ResponseEntity<AccessTokenResponse> refreshToken(@Valid @RequestBody RefreshTokenRequest request) { AccessTokenResponse response = accessService.refreshToken(request); return ResponseEntity.ok(response); } @PostMapping("/validate") public ResponseEntity<ValidationResponse> validateToken(@Valid @RequestBody TokenValidationRequest request) { ValidationResponse response = accessService.validateToken(request); return ResponseEntity.ok(response); } @GetMapping("/client/{clientId}") public ResponseEntity<ClientInfoResponse> getClientInfo(@PathVariable String clientId) { ClientInfoResponse response = accessService.getClientInfo(clientId); return ResponseEntity.ok(response); } @PutMapping("/client/{clientId}") public ResponseEntity<ClientInfoResponse> updateClientConfig( @PathVariable String clientId, @Valid @RequestBody ClientConfigUpdateRequest request) { ClientInfoResponse response = accessService.updateClientConfig(clientId, request); return ResponseEntity.ok(response); } @GetMapping("/logs") public ResponseEntity<Page<AccessLogResponse>> getAccessLogs( @RequestParam(required = false) String clientId, @RequestParam(required = false) String userId, @RequestParam(required = false) String serviceName, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime, @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime) { Page<AccessLogResponse> logs = accessLogService.getAccessLogs( clientId, userId, serviceName, page, size, startTime, endTime); return ResponseEntity.ok(logs); } @GetMapping("/stats") public ResponseEntity<AccessStatsResponse> getAccessStats( @RequestParam(required = false) String clientId, @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { AccessStatsResponse response = accessLogService.getAccessStats(clientId, startDate, endDate); return ResponseEntity.ok(response); } @PostMapping("/revoke") public ResponseEntity<Void> revokeToken(@Valid @RequestBody TokenRevokeRequest request) { accessService.revokeToken(request); return ResponseEntity.ok().build(); } }
|
6.3 接入服务业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
| @Service @Transactional public class AccessServiceImpl implements AccessService { private final AccessConfigRepository accessConfigRepository; private final RedisTemplate<String, Object> redisTemplate; private final JwtTokenProvider jwtTokenProvider; public AccessServiceImpl(AccessConfigRepository accessConfigRepository, RedisTemplate<String, Object> redisTemplate, JwtTokenProvider jwtTokenProvider) { this.accessConfigRepository = accessConfigRepository; this.redisTemplate = redisTemplate; this.jwtTokenProvider = jwtTokenProvider; } @Override public AccessTokenResponse authenticate(AccessRequest request) { AccessConfig config = accessConfigRepository.findByClientId(request.getClientId()) .orElseThrow(() -> new BusinessException("无效的客户端ID")); if (!config.getClientSecret().equals(request.getClientSecret())) { throw new BusinessException("无效的客户端密钥"); } if (!config.getIsActive()) { throw new BusinessException("客户端已被禁用"); } String accessToken = generateAccessToken(config); String refreshToken = generateRefreshToken(config); String accessTokenKey = "access_token:" + request.getClientId(); String refreshTokenKey = "refresh_token:" + request.getClientId(); redisTemplate.opsForValue().set(accessTokenKey, accessToken, Duration.ofHours(1)); redisTemplate.opsForValue().set(refreshTokenKey, refreshToken, Duration.ofDays(7)); return AccessTokenResponse.builder() .accessToken(accessToken) .refreshToken(refreshToken) .tokenType("Bearer") .expiresIn(3600) .clientId(request.getClientId()) .build(); } @Override public AccessTokenResponse refreshToken(RefreshTokenRequest request) { if (!jwtTokenProvider.validateRefreshToken(request.getRefreshToken())) { throw new BusinessException("无效的刷新令牌"); } String clientId = jwtTokenProvider.getClientIdFromRefreshToken(request.getRefreshToken()); String refreshTokenKey = "refresh_token:" + clientId; String storedRefreshToken = (String) redisTemplate.opsForValue().get(refreshTokenKey); if (!request.getRefreshToken().equals(storedRefreshToken)) { throw new BusinessException("刷新令牌已失效"); } AccessConfig config = accessConfigRepository.findByClientId(clientId) .orElseThrow(() -> new BusinessException("客户端不存在")); String newAccessToken = generateAccessToken(config); String accessTokenKey = "access_token:" + clientId; redisTemplate.opsForValue().set(accessTokenKey, newAccessToken, Duration.ofHours(1)); return AccessTokenResponse.builder() .accessToken(newAccessToken) .refreshToken(request.getRefreshToken()) .tokenType("Bearer") .expiresIn(3600) .clientId(clientId) .build(); } @Override public ValidationResponse validateToken(TokenValidationRequest request) { String token = request.getToken(); if (!jwtTokenProvider.validateToken(token)) { return ValidationResponse.builder() .valid(false) .message("无效的令牌") .build(); } String clientId = jwtTokenProvider.getClientIdFromToken(token); String accessTokenKey = "access_token:" + clientId; String storedToken = (String) redisTemplate.opsForValue().get(accessTokenKey); if (!token.equals(storedToken)) { return ValidationResponse.builder() .valid(false) .message("令牌已失效") .build(); } AccessConfig config = accessConfigRepository.findByClientId(clientId) .orElse(null); if (config == null || !config.getIsActive()) { return ValidationResponse.builder() .valid(false) .message("客户端不存在或已被禁用") .build(); } return ValidationResponse.builder() .valid(true) .clientId(clientId) .clientName(config.getClientName()) .accessType(config.getAccessType()) .allowedServices(config.getAllowedServices()) .allowedEndpoints(config.getAllowedEndpoints()) .build(); } @Override public ClientInfoResponse getClientInfo(String clientId) { AccessConfig config = accessConfigRepository.findByClientId(clientId) .orElseThrow(() -> new BusinessException("客户端不存在")); return ClientInfoResponse.builder() .clientId(config.getClientId()) .clientName(config.getClientName()) .accessType(config.getAccessType()) .allowedServices(config.getAllowedServices()) .allowedEndpoints(config.getAllowedEndpoints()) .rateLimit(config.getRateLimit()) .isActive(config.getIsActive()) .createdAt(config.getCreatedAt()) .updatedAt(config.getUpdatedAt()) .build(); } @Override public ClientInfoResponse updateClientConfig(String clientId, ClientConfigUpdateRequest request) { AccessConfig config = accessConfigRepository.findByClientId(clientId) .orElseThrow(() -> new BusinessException("客户端不存在")); if (request.getAllowedServices() != null) { config.setAllowedServices(request.getAllowedServices()); } if (request.getAllowedEndpoints() != null) { config.setAllowedEndpoints(request.getAllowedEndpoints()); } if (request.getRateLimit() != null) { config.setRateLimit(request.getRateLimit()); } if (request.getIsActive() != null) { config.setIsActive(request.getIsActive()); } AccessConfig savedConfig = accessConfigRepository.save(config); return ClientInfoResponse.builder() .clientId(savedConfig.getClientId()) .clientName(savedConfig.getClientName()) .accessType(savedConfig.getAccessType()) .allowedServices(savedConfig.getAllowedServices()) .allowedEndpoints(savedConfig.getAllowedEndpoints()) .rateLimit(savedConfig.getRateLimit()) .isActive(savedConfig.getIsActive()) .createdAt(savedConfig.getCreatedAt()) .updatedAt(savedConfig.getUpdatedAt()) .build(); } @Override public void revokeToken(TokenRevokeRequest request) { String clientId = request.getClientId(); String accessTokenKey = "access_token:" + clientId; String refreshTokenKey = "refresh_token:" + clientId; redisTemplate.delete(accessTokenKey); redisTemplate.delete(refreshTokenKey); } private String generateAccessToken(AccessConfig config) { Map<String, Object> claims = new HashMap<>(); claims.put("clientId", config.getClientId()); claims.put("clientName", config.getClientName()); claims.put("accessType", config.getAccessType().name()); claims.put("type", "access"); return jwtTokenProvider.createToken(claims, config.getClientId(), 3600000); } private String generateRefreshToken(AccessConfig config) { Map<String, Object> claims = new HashMap<>(); claims.put("clientId", config.getClientId()); claims.put("type", "refresh"); return jwtTokenProvider.createToken(claims, config.getClientId(), 604800000); } }
|
7. 服务间通信与集成
7.1 Feign客户端配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| @FeignClient(name = "user-service", fallback = UserServiceClientFallback.class) public interface UserServiceClient { @PostMapping("/api/users/authenticate") AuthenticationResponse authenticate(@RequestBody AuthenticationRequest request); @GetMapping("/api/users/{id}") UserResponse getUser(@PathVariable("id") Long id); @GetMapping("/api/users") Page<UserResponse> getUsers(@RequestParam int page, @RequestParam int size); }
@Component public class UserServiceClientFallback implements UserServiceClient { @Override public AuthenticationResponse authenticate(AuthenticationRequest request) { return AuthenticationResponse.builder() .authenticated(false) .message("用户服务暂时不可用") .build(); } @Override public UserResponse getUser(Long id) { return UserResponse.builder() .id(id) .username("unknown") .email("unknown@example.com") .role(UserRole.GUEST) .status(UserStatus.INACTIVE) .build(); } @Override public Page<UserResponse> getUsers(int page, int size) { return Page.empty(); } }
@FeignClient(name = "product-service", fallback = ProductServiceClientFallback.class) public interface ProductServiceClient { @GetMapping("/api/products/{id}") ProductResponse getProduct(@PathVariable("id") String id); @GetMapping("/api/products") Page<ProductResponse> getProducts(@RequestParam int page, @RequestParam int size); }
@FeignClient(name = "payment-service", fallback = PaymentServiceClientFallback.class) public interface PaymentServiceClient { @PostMapping("/api/payments/process") PaymentResponse processPayment(@RequestBody PaymentRequest request); @PostMapping("/api/payments/refund") RefundResponse processRefund(@RequestBody RefundRequest request); }
|
7.2 消息队列集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| @Component public class MessagePublisher { private final RabbitTemplate rabbitTemplate; public MessagePublisher(RabbitTemplate rabbitTemplate) { this.rabbitTemplate = rabbitTemplate; } public void publishOrderEvent(OrderEvent event) { rabbitTemplate.convertAndSend("order.exchange", "order.created", event); } public void publishUserEvent(UserEvent event) { rabbitTemplate.convertAndSend("user.exchange", "user.registered", event); } public void publishDeviceEvent(DeviceEvent event) { rabbitTemplate.convertAndSend("device.exchange", "device.status.changed", event); } }
@Component @RabbitListener(queues = "order.created.queue") public class OrderEventListener { private final NotificationService notificationService; public OrderEventListener(NotificationService notificationService) { this.notificationService = notificationService; } @RabbitHandler public void handleOrderCreated(OrderEvent event) { notificationService.sendOrderCreatedNotification(event); } }
@Data @Builder public class OrderEvent { private Long orderId; private String orderNumber; private Long userId; private OrderStatus status; private BigDecimal totalAmount; private LocalDateTime createdAt; }
@Data @Builder public class UserEvent { private Long userId; private String username; private String email; private UserStatus status; private LocalDateTime createdAt; }
@Data @Builder public class DeviceEvent { private Long deviceId; private String deviceName; private DeviceStatus status; private LocalDateTime timestamp; }
|
8. 监控与运维
8.1 服务监控配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| @Configuration @EnableScheduling public class MonitoringConfig { @Bean public MeterRegistry meterRegistry() { return new SimpleMeterRegistry(); } @Bean public TimedAspect timedAspect(MeterRegistry registry) { return new TimedAspect(registry); } @Bean public CountedAspect countedAspect(MeterRegistry registry) { return new CountedAspect(registry); } }
@Component public class ServiceHealthIndicator implements HealthIndicator { private final UserServiceClient userServiceClient; private final OrderServiceClient orderServiceClient; private final DeviceServiceClient deviceServiceClient; public ServiceHealthIndicator(UserServiceClient userServiceClient, OrderServiceClient orderServiceClient, DeviceServiceClient deviceServiceClient) { this.userServiceClient = userServiceClient; this.orderServiceClient = orderServiceClient; this.deviceServiceClient = deviceServiceClient; } @Override public Health health() { Health.Builder builder = new Health.Builder(); try { userServiceClient.getUsers(0, 1); builder.withDetail("user-service", "UP"); } catch (Exception e) { builder.withDetail("user-service", "DOWN"); builder.down(); } try { orderServiceClient.getUserOrders(1L, 0, 1, null); builder.withDetail("order-service", "UP"); } catch (Exception e) { builder.withDetail("order-service", "DOWN"); builder.down(); } try { deviceServiceClient.getUserDevices(1L, 0, 1, null); builder.withDetail("device-service", "UP"); } catch (Exception e) { builder.withDetail("device-service", "DOWN"); builder.down(); } return builder.build(); } }
|
8.2 链路追踪配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Configuration public class TracingConfig { @Bean public Sampler alwaysSampler() { return Sampler.create(1.0f); } @Bean public Sender sender() { return OkHttpSender.create("http://zipkin:9411/api/v2/spans"); } @Bean public AsyncReporter<Span> spanReporter() { return AsyncReporter.create(sender()); } @Bean public Tracing tracing() { return Tracing.newBuilder() .localServiceName("microservices-gateway") .sampler(alwaysSampler()) .spanReporter(spanReporter()) .build(); } @Bean public Tracer tracer() { return tracing().tracer(); } }
|
9. 总结
微服务架构通过将单一应用程序分解为多个小型服务,实现了系统的模块化、可扩展性和可维护性。本文详细介绍的用户服务、API网关、设备服务、订单服务、接入服务等核心微服务,构成了一个完整的微服务生态系统。
9.1 架构优势
- 服务独立: 每个服务可以独立开发、部署和扩展
- 技术多样性: 不同服务可以使用最适合的技术栈
- 故障隔离: 单个服务的故障不会影响整个系统
- 团队自治: 不同团队可以独立负责不同的服务
- 持续交付: 支持快速迭代和持续部署
9.2 实施要点
- 服务拆分: 按照业务领域进行合理的服务拆分
- 数据管理: 每个服务拥有自己的数据存储
- 服务治理: 实现服务发现、负载均衡、熔断降级
- 监控运维: 建立完善的监控和日志系统
- 安全管控: 实现服务间认证和授权
9.3 最佳实践
- API设计: 遵循RESTful设计原则
- 错误处理: 实现统一的错误处理机制
- 配置管理: 使用配置中心管理服务配置
- 版本控制: 实现API版本管理
- 文档管理: 维护完整的API文档
通过本文的学习,您应该已经掌握了微服务架构的核心技术,能够设计和实现完整的微服务系统,为企业的数字化转型提供技术支撑。