第111天学习打卡(SpringCloud 集群环境配置 对比Zookeeper 负载均衡及Ribbon Fegin负载均衡)

本文详细讲解了如何在Spring Cloud中配置Eureka服务注册中心,实现服务实例的高可用和负载均衡,以及Ribbon和Feign的使用,包括自定义负载均衡算法和应用实例配置。

集群环境配置

修改host的地址:C:\Windows\System32\drivers\etc

image-20210429110219069

EurekaServer_7001.java

package com.kuang.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
//启动之后访问页面  http://localhost:7001/
@SpringBootApplication
@EnableEurekaServer// EnableEurekaServer服务端启动类,可以接受别人注册进来
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class,args);
    }
}

application.yml

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #Eureka 服务端的实例名称
  client:
    register-with-eureka: false # 表示是否向eureka注册中心注册自己
    fetch-registry: false  # fetch-registry 如果为false,则表示自己为注册中心
    service-url: # 单机  defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 集群(关联)
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/


EurekaServer_7002.java

package com.kuang.springcloud;

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

//启动之后访问页面  http://localhost:7001/
@SpringBootApplication
@EnableEurekaServer// EnableEurekaServer服务端启动类,可以接受别人注册进来
public class EurekaServer_7002 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7002.class,args);
    }

}

application.yml

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/


EurekaServer_7003.java

package com.kuang.springcloud;

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

//启动之后访问页面  http://localhost:7001/
@SpringBootApplication
@EnableEurekaServer// EnableEurekaServer服务端启动类,可以接受别人注册进来
public class EurekaServer_7003 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7003.class,args);
    }
}

application.yml

server:
  port: 7003

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/


application.yml(springcloud-provider-dept-8001)

server:
  port: 8001

mybatis:
  type-aliases-package: com.kuang.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml



spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456

# Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

  instance:
    instance-id: springcloud-provider-dept8001 # 修改eureka上的默认描述信息

# info 配置
info:
  app.name: kuangshen-springcloud
  company.name: blog.kuangstudy.com

环境配置目录

image-20210429143616248

先启动7001 然后7002 7003 ,最后8001

访问结果图:

image-20210429143725156

可以分别访问7001 7002 7003 这几个端口Eureka (eureka7003.com)

对比Zookeeper

回顾CAP原则

RDBMS:关系型数据库 (Mysql、Oracle、sqlServer)====>ACID

NoSQL(redis、mongdb)====>CAP

ACID是什么?

  • A(Atomicity) 原子性
  • C(Consistency)一致性
  • I(Isolation)隔离性
  • D(Durability)持久性

CAP是什么?

  • C(Consistency) 强一致性
  • A(Availability)可用性
  • P(Partition tolerance) 分区容错性

CAP的三进二:CA、AP、CP

CAP理论的核心

  • 一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时实现两点。
  • 根据CAP原理,将NoSQL数据库分成满足CA原则,满足CP原则三大类:
    • CA:单点集群,满足一致性,可用性的系统,通常可扩展性较差
    • CP:满足一致性,分区容错性的系统,通常性能不是特别高
    • AP:满足可用性,分区容错性的系统,通常可能对一致性要求低一些。
作为服务注册中心,Eureka比Zookeeper好在哪里?

著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)、P(容错性)。

由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡。

  • Zookeeper保证的是CP;
  • Eureka保证的是AP;

Zookeeper保证的是CP

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接收服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的事件太长,30~120秒,且选举期间整个zookeepeer集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署环境下,因为网络问题是的zk集群失去master节点是较大概率会发生的事件,虽然服务最终能够恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

Eureka保证的是AP

Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保证注册服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了故障,此时会出现一下几种情况:

1.Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务

2.Eureka仍然能够接收新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点仍然可用)。

3.当网络稳定时,当前实例新的注册信息会被同步到其他节点中。

因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不像Zookeeper那样使整个注册服务瘫痪

ribbon

  • Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具
  • 简单来说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项如:连接超市、重试等等。简单来说,就是在配置文件中列出LoadBalance(简称LB:负载均衡)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法!

ribbon能干嘛?

  • LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
  • 负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)
  • 常见的负载均衡软件有Nginx, Lvs等等
  • dubbo、SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义
  • 负载均衡简单分类:
    • 集中式LB
      • 即在服务的消费方和提供方之间使用独立的LB设施,如Nginx:反向代理服务器,由该设施负责把访问请求通过某种策略转发至服务的提供方!
    • 进程式LB
      • 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
      • Ribbon就属于进内LB,它只是一个类库,集成于消费进程,消费方通过它来获取到服务提供方的地址。

