1:跨域请求配置

后端Controller 添加注解 @CrossOrigin(origins = {"*"},allowCredentials = "true")
前端ajax 请求添加xhrFields: { withCredentials: true }

2:跨域解决方案

1:jsonp
2:cors
3:node 转发
4:nginx 反向代理

3:命令

telnet  172.36.189.36 9850 判断是否可用

ps -ef | grep mysql 查看服务
netstat -anp |grep 3306 查看端口

pstree -p 5240 查看当前进程下的线程
pstree -p 5240 | wc -l 查看当前进程下的线程数量
top -H 显示系统上正在运行的进程 load average 三个值
表示最近三个时间段的cpu运行情况 cpu 
us 表示用户区域占用cpu状态 sy 表示内核区域占用cpu状态

4:启动脚本

deploy.sh 内容
nohup java -Xms400m -Xmx400m -XX:NewSize=200m -XXMaxNewSize=200m -jar miaosha.jar -spring.config.addition-location=
./deploy.sh & 启动即可
tail 可以查看启动日志内容

5:jmeter性能压测

1:线程组

2:http请求

3:查看结果树

4:聚合报告

6:默认内嵌的tomcat配置

server.tomcat.accept-count 等待队列长度 默认100
server.tomcat.max-connections 最大可被连接数 默认10000
server.tomcat.max-threads 最大工作线程数 默认200
server.tomcat.min-spare-threads 最小工作线程数 默认10

默认配置下 连接数超过10000出现拒绝连接情况
默认配置下 触发的请求超过200+100拒绝请求处理

建议配置

4核8g的服务器当线程数在800-1000以上的时候会花大量时间在cpu调度上

server.tomcat.accept-count=1000

server.tomcat.max-threads=800

server.tomcat.min-spare-threads=100

  1. //当Spring容器内没有TomcatEmbeddedServletContainerFactory这个bean时,会吧此bean加载进spring容器中
    @Component
    public class WebServerConfiguration implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
    @Override
    public void customize(ConfigurableWebServerFactory configurableWebServerFactory) {
    //使用对应工厂类提供给我们的接口定制化我们的tomcat connector
    ((TomcatServletWebServerFactory)configurableWebServerFactory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
    @Override
    public void customize(Connector connector) {
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
  2.  
  3. //定制化keepalivetimeout,设置30秒内没有请求则服务端自动断开keepalive链接
    protocol.setKeepAliveTimeout(30000);
    //当客户端发送超过10000个请求则自动断开keepalive链接
    protocol.setMaxKeepAliveRequests(10000);
    }
    });
    }
    }

7:mysql 权限
grant all privileges on *.* to joe@localhost identified by '1';
grant 权限1,权限2,…权限n on 数据库名称.表名称 to 用户名@用户地址 identified by ‘连接口令’;
flush privileges;

8:nginx 作为反向代理服务器 静态资源服务器

nginx 高效原因
 epoll多路复用
 master-worker 进程模型
协成机制

9:对于Redis中bind的正确的理解是:

bind:是绑定本机的IP地址,(准确的是:本机的网卡对应的IP地址,每一个网卡都有一个IP地址),而不是redis允许来自其他计算机的IP地址。

如果指定了bind,则说明只允许来自指定网卡的Redis请求。如果没有指定,就说明可以接受来自任意一个网卡的Redis请求。

举个例子:如果redis服务器(本机)上有两个网卡,每一个网卡对应一个IP地址,例如IP1和IP2。(注意这个IP1和IP2都是本机的IP地址)。

我们的配置文件:bind IP1。  只有我们通过IP1来访问redis服务器,才允许连接Redis服务器,如果我们通过IP2来访问Redis服务器,就会连不上Redis。
————————————————
bind 127.0.0.1的解释:(为什么只有本机可以连接,而其他不可以连接)

