SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

微服务:
- 拆分(单体项目拆分为许多个独立的项目,独立开发和部署。形成服务集群)
- (异步通信可以大大的提高并发,秒杀、等大的场景下可以去利用。)


自动化部署
持续集成

单体架构
单体架构:将业务的所有功能集中在一个项目中假发,打成一个包部署。
优点:
架构简单
部署成本低(负载均衡的集群)
缺点:
耦合度高
分布式架构
分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
优点:
降低服务耦合
有利于服务升级拓展

分布式架构要考虑的问题:

微服务:
微服务就是一种经过良好框架设计的分布式架构方案,微服务架构特征:
- 单一职责:微服务拆分力度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发。
- 面向服务: 微服务对外暴露业务接口
- 自治: 团队独立、技术独立、数据独立、部署独立
- 隔离性强: 服务调用做好隔离、容错、降级,避免出现级联问题

实现了cloud的接口规范,


SpringCloud
- SpringCloud是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud

springboot和springcloud版本兼容性:

服务拆分注意事项

微服务远程调用

1)注册RestTemplate


提供者与消费者
一个服务既可以是提供者,也可以是消费者。


Eureka注册中心
作用:
服务消费者和服务提供者统称为Eureka客户端。
1)注册服务信息
2)拉去服务的信息
3)负载均衡
4)远程调用
(服务每隔30s都会向Eureka发送一次心跳,确认自己的状态)心跳续约,每30s/次


搭建EurekaServer
搭建EurekaServer服务步骤如下:
1.创建项目,引入spring-cloud-starter-eureka-server的依赖
2.编写启动类,添加@EnableEurekaServer注解
3.添加application.yml文件,配置如下信息:
server:
port: 9090
# 单机版
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
lease-expiration-duration-in-seconds: 15
lease-renewal-interval-in-seconds: 5
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
service-url: #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: #地址信息http://${eureka.instance.hostname}:${server.port}/eureka/



Ribbon负载均衡
负载均衡流程

拦截客户端发起的http请求(客户端http请求拦截器)接口
实现类 loadbalan
获取请求uri,根据uri获取host(主机名/服务名),
拉取服务列表

getserver()负载均衡,(取出一个)
内部调用chooseserver(),[选择服务]
rule.choose() 规则
ctrl+h查看实现类
randomrule (随机)
roundrobinrule(轮询调度--轮询负载均衡)
默认:ZoneAvoidanceRule



负载均衡策略:
通过定义IRule实现跨越修改负载均衡规则,有两种方式:
- 代码方式: 在order-service中的orderApplication类中,定义一个新的irule:(作用于全局,即无论调用哪一个服务都是随机的。)
- 配置文件方式: 在 order-service的application.yml文件中,添加新的配置也可以修改规则。

饥饿加载
ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon:
eager-load:
enable: true #开启饥饿加载
clients: userserver #指定对userservice这个服务饥饿加载
【拉去服务日志,即所谓懒加载。将会缓存在内存中。】


Nacos注册中心
功能: 服务的注册和发现;分布式配置
https://nacos.io/zh-cn/docs/quick-start.html
启动命令:
startup.cmd -m standalone (-m: 配置; standalone : 单机启动)
依赖,组测中心地址
服务跨集群调用问题
服务调用是尽可能的选择本地集群服务,跨集群调用延迟较高
本地集群不可访问时,再去访问其他集群。
(防止跨集群调用)
无集群情况下:

配置集群信息:



本地+随机(优先选择本地集群,然后根据随机方式进行负载和均衡)

根据权重负载均衡
实际部署中会出现这样的场景:
- 服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。
Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。
1.在Nacos控制台可以设置实例的权重值,首先选中实力后面的编辑按钮
2.将权重设置为0.1(一般0 ~ 1)

如果权重为0,服务将不会被访问。
(版本升级)

环境隔离-namespace
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离

服务划分,实例划分是基于业务/地域做得。
namespace基于环境做的隔离
新建一个命名空间信息,生成一个id(uuid)


添加namespace:

最终效果:

分组之后会出现无法访问(环境隔离)


注册中心细节分析

差别在于服务提供者的健康检测(临时实例和非临时实例,默认为true,所有的均为临时实例)

nacos不会把非临时实力从列表中剔除,而仅仅是标记为不健康了。等待此服务恢复健康


临时实例和非临时实例
服务注册到nacos时,可以选择注册为临时或非临时实例,通过下面的配置来设置。

非临时实例挂机前后对比
【挂机前】

【挂机后】

(除非手动提出,否则服务将一直存在)
(临时服务挂机后将会失去监控,--从列表中消失)

ap:强调服务的可用性;cp: 强调数据可靠性和一致性
目录:


nacos配置调用
统一配置管理
- 配置更新热更新
(服务配置和本地配置结合使用,可以直接在配置服务上更新配置信息,)


dateid:配置文件名称,不建议使用application.yml,会产生冲突,命名规则: [服务名称]-[运行环境].[后缀名(yml全称yaml)示例: userserver-dev.yaml
配置内容:(需要做热更新的配置,不需要变更的配置可以不使用,一般开关类的配置,模板类型的,)
logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss
【示例图】

配置获取步骤如下:

去哪读取,读取谁?

spring提供bootstrap.yml文件

1.引入配置
<!--springcloudalibaba nacos客户端配置文件依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
2.在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml:

配置自动刷新
nacos中的配置文件变更后,微服务无需重启就可以自动感知。不过需要通过下面两种配置实现:
- 方式一: 在@Value注入的变量所在类上添加注解@RefreshScope

方式二:使用@ConfigurationProperties注解
(完成配置的自动加载)

推荐使用第二种

多环境配置共享
(开发、生产、测试环境一直,)
微服务启动时会从nacos读取多个配置文件:
- [spring.application.name]-[spring.profiles.active].yaml,例如: userservice-dev.yaml
- [spring.application.name].yaml,例如: userservice.yaml,
无论profile(环境)如何变化,[spring.application.name].yaml这个文件一定会加载,因此多环境共享可以写入这个文件。
自动激活环境的简单操作,避免更改代码
(选中项目,右键,选择edit configration,)

运行日志查看:

当userservice和本地代码都有的时候,属性以userservice.yaml为准。
配置文件优先级:
[spring.application.name]-[spring.profiles.active].yaml > [spring.application.name].yaml > 本地代码环境
多种配置的优先级:
- 服务名-profile.yaml > 服务名.yaml > 本地配置


nacos集群搭建:

(默认集群启动)

Feign远程调用(http客户端)
feign代替RestTemplate
RestTemplate方式调用存在的问题:

存在下面的问题:
- 代码可读性差,编程体验不统一
- 参数复杂URL难以维护
feign是一个声明式的http客户端,官网地址: https://github.com/OpenFeign/feign
其作用就是帮助优雅的实现http请求发送,
1.引入依赖:
<!--feign 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2.启动类添加注解开启feign功能
@EnableFeignClients

3.编写feign客户端

4.feign代替restTeplate


自定义配置

避免网络波动导致的访问结果失败

将会打印日志:
2022-04-03 21:42:30.946 WARN 28168 --- [nio-9081-exec-7] c.alibaba.cloud.nacos.ribbon.NacosRule : A cross-cluster call occurs,name = orderserver, clusterName = HN, instance = [Instance{instanceId='192.168.0.103#9091#DEFAULT#DEFAULT_GROUP@@orderserver', ip='192.168.0.103', port=9091, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@orderserver', metadata={preserved.register.source=SPRING_CLOUD}}]


Feign使用优化
Feign底层客户端实现:
- URLConnection: 默认实现,不支持连接池(性能不太好)
- Apache HttpClient: 支持连接池
- OKHttp: 支持连接池
因此优化Feign的性能主要包括:
使用连接池替代默认的URLConnection
日志级别,最好用basic或none
Feign使用优化-连接池配置
Feign添加HttpClient的支持
引入依赖:
<!--httpclient的依赖--> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
配置连接池
feign: client: config: # default: 全局配置,orderserver(服务名): 局域配置 default: # 日志级别 full BASIC 就是基本的请求和相应信息 loggerLevel: BASIC httpclient: # 开启feign对httpclient的支持 enabled: true # 最大连接数 max-connections: 200 # 每个路径的最大连接数 feign.httpclient.max-connections-per-route: 50

最佳实践
- 方式一(继承): 给消费者的feignclient和提供者的controller定义统一的父接口作为标准

不推荐该方式,因为会造成紧耦合,而且这种继承方案对于springmvc不起作用
方式二(抽取): 将feignclient抽取为独立模块,并且把接口有关的pojo,默认的feign配置都放到这个模块中,提供给所有消费者使用。

//如果feignclient不在SpringBootApplication扫描范围内,这些FeignClients无法使用有两种解决方案 //第一 指定所在包 //@EnableFeignClients(basePackageClasses = "com.estelle.feign") //方式二 指定 字节码 //@EnableFeignClients(clients = {OrderClient.class})

区别在于 扫描包方案属于全拿来,批量搞;字节码方案属于精准打击(更推荐第二种方案,不用的加载进来浪费)

统一网关Gateway服务网关
为什么需要网关
网关功能:
- 身份认证和权限校验
- 服务路由、负载均衡
- 请求限流(限定流量)

springcloud网关的实现包括两种:
- gateway(新版)
- zuul(早期版本)
zuul是基于servlet的实现,属于阻塞式编程。而springcloudgateway则是基于spring5中提供的webflux,属于响应式编程的实现,具备更好的性能。

Gateway快速入门
搭建网关服务
1.创建model,引入gateway依赖和nacos的服务发现依赖:
<!--nacos 服务发现依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--网关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>

2.编写路由配置及nacos地址

spring: application: # 服务名称 name: gateway # cloud部署 cloud: gateway: lowerCaseServiceId: true # 网关路由配置 routes: # 路由id,自定义,需要唯一 - id: user-service # 路由目标地址 lb: loadbalance,后面跟服务名 uri: lb://userserver # 路由规则: 判断请求是否符合路由规则的条件 predicates: # 按照路径匹配,表示/user开头的请求都会路由到这里 - Path=/user/** - RemoteAddr= 192.168.0.103, 127.0.0.1 - id: order-service uri: lb://orderserver predicates: - Path=/order/** - RemoteAddr= 192.168.0.103, 127.0.0.1 nacos: discovery: server-addr: 127.0.0.1:8848

路由断言工厂 Route Predicate Fattory



读取用户配置的断言规则,解析成对应的判断条件

过滤器工厂
gatewayfilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
请求头加上信息等等,


通过名字做简单推测,决定使用哪个过滤器

默认过滤器配置方法:


全局过滤器 GlobalFilter
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与gatewayFilter的作用一样。
区别在于gatewayfilter通过配置定义,处理逻辑是固定的,而globalfilter的逻辑需要自己携带写代码实现。
定义方式就是实现globalfilter接口。


过滤器的执行顺序
请求进入网关会碰到三类过滤器: 当前路由的过滤器、defaultfilter、globalfilter
请求路由后,会将当前路由过滤器和defaultfilter、globalfilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器

过滤器执行顺序:
- 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
- globalfilter通过实现ordered接口或者添加@order注解来指定order值,由开发人员自己指定
- 路由过滤器和defaultfilter的order由spring指定,默认按照声明顺序从1递增。
- 当过滤器的order值一样时,会按照defaultfilter > 路由过滤器 > globalfilter的顺序执行


跨域问题处理
跨域:域名不一致就是跨域,主要包括:
- 域名不同: www.taobao.com和www.taobao.org和www.jd.com和miaosha.jd.com
- 域名相同,端口不同: localhost:8080和localhost:8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题。
解决方案: CORS
浏览器问下服务器,是否允许跨域

CROS里面有一次询问(),是options,默认是被拦截的。性能上有一定损耗(一定时间内将不会再发起询问,有效期的作用)
/** 拦截一切请求
