Spring Cloud Ribbon实例

Spring Cloud Netflix Ribbon简介

Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。ribbon默认提供很多种负载均衡算法,例如 轮询、随机 等等。甚至包含自定义的负载均衡算法。Ribbon可以用于解决并提供微服务的负载均衡的问题。

使用 Ribbon开发微服务项目

  1. 创建Spring Cloud项目
  • 配置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>
    <packaging>pom</packaging>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>
    <groupId>com.wangpx</groupId>
    <artifactId>springCloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


</project>
  1. 创建公共资源model
package com.wangpx.pojo;

import java.io.Serializable;
import java.util.Objects;

/**
 * @program: springCloud
 * @description:
 * @author: wangpx
 * @create: 2020-02-06 14:52
 */
public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private String remark;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", remark='" + remark + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) &&
                Objects.equals(username, user.username) &&
                Objects.equals(password, user.password) &&
                Objects.equals(remark, user.remark);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password, remark);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public User() {
    }

    public User(Integer id, String username, String password, String remark) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.remark = remark;
    }
}

  1. 创建Application Service服务提供者model
  • 配置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">
    <parent>
        <artifactId>springCloud</artifactId>
        <groupId>com.wangpx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>applicationService</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.wangpx</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

</project>
  • 服务提供者代码
package com.wangpx.controller;

import com.wangpx.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * @program: springCloud
 * @description:
 * @author: wangpx
 * @create: 2020-02-06 14:54
 */
@RestController
public class UserController {
    @RequestMapping("/user/save")
    public Map<String,Object> save(User user){
        System.out.println("新增用户数据:"+user);
        Map<String,Object> map=new HashMap<>();
        map.put("code",200);
        map.put("message","用户新增成功");
        return map;
    }
}

  • 启动类
package com.wangpx;

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

/**
 * @program: springCloud
 * @description:
 * @author: wangpx
 * @create: 2020-02-06 14:58
 */
@SpringBootApplication
@EnableEurekaClient
public class RibbonAppServiceApp {
    public static void main(String[] args) {
        SpringApplication.run(RibbonAppServiceApp.class,args);
    }
}

  • yml配置文件
server:
    port: 8080

spring:
    application:
        name: ribbon-app-service
eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/
  1. 创建Application Client服务消费者model
  • 配置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">
   <parent>
       <artifactId>springCloud</artifactId>
       <groupId>com.wangpx</groupId>
       <version>1.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>

   <artifactId>applicationClient</artifactId>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
       </dependency>
       <dependency>
           <groupId>com.wangpx</groupId>
           <artifactId>commons</artifactId>
           <version>1.0-SNAPSHOT</version>
       </dependency>
   </dependencies>


</project>
  • 服务接口
package com.wangpx.client.service;

import com.wangpx.pojo.User;

import java.util.Map;

public interface UserService {
    Map<String,Object> save(User user);
}

  • 服务接口的实现类
package com.wangpx.client.service.impl;

import com.wangpx.client.service.UserService;
import com.wangpx.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;


import java.util.Map;

/**
 * @program: springCloud
 * @description:
 * @author: wangpx
 * @create: 2020-02-06 15:05
 */
@Service
public class UserServiceImpl implements UserService {
    /**
     * LoadBalancerClient 是封装了Ribbon技术中的负载均衡客户端对象
     */
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Override
    public Map<String, Object> save(User user) {
        //ServiceInstance 封装了服务的基本信息,如 IP,端口
        ServiceInstance instance = loadBalancerClient.choose("ribbon-app-service");
        //拼接访问地址
        StringBuilder builder = new StringBuilder("");
        builder.append("http://").append(instance.getHost()).append(":")
                .append(instance.getPort()).append("/user/save")
                .append("?username=").append(user.getUsername())
                .append("&password=").append(user.getPassword())
                .append("&remark=").append(user.getRemark());
        System.out.println("本地访问地址:"+builder.toString());
        //创建一个Rest访问客户端模板对象
        RestTemplate restTemplate = new RestTemplate();
        //约束响应结果类型
        ParameterizedTypeReference<Map<String,Object>> reference
                = new ParameterizedTypeReference<Map<String,Object>>(){};
        //远程访问application service
        ResponseEntity<Map<String, Object>> exchange =
                restTemplate.exchange(builder.toString(), HttpMethod.GET, null, reference);
        Map<String, Object> body = exchange.getBody();
        return body;
    }
}

  • 控制器代码