我们从ifconfig可以看出:lo网卡(对应127.0.0.1IP地址):是一个回环地址(Local Loopback),也就是只有本地才能访问到这个回环地址,而其他的计算机也只能访问他们自己的回环地址。

那么来自这个lo网卡的计算机只有本机,所以只有本机可以访问,而其他计算机不能访问。
————————————————

1.如果你的bind设置为:bind 127.0.0.1,这是非常安全的,因为只有本台主机可以连接到redis,就算不设置密码,也是安全的,除非有人登入到你的服务器上。

2.如果你的bind设置为:bind 0.0.0.0,表示所有主机都可以连接到redis。(前提:你的服务器必须开放redis的端口)。这时设置密码,就会多一层保护,只有知道密码的才可以访问。也就是任何知道密码的主机都可以访问到你的redis。
————————————————

10:tomcat访问日志开启

11:nginx 配置 反向代理 静态服务器 以及 nginx连接后端服务保持长连接

12:redis 存储会话

引入以下依赖

  1. <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>2.0.5.RELEASE</version>
    </dependency>
    </dependencies>
  2.  
  3. 添加以下配置即可
  1. @Component
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
    public class RedisConfig {
    }
    13CAP原则又称CAP定理
CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
 
14:@PostConstruct和@PreDestroy
javaEE5引入了@PostConstruct和@PreDestroy两个作用于Servlet生命周期的注解,实现Bean初始化之前和销毁之前的自定义操作
 
15:引入rocketmq消息队列
  1. <dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.3.0</version>
    </dependency>
  2.  
  3. 相关配置
  1. mq.nameserver.addr=115.28.67.199:9876
    mq.topicname=TopicTest
  2.  
  3. rocketmq product端代码
  1. @Component
    public class MqProducer {
    private DefaultMQProducer producer;
    private TransactionMQProducer transactionMQProducer;
    @Value("${mq.nameserver.addr}")
    private String nameAddr;
    @Value("${mq.topicname}")
    private String topicName;
    @Autowired
    private OrderService orderService;
    @Autowired
    private StockLogDOMapper stockLogDOMapper;
    @PostConstruct
    public void init() throws MQClientException {
    //做mq producer的初始化
    producer = new DefaultMQProducer("producer_group");
    producer.setNamesrvAddr(nameAddr);
    producer.start();
    }
    //同步库存扣减消息
    public boolean asyncReduceStock(Integer itemId,Integer amount) {
    Map<String,Object> bodyMap = new HashMap<>();
    bodyMap.put("itemId",itemId);
    bodyMap.put("amount",amount);
    Message message = new Message(topicName,"increase",
    JSON.toJSON(bodyMap).toString().getBytes(Charset.forName("UTF-8")));
    try {
    producer.send(message);
    } catch (MQClientException e) {
    e.printStackTrace();
    return false;
    } catch (RemotingException e) {
    e.printStackTrace();
    return false;
    } catch (MQBrokerException e) {
    e.printStackTrace();
    return false;
    } catch (InterruptedException e) {
    e.printStackTrace();
    return false;
    }
    return true;
    }
    }
  2.  
  3. rocketmq消费端代码
  1. @Component
    public class MqConsumer {
  2.  
  3. private DefaultMQPushConsumer consumer;
    @Value("${mq.nameserver.addr}")
    private String nameAddr;
  4.  
  5. @Value("${mq.topicname}")
    private String topicName;
  6.  
  7. @Autowired
    private ItemStockDOMapper itemStockDOMapper;
  8.  
  9. @PostConstruct
    public void init() throws MQClientException {
    consumer = new DefaultMQPushConsumer("stock_consumer_group");
    consumer.setNamesrvAddr(nameAddr);
    consumer.subscribe(topicName,"*");
  10.  
  11. consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
    //实现库存真正到数据库内扣减的逻辑
    Message msg = msgs.get(0);
    String jsonString = new String(msg.getBody());
    Map<String,Object>map = JSON.parseObject(jsonString, Map.class);
    Integer itemId = (Integer) map.get("itemId");
    Integer amount = (Integer) map.get("amount");
  12.  
  13. itemStockDOMapper.decreaseStock(itemId,amount);
    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
    });
  14.  
  15. consumer.start();
  16.  
  17. }
    }
  18.  
  19. 16:guava限流算法

