Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build Spring Boot 3.x REST APIs, microservices, and reactive WebFlux apps with Spring Security 6 and JPA.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/cloud.md
1# Cloud Native - Spring Cloud23## Spring Cloud Config Server45```java6// Config Server7@SpringBootApplication8@EnableConfigServer9public class ConfigServerApplication {10public static void main(String[] args) {11SpringApplication.run(ConfigServerApplication.class, args);12}13}1415// application.yml16server:17port: 88881819spring:20cloud:21config:22server:23git:24uri: https://github.com/example/config-repo25default-label: main26search-paths: '{application}'27username: ${GIT_USERNAME}28password: ${GIT_PASSWORD}29native:30search-locations: classpath:/config31security:32user:33name: config-user34password: ${CONFIG_PASSWORD}3536// Config Client37@SpringBootApplication38public class ClientApplication {39public static void main(String[] args) {40SpringApplication.run(ClientApplication.class, args);41}42}4344// application.yml (Config Client)45spring:46application:47name: user-service48config:49import: "configserver:http://localhost:8888"50cloud:51config:52username: config-user53password: ${CONFIG_PASSWORD}54fail-fast: true55retry:56max-attempts: 657initial-interval: 100058```5960## Dynamic Configuration Refresh6162```java63@RestController64@RefreshScope65public class ConfigController {66@Value("${app.feature.enabled:false}")67private boolean featureEnabled;6869@Value("${app.max-connections:100}")70private int maxConnections;7172@GetMapping("/config")73public Map<String, Object> getConfig() {74return Map.of(75"featureEnabled", featureEnabled,76"maxConnections", maxConnections77);78}79}8081// Refresh configuration via Actuator endpoint:82// POST /actuator/refresh83```8485## Service Discovery - Eureka8687```java88// Eureka Server89@SpringBootApplication90@EnableEurekaServer91public class EurekaServerApplication {92public static void main(String[] args) {93SpringApplication.run(EurekaServerApplication.class, args);94}95}9697// application.yml (Eureka Server)98server:99port: 8761100101eureka:102instance:103hostname: localhost104client:105register-with-eureka: false106fetch-registry: false107service-url:108defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/109110// Eureka Client111@SpringBootApplication112@EnableDiscoveryClient113public class UserServiceApplication {114public static void main(String[] args) {115SpringApplication.run(UserServiceApplication.class, args);116}117}118119// application.yml (Eureka Client)120spring:121application:122name: user-service123124eureka:125client:126service-url:127defaultZone: http://localhost:8761/eureka/128registry-fetch-interval-seconds: 5129instance:130prefer-ip-address: true131lease-renewal-interval-in-seconds: 10132lease-expiration-duration-in-seconds: 30133```134135## Spring Cloud Gateway136137```java138@SpringBootApplication139public class GatewayApplication {140public static void main(String[] args) {141SpringApplication.run(GatewayApplication.class, args);142}143144@Bean145public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {146return builder.routes()147.route("user-service", r -> r148.path("/api/users/**")149.filters(f -> f150.rewritePath("/api/users/(?<segment>.*)", "/users/${segment}")151.addRequestHeader("X-Gateway", "Spring-Cloud-Gateway")152.circuitBreaker(config -> config153.setName("userServiceCircuitBreaker")154.setFallbackUri("forward:/fallback/users")155)156.retry(config -> config157.setRetries(3)158.setStatuses(HttpStatus.SERVICE_UNAVAILABLE)159)160)161.uri("lb://user-service")162)163.route("order-service", r -> r164.path("/api/orders/**")165.filters(f -> f166.rewritePath("/api/orders/(?<segment>.*)", "/orders/${segment}")167.requestRateLimiter(config -> config168.setRateLimiter(redisRateLimiter())169.setKeyResolver(userKeyResolver())170)171)172.uri("lb://order-service")173)174.build();175}176177@Bean178public RedisRateLimiter redisRateLimiter() {179return new RedisRateLimiter(10, 20); // replenishRate, burstCapacity180}181182@Bean183public KeyResolver userKeyResolver() {184return exchange -> Mono.just(185exchange.getRequest().getHeaders().getFirst("X-User-Id")186);187}188}189190// application.yml (Gateway)191spring:192cloud:193gateway:194discovery:195locator:196enabled: true197lower-case-service-id: true198default-filters:199- DedupeResponseHeader=Access-Control-Allow-Origin200globalcors:201cors-configurations:202'[/**]':203allowed-origins: "*"204allowed-methods:205- GET206- POST207- PUT208- DELETE209allowed-headers: "*"210```211212## Circuit Breaker - Resilience4j213214```java215@Service216@RequiredArgsConstructor217public class ExternalApiService {218private final WebClient webClient;219220@CircuitBreaker(name = "externalApi", fallbackMethod = "getFallbackData")221@Retry(name = "externalApi")222@RateLimiter(name = "externalApi")223public Mono<ExternalData> getData(String id) {224return webClient225.get()226.uri("/data/{id}", id)227.retrieve()228.bodyToMono(ExternalData.class)229.timeout(Duration.ofSeconds(3));230}231232private Mono<ExternalData> getFallbackData(String id, Exception e) {233log.warn("Fallback triggered for id: {}, error: {}", id, e.getMessage());234return Mono.just(new ExternalData(id, "Fallback data", LocalDateTime.now()));235}236}237238// application.yml239resilience4j:240circuitbreaker:241instances:242externalApi:243register-health-indicator: true244sliding-window-size: 10245minimum-number-of-calls: 5246permitted-number-of-calls-in-half-open-state: 3247automatic-transition-from-open-to-half-open-enabled: true248wait-duration-in-open-state: 5s249failure-rate-threshold: 50250event-consumer-buffer-size: 10251252retry:253instances:254externalApi:255max-attempts: 3256wait-duration: 1s257enable-exponential-backoff: true258exponential-backoff-multiplier: 2259260ratelimiter:261instances:262externalApi:263limit-for-period: 10264limit-refresh-period: 1s265timeout-duration: 0s266```267268## Distributed Tracing - Micrometer Tracing269270```java271// application.yml272management:273tracing:274sampling:275probability: 1.0276zipkin:277tracing:278endpoint: http://localhost:9411/api/v2/spans279280logging:281pattern:282level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"283284// Custom spans285@Service286@RequiredArgsConstructor287public class OrderService {288private final Tracer tracer;289private final OrderRepository orderRepository;290291public Order processOrder(OrderRequest request) {292Span span = tracer.nextSpan().name("processOrder").start();293try (Tracer.SpanInScope ws = tracer.withSpan(span)) {294span.tag("order.type", request.type());295span.tag("order.items", String.valueOf(request.items().size()));296297// Business logic298Order order = createOrder(request);299300span.event("order.created");301return order;302} finally {303span.end();304}305}306}307```308309## Load Balancing with Spring Cloud LoadBalancer310311```java312@Configuration313@LoadBalancerClient(name = "user-service", configuration = UserServiceLoadBalancerConfig.class)314public class LoadBalancerConfiguration {315}316317@Configuration318public class UserServiceLoadBalancerConfig {319320@Bean321public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(322LoadBalancerClientFactory clientFactory,323ObjectProvider<LoadBalancerProperties> properties) {324return new RandomLoadBalancer(325clientFactory.getLazyProvider("user-service", ServiceInstanceListSupplier.class),326"user-service"327);328}329}330331@Service332@RequiredArgsConstructor333public class UserClientService {334private final WebClient.Builder webClientBuilder;335336public Mono<User> getUser(Long id) {337return webClientBuilder338.baseUrl("http://user-service")339.build()340.get()341.uri("/users/{id}", id)342.retrieve()343.bodyToMono(User.class);344}345}346```347348## Health Checks & Actuator349350```java351@Component352public class CustomHealthIndicator implements HealthIndicator {353354@Override355public Health health() {356boolean serviceUp = checkExternalService();357358if (serviceUp) {359return Health.up()360.withDetail("externalService", "Available")361.withDetail("timestamp", LocalDateTime.now())362.build();363} else {364return Health.down()365.withDetail("externalService", "Unavailable")366.withDetail("error", "Connection timeout")367.build();368}369}370371private boolean checkExternalService() {372// Check external dependency373return true;374}375}376377// application.yml378management:379endpoints:380web:381exposure:382include: health,info,metrics,prometheus383endpoint:384health:385show-details: always386probes:387enabled: true388health:389livenessState:390enabled: true391readinessState:392enabled: true393metrics:394export:395prometheus:396enabled: true397tags:398application: ${spring.application.name}399```400401## Kubernetes Deployment402403```yaml404# deployment.yaml405apiVersion: apps/v1406kind: Deployment407metadata:408name: user-service409spec:410replicas: 3411selector:412matchLabels:413app: user-service414template:415metadata:416labels:417app: user-service418spec:419containers:420- name: user-service421image: user-service:1.0.0422ports:423- containerPort: 8080424env:425- name: SPRING_PROFILES_ACTIVE426value: "kubernetes"427- name: JAVA_OPTS428value: "-Xmx512m -Xms256m"429livenessProbe:430httpGet:431path: /actuator/health/liveness432port: 8080433initialDelaySeconds: 60434periodSeconds: 10435readinessProbe:436httpGet:437path: /actuator/health/readiness438port: 8080439initialDelaySeconds: 30440periodSeconds: 5441resources:442requests:443memory: "512Mi"444cpu: "500m"445limits:446memory: "1Gi"447cpu: "1000m"448---449apiVersion: v1450kind: Service451metadata:452name: user-service453spec:454selector:455app: user-service456ports:457- port: 80458targetPort: 8080459type: ClusterIP460```461462## Docker Configuration463464```dockerfile465# Dockerfile (Multi-stage)466FROM eclipse-temurin:17-jdk-alpine AS build467WORKDIR /workspace/app468469COPY mvnw .470COPY .mvn .mvn471COPY pom.xml .472COPY src src473474RUN ./mvnw install -DskipTests475RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)476477FROM eclipse-temurin:17-jre-alpine478VOLUME /tmp479ARG DEPENDENCY=/workspace/app/target/dependency480COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib481COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF482COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app483484ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.Application"]485```486487## Quick Reference488489| Component | Purpose |490|-----------|---------|491| **Config Server** | Centralized configuration management |492| **Eureka** | Service discovery and registration |493| **Gateway** | API gateway with routing, filtering, load balancing |494| **Circuit Breaker** | Fault tolerance and fallback patterns |495| **Load Balancer** | Client-side load balancing |496| **Tracing** | Distributed tracing across services |497| **Actuator** | Production-ready monitoring and management |498| **Kubernetes** | Container orchestration and deployment |499