SpringCloud 学习及其相关组件的认识

Spring Cloud 相关的组件

微服务

提出微服务的原文与译文

原文:https://martinfowler.com/articles/microservices.html

汉化:https://www.cnblogs.com/HHbJ/articles/14689415.html

一些参考学习路径

spring cloud netflix

https://www.springcloud.cc/spring-cloud-netflix.html

中文:https://www.springcloud.cc/spring-cloud-dalston.html

dubbo 和 Springcloud对比

社区活跃度

springcloud 比dubbo活跃

Dubbo Springcloud
服务注册中心 Zookeeper Spring Cloud Netflix Eureka
服务调用方式 RPC REST API
服务监控 Dubbo-monitor Spring Boot Admin
断路器 不完善 Spring Cloud Netflix Hystrix
服务网关 Spring Cloud Netflix Zuul
分布式配置 Spring Cloud Config
服务跟踪 Spring Cloud Sleuth
消息总线 Spring Cloud Bus
数据流 Spring Cloud Stream
批量任务 Spring Cloud Task

最大的区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式

CAP定理CAP原则

RDBMS(Mysql Oracle sqlServer) ====>ACID

ACID是什么?

  • A(Atomicity) 原子性
  • C(Consistency) 一致性
  • I (Isolation) 隔离性
  • D (Durability) 持久性
    NoSQL(redis mongdb) ====>CAP

CAP是什么?

  • C (Consistency) 强一致性

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

  • A (Availability) 可用性

    保证每个请求不管成功或者失败都有响应。

  • P (Partition tolerance) 分区容错性

    系统中任意信息的丢失或失败不会影响系统的继续运作

CAP的三进二: CA AP CP

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

因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。通常使用类似 memcached 之类的 NOSQL 作为实现手段。虽然 memcached 也可以是分布式集群环境的,但是对于一份数据来说,它总是存储在某一台 memcached 服务器上。如果发生网络故障或是服务器死机,则存储在这台服务器上的所有数据都将不可访问。由于数据是存储在内存中的,重启服务器,将导致数据全部丢失。当然也可以自己实现一套机制,用来在分布式 memcached 之间进行数据的同步和持久化,但是实现难度是非常大的 。

Ribbon负载均衡

Ribbon是什么

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

Ribbon能干什么?

  • LB,即负载均衡(Load Balancer),在微服务或分布式集群中经常使用的一种应用。
  • 负载均衡简单的说,就是将用户的请求平摊到分配的多个服务器上,从而达到系统的HA(高可用)。
  • 常见的负载均衡软件有Nginx,Lvs等等
  • dubbo、SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义
  • 负载均衡简单分类:
    • 集中式LB
      • 即在服务的消费方和提供方之间使用独立的LB设施,如Nginx(反向代理服务器),由该设施负责把访问请求通过某种策略转发至服务的提供方!
    • 进程式LB
      • 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器
      • Ribbon就属于进程式LB,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方的地址

Feign负载均衡

简介

feign是声明式的web service客户端,它让服务之间的调用变得简单了 ,类似controller调用service.SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端

只需要创建一个借口,然后添加注解即可

feign,调用微服务访问两种方法

  1. 微服务名字[ribbon]
  2. 接口和注解[feign]

