SpringCloud入门教程(全集)_springcloud教程-程序员宅基地

技术标签: java  spring cloud  新手入门教程  分布式  

一、SpringCloud介绍

1.概念:Spring cloud 是一系列框架的有序集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。
2.优缺点(主要针对微服务)
(1)优点
①每个服务足够内聚,足够小,代码容易理解、开发效率提高;
②服务之间可以独立部署,微服务架构让持续部署成为可能;
③精细度业务控制,每个服务可以各自进行负载均衡扩展和数据库扩展,而且,每个服务可以根据自己的需要部署到合适的硬件服务器上;
④容易扩大开发团队,可以针对每个服务组件开发团队;
⑤提高容错性,一个服务的内存泄露并不会让整个系统瘫痪;
⑥每个服务可以用不同的技术开发,系统不会被长期限制在某个技术栈上。
(2)缺点
①开发人员要处理分布式系统的复杂性:
设计服务之间的通信机制,需要考虑分布式事务等问题;
涉及多个服务直接的自动化测试;
服务管理的复杂性(PS:现在docker的出现适合解决这个问题);
②对于业务数据和处理能力不是很明确的创业公司,不适合微服务架构模式,这时候最重要的是快速开发、快速部署、快速试错。
3.应用场景
适合业务复杂度较大、并发量较高的场景
4.SpringCloud与Dubbo对比
(1)架构完整度上,Dubbo只有服务注册/服务治理(zookeeper)两个模块,SpringCloud已经有二十多个模块,而且还在增加;
(2)SpringCloud的社区活跃度高;但Dubbo有高质量的中文文档
(3)Dubbo服务间的通讯采用的RPC,SpringCloud则是Http的Rset,RPC对业务接口有强依赖性,需要通讯双方有一样的接口,REST更为轻量化,只通过一个约定进行规范,不存在代码间的耦合。

二、SpringCloud子项目

SpringCloud的子项目一直在持续更新,这里我把SpringCloud中文网首页的直接放上来了。
详情参照:https://www.springcloud.cc/
1.Spring Cloud Config(Spring)
配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。
2.Spring Cloud Bus(Spring)
事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
3.Eureka(Netflix)
云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
4.Hystrix(Netflix)
熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
5.Zuul(Netflix)
Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
6.Archaius(Netflix)
配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。
7.Consul(HashiCorp)
封装了Consul操作,consul是一个服务发现与配置工具,与Docker容器可以无缝集成。
8.Spring Cloud for Cloud Foundry(Pivotal)
通过Oauth2协议绑定服务到CloudFoundry,CloudFoundry是VMware推出的开源PaaS云平台。
9.Spring Cloud Sleuth(Spring)
日志收集工具包,封装了Dapper和log-based追踪以及Zipkin和HTrace操作,为SpringCloud应用实现了一种分布式追踪解决方案。
10.Spring Cloud Data Flow(Pivotal)
大数据操作工具,作为Spring XD的替代产品,它是一个混合计算模型,结合了流数据与批量数据的处理方式。
11.Spring Cloud Security(Spring)
基于spring security的安全工具包,为你的应用程序添加安全控制。
12.Spring Cloud Zookeeper(Spring)
操作Zookeeper的工具包,用于使用zookeeper方式的服务发现和配置管理。
13.Spring Cloud Stream(Spring)
数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息。
14.Spring Cloud CLI(Spring)
基于 Spring Boot CLI,可以让你以命令行方式快速建立云组件。
15.Ribbon(Netflix)
提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。
16.Turbine(Netflix)
Turbine是聚合服务器发送事件流数据的一个工具,用来监控集群下hystrix的metrics情况。
17.Feign(OpenFeign)
Feign是一种声明式、模板化的HTTP客户端。
18.Spring Cloud Task(Spring)
提供云端计划任务管理、任务调度。
19.Spring Cloud Connectors(Spring)
便于云端应用程序在各种PaaS平台连接到后端,如:数据库和消息代理服务。
20.Spring Cloud Cluster(Spring)
提供Leadership选举,如:Zookeeper, Redis, Hazelcast, Consul等常见状态模式的抽象和实现。
21.Spring Cloud Starters(Pivotal)
Spring Boot式的启动项目,为Spring Cloud提供开箱即用的依赖管理。

三、Spring Cloud实战教程

这里我把部分代码传到github上了:https://github.com/tttalkR9/cloud/tree/cloud
或者直接从CSDN下载:https://download.csdn.net/download/tttalk/87362530

1.搭建Springboot父子项目

(1)首先创建一个File-New-Project选择Spring Initializr,JDK你们可以选择1.8的
在这里插入图片描述
(2)修改项目名称,我这里叫springcloud
在这里插入图片描述
(3)选择一下基础依赖,这里选择web-spring web,右上角可以修改springboot版本。
在这里插入图片描述
(4)然后一个springboot基础的父项目就创建成功了
在这里插入图片描述
(5)右击项目,New-Module,同样选择Spring Initializr,项目名称为user,跟刚刚不同的是,我们这里不选择web了,可以直接从父项目获得web的依赖
在这里插入图片描述

(6)修改父项目的pom文件,添加packaging的方式和modules

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springcloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud</name>
    <description>Demo project for Spring Boot</description>
    <!-- 建立父子 Module 依赖-->
    <packaging>pom</packaging>

    <modules>
        <module>user</module>
    </modules>
    <!-- 建立父子 Module 依赖 end-->
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>




</project>

(7)修改子项目的pom文件
将以下父项目的配置放到子项目的parent标签中,并将子项目中多余的依赖删掉

	<groupId>com.example</groupId>
    <artifactId>springcloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>

最后子项目的pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <!--修改子项目的parent依赖-->
        <!--<groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> &lt;!&ndash; lookup parent from repository &ndash;&gt;-->
        <groupId>com.example</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>user</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>user</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <!--注释掉多余的依赖,这些在父项目中已经存在-->
    <!--<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>-->

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

(8)添加controller文件,将properties文件替换成yml文件,项目结构如下:
在这里插入图片描述

(9)UserController.java和application.yml代码如下:
UserController.java

package com.example.user.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalTime;
import java.util.List;

@Controller
public class UserController {
    


    @RequestMapping(value="/test")
    @ResponseBody
    public String test(){
    
        return "test111";
    }

}

application.yml

server:
  port: 8030
spring:
  application:
    name: user-test

在这里插入图片描述
(10)右击启动UserApplication,进入http://localhost:8030/test,看到test111则验证成功
在这里插入图片描述

2.Feign/Ribbon/OpenFeign

(1)概念介绍

