zoukankan      html  css  js  c++  java
  • SpringCloud(6)

    一、Ribbon

    1)Ribbon简述

    1.1)Ribbon是什么?

    ● Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。

    ● 简单的说,Ribbon是Netflix发布的云中间层服务开源项目,其主要功能是提供客户端实现负载均衡算法。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中Load Balancer后面的所有机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。

    1.2)Ribbon能干什么?

    ● LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。

    ● 负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。

    ● 常见的负载均衡有软件Nginx,LVS,硬件 F5等。

    ● Dubbo 和 SpringCloud 中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义

    ● 负载均衡简单分类:

      ○ 集中式LB

        - 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;

      ○ 进程式LB

        - 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。

        - Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

    1.3)集成Ribbon

    在消费者模块 springcloud-consumer-dept-80 添加依赖

    <!-- 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>

    在消费者模块 springcloud-consumer-dept-80 编写配置

    eureka:
      client:
        register-with-eureka: false # 不向Eureka注册自己
        service-url:
          defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/    # 去哪拿服务

    在主启动类添加注解 @EnableEurekaClient,让Eureka生效

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

    在消费者模块 springcloud-consumer-dept-80 的配置类 ConfigBean 中进行配置负载均衡,只需要在 RestTemplate 注入IOC的方法上添加一个注解  @LoadBalanced

    @Configuration
    public class ConfigBean {   // 相当于spring中的 applicationContext.xml  来把对象放入IOC中
    
        // 配置负载均衡实现RestTemplate
        /*
         * 负载均衡核心实现:IRule
         *  RoundRobinRule - 轮询
         *  RandomRule - 随机
         *  AvailabilityFilteringRule - 先过滤掉跳闸、访问故障的服务,然后对剩下的服务进行轮询。
         *  RetryRule - 先按照轮询查询服务,如果服务获取失败,则会在指定的时间内执行重试。
         */
        @Bean
        @LoadBalanced // Ribbon
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }

    在消费者模块 springcloud-consumer-dept-80 修改 Controller 的访问路径,路径不能写死,应该通过服务名来访问

    private static final String Rest_URL_PREFIX = "http://SPRIRNG-PROVIDER-DEPT";   // 这里指向的是服务提供者模块中application.yaml中配置的application.name

    Ribbon和Eureka整合以后,客户端可以之间调用,不用关心IP地址和端口号!

    2)使用Ribbon实现负载均衡

    Ribbon默认算法:轮询

    新建两个数据库,db02和db03,都和db01相同

    再新建两个服务提供者模块, spirngcloud-provider-dept-8002 spirngcloud-provider-dept-8003 

    分别对新建的两个模块导入和 spirngcloud-provider-dept-8001 相同的依赖,修改 application.yaml 配置,改为自己所指向的数据库

    一定要保证三个服务的 application.name 一致!!

    spirngcloud-provider-dept-8001 中 java 下的所有和 resource 下的 mybatis 复制到两个模块,只需要修改主启动类为自己的类名

    这三个服务提供者模块,只有连接的数据库不同~~

    启动集群进行测试

    可以看到注册进来了三个服务,相同的服务的名字

    然后访问服务消费者的接口 localhost:80/consumer/queryAll ,不断的刷新,我们就可以看到,我们拿到的数据是来自不同的数据库,这样就实现了负载均衡

    3)自定义Ribbon负载均衡算法

    Ribbon实现负载均衡,最核心的一个注解就是 @LoadBalanced 

    负载有一个核心的接口 IRule 

    实现类:

    ● AvailabilityFilteringRule:先过滤掉崩溃,跳闸,访问故障的服务,对剩下的服务进行轮询

    ● RoundRobinRule:轮询(默认的)

    ● RandomRule:随机

    ● RetryRule:会先按照轮询获取服务,如果服务获取失败,则会在指定的时间内进行重试

    我们要自定义 Ribbon 负载均衡算法,在消费者模块 springcloud-consumer-dept-80 中进行自定义,注意,我们自定义的负载均衡算法,不能和主启动类同级,可以主启动类的上一级

    在主启动类的上一级进一个包 myrule,新建一个类 CodeweiRule,继承 AbstractLoadBalancerRule ,然后写负载均衡算法

    @Configuration
    public class CyanRandomRule extends AbstractLoadBalancerRule {
    
        /*
         * 每个服务访问5次,然后换下一个服务访问5次(3个服务)
         * total = 1,默认为 1,如果等于 5,将指向下一个服务
         * index = 0,默认为 0,如果等于 5,index+1
         */
    
        // 被调用的次数
        private int total = 0;
        // 当前服务
        private int currentIndex = 0;
    
        public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                return null;
            }
            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 = chooseRandomInt(serverCount);   // 生成区间随机数
                // server = upList.get(index);                 // 从活着的服务中,随机获取一个
    
                //=============================================================================
    
                if (total < 5) {
                    server = upList.get(currentIndex);
                    total++;
                } else {
                    total = 1;
                    currentIndex += 1;
                    if (currentIndex > upList.size() - 1) {
                        currentIndex = 0;
                    }
                    server = upList.get(currentIndex);  // 从活着的服务中获取一个服务
                }
    
                //=============================================================================
    
                if (server == null) {
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
                server = null;
                Thread.yield();
            }
            return server;
        }
    
        protected int chooseRandomInt(int serverCount) {
            return ThreadLocalRandom.current().nextInt(serverCount);
        }
    
        @Override
        public Server choose(Object key) {
            return choose(getLoadBalancer(), key);
        }
    
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
            // TODO Auto-generated method stub
        }
    }

    然后再myrule包下,写一个配置类 AweiRule,添加注解 Configuration ,进行配置,指定我们自定义的负载均衡算法

    @Configuration
    public class CyanRule {
        @Bean
        public IRule myIRule() {
            return new CyanRandomRule();    // 自定义
        }
    }

    在主启动类上,添加注解 @RibbonClient(name="SPIRINTCLOUD-PROVIDER-DEPT",configuration = MyRule.class) ,通过name指定服务的名字,通过 configuration 来指定自己自定义的 Ribbon 配置类

    @SpringBootApplication
    @EnableEurekaClient
    @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = CyanRule.class)
    public class DeptConsumer_80 {
        public static void main(String[] args) {
            SpringApplication.run(DeptConsumer_80.class, args);
        }
    }

    这样,通过我们自定义的负载均衡算法,就可以实现,每一个服务访问5次后,访问下一个服务,一个轮回后,再回来访问第一个服务

    二、Feign

    1)简介

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

    只需要创建一个接口,然后添加注解即可!

    Feign,主要是社区,大家都习惯面向接口编程,这个是很多开发人员的规范。调用微服务访问两种方法

    ● 微服务名字【ribbon】
    ● 接口和注解【feign】

    ① Feign能干什么?

    ● Feign旨在使编写Java Http客户端变得更容易。

    ● 前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。

    ② Feign集成了Ribbon

    利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用

    2)Feign使用步骤

    新建一个模块, springcloud-consumer-dept-feign 

    springcloud-consumer-dept-80 相同,导入和 springcloud-consumer-dept-80 相同的依赖,再导入 feign 的依赖,修改主启动类

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

    springcloud-api 模块下添加依赖

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

    在 springcloud-api 模块下新建一个 serivce 包,创建一个接口 DeptClientService ,在类上添加注解 @FeignClient(value="SPIRNGCLOUD-PROVIDER-DEPT") ,value 是指定服务的名字,然后写对应提供者中服务的方法

    @Serivce
    @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallbackFactory = DeptClientServiceFallBackFactory.class)
    public interface DeptClientService {
    
        @GetMapping("/dept/get/{id}")
        public Dept queryById(@PathVariable("id") Long id);
    
        @GetMapping("/dept/list")
        public List<Dept> queryAll();
    
        @GetMapping("/dept/add")
        public boolean addDept(Dept dept);
    }

    这样在 springcloud-consumer-dept-feign 模块的 Controller 中,之间通过 @Autowired 来获取到服务

    @RestController
    public class DeptConsumerController {
    
        @Autowired
        public DeptClientService deptClientService;
    
        // 不需要service层,直接通过REST远程过程调用
        @RequestMapping("/consumer/dept/add")
        public boolean add(Dept dept) {
            return deptClientService.addDept(dept);
        }
    
        @RequestMapping("/consumer/dept/get/{id}")
        public Dept get(@PathVariable("id") Long id) {
            return deptClientService.queryById(id);
        }
    
        @RequestMapping("/consumer/dept/list")
        public List<Dept> list() {
            return deptClientService.queryAll();
        }
    
        @RequestMapping("/consumer/dept/test")
        public String test() {
            return "test";
        }
    }

    springcloud-consumer-dept-feign 模块的主启动类上添加注解 @EnableFeignClients ,让 Feign 生效

    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients(basePackages = {"com.cyan.service"})    // 扫描提供服务feign接口所在的包
    public class FeignDeptConsumer_80 {
        public static void main(String[] args) {
            SpringApplication.run(FeignDeptConsumer_80.class, args);
        }
    }
  • 相关阅读:
    KDiff
    如何用Javascript检测到所有的IE版本
    Chrome中的哪些端口是限制使用的?
    如何防止XSRF攻击
    External component has thrown an exception
    OpenGL中的原子操作需要注意的地方
    Unable to create new web application
    How to Redirect in ASPNET Web API
    图形转换的组合(注意从右向左读)
    如何用Client OM获取页面上一个Content web part的内容
  • 原文地址:https://www.cnblogs.com/Dm920/p/13194881.html
Copyright © 2011-2022 走看看