Feign能干什么?

  • Feign旨在使编写Java http客户端变得容易
  • 前面在使用Ribbon + RestTemplate时,利用RestTemplate对http请求的封装处理,行程了一套模板化的调用方法.但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个借口会被多出调用,所以通常都会针对每个微服务自行封装一些客户端来包装这些依赖服务的调用,所以,feign在此基础上做了进一步封装,由他来帮我们定义和实现依赖服务借口的定义,在feign的实现下,我们只需要创建一个借口并使用注解的方式来配置他(类似以前Dao借口上绑定Mapper注解,现在是微服务借口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了Spring Cloud Ribbon时,自动封装服务调用客户端的开发量.

Feign集成了Ribbon

  • 利用Ribbon维护MicroServiceCloud-Dept的服务列表,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而且简单的实现了服务调用.

Hystrix

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖初出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性.

"断路器"本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方法返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩.

服务雪崩

多个微服务之间调用的时候,假设微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的"扇出",如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的"雪崩效应".
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒内饱和.比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统.

我们需要 弃车保帅

服务熔断

熔断机制是应对雪崩效应的一种服务链路保护机制.

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

  1. 当所依赖的对象不稳定时,能够起到快速失败的目的;
  2. 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。

Zuul 路由网关

什么是zuul?

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

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

注意:zuul服务最终还是会注册进Eureka

提供:代理 + 路由 + 过滤 三大功能!

Springcloud项目创建和简单调用

1.创建一个maven项目

添加父级依赖

<!--打包方式 pom-->
    <packaging>pom</packaging>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.18.20</lombok.version>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <!--springcloud的依赖-->
            <!-- https://mvnrepository.com/artifact/org.apache.camel/camel-spring-boot-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR1</version>
                <type>pom</type>
    
            </dependency>
            <!--springboot的依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.4.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.10</version>
            </dependency>
            <!--springboot启动器-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.2</version>
            </dependency>
            <!--juint-->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.创建一个公共的common module

依赖如下:

    <dependencies>
		<!-- 也可以不用不必强求-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

编写实体类:

package com.zhonglao.pojo;


import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@NoArgsConstructor
//开启链式写法
@Accessors(chain = true)
public class Department implements Serializable {
    
    private Integer id;
    private String name;
    private String dep_no;

    public Department(String name) {
        this.name = name;
    }
}

3.创建注册中心

依赖如下:

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

配置文件如下

server:
  port: 9003
eureka:
  instance:
    hostname: eureka9003.com # eureka服务端的名字
  client:
    register-with-eureka: false  # 表示是否向eureka注册中心注册自己
    fetch-registry: false  # fetch-registry 如果为false 表示自己为注册中心
    service-url:  # 重写注册中心的地址  不走 默认的
#    单机配置
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/   
#    集群配置
      defaultZone: http://eureka9004.com:9004/eureka/,http://eureka9005.com:9005/eureka/

在启动类上配置 @EnableEurekaServer注解,打开配置

4.创建第一个提供者prodiver

依赖如下:

    <dependencies>
        <!--导入实体的依赖 这里common-->
        <dependency>
            <groupId>spring-cloud</groupId>
            <artifactId>springcloud-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </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>
        <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>
        <!--热部署-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-devtools</artifactId>-->
<!--        </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.7.RELEASE</version>
        </dependency>

    </dependencies>

配置文件如下:

server:
  port: 9001
mybatis:
  type-aliases-package: com.zhonglao.pojo
  mapper-locations: classpath:mapper/*.xml
spring:
  application:
    name: springcloud-provider
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    username: 
    password: 
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&useSSL=false


# 注册到注册中心
eureka:
  client:
    service-url: 
    # 多个,分开创建eureka集群
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9004.com:9004/eureka/,http://eureka9005.com:9005/eureka/
  instance:
    instance-id: springcloud-provider-9001 #修改状态信息

#info配置  就是eureka页面 当前实例的status 
info:
  app.name: zhonglao-springcloud-provider
  compang.name: blog.zhonglao.com

mapper文件

<?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">
<mapper namespace="com.zhonglao.springcloud.dao.DepartmentDao">
    <insert id="addDepartment" parameterType="Department">
        insert into department (name,dep_no) values (#{name},DATABASE());
    </insert>
    <select id="getDepartmentById" resultType="Department" parameterType="Integer">
        select * from department where id = #{id};
    </select>
    <select id="listDepartment" resultType="Department">
        select * from department;
    </select>
</mapper>

dao service等

package com.zhonglao.springcloud.dao;

import com.zhonglao.pojo.Department;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface DepartmentDao {
    
    boolean addDepartment(Department department);

    Department getDepartmentById(Integer id);
    
    List<Department> listDepartment();
}

package com.zhonglao.springcloud.service;

import com.zhonglao.pojo.Department;

import java.util.List;

public interface DepartmentService {

    boolean addDepartment(Department department);

    Department getDepartmentById(Integer id);

    List<Department> listDepartment();
}

//--------------------------------------------------

package com.zhonglao.springcloud.service;


import com.zhonglao.springcloud.dao.DepartmentDao;
import com.zhonglao.pojo.Department;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author Administrator
 */
@Service
public class DepartmentServiceImpl implements DepartmentService {
    
    @Resource
    DepartmentDao departmentDao;
    
    @Override
    public boolean addDepartment(Department department) {
        return departmentDao.addDepartment(department);
    }

    @Override
    public Department getDepartmentById(Integer id) {
        return departmentDao.getDepartmentById(id);
    }

    @Override
    public List<Department> listDepartment() {
        return departmentDao.listDepartment();
    }
}

package com.zhonglao.springcloud.controller;


import com.zhonglao.pojo.Department;
import com.zhonglao.springcloud.service.DepartmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author Administrator
 * //提供restful服务
 */ 
@RestController
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    DiscoveryClient discoveryClient;
    
    @RequestMapping("/dep/add")
    public boolean addDepartment(Department department){
        return departmentService.addDepartment(department);
    }
    
    @RequestMapping("/dep/get/{id}")
    public Department getDepartmentById(@PathVariable("id") Integer id){
        return departmentService.getDepartmentById(id);
    }

    @RequestMapping("/dep/list")
    public List<Department> listDepartment(){
        return departmentService.listDepartment();
    }

    //注册进来的微服务 获取一些
    //获取信息时候   需要 在启动类上开启  @EnableDiscoveryClient 
    @RequestMapping("/dep/discovery")
    public Object discovery(){
        
        //获取微服务列表的清单
        List<String> services = discoveryClient.getServices();
        System.out.println("services============>");
        System.out.println(services);


        List<ServiceInstance> instances = discoveryClient.getInstances("SPRINGCLOUD-PROVIDER");
        for (ServiceInstance instance : instances) {
            System.out.println(
                    instance.getHost() + "	" + 
                    instance.getPort() + "	" + 
                    instance.getUri() + "	" +
                    instance.getServiceId() 
            );
        }
        return this.discoveryClient;
    }
}

