注意: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相当于下图的ribbon,服务器相当于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 效果
启动服务注册,可以通过如下地址,看到如下图所示。
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的服务,再启动本服务,不然会报错。
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.效果
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,操作更方便哦