Spring-Cloud学习笔记

Spring Cloud

Eureka

Eureka注册中心

eureka注册中心图.png

EurekaServer:服务端,注册中心
  • 记录服务信息
  • 心跳监控
EurekaClient:客户端
  • Provider:服务提供者
    • 注册自己的信息到EurekaServer
    • 每隔30秒向EurekaServer发送心跳
  • Consumer:服务消费者
    • 根据服务名称从EurekaServer拉取服务列表
    • 基于服务列表做负载均衡,选中一个微服务后发起远程调用
搭建EurekaServer
  • 创建项目,引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
  • 编写启动类,添加@EnableEurekaServer注解
  • 添加application.yml文件
server:
  port: 10086	# 服务端口
spring:
  application:
  	name: eurekaserver	# eureka服务名称
eureka:
  client:
  	service-url:
  	  defaultZone: http://localhost:10086/eureka/
注册user-service
将user-service服务注册到EurekaServer
  • 在user-service项目中引入spring-cloud-starter-netflix-eureka-client的依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  • 添加application.yml文件
spring:
  application:
  	name: user-service
eureka:
  client:
  	service-url:
  	  defaultZone: http://localhost:10086/eureka/
服务拉取(远程调用其他服务接口)
服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡

RestTemplate类是发送http请求的

  • 创建RestTemplate并注入到Spring容器(放在Application启动类中)
@Bean
@LoadBalanced	//负载均衡
public RestTemplate restTemplate(){
    return new RestTemplate();
}
  • 发送请求(在order-service完成服务拉取)
String url = "http://userservice/user"+order.getUserId();
//发送http请求,实现远程调用(get、post)	  返回json格式,会自动封装成User对象
User user = restTemplate.getForObject(url,User.class);

Ribbon负载均衡

负载均衡流程

Ribbon负载均衡.png

修改负载均衡策略
默认实现的是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
  • 代码方式:在Application启动类中,定义一个新的IRule(针对全局的配置
@Bean
public IRule randomRule(){
	return new RandomRule();
}
  • 配置文件方式:在order-service的application.yml文件中,添加新的配置即可修改(针对order-service服务配置
userservice: 
  ribbon:
  	NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule	#负载均衡规则
饥饿加载
Ribbon默认采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。

饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

ribbon:  
    eager-load:    
        enabled: true # 开启饥饿加载    
        clients: userservice # 指定对userservice这个服务饥饿加载

Nacos

Nacos注册中心

nacos注册中心细节.png

服务注册到Nacos
  • 在clund_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>
  • 注释掉order-service和user-service中原有的eureka依赖
  • 添加nacos的客户端依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  • 修改user-service&order-service中的application.yml文件,注释eureka地址,添加nacos地址
spring: 
  cloud: 
  	nacos:
  	  serve-addr: localhost:8848	# nacos服务地址
  • 启动测试
临时实例和非临时实例
  • 临时实例:采用心跳检测(每隔一段时间发一个请求到nacos告诉我还活着)和erueka心跳检测一样。如果有一天心跳不跳了nacos会将其在服务列表中直接剔除。(有点慢)
  • 非临时实例:nacos会主动发请求询问是否活着,若死了则会等待其恢复健康而不是剔除(主动发起速度更快)

可在yml文件中修改是否为临时实例

spring:
  cloud:
    nacos:
      discovery:
        ephemeral: false	#设置为非临时实例
服务集群属性
  • 设置集群
spring: 
  cloud: 
  	nacos:
  	  serve-addr: localhost:8848	# nacos服务地址
  	  discovery:
  	  	cluster-name: HZ	# 配置集群名称,也就是机房位置,例如:HZ,杭州
  • 修改集群的负载均衡的IRule(NacosRule是AlibabaNacos自己实现的一个负载均衡策略,可以在nacos平台中根据自定义权重进行访问)
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule	# 负载均衡规则
  • 注意要将user-service的权重都设置为1
NacosRule负载均衡策略
  1. 优先选择同集群服务实例列表
  2. 本地集群找不到提供者,才去其他集群寻找,并且会报警告
  3. 确定了可用实例列表后,再采用随机负载均衡挑选实例
实例的权重控制
  1. 权重值(0~1之间)
  2. 权重越高访问频率越高
  3. 权重设置为0则完全不被访问(大多数用于服务升级)
环境隔离-namespace
  1. 创建新的命名空间

创建命名空间.png

  1. 填写命名空间信息

填写命名空间信息.png

  1. 复制命名空间id

复制id.png

  1. 在application.yml文件中修改
spring:
  datasource:
    url: jdbc:mysql://mysql:3306/cloud_user?useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: userservice
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos服务地址
      discovery:
        cluster-name: HZ
        namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # dev环境id
  1. 重启order-service后,再查看控制台

结果.png
要想让服务可以访问,必须要在相同的命名空间下

Nacos和Eureka的区别
  1. Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
  2. 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
  3. Nacos支持服务列表变更的消息推送模式,服务列表更新及时
  4. Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式

Nacos配置管理

统一配置管理.png

统一配置管理

配置热更新1.png配置热更新2.png
获取配置步骤
1.png

  1. 引入Nacos的配置管理客户端依赖
<!--nacos的配置管理依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  1. 在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml
spring:
  application:
    name: userservice	# 服务名称
  profiles:
    avtive: dev	# 开发环境,这里是dev
  cloud:
    nacos:
      server-addr: localhost:8848	# Nacos地址
      config:
        file-extension: yaml	# 文件后缀名
配置自动刷新
  • 方式一:在@Value注入的变量所在类上添加注解@RefreshScope

添加注解配置.png

  • 方式二:使用@ConfigurationProperties注解(推荐使用)
@Component
@Data
@ConfigurationProperties(prefix="pattern")
public class PatternPreperties(){
	private String dateformat;
}
@RequestMapping("/user")
@Controller
public class UserController{
	
	@Autowired
	private PatternPreperties properties;
	
	@GetMapping("/now")
	public String now(){
		return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties).getDateformat());
	}
}
多服务共享配置
  • 多种配置的优先级