①Feign:声明式 REST 客户端–Feign 通过使用 JAX-RS(就是一种使用注解来实现 RESTful 的技术)或 SpringMVC 注解的装饰方式,生成接口的动态实现。旨在自动封装服务调用客户端,减少代码开发量

微服务中调用接口常见方式:
Ⅰ.通过http方式进行直接调用:即通过http+port的方式调用,可以使用okhttp、httpclient等进行接口的调用与解析,需要关注接口数据流、接口的数据转换等。
这种方式的缺点:接口调用有单点故障,对接口调用量大的情况没有熔断机制保护系统,防止雪崩效应。
Ⅱ.后端架设nginx服务做负载均衡。接口部署多路,此方式可避免单点故障但是要实现熔断机制等比较复杂,如:可以使用nginx+lua实现,或在代码层面实现限流机制算法。
Ⅲ.dubbo等其他服务治理架构方式请自行查阅资料。
Ⅳ.使用springcloud 家族的feign来实现接口调用。此方式可以解决单点故障和限流熔断,执行服务降级。

②Ribbon:实现负载均衡,从一个服务的多台机器中选择一台。

Nginx与Ribbon:
1)Nginx 是客户端所有请求统一交给 nginx,由 nginx 进行实现负载均衡请求转发,属于服务器端负载均衡。既请求由 nginx 服务器端进行转发。
2)Ribbon 是从 eureka 注册中心服务器端上获取服务注册信息列表,缓存到本地,然后在本地实现轮询负载均衡策略。
3)Nginx 适合于服务器端实现负载均衡 比如 Tomcat ,Ribbon 适合与在微服务中 RPC 远程调用实现本地服务负载均衡,比如 Dubbo、SpringCloud 中都是采用本地负载均衡。

Ribbon算法:实现Irule接口(choose方法),自带七种算法。
(1)RoundRobinRule:轮询;(默认)
(2)RandomRule:随机;
(3)AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
(4)WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule;
(5)RetryRule:先按照RoundRobinRule(轮询)策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务;
(6)BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
(7)ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择服务器。

③OpenFeign:Feign的强化版,内置了ribbon,简化了Eureka和Ribbon的使用。

(2)OpenFeign的基本使用方法

后面nacos的使用中会整合Feign+hystrix,有完整的代码实例,这里就只做介绍不贴代码
①首先,在pom.xml中引入openfeign和nacos的依赖
②在nacos注册中心端提供一个可以调用的接口,并在application中添加注释@EnableDiscoveryClient
③在nacosclient客户端入口建立一个接口,@FeignClient括号里填写的是注册中心的application-name(application.yml中配置的),然后在controller中调用这个客户端。application中添加注解@EnableDiscoveryClient和@EnableFeignClients

3.Hystrix

(1)服务雪崩解决方案

介绍Hystrix前,我们先了解一下它产生的背景–服务雪崩。
服务雪崩:一个服务失败(调用时间过长或失败),导致整条链路的服务都失败的情形,我们称之为服务雪崩。
解决服务雪崩的方式有下面几种:
1)应用扩容:加机器、升级硬件
2)流量控制: 限流(超出限定流量,返回让用户稍后再试)
3)缓存:将用户访问量大的数据放入缓存中,减少数据库访问。
4)服务降级:
①超时:当下游的服务响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度
②程序运行异常:当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户
5) 服务熔断:不调用该失败的服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用

而Hystrix的出现为微服务提供了服务降级与熔断的方案。

Hystrix 工作流程:
https://github.com/Netflix/Hystrix/wiki/How-it-Works

hystrix熔断触发:
当失败的调用到一定阀值,时间窗 5 秒内 20 次调用失败(默认),就会启动熔断机制。
此时断路器将会开启,当开启的时候,所有请求都不会进行转发
hystrix熔断恢复:
一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。

(2)Hystrix使用方法

有两种方式实现,一个是通过Feign内置的hystrix实现:
1、导入依赖spring-cloud-starter-hystrix
2、启动类开启hystrix支持@EnableHystrix
3、配置yml文件feign.hystrix.enabled=true
4、实现FeignClient接口或FallbackFactory接口
另一种是通过注解@HystrixCommand完成,属于方法级别的降级,后面nacos的使用有代码实例:
1、导入依赖spring-cloud-starter-hystrix
2、启动类开启hystrix支持@EnableHystrix
3、在方法上添加注解@HystrixCommand并配置降级后的方法

4.Eureka/Nacos

(1)常用的几种注册中心的区别

Ⅰ.Eureka与Zookeeper

①Eureka常用于SpringCloud项目,Zookeeper常用于dubbo
②ZooKeeper保证的是CP(一致、分区容错但不保证可用),Eureka保证的是AP(可用、分区容错但不保证一致)
ZooKeeper在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的
Eureka各个节点是平等关系,只要有一台Eureka就可以保证服务可用,而查询到的数据并不是最新的
③ZooKeeper有Leader和Follower角色,Eureka各个节点平等
④ZooKeeper采用过半数存活原则,Eureka采用自我保护机制解决分区问题
Eureka 自我保护机制:通过在Eureka Server配置如下参数,可启动保护机制。

eureka.server.enable-self-preservation=true

它的原理是,当Eureka Server节点在短时间内丢失过多的客户端时(可能发送了网络故障),那么这个节点将进入自我保护模式,不再注销任何微服务,当网络故障回复后,该节点会自动退出自我保护模式。

Ⅱ.Eureka与Nacos

参考:https://www.cnblogs.com/huangwentian/p/14504473.html
总的来说,目前Nacos的功能更强大一些,Nacos = Spring Cloud Eureka + Spring Cloud Config +Spring Cloud Bus,不需要为配置中心新增集群,不需要MQ实现就能实现配置的中心动态刷新,如果考虑引入阿里巴巴生态,是一个不错的选择;
但是Eureka起步比较早,属于Spring Cloud原生全家桶,短期内更加稳定一些。

Spring Cloud Config:是一种用来动态获取Git、SVN、本地的配置文件的一种工具,为分布式系统中的外部配置提供服务器和客户端支持。官方使用说明文档:https://www.springcloud.cc/spring-cloud-config.html

Springcloud Bus:是springcloud中消息总线。用来将分布式系统的节点与轻量级消息系统链接起来,在微服务系统中实现远端配置更新时通过广播形式通知所有客户端刷新配置信息,避免手动重启服务的工作。常结合RabbitMq和Kafka使用。官方文档参考:https://www.springcloud.cc/spring-cloud-bus.html

(2)Eureka搭建使用

