SpringBoot2.0整合ribbon+熔断机制项目实战

注意:spring boot版本是2.0以上,如果为2.0以下的,肯定不行,既然要用,肯定是用最新的版本。

1.简介

         Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的。

     1.1客户端负载均衡

         随着业务越来越庞大,使用的用户人群也越来越多,除了需要把业务进行按模块拆分外,负载均衡,是需要直面的一个问题,负载均衡可以保证系统的HA,减少服务器的压力,负载均衡分为两种,一种是硬件负载均衡,还有一种是软件负载均衡,市面上常见软件负载均衡的软件有nginx,dubbo。

       nginx主要是针对项目web页面集群,进行负载均衡。nginx相当于下图的rabbon,服务器相当于tomcat(也可以是其他服务器),这种主要是请求分流。

       dubbo,可以用于业务逻辑负载均衡,例如,用户在某购物网上下了一个订单,以我个人的理解,首先nginx针对请求分流,在通过dubbo对业务进行负载均衡,形成一个网状树形结构。个人感觉zk+dubbo用起来还是蛮简单的,唯一不好的一点,因为他用的是rpc远程调用方式,底层是使用的hession,首先,两个项目对接的接口参数都得系列化,两个项目的包路径要保持一致。说的有些远勒,这里我们回归到正题。

2.项目的结构

首先这个项目是聚合项目,不了解聚合项目的,可以去了解一下,这里不过多阐述。

首先创建一个springcloud项目,再分别创建4个模块

(1)springcloud-eureka-server    8761

             eureka 注册服务,提供其他服务器的注册。类似于zookeeper这个角色,建议多个,考虑到单个服务注册后,影响整个系统的使用。

(2)springcloud-eureka-clienta   8762

   服务提供方A,类似于dubbo里面的生产者角色,为了方便,这里简写为服务提供者A

(3)springcloud-eureka-clientB   8763

   服务提供方B,类似于dubbo里面的生产者角色,为了方便,这里简写为服务提供者B

(4)springcloud-ribbon-customer  8764

   服务消费者,类似于dubbo里面的消费者角色,为了方便,这里简写为消费者

  服务提供者A和服务器提供者B都会提供一个hello的方法,消费者通过轮询算法,分别调用服务器提供者A和服务器提供者B

2.1父项目springcloud项目

      主要是提取公有的依赖  

    2.1.1 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 http://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.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.fqyd</groupId>
    <artifactId>springcloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud</name>
    <packaging>pom</packaging>
    <description>Demo project for Spring Boot</description>

    <modules>
        <module>springcloud-eureka-server</module>
        <module>springcloud-eureka-clienta</module>
        <module>springcloud-eureka-clientb</module>
        <module>springcloud-ribbon-customer</module>
    </modules>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.RELEASE</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>
    </dependencies>

    <!-- Spring Cloud 管理依赖 -->
    <dependencyManagement>
        <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>

 2.2 springcloud-eureka-server项目

服务注册中心,建议使用集群的方式,多个通过","分开

2.2.1 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.fqyd</groupId>
    <artifactId>springcloud-eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-eureka-server</name>
    <packaging>jar</packaging>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>com.fqyd</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>
</project>

2.2.2 application.yml

server:
  port: 8761
spring:
  application:
    name: eurka-server
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    server:
      # 关闭自我保护
      enable-self-preservation: false
      # 清理服务器
      eviction-interval-timer-in-ms: 60000
  # 关闭程序
  endpoints:
    shutdown:
      enabled: true
      sensitive: true

注意:这里的端口是8761端口,后续会用到,eureka.client.registerWithEureka=false表明eureka自己不是服务提供者,不需要将自己注册到注册中心,否则会出错。fetchRegistry表示是否从Eureka服务器中获取注册信息,默认为true,这里因为是服务器本身,不需要获取。defaultZone为注册中信息服务器提供的注册地址。 

2.3.3 服务器启动类

package com.fqyd;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;


@EnableEurekaServer
@SpringBootApplication
public class SpringcloudEurekaServerApplication {

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

}

2.3.4 效果

http://localhost:8761/

启动服务注册,可以通过如下地址,看到如下图所示。

2.3 springcloud-eureka-clienta项目

  主要是提供服务注册,注册到eureka-server上。

  2.3.1 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.fqyd</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <groupId>com.fqyd</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>

这里只引入勒eureka-client的依赖,其他的依赖,父项目已导入。

2.3.2 application.yml

server:
  # 服务提供监听端口[eureka客户端],注意改端口不能与本机服务器端口冲突
  port: 8762
# 多环境通用部分[各个环境可以覆盖通用字段]
spring:
  application:
    # 通用名字,其他环境可以覆盖:为你的应用起个名字,该名字将注册到eureka注册中心
    name: client-service
eureka:
  client:
    # 是否将eureka自身作为应用注册到eureka注册中心,默认为true
    registerWithEureka: true
    serviceUrl:
      # 这里可以填写所有的eureka服务器地址并以','分离,当前面不能注册时候,自动选择后面的进行注册,排除单点故障问题
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}

prefer-ip-address:true主要是为了解决注册到服务器上显示为主机名的问题,一般建议显示为ip,方便区分。


2.3.3 服务器启动类

package com.fqyd.demo;

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

