文章目錄
- 前言
- 一、思考
-
- 1.1 什麼是負載均衡
- 1.1 什麼是Ribbon
- 二、入門案例
-
- 2.1 父工程pom檔案
- 2.2 注冊中心(Eureka-service)
-
- 2.2.1 pom.xml檔案
- 2.2.2 配置檔案application.yml
- 2.3 服務提供者(user-service)
-
- 2.3.1 pom.xml檔案
- 2.3.2 配置檔案application.yml
- 2.3.3 實體類User.java
- 2.3.4 持久層接口UserMapper.java
- 2.3.5 業務層接口UserService.java和實作類UserServiceImpl.java
- 2.3.6 控制層UserController.java
- 2.3.7 啟動類UserServiceApplication.java
- 2.4 服務消費者(consumer-service)
-
- 2.4.1 pom.xml檔案
- 2.4.2 配置檔案application.yml
- 2.4.3 實體類User.java
- 2.4.4 控制層UserController.java
- 2.4.5 啟動類ConsumerServiceApplication.java
- 2.5 測試結果
-
- 2.5.1 注冊中心
- 2.5.2 user-service
- 2.5.2 consumer-service
- 2.6 Ribbon負載均衡算發的修改
-
- 2.6.1 配置檔案application.yml
- 2.6.2 測試類
- 2.6.3 測試結果
-
- 2.6.3.1 預設的負載均衡政策
- 2.6.3.1 随機政策
- 2.7 小結
前言
這裡記錄我學習Ribbon負載均衡的學習過程;Ribbon代碼位址
記錄我的示範代碼和了解,如果有錯誤,歡迎各位指正!!!
提示:以下是本篇文章正文内容,下面案例可供參考
一、思考
1.1 什麼是負載均衡
在我們Eureka的學習經驗中,我們使用的user-service(服務提供者)都是隻有一個執行個體,我們通過DiscoveryClient擷取服務的執行個體資訊,然後從其中擷取i位址(ip)和端口(port),然後進行遠端通路;但是在實際的生産環境中,我們往往是多個user-service執行個體形成叢集,此時我們注冊中心的服務清單将會有很多個user-service 的執行個體,這時候我們應該通路哪一個呢?
這種情況下我們就需要編寫負載均衡算法,在多個執行個體清單中選擇我們要使用的執行個體,這就是負載均衡。
1.1 什麼是Ribbon
Eureka幫我們內建的負載均衡元件就是Ribbon
二、入門案例
2.1 父工程pom檔案
<?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.kaikeba</groupId>
<artifactId>springcloud04</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user-service</module>
<module>eureka-service</module>
<module>consumer-service</module>
</modules>
<!-- springboot -->
<parent>
<artifactId>spring-boot-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.1.5.RELEASE</version>
<relativePath/>
</parent>
<!-- 版本管理 -->
<properties>
<java.version>1.8</java.version>
<spring-cloud-version>Greenwich.SR1</spring-cloud-version>
<tk-mybatis.version>2.1.5</tk-mybatis.version>
<mysql-connection-version>8.0.25</mysql-connection-version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- tk mybatis -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<!-- mysql驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connection-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 注冊中心(Eureka-service)
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">
<parent>
<artifactId>springcloud04</artifactId>
<groupId>com.kaikeba</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-service</artifactId>
<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: 8080
spring:
application:
name: eureka-server
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka
register-with-eureka: false
server:
enable-self-preservation: false # 關閉自我保護模式(預設打開)
eviction-interval-timer-in-ms: 60000 # 掃描失效服務的時間間隔, 預設是60 * 1000ms
啟動類UserServiceApplication.java
package com.kaikeba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* 負載均衡Ribbon:注冊中心
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class);
}
}
2.3 服務提供者(user-service)
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">
<parent>
<artifactId>springcloud04</artifactId>
<groupId>com.kaikeba</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service</artifactId>
<dependencies>
<!-- Eureka用戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
2.3.2 配置檔案application.yml
server:
port: ${port:10000} # 端口
spring:
application:
name: user-service # 服務名稱
# 資料源資訊
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud
username: root
password: root
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka # 注冊中心的位址
instance:
ip-address: 127.0.0.1 # 顯示的ip
prefer-ip-address: true # 設定使用ip而不是主機名
lease-expiration-duration-in-seconds: 90 # 服務失效時間:預設90s
lease-renewal-interval-in-seconds: 30 # 服務續約間隔,預設30s
2.3.3 實體類User.java
package com.kaikeba.consumer;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.persistence.*;
import java.util.Date;
@Data
@Accessors(chain = true)
@Table(name = "tb_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "user_name")
private String username;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private Date created;
private Date updated;
private String note;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", birthday=" + birthday +
", created=" + created +
", updated=" + updated +
", note='" + note + '\'' +
'}';
}
}
2.3.4 持久層接口UserMapper.java
package com.kaikeba.mapper;
import com.kaikeba.consumer.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends tk.mybatis.mapper.common.Mapper<User> {
}
2.3.5 業務層接口UserService.java和實作類UserServiceImpl.java
UserService.java
package com.kaikeba.service;
import com.kaikeba.consumer.User;
public interface UserService {
/**
* 根據id查詢
*/
User findById(Integer userId);
}
UserServiceImpl.java
package com.kaikeba.service.impl;
import com.kaikeba.consumer.User;
import com.kaikeba.mapper.UserMapper;
import com.kaikeba.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
public User findById(Integer userId) {
User user = userMapper.selectByPrimaryKey(userId);
return user;
}
}
2.3.6 控制層UserController.java
package com.kaikeba.controller;
import com.kaikeba.consumer.User;
import com.kaikeba.service.UserService;
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;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/{userId}")
private User searchById(@PathVariable("userId")Integer userId) {
User user = userService.findById(userId);
return user;
}
}
2.3.7 啟動類UserServiceApplication.java
package com.kaikeba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class);
}
}
2.4 服務消費者(consumer-service)
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">
<parent>
<artifactId>springcloud04</artifactId>
<groupId>com.kaikeba</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer-service</artifactId>
<dependencies>
<!-- Eureka用戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
</dependency>
</dependencies>
</project>
2.4.2 配置檔案application.yml
server:
port: 20000
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka
registry-fetch-interval-seconds: 10 # 拉取服務位址清單的間隔時間,預設30s
spring:
application:
name: consumer-service
2.4.3 實體類User.java
這裡的實體類隻是負責接收遠端調用傳回的結果資料
package com.kaikeba.consumer;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
public class User {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private Date created;
private Date updated;
private String note;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", birthday=" + birthday +
", created=" + created +
", updated=" + updated +
", note='" + note + '\'' +
'}';
}
}
2.4.4 控制層UserController.java
package com.kaikeba.controller;
import com.kaikeba.consumer.User;
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 javax.annotation.Resource;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Resource
private RestTemplate restTemplate;
@RequestMapping("/{userId}")
public User searchById(@PathVariable("userId")Integer userId) {
// 通過服務名user-service擷取執行個體
String url = "http://user-service/user/" + userId;
return restTemplate.getForObject(url, User.class);
}
}
2.4.5 啟動類ConsumerServiceApplication.java
package com.kaikeba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class ConsumerServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerServiceApplication.class);
}
@Bean
@LoadBalanced // 添加負載均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2.5 測試結果
2.5.1 注冊中心

