/gmall-0722

谷粒商城电商平台

Primary LanguageJavaScriptApache License 2.0Apache-2.0

gmall-0722

谷粒商城电商平台

day01: nacos:注册及配置中心 eureka nacos服务:阿里官方提供了nacos-server(bat sh) 注册中心: 1. 引入依赖:discovery-starter 2. application.yml:spring.cloud.nacos.discovery.server-addr=地址 3. 注解@EnableDiscoveryClient

	配置中心
		1. 引入依赖:config-starter
		2. bootstrap.yml:
			spring.cloud.nacos.config.server-addr=地址
			spring.cloud.nacos.config.namespace=唯一标志uuid
			spring.cloud.nacos.config.group=组名
			spring.cloud.nacos.ext-config[0].data-id=配置名
			spring.cloud.nacos.ext-config[0].group=组名
			spring.cloud.nacos.ext-config[0].refresh=true
		3. 注解:@RefreshScope
		好处:
			1. 动态刷新配置,即使改动了配置,也不需要重启
			2. 统一管理配置文件
			3. 配置版本管理

gateway:网关组件 zuul
	动态路由、负载均衡、身份认证、限流、路径重写、熔断降级、请求过滤
	spring.cloud.gateway.routes[0]
		id: 唯一标志
		uri:路由的路径
		predicates:断言(判断)
		filters:过滤器(拦截)
			重写请求路径的过滤器:RewritePath
			自定义了过滤器:Auth
	自定义过滤器:
		1. 编写class实现GatewayFilter接口(推荐继承AbstractGatewayFilter)
		2. 编写xxxGatewayFilterFactory类实现GatewayFilterFactory(xxx就是过滤器的名称)
		3. 通过xxx使用

day02: 搭建环境 逆向工程生成单表操作的增删改查

mybatis-plus:在mybatis的基础上制作增强不做改变
	单表的增删改查,不需要自己实现
	1. 引入依赖:参照官网
	2. 配置
		mybatis-plus.mapper-locations=classpath:mappers/pms/**/*.xml
		mybatis-plus.global-config.db-config.id-type=auth(数据库自增,默认分布式id生成策略,input:指定id)
	3. 注解:@MapperScan(dao/mapper包路径)
		@TableName("表名") :当实体类和表名不一致的情况下
		@TableId(type= IdType.INPUT):指定表的主键
		@TableField("列名"):当字段名和列名不一致的情况下
	4. 分页插件:
		@Configuration
		public class MybatisPlusConfig {

			@Bean
			public PaginationInterceptor paginationInterceptor() {
				PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
				return paginationInterceptor;
			}
		}
	5. Wrapper:QueryWrapper   UpdateWrapper

cors解决跨域
	浏览器同源策略
	什么情况下会出现跨域? 
		1. ajax请求
		2. 域名、端口、协议不一致
	解决方案:
		1. nginx反向代理为不跨域
		2. jsonp方式,只能解决get
		3. cors浏览器规范
			发送两次请求:预检请求(允许跨域的请求方法 允许那些域名跨域 是否允许携带cookie 允许携带的头信息) 真正的请求
	过滤器统一解决:
		springMVC:CorsFilter
		springWebFlux: CorsWebFilter
		
阿里OSS: 浏览直传图片到阿里云服务器

day03: spu:标准商品单元,商品集合 sku:库存量单元,具体商品 规格参数 表关系 保存商品(pms sms wms)

day04: 本地事务: @Trancactional 事务:逻辑上的一组操作,组成这组操作的各个逻辑单元,要么都成功,要么都失败。 ACID:原子性 一致性 隔离性 持久性

	隔离级别:
		read uncommitted:读未提交,脏读(不允许发生) 
		read committed:读已提交,不可重复读(可允许) oracle
		repeatable read:可重复读,幻读/虚读(可允许) mysql
		serializable:序列化读,性能最低
		
	传播行为:7中传播行为,
		REQUIRED:一个事务,要么成功,要么失败
		REQUIRES_NEW:两个不同事务,彼此之间没有关系。一个事务失败了不影响另一个事务
		
	回滚策略
		默认的回滚策略:编译时(受检异常)异常不回滚,运行时异常(不受检异常)都会回滚
		rollBackFor
		rollBackForClassName
		noRollBackFor
		noRollBackForClassName
		
	只读事务:不能做增删改操作
		readOnly=true
		
	超时事务:timeOut=3

