SpringCloud-Ribbon-客户端负载均衡

客户端负载均衡架构

在这里插入图片描述

  • Ribbon在工作时分成两步 :
    • 第一步先选择EurekaServer,它优先选择在同一个区域内负载均衡较少的Server。
    • 第二步在根据用户指定的策略,在从server去到的服务注册列表中选择一个地址。
  • 其中Ribbon提供了多种策略,比如轮询(默认),随机和根据响应时间加权重…等等

测试

  • 3个数据库
  • 3个Eureka注册中心
  • 3个服务提供者
  • 1个服务消费者

3个数据库 db01、db02、db03

CREATE TABLE dept(
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source VARCHAR(60)
);
INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('运维

在这里插入图片描述

3个Eureka注册中心

  1. 导入依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
  1. 配置文件
server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com     #eureka服务端的实例名称
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

  1. 启动服务类
@SpringBootApplication
@EnableEurekaServer
public class Eureka_7001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Eureka_7001.class,args);
    }
}
  • 其他两个Eureka注册中心一样,只需要改动
    • 端口
    • 与另外两个Eureka注册中心互相注册的defaultZone
    • 启动类的名字

3个服务提供者

  • 因为服务提供者与数据库直接连接,所以配置比较复杂
  1. 导入依赖
<dependencies>
        <!--引入自定义的模块,我们就可以使用这个模块中的类了-->
        <dependency>
            <groupId>com.qk</groupId>
            <artifactId>springcloud-01-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>


    </dependencies>
  1. SSM三层
  • mapper层
