springcloud(2020)

springcloud升级

在这里插入图片描述

maven中的DependencyMangement和Dependencies区别

  • maven使用DependencyManagement元素来提供一种管理依赖的版本号的方式,通常会在项目的最顶层父POM中看到dependencymanagement元素。
  • 在父pom.xml中使用dependencymanagement元素能让所有子项目中引用一个依赖而不用显示的列出版本号,maven会使用父pom中的dependencymanagement元素中指定的版本号。例如:
    在这里插入图片描述
  • dependencymanagemet里只是声明依赖,并不是先引入,因此子项目需要显示的声明用到的依赖。
  • 如果不在子项目中声明依赖,是不会从父项目中继承下来;只有在子项目中声明了依赖,并且没有指定版本号才会从父项目中继承该项,并且version和scope都取自父pom。
  • 如果子项目中指定了版本号,那么会使用子项目中指定的版本。

解决 jar 包冲突(依赖的原则)

1. 路径最短者优先

项目A 依赖 B,B 又依赖 C。
并且 B 和 C 都依赖 D,不过依赖的版本不一样,B 依赖是 D-1.0 版本,C 依赖的是 D-2.0 版本。
因为项目 A 是先依赖的 B ;然后通过 B 依赖 C 的依赖传递,A 也依赖了 C 。
A 到 B 的路径相当于是 1,A 到 D-1.0 的路径相当于就是 2 ;

A 到 C 的路径相当于是 2,A 到 D-2.0 的路径相当于就是 3 ;
所以 A 会根据最短路径优先的原则,会使用 D-1.0 。
在这里插入图片描述

2. 路径相同时,先声明者优先

这里“声明”的先后指的是在 pom 中的 dependency 标签的先后顺序。
比如:
项目A 依赖 B,B 依赖 D-1.0;
项目A 依赖 C,C 依赖 D-2.0。
因为 D-1.0 与 D-2.0 的路径相同,路径最短者优先原则就没用了。

这个时候应该看 B 和 C 在 pom 中声明的顺序,先声明的优先。

   <dependencies>

       <dependency>
           <groupId>com.yuanxion.maven</groupId>
           <artifactId>B</artifactId>
           <version>${yx.Spring.version}</version>
       </dependency>

       <dependency>
           <groupId>com.yuanxion.maven</groupId>
           <artifactId>C</artifactId>
           <version>${yx.Spring.version}</version>
       </dependency>

   </dependencies>

创建一个微服务项目

在这里插入图片描述

支付模块

在这里插入图片描述在这里插入图片描述在这里插入图片描述

热部署自动化配置

对项目进行修改后不需要手动重启,项目自动进行重启运行提高编码效率。
在这里插入图片描述

订单模块调用支付模块

RestTemplate提供了多种远程访问http服务的方法,是一种简单的便捷的访问restful服务模板类,是spring提供访问rest服务的客户端模板工具集
在这里插入图片描述

项目重构

两个子项目中entities包中有相同代码将它们抽取出来放到一个新建的子模块中cloud-api-commons将原来子项目中entities包删除掉,将新建的cloud-api-commons下载到本地maven仓库引入到其他子项目中。
在这里插入图片描述

服务注册与发现

Eureka(已停更)

Zookeeper

Consul

consul是什么?

consul是一套开源的分布式服务发现和配置管理系统,有HashiCorp公司用go语言开发。提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个可以根据需要单独使用,也可以一起使用构建全方位的服务网格,总之consul提供了一种完整的服务网格解决方案。
它具有很多优点。包括:支持跨数据中心的WAN集群、跨平台支持linux、mac、windows

consul能干嘛?
  • 服务发现
  • 健康监测
  • kv存储
  • 多数据中心
  • 可谁啊web界面

三个注册中心的异同点

在这里插入图片描述

负载均衡与服务调用

ribbon

ribbon是什么?

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

ribbon能干嘛?

主要是负载均衡(LB)

负载均衡(LB)是什么?

简单来说就是将用户的请求平均分摊到多个服务上,从而达到系统的高可用,常见的负载均衡有Nginx,LV5,硬件H5等。

Ribbon负载均衡和nginx负载均衡的区别?

nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。
ribbon是本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。

ribbon自带的负载均衡规则

