天天看点

SpringCloud学习记录(四)—— Ribbon负载均衡前言一、思考二、入门案例

文章目录

  • 前言
  • 一、思考
    • 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 注册中心

SpringCloud学习记录(四)—— Ribbon负载均衡前言一、思考二、入门案例

2.5.2 user-service

SpringCloud学习记录(四)—— Ribbon负载均衡前言一、思考二、入门案例

2.5.2 consumer-service

SpringCloud学习记录(四)—— Ribbon负载均衡前言一、思考二、入门案例

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 默认的负载均衡策略

轮询:实例循环逐个进行访问

SpringCloud学习记录(四)—— Ribbon负载均衡前言一、思考二、入门案例

2.6.3.1 随机策略

随机策略:随机访问实例

SpringCloud学习记录(四)—— Ribbon负载均衡前言一、思考二、入门案例

2.7 小结

  1. 负载均衡是一种思想,是帮助我们在同一服务多实例的情况下选择出可使用的具体的某一实例的一种思想;
  2. ribbon是Eureka集成的一种负载均衡的组件;
  3. ribbon的敷在均衡策略可以是spring内置的,也可以是自定义实现的。

继续阅读