ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

学习笔记-springCloud

2022-02-02 21:32:35  阅读:162  来源: 互联网

标签:服务 springCloud 笔记 eureka 学习 spring org public cloud


网站整体架构

image

SpringCloud与springBoot

  • SpringBoot专注于快速方便的开发单个个体微服务。

  • SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供∶配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等集成服务。

  • .SpringBoot可以离开SpringClooud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系

  • 总结:SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架

面试问题

1.1、什么是微服务?

1.2、微服务之间是如何独立通讯的?

1.3、SpringCloud和 Dubbo有哪些区别?

1.4、SpringBoot和SpringCloud,请你谈谈对他们的理解

1.5、什么是服务熔断?什么是服务降级

1.6、微服务的优缺点是分别是什么?说下你在项目开发中遇到的坑

1.7、你所知道的微服务技术栈有哪些?请列举一二

1.8、eureka和zookeeper都可以提供服务注册与发现的功能,请说说两个的区别?

参考文档

Spring Cloud Netflix 中文文档 参考手册 中文版

Spring Cloud中文网-官方文档中文版

基础环境搭建

父项目依赖

空的maven项目

image

<modules>
    <module>springCloudapi</module>
    <module>springCloud-provider-dept-8001</module>
    <module>springcloud-consumer-dept-80</module>
</modules>

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

<!--打包方式-->
<packaging>pom</packaging>

<dependencyManagement>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
        <!--springCLoud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!--sprinyBoot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.3.7.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!--数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>

        <!--Springboot 启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

        <!--log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
</dependencyManagement>

API

实体类提供者

不用定义版本号,会从父项目中获取!!!

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
@Data
@NoArgsConstructor
@Accessors(chain = true)//链式写法 @Builder
//支持 dept.setName().setDeptno();
public class Dept implements Serializable {
    private long deptno;
    private String dname;
    private String db_source;

    public Dept(String dname){
        this.dname=dname;
    }
}

Provider-8001

springboot项目

image

依赖

注意以依赖的形式导入另一个项目实体:springCloudapi

 <dependencies>
        <!--需要拿到实体类,所以配置api module-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springCloudapi</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
     
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

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


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

        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

    </dependencies>

springboot配置

server:
  port: 8001