负载均衡及Ribbon

springcloud-consumer-dept-80

ConfigBean.java

package com.kuang.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;

@Configuration
public class ConfigBean { // @Configuration  相当于---spring  里面的applicationContext.xml

    //配置负载均衡实现RestTemplate

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

DeptConsumerController.java

package com.kuang.springcloud.controller;

import com.kuang.springcloud.pojo.Dept;
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;

@RestController
public class DeptConsumerController {

    //理解:消费者,不应该有service层  service是通过url去请求
    //RestTemplate....有很多模板供我们直接调用   使用RestTemplate,需要注册到spring中
    //String url,实体: Map, Class<T> responseType  :请求的地址及响应的类型
    @Autowired
    private RestTemplate restTemplate; // RestTemplate 提供多种便捷访问远程http服务的方法,是简单的restful模板

//请求的地址是固定的所以把地址写死
 //   private static final String REST_URL_PREFIX = "http://localhost:8001";
    //Ribbon 我们这里的地址应该是一个变量,通过服务名来访问  服务名是SPRINGCLOUD-PROVIDER-DEPT  然后让它选择7001  或者7002 或者7003
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept){
        // REST_URL_PREFIX+"/dept/add",dept,Boolean.class  地址+ 具体的位置  传的对象   返回类型
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id")Long id) {

        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);//去远程服务上拿

    }
    @RequestMapping("/consumer/dept/list")
    public List<Dept> list(){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }

}

application.yml

server:
  port: 80
  # Eureka配置
eureka:
  client:
    register-with-eureka: false # 不向Eureka 注册自己
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

DeptConsumer_80.java

package com.kuang.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//Ribbon和Eureka整合以后,客户端可以直接调用  不用关心IP地址和端口号
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
    public static void main(String[] args){
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}

注意:要把先前的运行的全部停掉,然后运行7001, 7002, 7003,8001,运行完后进行测试

image-20210429163616806

最后再启动80端口(如果报错 。再把所有端口重新启动一次 然后测试就好了)

image-20210429163657705

image-20210429163712376

使用Ribbon实现负载均衡

image-20210429182601784

在数据库中创建表,先把数据库的表db01导出 ,导出后在把这个表移进SQL中进行 db02, db03的创建,然后在IDEA中进行连接

CREATE DATABASE /*!32312 IF NOT EXISTS*/`db02` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `db02`;

/*Table structure for table `dept` */

DROP TABLE IF EXISTS `dept`;

CREATE TABLE `dept` (
  `deptno` BIGINT NOT NULL AUTO_INCREMENT,
  `dname` VARCHAR(60) DEFAULT NULL,
  `db_source` VARCHAR(60) DEFAULT NULL,
  PRIMARY KEY (`deptno`)
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='部门表';

/*Data for the table `dept` */

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 ('运维部',DATABASE());




CREATE DATABASE /*!32312 IF NOT EXISTS*/`db03` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `db03`;

/*Table structure for table `dept` */

DROP TABLE IF EXISTS `dept`;

CREATE TABLE `dept` (
  `deptno` BIGINT NOT NULL AUTO_INCREMENT,
  `dname` VARCHAR(60) DEFAULT NULL,
  `db_source` VARCHAR(60) DEFAULT NULL,
  PRIMARY KEY (`deptno`)
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='部门表';

/*Data for the table `dept` */

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 ('运维部',DATABASE());




image-20210429165135746

springcloud-provider-dept-8001,springcloud-provider-dept-8002,springcloud-provider-dept-8003 三个项目里面的内容都是一致的 只是application.yml端口号改变了而已

image-20210429182731256

springcloud-provider-dept-8001

controller

DeptController.java

package com.kuang.springcloud.controller;

import com.kuang.springcloud.pojo.Dept;
import com.kuang.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

//Controller提供restful服务
@RestController  //以json格式传输
public class DeptController {
    @Autowired //这里就体现了controller调service层
    private DeptService deptService;
    //获取一些配置的信息,得到具体的微服务
    @Autowired
    private DiscoveryClient client;

    @PostMapping("/dept/add")//添加请求一般以post提交  get不安全
    public boolean addDept(Dept dept){
        return deptService.addDept(dept);

    }
    @GetMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id){
        return deptService.queryById(id);
    }
    @GetMapping ("/dept/list")
    public List<Dept> queryAll(){
        return deptService.queryAll();
    }

    //注册进来的微服务~ 获取一些消息~
    @GetMapping("/dept/discovery")
    public Object discovery(){
        // 获取微服务列表的清单
        List<String> services = client.getServices();
        System.out.println("discovery=>services:" + services);
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance instance : instances) {
            System.out.println(
                    instance.getHost()+"\t"
                    + instance.getPort()+"\t"
                    + instance.getUri()+"\t"
                    + instance.getServiceId()

            );


        }
        return this.client;


    }

}