根据特定算法从服务列表中选取一个要访问的服务
在这里插入图片描述

ribbon负载均衡规则替换

在这里插入图片描述

ribbon轮询算法原理

openfeign

什么是openfeign?

Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口并加上对应的注解,比如:@FeignClient注解。

openfeign(服务调用)怎么使用?
  • 在主启动类上使用@enablefeignclients开启服务调用功能
  • 创建服务接口且使用@feignclient声明要调用的服务
    细节:openfeign中整合了ribbon功能
    在这里插入图片描述
openfeign超时控制

openfeign默认等待1s,被调用端服务需要等待3s,需要在调用端服务进行超时设置。

#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

在这里插入图片描述

openfeign日志增强功能
  • 创建日志配置类并指定openfeign的日志级别
  • yml配置文件中配置日志级别和监控接口
    在这里插入图片描述

服务网关

gateway

gateway是什么?

网关还可以用zuul,但是zuul网关在维护中出现了一些问题,所以gateway就代替zuul。
而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

SpringCloud Gateway的目标是提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/指标、限流等。

gateway能干嘛?

反向代理、鉴权、流量控制、熔断、日志监控等等

gateway核心组件?
  • 路由Route:路由是构建网关的基本模块,它由ID、目标URI、一系列的断言和过滤器组成,如果断言为true则匹配路由
  • 断言Predicate:开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言匹配则进行路由。
  • 过滤器Filter:Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由之前或者之后对请求进行修改。
gateway的使用?

方式一:yml配置文件

在路由routes中我们配置了1个路由,该路由是针对8001的getPaymentLB服务,然后我们分别启动Eureka服务注册中心、服务提供方8001微服务、9527网关微服务。我们发现,在添加网关前,我们需要通过 http://localhost:8001/payment/get/31 来访问服务提供方的服务,在添加网关后,我们通过 http://localhost:9527/payment/get/31访问网关也可以访问到服务提供方的微服务。
这样的话用网关对微服务进行路由访问,就可以不再对外暴露微服务的真实地址,而是统一暴露为网关的地址
在这里插入图片描述

方式二:编码方式
SpringCloud Gateway的网关路由有两种配置方式,一种就是上面通过配置文件application.yml进行网关路由配置,还可以在代码中注入RouteLocator的Bean进行配置,下面实现用编码的方式,实现通过9527网关对百度新闻的访问。

在9527网关微服务中编写如下的配置类:

@Configuration
public class GateWayConfig
{
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)
    {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();

        routes.route("path_route",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();

        return routes.build();
    }

上面的配置类注入RouteLocator的Bean,其配置的一个id为"path_route"的路由,当访问地址 http://localhost:9527/guonei 时,该路由会将访问自动转发到 http://news.baidu.com/guonei :
在这里插入图片描述

通过微服务名实现动态路由

在这里插入图片描述然后通过网关我们访问CLOUD-PAYMENT-SERVICE服务,可以看到网关同之前用过的Ribbon一样,可以实现动态路由,可以看出其默认的负载均衡算法也是轮询负载均衡:
在这里插入图片描述

gateway常用的predicate
  • AfterRoutePredicate:在指定时间之后路由才有效
  • BeforeRoutePredicate:在指定时间之前路由才有效
  • BetweenRoutePredicate:在指定时间之间路由才有效

在2020-02-21T15:51:37.485+08:00[Asia/Shanghai]时间之后进行路由才有效

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service      #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            - After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
  • CookieRoutePredicate:带cookie或者不带cookie时进行路由
server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            - After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            - Cookie=username,zzyy

路由的时候带上cookie的k-v路由才有效果。
在这里插入图片描述

  • HeadRoutePredicate:路由带上请求头才有效,且一个是属性名一个是正则表达式。

路由时候带上请求头,属性名称是X-Request-Id且值必须是整数。

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            - Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式

在这里插入图片描述

  • HostRoutePredicate:路由的时候带上主机名才有效。
predicates:
    - Host=**.atguigu.com

在这里插入图片描述

  • MethodRoutePredicate:路由的时候带上方法提交方式才有效。
predicates:
 - Method=GET
  • QueryRoutePredicate:路由的时候带有路径才有效。

路由的时候路径要带有username属性且值还必须是整数才有效。

predicates:
  -Query=username, \d

在这里插入图片描述

gateway自定义过滤器

Gateway中自定义过滤器要是实现两个接口GlobalFilterOrdered,前者实现了全局过滤器,而后者规定了过滤器的执行顺序,该顺序数字越小,过滤器越先被执行。下面编写具体的过滤器并实现上面两个接口的方法:

package cn.sher6j.springcloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

/**
 * @author sher6j
 * @create 2020-05-22-21:59
 */
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("——————————————全局过滤器:" + new Date());
        //获得uname属性
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        //如果不包含该属性,则过滤器对请求进行拦截
        if (uname == null) {
            log.info("——————————————用户名为null,非法用户");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 加载过滤器的顺序,数字越小优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 3;
    }
}

