SpringCloud学习笔记(一)
- 一、认识SpringCloud
- 1.1 简介
- 1.2 服务与拆分与远程调用
- 1.3 微服务的远程调用
- 二、微服务的几大组件
- 2.1 EureKa注册中心
- 2.1.1 Eureka介绍:
- 2.1.2 Eureka实践:
- 2.2 Ribbon负载均衡
- 2.2.1 负载均衡流程
- 2.2.2 负载均衡策略
- 2.2.3 饥饿加载
- 3.Nacos注册中心
- 认识Nacos
- 安装Nacos
- Nacos快速入门
- 服务注册到Nacos
- nacos服务分级存储模型
- nacos服务实例的权重设置
- nacos环境隔离
- Nacos配置管理
- 统一配置管理
- 获取配置
- 配置自动刷新
- 多环境配置共享
- nacos集群搭建
- Nacos和ErueKa注册中心的比较
- 4 Feign远程调用
- 快速入门
- 自定义Feign配置
- Feign性能优化
- 日志优化
- 连接池配置
- Feign最佳实践
- 实践
- 5 Gateway统一网关
- 搭建网关服务
- 路由断言工厂Route Predicate Factory
- 路由过滤器 GatewayFilter
- 全局过滤器 GlobalFilter
- 过滤器执行顺序
- 跨域问题处理
- 5 Sentinel
一、认识SpringCloud
1.1 简介
SpringCloud是一个微服务框架,集成阞各种微服务功能组件,并基于SpringBoot实现了几大组件的自动装配,从而提供了良好的开箱即用。
1.2 服务与拆分与远程调用
从之前的单体项目,根据功能和业务拆分成过个多个微服务。
服务拆分要注意几个事项
1.不同微服务,不可重复开发相同业务2.微服务数据独立,不要访问其他微服务的数据库3.微服务可以将自己的业务暴露为接口,供其他微服务调用
1.3 微服务的远程调用
服务调用关系:
服务提供者:暴露接口给其他微服务调用
服务消费者:调用其他微服务提供的接口
提供者与消费者的角色是相对的,提供者可以是消费者,消费者也可以是提供者
二、微服务的几大组件
2.1 EureKa注册中心
2.1.1 Eureka介绍:
EureKa的作用
1.记录微服务的信息,并提供给eureka客户端(微服务)要拉取的提供者信息。
2.结合负载均衡,给消费者提供一个合适的服务者
3.感知服务者的状态,检查监控状态
EureKa角色关系
在EureKa架构中,微服务角色有两类:
- EureKaServer:服务端,注册中心
- 记录服务信息
- 心跳监控
- EureKaClient:客户端
- Provider:服务提供者,例如user-service(一个微服务)
- 注册自己的信息到EureKaServer
- 每隔30秒向EureKaServer发送心跳
- consumer:服务消费者,例如order-service(一个微服务)
- 根据服务名称从EureKaServer拉取服务列表
- 基于服务列表做负载均衡,选中一个微服务后发起远程调用
- Provider:服务提供者,例如user-service(一个微服务)
2.1.2 Eureka实践:
一、搭建EureKaServer
1.创建独立的EureKaServer项目,引入spring-cloud-start-netflix-eureka-server依赖
2.编写启动类,添加@EnableEureKaServer注解
3.添加application.yml文件,编写配置:
二、服务注册
步骤如下:
1.在user-service项目中引入spring-cloud-start-netflix-eureka-client的依赖
2.在application.yml文件,编写配置
三、服务发现(拉取)
服务器拉取是基于服务器名称获取服务器列表
然后在对服务器列表做负载均衡
步骤:
1.修改OrderService的代码,修改访问路径,用服务名代替ip、端口
2.在order-server项目的启动类OrderApplication中的RestTemplate添加负载均衡注解@LoadBalanced
2.2 Ribbon负载均衡
对于之前的配置,我们只做了EureKa的服务器注册和客户端注册,之后就自动进行了拉取和负载均衡等操作,那么这一些列动作是谁完成的呢?是怎么完成呢?流程是什么呢?接下来我们一起了解SpringCloud核心组件之一Ribbon
2.2.1 负载均衡流程
1.当微服务发起请求时,会被LoadBalancerInterceptor
负载均衡拦截器拦截,并获取请求名称
2.把获取到的请求名称交给RibbonLoadBanlancerClient
,接着这个RibbonLoadBanlancerClient
会把获取的url中的服务ID交给DynamicServerListLoadBalancer
3.EureKa从DynamicServerListLoadBalancer
中拉取userservice,然后得到多个服务。
4.DynamicServerListLoadBalancer
会使用IRule做负载均衡(轮询),选择一个服务器返回给LoadBalancerInterceptor
5.接着 RibbonLoadBanlancerClient
修改url,发起向服务器请求
2.2.2 负载均衡策略
Ribbon的负载均衡规则是是一个叫IRule的接口来定义的,每一个子接口都是有着不同的规则
负载均衡常见策略
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobbinRule | 简单轮询列表来选择服务器。它是Ribbon默认的负载均衡规则 |
AvailableilityFilteringRule | 对一下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间会几何级的增加 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置AvailablityFilteringRule规则的客户端也会将其忽略。并发连接数的上线。可以由客户端的..ActiveConnetctionsLimit属性进行配置 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重会印象服务器的选择 |
ZoneAviodanceRule | 以区域(Zone)可用的服务器为基础的选择,使用Zone对服务器进行分类,这个Zone可以理解为一个机房,一个机架等。而后再对Zone内多个服务做轮询 |
BestAvailableRule | 忽略哪些短路的服务器,并进选择并发数较低的服务器 |
RandomRule | 随机选择一个可用的服务器 |
RetryRule | 重试机制的选择逻辑 |
调整负载均衡策略
通过定义IRule实现可以修改负载均衡规则,有两种:
(1)代码方式:在order-servce中的OrderApplication类中,定义一个新的IRule
这种是针对于全体的。即order-servce访问任何服务都是随机的
上面这个轮询机制就被修改成了随机选择了。
(2)配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
这种是针对一个的,指定哪一个微服务的访问方式
2.2.3 饥饿加载
Ribbon默认是采用的懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问时的耗时,通过下面配置开启饥饿加载:
总结:
3.Nacos注册中心
认识Nacos
Nacos是阿里巴巴的产品。现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内比较受欢迎
安装Nacos
(1) Windows安装
下载
在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码:
GitHub主页:https://github.com/alibaba/nacos
GitHub的Release下载页:https://github.com/alibaba/nacos/releases
如图:
解压
下载好后进行解压(在一个没有中文的目录下解压)
解压后的目录如下
启动
单机启动
startup.cmd -m standalone
启动后如图
访问
点击链接,账号nacos
密码nacos
Nacos快速入门
服务注册到Nacos
1.在cloud-demo父工程中添加spring-cloud-alibaba的管理依赖:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.5.RELEASE</version><type>pom</type><scope>import</scope></dependency>
2.注释掉order-service和user-service中原有的eureka依赖
3.添加nacos的客户端依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
4.修改user-service&order-service的application.yml文件,注释eurka地址,添加nacos地址:
5.启动(nacos服务器和微服务)并测试
nacos服务分级存储模型
可以理解为将不同的实例(微服务)化分为一个区域(集群)
服务跨集群调用问题
服务调用尽可能选择本地集群的服务,跨集群调用延迟较高
本地集群不可访问时,再去访问其他集群
服务集群属性
1.修改application.yml,设置集群为HZ,添加如下内容
2.然后在order-service中设置负载均衡的IRule为NacosRule,这个规则优先会寻找与自己同集群的服务:
NacosRule
一般会优先访问本地集群服务
然后在nacos中看到:
总结:
nacos服务实例的权重设置
**实例的权重越大,就越会被优先访问到**
修改权重:
总结:
nacos环境隔离
1.namespace
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离
1.nacos的命名空间可以在nacos控制台上面进行设置和修改
2.修改order-service的application.yml,添加namespace:
3.回到nacos控制中心,我们可以看到orderservice的命名空间已经变化了
总结:
Nacos配置管理
统一配置管理
在一些大型的项目,一个项目可能有很多微服务,如果我们需要修改配置微服务的话,可能需要逐个修改,并再重启。可想而知,这样的操作是繁琐的,不利于生产的。
那么接下来我们可以进行统一配置
步骤:
1.找到配置管理,点击配置列表点击加号,填写相应的信息
获取配置
读取配置流程
实操:
1.引入nacos的配置管理客户端依赖
<!--nacos的配置管理依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>
2.在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是一个引导文件,优先级高于application.yml:
spring:application:name: userserviceprofiles:active: dev # 环境cloud:nacos:server-addr: localhost:8848 # nacos地址discovery:cluster-name: HZconfig:file-extension: yaml # 文件后缀名
总结:
配置自动刷新
Nacos中的配置文件变更后,微服务无需重启就可以感知。不需要通过下面两种配置实现:
-
方式一: 在@Value注入的变量所在类添加注解@RefreshScope
-
方式二,使用@ConfigurationProperties注解
总结:
多环境配置共享
配置的优先级
远程>本地
总结:
nacos集群搭建
搭建集群的基本步骤:
- 搭建数据库,初始化数据库表结构(单点)
- 下载nacos安装包
- 配置nacos
- 启动nacos集群
- nginx反向代理
1.搭建数据库
这里由于是个人电脑,所以就不搭建MySQL集群了
SQL,nacos有提供,可以直接使用nacos提供的SQL
下图是数据库
2.下载nacos安装包,略
3.配置nacos
进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf:
然后添加内容:
127.0.0.1:8846
127.0.0.1.8847
127.0.0.1.8848
然后修改application.properties文件,添加数据库配置
spring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=xxxxxx
修改每个nacos端口
4.启动nacos集群
分别启动三个并访问
5.Nginx反向代理
在Nginx服务器中配置如下信息
#nacos集群upstream nacos-cluster {server 127.0.0.1:8846;server 127.0.0.1:8847;server 127.0.0.1:8848;
}server {listen 90;server_name localhost;location /nacos {proxy_pass http://nacos-cluster;}
}
接着修改服务中的之前的端口号,统一修改成90
cloud:nacos:server-addr: localhost:90 # nacos服务地址discovery:cluster-name: SHnamespace: 4bcd9908-987a-4d32-aa39-b60f9b0c08ef # dev环境ephemeral: false # 是否是临时实例
然后启动Nginx,访问http://localhost:90/nacos
Nacos和ErueKa注册中心的比较
nacos注册中心细节分析
在nacos中,有临时实例和非临时实例,对于这两个实例,nacos有不同的对待策略,对于临时实例,nacos会采用心跳检测来查看服务是否健康,对于非临时实例,nacos则会主动询问服务是否健康。同时,对于临时服务实例产生宕机超过一定时间,则会把该宕机的服务从服务列表中剔除,而非临时实例则不会
配置临时实例和非临时实例
cloud:nacos:server-addr: localhost:8848 # nacos服务地址discovery:cluster-name: SHnamespace: 4bcd9908-987a-4d32-aa39-b60f9b0c08ef # dev环境ephemeral: false # 是否是临时实例
总结:
4 Feign远程调用
以前我们通过RestTemplate调用远程服务,但是调用方式和写法不太优雅,维护性比较差
因此,我们使用Feign来进行远程调用
快速入门
1.引入依赖
2.在order-service的启动类添加注解开启Feign的功能
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class)
public class OrderApplication {
3.编写Feign客户端
@FeignClient(value = "userservice")
public interface UserClient {@GetMapping("/user/{id}")User findById(@PathVariable("id") Long id);
}
修改后的远程调用代码:
@GetMapping("{orderId}")public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {//1.查询订单Order order = orderMapper.findById(orderId);
// //2.利用RestTemplate发起http请求,查询用户
// //2.1 url路径
// String url = "http://localhost:8081/user/"+order.getUserId();
// //2.2 发送http请求,实现远程调用:get请求
// User user = restTemplate.getForObject(url, User.class);User user = userClient.findById(order.getUserId());//3.封装user信息order.setUser(user);//4.返回// 根据id查询订单并返回return order;}
Feign主要是基于SpringMVC的注解来声明远程调用的信息,比如:
服务名称:userservice
请求方式:GET
请求路径:/user/{id}
请求参数:Long id
返回值类型:User
总结:
自定义Feign配置
修改方式:
配置Feign日志有两种方式
方式一:配置文件
- 全局生效:
- 局部生效:
日志打印效果
方式二:代码配置
总结:
Feign性能优化
URLConnection:性能较差,每次连接都会造成大量资源浪费
性能优化的主要方向:
日志优化
略
连接池配置
总结:
Feign最佳实践
方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
不太可取,会造成紧耦合,且对SpringMVC不生效
方法二(抽取):将FeignClient抽取为独立模块,并把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
总结:
实践
步骤:
1.创建一个module,命名为feign-api,然后引入feign的start依赖
2.将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3.在order-service中引入feign-api的依赖
4.修改order-service中所有与上述三个组件有关的import部分,改成导入feign-api中的包
1.创建一个module,命名为feign-api,然后引入feign的start依赖
导入依赖
2.将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3.在order-service中引入feign-api的依赖
4.修改order-service中所有与上述三个组件有关的import部分,改成导入feign-api中的包
import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:
方式一:指定FeignClient所在包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
方式二:指定FeignClients字节码
@EnableFeignClients(clients={UserClient.class})
5 Gateway统一网关
网关功能
- 身份认证和权限校验
- 服务路由、负载均衡
- 请求限流
网关的技术实现
在springcloud中网关的实现包括两种:
- gateway
- zuul
Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway者是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能
总结:
搭建网关服务
1.新建module。引入SpringCloudGateway的依赖和nacos的服务发现依赖
<!--nacos服务注册发现依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--网关gateway依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>
2.编写路由配置以及nacos地址
server:port: 10010
logging:level:cn.itcast: debugpattern:dateformat: MM-dd HH:mm:ss:SSS
spring:application:name: gatewaycloud:nacos:server-addr: localhost:90 # nacos地址gateway:routes:- id: user-service # 路由标示,必须唯一uri: lb://userservice # 路由的目标地址predicates: # 路由断言,判断请求是否符合规则- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合- id: order-serviceuri: lb://orderservicepredicates:- Path=/order/**default-filters:- AddRequestHeader=Truth,Itcast is freaking awesome!
通过统一网关访问
访问流程:
总结:
路由断言工厂Route Predicate Factory
网关路由可以配置的内容包括:
- 路由ID:路由唯一标示
- URI:路由目的地,支持lb和http两种
- predicates:路由断言,判断请求是否符合要求,符合要求则转发到路由目的地
- filters:路由过滤器,处理请求或相应
我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
例如Path=/user/** 是按照路径匹配,这个规则是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理
路由过滤器 GatewayFilter
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
spring一共提供了差不多30多个过滤器
案例,给进入所有微服务添加一个请求头
gateway:routes:- id: user-service # 路由标示,必须唯一uri: lb://userservice # 路由的目标地址predicates: # 路由断言,判断请求是否符合规则- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合- id: order-serviceuri: lb://orderservicepredicates:- Path=/order/**default-filters:- AddRequestHeader=Truth,Itcast is freaking awesome!
全局过滤器 GlobalFilter
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。
区别在于GatewayFilter通过配置定义,处理逻辑是固定的。二GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GolbalFilter接口。
案例:拦截并判断用户身份
实现代码:
// @Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求参数ServerHttpRequest request = exchange.getRequest();MultiValueMap<String, String> params = request.getQueryParams();// 2.获取参数中的 authorization 参数String auth = params.getFirst("authorization");// 3.判断参数值是否等于 adminif ("admin".equals(auth)) {// 4.是,放行return chain.filter(exchange);}// 5.否,拦截// 5.1.设置状态码exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);// 5.2.拦截请求return exchange.getResponse().setComplete();}//执行顺序,值越小越先执行 ,等同于@Order(-1)注解@Overridepublic int getOrder() {return -1;}
}
注:
在上面代码中、@Overridepublic int getOrder() {return -1;}
和注解@Order(-1)的作用是相同的,代表着该过滤器执行的先后顺序,数值越小,越先执行
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFiter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后一次执行每个过滤器
当前路由过滤器
DefaultFilter
1.在过滤器中,每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行的顺序越靠前。
2.GlobalFilter通过实现Ordered接口,或添加@Order注解来指定order的值,由我们自己指定
3.路由过滤器和defaultFilter的order由spring指定,默认是按照声明顺序从1递增
4.当过滤器的order值一样时,会按照defultFilter>路由过滤器>GlibalFilter的顺序执行
跨域问题处理
跨域:域名不一致就是跨域,主要包括
域名不同:www.taobao.com和www.taobao.org
域名相同,端口不同:localhost:8080和localhost8081
跨域问题:浏览器进制请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
可以通过在网关中配置跨域以达到跨域的目的
globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:'[/**]':allowedOrigins: # 允许哪些网站的跨域请求- "http://localhost:8090"- "http://www.leyou.com"allowedMethods: # 允许的跨域ajax的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000 # 这次跨域检测的有效期
总结: