1.添加lombok的@Data注解,相当于 在编译期间, lombok会自动给这些类创建getter 和 setter方法。
2.无法导入import javax.servlet.http.HttpServletRequest; 在项目设置中,添加该包的依赖、
3.使用逆向工程生成后端基础的curd代码之后,将原本的代码删除,将逆向生成的代码复制到项目目录下边。
4.forEach用法, :
list.forEach((item)->{
System.out.println(item);
});
5.nacos:自己的微服务想要被nacos发现,要在yml文件中,application:name:xxx
6.Feign:远程调用功能:1个微服务向注册中心,调用另外一个微服务的功能。
(在微服务中引入openfeign的依赖)
Feign整合了Ribbon(负载均衡)和Hystrix(服务熔断),可以让我门不需要显式地使用这两个组件。
7.Nacos作为配置中心,实时更新配置文件的内容,需要在控制类中加入注解 @RefreshScope,则可以实时的更新配置。
如果配置中心和当前服务的配置文件中都配置了相同的项,则优先使用配置中心的配置。
8.Nacos作为注册中心,其它的微服务,要将自己注册到注册中心,然后完成各自的功能。
9.前端的逆向生成的效果也可以进行优化, 对于前端的vue组件,其请求地址为 后端的 controller类的地址。
10. vue检查的报错比较严格,关掉相关的报错: 在 build下的 webpack.base.conf.js中,将 const createLintingRule中的内容注释掉,重启项目即可。
11.阿里云的子账户: 登录名称: gulimall-dj
子账户的Acc ID, Acc Secret.
12.在阿里云dependency里边配置好aliyun之后,aliyun账户相关的配置,可以在配置文件中进行配置。
13.前端要进行表单验证功能,比如: 检索的首字母,不能为一串字母。
前端发来的信息,在后端也需要进行验证。 通过 JSR303:java 规范 提案,第303号,规定了数据校验相关的标准。
14.统一的异常处理
@ControllerAdvice,
自定义校验注解,自己写一个 注解,然后引用。 根据 JSR303的标准和规范。
15.在电商项目中,一般不做关联查询。
关联查询:(84条消息) SQL关联查询_一杯清茶看世界的博客-CSDN博客_sql关联查询
16.对于前端的属性,第一层数据为data,然后每一层的属性名,对应 后端controller代码的属性名---->是对应的实体类的参数如BrandEntity,xxxEntity里边的参数字段,只有这样,才能将数据返回并渲染到前端页面中。
17.实体类 Entity对应数据库中的表,而真正操作 数据库的是 Dao--mapper映射,简单的方法是 MyBatis-Plus,该框架 可以使得原本的mybatis操作数据库变得更加简单。
18.在服务之间相互调用的时候,是以 TO 模式(就是不同的数据类型,类似于Entity类,包含了多种数据参数信息)进行的,发送的json数据只要是兼容的,双方服务就无需使用同一个to。
19.要在 ES中 去 搜索商品,需要给商品按照 ES的模式对其进行存储,因此这里就有关于 商品的数据结构。
具体信息:
(84条消息) ElasticSearch与Kibana简介及使用入门_zh_94的博客-CSDN博客_elasticsearch kibana
与其它类型数据库的对比:
通过Kibana(可视化界面)完成对数据的存储和搜索等。
(重点,在idea中,实现ES的具体组件,coding,实现搜索功能)
RestHighLevelClient,es自带的客户端组件。(84条消息) RestHighLevelClient的简单使用_明快de玄米61的博客-CSDN博客_resthighlevelclient使用
20. ES保存数据是 扁平化处理的, 为了使其没有这样的效果,则可以 在 type下,设置为 nested--->嵌入式的,就可以避免掉 因扁平化处理,仍然可以搜索到的,我们不想要的文档。
21. MybatisPlus,展现在代码中的 QueryWrapper 中
22. 设置nginx自动启动: docker update nginx --restart=always
23.nginx 反向代理(拥有公网ip,大家都可以访问),就是本地发送请求,找到nginx,nginx找到 服务(还是本地的),然后nginx给服务加上一个外壳,再将服务返回给 请求方。 (请求 发出到返回 都要经过nginx)
24.继续优化是,nginx --> 找到网关,网关再去调取服务(项目中是这样用的)
25.整个流程:(在开发的时候,模拟项目上线的流程)
Nginx + Windows搭建域名访问环境
举例:
1.1)比如先在自己电脑上的SwitchHosts,给电脑配置上相应的域名,如 gulimall.com...,
1.2)在电脑浏览器中输入 gulimall.com,则windows如何知道 该域名对应的ip地址呢?
way1:查看系统内部的域名映射规则,如果gulimall.com已经有映射,则浏览器可以直接去网该地址(该功能是由 网卡帮忙转到的)
way2:如果系统内部没有找到对用的域名映射规则,则:
去网络上的DNS,先解析出域名,以及域名对应的IP地址(公网上保存的)
开发时候,就是按照way1实现的,先把 域名对应到虚拟机地址上 ,如: 192.168.xx.xx(虚拟机地址) -->gulimall.com,随后如果需要访问kibana5601,ES是9200端口,则直接输入 192.168.xx.xx:9200 等价于访问 gulimall.com:9200
我们希望访问gulimall.com等价于访问本机的 localhost:10000端口(商品服务的地址),即让ngnix帮我们进行反向代理,实现该需求(跳过网关,直接在ngnix上配置)
客户端访问 gulimall.com,gulimall.com在虚拟机上,所以进入到虚拟机,虚拟机上又装了nginx,所以来到 nginx,因为访问gulimall.com,所以默认访问80端口(nginx的端口),又nginx80端口的配置是监听gulimall.com这个域名。 当监听到了之后,nginx又将其代理给了网关,因为来的域名是gulimall.com的,但是网关又有配置(配置的是,将来自gulimall.com的请求转交给product服务)(该段配置要放置在配置路由最后边的位置)原因:如果放在前边,则无法访问服务中的api方法,因为该段配置相当于把后边的api截串的配置给禁用掉了。
,将服务路由给了 product服务,服务里边有Tomcat,Tomcat调用程序完成服务。
如下,相当于,让nginx代理了我们本机的服务,客户端-->{ gulimall.com(地址是虚拟机的ip)、nginx(反向代理)、代理的是本机服务(包含了地址) } -->nginx再将服务结果返回给客户端
nginx配置文件信息:
正向代理和反向代理
正向代理与反向代理是相对于我们自己(客户端)而言,帮助客户端(对于服务器而言,隐藏了客户端信息)则是正向代理,帮助服务端(对于服务器而言,隐藏了服务器的信息)则是反向代理。
正向代理---先搭建一台代理服务器(可以访问服务器),自身电脑(客户端)只用配置该代理服务器的地址,以后客户端想要访问的网址,则都由代理服务器代理访问,并将结果返回给客户端。总的来看,正向代理服务器相当于是帮助我们自己上网。
反向代理--- 搭建集群环境的时候非常需要 ,Nginx相当于对外界屏蔽了 所有的内网服务器(只有局域网可以访问到,外界无法访问)信息,不将内网的IP地址暴露给外界,起到防止攻击保护服务器的效果。
nginx负载均衡到网关
nginx配置上游服务器名称为 gulimall,本机网关--地址和端口号
首先:配置上游服务器的地址,也就是给本机网关地址
然后:修改nginx的代理地址,也就是上游服务器的地址(gulimall --> 网关的地址)
也是在nginx的config中配置。
最后,网关再配置每一个服务的 路由地址。
形成: 客户端--> nginx --> 网关-->微服务
在Nginx代理网关的时候,传给网关会出现 丢失请求的host信息,需要在修改nginx中的路径映射规则:
在location后边设置上: proxy_set_header Host $host
26.性能压测:从外部看,性能测试主要有三个指标:
吞吐量:每秒钟系统能够处理的请求数,任务数
响应时间(RT):服务处理一个请求或一个任务的耗时
错误率:一批请求中结果出错的请求所占比例。
JMeter压测工具,启动以下程序即可。
运行JMeter会出现的错误:
27: 性能监控-堆内存与垃圾回收
使用jconsole 和 jvisualvm(是jconsole的升级版),可视化GC过程 “谷粒商城 p144-5min”
启动两者,直接cmd,输入两者名称即可。
28.具体的对中间件的优化: 开Thymeleaf缓存,以及 调整日志的级别,由debug,改为 error,
对数据库的属性设置索引。
29.动静分离的时候,首页的静态资源(js,css,图片,视频等等),全是由nginx返回,(之前的做法是,将这些静态资源放在 微服务的 静态资源文件夹下 static下边)
其次将静态资源放到 虚拟机中,交由nginx返回,前端页面中的链接地址,改成虚拟机中,静态资源的地址。
配置nginx的路径,只要是 /static/路径下的,都去root /usr/share/nginx/html下边去找资源。
首页的数据,全部都是由tomcat返回、
三级分类查询中,将多次对数据库查询优化为1次查询。
哪些数据适合放入缓存?
·即时性、数据一致性要求不高的(只需要 满足最终一致性的)
·访问量大且更新频率不高的数据(读多,写少)
本地缓存:可以用map,可以联想到Spring里边的三级缓存就是这个概念。使用这样的缓存模式,会出现在分布式情况下,缓存出现不一致的情况。如下图,在每一次负载均衡到不同的服务器时候,可能查询到的缓存是不一样的。
使用分布式缓存:共享一个缓存中间件,redis,解决缓存不一致的问题,并且假设一个redis不足以存储较大的缓存大小,还可以redis的集群工作,实现分片存储。
整合redis:
1.在服务中引入redis(pom)
2.在yml文件中,配置redis
3.使用springboot自动配置好的StringRedisTemplate来操作redis
加入缓存逻辑,缓存中存的数据是json字符串,因为JSON是跨语言,跨平台兼容。
【序列化与反序列的过程】给缓存中放json字符串,当使用的时候,还要逆转成可以使用的对象类型。都是使用alibaba的fastjson工具进行序列化和反序列化。
反序列化过程: 使用匿名内部类的方式
JSON.parseObject(new TypeReference
出现的问题:在进行压测时,缓存会产生 : 堆外内存溢出: OutOfDirectMemoryError,原因是:
1.springboot2.0以后默认使用 lettuce作为操作 redis的客户端,它使用netty进行网络通信
2. lettuce的bug导致堆外内存溢出, netty默认 -Xmx300m,如果没有指定则默认使用300m大小的内存。
解决办法:不能只增大内存,增大内存只是延缓了出现堆外内存溢出的速度。 1.升级lettuce客户端 2.切换使用jedis
30.lettuce、jedis是 操作redis的底层客户端,相当于封装了操作redis的一些api,在java中可以使用jedis,并对redis进行相关的操作。
最终,spring为了简化操作,对 lettuce和jedis进行了再次封装——redisTemplate;后序操作只需要 直接使用redisTemplate即可。
缓存出现的三个问题:
总结: 空结果(null)缓存:解决缓存穿透
设置过期时间(加随机值): 解决缓存雪崩(key大面积失效)
加锁: 解决缓存击穿(单点key失效)
加锁的具体逻辑:因为springboot所有的组件在容器中都是单例的,因此sychronized(this)就可以满足上锁的逻辑。
但是以上的加锁逻辑,在分布式的情况下,就不适用了,只能锁住本地线程,就无法保证,在高并发的情况下,依然保证,只有一个线程进行查询数据库,其它的线程只需要查询缓存即可。
保证只用查询一次数据库,其它的线程进来之后就可以直接从数据库中查找数据的加锁逻辑如下:黑色背景就是代表一把锁。
有专门的分布式锁框架。 Redisson锁
分布式锁的简单逻辑,转换成业务逻辑代码如下:
对上的锁,设置一个过期时间:
问了解决如果出现突发情况,没有设置过期时间的时候程序崩溃,则需要保证加锁和过期时间是同步的,原子的,因此,直接 "set lock 111 EX 300 NX",将过期时间和加锁的过程在一个步骤下设置。
由于网络交互,比较消耗时间,因此在删除锁的时候还是会出现删除别人锁的情况,因此解决办法就是: 使用redis + Lua脚本完成。
Redisson锁 实现分布式锁的功能。
要实现redisson的功能,需要配置类,对redisson进行相关配置,所有对redisson的使用,都是通过redissonClient对象。如下则是配置的一个单redis节点模式。
redisson加的锁:不会出现死锁问题
1.可以通过redisson的看门狗原理实现锁的自动续期,所以我们不需要担心业务时间过长,锁自动过期被删除的情况。
2.加锁的业务只要完成,就不会对锁进行自动续期,当锁的存活时间达到我们设置的值之后(默认为30s),就会自动删除锁。
读写锁: 当加上这个锁之后,只有 “写锁” (排它锁,互斥锁,独享锁)成功之后,才能进行 “读”(共享锁) 的操作。 只要 写锁没有释放,读就必须等待。
Redisson读写锁的特点总结:
信号量的概念:(4条消息) Java中Semaphore(信号量)的使用_大愚若智_的博客-CSDN博客_java semaphore
闭锁的概念:java闭锁用于多个线程共同执行后,统一执行一个动作。比如:多个线程执行计算操作,最后汇总到同一个线程执行汇总计算。需要注意的是,java中的闭锁是仅一次的。当闭锁打开后就会统一执行下面的动作。
缓存的一致性问题:
在双写模式下:会出现暂时性的脏数据问题,如果业务要求是 最终一致性即可,那么就不需要解决这种问题; 要想解决: 可以 在 “写数据库--写缓存” 这两步骤都加锁,只有执行完写数据库和写缓存之后,才进行下一个线程的写数据库和写缓存。
解决缓存数据的一致性问题:
cannl:阿里开源的中间件,可以模式数据库(MySQL)的从服务器,当数据库有什么变化,则cannl就会同步过去。
在大数据情况下,可以解决数据异构问题。cannl订阅binlog日志,和访问记录表,然后给出用户喜好推荐。
本项目中,保证缓存数据一致性的解决方案,采用的是——失效模式
1.缓存的所有数据都设有过期时间,数据过期后,当下一次查询时,出发主动更新;
2.读写数据的时候,加上分布式的读写锁。
注意:对于加上分布式读写锁,如果是经常写和经常读的,是会对系统性能产生影响的;但是本项目是写一次,就可以读很多次,且在读的时候,其实就是相当于无锁的状态。
springCache——当设计好缓存之后,就需要设计对缓存的使用了,分别为 对缓存的读和对缓存的写:
整合springCache缓存的开发:
只需要开启缓存功能,并使用注解就可以完成缓存操作。
如在某个方法上加入注解,@Cacheable,则最后将方法中的结果放入缓存 ; 也可以在
@Cacheable后边加多个缓冲区,可以自定义多个缓冲区
缓存的默认行为:
1)如果缓存中有,方法不再调用
2)key默认自动生成,缓存的名字:SimpleKey{}(自主生成的key值)
3)缓存的value值,默认使用jdk序列化机制,序列化后的数据存到redis中
4)默认 ttl 时间 为 -1
自定义:
1)指定生成的缓存使用的key: key属性指定,接受一个SpEL
2)指定缓存的数据的存活时间,配置文件中修改 ttl
3)将数据保存为json格式
@CacheEvict,删除缓存,注意对于 缓存的 key 要加 单引号 ‘ ’
其中,在写模式下,只需要缓存的数据设置一个过期时间就足够了。
32. Rabbit MQ:
Producter服务制造端,将消息 发送给交换机(Exchange), 客户端监听消息是 监听队列 Queue。
交换机可以绑定交换机,也可以绑定队列,因此可能会出现多层的路由。
33.spring session 相当于是让 登录状态,可以延续到多个服务的阶段。(如加入购物车,结算等等)
34.Seata控制分布式事务:有几种常见的分布式事务解决方案,alibaba提供的Seata
1)每一个微服务必须先 创建 undo_log表
2)安装事务协调器: seata-server
35.分布式信号量:redissonClient.getSemaphore
36.秒杀的时候,用 MQ 来进行流量的削峰。
36.Sentinel用于 高并发服务 (秒杀服务) 的限流 & 熔断 & 降级
37.分布式高并发三个重点 : 缓存(Redis), 异步(线程池),队排好(Rabbit MQ)--用于分布式事务中的,付款服务 / 秒杀服务的 流量削峰
38.在导入服务时,要导入接口类型,而不能导入实现类型。
39.正常如果项目上线的话,需要买一台服务器,且有一个公网IP地址,有该IP则所有人都可以访问该服务器,在服务器上绑定项目的域名(备案)。
然后别人访问域名,就可以访问该服务器,就可以访问该项目了。
40.分布式框架整个流程:
前端页面--> controller -- > service --> mapper -->(pojo文件)-->数据库