Ⅰ.新建子模块eureka,这里新建的是注册中心,后面还需要个客户端去调用,建立方法在上面介绍过了
Ⅱ.在父项目的pom文件添加相关依赖,主要是springcloud的依赖和eureka的依赖等,注意版本。修改后文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springcloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud</name>
    <description>Demo project for Spring Boot</description>
    <!-- 建立父子 Module 依赖-->
    <packaging>pom</packaging>

    <modules>
        <module>user</module>
        <module>eureka</module>
    </modules>
    <!-- 建立父子 Module 依赖 end-->
    <properties>
        <java.version>11</java.version>
        <!--springCloud版本-->
        <spring-cloud.version>2021.0.1</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- eureka服务端的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

        <!--实现Eureka认证,保证Eureka的安全性-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <!--springBoot的核心依赖,也是继承,可插拔机制,只会加载配置的依赖-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>




</project>


Ⅲ.在子项目中新添加application.yml和修改EurekaApplication、pom.xml,项目结构如下
在这里插入图片描述
pom.xml:主要是修改parent标签

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>eureka</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml:

server:
  #服务端口
  port: 8899
eureka:
  client:
    service-url:
      # 需要进行消息注册的请求地址,不需要用户名密码验证
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
      #其他服务想注册到Eureka上需要添加用户名和密码进行认证
      defaultZone: http://${
    spring.security.user.name}:${
    spring.security.user.password}@${
    eureka.instance.hostname}:${
    server.port}/eureka
    #表示当前服务不会注册到服务中心
    register-with-eureka: false
    #表示当前服务不会拉取其他服务
    fetch-registry: false
  instance:
    hostname: localhost
spring:
  #登录Eureka所需的用户名和密码
  security:
    user:
      name: admin
      password: admin

EurekaApplication:添加@EnableEurekaServer注释,并重新配置spring security

package com.example.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@SpringBootApplication
//eureka的客户端配置
@EnableEurekaServer
public class EurekaApplication {
    

    public static void main(String[] args) {
    
        SpringApplication.run(EurekaApplication.class, args);
    }

    /*
     * Eureka会自动配置CSRF防御机制,spring security认为post,put and delete http methods 都是有风险的,
     * 如果这些method发送过程中没有带上CSRF token,会被直接拦截并返回403 forbidden
     * */
    @EnableWebSecurity
    static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            http.csrf().ignoringAntMatchers("/eureka/**");
            super.configure(http);
        }
    }
}


Ⅳ.访问Eureka注册中心:http://localhost:8899/,输入admin/admin,可以看到下面的界面则搭建成功了,application为空代表还没有应用注册了这个服务

Ⅴ.搭建Eureka客户端eurekaClient,新建module同上,新增EurekaclientApplication .java和application.yml、pom.xml
在这里插入图片描述

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>eurekaclient</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eurekaclient</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml:

server:
  # 该服务的端口号
  port: 8900
eureka:
  client:
    service-url:
      #此服务注册的地址,需要跟eureka模块中地址一致,注意这里用server.port是8900不是8899不要用变量
      #defaultZone:  http://${eureka.instance.hostname}:8899/eureka
      defaultZone: http://${
    spring.security.user.name}:${
    spring.security.user.password}@localhost:8899/eureka
  instance:
    hostname: localhost



spring:
  application:
    #注册到注册中心的名称,注意不能使用“_”
    name: eureka-client-8900
  # 指定用户名和密码,其他服务想注册到Eureka上需要添加用户名和密码进行认证
  security:
    user:
      name: admin
      password: admin
  cloud:
    inetutils:
      timeout-seconds: 10


EurekaclientApplication .java

package com.example.eurekaclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

@SpringBootApplication
//eureka的客户端配置
@EnableEurekaServer
// 开启EnableWebSecurity组件
@EnableWebSecurity
public class EurekaclientApplication {
    

    public static void main(String[] args) {
    
        SpringApplication.run(EurekaclientApplication.class, args);
    }

}


Ⅵ.启动,http://localhost:8900/,登录admin/admin,发现服务注册成功了,如果有报错,查看后面的报错解决模块。
在这里插入图片描述

(3)Nacos搭建使用

a.Nacos单机+openFiegn+Hystrix

Ⅰ.下载源码压缩包,nacos-server-XXX.zip 或者 nacos-server-XXX.tar.gz,我这里是windows版本,下了个nacos-server-1.4.3.zip。
下载地址:https://github.com/alibaba/nacos/releases
Ⅱ.启动,解压后找到startup.cmd。修改startup.cmd(由于我们没配置集群,需要修改为单机启动),将set MODE = "cluster"替换为

rem set MODE="cluster"
set MODE="standalone"

启动成功后访问:http://localhost:8848/nacos,用户名密码:nacos/nacos在这里插入图片描述Ⅲ.同样的我们也需要创建nacos的注册中心和客户端。我这里直接在刚刚创建好的父子项目中添加模块nacos作为注册中心,项目结构如下
在这里插入图片描述

Ⅳ.相关文件代码如下,注意要把之前Eureka的依赖都注释掉,会和nacos的依赖有冲突
nacos注册中心的pom.xml:引入父模块

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>nacos</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


父项目的pom.xml:添加nacos的依赖,同时注释掉eureka依赖,服务发现时两者jar包会冲突;同时添加openfeign依赖及配套的loadbalancer,同时openfeign内置了负载均衡的ribbon,所以还需要把nacos中的ribbon注释掉;添加hystrix依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springcloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud</name>
    <description>Demo project for Spring Boot</description>
    <!-- 建立父子 Module 依赖-->
    <packaging>pom</packaging>

    <modules>
        <module>user</module>
        <module>eureka</module>
		<module>nacos</module>
    </modules>
    <!-- 建立父子 Module 依赖 end-->
    <properties>
        <java.version>11</java.version>
        <!--springCloud版本-->
        <spring-cloud.version>2021.0.1</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- eureka服务端的依赖-->
       <!-- <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

        &lt;!&ndash;实现Eureka认证,保证Eureka的安全性&ndash;&gt;
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>-->

        <!--Nacos依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.6.RELEASE</version>
            <!--openfeign里面有复载均衡,所以这里必须注释掉,不然会冲突-->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

        <!--openfeign依赖 Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--openfiegn使用时需要搭配loadbalancer负载均衡-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

        <!--hystrix依赖 spring-cloud-starter-hystrix已经被废弃了-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <!--springBoot的核心依赖,也是继承,可插拔机制,只会加载配置的依赖-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>



application.yml

server:
  port: 8070
spring:
  application:
    name: nacos-provider
  cloud:
    # nacos服务地址
    nacos:
      server-addr: 127.0.0.1:8848