这里需要注意的是,在getOrder()方法中,为了保证过滤器可拓展,尽量不要用0/1 这种拓展性不够好的数字,用0/1会使该自定义过滤器几乎永远先被执行,然后我们对服务进行访问我们可以发现,当我们包含请求参数uname时,可以正常访问,如果不含该请求参数,则访问无法正常进行。
在这里插入图片描述

服务注册中心和配置中心nacos

nacos服务注册中心

什么是nacos?

一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
nacos=eureka+config+bus,也就是注册中心与配置中心的组合。

nacos之window安装
  • 本地JDK8+maven环境
  • 解压安装包,直接运行bin目录下的startup.cmd
  • 运行成功后直接访问http://localhost:8848/nacos
服务提供者注册到nacos中

在父工程pom中添加alibaba依赖:

<!--spring cloud alibaba 2.1.0.RELEASE-->
   <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.1.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>

在子工程(服务的提供者)中添加nacos启动器依赖:

<!--SpringCloud ailibaba nacos -->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  </dependency>

编写子工程的yml配置文件,将服务提供者注册到nacos中:

server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider   #服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 		#nacos注册中心地址

management:
  endpoints:
    web:
      exposure:
        include: '*'      #暴露所有服务断点

编写子工程主启动类:

package com.atguigu.springcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @auther zzyy
 * @create 2020-02-23 14:12
 */
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9001
{
    public static void main(String[] args) {
            SpringApplication.run(PaymentMain9001.class, args);
    }
}

编写子工程的表现层:

package com.atguigu.springcloud.alibaba.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * @auther zzyy
 * @create 2020-02-23 14:13
 */
@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/nacos/{id}")
    public String getPayment(@PathVariable("id") Integer id)
    {
        return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
    }
}

服务消费者成功注册到nacos注册中心中:
在这里插入图片描述
@EnableDiscoveryClient注解
作用地方:主启动类上
作用:将服务注册到nacos中

服务消费者注册到nacos中和负载均衡

细节:nacos天生自带负载均衡,因为引入的nacos启动器依赖中整合有netflix-ribbon依赖。

2个服务提供者nacos-payment-provider都注册到nacos中,等待消费者来进行消费:
在这里插入图片描述
编写消费者的yml配置文件,将消费者也注册到nacos中:

server:
  port: 83


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848      #消费者服务注册到nacos中


#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

nacos中天生整合了netflix-ribbon依赖,既然有ribbon依赖我们就使用RestTemplate实例,我们将RestTemplate其注入到容器中:

package com.atguigu.springcloud.alibaba.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @auther zzyy
 * @create 2020-02-23 14:45
 */
@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

编写消费者的表现层并在表现成中注入RestTemplate实例,然后进行url路径映射http://localhost:80/consumer/payment/nacos/13:

package com.atguigu.springcloud.alibaba.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @auther zzyy
 * @create 2020-02-23 15:01
 */
@RestController
@Slf4j
public class OrderNacosController
{
    @Resource
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @GetMapping(value = "/consumer/payment/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Long id)
    {
        return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
    }

}

在地址栏中访问http://localhost:80/consumer/payment/nacos/13路径通过paymentInfo方法跳转到http://nacos-payment-provider/payment/nacos/13请求中,采用轮询方式(负载均衡)访问nacos中的两个服务提供者:
在这里插入图片描述@LoadBalance注解
作用地方:注入RestTemplate实例的方法上
细节:使用ribbon+RestTemplate做负载均衡时候必须加该注解
作用:其一开启负载均衡功能(轮询、随机等);其二就能在调用其他微服务的时候,通过服务实例名称就能进行调用其他的微服务,而不是直接把要调用的微服务的ip和端口号写死在代码当中。如下:

@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced   #开启负载均衡、以及通过服务名称就能调用该服务
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}
服务注册中心对比