mybatis:
  type-aliases-package: com.yang.pojo
  config-location: classpath:mybatis/mybatis-config.xml  #没啥用
  mapper-locations: classpath:mybatis/mapper/*.xml       #重要,注册mapper 


spring:
  application:
    name: springCloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
    password: 123456
    username: root

Dao

@Repository
@Mapper
public interface DeptDao {
    boolean addDept(Dept dept);

    Dept queryDeptById(long deptno);

    List<Dept> queryAll();

}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace = 绑定一个对应的Dao/Mapper 接口 -->
<mapper namespace="com.yang.dao.DeptDao">
    <select id="queryAll" resultType="dept" >
        select * from db01.dept
    </select>
    
    <insert id="addDept" parameterType="dept">
        insert into dept (dname,db_source) values (#{dname},DATABASE())
    </insert>

    <select id="queryDeptById" resultType="dept">
        select * from dept where deptno=#{deptno}
    </select>
</mapper>

Service

@Service
public class DeptServiceImpl implements DeptService{

    @Autowired
    private DeptDao deptDao;
    @Override
    public boolean addDept(Dept dept) {
        return deptDao.addDept(dept);
    }

    @Override
    public Dept queryDeptById(long deptno) {
        return deptDao.queryDeptById(deptno);
    }

    @Override
    public List<Dept> queryAll() {
        return deptDao.queryAll();
    }
}

Controller

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    @PostMapping("/dept/add")
    public boolean addDept(@RequestBody Dept dept){
        return deptService.addDept(dept);
    }

    @GetMapping("/dept/get/{deptno}")
    public Dept getDept(@PathVariable("deptno") long deptno){
        return deptService.queryDeptById(deptno);
    }

    @GetMapping("/dept/list")
    public List<Dept> list(){
        return deptService.queryAll();
    }

}

Consumer-80

消费端,没有service层,只有Controller,通过远程请求Provider 的地址从而实现对客户的服务

image
)

依赖: 同样要引入 springCloudapi

<dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springCloudapi</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

    </dependencies>

配置端口:

server:
  port: 80

Config中注册一个工具:RestTemplate

@Configuration
public class MyConfig {
    @Bean
    public RestTemplate restTemplate()
    {
        return new RestTemplate();
    }
}

Controller实现远程调用:

restTemplate.getForObject/postForObject等方法选择,根据要调用的请求方式来判断,如调用provider的”/dept/add“请求时,provider中是PostMapping处理请求,则这里就用 postForObject 发送请求.

@RestController
public class DeptConsumerController {

    //消费者没有service层
    @Autowired
    //需要手动在config中注册Bean
    private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法,简单的Restful服务模板

    private static  final String REST_URL_PREFIX="http://localhost:8001";

    @GetMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id")long id){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
    }


    @RequestMapping ("/consumer/dept/add")
    public boolean addDept(Dept dept){
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }

    @GetMapping("/consumer/dept/list")
    public List<Dept> list(){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }

}

Eureka

自我保护机制

某时刻某一个微服务不可以用了,eureka不会立刻清理,依旧会对该微服务的信息进行保存!

  • 默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka之间无法正常通行,以上行为可能变得非常危险了--因为微服务本身其实是健康的,此时本不应该注销这个服务。Eureka通过自我保护机制来解决这个问题--当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该EurekaServer节点会自动退出自我保护模
  • 在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该EurekaServer节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话:好死不如赖活着
  • 综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮和稳定
  • 在SpringCloud中,可以使用eureka.server.enable-se1f-preservation = false禁用自我保护模式
    【不推荐关闭自我保护机制】

注册中心

由于无法正常启动,将父工程的springboot版本讲了一些:

<!--导入eureka报错 降低了版本:2.1.6.RELEASE、 2.3.7.RELEASE-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.1.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

依赖:

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

<!--热部署-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

springBoot配置:

server:
  port: 7001

eureka:
  instance:
    hostname: localhost # Eureka 服务端的实例化名称
  client:
    register-with-eureka: false #是否向eureka注册中心注册自己
    fetch-registry: false # false 表示自己的注册中心
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

主启动类开启Eureka: @EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer //EurekaServer 服务端的启动类,可以接受别人注册进来
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class,args);
    }
}

测试:

运行主启动类,访问:http://localhost:7001/

image

Provider

在之前搭建好的环境上添加:

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
eureka:
  instance:
    hostname: localhost # Eureka 服务端的实例化名称
    instance-id: springCloud-provider-dept-hystrix-8001 #修改eureka上的默认描述信息
    prefer-ip-address: true # 显示真实的ip地址 而不是localhost
    
  client:
    #register-with-eureka: true #是否向eureka注册中心注册自己
    #fetch-registry: true # false 表示自己的注册中心
    service-url:
      defaultZone: http://${eureka.instance.hostname}:7001/eureka/

在主启动类上启动Eureka注册 : @EnableEurekaClient

//手动注册启动类
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到Eureka中
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

信息配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
info: 
  app.name: yang-springcloud
  company.name: haimeiyou

image

image

服务发现

下面以provider为例:

导入依赖:

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

开启发现服务: @EnableDiscoveryClient

@EnableDiscoveryClient//服务发现
@SpringBootApplication//手动注册启动类
@EnableEurekaClient //在服务启动后自动注册到Eureka中
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

使用: DiscoveryClient

@RestController
public class DeptController {

    //能够获取一些配置信息,得到具体的微服务
    @Autowired
    private DiscoveryClient client;
    
    @RequestMapping("/dept/discovery")
    public Object discovery(){
        List<String> services = client.getServices();

        System.out.println("discovery==》services"+services);
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance instance : instances) {
            System.out.println(instance.getHost()+"\t"
                    +instance.getPort()+"\t"
                     +instance.getUri()+"\t"
                    +instance.getServiceId()
                    );
        }
        return this.client;
    }

}

集群

image
)

配置3个注册中心:eureka7001、eureka7002、eureka7003

service-url绑定除自己以外的其他地址

  • eureka7001
server:
  port: 7001

eureka:
  instance:
    hostname: Eureka7001 # Eureka 服务端的实例化名称
  client:
    register-with-eureka: false #是否向eureka注册中心注册自己
    fetch-registry: false # false 表示自己的注册中心
    service-url:
      defaultZone: http://Eureka7002:7002/eureka/,http://Eureka7003:7003/eureka/
  • eureka7002
server:
  port: 7002

eureka:
  instance:
    hostname: Eureka7001 # Eureka 服务端的实例化名称
  client:
    register-with-eureka: false #是否向eureka注册中心注册自己
    fetch-registry: false # false 表示自己的注册中心
    service-url:
      defaultZone: http://Eureka7001:7001/eureka/,http://Eureka7003:7003/eureka/
  • eureka7003
server:
  port: 7003

eureka:
  instance:
    hostname: Eureka7001 # Eureka 服务端的实例化名称
  client:
    register-with-eureka: false #是否向eureka注册中心注册自己
    fetch-registry: false # false 表示自己的注册中心
    service-url:
      defaultZone: http://Eureka7001:7001/eureka/,http://Eureka7002:7002/eureka/

provider注册:对三个都注册

eureka:
  instance:
    hostname: localhost # Eureka 服务端的实例化名称
    instance-id: springCloud-provider-dept8001 #修改eureka上的默认描述信息
  client:
    service-url:
      defaultZone: http://Eureka7001:7001/eureka/,http://Eureka7002:7002/eureka/,http://Eureka7003:7003/eureka/

显示:

image

Eureka vs zookeeper

CAP原则

​ CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

  • 可用性(A):保证每个请求不管成功或者失败都有响应。

  • 分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。 [1]

​ CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足

zookeeper CP

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况:当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因为网络问题使得水集群失去master节点是较大概率会发生的事件,虽然服务最终能够恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

Eureka AP?

Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册服务的可用性,
只不过查到的信息可能不是最新的,除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:

  1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
  2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用)
  3. 当网络稳定时,当前实例新的注册信息会被同步到其他节点中

因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪

ribbon

是什么

  • Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具

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

  • 负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。

  • 负载均衡分类

    • 集中式:

      即在服务的消费方和提供方之间使用独立的LB设施,如Nginx:反向代理服务器!,由该设施负责把访问请求通过某种策略转发至服务的提供方!

    • 进程式

      ​ 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。

      ​ Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!

配置consumer

(受限于电脑配置从集群开始没有实操,下面只是理论)

依赖:

下面的ribbon二选一,哪个能用选哪个,spring-cloud-starter-netflix-ribbon 适合新版本

(**新版的erueka是包含ribbon,不需要加依赖,不然会启动不了 ????? **)

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

配置并启动eureka

server:
  port: 80
  
eureka:
   client:
     register-with-eureka: false # 不向eureka注册自己
     service-url:
       defaultZone: http://localhost:7001/eureka/,...,... # 这里写集群的三个地址

@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}

开启负载均衡 @LoadBalanced

@Configuration
public class MyConfig {
    @Bean
    @LoadBalanced //配置负载均衡实现RestTemplate
    public RestTemplate restTemplate()
    {
        return new RestTemplate();
    }
}

在controller中使用:

(服务名不支持下划线 ???

//Ribbon的地址应该是变量,通过服务名来访问
//private static  final String REST_URL_PREFIX="http://localhost:8001";
private static  final String REST_URL_PREFIX="http://SPRINGCLOUDE-PROVIDER-DEPT";
//后面跟的是provider的spring配置文件中配置的spring-app的名字

配置多个provider

创建3个数据库表,3个provider项目,端口号分别为8001,8002,8003,保证数据库内容一致(数据库字段除外) 3个项目spring-app-name (服务名)完全一样,都链接进入集群,启动后可以在eureka中看见一个服务有三个服务实例:

image

测试

使用consumer连接数据库查询,在负载均衡作用下,会依次进入三个不同的服务实例,从三个不同的数据库中查询

负载均衡策略

接口:IRule:

  • RoundRobinRule 轮询 默认

  • AvailabilityFilteringRule

    会先过滤掉跳闸、出现访问故障的服务,对剩下的服务进行轮询

  • RandomRule 随机

配置其他策略:

image

@Configuration
public class MyRule {
    @Bean
    public IRule iRule(){
        return new RandomRule();
    }
}

主启动类上配置:

@RibbonClient name=服务名 configuration=配置策略类

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "SPRINGCLOUDE-PROVIDER-DEPT",configuration = MyRule.class)
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}

自定义策略

  • 也在myrule包里,不和主启动类同级
  • 继承 AbstractLoadBalancerRule
  • 如下实现 每个服务5次后轮询
package com.myrule;
public class MyRoundRule extends AbstractLoadBalancerRule {

    private int currentIndex=0;
    private int count=0;

    public MyRoundRule() {
    }

    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;
            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }

                List<Server> upList = lb.getReachableServers();//获取 ‘活着的’ 服务
                List<Server> allList = lb.getAllServers();//获取所有服务
                int serverCount = upList.size();
                if (serverCount == 0) {
                    return null;
                }

                //=======================================
                if (count<5){
                    server=upList.get(currentIndex);
                    count++;
                }else {
                    count=0;
                    currentIndex++;
                    if (currentIndex>serverCount-1){
                        currentIndex=0;
                    }
                    server=upList.get(currentIndex);
                }

                //======================================
                int index = this.chooseRandomInt(serverCount);//获取一个区间的随机数

                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

Feign

feign不是做负载均衡的,feign只是集成了ribbon,负载均衡还是feign内置的ribbon做。feign的作用的替代RestTemplate,性能比较低,但是可以使代码可读性很强。

依赖:

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>



<!--新版用下面的-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

配置服务service:

image

@Component
@FeignClient(value = "SPRINGCLOUDE-PROVIDER-DEPT") //服务名
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")
    Dept queryById(@PathVariable("id")long id);

    Dept queryAll();

    boolean addDept(Dept dept);
}

在feign-consumer中使用:

@Autowired
private DeptClientService service;

@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
    return this.service.addDept(dept);
}

@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id")long id){
    return this.service.queryById(id);
}

@RequestMapping("/consumer/dept/list")
public List<Dept> list(){
    return this.service.queryAll();
}

放弃了下面的服务访问方式,转而面向接口编程:

@Autowired
private RestTemplate restTemplate;
private static  final String REST_URL_PREFIX="http://SPRINGCLOUDE-PROVIDER-DEPT";

consumer主启动类下开启feign

@SpringBootApplication
@EnableEurekaClient
//@RibbonClient(name = "SPRINGCLOUDE-PROVIDER-DEPT",configuration = MyRule.class)
@EnableFeignClients(basePackages = {"com.yang"})
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}

服务熔断

介绍

什么是Hystrix

​ Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
​ “断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩

服务熔断

熔断机制是对应雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。

使用服务熔断

新建项目,复制provider-8001

依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

载入备选方法:@HystrixCommand(fallbackMethod = "hystrixGetDept")//使用熔断机制

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    @GetMapping("/dept/get/{deptno}")
    @HystrixCommand(fallbackMethod = "hystrixGetDept")//使用熔断机制
    public Dept getDept(@PathVariable("deptno") long deptno){
        Dept dept = deptService.queryDeptById(deptno);
        if (dept==null){
            throw new RuntimeException("id->"+deptno+"没有对应信息 @Exception");
        }
        return dept;
    }
    //熔断的备选方案
    public Dept hystrixGetDept(@PathVariable("deptno") long deptno){
        return new Dept()
                .setDeptno(deptno)
                .setDname("id->"+deptno+"没有对应信息 @Hystrix")
                .setDb_source("no this database");
    }
}

开启支持: @EnableCircuitBreaker

@EnableDiscoveryClient//服务发现
@SpringBootApplication//手动注册启动类
@EnableEurekaClient //在服务启动后自动注册到Eureka中
@EnableCircuitBreaker //添加熔断支持、断路器
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class,args);
    }
}

测试:查询不存的对象,通过熔断机制返回的信息:

image

使用服务降级

结合feign使用

api 服务接口中调用服务降级: fallbackFactory= xxx.class

@Component
@FeignClient(value = "SPRINGCLOUDE-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class) //服务名
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")
    Dept queryById(@PathVariable("id")long id);

    @GetMapping("/dept/list")
    List<Dept> queryAll();

    @GetMapping("/dept/add")
    boolean addDept(Dept dept);
}

提供降级的服务:
image-20220126150839145

@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
    @Override
    public DeptClientService create(Throwable throwable) {
        return new DeptClientService() {
            @Override
            public Dept queryById(long id) {
                return new Dept()
                        .setDeptno(id)
                        .setDname("id->"+id+"没有对应信息,这个服务已关闭,请稍后再试")
                        .setDb_source("no database");
            }

            @Override
            public List<Dept> queryAll() {
                return null;
            }

            @Override
            public boolean addDept(Dept dept) {
                return false;
            }
        };
    }
}

在使用feign的客户端配置开启服务降级:

feign:
  hystrix:
    enabled: true

个人理解:

服务降级是客户端consumer的设置,使用后,即使服务端provider 崩溃(或者为了资源合理分布而暂时关闭)还能使用备用的降级服务。

服务熔断是服务端provider 的设置,当服务出现超时、异常时进行捕捉并作出对应处理

Dashboard流量监控

监控工具

新建客户端springcloud-consumer-hystrix-dashboard,端口9001

server:
  port: 9001
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springCloudapi</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

主启动类上开启流量监控: @EnableHystrixDashboard

@SpringBootApplication
@EnableHystrixDashboard
public class ConsumerDashboard_9001 {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerDashboard_9001.class,args);
    }
}

测试:访问Hystrix Dashboard

image

监控对象

监控对象是provider,

在导入了hystrix和actuator 依赖的provider中的主启动类上添加servlet:

@EnableDiscoveryClient//服务发现
@SpringBootApplication//手动注册启动类
@EnableEurekaClient //在服务启动后自动注册到Eureka中
@EnableCircuitBreaker //添加熔断支持、断路器
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class,args);
    }

    @Bean
    public ServletRegistrationBean hystrixMetricsStreamServlet(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        return registrationBean;
    }
}

测试:访问:localhost:8001/actuator/hystrix.stream

image

监控

在监控页面上输入 https://localhost:8001/actuator/hystrix.stream delay=2000ms Title= name

进入下面的页面:

image

  • 实心圆:

    共有两种含义,他通过颜色的变化代表了实例的健康程度它的健康程度从绿色<黄色<橙色<红色递减。该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大,该实心圆就越大,所以通过该实心圆的展示,就可以在大量的实例中快速发现故障实例和高压力实例。

  • 线:

    用来记录2分钟内流量的相对变化,可以通过它观察到流量的上升和下降趋势

  • 整图:

image

Zuul路由网关

概述:

zuul包含了对请求的路由和过滤两个最主要的功能:

​ 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。

构建

新建项目:springcloud-zuul-9527

依赖:新加 zuul

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>


    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>springCloudapi</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>
server:
  port: 9527

spring:
  application:
    name: springcloud-zuul

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/  # 这里写集群(3个地址)

  instance:
    instance-id: zuul9527
    prefer-ip-address: true

info:
  app.name: yang-springcloud

开启网关: @EnableZuulProxy

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication_9527 {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication_9527.class,args);
    }
}

测试

运行eureka 7001 、provider 8001 、 zuul 9527

可以看到注册中心有两个服务:

image

直接访问provider可以拿到数据:

image

通过网关也能拿到数据:

image

隐藏路径中的服务名

在springcloud-zuul-9527 中添加:

zuul:
  routes:
    mydept.serviceId: springcloud-provider-dept
    mydept.path: /mydept/**
  ignored-services: springcloud-provider-dept # 代服务名的路径不再支持访问
  #ignored-services: "*"  隐藏所有的服务名路径

测试:

image

设置前缀

zuul:
  prefix: /yang

完整路径: http://localhost:9527/yang/mydept/dept/get/2

Git

下载Git:https://git-scm.com/download/win

上传

git add .
git commit -m "注释语句"
git push

远程访问项目-Server

新建项目: springcloud-config-server-3344

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>


<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
server:
  port: 3344

spring:
  application:
    name: springcloud-config-server
  #连接远程仓库
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/yang-KUKU/springcloud-config.git

开启服务: @EnableConfigServer

@SpringBootApplication
@EnableConfigServer
public class Config_Server_3344 {
    public static void main(String[] args) {
        SpringApplication.run(Config_Server_3344.class,args);
    }
}

测试:

image

image

image

Client

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
        <version>2.1.1.RELEASE</version>
    </dependency>


    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

配置:

  • bootstrap.yml
#系统级的配置 application 是用户级别的配置
spring:
  cloud:
    config:
      name: config-client # 需要远程读取的资源名称,不用后缀
      uri: http://localhost:3344
      profile: dev
      label: master
  • application.yml
spring:
  application:
    name: springcloud-config3355

controller:

@RestController
public class ConifgClientController  {

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaServer;

    @Value("${server.port}")
    private String port;

    @RequestMapping("/config")
    public String getConifg(){
        return "applicationName"+applicationName+
                "eurekaServer"+eurekaServer+
                "port"+port;
    }
}

注意:controller的编写只是为了更形象的显示从远程获取到了配置,此配置已经发生作用,不用编写另外的代码

编写主启动类测试: 可见客户端从远程拿到了配置信息

image

总结:

实现远程统一配置管理,源代码干净简洁、安全

image

标签:服务,springCloud,笔记,eureka,学习,spring,org,public,cloud
来源: https://www.cnblogs.com/yangzx-hust/p/15860806.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有