@EnableDiscoveryClient
@SpringBootApplication
@ComponentScan(basePackages={"com"})
public class DemoApplication {

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

}

2.3.4 暴露的接口

package com.fqyd.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class ServiceAController {

    @RequestMapping("/hello")
    public String testA() {
        return "Hello world AAAAAAAAAA!";
        }
}

2.3.5 效果

注意:先启动服务器注册的项目eureka-server的服务,再启动本服务,不然会报错。

http://localhost:8762/hello

2.4 springcloud-eureka-clientb项目 

    2.4.1 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.fqyd</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.fqyd</groupId>
    <artifactId>springcloud-eureka-clientb</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-eureka-clientb</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

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

</project>

    2.4.2 application.yml

server:
  # 服务提供监听端口[eureka客户端],注意改端口不能与本机服务器端口冲突
  port: 8763
# 多环境通用部分[各个环境可以覆盖通用字段]
spring:
  application:
    # 通用名字,其他环境可以覆盖:为你的应用起个名字,该名字将注册到eureka注册中心
    name: client-service
eureka:
  client:
    # 是否将eureka自身作为应用注册到eureka注册中心,默认为true
    registerWithEureka: true
    serviceUrl:
      # 这里可以填写所有的eureka服务器地址并以','分离,当前面不能注册时候,自动选择后面的进行注册,排除单点故障问题
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}

 A和B服务的名称保持一致,后续,进行负载均衡调用的时候会用到该服务名。

2.4.3 controller类

package com.fqyd.controller;

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

@RestController
public class ServiceAController {

    @RequestMapping("/hello")
    public String testA() {
        return "Hello world BBBBBBBB!";
    }
}

2.4.4 启动类

package com.fqyd;

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

@EnableDiscoveryClient
@SpringBootApplication
public class SpringcloudEurekaClientbApplication {

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

}

2.4.5.效果 

http://localhost:8763/hello

 2.5 springcloud-ribbon-customer 消费者项目

 2.5.1 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.fqyd</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.fqyd</groupId>
    <artifactId>ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ribbon</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- spring cloud 客户注册 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- Ribbon依赖 -->
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-httpclient</artifactId>
        </dependency>
        <!-- 加入熔断机制依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>
</project>

2.5.2 application.yml

# 多环境通用部分[各个环境可以覆盖通用字段]
spring:
  application:
    # 通用名字,其他环境可以覆盖:为你的应用起个名字,该名字将注册到eureka注册中心
    name: ribbon-consumer
server:
  # 服务提供监听端口[eureka客户端],注意改端口不能与本机服务器端口冲突
  port: 8764
eureka:
  client:
    # 是否将eureka自身作为应用注册到eureka注册中心,默认为true
    registerWithEureka: true
    serviceUrl:
      # 这里可以填写所有的eureka服务器地址并以','分离,当前面不能注册时候,自动选择后面的进行注册,排除单点故障问题
      defaultZone: http://localhost:8761/eureka/
      # 这里可以填写所有的eureka服务器地址并以','分离,当前面不能注册时候,自动选择后面的进行注册,排除单点故障问题

2.5.3 controller

package com.fqyd.controller;

import com.fqyd.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CustomerController {
    
    @Autowired
    HelloService service;
    
    @RequestMapping("/hello")
    public String customer(){
        return service.helloService();
    }
}

2.5.4 service类

package com.fqyd.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;            // 负载均衡ribbon对象

    // 熔断错误回调方法
    public String helloFallBack(){
        return "Error occurre熔断错误!";
    }

    /**
     * 调用Eureka系统中名都为test-service的ribbon_service_a或ribbon_service_b的方法/hello
     * @return
     */
    // 注解指定发生错误时的回调方法
    @HystrixCommand(fallbackMethod="helloFallBack")
    public String helloService(){
        // Get请求调用服务,restTemplate被@LoadBalanced注解标记,Get方法会自动进行负载均衡
        // restTemplate会交替调用service_a和service_b
        return restTemplate.getForObject("http://client-service/hello", String.class);
    }
}

注意:这里增加了熔断机制,如果直接调用helloService失败后,会直接调用fallbackMethod="helloFallBack"对应的方法。主要是解决,例如,服务提供者没有启动,先启动消费者,调用失败的问题。

2.5.5启动者

package com.fqyd;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.client.RestTemplate;

@EnableCircuitBreaker         // 开启断路器功能,进行容错管理
@EnableDiscoveryClient        // 开启服务发现功能
@EnableEurekaClient
@SpringBootApplication
@ComponentScan(basePackages={"com"})
public class RibbonApplication {

    @Bean                     // 注册一个具有容错功能的RestTemplate
    @LoadBalanced
        // 开启负载均衡客户端
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

}

2.5.6效果

不启动服务提供者A或者B,启动服务注册者项目和消费者项目

启动服务提供者A或者B,启动服务注册者项目和消费者项目

一直刷新页面,发现AAAAA和BBBB是1:1轮询的,负载均衡默认就是1:1各自的比重一样。

项目代码地址:

链接:https://pan.baidu.com/s/1cNxgbzk2X_oe4sMCTjIZjQ 
提取码:ujnt 
复制这段内容后打开百度网盘手机App,操作更方便哦

如果你热衷技术,喜欢交流,欢迎加入我们! 

猜你喜欢

转载自blog.csdn.net/qq_16855077/article/details/93207884