前言

最近刚看完springcloud、dubbo的学习视频,但感觉不是那么扎实,所以打算写一个系列的博客来巩固自身所学。
当然有些内容是参考了别的博客,毕竟我也是初探分布式微服务的,并不是所谓的大神,只是一个新手在初探分布式微服务后写下的一些自己的理解和总结。


在了解了概述和Eureka之后,我们对于分布式微服务架构有了初步的认识,本文将介绍Netflix系列的另一大“神兽”——Ribbon,以及通过其封装的Feign,我将会详细介绍两者以及使用方式,希望能给各位一个较为清晰的认识。


一、Ribbon是什么?

1.思考一个问题

在回答Ribbon是什么之前,我们先来思考一个问题。

假设我们有很多服务(设有集群),并且服务端都已经注册到Eureka中,接下来就是客户端调用服务。

那么问题来了,我们该怎么调用?

理论上来说写上服务的地址确实是可以调用的。

但是同一个服务如果有多个服务器,即同一个服务设有集群,那么我们又该调用哪个。你如果把地址写死,那么设置集群便没有了意义。

我们必须有一种策略,让它能自动选择合适的服务去调用。

Ribbon就是解决这个问题而生的!

2.Ribbon简介

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。

二、Ribbon和Feign区别

其实两者差别就在于使用的方式,Ribbon是通过服务名字去调用,而Feign通过接口和注解来达到类似Controller调用Service的效果。

因为有部分使用者认为Ribbon的使用方式很不习惯,所有才有了Feign的出现。

Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。

在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。

Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。

Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。

Spring Cloud Feign帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。

Spring Cloud Feign具备可插拔的注解支持,支持Feign注解、JAX-RS注解和Spring MVC的注解。

简而言之,Feign就是在Ribbon的基础上进一步封装,换了一种使用方式,其底层还是使用Ribbon。


如上图看到,我们在feign依赖里找到了ribbon的依赖。

三、Ribbon使用步骤(Ribbon+RestTemplate )

1.导入依赖

<!--消费端做负载均衡用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

2.将创建配置类,将RestTemplate 加入Spring容器

@Configuration
public class ConfigBean {

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

RestTemplate 其实spring框架里的类,加上@LoadBalanced后Ribbon会对其进行处理,达到负载均衡的效果。

如果对源码实现感兴趣的同学可以去看这篇文章Spring Cloud Feign使用详解,它里面讲的挺详细的,不过关看可能看不懂(反正我是这样的)。

3.设置负载均衡策略

方法其实有很多种,这里介绍一种最简单的方法:

创建配置类,将规则放入Spring容器中

package com.dreamchaser.myrule;

import com.netflix.loadbalancer.AvailabilityFilteringRule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/** * 规定负载均衡的方法 * 不要写在主应用程序的上下文中,否则它会被所有RibbonClient共享 * 要自定义算法 */
@Configuration
public class RibbonRule {
    @Bean
    public IRule getRule(){
        return new AvailabilityFilteringRule();
    }
// 自定义负载均衡算法
// @Bean
// public IRule getMyRule(){
// return new MyRule();
// }
}

这里可以返回它自己附带的负载均衡算法,也可以是自定义的方法。

注意:此配置文件不在主应用程序上下文,这点在官方文档里也有说明
这里意思就是因为后面会配置多个RibbonClient,如果在主应用程序上下文配置,它就不在@ComponentScan中。这时,它由所有@RibbonClients共享。

我们可以在项目外面创建一个新包,就像这样,

当然你也可以自定义负载均衡算法,只要你继承了AbstractLoadBalancerRule类即可,如下面:

package com.dreamchaser.myrule;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;

public class MyRule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object o) {
        return null;
    }
}

4.在启动类上加上@RibbonClient注解

在启动类上配置@RibbonClient注解

@RibbonClient(name ="SPRINGCLOUD-PROVIDER-TAG",configuration = RibbonRule.class)

这里name写你要配置策略的服务名字,configuration 写你上面配置负载均衡策略时写的配置类的class