nacos既支持AP有支持CP,如何选用哪钟模式呢?
一般来说,如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。

如果需要在服务级别编辑或存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。
CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

切换命令:curl -X PUT'$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
在这里插入图片描述

nacos之服务配置中心

nacos入门配置

新建module服务后,引入nacos的服务注册和服务配置启动器依赖:

 <!--nacos-config-->
 <dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
 </dependency>
   <!--nacos-discovery-->
 <dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
 </dependency>

编写bootstrap.yaml配置文件,将3377微服务注册nacos注册中心,同时将该服务配置到nacos配置中心。
bootstrap.yaml:

# nacos配置
server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 		#Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 		#Nacos作为配置中心地址
        file-extension: yaml				 #指定yaml格式的配置
      

在该微服务中再创建一个application.yaml配置文件,application.yaml:

spring:
  profiles:
    active: dev 		# 表示开发环境

编写启动类:

package com.atguigu.springcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @auther zzyy
 * @create 2020-02-23 17:01
 */
@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377
{
    public static void main(String[] args) {
        SpringApplication.run(NacosConfigClientMain3377.class, args);
    }
}

在nacos中进行服务配置,DataId有固定的格式,必须是:服务名称-服务环境.配置文件格式(其中格式必须是yaml不能是yml)

nacos配置中心:
在这里插入图片描述

package com.atguigu.springcloud.alibaba.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @auther zzyy
 * @create 2020-02-23 17:02
 */
@RestController
@RefreshScope 			//nacos配置中心发生改变,支持动态刷新
public class ConfigClientController
{
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}

测试结果:
在这里插入图片描述

@RefreshScope
作用地方:表现层类上
作用:修改nacos配置中心中的配置,支持动态刷新功能。

nacos配置中心之命名空间、分组、DataId三者关系

Nacos默认的命名空间是public,Namespace主要是用来实现隔离的,比方说我们现在有3个环境:开发、测试、生产,我们就可以创建3个Namespace,不同的Namespace之间是隔离的。
Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里去。
在这里插入图片描述

nacos之DataId

如果nacos配置中心中有个服务的配置DataId是:nacos-config-client-dev.yaml 那么bootstrap.yaml中配置是:

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      config:
        file-extension: yaml #指定yaml格式的配置

application.yaml中配置是:

spring:
  profiles:
    active: dev # 表示开发环境
nacos之group

如果nacos配置中心有个服务的配置group是dev_GROUP 那么bootstarp.yaml配置是:

spring:
  cloud:
    nacos:
      config:
        group: DEV_GROUP
nacos之Namespace

如果nacos配置中心有test命名空间,且有个服务的配置group是:DEV_GROUP 那么bootstrap.yaml配置是:

spring:
  cloud:
    nacos:
      config:
        group: DEV_GROUP
        namespace: 0aa2b61f-737a-4923-b93e-29a8c51e8f03

在这里插入图片描述

nacos持久化切换配置(derby—>mysql)

默认Nacos使用嵌入式数据库derby实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部罢,目前只支持MySQL的存储。

切换mysql数据库步骤:

  • 安装mysql,版本要求:5.6.5+
  • 找到nacos-server-1.1.4\nacos\conf目录下的nacos-mysql.sql将里面内容在mysql数据库中进行初始化
  • 修改nacos-server-1.1.4\nacos\conf目录下的application.properties文件,添加支持mysql数据源配置、添加url、用户名和密码
spring.datasource.platform=mysql
 
db.num=1
db.url.0=jdbc:mysql://127.xx.xx.xx:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true   #换成自己的地址
db.user=root    	  #切换成自己的用户名
db.password=123456    #切换成自己的用户名
  • 再启动nacos,nacos所有写嵌入式数据库的数据迁移到mysql中了
    在这里插入图片描述
