对于高并发系统来说,有三个重要的机制来保障其高效运行,它们分别是:缓存、限流和熔断。而缓存是排在最前面也是高并发系统之所以高效运行的关键手段,那么问题来了:缓存只使用 Redis 就够了吗?
当然不是,不要把所有鸡蛋放到一个篮子里,成熟的系统在关键功能实现时一定会考虑冗余设计,注意这里的冗余设计不是贬义词。
冗余设计是在系统或设备完成任务起关键作用的地方,增加一套以上完成相同功能的功能通道(or 系统)、工作元件或部件,以保证当该部分出现故障时,系统或设备仍能正常工作,以减少系统或者设备的故障概率,提高系统可靠性。
例如,飞机的设计,飞机正常运行只需要两个发动机,但在每台飞机的设计中可能至少会设计四个发动机,这就有冗余设计的典型使用场景,这样设计的目的是为了保证极端情况下,如果有一个或两个发动机出现故障,不会因为某个发动机的故障而引起重大的安全事故。
缓存功能的设计也是一样,我们在高并发系统中通常会使用多级缓存来保证其高效运行,其中的多级缓存就包含以下这些:
以下是它们的具体使用。
在 Java Web应用中,实现浏览器缓存可以使用 HttpServletResponse 对象来设置与缓存相关的响应头,以开启浏览器的缓存功能,它的具体实现分为以下几步。
Cache-Control 是 HTTP/1.1 中用于控制缓存策略的主要方式。它可以设置多个指令,如 max-age(定义资源的最大存活时间,单位秒)、no-cache(要求重新验证)、public(指示可以被任何缓存区缓存)、private(只能被单个用户私有缓存存储)等,设置如下:
response.setHeader("Cache-Control", "max-age=3600, public"); // 缓存一小时
设置一个绝对的过期时间,超过这个时间点后浏览器将不再使用缓存的内容而向服务器请求新的资源,设置如下:
response.setDateHeader("Expires", System.currentTimeMillis() + 3600 * 1000); // 缓存一小时
ETag(实体标签)一种验证机制,它为每个版本的资源生成一个唯一标识符。当客户端发起请求时,会携带上先前接收到的 ETag,服务器根据 ETag 判断资源是否已更新,若未更新则返回 304 Not Modified 状态码,通知浏览器继续使用本地缓存,设置如下:
String etag = generateETagForContent(); // 根据内容生成ETag
response.setHeader("ETag", etag);
指定资源最后修改的时间戳,浏览器下次请求时会带上 If-Modified-Since 头,服务器对比时间戳决定是否返回新内容或发送 304 状态码,设置如下:
long lastModifiedDate = getLastModifiedDate();
response.setDateHeader("Last-Modified", lastModifiedDate);
在 Spring Web 框架中,可以通过 HttpServletResponse 对象来设置这些头信息。例如,在过滤器中设置响应头以启用缓存:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 设置缓存策略
httpResponse.setHeader("Cache-Control", "max-age=3600");
// 其他响应头设置...
chain.doFilter(request, response);
}
以上就是在 Java Web 应用程序中利用 HTTP 协议特性控制浏览器缓存的基本方法。
Nginx 中开启缓存的配置总共有以下 5 步。
在 Nginx 配置中定义一个缓存路径和配置,通过 proxy_cache_path 指令完成,例如,以下配置:
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
其中:
my_cache
的共享内存区域,大小为 10MB。例如,使用 @Cacheable 注解来缓存方法的返回值:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable("users")
public User findUserById(Long id) {
// 模拟从数据库中查询用户
return new User(id, "Alice");
}
}
也可以使用 @CacheEvict 注解来删除缓存:
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
// 模拟从数据库中删除用户
}
}
在这个例子中,deleteUser 方法会删除 "users" 缓存中 key 为 id 的缓存项。
可以使用 @CachePut 注解来更新缓存:
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 模拟更新数据库中的用户信息
return user;
}
}
在这个例子中,updateUser 方法会更新 "users" 缓存中 key 为 user.id 的缓存项,缓存的值是方法的返回值。
以 Caffeine 本地缓存的使用为例,它在 Spring Boot 项目中的使用如下。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
在 application.properties 或 application.yml 文件中配置 Caffeine 缓存的相关参数。例如:
# application.properties
spring.cache.type=caffeine
spring.cache.caffeine.spec=initialCapacity=100,maximumSize=1000,expireAfterWrite=10s
这里 spring.cache.caffeine.spec 是一个 Caffeine 规范字符串,用于设置初始容量、最大容量和写入后过期时间等缓存策略,其中:
本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。