2.5.2 user-service
2.5.2 consumer-service
2.6 Ribbon負載均衡算發的修改
ribbon的敷在均衡算法預設是輪詢,但是我們可以在配置檔案中進行修改;
修改的配置,修改格式: {服務名稱}.ribbon.NFLoadBalancerRuleClassName ,值就是IRule的實作類,這個實作類既可以是spring已經提供的,也可以是自己自定義實作的。
如:
# 修改負載均衡的規則
user-service: # 服務明
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 随機政策
2.6.1 配置檔案application.yml
server:
port: 20000
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka
registry-fetch-interval-seconds: 10 # 拉取服務位址清單的間隔時間,預設30s
spring:
application:
name: consumer-service
# 修改負載均衡的規則
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随機政策
2.6.2 測試類
package com.kaikeba.consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* 負載均衡政策 測試
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class LoadBalancerTest {
@Resource
private RibbonLoadBalancerClient ribbonLoadBalancerClient;
@Test
public void testBalancer() {
for (int i = 0; i < 100; i++) {
ServiceInstance serviceInstance = ribbonLoadBalancerClient.choose("user-service");
System.out.println(serviceInstance.getHost() + ":" + serviceInstance.getPort());
}
}
}
2.6.3 測試結果
2.6.3.1 預設的負載均衡政策
輪詢:執行個體循環逐個進行通路
2.6.3.1 随機政策
随機政策:随機通路執行個體
2.7 小結
- 負載均衡是一種思想,是幫助我們在同一服務多執行個體的情況下選擇出可使用的具體的某一執行個體的一種思想;
- ribbon是Eureka內建的一種負載均衡的元件;
- ribbon的敷在均衡政策可以是spring内置的,也可以是自定義實作的。