nacos之linux版本安装
  • nacos下载地址
  • 上传到linux中
  • 解压tar - zxvf nacos-server-1.4.1.tar.gz
  • 将解压后的nacos移动到自己设置的目录中
  • 单机启动nacos sh startup.sh -m standlone
nacos集群配置
  • 找到linux中的nacos-server-1.1.4\nacos\conf目录下的nacos-mysql.sql将里面内容在linux的mysql数据库中进行初始化。
    在这里插入图片描述
  • 修改linux中的nacos-server-1.1.4\nacos\conf目录下的application.properties文件,添加支持linux中mysql数据源配置、添加url、用户名和密码。
spring.datasource.platform=mysql
 
db.num=1
db.url.0=jdbc:mysql://127.xx.xx.xx:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true   #换成自己的地址
db.user=root    	  #切换成自己的用户名
db.password=123456    #切换成自己的用户名
  • 在linux中的nacos-server-1.1.4\nacos\conf目录下的cluster.conf.example中配置nacos集群
    在这里插入图片描述

  • 编辑nacos启动脚本startup.sh,使它能够接受不同的启动端口。
    nacos-server-1.1.4\nacos\bin目录下有startup.sh,平时单机版启动都是sh startup.sh或者./startup.sh就可以,但是集群启动我们希望可以类似其他软件的shell命令,传递不同端口号启动不同的nacos实例。如:./startup.sh -p 3333表示启动端口号为3333d nacos服务实例。
    在这里插入图片描述

  • 配置linux中的nginx,由他作为负载均衡。(在nginx/conf/nginx.conf中进行配置)
    在这里插入图片描述

  • 启动linux中的nginx
    ./nginx - c /usr/local/nginx/conf/nginx.conf #指定nginx配置文件的位置

  • 启动nginx的集群

./startup.sh -p 3333
./startup.sh -p 4444
./startup.sh -p 4444

在这里插入图片描述高可用总结:
在这里插入图片描述

服务降级

sentinel

是什么是sentinel?

Sentinel是面向微服务的轻量级流量控制框架,从流量控制、熔断降级、热点防护、系统负载保护等多个维度保护服务的稳定性。

sentinel能干啥?

在这里插入图片描述

sentinel下载安装
  • 下载地址
  • 安装 java -jar sentinel-dashboard-1.8.0.jar
  • 运行localhost:8080
    在这里插入图片描述
sentinel监控入门

引入sentinel启动器依赖:

  <dependency>
     <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
     </dependency>

修改yaml配置文件:

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 		#微服务注册到nacos中
    sentinel:
      transport:
        dashboard: localhost:8080 			#对微服务进行sentinel监控
        port: 8719							#sentinel端口号
        
management:
  endpoints:
    web:
      exposure:
        include: '*'

编写主启动类:

package com.atguigu.springcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @auther zzyy
 * @create 2020-02-24 16:26
 */
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401
{
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
    }
}

编写表现层:

package com.atguigu.springcloud.alibaba.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @auther zzyy
 * @create 2020-02-24 16:26
 */