NacosApplication.java::主要是添加@EnableDiscoveryClient、@EnableHystrix注释

package com.example.nacos;

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

@SpringBootApplication
//实现服务注册
@EnableDiscoveryClient
//开启hystrix支持
@EnableHystrix
public class NacosApplication {
    

    public static void main(String[] args) {
    
        SpringApplication.run(NacosApplication.class, args);
    }

}


NacosController.java

package com.example.nacos.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalTime;

@RestController
public class NacosController {
    

    @GetMapping("invoke")
    //配置降级方法
    @HystrixCommand(fallbackMethod = "timeOutInvoke",commandProperties = {
    
            //设置服务调用超时10秒时触发服务降级
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "10000")
    })
    public String invoke() throws InterruptedException {
    
        //模拟服务超时
        //Thread.sleep(10000);
        return LocalTime.now() + ":invoke";

    }

    public String timeOutInvoke() {
    
        return "系统繁忙,请稍后再试";

    }
}


Ⅴ.启动后,再去网址http://localhost:8848/nacos上查看服务管理-服务列表模块,发现注册中心成功了
在这里插入图片描述
Ⅵ.同样我们需要一个客户端去调用,新建一个nacosclient模块,项目结构如下:
在这里插入图片描述
Ⅶ.相关文件代码
pom.xml:引入父模块依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>nacosclient</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacosclient</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>



application.yml

server:
  port: 8071
spring:
  application:
    name: nacos-consumer
  cloud:
    # nacos服务地址
    nacos:
      server-addr: 127.0.0.1:8848

NacosClientApplication.java:主要是添加@EnableDiscoveryClient、@EnableFeignClients注释

package com.example.nacosclient;

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

@SpringBootApplication
//实现服务注册
@EnableDiscoveryClient
//使用feign客户端
@EnableFeignClients
public class NacosclientApplication {
    

    public static void main(String[] args) {
    
        SpringApplication.run(NacosclientApplication.class, args);
    }

}


NacosClientController.java:添加了FeignClient(后面会介绍这个组件),提供程序入口

package com.example.nacosclient.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalTime;

@RestController
public class NacosClientController {
    

    @Autowired
    private ProviderFeign providerFeign;

    @GetMapping("/test")
    public String test() {
    
        return providerFeign.invoke();

    }

}

@FeignClient("nacos-provider")
 interface ProviderFeign {
    

    @GetMapping("invoke")
    String invoke();

}


Ⅷ.启动后再次查看,服务创建成功了,进入http://localhost:8071/test,发现有输出,则调用成功
在这里插入图片描述
在这里插入图片描述
Ⅸ.修改NacosController,将"Thread.sleep(10000);"的注释放开

package com.example.nacos.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalTime;

@RestController
public class NacosController {
    

    @GetMapping("invoke")
    //配置降级方法
    @HystrixCommand(fallbackMethod = "timeOutInvoke",commandProperties = {
    
            //设置服务调用超时10秒时触发服务降级
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "10000")
    })
    public String invoke() throws InterruptedException {
    
        //模拟服务超时
        Thread.sleep(10000);
        return LocalTime.now() + ":invoke";

    }

    public String timeOutInvoke() {
    
        return "系统繁忙,请稍后再试";

    }
}

重启nacos和nacosclient,再次访问,发现服务降级生效了
在这里插入图片描述

b.Nacos集群+nginx负载均衡

①nacos集群数据库配置(用于数据持久化)–目前版本的依赖mysql
1)为保证数据一致性,集群需使用公共数据库来保存服务管理中的数据,目前支持mysql数据库。
mysql搭建可以参考这篇文章:https://blog.csdn.net/tttalk/article/details/121929642?spm=1001.2014.3001.5501
2)在解压后的文件的\nacos\conf\目录下找到mysql数据库建表文件nacos-mysql.sql,在mysql中执行以创建一系列配置表。进入mysql后选择数据库后执行source /root/mysql/nacos-mysql.sql
在这里插入图片描述
可以看到这些表创建成功了
在这里插入图片描述

3)在\nacos\conf目录下的application.properties文件最下方添加数据库连接配置,注意更改ip密码啥的

### mysql配置
spring.datasource.platform = mysql
db.num = 1
db.url.0 = jdbc:mysql://192.168.124.128:3306/mysql?characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
db.user=root
db.password=Ppp11111111!

②集群节点配置
1)将\nacos\conf\目录下的cluster.conf.example文件拷贝一份,重命名为cluster.conf,打开添加如下配置,注意!这里一定要写真实的IP(cmd ipconfig查看),不能写localhost,127.0.0.1,不然集群启动会找不到别的实例,导致无法选举出leader

192.168.124.1:8841
192.168.124.1:8842
192.168.124.1:8843

2)修改启动文件startup.cmd,还记得我们之前修改成了单机么,改回来即可,rem是注释的意思。将set MODE="standalone"注释掉,并把set MODE="cluster"的注释取消。

set MODE="cluster"
rem set MODE="standalone"

3)将调整后的nacos文件拷贝三份
将拷贝后的文件分别命名为 nacos8841、nacos8842、nacos8843
在这里插入图片描述

4)分别在\nacos\conf目录下的application.properties文件中修改对应的端口号server.port=8848为:8841、8842、8843(nacos8841的application.properties端口改为8841,以次类推)
在这里插入图片描述
③Nginx配置
1)Nginx下载:http://nginx.org/en/download.html,选择stable版本
在这里插入图片描述
2)解压后修改\nginx-1.20.2\conf目录的nginx.conf文件。
找到server和location模块,在server前添加upstream,修改location,添加下面的代码:

#集群的3个节点
upstream nacos {
    
    server 127.0.0.1:8841 weight=2;
    server 127.0.0.1:8842 weight=2;
    server 127.0.0.1:8843 weight=2;
}
location / {
    
           # root   html;
           # index  index.html index.htm;
           #代理
           proxy_pass http://nacos/;
       }

添加后效果如图:
在这里插入图片描述
④启动测试
分别启动nacos8841、nacos8842、nacos8843下的startup.cmd文件,并启动nginx.exe。
登录location中配置的路径:http://localhost/nacos,nacos/nacos,登录成功。
在这里插入图片描述
刷新后,端口变成了8842,说明利用nginx已经实现了负载均衡
在这里插入图片描述

c.Nacos配置中心使用

Ⅰ.首先了解SpringBoot 配置文件读取顺序:
1.bootstrap.yml(用来程序引导时执行,应用于更加早期配置信息读取)
2.config/application.properties(项目根目录中config目录下)
3.config/application.yml
4.application.properties(项目根目录下)
5.application.yml
6.resources/config/application.properties(项目resources目录中config目录下)
7.resources/config/application.yml
8.resources/application.properties(项目的resources目录下)
9.resources/application.yml
当配置信息冲突时,先读取到的优先级高,也就是properties>yaml>yml