启动类需要开启:@EnableEurekaClient注解

5.创建消费者 consumer

依赖如下:

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

        <dependency>
            <groupId>spring-cloud</groupId>
            <artifactId>springcloud-commom</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

配置文件

server:
  port: 9002
  
#Eureka配置
eureka:
  client:
    register-with-eureka: false   #不向eureka中注册自己
    service-url:
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9004.com:9004/eureka/,http://eureka9005.com:9005/eureka/

controller调用

package com.zhonglao.springcloud.controller;

import com.zhonglao.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class DepartmentController {
    
    @Autowired
    RestTemplate restTemplate;
    
    //Ribbon,这里的地址应该是一个变量,通过服务名来访问
//    private static final String REST_URL_PREFIX = "http://localhost:9001";
    private static final String REST_URL_PREFIX = "http://springcloud-provider";

    @RequestMapping("/consumer/dep/add")
    public boolean add(Department department){
        Boolean aBoolean = restTemplate.postForObject(REST_URL_PREFIX + "/dep/add", department, Boolean.class);
        return aBoolean;
    }
    
    @RequestMapping("/consumer/dep/get/{id}")
    public Department get(@PathVariable("id") Integer id){
        Department department = restTemplate.getForObject(REST_URL_PREFIX + "/dep/get/" + id, Department.class);
        return department;
    }

    @RequestMapping("/consumer/dep/list")
    public  List<Department> list(){
        List<Department> department = restTemplate.getForObject(REST_URL_PREFIX + "/dep/list", List.class);
        return department;
    }
     
}

把restTemplate注册到容器中

package com.zhonglao.springcloud.config;

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