如果有多个RibbonClient,可以写多个。

5.结合Eureka使用

这里我给出一个例子:

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
    public class TagConsumerController {


        @Autowired
        //提供多种便捷访问远程http服务的方法,简单的restful服务模板
        private RestTemplate restTemplate;

        /*private static final String REST_URL_PREFIX="http://localhost:8001";*/

        //通过Ribbon来实现负载均衡,这时就得写服务名称
        private static final String REST_URL_PREFIX="http://SPRINGCLOUD-PROVIDER-TAG";
        @GetMapping("/consumer/tag")
        public @ResponseBody List<Tag> getTags(){
            return restTemplate.getForObject(REST_URL_PREFIX+"/tag",List.class);
        }

        @PostMapping("/consumer/tag")
        public @ResponseBody String addTag(Tag tag){
        return restTemplate.getForObject(REST_URL_PREFIX+"/tag",String.class);
    }
}

我们可以看到调用远程的方法,只要写上http://+服务名即可,至于其中它会如何选择,Ribbon内部会帮你去干。

这里我演示一下效果:

启一下进程,然后看一下Eureka

我们看到我们启了一个服务提供者,两个提供相同服务的“实例”(作为集群演示),这里我为了显示出负载均衡的效果,我在两者返回数据时做出了点小修改,如下图:



这样,返回数组时,第一个就会显示实例的端口号。

现在访问消费端localhost:80/consumer/tag

刷新一下

这样就可以让客户端按照既定的负载均衡策略来访问服务端,达到负载均衡的效果!

四、Feign使用步骤

1.在接口工程中加入Service层

所谓接口工程就是一个作为所有模块依赖的模块,里面包含公共的实体类,和规定好的接口。我们在其下面创建service包,包里创建远程调用所需的接口

package com.dreamchaser.service;

import com.dreamchaser.pojo.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

//服务名称
@FeignClient(value = "SPRINGCLOUD-PROVIDER-TAG")
@Component
public interface TagClientService {

    @GetMapping(value = "/tag",produces = "application/json;charset=UTF-8")
    public @ResponseBody List<Tag> findTags();

    @PostMapping("/tag")
    public @ResponseBody String insertTag(Tag tag);
}

加上@FeignClient(value = “SPRINGCLOUD-PROVIDER-TAG”)注解,value值表示你要访问的服务名字,接口方式与服务提供者一致。

2.设置负载均衡策略

这一步参照Ribbon使用,因为Feign底层还是借助Ribbon的。

3.在客户端上加上@EnableFeignClients注解

@EnableFeignClients({"com.dreamchaser"})

里面填包名,Feign回去扫描接口工程中包里的接口,并且注入到spring容器中

4.调用接口

package com.dreamchaser.springcloud.controller;

import com.dreamchaser.pojo.Tag;
import com.dreamchaser.service.TagClientService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@RestController
public class TagConsumerController {



    @Resource
    private TagClientService tagClientService;
    //通过Ribbon来实现负载均衡,这时就得写服务名称
    @GetMapping("/consumer/tag")
    public @ResponseBody List<Tag> getTags(){
        return tagClientService.findTags();
    }

    @PostMapping("/consumer/tag")
    public @ResponseBody String addTag(Tag tag){
        return tagClientService.insertTag(tag);
    }
}


使用起来还是蛮简单的!

5.访问消费者

与原来效果一致!

总结

Ribbon和Feign都有实现负载均衡的功能,Feign是在Ribbon的基础上进行封装,使用上变得更便捷,但同样的,效率会比用原来的Ribbon稍微差一点。

至于使用哪个,看个人喜好吧。我个人可能会喜欢Feign一点,因为我觉得Feign有助于把接口写到一起,这样负责不同服务的人调用其其他人服务时也会更加方便。


如果随着我的学习发现自己所写的有所纰漏或者错误,我会第一时间来修正博客,当然如果有什么错误,欢迎大家评论区评论指正,不胜感激。

愿我们都能以梦为马,不负人生韶华!
以此为记,与君共勉!