package com.wangpx.client.controller;

import com.wangpx.client.service.UserService;
import com.wangpx.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * @program: springCloud
 * @description:  服务消费端
 * @author: wangpx
 * @create: 2020-02-06 15:03
 */
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/user/save")
    public Map<String,Object> save(User user){
        Map<String, Object> save = userService.save(user);
        System.out.println("远程调用返回的结果:"+save);
        return save;
    }
}

  • 配置yml配置文件
server:
    port: 8082

spring:
    application:
        name: ribbon-app-client

eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/
  • 启动类
package com.wangpx.client;

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

/**
 * @program: springCloud
 * @description:
 * @author: wangpx
 * @create: 2020-02-06 16:51
 */

@SpringBootApplication
@EnableEurekaClient
public class RibbonClientApp {
    public static void main(String[] args) {
        SpringApplication.run(RibbonClientApp.class,args);
    }
}

  • 测试
    先启动eureka,然后再启动用户微服务,启动两个端口8080和8081,修改application.yml便可以。最后启动ribbon-client。
  • 启动成功后,访问页面,输入url地址:http://localhost:8761/
    在这里插入图片描述

Ribbon常见的负载均衡策略

1.使用负载均衡带来的好处

  • 当集群里的 1台或多台服务器宕机时,剩余没有宕机的服务器可以保证服务的正常使用。
  • 使用了更多的机器保证了机器的良性使用,不会由于某一高峰时刻导致系统cpu急剧上升
  1. 使用了更多的机器保证了机器的良性使用,不会由于某一高峰时刻导致系统cpu急剧上升
  2. Ribbon就属于进程内负载均衡,它只是一个类库,集成于Eureka Client进程,Eureka Client进程通过访问注册中心Eureka Server发现服务列表,发现的服务列表信息是由ribbon来管理的。当访问Application Service的时候,Application Client会通过ribbon来找到合适的Application Service地址信息,并发起远程调用请求。
    在这里插入图片描述
id 策略名称 策略对应的 类名 实现原理
1 轮询策略(默认) RoundRobinRule 轮询策略表示每次都顺序取下一个provider,比如一共有5个provider,第1次取第1个,第2次取第2个,第3次取第3个,以此类推
2 权重轮询策略(常用) WeightedResponseTimeRule 1.根据每个provider的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个provider的平均响应时间,当信息足够时,给每个provider附上一个权重,并按权重随机选择provider,高权越重的provider会被高概率选中。
3 随机策略(不推荐) RandomRule 从provider列表中随机选择一个provider
4 最少并发数策略(应用在硬件软件环境一致的情况下) BestAvailableRule 选择正在请求中的并发数最小的provider,除非这个provider在熔断中。
5 在“选定的负载均衡策略”基础上进行重试机制 RetryRule 1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择provider不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider
6 可用性敏感策略(一般在同区域内服务集群环境中使用) AvailabilityFilteringRule 过滤性能差的provider,有2种:第一种:过滤掉在eureka中处于一直连接失败provider第二种:过滤掉高并发的provider
7 区域敏感性策略(应用在大型的,物理隔离分布式环境中) ZoneAvoidanceRule 1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。
  1. 配置负载均衡策略
server:
    port: 8082

spring:
    application:
        name: ribbon-app-client

eureka:
    client:
        service-url:
            defaultZone: http://localhost:8761/eureka/
# 设置负载均衡策略。application-service为设置负载均衡的服务名称
application-service:
    ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

发布了18 篇原创文章 · 获赞 6 · 访问量 231

猜你喜欢

转载自blog.csdn.net/penerx/article/details/104200918