guava限流算法使用

引入依赖

  1. <dependency>
  2.   <groupId>com.google.guava</groupId>
  3.   <artifactId>guava</artifactId>
  4.   <version>18.0</version>
  5. </dependency>
  1. private RateLimiter orderCreateRateLimiter;
  2. @PostConstruct
  3. public void init(){
  4.     orderCreateRateLimiter = RateLimiter.create(300);
  5. }

这行代码,我们知道是创建一个每秒产生300个permit的速率器

  1. if(!orderCreateRateLimiter.tryAcquire()){
  2.     throw new BusinessException(EmBusinessError.RATELIMIT);
  3. }

17:限流方案

1:限并发
2;令牌桶算法
3;漏桶算法
限流粒度
1:接口维度
2:总维度
限流范围
1:集群限流 依赖redis以及其他中间件做统一计数器往往会产生性能瓶颈
2:单机限流 负载均衡的前提下单机平均限流效果更好

防刷方案

限制同一个会话的次数

限制同一个ip的次数

通过设备指纹 引入风控风险识别来做验证

18:

依靠秒杀商品的库存 设置一个阈值 当超过这个阈值就拒绝 来控制大闸流量(因为生成的秒杀地址token也是蛮耗时的,所以这样可以避免生成过多的token)

依靠消息队列排队来限制并发流量 更细粒度可以设置消费端一次同时处理几条消息来限制并发

19: redis序列化代码

  1. @Component
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
    public class RedisConfig {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
    RedisTemplate redisTemplate = new RedisTemplate();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
  2.  
  3. //首先解决key的序列化方式
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringRedisSerializer);
  4.  
  5. //解决value的序列化方式
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  6.  
  7. ObjectMapper objectMapper = new ObjectMapper();
    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addSerializer(DateTime.class,new JodaDateTimeJsonSerializer());
    simpleModule.addDeserializer(DateTime.class,new JodaDateTimeJsonDeserializer());
  8.  
  9. objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  10.  
  11. objectMapper.registerModule(simpleModule);
  12.  
  13. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  14.  
  15. redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
  16.  
  17. return redisTemplate;
    }
    }
  1. public class JodaDateTimeJsonDeserializer extends JsonDeserializer<DateTime> {
    @Override
    public DateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
    String dateString =jsonParser.readValueAs(String.class);
    DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
  2.  
  3. return DateTime.parse(dateString,formatter);
    }
  1. public class JodaDateTimeJsonSerializer extends JsonSerializer<DateTime> {
    @Override
    public void serialize(DateTime dateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
    jsonGenerator.writeString(dateTime.toString("yyyy-MM-dd HH:mm:ss"));
    }
    }
  1. }
  2.  
  3. 20多级缓存

1:Redis缓存

2:热点数据内存缓存

3:nginx proxy cache缓存(key在内存中,value为文件 所以性能不是很高)

4:nginx lua 缓存

2:热点数据内存缓存

可以选择guava

可以控制大小和超时时间

可以配置lru缓存策越

线程安全

@Service

public class CacheServiceImpl implements CacheService {

private Cache<String,Object> commonCache = null;

@PostConstruct

public void init(){

commonCache = CacheBuilder.newBuilder()

//设置缓存容器的初始容量为10

.initialCapacity(10)

//设置缓存中最大可以存储100个KEY,超过100个之后会按照LRU的策略移除缓存项

.maximumSize(100)

//设置写缓存后多少秒过期

.expireAfterWrite(60, TimeUnit.SECONDS).build();

}

@Override

public void setCommonCache(String key, Object value) {

commonCache.put(key,value);

}

@Override

public Object getFromCommonCache(String key) {

return commonCache.getIfPresent(key);

}

}

21:nginx处理阶段

nginx lua插载点

 

  1. Init_by_lua:系统启动时调用
  2. init_worker_by_lua:worker进程启动时调用
  3. set_by_lua:nginx变量使用复杂lua return
  4. rewrite_by_lua:重写url规则
  5. access_by_lua:权限验证阶段
  6. content_by_lua:内容输出节点

content_by_lua:内容输出节点

目的

拦截url为/staticitem/get的请求

实现

1.在/usr/local/Cellar/openresty/1.15.8.1/lua/目录下新建staticitem.lua脚本

staticitem.lua

  1. ngx.say("hello static item lua");

意思就是:

以HTTP response的形式返回字符串"hello static item lua"

2.修改nginx.conf

  1. location /staticitem/get {
  2. content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/staticitem.lua;
  3. }
实验

屏幕快照 2019-06-23 下午8.35.18

好处

nginx可以拦截nginx.conf中的location请求,将url路由到我们配置lua脚本做相应的处理。比如上面的例子中nginx就可以将/staticitem/get请求对应到我们编写的taticitem.lua,最终向浏览器返回hello static item lua

OpenResty

OpenResty

  • OpenResty由Nginx核心+很多第三方模块组成,默认集成Lua开发环境,使得Nginx可以作为一个Web Server使用。
  • 借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程序
  • OpenResty提供了大量组件如mysql、redis、memcached等等,使在nginx上开发Web应用程序更方便更简单

实战

  • OpenResty hello world
  • shared dic:共享内存字典,所有进程可见,lru淘汰(替换到nginx自己的基于文件系统的proxy cache)
  • OpenResty对redis的支持
OpenResty hello world
  1. /usr/local/Cellar/openresty/1.15.8.1/lua/目录下创建helloworld.lua脚本
  1. ngx.exec("/item/get?id=6");
  1. 修改nginx.conf配置

    1. #openresty helloworld
    2. location /helloworld{
    3. content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/helloworld.lua;
    4. }
  2. 实验

输入http://localhost/helloworld

显示:

11
shared dic
  1. /usr/local/Cellar/openresty/1.15.8.1/lua/目录下创建itemshareddic.lua脚本

    1. function get_from_cache(key)
    2. local cache_ngx=ngx.shared.my_cache
    3. local value=cache_ngx:get(key)
    4. return value
    5. end
    6. function set_to_cache(key,value,exptime)
    7. if not exptime then
    8. exptime =0
    9. end
    10. local cache_ngx=ngx.shared.my_cache
    11. local succ,err,forcible=cache_ngx:set(key,value,exptime)
    12. return succ
    13. end
    14. local args=ngx.req.get_uri_args();
    15. local id=args["id"]
    16. local item_model=get_from_cache("item_"..id);
    17. if item_model == nil then
    18. local resp=ngx.location.capture("/item/get?id="..id)
    19. item_model = resp.body
    20. set_to_cache("item_"..id,item_model,1*60)
    21. end
    22. ngx.say(item_model)
  2. 修改nginx.conf

    1. location /luaitem/get {
    2. default_type "application/json";
    3. content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/itemshareddic.lua;
    4. }
  3. 实验

    输入http://localhost/luaitem/get?id=6

    实验图
OpenResty对redis的支持

openresty对redis支持
  1. /usr/local/Cellar/openresty/1.15.8.1/lua/目录下创建itemredi.lua脚本

    1. local args=ngx.req.get_uri_args()
    2. local id=args["id"]
    3. local redis= require "resty.redis"
    4. local cache=redis:new()
    5. local ok,err= cache:connect("localhost",6379)
    6. local item_model=cache:get("item_"..id)
    7. if item_model==ngx.null or item_model==nil then
    8. local resp=ngx.location.capture("/item/get?id="..id)
    9. item_model=resp.body
    10. end
    11. ngx.say(item_model)
  2. 修改nginx.conf

    1. location /luaitem/get {
    2. default_type "application/json";
    3. content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/itemredis.lua;
    4. }
  3. 实验

    输入http://localhost/luaitem/get?id=6

    实验图

 