dao

DeptDao.java

package com.kuang.springcloud.dao;

import com.kuang.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository //被spring托管
public interface DeptDao {

    public boolean addDept(Dept dept);
    public Dept queryById(Long id);//查询部门信息
    public List<Dept> queryAll();

}

service

DeptService.java

package com.kuang.springcloud.service;

import com.kuang.springcloud.pojo.Dept;

import java.util.List;

public interface DeptService {
    public boolean addDept(Dept dept);
    public Dept queryById(Long id);
    public List<Dept> queryAll();
}

DeptServiceImpl.java

package com.kuang.springcloud.service;

import com.kuang.springcloud.dao.DeptDao;
import com.kuang.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
@Service
public class DeptServiceImpl implements DeptService{

    @Autowired
    private DeptDao deptDao;
    @Override
    public boolean addDept(Dept dept) {
        return deptDao.addDept(dept);
    }

    @Override
    public Dept queryById(Long id) {
        return deptDao.queryById(id);
    }

    @Override
    public List<Dept> queryAll() {
        return deptDao.queryAll();
    }
}

DeptProvider_8001.java

package com.kuang.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;


//启动类

@SpringBootApplication
@EnableEurekaClient//在服务启动后自动注册到Eureka中
@EnableDiscoveryClient //服务发现
public class DeptProvider_8001 {
    public static void main(String[] args){
        SpringApplication.run(DeptProvider_8001.class,args);

    }
}

resources

mybatis.mapper

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
<!--  一级缓存是默认的    开启二级缓存-->
      <setting name="cacheEnabled" value="true"/>
  </settings>
</configuration>

application.yml

server:
  port: 8001

mybatis:
  type-aliases-package: com.kuang.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml



spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 3个服务名称一致, 只是请求不一样了
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456

# Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

  instance:
    instance-id: springcloud-provider-dept8001 # 修改eureka上的默认描述信息

# info 配置
info:
  app.name: kuangshen-springcloud
  company.name: blog.kuangstudy.com

application.yml(springcloud-provider-dept-8002)

server:
  port: 8002

mybatis:
  type-aliases-package: com.kuang.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml



spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/db02?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456

# Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

  instance:
    instance-id: springcloud-provider-dept8002 # 修改eureka上的默认描述信息

# info 配置
info:
  app.name: kuangshen-springcloud
  company.name: blog.kuangstudy.com

application.yml(springcloud-provider-dept-8003)

server:
  port: 8003

mybatis:
  type-aliases-package: com.kuang.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml



spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/db03?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456

# Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

  instance:
    instance-id: springcloud-provider-dept8003 # 修改eureka上的默认描述信息

# info 配置
info:
  app.name: kuangshen-springcloud
  company.name: blog.kuangstudy.com

因为内存不够:启动了7001端口 8001 8002 8003 端口,如果在期间报错也没问题可以访问到

结果访问:

image-20210429184019764

image-20210429184045641

image-20210429184103533

image-20210429184144861

随机的负载均衡

ConfigBean.java

 package com.kuang.springcloud.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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;

@Configuration
public class ConfigBean { // @Configuration  相当于---spring  里面的applicationContext.xml

    //配置负载均衡实现RestTemplate
    //IRule
    // AvailabilityFilteringRule :会先过滤掉,跳闸,访问故障的服务,对剩下的进行轮询
    // RoundRobinRule: 轮询
    //RandomRule: 随机
    // RetryRule: 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行重试


    @Bean
    @LoadBalanced//Ribbon
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}

访问这里是随机变的

image-20210429192532209

自定义负载均衡算法:
springcloud-consumer-dept-80

项目的目录顺序:

image-20210429200847721

myrule

KuangRandomRule.java