@Configuration
public class ConfigBean {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

使用feign形式调用接口

在common中加入feign依赖

 <!--feign 依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

写一个接口的映射,(可以这样抽象的理解)

package com.zhonglao.springcloud.service;

import com.zhonglao.pojo.Department;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(value = "springcloud-provider")
public interface DepartmentServiceClient {

    @RequestMapping("/dep/add")
    boolean add(Department department);

    @RequestMapping("/dep/get/{id}")
    Department get(@PathVariable("id") Integer id);

    @RequestMapping("/dep/list")
    List<Department> list();
}

consumer调用的话,也需要加上这个依赖

 <!--feign 依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

调用代码就如正常的调用

package com.zhonglao.springcloud.controller;

import com.zhonglao.pojo.Department;
import com.zhonglao.springcloud.service.DepartmentServiceClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@RestController
public class DepartmentController {
    
    @Resource
    DepartmentServiceClient departmentServiceClient;

    @RequestMapping("/consumer/dep/add")
    public boolean add(Department department){
        return this.departmentServiceClient.add(department);
    }
    
    @RequestMapping("/consumer/dep/get/{id}")
    public Department get(@PathVariable("id") Integer id){
        return this.departmentServiceClient.get(id);
    }

    @RequestMapping("/consumer/dep/list")
    public  List<Department> list(){
        return this.departmentServiceClient.list();
    }
     
}

需要在启动类上加上注解以表示启动

package com.zhonglao.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.zhonglao"})
public class DepartmentConsumerFeign {   
    public static void main(String[] args) {
        SpringApplication.run(DepartmentConsumerFeign.class,args);
    }
}

增加Ribbon轮询

消费者consumer中增加ribbon的依赖

        <!--ribbon-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>

在config中增加@LoadBalanced注解即可,简单使用

package com.zhonglao.springcloud.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ConfigBean {
    
    //配置负载均衡实现restTemplate
    //IRule
    //AvailabilityFilteringRule;  会过滤掉奔溃的服务,对剩下存活的进行轮询
    //MyRandomRule  随机轮询
    //MyRandomRule 轮询
    @Bean
    @LoadBalanced  //Ribbon
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    
    //配置简单的轮询配置
//    @Bean
//    public IRule myRule(){
//        return new MyRandomRule();
//    }
}

自定义配置ribbon规则的话需要在启动类目录之外配置,

image-20210428083555804

并且需要在启动类上使用注解:

//Ribbon 和 Eureka 整合后,客户端不用关心端口和ip
@SpringBootApplication
@EnableEurekaClient
//配置的ribbon轮询规则,不应该在应用程序的上下文中的@ComponentScan,否则所有的ribbon将共享 也就是不在主启动类重叠的包
@RibbonClient(name = "springcloud-provider",configuration = MyIRule.class)
public class DepartmentConsumer {   
    public static void main(String[] args) {
        SpringApplication.run(DepartmentConsumer.class,args);
    }
}

在官方文档中有这样一段话:

[警告]
CustomConfiguration类必须是@Configuration类,但请注意,对于主应用程序上下文,它不在@ComponentScan中。否则,它由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免将其包括在内(例如,可以将其放在单独的,不重叠的程序包中,或指定要在@ComponentScan)。

Hystrix 熔断与降级

熔断是在提供者这边做的处理(这里的例子是基于feign方式编写的)

hystrix依赖如下

<!--导入熔断依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

代码如下

package com.zhonglao.springcloud.controller;


import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.zhonglao.pojo.Department;
import com.zhonglao.springcloud.service.DepartmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Administrator
 * //提供restful服务
 */ 
@RestController
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
  
    @RequestMapping("/dep/get/{id}")
    @HystrixCommand(fallbackMethod = "hystrixGet")//主要是这个注解
    public Department get(@PathVariable("id") Integer id){
        Department department = departmentService.getDepartmentById(id);
        if (department == null) {
            throw new RuntimeException("id=====>" + id + ",不存在该用户,或者无法找到");
        }
        return department;
    }