@RestController
@Slf4j
public class FlowLimitController
{
    @GetMapping("/testA")
    public String testA()
    {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB()
    {
        return "------testB";
    }

}

测试后结果,sentinel进行监控服务:
在这里插入图片描述

流量控制

流量控制之QPS直接失败

编辑限流规则,QPS表示每秒钟查询次数。设置为1,表示1秒钟查询1次就ok:
在这里插入图片描述每秒钟查询次数超过1次,进行限流:
在这里插入图片描述

流量控制之线程数直接失败

线程数设置为1,表示只允许一个线程进来访问:
在这里插入图片描述当达到线程数阈值时候进行限流:
在这里插入图片描述

流量控制之关联

关联表示当与A关联的资源B达到阈值的时,就限流A自己。

当关联资源/testB的qps阈值超过1时,就限流/testA的访问:
在这里插入图片描述

流控控制之预热

warm up方式,即预热方式,当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过预热的方式,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给系统一个预热的时间,避免冷系统被压垮。

默认coldfactor为3,即请求QPS从(threshold/3)开始,经过多少预热时长才逐渐升至设定的qps阈值。
如:系统初始化阈值为10/3约等于3,即阈值刚开始为3,经过5s后阈值才慢慢恢复到10
在这里插入图片描述应用场景:秒杀系统在开启的瞬间会有很多流量上来,很有可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。

流量控制之排队等待

匀速排队,让请求以均匀的速度通过,阈值类型必须设成QPS,否则无效。

含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20s。
在这里插入图片描述

降级

在这里插入图片描述RT(平均响应时间,秒级)
平均响应时间>阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级(断路器打开);时间窗口结束后,关闭降级。

每次线程进来都要1s,平均响应时间是10s:

/**
 * @auther zzyy
 * @create 2020-02-24 16:26
 */
@RestController
@Slf4j
public class FlowLimitController
{
    @GetMapping("/testD")
    public String testD()
    {
        log.info("testD RT");
         try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        return "------testD";
    }

}

在这里插入图片描述
1秒钟打进来10个线程(大于5个了),调用testD,阈值是200ms,超过200ms还没有处理完,在未来1s的时间窗口内,断路器打开(保险丝跳闸),服务不可用。

异常比例(秒级)
QPS>=5且 异常比例>阈值,触发降级;时间窗口结束后,关闭降级。

每次线程进来都是报错,异常比例是100%:

/**
 * @auther zzyy
 * @create 2020-02-24 16:26
 */
@RestController
@Slf4j
public class FlowLimitController
{
    @GetMapping("/testD")
    public String testD()
    {
        log.info("testD 异常比例");
        int age = 10/0;
        return "------testD";
    }

}

在这里插入图片描述1s有10个线程打进来(大于5个了),调用testD,每次进来都会报错异常比例是100%,阈值是0.2,在未来1s内时间窗口内断路器打开,服务不可用。

异常数(分钟级)
异常数>阈值,触发降级;时间窗口结束后,关闭降级(时间窗口必须>60s)。

每次进来都是错误,异常数很高:

/**
 * @auther zzyy
 * @create 2020-02-24 16:26
 */
@RestController
@Slf4j
public class FlowLimitController
{
   @GetMapping("/testE")
    public String testE()
    {
        log.info("testE 测试异常数");
        int age = 10/0;
        return "------testE 测试异常数";
    }

}

在这里插入图片描述第一次访问绝对报错,因为算术运算异常(除数不能为0),但是访问达到5次后,异常数超过阈值,断路器打开,服务不可用。

@sentinelresource

作用地方:方法上
属性:
value,指定sentinel控制台中热点资源名称,需要通过 value 值找到对应的规则进行配置。
blockHandler,负责sentinel控制台配置违规兜底处理。
blockhandlerclass,指定sentinel控制台兜底处理方法存放在哪个类中。
fallback,负责java运行时异常兜底处理。
exceptionstoignore,忽略指定异常,且不执行fallback属性指定的异常兜底方法。

配置属性blockhandler和blockhandlerclass

设置兜底类CustomerBlockHandler,如果热点配置规则不匹配则执行CustomerBlockHandler类中的handlerException2方法:
在这里插入图片描述

资源名称是customerBlockHandler,设置QPS等于1,当每秒查询次数等于1时,返回200客户自定义;当每秒查询次数超过1时,执行兜底类中的兜底方法返回4444客户自定义:
在这里插入图片描述

配置fallback属性

设置fallback属性,当产生运行时异常时执行异常处理方法handlerFallback

@RestController
@Slf4j
public class CircleBreakerController
{
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handlerFallback") 
    public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

        if (id == 4) {
                throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
            }else if (result.getData() == null) {
                throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }


    //本例是fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }


}

在这里插入图片描述

fallback和blockhandler属性都配置

配置fallback属性,当产生java运行时异常时执行handlerFallback兜底方法;同时配置blockhandler属性,当sentinel控制台出现配置违规时执行blockHandler兜底方法:

/**
 * @auther zzyy
 * @create 2020-02-25 16:05
 */
@RestController
@Slf4j
public class CircleBreakerController
{
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class})
    public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

        if (id == 4) {
                throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
            }else if (result.getData() == null) {
                throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }


    //本例是fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }
    
    //本例是blockHandler
    public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }


}

当QPS超过1时执行blockHandler兜底方法;当产生java运行时异常时执行handlerFallback兜底方法;当QPS既高又产生java运行时异常时服从blockhandler属性方法:
在这里插入图片描述