Ⅱ.使用示例

使用Nacos配置中心须知:注意nacos版本、springBoot版本和SpringCloud版本的对应,否则配置不生效。比如Nacos用的2.1版本,Cloud可以用2.2.8.RELEASE,SpringBoot可以用2.3.12.RELEASE

(1)在nacos中增加一个新的命名空间,然后记录其命名空间的id
在这里插入图片描述
(2)将之前的properties/yml从项目中删除,添加bootstrap.yml,配置对应环境的命名空间

spring:
  application:
    name: my-service
  cloud:
    nacos:
      username: nacos
      password: nacos
	  #注册中心的命名空间,视情况添加
      #discovery:
      #  namespace: 6c20adf7-1b7e-45ba-9349-a999c3b84229
      config:
        file-extension: properties
        contextPath: /nacos
  profiles:
    active: dev


---
spring:
  profiles: dev
  cloud:
    nacos:
      server-addr: 开发环境nacos的ip:8848
      config:
        namespace: 8c5a3768-7527-452b-ae6d-42ad31de44ce


---
spring:
  profiles: test
  cloud:
    nacos:
      server-addr: 测试环境nacos的ip:8848
      config:
        namespace: 0c262216-1468-424c-adf4-085b1dff01eb

(3)将原来的properties/yml文件放到nacos配置中心,注意命名要跟application的name一致,我这里叫my-service.yml

在这里插入图片描述

5.Zuul

(1)Zuul概念介绍

①概念功能:Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。
②实现:Zuul的核心是一系列的filters,使用Zuul后,在集群中将一对多的关系转变成了一对一的关系。
③优点:可以在网关层进行数据合法校验、权限认证、负载均衡等统一处理;客户端只须跟网关交互,减少了客户端与各个微服务之间的交互次数

(2)使用教程

1.Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用。
使用步骤如下:
①添加依赖
②启动类添加注释@EnableZuulProxy
③配置application.yml路由分发
2.Zuul + Nacos
代码实例
(1)在父项目的 pom.xml引入如下依赖

 <!--zuul依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        <!--ribbon-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>


        <!--Nacos依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.6.RELEASE</version>
            <!--openfeign里面有复载均衡,所以这里必须注释掉,不然会冲突-->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

(2)naco子项目代码,我之前做过,直接复用之前的代码。注意下application的name:nacos-provider后面zuul配置要用到
在这里插入图片描述
然后我这边在controller中加了个方法以供测试

@GetMapping("/gateway")
    @ResponseBody
    public String gateway() {
    
        return "gateway test";

    }

(3)创建网关模块,结构如下:
在这里插入图片描述
pom文件就引用父模块的依赖就行
ZuulConfiguration.java这个配置用来解决版本冲突问题

package com.example.gateway.config;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.web.ZuulController;
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Fix for Zuul configuration with Spring Boot 2.5.x + Zuul - "NoSuchMethodError: ErrorController.getErrorPath()":
 */
@Configuration
public class ZuulConfiguration {
    
  /**
   * The path returned by ErrorController.getErrorPath() with Spring Boot < 2.5
   * (and no longer available on Spring Boot >= 2.5).
   */
  private static final String ERROR_PATH = "/error";
  private static final String METHOD = "lookupHandler";

  /**
   * Constructs a new bean post-processor for Zuul.
   *
   * @param routeLocator    the route locator.
   * @param zuulController  the Zuul controller.
   * @param errorController the error controller.
   * @return the new bean post-processor.
   */
  @Bean
  public ZuulPostProcessor zuulPostProcessor(@Autowired RouteLocator routeLocator,
                                             @Autowired ZuulController zuulController,
                                             @Autowired(required = false) ErrorController errorController) {
    
    return new ZuulPostProcessor(routeLocator, zuulController, errorController);
  }

  private enum LookupHandlerCallbackFilter implements CallbackFilter {
    
    INSTANCE;

    @Override
    public int accept(Method method) {
    
      if (METHOD.equals(method.getName())) {
    
        return 0;
      }
      return 1;
    }
  }

  private enum LookupHandlerMethodInterceptor implements MethodInterceptor {
    
    INSTANCE;

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    
      if (ERROR_PATH.equals(args[0])) {
    
        // by entering this branch we avoid the ZuulHandlerMapping.lookupHandler method to trigger the
        // NoSuchMethodError
        return null;
      }
      return methodProxy.invokeSuper(target, args);
    }
  }

  private static final class ZuulPostProcessor implements BeanPostProcessor {
    

    private final RouteLocator routeLocator;
    private final ZuulController zuulController;
    private final boolean hasErrorController;

    ZuulPostProcessor(RouteLocator routeLocator, ZuulController zuulController, ErrorController errorController) {
    
      this.routeLocator = routeLocator;
      this.zuulController = zuulController;
      this.hasErrorController = (errorController != null);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
      if (hasErrorController && (bean instanceof ZuulHandlerMapping)) {
    
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ZuulHandlerMapping.class);
        enhancer.setCallbackFilter(LookupHandlerCallbackFilter.INSTANCE); // only for lookupHandler
        enhancer.setCallbacks(new Callback[] {
    LookupHandlerMethodInterceptor.INSTANCE, NoOp.INSTANCE});
        Constructor<?> ctor = ZuulHandlerMapping.class.getConstructors()[0];
        return enhancer.create(ctor.getParameterTypes(), new Object[] {
    routeLocator, zuulController});
      }
      return bean;
    }
  }
}

MyZuulFilter 过滤器用来统一处理逻辑

package com.example.gateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import javax.servlet.http.HttpServletRequest;

public class MyZuulFilter extends ZuulFilter {
    
    @Override
    public String filterType() {
    
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
    
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
    
        return true;
    }

    @Override
    public Object run() throws ZuulException {
    
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        System.out.println("filter test");
        return null;
    }
}

application.yml配置端口等,注意这里的serviceId是刚刚的nacos-provider

server:
  port: 8008

spring:
  application:
    name: gateway

cloud:
  nacos:
    discovery:
      server-addr: 127.0.0.1:8848
