Spring Boot 微service
1. 微servicearchitectureoverview
微servicearchitecture is a将application拆分 for many 个独立, 可独立deployment service architecture风格. 每个service都围绕specific业务functions构建, 并through轻量级通信mechanismfor交互.
1.1 微service 特点
- 独立deployment - 每个service可以独立deployment, 无需协调otherservice
- 独立scale - 可以根据service loadcircumstances独立scale
- techniques many 样性 - 每个service可以using不同 techniques栈
- failure隔离 - 一个servicefailure不会影响otherservice
- 团队自治 - 每个service可以由独立 团队负责
1.2 Spring Cloud and 微service
Spring Cloud is Spring ecosystemin用于构建distributedsystem and 微service frameworkcollection, providing了service发现, configurationin心, API gateway, load balancingetc.corefunctions.
2. service发现
service发现 is 微servicearchitecturein corecomponent, 用于helpingservice之间相互发现 and 通信.
2.1 Eureka service发现
<!-- serviceregisterin心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.2 creation Eureka serviceregisterin心
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
# application.properties
server.port=8761
spring.application.name=eureka-server
# 不register自己
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.hostname=localhost
2.3 serviceregister to Eureka
<!-- service端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
package com.example.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
# application.properties
server.port=8081
spring.application.name=user-service
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.instance.prefer-ip-address=true
3. configurationin心
configurationin心用于集inmanagement微service configurationinformation, support动态刷 new configuration.
3.1 Spring Cloud Config configurationin心
<!-- configurationin心service端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
3.2 creationconfigurationin心server
package com.example.configserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
# application.properties
server.port=8888
spring.application.name=config-server
# configuration Git 仓library地址
spring.cloud.config.server.git.uri=https://github.com/your-repo/config-repo.git
spring.cloud.config.server.git.search-paths=configs
spring.cloud.config.server.git.username=your-username
spring.cloud.config.server.git.password=your-password
3.3 客户端configuration
<!-- configurationin心客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 用于动态刷 new configuration -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# bootstrap.properties
spring.application.name=user-service
spring.cloud.config.uri=http://localhost:8888
spring.profiles.active=dev
# 开启刷 new 端点
management.endpoints.web.exposure.include=refresh
package com.example.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class ConfigController {
@Value("${config.message}")
private String message;
@GetMapping("/config")
public String getConfig() {
return message;
}
}
动态刷 new configuration:
curl -X POST http://localhost:8081/actuator/refresh
4. API gateway
API gateway is 微servicearchitecturein 入口点, 用于routingrequest, load balancing, authenticationauthorizationetc..
4.1 Spring Cloud Gateway
<!-- API gateway依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
4.2 creation API gateway
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
# application.properties
server.port=8080
spring.application.name=api-gateway
# register to Eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# routingconfiguration
spring.cloud.gateway.routes[0].id=user-service-route
spring.cloud.gateway.routes[0].uri=lb://user-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/users/**
spring.cloud.gateway.routes[0].filters[0]=RewritePath=/api/users/(?.*), /${path}
spring.cloud.gateway.routes[1].id=order-service-route
spring.cloud.gateway.routes[1].uri=lb://order-service
spring.cloud.gateway.routes[1].predicates[0]=Path=/api/orders/**
spring.cloud.gateway.routes[1].filters[0]=RewritePath=/api/orders/(?.*), /${path}
4.3 filter器configuration
# 全局filter器
spring.cloud.gateway.default-filters[0]=AddResponseHeader=X-Response-Time, ${response-time}
# 限流configuration
spring.cloud.gateway.routes[0].filters[1]=RequestRateLimiter=
args[redis-rate-limiter.replenishRate]=10,
args[redis-rate-limiter.burstCapacity]=20,
args[key-resolver]=#{@userKeyResolver}
package com.example.gateway.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
public class RateLimitConfig {
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
}
5. service间通信
微service之间需要for通信, Spring Cloud providing了 many 种通信方式.
5.1 RestTemplate 通信
package com.example.service.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.example.service.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
public String getUserInfo(Long userId) {
String url = "http://user-service/api/users/" + userId;
return restTemplate.getForObject(url, String.class);
}
}
5.2 WebClient 通信
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
package com.example.service.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.builder webClientbuilder() {
return WebClient.builder();
}
}
package com.example.service.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class UserService {
@Autowired
private WebClient.builder webClientbuilder;
public Mono<String> getUserInfo(Long userId) {
return webClientbuilder.build()
.get()
.uri("http://user-service/api/users/{id}", userId)
.retrieve()
.bodyToMono(String.class);
}
}
5.3 OpenFeign 声明式客户端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
package com.example.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
package com.example.service.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/api/users/{id}")
String getUserById(@PathVariable("id") Long id);
@GetMapping("/api/users")
String getAllUsers();
}
package com.example.service.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.service.client.UserClient;
@Service
public class OrderService {
@Autowired
private UserClient userClient;
public String getOrderWithUserInfo(Long orderId) {
// 获取订单information
// ...
// 调用userservice获取userinformation
String userInfo = userClient.getUserById(1L);
return "Order info + " + userInfo;
}
}
6. load balancing
load balancing用于 in many 个serviceinstance之间分配request, improvingsystem availability and reliability.
6.1 Ribbon load balancing
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
# load balancingconfiguration
user-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
user-service.ribbon.ConnectTimeout=5000
user-service.ribbon.ReadTimeout=5000
6.2 load balancing策略
- RoundRobinRule - 轮询策略 (默认)
- RandomRule - 随机策略
- WeightedResponseTimeRule - 加权response时间策略
- BestAvailableRule - 最佳可用策略
- ZoneAvoidanceRule - 区域感知策略
7. 熔断器
熔断器用于防止service雪崩, 当service调用失败率达 to 阈值时, 会 fast 速失败并返回默认response.
7.1 Hystrix 熔断器
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
package com.example.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableHystrix
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
package com.example.service.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "getUserFallback")
public String getUserInfo(Long userId) {
String url = "http://user-service/api/users/" + userId;
return restTemplate.getForObject(url, String.class);
}
public String getUserFallback(Long userId) {
return "User service unavailable, please try again later.";
}
}
7.2 Resilience4j 熔断器
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
package com.example.service.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
public String getUserInfo(Long userId) {
String url = "http://user-service/api/users/" + userId;
return restTemplate.getForObject(url, String.class);
}
public String getUserFallback(Long userId, Exception e) {
return "User service unavailable: " + e.getMessage();
}
}
# Resilience4j configuration
resilience4j.circuitbreaker.instances.userService.registerHealthIndicator=true
resilience4j.circuitbreaker.instances.userService.failureRateThreshold=50
resilience4j.circuitbreaker.instances.userService.waitDurationInOpenState=10s
resilience4j.circuitbreaker.instances.userService.permittedNumberOfCallsInHalfOpenState=3
resilience4j.circuitbreaker.instances.userService.slidingWindowSize=10
resilience4j.circuitbreaker.instances.userService.slidingWindowType=COUNT_BASED
8. distributed追踪
distributed追踪用于跟踪request in 微servicearchitecturein 流转过程, 便于issues排查 and performancemonitor.
8.1 Spring Cloud Sleuth + Zipkin
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
# configuration
spring.application.name=user-service
spring.sleuth.sampler.probability=1.0
spring.zipkin.base-url=http://localhost:9411
8.2 启动 Zipkin
docker run -d -p 9411:9411 openzipkin/zipkin
访问 Zipkin UI: http://localhost:9411
9. distributedtransaction
distributedtransaction用于保证跨serviceoperation atomicity.
9.1 Seata distributedtransaction
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.6.1</version>
</dependency>
# Seata configuration
seata.tx-service-group=my_test_tx_group
seata.service.vgroup-mapping.my_test_tx_group=default
seata.service.grouplist.default=127.0.0.1:8091
seata.config.type=nacos
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.group=SEATA_GROUP
seata.registry.type=nacos
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP
package com.example.service.service;
import org.springframework.stereotype.Service;
import io.seata.spring.annotation.GlobalTransactional;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductClient productClient;
@GlobalTransactional
public void createOrder(Order order) {
// creation订单
orderMapper.insert(order);
// 扣减library存
productClient.reduceStock(order.getProductId(), order.getQuantity());
// 扣减余额
// ...
}
}
10. 微servicebest practices
10.1 servicedesign
- serviceedge界清晰, 单一职责
- API design要 stable , 避免频繁变更
- using领域驱动design (DDD) 划分service
- service之间松耦合, high in 聚
10.2 通信design
- in 部serviceusing gRPC or Thrift etc. high 效protocol
- out 部serviceusing RESTful API or GraphQL
- 避免synchronization调用, 尽量usingasynchronous通信
- usingmessagequeue解耦service
10.3 datamanagement
- 每个service has 自己 datalibrary
- 避免distributedtransaction, 尽量using最终consistency
- usingevent驱动architecture保持dataconsistency
- 考虑datashard and partition策略
10.4 deployment and monitor
- usingcontainerizationdeployment (Docker + Kubernetes)
- implementationautomationdeployment and 滚动update
- 建立完善 monitor体系
- implementationdistributed追踪 and logaggregate
- 建立告警mechanism
10.5 security性
- implementationservice间authentication and authorization
- using API gateway统一processingauthentication
- encryption敏感data
- 定期forsecurityaudit and 漏洞扫描