@Mapper
@Repository
public interface DeptMapper {
    
    
    public boolean addDept(Dept dept); //添加一个部门
    public Dept queryById(Long id); //根据id查询部门
    public List<Dept> queryAll(); //查询所有部门
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qk.springcloud.mapper.DeptMapper">
    <insert id="addDept" parameterType="Dept">
        insert into dept (dname,db_source) values (#{dname},DATABASE());
    </insert>
    <select id="queryById" resultType="Dept" parameterType="Long">
        select deptno,dname,db_source from dept where deptno = #{deptno};
    </select>
    <select id="queryAll" resultType="Dept">
        select deptno,dname,db_source from dept;
    </select>
</mapper>

  • service层
public interface DeptService {
    
    

    public boolean addDept(Dept dept); //添加一个部门
    public Dept queryById(Long id); //根据id查询部门
    public List<Dept> queryAll(); //查询所有部门
}
@Service
public class DeptServiceImpl implements DeptService{
    
    

    //自动注入
    @Autowired
    private DeptMapper deptMapper;

    @Override
    public boolean addDept(Dept dept) {
    
    
        return deptMapper.addDept(dept);
    }
    @Override
    public Dept queryById(Long id) {
    
    
        return deptMapper.queryById(id);
    }
    @Override
    public List<Dept> queryAll() {
    
    
        return deptMapper.queryAll();
    }
}
  • controller层
@RestController
@RequestMapping("/dept")
public class DeptController {
    
    
    @Autowired
    private DeptService service;
    @Autowired
    private DiscoveryClient client;
    // @RequestBody
// 如果参数是放在请求体中,传入后台的话,那么后台要用@RequestBody才能接收到
    @PostMapping("/add")
    public boolean addDept(Dept dept) {
    
    
        return service.addDept(dept);
    }
    @GetMapping("/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
    
    
        return service.queryById(id);
    }
    @GetMapping("/list")
    public List<Dept> queryAll() {
    
    
        return service.queryAll();
    }

    @GetMapping("/discovery")
    public Object discovery(){
    
    
     //获得微服务列表清单
        List<String> list = client.getServices();
        System.out.println("client.getServices()==>"+list);
     //得到一个具体的微服务!
        List<ServiceInstance> serviceInstanceList =
                client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance serviceInstance : serviceInstanceList) {
    
    
            System.out.println(
                    serviceInstance.getServiceId()+"\t"+
                            serviceInstance.getHost()+"\t"+
                            serviceInstance.getPort()+"\t"+
                            serviceInstance.getUri()
            );
        }
        return this.client;
    }

}
  • 配置文件
server:
  port: 8001
mybatis:
  type-aliases-package: com.qk.springcloud.pojo   #扫描api Moudle 下的实体类
  mapper-locations: classpath:mybatis/mapper/*.xml   #扫描provider Moudle 下的实体类映射文件
  config-location: classpath:mybatis/mybatis-config.xml   #扫描provider Moudle 下的mybatis配置文件
spring:
  application:
    name: springcloud-provider-dept   #给项目起个名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: root
# 将服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-8001
    prefer-ip-address: true # true访问路径可以显示IP地址
info:
  app.name: qk-springcloud
  company.name: www.qk.com

  • 启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class DeptProvider_8001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}
  • 另外两个服务提供者与上面的代码一样,只需要改动
    • 配置文件中连接数据库的url里的数据库,3个分别为db01、db02、db03
    • 配置文件中Eureka配置的instance-id,3个分别为springcloud-800、springcloud-8002、springcloud-8003

1个服务消费者

  1. 导入依赖
<dependencies>
        <dependency>
            <groupId>com.qk</groupId>
            <artifactId>springcloud-01-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <!--ribbon-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

        <!--消费方从服务注册中心获知有哪些地址可用:因此也需要Eureka-->
        <!--Eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
    </dependencies>
  1. 消费者的接口
@RestController
@RequestMapping("/consumer")
public class DeptConsumerController {
    
    
    //理解:消费者,不应该有service层
    // 远程调用 调用http://localhost:8001下的数据
    // 使用RestTemplate访问restful接口非常的简单粗暴且无脑,提供多种便捷访问远程http服务的方法,简单的restful服务模板
    // getForObject (请求地址 , 实体 ,响应类型)
    // (url,requestMap,ResponseBean.class) 这三个参数分别代表
    // REST请求地址,请求参数,Http响应转换 被 转换成的对象类型
    @Autowired
    private RestTemplate restTemplate;
    //private static final String REST_URL_PREFIX = "http://localhost:8001";

    //ribbon 通过服务名来访问
    private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";
    @RequestMapping("/dept/add")
    public boolean add(Dept dept){
    
    
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }
    @RequestMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
    }
    @RequestMapping("/dept/list")
    public List<Dept> list(){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }

    /**
     * 以前,消费者访问生产者,访问的是固定的生产者的url,
     * 在使用负载均衡后,url应该作为一个变量,通过服务名(生产者的名字)来访问,
     * 身为消费者,自身的defaultZone,设置了3个消费路径(注册中心),想要消费服务可以通过三个服务注册中心,消费
     * 一个生产者服务也在三个服务注册中心注册
     * 所以消费者想要消费,先通过负载均衡选择一个注册中心,然后根据服务名消费对应的服务
     */

}

  • 接口配置文件
@Configuration
public class RestTemplateBean {
    
    
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
    
    
        return new RestTemplate();
    }

    /**
     * 配置负载均衡实现RestTemplate。
     * 原本是通过RestTemplate远程访问指定url的接口,现在只需要在RestTemplate上添加@LoadBalanced就可以实现Ribbon负载均衡
     */

}
  1. 配置文件
server:
  port: 80


eureka:
  client:
    register-with-eureka: false   # 不向注册中心注册自己,作为消费者只需要去注册中心消费服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/   # 可以从哪几个注册中心去取
  1. 启动类
//Eureka和Ribbon整合以后,客户端可以根据服务名称直接调用,不用关心IP地址和端口号
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}

搭建完毕

测试结果

在这里插入图片描述
在这里插入图片描述


tips

  • 服务的名字在Eureka注册中心会被自动转换为大写,在消费者里,服务名无论大小写都不影响获取指定服务

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40429067/article/details/114282664