SpringCloud之Config配置中心与Redis分布式锁详解
在分布式系统中,配置的统一管理以及分布式锁的实现都是非常重要的一部分。Spring Cloud提供了Config Server和Redis分布式锁这两个强大的功能来支持分布式系统的开发。本文将详细介绍Spring Cloud Config的使用和Redis分布式锁的实现方法。
一、Spring Cloud Config的使用
1.1 简介
Spring Cloud Config提供了一种集中式的外部配置管理方法,用于管理微服务应用、集群应用和分布式系统的各种配置文件。通过它,程序员可以用统一的方式管理各个微服务中的配置信息。该配置信息存储在Git、SVN等版本管理系统中,方便进行版本控制。Spring Cloud Config Server作为配置服务的中心,管理所有的配置信息,各个微服务则通过Config Client从Config Server中读取配置信息。
1.2 Config Server的搭建和配置
首先,在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
然后,在启动类上添加@EnableConfigServer注解,开启配置服务功能:
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class,args);
}
}
接着,在application.properties中配置Git或SVN的地址和用户名密码:
spring.cloud.config.server.git.uri=https://github.com/your-username/your-repo
spring.cloud.config.server.git.username=your-username
spring.cloud.config.server.git.password=your-password
最后,在启动服务后,可以通过以下URL访问配置信息:
http://localhost:8888/{application}/{profile}/{label}
其中,application代表应用名,profile代表环境,如dev、test、prod等,label代表版本。
1.3 Config Client的使用
首先,在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
接着,在启动类上添加@EnableConfiguraitonProperties注解,将配置文件中的配置属性映射到Bean中:
@SpringBootApplication
@EnableConfigurationProperties
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class,args);
}
}
在application.properties中配置Config Server的地址:
spring.cloud.config.uri=http://localhost:8888
最后,在需要读取配置信息的地方,使用@Value注解获取配置信息即可:
@Component
public class DemoComponent {
@Value("${config.name}")
private String name;
public void show() {
System.out.println("name:" + name);
}
}
1.4 示例:配置中心的综合应用
假设有两个微服务——order和product,都需要读取数据库的配置信息。可以将数据库的配置信息统一存储在配置中心,让order和product从配置中心中读取。
- 配置中心的搭建和配置
按照1.2所述的方式,搭建配置中心,并将数据库的配置信息上传到Git版本管理系统中。
- order服务的配置
在order服务的配置文件中,添加以下配置:
spring.datasource.url=${spring.cloud.config.uri}/order/mydb
spring.datasource.username=${spring.cloud.config.username}
spring.datasource.password=${spring.cloud.config.password}
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
在启动类上添加注解@EnableDiscoveryClient和@EnableFeignClients使其具有服务注册和服务发现的能力。使用Feign Client调用product服务的接口,查询商品信息:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class,args);
}
}
// Feign Client
@FeignClient(name="product-service")
public interface ProductClient {
@GetMapping("/product/{productId}")
public Product getProduct(@PathVariable("productId") Long productId);
}
// Controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private ProductClient productClient;
@GetMapping("/{orderId}")
public Order getOrder(@PathVariable("orderId") Long orderId) {
Order order = new Order();
order.setOrderId(orderId);
Product product = productClient.getProduct(orderId);
order.setProduct(product);
return order;
}
}
- product服务的配置
在product服务的配置文件中,添加以下配置:
spring.datasource.url=${spring.cloud.config.uri}/product/mydb
spring.datasource.username=${spring.cloud.config.username}
spring.datasource.password=${spring.cloud.config.password}
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
在启动类上添加注解@EnableDiscoveryClient以具有服务注册和服务发现的能力。将商品信息存储在内存中,用于返回查询结果:
@SpringBootApplication
@EnableDiscoveryClient
public class ProductServiceApplication {
public static Map<Long, Product> products = new HashMap<>();
static {
Product product1 = new Product(1L, "华为手机P40", 4999.99);
Product product2 = new Product(2L, "小米电视", 2999.99);
products.put(product1.getProductId(), product1);
products.put(product2.getProductId(), product2);
}
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class,args);
}
}
// Controller
@RestController
@RequestMapping("/product")
public class ProductController {
@GetMapping("/{productId}")
public Product getProduct(@PathVariable("productId") Long productId) {
Product product = ProductServiceApplication.products.get(productId);
if(product == null) {
throw new RuntimeException("Product not found.");
}
return product;
}
}
二、Redis分布式锁的实现方法
2.1 简介
在分布式系统中,为了保证数据的正确性和一致性,需要使用分布式锁来实现资源的互斥访问。Redis是一种高性能的NoSQL数据库,非常适合用于分布式锁的实现。Redis分布式锁的基本思路是通过Redis的SETNX命令来实现互斥访问,同时设置一个过期时间,确保在某些情况下锁被意外释放时不会对程序造成不良的影响。
2.2 实现方法
使用Spring Data Redis连接Redis,并编写加锁和释放锁的代码:
@Component
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 加锁
* @param key 锁的名称
* @param timeout 锁的有效期
* @return 锁标识
*/
public String lock(String key, long timeout) {
String identifier = UUID.randomUUID().toString();
long end = System.currentTimeMillis() + timeout;
while (System.currentTimeMillis() < end) {
if (redisTemplate.opsForValue().setIfAbsent(key, identifier)) {
redisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS);
return identifier;
}
try {
Thread.sleep(100);//等待一段时间后重试
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 释放锁
* @param key 锁的名称
* @param identifier 锁标识
*/
public void unlock(String key, String identifier) {
while (true) {
redisTemplate.watch(key);
if (identifier.equals(redisTemplate.opsForValue().get(key))) {
redisTemplate.multi();
redisTemplate.delete(key);
List<Object> results = redisTemplate.exec();
if (results == null) {//如果返回null代表执行失败,需要重试
continue;
}
}
redisTemplate.unwatch();
break;
}
}
}
2.3 示例:秒杀系统的分布式锁
假设有一个秒杀系统,需实现高并发的订单处理。可以设定一定的抢购时间,并用分布式锁解决订单重复处理的问题。
- 设计模型
假设订单数据模型如下:
@Data
public class Order {
private Long orderId;
private Long productId;
private Integer amount;
private Integer status; // 订单状态:0-未支付,1-已支付
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
- 创建订单
使用数据库中的库存信息和请求中的抢购数量计算出真实的库存量,然后使用Redis分布式锁保证订单不会重复创建:
@Autowired
private RedisLock redisLock;
@Autowired
private OrderRepository orderRepository;
@Transactional
public Order createOrder(Long productId, Integer amount, long timeout) {
// 获取分布式锁
String identifier = redisLock.lock("product_" + productId, timeout);
if (identifier == null) {
throw new RuntimeException("No lock available");
}
try {
// 查询库存信息
Product product = productService.getProduct(productId);
if (product.getAmount() < amount) {
throw new RuntimeException("Not enough stock");
}
// 扣减库存
product.setAmount(product.getAmount() - amount);
productService.updateProduct(product);
// 创建订单
Order order = new Order();
order.setOrderId(System.currentTimeMillis());
order.setProductId(productId);
order.setAmount(amount);
order.setStatus(0);
order.setCreateTime(LocalDateTime.now());
order.setUpdateTime(LocalDateTime.now());
orderRepository.save(order);
return order;
} finally {
// 释放分布式锁
redisLock.unlock("product_" + productId, identifier);
}
}
- 查询订单
查询订单的流程较为简单,不再赘述。
总结
通过本文的介绍,希望读者能够了解Spring Cloud Config配置中心和Redis分布式锁的使用方法。配置中心用于管理分布式系统中的各种配置信息,避免在每个微服务中都需要进行相同的配置;而Redis分布式锁用于保证互斥访问,避免数据不一致的情况出现。本文还给出了一些示例,希望对读者的实际开发起到一些参考作用。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringCloud之Config配置中心与Redis分布式锁详解 - Python技术站