zuul:
  routes:
    auth:
      path: /auth/**
      serviceId: nacos-provider

GatewayApplication添加filter,并增加注释

package com.example.gateway;

import com.example.gateway.filter.MyZuulFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableZuulProxy
public class GatewayApplication {
    

    @Bean
    public MyZuulFilter preRequestLogFilter() {
    
        return new MyZuulFilter ();
    }

    public static void main(String[] args) {
    
        SpringApplication.run(GatewayApplication.class, args);
    }

}

3.测试,可以看到下面两个网址效果一致,并且filter生效了
http://localhost:8070/gateway
http://localhost:8008/auth/gateway
在这里插入图片描述
在这里插入图片描述

6.SpringSecurity/SpringCloudSecurity

(1)概念介绍

1)Spring Security
SpringSecurity是基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。 核心功能主要包括:认证 、授权 、攻击防护等。 其核心就是一组过滤器链。

2)Spring Cloud Security
Spring Cloud Security是基于spring security的安全工具包,在分布式项目中为你的应用程序添加安全控制。
主要功能包括单点登录,令牌中继和令牌交换等。

我这里主要介绍的springsecurity的使用,cloud security的用法基本一致

(2)使用教程

1)SpringSecurity

1.去官网查看文档,了解下你所使用的版本特性。网址:https://spring.io/projects/spring-security,从Projects-View all projects点进去,找到Spring Security,然后查看相关文档。
在这里插入图片描述
2.SpringSecurity实战
(1)简单实现用户认证功能
①首先创建一个springboot的web项目,并引入springsecurity的依赖
pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springsecurity</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springsecurity</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--spring security 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!--页面模板-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

②创建controller和config等类,controller提供入口,config中配置用户名和密码
在这里插入图片描述
TestController:

package com.example.springsecurity.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
    

    @RequestMapping(value="/hello")
    @ResponseBody
    public String hello(){
    
        return "hello";
    }
}

WebSecurityConfig.java:

package com.example.springsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    

        //用户名密码设置  这里使用的在内存中定义,可以把这里改成通过数据库查询
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("admin123")).roles("admin");

    }



}

③验证:进入http://localhost:8080/hello,会弹出登录页面,输入我们设置的用户名及密码即可
在这里插入图片描述
最终结果如下:
在这里插入图片描述
(2)在此基础上,通过角色来控制用户权限。这里使用了自定义的登陆页面,并将用户名密码的配置通过userDetailsService实现。
代码如下:
在这里插入图片描述
①java类
WebSecurityConfig.java:

package com.example.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.List;

@Configuration
//@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    

        //用户名密码设置  可以把这里改成通过数据库查询
        // 这里使用的在内存中定义
        /*auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("admin123")).roles("admin");*/
        //这里使用自定义类的方式
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    

        //自定义登录页面
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                //配置接收的用户名密码参数
                .usernameParameter("username")
                .passwordParameter("password")
                //登陆成功后调转的请求
                .defaultSuccessUrl("/index").permitAll();

        //关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
        http.csrf().disable();

        http.authorizeRequests()
                //permitAll()的意思就是放行,这里除了登录页放行,其他的都需要认证
                .antMatchers("/","/login").permitAll()
                //赋权,指定目录下的页面需要特定角色 这里我把admin权限设置的比较高,也能访问role1的页面
                .antMatchers("/admin/2").hasAnyAuthority("admin")
                .antMatchers("/role1/1").hasAnyAuthority("admin,role1");

    }
}


TestController.java

package com.example.springsecurity.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
    

    @RequestMapping(value="/hello")
    @ResponseBody
    public String hello(){
    
        return "hello";
    }

    @RequestMapping("role1/1")
    public String role1Page(){
    
        return "role1/1";
    }

    @RequestMapping("admin/2")
    public String adminPage(){
    
        return "admin/2";
    }

    @RequestMapping(value="/")
    public String test1(){
    
        return "login";
    }

    @RequestMapping("login")
    public String login(){
    
        return "login";
    }

    @RequestMapping("index")
    public String index(){
    
        System.out.println("logging...");
        return "index";
    }
}

UserDetailService.java

package com.example.springsecurity.service;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("userDetailsService")
public class UserDetailService implements UserDetailsService {
    
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    
        //这里设定角色
        /*List<GrantedAuthority> authList = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        return new User("admin",new BCryptPasswordEncoder().encode("admin123"),authList);*/
        List<GrantedAuthority> authList = AuthorityUtils.commaSeparatedStringToAuthorityList("role1");
        return new User("role1",new BCryptPasswordEncoder().encode("123"),authList);
    }
}

②html类
index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    登录成功!
    <br/>
    <a href="/role1/1">role1 </a>
    <br/>
    <a href="/admin/2">admin </a>
</body>
</html>

login.thml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
    用户名或密码不正确。
</div>

<form th:action="@{/login}" method="post">
    <div><label> 用户名 : <input type="text" name="username"/> </label></div>
    <div><label> 密码::<input type="password" name="password"/> </label></div>
    <div><input type="submit" value="提交"/></div>
</form>
</body>
</html>


1.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
        role1 Page
</body>
</html>

2.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
        admin Page
</body>
</html>

③其他(pom文件没有修改)
application.yml

spring:
    thymeleaf:
      #模板的模式,支持 HTML, XML TEXT JAVASCRIPT
      mode: HTML5
      #开发配置为false,避免修改模板还要重启服务器
      cache: false


④测试
第一步:UserDetailService 中使用role1的用户名及密码
在这里插入图片描述
然后登录
在这里插入图片描述
点击role1,可以进入;但是点击admin,显示403,没权限访问
在这里插入图片描述
在这里插入图片描述
第二步:UserDetailService 中使用admin的用户名及密码,修改如下图
在这里插入图片描述
然后重启,按照上面的步骤再操作一次(用户名密码要用admin的),admin的页面可以进去。
在这里插入图片描述

2) SpringSecurity + Oauth2 + redis

1.Oauth2介绍
(1)概念:是用于REST/APIs的代理授权框架,它指定了将长期的令牌替换为短期的令牌的策略;
(2)特性:基于令牌Token的授权,在无需暴露用户密码的情况下,使应用有获取用户资源的权限;
(3)使用场景:常用于APP、第三方服务的授权,如使用微信授权第三方应用系统;
(4)有四种模式:密码模式(不推荐)、授权码模式(常用)、简化模式、客户端模式。

2.SpringCloudSecurity + Oauth2实践
(1)不使用redis的用法
pom.xml:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--spring security 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!--页面模板-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.3.RELEASE</version>
        </dependency>

        <!--高版本JDK缺少的jax相关jar包(大于1.8)-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

        <!-- redis  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

授权服务器AuthorizationServerConfig :