配置exceptionstoignore属性

配置了exceptionstoignore属性,指定忽略IllegalArgumentException异常:
在这里插入图片描述当产生了IllegalArgumentException异常时并没有执行fallback属性指定的兜底方法;但是当产生了NullPointerException异常时,继续执行fallback属性指定的异常兜底方法。
在这里插入图片描述

热点key限流

热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的TOP K数据,并对其访问进行限制。如:

  • 商品ID为参数,统计一段时间内最常购买的商品ID并进行限制。
  • 用户ID为参数,统计一段时间内频繁访问的用户ID并进行限制。
    热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。

使用@sentinelresource注解配置热点资源名称,如果没有遵循配置的规则调用兜底方法deal_testHotKey :

package com.atguigu.springcloud.alibaba.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;


@RestController
@Slf4j
public class FlowLimitController
{
    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2)
    {
        return "------testHotKey";
    }
    public String deal_testHotKey (String p1, String p2, BlockException exception)
    {
        return "------deal_testHotKey,o(╥﹏╥)o";  //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
    }

}

设置热点的资源名称,就是@sentinelresource注解的value属性值,指定参数从第一个开始:
在这里插入图片描述
方法testHotKey里面的第一个参数QPS只要超过了每秒1次,马上降级处理,执行兜底方法deal_testHotKey :
在这里插入图片描述

热点之参数例外项

如果请求url的第一个参数等于5时,第一个参数的QPS阈值是200,当第一个参数不是5时,第一个参数的QPS阈值就是1:
在这里插入图片描述

系统规则

在这里插入图片描述cpu使用率:当cpu使用率超过阈值就触发系统保护。
RT:当单台机器上的所有入口流量的平均RT达到阈值就触发系统保护。
并发线程数:当单台机器上的所有入口流量的并发线程数达到阈值就触发系统保护。
入口QPS:当单台机器上的所有入口流量的QPS达到阈值就触发系统保护。

设置入口QPS等于1,当访问请求testA和testB时,每秒查询次数超过1时,都触发系统保护:
在这里插入图片描述

sentinel持久化规则

为了保证服务重启后,sentinel限流等规则依然有效,我们将sentinel配置规则持久化到nacos中保存,只要刷新某个微服务地址,sentinel控制台的流控规则就能看到,只要nacos里面的配置不删除,针对该微服务的流控规则持久有效

持久化步骤:

  • 添加sentinel-nacos数据源依赖:
<dependency>
   <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
  • 配置yml配置文件:
spring:
  cloud:
    sentinel:
		datasource:
		        ds1:
		          nacos:
		            server-addr: localhost:8848
		            dataId: cloudalibaba-sentinel-service
		            groupId: DEFAULT_GROUP
		            data-type: json
		            rule-type: flow
  • 在nacos配置中心中对服务进行配置:
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
spring.config.import属性是Spring Cloud中的一个重要配置属性,用于指定要加载的外部配置文件。 在Spring Cloud 2020中,spring.config.import属性用于导入外部的配置文件或配置文件的路径。它的使用方式是在应用的配置文件中添加该属性,并将要导入的配置文件路径作为属性值。 通过使用spring.config.import属性,可以将外部的配置文件注入到应用的配置环境中,从而实现动态地加载和管理应用的配置。 在Spring Cloud中,常见的配置文件格式有.properties和.yml等。通过spring.config.import属性,可以灵活地导入不同格式的配置文件,以满足不同应用的配置需求。 举个例子来说,假设我们有一个名为application.properties的配置文件,里面定义了一些应用的基本配置。同时,我们还有一个名为custom.properties的配置文件,里面定义了一些自定义的配置。如果我们希望将custom.properties文件的配置注入到应用中,我们可以在application.properties文件中添加spring.config.import属性,属性值为custom.properties的路径。 通过这种方式,应用启动时会自动加载并应用custom.properties文件中的配置。 总结来说,spring.config.import属性是Spring Cloud中用于导入外部配置文件的重要属性,可以动态地加载和管理应用的配置。在Spring Cloud 2020中,它仍然被广泛使用,用于实现配置的灵活性和可扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前撤步登哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值