多种配置的优先级.png

  • 从nacos读取的配置文件
    • [服务名]-[spring.profile.active].yaml,环境配置
    • [服务名].yaml,默认配置,多环境共享
集群搭建

6.png

  1. 搭建MySQL集群并初始化数据库表
  2. 下载解压nacos
  3. 修改集群配置(节点信息)、数据库配置
  4. 分别启动多个nacos节点
  5. nginx反向代理

Feign

使用Feign步骤

  1. 引入依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在order-service的启动类添加注解开启Feign的功能
@EnableFeignClients
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication{

	public static void main(String[] args){
        SpringApplication.run(OrderApplication.class,args);
    }
}
  1. 编写Feign客户端
@FeignClient("userservice")
public interface UserClient{
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

主要是基于SpringMVC的注解来声明远程调用的信息,比如:

  • 服务名称:userservice
  • 请求方式:GET
  • 请求路径:/user/{id}
  • 请求参数:Long id
  • 放回值类型:User

自定义Feign配置

方式一:配置文件方式
  1. 全局生效
feign:
  client:
    config:
      default:	#这里用default就是全局配置,如果写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL
  1. 局部生效
feign:
  client:
    config:
      userservice:	#这里用default就是全局配置,如果写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL
方式二:Java代码配置
  1. 先声明一个Bean
public class DefaultConfiguration {
    @Bean
    public Logger.Level logLevel(){
        return Logger.Level.BASIC;
    }
}
  1. 全局或局部配置
    1. 全局配置
@EnableFeignClients(defaultConfiguration = FeignClientProperties.FeignClientConfiguration.class)
  1. 局部配置
  @FeignClient(defaultConfiguration = FeignClientProperties.FeignClientConfiguration.class)

Feign性能优化

步骤