25:分布式事务代码

 
 
 
26:静态请求CDN

  • 优化的是H5(static),请求走静态资源文件。

  • 将静态资源的请求路由到CDN

v2-2080dfe83dd76bd079dbf3e7348ca795_hd

结构图

秒杀结构图

DNS用CNAME解析源站

回源缓存设置

cache control响应头

Request Headers:

例:

  1. GET / HTTP/1.1
  2. Host: localhost:4000
  3. Connection: keep-alive
  4. Pragma: no-cache
  5. Cache-Control: no-cache
  6. Upgrade-Insecure-Requests: 1
  7. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
  8. Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
  9. Accept-Encoding: gzip, deflate, br
  10. Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
  11. Cookie: Hm_lvt_512065947708a980c982b4401d14c2f5=1551084094; SESSION=OGQzOGIzMGEtZGYyNy00MjQ4LWI1ZGYtMjc2OTAxYjJmNjEz

###cache control有几种取值:

  • private:客户端可以缓存(请求发起的浏览器才可以缓存)

  • public:客户端和代理服务器(反向或正向代理)都可以缓存

  • max-age=xxx:缓存的内容将在xxx秒之后失效

    也就是说我们对应缓存的内容,从收到服务端的这个max-age返回数据开始,存储xxx秒后这个请求就失效,客户端必须往对应的服务端上面再次发起请求,用来验证和结束对应的请求体

  • no-cache:强制向服务端再验证一次(会将对应的缓存存储在客户端,但是在下次用的时候要向服务端验证一次这个缓存到底是能用还是不能用,no-store是压根不存)

  • no-store:不缓存请求的任何返回内容

怎么选择HTTP cache control头

有效性判断

  • ETag:资源唯一标识
  • If-None-Match:客户端发送的匹配ETag标识符
  • Last-modified:资源最后被修改时间
  • If-Modified-Since:客户端发送的匹配资源最后修改时间的标识符

补充:

ETag:

一般是将请求的资源做一个MD5或者类似hash操作,生成一个ETag的唯一标识,服务端在第一次返回内容中加上这个ETag的唯一标识,一起返回给浏览器,浏览器会存储下来这个ETag;下一次请求的时候,所谓的有效性判断,是浏览器将之前缓存下来内容的ETag的值一起带到服务器上,用来验证说它不发送具体的响应,而是发送一个对应的HTTP请求并且带上这个ETag的值,服务端会将这个ETag的值和我本地的文件ETag内容做比较,若比较是一致的,就返回304 not-modified,告诉客户端说服务端这个内容是有效的,直接使用浏览器里的缓存即可

若If-Modified-Since的值早于Last-modified的值,证明是无效的;若晚于Last-modified的值,则是有效的,说明这段时间资源没有被修改过。

用户请求浏览器资源的网站路径

未命名文件

浏览器三种刷新方式

  • 回车刷新或者a链接(get请求跳转):看cache-control对应的max-age是否仍然有效,有效则直接从cache取对应的数据,若cache-control中为no-cache,则进入缓存协商逻辑
  • F5刷新(Windows)或者command+R(macOS)刷新:去掉cache-control中的max-age或者直接设置max-age为0,然后进入缓存协商逻辑
  • ctrl+F5或commond+shift+R刷新:去掉cache-control和协商头,强制刷新 (强制从服务端拿内容)

对应的协商机制:

比较Last-modified和ETag到服务端,若服务端判断没变化则304不返回数据,否则200返回数据

CDN自定义缓存策略

  • 可自定义目录过期时间
  • 可自定义后缀名过期时间
  • 可自定义对应权重
  • 可通过界面或api强制cdn对应目录刷新(不一定保证成功)