package com.example.testsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    

    // 该对象用来支持 password 模式
    @Autowired
    AuthenticationManager authenticationManager;

    // 该对象用来将令牌信息存储到内存中
    @Autowired(required = false)
    TokenStore inMemoryTokenStore;

    // 该对象将为刷新token提供支持
    @Autowired
    UserDetailsService userDetailsService;

    // 指定密码的加密方式
    @Bean
    PasswordEncoder passwordEncoder() {
    
        // 使用BCrypt强哈希函数加密方案(密钥迭代次数默认为10)
        return new BCryptPasswordEncoder();
    }

    // 该对象用来将令牌信息存储到Redis中
    @Autowired
    RedisConnectionFactory redisConnectionFactory;


    // 配置 password 授权模式
    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
    
        clients.inMemory()
                .withClient("client1")
                // 该client允许的授权类型 authorization_code,password,refresh_token
                .authorizedGrantTypes("authorization_code","password", "refresh_token")
                .accessTokenValiditySeconds(1800) // 配置access_token的过期时间
                .resourceIds("hello") //配置资源id
                .scopes("all")
                .secret(passwordEncoder().encode("123"));
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    
        endpoints.tokenStore(inMemoryTokenStore) //配置令牌的存储(这里存放在内存中)
        //endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory)) //配置令牌的存储(这里存放在redis中)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
    
        // 表示支持 client_id 和 client_secret 做登录认证
        security.allowFormAuthenticationForClients();
    }
}


资源服务器ResourceServerConfig :

