Ribbon客户端负载均衡

前面我们了解了服务调用Rest和服务注册中心Eureka。现在我们来了解微服务架构springcloud技术栈之Ribbon负载均衡器。

  • 什么是负载均衡?
  • LB,即负载均衡(Load Balance),负载均衡是微服务架构中经常使用的一种技术。 负载均衡是我们处理高并发、缓解网络压力和进行服务端扩容的重要手段之一,简单的说就是将用户的请求平摊的分配到多个服务上,从而实现系统的高可用性集群。
  • 负载均衡可通过 硬件设备 及 软件 进行实现,软件比如:Nginx等,硬件比如:F5等
  • 负载均衡相应的在中间件,例如:Dubbo 和 SpringCloud 中均给我们提供了负载均衡组件。
    负载均衡架构图:
    Ribbon客户端负载均衡_第1张图片
    用户请求先到达负载均衡器(也相当于一个服务),负载均衡器根据负载均衡算法将请求转发到微服务。负载均衡器维护一份服务端列表,根据负载均衡算法 将请求转发到相应的微服务上,负载均衡 算法有:轮训、随机、加权轮训、加权随机、地址哈希等方法,所以负载均衡可以为微服务集群分担请求,降低系统的压力。
  • 什么是客户端负载均衡(Ribbon)?
    上图是服务端负载均衡,客户端负载均衡 与 服务端负载均衡的区别在于:客户端负载均衡要维护一份服务列表。客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置。在客户端负载均衡中,每个客户端服务都有一份自己要访问的服务端清单,这些清单统统都是从Eureka服务注册中心获取的。而在服务端负载均衡中,只要负载均衡器维护一份服务端列表 。
    Spring Cloud Ribbon 是基于 Netflix 公司发布的开源项目 Ribbon 进行封装的一套客户端负载均衡器 。
    Ribbon 从 Eureka Server 获取服务列表,Ribbon根据负载均衡算法直接请求到具体的微服务,中间省去了负载均衡服务。
    下图是Ribbon负载均衡的流程图:
    Ribbon客户端负载均衡_第2张图片
  1. 在消费者微服务中使用 Ribbon 实现负载均衡,Ribbon 先从EurekaServer中获取服务列表。
  2. Ribbon 根据负载均衡的算法(默认轮训算法)去调用微服务。
  • Ribbon服务调用配置实战
    针对我们在Rest一节中创建的microservice-cloud-04-consumer-product-80服务消费者工程做以下修改:
  1. 在pom.xml文件里面添加以下配置


	org.springframework.cloud
	spring-cloud-starter-netflix-eureka-client

添加以上Eureka-client客户端依赖时,工程会自动引入Ribbon相关的依赖

  1. 修改application.yml文件
server:
	port: 80
eureka:
	client:
		registerWithEureka: false # 服务注册,false表示不将本消费者注册到 Eureka 服务器
		fetchRegistry: true # 服务发现,true从 Eureka 服务器中获取注册信息
		serviceUrl:
			defaultZone:
		http://eureka6001.com:6001/eureka/,http://eureka6002.com:6002/eureka/
  1. 修改自定义配置类ConfigBean
    在自定义配置类中添加新注解 @LoadBalanced ,
    作用是:@LoadBalanced表示这个RestTemplate开启负载均衡,在调用服务提供者的接口时,可使用 服务名称
    替代真实IP地址。服务名称 就是服务提供者在application.yml中配置的spring.application.name属性的值 。
package com.mengxuegu.springcloud.config;
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;
/**
* 自定义配置类,
* @Auther: xiaomingjun
*/
@Configuration
public class ConfigBean {
	//@LoadBalanced表示这个RestTemplate开启负载均衡,在调用服务提供者的接口时,
	//可使用 服务名称 替代真实IP地址。服务名称 就是服务提供者在application.yml中
	//配置的spring.application.name属性的值。
	@LoadBalanced
	@Bean // 向容器中添加 RestTemplate 组件,直接通过此组件可调用 REST 接口
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}
}
  1. 修改消费者控制层ProductController_Consumer
    只需要修改调用Rest风格的服务提供者地址就可以了
package com.mengxuegu.springcloud.controller;
import com.mengxuegu.springcloud.entities.Product;
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;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @Auther: xiaomingjun
*/
@RestController
public class ProductController_Consumer {
	//private static final String REST_URL_PREFIX = "http://localhost:8001";
	//修改为Eureka注册中心的地址
	private static final String REST_URL_PREFIX = "http://MICROSERVICE-PRODUCT";
	@Autowired
	private RestTemplate restTemplate;
	
	@RequestMapping(value = "/consumer/product/add")
	public boolean add(Product product) {
		return restTemplate.postForObject(REST_URL_PREFIX + "/product/add", product,Boolean.class);
	}
	@RequestMapping(value = "/consumer/product/get/{id}")
	public Product get(@PathVariable("id") Long id) {
		return restTemplate.getForObject(REST_URL_PREFIX + "/product/get/" + id,Product.class);
	}
	@RequestMapping(value = "/consumer/product/list")
	public List list() {
		return restTemplate.getForObject(REST_URL_PREFIX + "/product/list",List.class);
	}
}
  1. 修改主启动类
    在主启动类上添加注解@EnableEurekaClient