阿里云CDN配置

cdn缓存

cdn缓存是一种服务端缓存,CDN服务商将源站的资源缓存到遍布全国的高性能加速节点上,当用户访问相应的业务资源时,用户会被调度至最接近的节点最近的节点ip返回给用户,在web性能优化中,它主要起到了,缓解源站压力,优化不同用户的访问速度与体验的作用。

缓存规则

与http缓存规则不同的是,这个规则并不是规范性的,而是由cdn服务商来制定,我们以腾讯云举例,打开cdn加速服务配置,面板如下。

可以看到,提供给我们的配置项只有文件类型(或文件目录)和刷新时间,意义也很简单,针对不同文件类型,在cdn节点上缓存对应的时间。

cdn运作流程

由图我们可以看出,cdn缓存的配置主要作用在缓存处理阶段,虽然配置项只有文件类型和缓存时间,但流程却并不简单,我们先来明确一个概念——回源,回源的意思就是返回源站,何为源站,就是我们自己的服务器,很多人误解接入cdn就是把资源放在了cdn上,其实不然,如图中所示,接入cdn后,我们的服务器就是源站,源站一般情况下只会在cdn节点没有资源或cdn资源失效时接收到cdn节点的请求,其他时间,源站并不会接收请求(当然,如果我们知道源站的地址,我们可以直接访问源站)。明确了回源的概念后,cdn的流程就显得不那么复杂了,简单的理解就是,没有资源就去源站读取,有资源就直接发送给用户。与http缓存不同的是,cdn中没有no-cache(max-age=0)的情况,当我们设置缓存时间为0的时候,该类型文件就被认定为不缓存文件,就是所有请求直接转发源站,只有当缓存时间大于0且缓存过期的时候,才会与源站对比缓存是否被修改。

缓存配置

cdn缓存配置并不麻烦,整体来说,建议和http缓存配置保持统一。需要特别注意的是,cdn的缓存配置会受到http缓存配置的影响,而且各个cdn服务商并不完全一致,以腾讯云为例,在缓存配置的文档中特别有以下说明。

这会对我们有什么影响呢?

  1. 如果我们http缓存设置cache-control: max-age=600,即缓存10分钟,但cdn缓存配置中设置文件缓存时间为1小时,那么就会出现如下情况,文件被访问后第12分钟修改并上传到服务器,用户重新访问资源,响应码会是304,对比缓存未修改,资源依然是旧的,一个小时后再次访问才能更新为最新资源
  2. 如果不设置cache-control呢,在http缓存中我们说过,如果不设置cache-control,那么会有默认的缓存时间,但在这里,cdn服务商明确会在没有cache-control字段时主动帮我们添加cache-control: max-age=600。

注:针对问题1,也并非没有办法,当我们必须要在缓存期内修改文件,并且不向想影响用户体验,那么我们可以使用cdn服务商提供的强制更新缓存功能,主要注意的是,这里的强制更新是更新服务端缓存,http缓存依然按照http头部规则进行自己的缓存处理,并不会受到影响。

小结

cdn缓存的配置并不复杂, 复杂的情况在于cdn缓存配置会受到http缓存配置的影响,并且不同的cdn运营商对于这种影响的处理也都不一致,实际使用时,建议去对应的cdn服务商文档中找到对应的注意事项。

 