package com.kuang.myrule;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class KuangRandomRule extends AbstractLoadBalancerRule {

    //每个服务,访问5次,就换下一个服务(总共有3个服务)
    //total=0 总数默认为0 ,如果=5,我们指向下一个服务节点
    //index = 0,总共有三次, 如果total = 5,那么index+1
    private int total = 0; //被调用的次数
    private int currentIndex = 0;//当前是谁在提供服务


    public KuangRandomRule() {
    }

    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;

            while(server == null) {
                //线程中断
                if (Thread.interrupted()) {
                    return null;
                }

                List<Server> upList = lb.getReachableServers(); //获得活着的服务
                List<Server> allList = lb.getAllServers();//获得全部的服务
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }

            //    int index = this.chooseRandomInt(serverCount); //生成区间随机数
             //   server = (Server)upList.get(index);//从活着的服务中,随机获取一个
                //=================================================== 自定义部分
                if (total<5){
                   server = upList.get(currentIndex);
                   total++;
                }else{
                    total = 0;
                    currentIndex++;
                    if (currentIndex>upList.size()){
                        currentIndex = 0;//大于3要从0开始
                    }
                  server = upList.get(currentIndex);//从活着的服务中,获取指定的服务来进行操作

                }





                //======================================================
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

KuangRule.java

package com.kuang.myrule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class KuangRule {
    @Bean
    public IRule myRule(){
        return new KuangRandomRule();//默认为轮询,现在自定义了KuangRandomRule
    }
}

image-20210429193430245

config

ConfigBean.java

package com.kuang.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;

@Configuration
public class ConfigBean { // @Configuration  相当于---spring  里面的applicationContext.xml

    //配置负载均衡实现RestTemplate
    //IRule
    // AvailabilityFilteringRule :会先过滤掉,跳闸,访问故障的服务,对剩下的进行轮询
    // RoundRobinRule: 轮询
    //RandomRule: 随机
    // RetryRule: 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行重试


    @Bean
    @LoadBalanced//Ribbon
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }


}

controller

ConfigBean.java

package com.kuang.springcloud.controller;

import com.kuang.springcloud.pojo.Dept;
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;

@RestController
public class DeptConsumerController {

    //理解:消费者,不应该有service层  service是通过url去请求
    //RestTemplate....有很多模板供我们直接调用   使用RestTemplate,需要注册到spring中
    //String url,实体: Map, Class<T> responseType  :请求的地址及响应的类型
    @Autowired
    private RestTemplate restTemplate; // RestTemplate 提供多种便捷访问远程http服务的方法,是简单的restful模板

//请求的地址是固定的所以把地址写死
 //   private static final String REST_URL_PREFIX = "http://localhost:8001";
    //Ribbon 我们这里的地址应该是一个变量,通过服务名来访问  服务名是SPRINGCLOUD-PROVIDER-DEPT  然后让它选择7001  或者7002 或者7003
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept){
        // REST_URL_PREFIX+"/dept/add",dept,Boolean.class  地址+ 具体的位置  传的对象   返回类型
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id")Long id) {

        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);//去远程服务上拿

    }
    @RequestMapping("/consumer/dept/list")
    public List<Dept> list(){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }




}

DeptConsumer_80.java

package com.kuang.springcloud;

import com.kuang.myrule.KuangRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

//Ribbon和Eureka整合以后,客户端可以直接调用  不用关心IP地址和端口号
@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候就能去加载我们自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = KuangRule.class)
public class DeptConsumer_80 {
    public static void main(String[] args){
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}

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>springcloud</artifactId>
        <groupId>com.kuang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-consumer-dept-80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
<!--实体类+web-->
    <dependencies>
<!--        Ribbon-->

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

        <!--        Eureka的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.kuang</groupId>
            <artifactId>springcloud-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>
    </dependencies>

</project>

最后只要重启80端口就可以了,如果开始把所有项目都停了,那么就要重启所有的项目

image-20210429201129518

Feign负载均衡

简介

feign是声明式的web service客户端,它让微服务之间的调用变得更加简单了,类似controller调用service。SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。

只需要创建一个接口,然后添加注解即可!
feign, 主要是社区,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种方法:

  1. 微服务名字【ribbon】
  2. 接口和注解【feign】
Fegin能干什么?
  • Fegin旨在编写Java Http客户端变得更容易
  • 前面在使用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发应用中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(类似于以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Fegin注解即可)即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign 集成了Ribbon
  • 利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而且简单的实现了服务调用。

B站学习网址:【狂神说Java】SpringCloud最新教程IDEA版_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值