前言
最近刚看完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"})
1
里面填包名,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有助于把接口写到一起,这样负责不同服务的人调用其其他人服务时也会更加方便。
如果随着我的学习发现自己所写的有所纰漏或者错误,我会第一时间来修正博客,当然如果有什么错误,欢迎大家评论区评论指正,不胜感激。
愿我们都能以梦为马,不负人生韶华!
以此为记,与君共勉!