    //备选方法
    public Department hystrixGet(@PathVariable("id") Integer id){
        return new Department()
                .setId(id)
                .setName("id=====>" + id + ",不存在该用户,或者无法找到--@hystrixGet")
                .setDep_no("no this database");
    }
}


启动类上需要配置

package com.zhonglao;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient  //服务发现
@EnableCircuitBreaker //对熔断的支持
public class DepartmentProviderHystrix9001 {
    public static void main(String[] args) {
        SpringApplication.run(DepartmentProviderHystrix9001.class,args);
    }
}

降级(也是基于feign上实现的)需要实现FallbackFactory接口

  • 返回对应的feign接口
package com.zhonglao.springcloud.service;

import com.zhonglao.pojo.Department;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.List;

//降级
@Component
public class DepartmentServiceFallbackFactory implements FallbackFactory {
    @Override
    public DepartmentServiceClient create(Throwable throwable) {
        return new DepartmentServiceClient() {
            @Override
            public boolean add(Department department) {
                return false;
            }

            @Override
            public Department get(Integer id) {
                return new Department()
                        .setId(id)
                        .setName("id==>"+ id + "没有对应的消息,客户端提供了降级信息,这个服务已被关闭");
            }

            @Override
            public List<Department> list() {
                return null;
            }
        };
    }
}

  • 在接口上配置 fallbackFactory指定刚才定义好的类
package com.zhonglao.springcloud.service;

import com.zhonglao.pojo.Department;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(value = "springcloud-provider",fallbackFactory = DepartmentServiceFallbackFactory.class)
public interface DepartmentServiceClient {

    @RequestMapping("/dep/add")
    boolean add(Department department);

    @RequestMapping("/dep/get/{id}")
    Department get(@PathVariable("id") Integer id);

    @RequestMapping("/dep/list")
    List<Department> list();
}

  • 在消费者的配置文件中开启降级
#开启降级feign.hystrix
feign:
  hystrix:
    enabled: true

hystrix-dashboard 监控页面

  • 创建新的modle

依赖如下

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

在启动类上添加注解

package com.zhonglao.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

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

  • 提供者那边注册一个bean 放在容器里

添加依赖 完善监控信息

<!--完善监控信息-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

代码:

package com.zhonglao;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient  //服务发现
@EnableCircuitBreaker //对熔断的支持
public class DepartmentProviderHystrix9001 {
    public static void main(String[] args) {
        SpringApplication.run(DepartmentProviderHystrix9001.class,args);
    }

    //增加一个servlet
    //主要是监控提供者的/actuator/hystrix.stream 流编程可视化界面
    @Bean
    ServletRegistrationBean hystrixMetricsStreamServletServletRegistrationBean(new HystrixMetricsStreamServlet());
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        return registrationBean;
    }
}

访问路径:http://localhost:[port]/hystrix

image-20210428154838633

根据提供者的地址http://localhost:[port]/actuator/hystrix.stream

image-20210429100005657

增加zuul网关的使用

创建新的module,加入以下依赖

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

配置如下:

server:
  port: 9200
spring:
  application:
    name: springcloud-zuul

eureka:
  client:
    service-url:
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9004.com:9004/eureka/,http://eureka9005.com:9005/eureka/
  instance:
    instance-id: zuul9200.com
    perfer-ip-address: true

info: 
  app.name: zhonglao-springcloud
  company.name: bolg.zhonglao.com
  

zuul:
  routes:
    #对服务名称做一个映射
    springcloud-provider.serverId: springcloud-provider
    springcloud-provider.path: /mydep/**
  # 忽略使用 服务名字进行访问 * 隐藏所有的地址

  ignored-services: "*"

zuul.routes serverId这个前缀尽量与服务实例名称保持一致,不然会报错

启动类配置

package com.zhonglao.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy  //开启zuul
public class ZuulApplication9200 {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication9200.class,args);
    }
}
原文地址:https://www.cnblogs.com/HHbJ/p/14783916.html