谷粒商城关键技术总结-复习必备

本文详述了谷粒商城的各个关键技术点,包括配置、注册中心Nacos、跨域解决方案、文件存储、ElasticSearch基础、Nginx配置、缓存与Redis管理、线程池、认证登录、分布式锁、Spring Cache、异步处理、接口幂等性、本地与分布式事务、消息队列(RabbitMQ)以及Seata分布式事务等内容,全面解析了系统中涉及的各个环节和技术细节。
摘要由CSDN通过智能技术生成

配置、注册中心nacos

在这里插入图片描述

网关

抽取各服务的鉴权、限流】日志输出
在这里插入图片描述

跨域

跨域

指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
在这里插入图片描述

跨域流程

非简单请求(PUT,DELETE)等,需要先发送预检请求

  1. 预检请求,OPTIONS
  2. 响应允许跨域
  3. 发送真实请求
  4. 响应数据

在这里插入图片描述

解决跨域

使用nginx部署为同一域

在这里插入图片描述

配置当次请求允许跨域

在网关服务的配置类添加跨域配置
添加响应头
• Access-Control-Allow-Origin:支持哪些来源的请求跨域
• Access-Control-Allow-Methods:支持哪些方法跨域
• Access-Control-Allow-Credentials:跨域请求默认不包含cookie,设置为true可以包含cookie
• Access-Control-Expose-Headers:跨域请求暴露的字段
• CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
• Access-Control-Max-Age:表明该响应的有效时间为多少秒。在有效时间内,浏览器无
须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果
该首部字段的值超过了最大有效时间,将不会生效。

文件存储

在这里插入图片描述
阿里云对象存储
服务端签名后直传
在这里插入图片描述

ElasticSearch

基础概念

在这里插入图片描述
ElasticSearch7-去掉type概念
• 关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES
中不是这样的。elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同
的filed最终在Lucene中的处理方式是一样的。
• 两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必
须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在
处理中出现冲突的情况,导致Lucene处理效率下降。
• 去掉type就是为了提高ES处理数据的效率。
• Elasticsearch 7.x
• URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。
• Elasticsearch 8.x
• 不再支持URL中的type参数。
• 解决:将索引从多类型迁移到单类型,每种类型文档一个独立索引

nginx

Nginx+Windows搭建域名访问环境

在这里插入图片描述

域名映射效果

• 请求接口 gulimall.com
• 请求页面 gulimall.com
• nginx直接代理给网关,网关判断
• 如果/api/****
,转交给对应的服务器
• 如果是 满足域名,转交给对应的服务

Nginx动静分离

在这里插入图片描述
1、以后将所有项目的静态资源都应该放在nginx里面
2、规则:/static/**所有请求都由nginx直接返回

缓存与Redis

高并发下缓存失效问题

缓存雪崩

缓存雪崩是指在我们设置缓存时key采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
解决:
原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

缓存穿透

指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数据库也无此记录,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义
风险:
利用不存在的数据进行攻击,数据库瞬时压力增大,最终导致崩溃缓存
解决:
null结果缓存,并加入短暂过期时间

缓存击穿

• 对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。
• 如果这个key在大量请求同时进来前正好失效,那么所有对这个key的数据查询都落到db,我们称为缓存击穿。
解决:
加锁
大量并发只让一个去查,其他人等待,查到以后释放锁,其他人获取到锁,先查缓存,就会有数据,不用去db

分布式下加锁

本地锁,只能锁住当前进程,所以我们需要分布式锁
在这里插入图片描述

缓存数据一致性

双写模式

在这里插入图片描述

失效模式

如图所示,线程a修改完数据库库后,删除缓存,这时候线程b来对数据库进行修改,结果因为这次IO很慢很慢事务还没有提交,线程c进来的时候发现缓存没有数据,就去读取a对数据库更改的数据然后更新到缓存中,如果这次更新操作很快,线程b还没更新完数据库呢,这时,并没有什么影响,无非就是更新了一个错误数据,后面线程b对数据库操作完就会删除掉缓存,等到下一个请求进来就可以得到正确数据了。
在这里插入图片描述

缓存数据一致性-解决方案

无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?
• 1、如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可
• 2、如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。
• 3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
• 4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心脏数据,允许临时脏数据可忽略);
总结:
• 我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。
• 我们不应该过度设计,增加系统的复杂性
• 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。
在这里插入图片描述

Redisson 完成分布式锁

分布式锁
占坑set和设置过期时间要是原子操作:lua脚本

配置

// 默认连接地址 127.0.0.1:6379
RedissonClient redisson = Redisson.create();
Config config = new Config();
//redis://127.0.0.1:7181
//可以用"rediss://"来启用 SSL 连接
config.useSingleServer().setAddress("redis://192.168.56.10:6379");
RedissonClient redisson = Redisson.create(config);

使用分布式锁

RLock lock = redisson.getLock("anyLock");// 最常见的使用方法
lock.lock();
// 加锁以后 10 秒钟自动解锁// 无需调用 unlock 方法手动解锁
lock.lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待 100 秒,上锁以后 10 秒自动解锁 
boolean res = lock.tryLock(100,10, TimeUnit.SECONDS);
if (res) {
   
	try {
   
		...
	} finally {
   
		lock.unlock();
	}
}

Spring Cache

Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合;Cache 接 口 下 Spring 提 供 了 各 种 xxxCache 的 实 现 ; 如 RedisCache , EhCacheCache , ConcurrentMapCache 等;
每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

注解

在这里插入图片描述

异步&线程池

线程池

初始化线程的 4 种方式

1)、继承 Thread
2)、实现 Runnable 接口
3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)
4)、线程池
方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景
方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致
服务器资源耗尽。
方式 4:通过如下两种方式初始化线程池
Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, handler);

常见的 4 种线程

 newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若
无可回收,则新建线程。
 newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
 newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
 newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务
按照指定顺序(FIFO, LIFO, 优先级)执行。

开发中为什么使用线程池

 降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
 提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
 提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

Executor

public interface ExecutorService extends Executor
ExecutorService service = Executors.newCachedThreadPool();

在这里插入图片描述

CompletableFuture

CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。
在这里插入图片描述

创建异步对象

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier
  • 9
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值