分布式事务:网络、服务器宕机、消息丢失
	场景:
		1. 不同的服务不同数据库
		2. 不同的服务相同数据库
		3. 相同的服务不同的数据库
	解决方案:
		1. 两阶段提交(XA:数据库支持  seata, 性能较低)
		2. TCC补偿机制(T:try  C:confirm  Cancel:取消)
		3. 消息队列最终一致性(性能最高)
	seata:
		seata-server服务
		每一个数据库都要有一个undo_log表
		1. 引入依赖:参照官方文档
		2. 配置:
			1. registry.conf
			2. file.conf
			3. java配置:配置了数据源代理
				@Primary
				@Bean("dataSource")
				public DataSource dataSource(@Value("${spring.datasource.url}") String jdbcUrl, @Value("${spring.datasource.username}") String username,
											 @Value("${spring.datasource.password}") String password, @Value("${spring.datasource.driver-class-name}") String driverClassName) {
					HikariDataSource hikariDataSource = new HikariDataSource();
					hikariDataSource.setJdbcUrl(jdbcUrl);
					hikariDataSource.setDriverClassName(driverClassName);
					hikariDataSource.setUsername(username);
					hikariDataSource.setPassword(password);
					return new DataSourceProxy(hikariDataSource);
				}
		3. 注解:@GlobalTransactional @Transactional

day05 倒排索引:文档列表、倒排索引区 全文检索:从海量数据中快速获取需要的信息 lucene:底层api 搜索产品:solr elasticsearch

elasticsearch:
	安装:jvm.options  elasticsearch.yml
	kibana:
	ik分词器:ik_max_word ik_smart
		扩展词典(niginx配置,添加分词后不用重启) 停用词典

DSL语法:
java客户端:jest springData-elasticsearch(ElasticsearchRestTemplate ElasticsearchRepository(Repository)  HignLevelRestClient SearchSourceBuilder)
	springData-elasticsearch(ElasticsearchRestTemplate ElasticsearchRepository(Repository)  HignLevelRestClient SearchSourceBuilder)
	spring.elasticsearch.rest.uris=地址

day06 数据模型的设计 Document(indexName = "goods", type = "info", shards = 3, replicas = 2)作用在实体类上 @Id:主键字段 @Field(type = FieldType.Keyword/Text/Integer/Boolean/Long/Float/Nested, index = false, analyzer = "ik_max_word")

数据导入功能
DSL语句
代码实现搜索功能

day07 rabbitmq及数据同步 MQ:message queue 作用:解耦 异步 削峰 实现: AMQP(rabbitmq 协议 五种消息模型 任何语言都可以实现) JMS(activemq java规范 提供了两种消息模型 必须是java实现) 安装:5672(java) 15672(浏览器客户端) 25672(集群) 五种消息模型: 1.simple(简单模型) 2.worker(工作模型) 3.发布订阅之fanout(广播:消息发送之后,所有的队列都可以获取消息) 4.发布订阅之direct(路由:定向发送消息) 5.发布订阅之topic(通配符:*一个词 #一个或者多个词) ACK:消息确认机制 能者多劳:channel.basicQos(1) 持久化:交换机持久化 队列持久化 消息持久化 springBoot整合rabbitMQ 1. 引入依赖:amqp-starter 2. 配置:rabbit链接信息 spring.rabbitmq.host=地址 spring.rabbitmq.post spring.rabbitmq.virtual-host=虚拟主机 spring.rabbitmq.username= spring.rabbitmq.password= 3. 注解: 接受消息: @RabbitListener(bindings = @QueueBinding( value = @Queue(value = "队列名称", durable = "true"), exchange = @Exchange(value = "交换机名称", ignoreDeclareExchange = "true", type = ExchangeTypes.Topic), key = {"routingKey"} ))

		发送消息:AmqpTemplate.converteAndSend()

day08 三级分类的查询 添加缓存: 标准:1. 写的频率低 2. 读的频率高 过程: 1.查询缓存有没有
2.缓存中没有查询数据库 3.放入缓存 缓存存在的问题: 雪崩:给缓存的过期时间添加随机值 穿透:即使数据库中的数据为null,也缓存 击穿:分布式锁 实现分布式锁: 标准: 1. 排他 2. 防止死锁发生,设置有效时间 3. 防止释放别人的锁 实现: 1. 获取锁(原子性) String uuid = UUID.randomUUID().toString(); Boolean lock = this.redisTemplate.opsForValue().setIfAbsent("lock", uuid, 2, TimeUnit.SECONDS); 2. 释放锁(原子性) String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; this.redisTemplate.execute(new DefaultRedisScript<>(script), Arrays.asList("lock"), Arrays.asList(uuid)); 3. 重试(没有锁的请求) try { Thread.sleep(1000); testLock(); } catch (InterruptedException e) { e.printStackTrace(); } redisson: 1. 引入依赖 2. java配置 @Configuration public class RedissonConfig {

			@Bean
			public RedissonClient redissonClient(){
				Config config = new Config();
				config.useSingleServer().setAddress("redis://172.16.116.100:6379");
				return Redisson.create(config);
			}
		}
	3. RedissonClient.getLock() lock.lock() lock.unlock()
		semaphore countdownlatch readWriteLock

使用AOP结合分布式锁实现缓存的封装
	1. 自定义注解@GmallCache
	2. 编写切面实现缓存功能
		@Aspect:切面类
		@Around:环绕通知 @Around(@Annotation=注解的全路径)
			四个条件:1.方法的返回值必须是Object 2.方法的参数ProceedingJoinPoint  3.方法必须抛出Throwable异常  4.joinPoint.proceed(joinPoint.getArgs())
			(MethodSignature)joinPoint.getSignature();

day09 商品详情页:大量的远程调用 优化:页面静态化、缓存、异步编排 异步编排:CompletableFuture 线程初始化回顾: 1.继承thread抽象类 2.实现Runnable接口 3.实现Callable接口 + FutureTask 4.线程池 异步任务初始化: runAsync():没有返回值 supplyAsync():有返回值 任务完成时方法: whenComplete((t,u) -> {正常或异常情况下开启另一个任务}) whenCompleteAsync:异步 exceptionally(t -> {异常情况下开启另一个任务}) 处理任务完成结果 handle(t -> {处理任务完成时的结果}) 中间的串行方法:9个方法 thenApply():获取上一个任务的返回结果集,并返回自己的结果集 thenAccept():获取上一个任务的结果集,执行自己的任务,没有返回值 thenRun():上一个任务结束,执行自己的任务,不获取返回结果集,也没有自己的返回结果集 thenApplyAsync() thenAcceptAsync() thenRunAsync() 两个任务组合,都要完成:9个方法 thenCombine:组合两个future,获取两个future的返回结果,并返回当前任务的返回值 thenAcceptBoth:组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。 runAfterBoth:组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务。 两任务组合,一个完成:9个方法 applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。 acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。 runAfterEither:两个任务有一个执行完成,不需要获取future的结果,处理任务,也没有返回值。 多任务组合: allof():所有任务完成,执行新的任务 anyof():任何一个任务完成,执行新的任务

day10 注册功能: 1. 校验数据是否可用:用户名 手机号 邮箱 2. 发送短信验证码:生成验证码 发消息 并把短信验证码保存到redis中 3. 用户注册功能(新增用户) 1)校验验证码 2)生成盐 3)对密码加盐加密 4)保存用户信息 5)删除redis中的验证码 4. 根据用户名和密码查询用户 1)根据用户名查询用户信息 2)判断用户是否存在 3)对用户输入的密码加盐加密 4)和数据库中的密码比较 cookie: 作用域:子可用访问父,父不可操作子。token的域使用一级域名 作用路径:/ 过期时间: 单点登录: 无状态登录(jwt) 有状态登录(session redis) jwt + rsa jwt: 头部信息:token类型,编码方式 载荷信息:用户具体信息 签名信息:校验前两部信息是否合法 rsa加密 加密方式: 对称加密:base64 不可逆加密:md5 非对称加密:rsa(公钥 私钥) 代码具体实现:参照课堂代码