package com.mengxuegu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
 * @Auther:xiaomingjun
*/
@EnableEurekaClient //向服务注册中心进行注册
@SpringBootApplication
public class MicroserviceProductConsumer_80 {
	public static void main(String[] args) {
		SpringApplication.run(MicroserviceProductConsumer_80.class, args);
	}
}

测试:
启动2个Eureka集群,再启动microservice-cloud-03-provider-product-8001服务提供者工程兵注册进Eureka
接着启动microservice-cloud-04-consumer-product-80服务消费者工程
访问: http://localhost/consumer/product/get/1
http://localhost/consumer/product/list
http://localhost/consumer/product/add?product_name=spring
通过以上测试,我们发现Ribbon和Eureka整合之后,消费者Consumer可以直接调用提供者服务,而不用再关心地址和端口号

  • ribbon负载均衡实战
    Ribbon 在工作时分成两步:
    第1步先选择 Eureka Server ,它优先选择在同一个区域内负载较少的server
    第2步再根据用户指定的策略,在从 Eureka Server 获取的服务注册列表中选择一个地址。 其中Ribbon提供
    了多种策略:比如轮询、随机和根据响应时间加权等。
    前面已经有一个商品提供者服务,为了能够体现ribbon负载均衡的作用,我们又创建了商品提供者服务 microservice-cloud-06-provider-product-8002。
  1. 配置pom.xml文件

	
		com.mengxuegu.springcloud
		microservice-cloud-02-api
		${project.version}
	
	
	
		org.springframework.cloud
		spring-cloud-starter-netflix-eureka-client
	
	
	
		org.springframework.boot
		spring-boot-starter-web
	
	
	
		org.mybatis.spring.boot
		mybatis-spring-boot-starter
	
	
		org.springframework.boot
		spring-boot-starter-test
	
	
		junit
		junit
	
	
		mysql
		mysql-connector-java
	
	
		com.alibaba
		druid
	

  1. 新建一个数据库
    各自的微服务分别连各自的数据库
DROP DATABASE IF EXISTS springcloud_db02;
CREATE DATABASE springcloud_db02 CHARACTER SET UTF8;
USE springcloud_db02;
CREATE TABLE product
(
	pid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
	product_name VARCHAR(50),
	db_source VARCHAR(50)
);
INSERT INTO product(product_name,db_source) VALUES('格力空调',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('海尔冰箱',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('小短裙',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('羽绒服',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('韩版休闲鞋',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('高贵鞋',DATABASE());
SELECT * FROM product;
  1. 拷贝8001服务消费者工程下Mybatis核心配置与映射配置文件到该该工程下
  2. 配置application.yml文件
server:
	port: 8002 # 服务端口
mybatis:
	config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
	type-aliases-package: com.mengxuegu.springcloud.entities # 所有Entity别名类所在包
	mapper-locations: classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
	application:
		name: microservice-product #这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name
	datasource:
		type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
		driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
		url: jdbc:mysql://127.0.0.1:3306/springcloud_db02?serverTimezone=GMT%2B8 #数据库名称
		username: root
		password: root
		dbcp2:
			min-idle: 5 # 数据库连接池的最小维持连接数
			initial-size: 5 # 初始化连接数
			max-total: 5 # 最大连接数
			max-wait-millis: 150 # 等待连接获取的最大超时时间
eureka:
	client:
		registerWithEureka: true # 服务注册开关
		fetchRegistry: true # 服务发现开关
		serviceUrl: # 客户端(服务提供者)注册到哪一个Eureka Server服务注册中心,多个用逗号分隔
			# 单机版 Eureka 服务注册中心
			#defaultZone: http://localhost:6001/eureka
			# 集群版 Eureka 服务注册中心
			defaultZone:
		http://eureka6001.com:6001/eureka,http://eureka6002.com:6002/eureka
	instance:
		instanceId: ${spring.application.name}:${server.port} # 指定实例ID,就不会显示主机名了
		preferIpAddress: true #访问路径可以显示IP地址
  1. 配置启动类
package com.mengxuegu.springcloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @Auther:xiaomingjun
*/
@EnableEurekaClient //本服务启动后会自动注册进Eureka服务器中
@MapperScan("com.mengxuegu.springcloud.mapper") //扫描包下面所有Mapper接口
@SpringBootApplication
public class ProductProvider_8002 {
	public static void main(String[] args) {
		SpringApplication.run(MicroserviceProductProvider_8002.class, args);
	}
}

测试:
启动2个Eureka集群
microservice-cloud-05-eureka-6001、 microservice-cloud-05-eureka-6002
启动2个商品提供者 微服务,并各自测试通过
http://localhost:8001/product/list
http://localhost:8002/product/list
启动服务消费者工程
microservice-cloud-04-consumer-product-80
客户端通过Ribbon完成负载均衡并访问上一步的Product微服务
http://localhost/consumer/product/list
注意观察看到返回的数据库名字,各不相同,负载均衡实现

你可能感兴趣的:(java,springcloud)