  1. 引入依赖
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
</dependency>
  1. 配置连接池
feign:
  client:
	  config:
      default:		# default全局配置
      	loggerLevel: BASIC		# 日志级别
	httpclient:
  	enabled: true		# 开启feign对httpClient的支持
    max-connections: 200		# 最大的连接数
  	max-connections-per-route: 50		# 每个路径的最大连接数

抽取FeignClient

  1. 首先创建一个module,命名为feign-api,然后引入feign的starter依赖
  2. 将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
  3. 在order-service中引入feign-api的依赖
  4. 修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
  5. 重启测试

当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用,以下两种解决方式:

  • 指定FeignClient所在包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
  • 指定FeignClient字节码
@EnableFeignClients(clients = {UserClient.class})

Gateway

网关作用

  • 对于用户请求做身份认证、权限校验
  • 将用户请求路由到微服务,并实现负载均衡
  • 对用户请求做限流

Gateway流程图

6.png

搭建网关服务步骤

  1. 引入依赖
<!--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>
  1. 编写路由配置及nacos地址
server:
  prot:10010	#	网关端口
spring:
	application:
  	name: gateway  # 服务名称
	cloud:
  	nacos:
    	server-addr: localhost:8848  # nacos地址
  	gateway:
    	routes:  # 网关路由配置
      	- id: user-service	#	路由id,自定义,只要唯一即可
        	# uri: http://127.0.0.1:8081  # 路由的目标地址http就是固定地址
        	uri: lb://userservice  # 路由的目标地址  lb就是负载均衡,后面跟服务名称
        	predicates:  # 路由断言,也就是判断请求是否符合路由规则的条件
          	- Path=/user/**  # 这个是按照路径匹配,只要以/user/开头就符合要求

路由断言工厂

6.png路由过滤器配置

给所有进入userservice的请求添加一个请求头:Truth= Itcast is freaking aowsome!

实现某个服务的路由过滤器

在gateway中修改application.yml文件,给userservice的路由添加过滤器

spring:
  cloud:
    gateway:
      routes:		# 网关路由配置
        - id: user-service
          uri: lb://userservice
          predicates:
            - Path=/user/**
          filters:		# 过滤器
            -AddReqestHeader=Truth, Itcast is freaking aowsome!  # 添加请求头

实现所有路由都生效

spring:
  cloud:
    gateway:
      routes:		# 网关路由配置
        - id: user-service
          uri: lb://userservice
          predicates:
            - Path=/user/**
      default-filterss:   # 默认过滤器,会对所有的路由请求都生效
        - AddRequestHeader=Truth, Itcast is freaking aowsome!  # 添加请求头

全局过滤器

案例

定义全局过滤器,拦截并判断用户身份
需求:定义全局过滤器,拦截请求,判断请求参数是否满足下面条件:

  • 参数中是否有authorization
  • authorization参数值是否为admin

如果同时满足则放行,否则拦截

  • 自定义类,实现GlobalFilter接口,添加@Order注解
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public 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.判断参数值是否等于 admin
        if ("admin".equals(auth)) {
            // 4.是,放行
            return chain.filter(exchange);
        }
        // 5.否,拦截
        // 5.1.设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 5.2.拦截请求
        return exchange.getResponse().setComplete();
    }
}
  • 自定义类,实现GlobalFilter、Ordered接口
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered  {
    @Override
    public 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.判断参数值是否等于 admin
        if ("admin".equals(auth)) {
            // 4.是,放行
            return chain.filter(exchange);
        }
        // 5.否,拦截
        // 5.1.设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 5.2.拦截请求
        return exchange.getResponse().setComplete();
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

过滤器执行顺序

屏幕截图 2023-06-27 213333.png

  • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
  • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增
  • 当过滤器的order值一样时,会按照defaultFilter>路由过滤器>GlobalFilter的顺序执行

网关跨域问题

跨域:域名不一致就是跨域,主要包括:

  • 域名不同:www.taobao.com和www.taobao.org
  • 域名相同,端口不同:localhost:8080和localhost:8081

跨域问题处理

spring:
	cloud:
  	gateway:
    	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	# 是否允许携带cookie
            maxAge: 360000	# 这次跨域检测的有效期
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值