package com.example.testsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
    

        resources.resourceId("hello") // 配置资源id,这里的资源id和授权服务器中的资源id一致
                .stateless(true); // 设置这些资源仅基于令牌认证
    }

    // 配置 URL 访问权限
    @Override
    public void configure(HttpSecurity http) throws Exception {
    
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/role1/**").hasRole("role1")
                .anyRequest().authenticated();
    }
}

security配置:

package com.example.testsecurity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
    
        return super.authenticationManagerBean();
    }

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
    

        return super.userDetailsService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(new BCryptPasswordEncoder().encode("123"))
                .roles("admin")
                .and()
                .withUser("role1")
                .password(new BCryptPasswordEncoder().encode("123"))
                .roles("role1");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
        http.antMatcher("/oauth/**").authorizeRequests()
                //放行授权的请求
                .antMatchers("/oauth/**").permitAll()
                //关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
                .and().csrf().disable();
    }
}

测试入口HelloController:

package com.example.testsecurity.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    

    @GetMapping("/admin/hello")
    public String admin() {
    
        return "hello admin";
    }

    @GetMapping("/role1/hello")
    public String user() {
    
        return "hello role1";
    }

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

测试:注意这里必须用postman,首先要获取access_token
在这里插入图片描述
然后通过accesstoken可以访问对应的资源,其中除role1的资源外其他资源均可访问
http://localhost:8080/hello?access_token=3645490f-9822-4adb-8dc7-1b59c5b8cf8c
http://localhost:8080/admin/hello?access_token=3645490f-9822-4adb-8dc7-1b59c5b8cf8c
http://localhost:8080/role1/hello?access_token=3645490f-9822-4adb-8dc7-1b59c5b8cf8c

(2)使用redis:
1.首先我们了解下使用redis储存token的好处:
token具有时效性,redis有过期功能处理有优势;
可以在多个服务之间实现令牌共享

redis安装使用参考:https://blog.csdn.net/tttalk/article/details/122042206?spm=1001.2014.3001.5501

2.使用代码
application.yml:

spring:
  redis:
    timeout: 6000ms
    # redis数据库索引(默认为0)
    database: 0
    host: 192.168.124.128
    port: 6379
    lettuce:
      pool:
        max-active: 1000 #连接池最大连接数(使用负值表示没有限制)
        max-idle: 10 #连接池中的最大空闲连接
        min-idle: 3 #连接池中的最小空闲连接
        max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
    password: 123

在AuthorizationServerConfig中将令牌放到redis中:
在这里插入图片描述

重新进行access_token的请求,可以看到已经存在redis中了
在这里插入图片描述

7.Spring Cloud Alibaba Sentinel

参考我最近写的这一篇Sentinel使用教程

8.Spring Cloud Alibaba Seata

参考我最近写的这一篇Seata使用教程

四、SpringCloud常见报错解决

1.SpringBoot和SpringCloud版本对应错误

(1)Failed to introspect Class [org.springframework.cloud.context.properties.ConfigurationPropertiesBean
解决方式:查看官网地址https://start.spring.io/actuator/info
把拿到的字符串去https://www.bejson.com/上面解析一下,结果如下
在这里插入图片描述
可以看到我用的springboot2.6.3版本对应的springcloud版本为2021.0.1

2.Eureka常见报错解决

(1) Request execution failure with status code 403; retrying on another server if available
Cannot execute request on any known server
报错原因:Eureka会自动配置CSRF防御机制,spring security认为post,put and delete http methods 都是有风险的,
如果这些method发送过程中没有带上CSRF token,会被直接拦截并返回403 forbidden
解决:见SpringCloudEurekaServerApplication,继承WebSecurityConfigurerAdapter放行eureka相关csrf 请求

3.Nacos常见报错解决(windows版)

(1)启动服务时报错:java.lang.IllegalArgumentException: db.num is null
报错原因:默认集群启动,而集群没有配置
解决:修改为单机启动。修改startup.cmd,将set MODE = "cluster"替换为

rem set MODE="cluster"
set MODE="standalone"

4.Hystrix常见报错解决

(1)ServerPropertiesAutoConfiguration.class] cannot be opened because it does not exist
报错原因:依赖有问题
解决:spring-cloud-starter-hystrix已经被废弃了,现在应该使用spring-cloud-starter-netflix-hystrix。
如何解决类似问题呢?我这里给个基本步骤
①进入maven网址:https://mvnrepository.com/,搜索框输入hystrix。可以看到有两个依赖。
在这里插入图片描述
②点进第二个,可以看到deprecated标识,代表已经废弃了,现在使用spring-cloud-starter-netflix-hystrix
在这里插入图片描述
(2)注入Feign接口时提示:Could not autowire. There is more than one beans of ‘UserFeign’ type
在这里插入图片描述
错误原因:这是IDEA工具 中spring 插件的问题
解决方法:settings -editor -Inspections- Spring-Spring Core-Code-Autowiring for Bean Class >>>>>>>> 右侧:severity 修改成warning 保存即可。
在这里插入图片描述

5.SpringSecurity报错解决

(1)There is no passwordEncoder mapped for the id “null”
原因:Spring Security5.0开始改变了默认的密码格式,需要要将前端传过来的密码进行某种方式加密,官方推荐的是使用bcrypt加密方式
如:

auth.inMemoryAuthentication()
       .withUser("admin").password("admin123").roles("admin")
   改为
   auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("admin123")).roles("admin");

6.Zuul报错解决

(1)NoSuchMethodError: org.springframework.boot.web.servlet.error.ErrorController.getErrorPath
原因: spring-boot 较新的版本,从 ErrorController 中删除了 getErrorPath() API,但spring-cloud-starter-netflix-zuul 2.2.6.RELEASE 仍然调用此 API 并导致此错误。
解决:换较低版本的spring-boot版本或者创建BeanPostProcessor拦截 lookupHandler 方法的调用,参考我在zuul使用教程中写的ZuulConfiguration,然后在启动类引用一下就可以。

各位看官如果你已经从前看到最后了,点赞收藏一下不过分吧。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/tttalk/article/details/122686591

智能推荐

Mybatis分页插件pagehelper的使用_mybatis分页插件-pagehelper-程序员宅基地

文章浏览阅读1.1k次。@Configurationpublic class MybatisConfig { @Bean public PageHelper pageHelper() { PageHelper pageHelper = new PageHelper(); Properties properties = new Properties(); p..._mybatis分页插件-pagehelper

网页中导入本机Excel,并用bootstrap table 显示_excel 转 bootstrap table-程序员宅基地

文章浏览阅读1.7k次。@ [TOC](网页中导入本机Excel,并用bootstrap table 显示)html <div class="table-box" style="margin: 5px"> <div class="toolbar" style="width: 100%; margin-left: 20px; display: flex; flex-direction:..._excel 转 bootstrap table

平衡树·从BST到treap到fhq-treap_码量最小的平衡树-程序员宅基地

文章浏览阅读281次。挖坑_码量最小的平衡树

Vue中的六种过渡动画解析_vue 过渡动画类型-程序员宅基地

文章浏览阅读1.3k次。过渡CSS的类名:v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。 v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。 v-enter-to:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生..._vue 过渡动画类型

TensorFlow中的梯度裁剪(Gradient Clipping)_tensorflow梯度裁剪-程序员宅基地

文章浏览阅读1.1w次,点赞3次,收藏30次。梯度爆炸是深度学习中十分常见的现象,有时会导致寻优过程不收敛,或者算出来的结果干脆直接溢出,例如在Python里都是Nan,使迭代无法继续下去。TensorFlow里提供了一系列简单可行的梯度裁剪函数,方便我们对超过阈值的梯度值进行规约,使优化算法相对更加数值稳定。 TensorFlow里提供的几个Gradient Clipping函数都是以clip_by开头,分别是tf.clip_by_nor..._tensorflow梯度裁剪

Chromium书签同步无法连接到服务器的解决办法-程序员宅基地

文章浏览阅读438次。问题:书签同步时可能会无法连接服务器。解决办法:只需将浏览器语言设为英文即可顺利连接。转载于:https://www.cnblogs.com/jiels/archive/2010/03/08/1681133.html..._vr chromium无法连接

随便推点

CL.exe-程序员宅基地

文章浏览阅读42次。CL.exe CL.exe 是控制 Microsoft C 和 C++ 编译器与链接器的 32 位工具。编译器产生通用对象文件格式 (COFF) 对象 (.obj) 文件。链接器产生可运行文件 (.exe) 或动态链接库文件 (DLL)。注意,全部编译器选项都区分大写和小写。若要编译...

R语言中Fisher判别的使用方法-程序员宅基地

文章浏览阅读4.3k次,点赞4次,收藏20次。最近编写了Fisher判别的相关代码时,需要与已有软件比照结果以确定自己代码的正确性,于是找到了安装方便且免费的R。这里把R中进行Fisher判别的方法记录下来。1. 判别分析与Fisher判别不严谨但是通俗的说法,判别分析(Discriminant Analysis)是一种多元(多个变量)统计分析方法,它根据样本的多个已知变量的值对样本进行分类的方法。一般来说,判别分析由两个阶..._r语言试对胃癌检验的生化指标值用fisher 判别的方法进行判别归类.

软考证书如何评职称?_软考评职称-程序员宅基地

文章浏览阅读1.9w次,点赞14次,收藏33次。文章目录1. 什么是职称2. 软考3. 软考和职称的关系3.1 软考3.2 职称资格3.3 职称3.4 三者之间的关系3.5 软考证书改版后的变化1. 什么是职称我们在拿到软考证书后,很多人最关心的一个问题就是关于职称评聘问题,今天就以软考证书如何申请评中级职称及职称申请流程的详细介绍,希望可以帮到大家。所谓职称其实是一个专业技术职务,每个单位根据上级单位要求设置相应比例的高、中、初级专业..._软考评职称

使用SaaS鉴权用户,以加快应用开发_用saas登录鉴权的-程序员宅基地

文章浏览阅读3.1k次。新一代的云服务不再要求应用开发者编写服务器端代码。几乎所有的应用都依赖服务器端的业务逻辑,回调为App 提供服务的API。应用依赖服务器来分析,存储数据,通讯,并与现有系统集成。虽然通用服务如分析一般都外包给专业服务商,许多开发者还是需要编写服务器端代码来支持他们应用的功能,特别是你编译要与后端系统集成的企业级应用时。把配置和业务逻辑放到服务器端是个好办法,这样你可以很快更新内容和业务规则。_用saas登录鉴权的

web期末作业网页设计实例代码 (建议收藏) HTML+CSS+JS (网页源码)_网页(web )程序设计-程序员宅基地

文章浏览阅读564次,点赞10次,收藏9次。网站布局方面:计划采用目前主流的、能兼容各大主流浏览器、显示效果稳定的浮动网页布局结构。网站程序方面:计划采用最新的网页编程语言HTML5+CSS3+JS程序语言完成网站的功能设计。并确保网站代码兼容目前市面上所有的主流浏览器,已达到打开后就能即时看到网站的效果。网站素材方面:计划收集各大平台好看的图片素材,并精挑细选适合网页风格的图片,然后使用PS做出适合网页尺寸的图片。网站文件方面:网站系统文件种类包含:html网页结构文件、css网页样式文件、js网页特效文件、images网页图片文件;_网页(web )程序设计

Chrome多设备书签同步_chrome标签怎么在不同电脑上同步-程序员宅基地

文章浏览阅读416次。架梯子,使用v2ray结合SwitchyOmega,其中SwitchyOmega设置自动切换,chrome://sync网站配置为使用代理; 重启chrome,关键,不然第3步没反应; 进入chrome://sync,点Trigger GetUpdates(不点也行,会自动更新)_chrome标签怎么在不同电脑上同步

推荐文章

热门文章

相关标签