day11 购物车需求: 新增购物车 删除购物车 查询购物车 修改数量 勾选购物车 比价 技术选型: 未登录情况下: redis mysql cookie indexDB webSQL localStorage mongodb(NoSQL数据库,硬盘,写比较频繁)

	登录情况下:
		mysql + redis
		redis
		mongodb
数据模型:hash(散列)
业务流程
	新增:判断是否登录
		未登录:使用userKey放入redis中
		登录:使用userId放入redis中
		判断该商品是否在购物车中
			在:更新数量
			不在:新增一条记录
	查询:判断是否登录
		未登录:根据userKey查询,查询完成直接响应
		登录:先判断是否有未登录的购物车
			有:同步到登录状态的购物车,再查询
			无:直接查询已登录的购物车,直接响应
比价功能:
	单独保存一份实时价格:{skuId:price}
	查询购物车,实时价格单独查询
	价格同步:使用消息队列
	
获取用户登录信息的拦截器:LoginInterceptor
	ThreadLocal
	1.实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类
	2.实现3个方法:
		1.preHandle:前置方法,返回值(true:放行  false:拦截)
			获取用户的登录状态(解析jwt类型的token)
			放入threadLocal
		2.postHandle:后置方法
		3.afterComplete:完成方法,视图渲染完成之后执行
			ThreadLocal.remove()释放线程局部变量,防止内存泄漏
	3.编写一个配置类,实现WebMvcConfigurer接口
		addInterceptors(InterceptorRegistry registry) {
			registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
		}

day12 订单 订单确认页 数据模型:orderToken防止表单重复 收货地址列表 配送方式 送货清单 积分信息 IdWorker:雪花算法

	提交订单
		提交数据模型:orderToken、收货地址、配送方式、支付方式、送货清单、发票信息、积分信息、总价
		业务流程:
			1. 防重
			2. 验价
			3. 验库存并锁库存
			4. 下单
			5. 删除购物车
			
	定时关单
		1. 定时任务(juc   @Scheduled  @EnableScheduling)
		2. 延时队列(延时队列:设置消息的有效时间/设置死信路由/设置死信rountingKey,死信队列绑定到私信路由,消费者监听死信队列)
	
支付
	内网穿透:花生壳 哲西云
	阿里沙箱:
	支付成功异步回调

秒杀
	页面静态化、限流、异步、缓存
	页面限流
	nginx限流:漏斗算法 令牌桶算法
	网关限流:限流过滤器
	服务器内部限流:信号量
	用户查询订单时,使用闭锁