miaosha的更多相关文章

  1. (转)开源项目miaosha(下)

    石墨文档:https://shimo.im/docs/2XlwliBQAYsKCHbq/ (二期)20.开源秒杀项目miaosha解读(下) [课程20]jmeter.xmind81.5KB [课程2 ...

  2. (转)开源项目miaosha(上)

    石墨文档:https://shimo.im/docs/iTDoZs4CVfICgSfV/ (二期)19.开源秒杀项目miaosha解读(上) [课程19]几张图.xmind0.6MB [课程19]开源 ...

  3. frame与frame之间怎么用jquery传值

    frame与frame之间如何用jquery传值 使用jquery操作iframe 1. 内容里有两个ifame <iframe id="leftiframe"...< ...

  4. ubuntu 14.04 解决JavaMelody 图片中文乱码

    从windows系统中,copy了MSYH.TTC和MSYHBD.TTC 2个文件到 服务器的%JAVA_HOME%\jre\lib\fonts\fallback 目录中, (如果fallback目录 ...

  5. 为什么MySQL死锁检测会严重降低TPS

    在大量的客户端,更新数据表的同一行时,会造成数据库的吞吐量大幅降低. 很多数据库的前辈和同行分别通过实验和源码的方法,定位到了罪魁祸首----MySQL死锁检测 实验方式:http://blog.cs ...

  6. JavaScript对象(窗口对象 定时器对象 )

    1:窗口时间 confirm(str):确认对话框,确认返回trun,取消返回false,但是必须要有两个return:不然就算按下取消也会提交 第一个return:用于保证确认按钮运行 <sc ...

  7. php学习之路

    1.php拼接字符串+查询 $floor_id = M('house_floor_input')->where($map1)->field('id')->select(); $flo ...

  8. .Net Core2.0秒杀CMS部署到Centos7.3遇到的坑,酸爽呀

    一.Centos7.3的安装 打开VirtualBox,点击新建,如下图所示: 点击“下一步”,弹出下面的对话框,调整内存大小,建议设置为2G,这样操作更流畅点 设置好,点击“OK”,再点击“启动”, ...

  9. PHP函数register_shutdown_function的用法

    register_shutdown_function这个函数是在PHP程序运行结束之前调用的,用这个函数可以做很多,比如调用运行发生致命错误中止的原因,或者调试程序的执行时间等. PHP终止的情况有哪 ...

随机推荐

  1. .net core swagger汉化

    基本swagger使用不再详解,具体百度其它帖子 1.将汉化的swagger js文件复制到项目根目录中 js代码如下 'use strict'; /** * Translator for docum ...

  2. Python3学习笔记——异常处理

    #!/usr/bin/env python # 1.异常处理 try: # 主要执行的代码 except IndexError as e: # 对于某些错误需要特殊处理的,可以对特殊错误进行捕捉 pr ...

  3. 分批插入数据基于mybatis

    DB框架:Mybatis.DataBase:Oracle. ---------------------------------------------------------------------- ...

  4. JS中常见的兼容

    1.阻止事件冒泡 e.stopPropagation()   ||    e.cancelBubble function stopBubble(e){ if (e.cancelBubble) { e. ...

  5. XMPP即时通讯协议使用(十一)——Openfire表结构汇总

    行号 字段名称 字段描述 字段类型 长度 主键 说明 允许为空 用户组数据表(ofGroup) 1 groupName 组名 varchar2 50 ★   NOT NULL 2 descriptio ...

  6. 【记录】linux docker 安装 tomcat

    前言:首先linux需要先安装docker,具体步骤可以参考博主之前博客,也可自行百度. 话不多说,开始安装tomcat: 通过docker安装tomcat docker pull tomcat:8. ...

  7. until 循环语句

  8. SourceTree 这是一个无效源路径/URL的 解决方法

    看网上的教程都解决不了,这是一个大坑,折腾了很久. 如果说你的项目存在,而不是url真的无效,那就是因为你的权限问题. 因为你的sourcetree登过其他账号,在sourceTree设置里面记录了他 ...

  9. Java集合和数组的比较(为什么引入集合)

    数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合.具体如下: 1)数组的效率高于集合类. 2)数组能存 ...

  10. 网络编程NIO-异步

    异步I/O是没有阻塞地读写数据的方法.通常在代码进行read调用时,代码会阻塞直至可供读取的数据.同样,write调用将会阻塞直至数据能够写入. 1.selector是一个对象,可以注册到很多个cha ...