学习地址:Java项目《谷粒商城》架构师级Java项目实战,对标阿里P6-P7,全网最强_哔哩哔哩_bilibili
微服务架构风格,就像是一把单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。
简而言之,拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。
集群是物理形态,分布式是个工作方式。
只要是一堆机器,就可以叫集群,他们是不是一起协作着干活,这个谁也不知道;
《分布式系统原理与范型》定义:“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”,分布式系统(distributed system)是建立在网络之上的软件系统。
分布式是指将不同的业务分布在不同的地方,集群指的是将几台服务器集中在一起,实现同一业务。
例如:京东是一个分布式系统,众多业务运行在不同的机器,所有业务构成一个大型的业务集群。每一个小的业务,比如用户系统,访问压力大的时候一台服务器是不够的,我们就应该将用户系统部署到多个服务器。也就是每一个业务系统也可以做集群化;分布式中的每一个节点都可以做集群。而集群并不一定就是分布式的。节点:集群中的一个服务器。
在分布式系统中,各个服务可能处于不同主机,但是服务之间不可避免的需要相互调用,我们称之为远程调用。SpringCloud中使用HTTP+JSON的方式完成远程调用
分布式系统中,A服务需要调用B服务,B服务在多台机器中都存在,A调用任意一个服务器均可完成功能。
为了使每一个服务器都不要太忙或者太闲,我们可以负载均衡的调用每一个服务器,提升网站的健壮性。
常见的负载均衡算法:
轮询:为第一个请求选择健康池中的第一个后端服务器,然后按顺序往后一次选择,直到最后一个,然后循环。
最小连接:优先选择连接数最少,也就是压力最小的后端服务器,在会话较长的情况下可以考虑采取这种方式。
散列:根据请求源的IP的散列(hash)来选择要转发的服务器。这种方式可以一定程度上保证特定用户能连接到相同的服务器,如果你的应用需要处理状态而要求用户能连接到和之前相同的服务器,可以考虑采取这种方式。
A服务调用B服务,A服务并不知道B服务当前在那几台服务器有,那些正常的,那些服务已经下线。解决这个问题可以引入注册中心;
如果某些服务下线,我们其他人可以实时的感知到其他服务的状态。从而避免调用不可用的服务。
每个服务最终都有大量的配置,并且每个服务都可能部署在多台机器上。我们经常需要变更配置,我们可以让每个服务在配置中心获取自己的配置。配置中心用来集中管理微服务的配置信息。
在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,有可能会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。
1)服务熔断:设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启保护机制,后来的请求不再去调用这个服务。本地直接返回默认的数据。
2)服务降级:在运维期间,当系统处于高峰期,系统资源紧张。我们可以让非核心业务降级运行。降级:某些服务不处理,或者简单处理【抛异常、返回null、调用MOCK数据、调用Fallback处理逻辑】。
在微服务架构中,API Gateway作为整体架构的重要组件,它抽象了微服务中都需要的公共功能,同时提供了客户端负载均衡、服务自动熔断、灰度发布,统一认证、限流控制、日志统计等丰富的功能,帮助我们解决很多API管理难题。
步骤1:安装虚拟机,步骤2:安装docker,步骤3:在docker中安装mysql,步骤4:在docker中安装redis
docker一些常用命令如下:
以下命令如果不是root用户,执行时命令前加sudo;或者使用su root方式切换到root用户
1、systemctl enable docker:设置docker开机自启
2、docker images:获取docker本地镜像
3、docker pull mysql:5.7 :从docker官网下载5.7版本的mysql镜像,如果没有指定版本默认下载最新版本的4、创建实例并启动:
docker run -p 3306:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
参数说明:
docker run -p 3306:3306 --name mysql 启动容器,将容器的3306端口映射到主机的3306端口,为当前启动容器起名mysql
-v /mydata/mysql/conf:/etc/mysql 将容器配置文件夹/etc/mysql挂载到主机/mydata/mysql/conf
-v /mydata/mysql/log:/var/log/mysql 将容器日志文件夹/var/log/mysql挂载到主机/mydata/mysql/log
-v /mydata/mysql/data:/var/lib/mysql 将容器配置文件夹/var/lib/mysql挂载到主机/mydata/mysql/data
-e MYSQL_ROOT_PASSWORD=root 初始化root用户的密码为root
-d mysql:5.7 后台启动的方式,启动mysql版本为5.7的这个镜像的容器
注意:文件挂载的作用是当我们需要修改容器里面的一些配置时可以不用进入到容器的内部指定目录而只需要在linux外部目录修改即可。5、docker ps:查看docker正在运行中的容器
6、docker exec -it mysql /bin/bash:进入mysql容器的内部
7、docker restart mysql:重启mysql
8、docker exec -it redis redis-cli:进入redis客户端(进入后可执行set a b,get a等操作指令)
步骤一:下载jdk配置环境变量
步骤二:下载maven,配置环境变量,配置阿里云镜像,配置使用jdk1.8编译项目
步骤三:在idea中配置jdk和maven
步骤四:在idea中下载简化开发的插件,lombok(简化开发通过注解不需要写get、set等方法),MyBatisX(可以从mapper文件定位到xml文件),gitee(在idea对git进行配置后可以在页面提交代码)
步骤五:下载安装前端开发软件VS code,在vs code安装常用组件
步骤六:安装配置git
步骤七:配置ssh免密登录
1)创建各个微服务模块
2)修改外层服务pom文件,使其聚合其他微服务
3)修改.gitignore文件,提交代码时对一些文件进行忽略
、
使用注解开启远程调用,并告诉springcloud当前feign接口处于那个包下:
编写feign接口注明需要调用那个远程服务的那个路径下接口:
4.4.1、核心概念
命名空间:用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的Group或Data ID的配置。Namespace的常用场景之一是不同环境的配置的分区隔离,例如开发测试环境和生成环境的资源(如配置、服务)隔离等。Nacos作为配置中心时,默认的配置文件添加到命名空间为public里面的,
(1)为了区分开发测试环境与生成生产环境不同的配置,可以新建不同的命名空间,然后在新的命名空间添加配置信息。如下所示:
(2)每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的配置,使用配置分组区分环境dev、test、prod(配置分组的概念见下面内容)
配置集:一组相关或者不相关的配置的集合成为配置集,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。
配置集ID:类似配置文件名,如下
配置分组:默认所有的配置集都属于:DEFAULT_GROUP;
如果一个命名空间下有多个配置分组信息,也可以指定使用那个分组的配置,如下:
加载多配置集:将配置信息拆分为多个注册到Nacos注册中心,最后对多个文件加载
网关作为流量的入口,常用功能包括路由转发、权限校验、限流控制等。而springcloud gateway作为SpringCloud官方推出的第二代网关框架,取代了zuul网关。
第一步创建网关服务,并添加依赖:
第二步:在网关服务中,添加Nacos依赖,配置文件中配置Nacos地址,开启服务注册与发现。以便将自己注册到Nacos的网关,并发现其他服务进行调用。
第三步:新建application.yml文件,在里面添加网关的配置,进行路由转发
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
同源策略:是指协议、域名、端口都要相同,其中一个不同都会产生跨域;
解决跨域
(一)使用nginx部署为同一域 (使用较少且麻烦)
(二)配置当前请求允许跨域
在网关服务中配置允许跨域代码如下:
1、注册登录阿里云网站,个人中心进行实名认证
2、开通对象存储,进入管理控制台的对象存储OSS页面
3、创建bucket
4、阿里云对象存储普通上传方式:用户请求发送的图片通过网关传到商品服务,商品服务通过java代码将要存储的图片或大文件存储到oss,并返回oss上的存储地址存到数据库,下次要获取图片先从数据库获取地址再到oss拿返回到前端展示。
但是这种方式效果并不好,当有大量请求时会给商品服务带来瓶颈。
5、阿里云对象存储-服务端签名后直传:为了防止前端使用oss的账号密码直接传照片文件给oss存储暴漏了密码等信息,用户的请求会先到达应用服务器(商品服务)拿到令牌,之后用户直接上传数据到oss,携带的数据令牌中包含账号密码等信息。
6、阿里云提供的oss实现文件存储方法
第一步在商品服务中引入依赖:
第二步代码实现:
7、SpringCloud提供的oss文件存储实现方法
第一步在商品服务引入依赖:
第二步在商品服务的application.yml中进行配置:配置参数根据实际情况在注册的阿里云网站查找个人的信息
第三步代码实现:
8、oss获取服务端签名实现文件存储
第一步:引入对象存储相关依赖
第二部配置oss相关信息:
第三步编写代码获取签名:
第四步:前端调用上述接口获取签名信息,再向oss发送请求实现文件存储
第一步实体类上加校验注解:全部可用的注解可参照javax.validation,dao包里面列举的
第二步在接口接收参数的地方使用@Valid注解通知该参数是需要校验的,上面entity的校验规则才能起到效果
第三步添加自定义错误信息提示:
第四步可以通过BindingResult result获取校验异常的字段和异常原因进行返回处理
建统一异常处理包,编写统一异常处理类:
异常码统一定义:
第一步使用group给校验注解标注什么情况下需要校验,并创建接口UpdataGroup和AddGroup内容为空即可
第二步将controller中@valid注解修改为@validated并标明使用新增分组的校验规则:
注意:默认没有使用校验分组的注解如@NotBlank,在分组校验情况下不生效;只有在使用@valid不用分组的情况下才生效。
第一步添加依赖:
第二步编写自定义注解接口:
第三步编写规则校验类:
第四步使用自定义注解:
第一步安装ElasticSearch镜像
第二步创建实例
第三步:安装Kibana,方便操作ElasticSearch的可视化界面
第四步:启动完成后访问192.168.56.10:5601可以进入Kibana客户端
使用postman验证如下:
- PUT customer/external/1
- {
- "name":"John Doe"
- }
postman测试截图如下:
返回结果如下:
ES中未直接提供删除类型(mysql中的叫法是表)的操作
ES支持两种基本方式检索:
1、查看mapping:
2、创建mapping:
3、修改mapping添加新的映射字段:
4、更新映射
对于已经存在的映射字段,我们不能更新。更新必须创建新的索引进行数据迁移实现更新映射的目的。
5、数据迁移
创建new_ttwitter的在正确映射。然后使用如下方式进行数据迁移。
例如:
第一步:创建新的索引
第二步:数据迁移
使用ik分词器进行智能分词:
第一步:nginx的设置
第二步:修改配置文件,完成后重启es
第三步:测试
实现方法:
第一步:引入依赖
第二步:编写配置文件
第三步:测试
未指定nested数据类型会将数据进行扁平化处理:
为避免出现扁平化处理的现象,我们将数据设置为nested类型:
第一步:编写上架商品属性类
第二步:编写Controller调用上架方法
第三步:在service层编写上架方法的实现类
压力测试考察当前软硬件环境下系统所能承受的最大负荷并帮助找出系统瓶颈所在。压测都是为了系统在线上的处理能力和稳定性维持在一个标准范围内,做到心中有数。
使用压力测试,我们有希望找到很多种用其他方法更难发现的错误。有两种错误类型是:内存泄漏,并发与同步。
有效的压力测试系统将应用以下这些关键条件:重复、并发、量级、随机变化。
https://jmeter.apache.org/download_jmeter.cgi
下载对应压缩包,解压运行bin目录下的jmeter.bat即可启动jmeter
1、添加线程组
2、添加http请求:
3、添加查看结果树:
3、添加汇总报告:
3、添加聚合报告:
4、启动请求发送:
5、查看压测结果:
1、JVM内存模型
2、堆
jdk的两个小工具jconsole\jvisualvm(升级版的jconsole);通过命令行启动,可监控本地和远程应用。远程应用需要配置
1、jvisualvm能干什么
2、安装插件方便查看gc
前端静态资源获取优化、业务优化、sql优化、增加索引、使用缓存、降低日志级别等待;
1、Nginx动静分离:
2、线上应用内存崩溃宕机:调大堆内存空间
3、优化业务,避免多次查询数据库,最好一次查询出来再通过业务进行过滤处理
为了系统性能的提升,我们一般都会将部分数据放入缓存中,加速访问。而db承担数据落盘工作。
那些数据适合放入缓存?
举例:电商类应用、商品分类,商品列表等适合缓存并加一个失效时间(根据数据更新频率来定),后台如果发布一个商品,买家需要5分钟才能看到新的商品一般还是可以接受的。
第一步:虚拟机安装redis
第二步:引入redis依赖
第三步:配置redis相关参数信息
第四步:使用springboot自动配置好的StringRedisTemplate来操作redis
使用redis改造三级分类业务:
缓存穿透:
缓存击穿:
缓存雪崩:
为解决以上问题,对缓存模块代码进行改造:
上述代码中的本地锁,只能锁住当前进程,如果我们的商品服务是部署在多台服务器上运行的,那么每台服务器上都会至少有一个线程对数据库进行操作,所有我们需要使用分布式锁:
为解决阶段二的问题,修改代码如下,让占锁和设置过期时间同时发生,成为原子的命令:
设置过期时间虽然可以解决阶段二的问题,但是但线程1的锁过期释放了,线程2进来了,此时线程1执行删除锁的操作,可以误删线程2的锁;
为解决阶段三的问题,使用uuid,只有当前锁是自己的锁才删除
使用lua脚本保证判断+删除的原子性:
防止业务还未执行完锁已经过期了,我们需要不断地给锁续期,还可以更简单的处理:直接将锁的过期时间设置久一点,等业务执行完再自己去删除锁即可。
整合redisson作为分布式锁等功能框架
第一步:引入依赖
第二步:配置redisson
第三步:redisson的使用测试
Redission的优势:
1、使用看门狗,当业务还未执行完,锁的过期时间已经到了,会自动续期锁的过期时间,保证业务执行完成后再释放锁;
2、当线程1业务执行完但是还未释放锁出现阻塞等问题无法释放锁,redisson会帮我们释放锁,让其他线程继续执行,避免死锁。
只有读锁时和没有加锁差不多,多个服务可以同时读取数据;但是当有写锁没释放时,写锁就一直等待,只有等当前写锁写完数据,其他服务才可以读数据或者写数据。
读写锁保证了一定能读取到最新数据,修改期间,写锁是一个排他锁(互斥锁),读锁是一个共享锁。
读+读:相当于无锁,并发读,只会在redis中记录好,所有当前的读锁。他们都会同时加锁成功
写+读:等待写锁释放
写+写:阻塞方式
读+写:先有读锁,写也需要等待
使用闭锁实现只有等五个班的人都走了才执行锁大门的操作:
在redis中设置值(park,3),执行park方法会获取一个信号量key=park对应值减一,执行go方法会释放一个信号量key=park对应值加一。
park.acquire()方法:获取不到信号量的时候会等待有信号量被释放再抢占。
park.tryAcquire()方法:获取不到信号量就不会等待,不会处理。
信号量可以用作分布式限流。
使用Redisson加锁实现缓存代码修改如下:
对数据库的数据进行缓存总结起来有两个方法如下:双写模式、失效模式
双写模式出现的问题:当有两个线程都需要修改数据,且线程1先修改线程2后修改,线程2的数据为最新数据,但是由于一些原因线程1写入缓存慢了一点,导致数据库存的是线程2的最新数据,但是缓存中存的是线程1写入的旧数据。
解决方案1:加锁,等线程1完成了数据库和缓存的修改操作释放了锁线程2再进行数据库和缓存的数据修改操作。
解决方案2:如果允许暂时的数据不一致问题,可以对缓存中的数据加过期时间,等缓存中的数据过期了,下次查询的时候会从数据库拿到最新数据更新到缓存中。
失效模式也会引发问题:如下图所示,最终导致数据库中存的是db2的新数据,但是缓存中存的是db1的旧数据。
解决方法1:加锁
解决方法2:对于经常修改的数据,无需缓存,直接读取数据库
总结:
详解使用canal的binlog实现数据一致性:
2、基础概念
第一步:引入依赖
spring-boot-starter-cache、spring-boot-starter-data-redis
第二步:写配置
(1)自动配置了哪些
CacheAuroConfiguration会导入RedisCacheConfiguration;
自动配置好了缓存管理器RedisCacheManager
(2)我们需要配置使用redis作为缓存
第三步:测试使用缓存
@Cacheable:触发将数据保存到缓存的操作
@CacheEvict:触发将数据从缓存删除的操作
@CachePut:不影响方法执行更新缓存
@Cacheing:组合以上多个操作
@CacheConfig:在类级别共享缓存的相同配置
(1)开启缓存功能:@EnableCaching
(2)使用注解完成缓存操作
@Cacheable默认行为:
1)如果缓存中存在,方法不用调用
2)key默认自动生成:缓存的名字:;Simplekey{}(自主生成的key值)
3)缓存的value值,默认使用jdb序列化机制,将序列化后的数据存到redis
4)默认ttl时间为-1,永不过期
自定义:
1)指定生成的缓存使用的key:key属性指定
2)指定缓存数据的存活时间;在配置文件中修改ttl
3)将数据保存为json格式
编写自定义缓存配置类:
@CacheEvict:触发将数据从缓存删除的操作
使用@CacheEvict修改数据库中级联菜单数据的时候删除缓存数据
@Cacheing:组合以上多个操作
总结:
常规数据(读多写少,即时性,一致性要求不高的数据);完全可以使用Spring-Cache;写模式(只要缓存的数据有过期时间就足够了)
特殊数据:特殊设计
1、初始线程的四种方式
区别:
1、2不能得到返回值,3可以获取返回值
1、2、3都不能控制资源,4可以控制资源、性能稳定
2、线程池详解
创建线程池的两种方式:
- 1、
- public static ExcutorService service = Excutor.newFixedThreadPool(10);
-
- service.execute(new Runable01());
- 2、
- /**
- *七大参数:
- *corePoolSize:核心线程数;线程池创建好以后就准备就绪的线程数量,就等待来接收异步任务去执行,初始化
- *线程池的时候指定核心线程数
- *maxnumPoolSize;最大线程数量;控制资源
- *keepAliveTime:存活时间;如果当前的线程数量大于核心线程数量,释放空闲的线程,相当于业务少了在存活时*间到了的时候消减外包人员去除非核心人员类似
- *unit:时间单位
- *BlockingQueue<Runnable> workQueue:阻塞队列;如果任务有很多,没有足够的空余线程去执行,就会将目前*多余的任务放在队列里面,等有线程空闲了,就回去队列里面取出新的任务继续执行
- *threadFactory():线程的创建工厂
- *handler:拒绝策略;如果队列满了,按照指定的拒绝策略拒绝执行任务
- *
- */
- ThreadPoolExecutor executor = new ThreadPoolExecutor();
线程池的运行流程:
3、常见的四种线程池
4、开发中为什么使用线程池
1、创建异步对象
2、计算完成时回调方法
3、handle方法
4、线程串行化方法
5、两任务组合-都要完成
6、两个任务组合-一个完成
7、多任务组合
需求:有5个任务,3、4、5需要等获取1的执行结果再执行,2没有限制,等五个都执行完成再将结果返回;
第一步编写配置类创建线程池
第二步编写线程池关联表,使线程池的参数可配置化
第三步在配置类中对线程池参数进行配置
第四步使用配置文件中的参数传入创建的线程池
第五步编写异步线程实现需求
使用MD5加密,用户根据加密后的密文很容易通过MD5解密工具进行破解,不安全,一般不推荐使用
盐值加密:要加密的数据拼接一个当前系统时间或其他数据再进行加密,拼接的数据存入数据库,进行数据验证的时候,将传入的数据再次进行拼接加密对比之前的看是否相同,缺点就是需要多设一个字段存需要拼接的数据
最终选择:密码加密器BCrypt,相同的明文密码每次加密后的密文都会不同,用户不容易破解,验证时只需传入明文密码和加密后的密文就可以验证密码是否正确,与盐值加密相比无需多设字段
1、进入微博开放平台
2、登录微博,进入微连接,选择网站接入,创建新应用,配置授权信息
1、登录流程梳理
2、导入依赖
3、编写代码实现微博登录流程第七步,处理微博登录成功的回调
1、session共享问题-session原理
2、session共享问题-分布式下session共享问题
3、session共享问题解决-session复制
4、session共享问题解决-客户端存储
4、session共享问题解决-hash一致性
5、session共享问题解决-统一存储
6、session共享问题解决-不同服务,子域session共享
第一步:导入依赖
第二步:配置类中编写session的配置信息