深入学习Spring Cloud-Ribbon
ribbon简介
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出LoadBalancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
ribion=负载均衡+重试
ribbon的工作步骤:
第一步先选择EurekaServer,它优先选择在同一个区域内负载较少的server。第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
创建springribbon项目
第一步:新建spring项目
第二步:添加EurekaDiscoveryClient,SpringWeb依赖
第三步:添加sp01-commons工具API依赖;eureka-client中已经包含ribbon依赖
4.0.0 org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE cn.tedu sp06-ribbon 0.0.1-SNAPSHOT sp06-ribbon DemoprojectforSpringBoot 1.8 Hoxton.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine cn.tedu sp01-commons 0.0.1-SNAPSHOT org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin
第四步:添加yml配置
spring: application: name:ribbon#服务器命名 server: port:3001#设置服务器端口号 #配置添加注册中心集群 eureka: client: service-url: defaultZone:http://eureka1:2001/eureka,http://eureka2:2002/eureka
远程调用RestTemplate
RestTemplate是SpringBoot提供的一个Rest远程调用工具。
类似于HttpClient,可以发送http请求,并处理响应。RestTemplate简化了RestAPI调用,只需要使用它的一个方法,就可以完成请求、响应、Json转换
方法:
- getForObject(url,转换的类型.class,提交的参数)
- postForObject(url,协议体数据,转换的类型.class)
RestTemplate和Dubbo远程调用的区别:
RestTemplate:
http调用
效率低
Dubbo:
RPC调用,Java的序列化
效率高
第一步:创建RestTemplate实例
RestTemplate是用来调用其他微服务的工具类,封装了远程调用代码,提供了一组用于远程调用的模板方法,例如:getForObject()、postForObject()等
packagecn.tedu.sp06; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; importorg.springframework.cloud.client.discovery.EnableDiscoveryClient; importorg.springframework.context.annotation.Bean; importorg.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication publicclassSp06RibbonApplication{ //创建RestTemplate实例,并存入spring容器 @Bean publicRestTemplategetRestTemplate(){ returnnewRestTemplate(); } publicstaticvoidmain(String[]args){ SpringApplication.run(Sp06RibbonApplication.class,args); } }
第二步:创建RibbonController
packagecn.tedu.sp06.controller; importjava.util.List; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.web.bind.annotation.GetMapping; importorg.springframework.web.bind.annotation.PathVariable; importorg.springframework.web.bind.annotation.PostMapping; importorg.springframework.web.bind.annotation.RequestBody; importorg.springframework.web.bind.annotation.RestController; importorg.springframework.web.client.RestTemplate; importcn.tedu.sp01.pojo.Item; importcn.tedu.sp01.pojo.Order; importcn.tedu.sp01.pojo.User; importcn.tedu.web.util.JsonResult; @RestController publicclassRibbonController{ @Autowired privateRestTemplatert; @GetMapping("/item-service/{orderId}") publicJsonResult>getItems(@PathVariableStringorderId){ //向指定微服务地址发送get请求,并获得该服务的返回结果 //{1}占位符,用orderId填充 returnrt.getForObject("http://localhost:8001/{1}",JsonResult.class,orderId); } @PostMapping("/item-service/decreaseNumber") publicJsonResultdecreaseNumber(@RequestBodyList
- items){ //发送post请求 returnrt.postForObject("http://localhost:8001/decreaseNumber",items,JsonResult.class); } / @GetMapping("/user-service/{userId}") publicJsonResult
getUser(@PathVariableIntegeruserId){ returnrt.getForObject("http://localhost:8101/{1}",JsonResult.class,userId); } @GetMapping("/user-service/{userId}/score") publicJsonResultaddScore( @PathVariableIntegeruserId,Integerscore){ returnrt.getForObject("http://localhost:8101/{1}/score?score={2}",JsonResult.class,userId,score); } / @GetMapping("/order-service/{orderId}") publicJsonResult getOrder(@PathVariableStringorderId){ returnrt.getForObject("http://localhost:8201/{1}",JsonResult.class,orderId); } @GetMapping("/order-service") publicJsonResultaddOrder(){ returnrt.getForObject("http://localhost:8201/",JsonResult.class); } }
第三步:启动服务,进行测试
http://localhost:3001/item-service/35
等。。
ribbon负载均衡
第一步:RestTemplate设置@LoadBalanced
@LoadBalanced负载均衡注解,会对RestTemplate实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器
packagecn.tedu.sp06; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; importorg.springframework.cloud.client.discovery.EnableDiscoveryClient; importorg.springframework.cloud.client.loadbalancer.LoadBalanced; importorg.springframework.context.annotation.Bean; importorg.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication publicclassSp06RibbonApplication{ @LoadBalanced//负载均衡注解 @Bean publicRestTemplategetRestTemplate(){ returnnewRestTemplate(); } publicstaticvoidmain(String[]args){ SpringApplication.run(Sp06RibbonApplication.class,args); } }
第二步:访问路径设置为id
packagecn.tedu.sp06.controller; importjava.util.List; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.web.bind.annotation.GetMapping; importorg.springframework.web.bind.annotation.PathVariable; importorg.springframework.web.bind.annotation.PostMapping; importorg.springframework.web.bind.annotation.RequestBody; importorg.springframework.web.bind.annotation.RestController; importorg.springframework.web.client.RestTemplate; importcn.tedu.sp01.pojo.Item; importcn.tedu.sp01.pojo.Order; importcn.tedu.sp01.pojo.User; importcn.tedu.web.util.JsonResult; @RestController publicclassRibbonController{ @Autowired privateRestTemplatert; @GetMapping("/item-service/{orderId}") publicJsonResult>getItems(@PathVariableStringorderId){ //这里服务器路径用service-id代替,ribbon会向服务的多台集群服务器分发请求 returnrt.getForObject("http://item-service/{1}",JsonResult.class,orderId); } @PostMapping("/item-service/decreaseNumber") publicJsonResultdecreaseNumber(@RequestBodyList
- items){ returnrt.postForObject("http://item-service/decreaseNumber",items,JsonResult.class); } / @GetMapping("/user-service/{userId}") publicJsonResult
getUser(@PathVariableIntegeruserId){ returnrt.getForObject("http://user-service/{1}",JsonResult.class,userId); } @GetMapping("/user-service/{userId}/score") publicJsonResultaddScore( @PathVariableIntegeruserId,Integerscore){ returnrt.getForObject("http://user-service/{1}/score?score={2}",JsonResult.class,userId,score); } / @GetMapping("/order-service/{orderId}") publicJsonResult getOrder(@PathVariableStringorderId){ returnrt.getForObject("http://order-service/{1}",JsonResult.class,orderId); } @GetMapping("/order-service") publicJsonResultaddOrder(){ returnrt.getForObject("http://order-service/",JsonResult.class); } }
第三步:访问测试,ribbon会把请求分发到8001和8002两个服务端口上
http://localhost:3001/item-service/34ribbon重试
第一步:添加spring-retry依赖
org.springframework.retry spring-retry
第二步:application.yml配置ribbon重试
#06项目用来测试远程调用和ribbon工具 #等功能测试完成后,直接删除 spring: application: name:ribbon server: port:3001 #连接eureka,从eureka发现其他服务的地址 eureka: client: service-url: defaultZone:http://eureka1:2001/eureka,http://eureka2:2002/eureka #配置ribbon重试次数 ribbon: #次数参数没有提示,并且会有黄色警告 #重试次数越少越好,一般建议用0,1 MaxAutoRetries:1 MaxAutoRetriesNextServer:2
第三步:设置RestTemplate的请求工厂的超时属性
packagecn.tedu.sp06; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; importorg.springframework.cloud.client.loadbalancer.LoadBalanced; importorg.springframework.context.annotation.Bean; importorg.springframework.http.client.SimpleClientHttpRequestFactory; importorg.springframework.web.client.RestTemplate; @SpringBootApplication publicclassSp06RibbonApplication{ publicstaticvoidmain(String[]args){ SpringApplication.run(Sp06RibbonApplication.class,args); } /** *创建RestTemplate实例 *放入spring容器 *@LoadBalanced-对RestTemplate进行增强,封装RestTemplate,添加负载均衡功能 */ @LoadBalanced @BeanpublicRestTemplaterestTemplate(){ //设置调用超时时间,超时后认为调用失败 SimpleClientHttpRequestFactoryf= newSimpleClientHttpRequestFactory(); f.setConnectTimeout(1000);//建立连接等待时间 f.setReadTimeout(1000);//连接建立后,发送请求后,等待接收响应的时间 returnnewRestTemplate(f); } }
第四步:ItemController添加延迟代码
packagecn.tedu.sp02.item.controller; importjava.util.List; importjava.util.Random; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.beans.factory.annotation.Value; importorg.springframework.web.bind.annotation.GetMapping; importorg.springframework.web.bind.annotation.PathVariable; importorg.springframework.web.bind.annotation.PostMapping; importorg.springframework.web.bind.annotation.RequestBody; importorg.springframework.web.bind.annotation.RestController; importcn.tedu.sp01.pojo.Item; importcn.tedu.sp01.service.ItemService; importcn.tedu.web.util.JsonResult; importlombok.extern.slf4j.Slf4j; @Slf4j @RestController publicclassItemController{ @Autowired privateItemServiceitemService; //配置文件application.yml中的server.port=8001注入到这个变量 //是为了后面做负载均衡测试,可以直接看到调用的是那个服务器 @Value("${server.port}") privateintport; //获取订单的商品列表 @GetMapping("/{orderId}") publicJsonResult>getItems(@PathVariableStringorderId)throwsInterruptedException{ log.info("server.port="+port+",orderId="+orderId); //模拟延迟代码 if(Math.random()<0.9){ longt=newRandom().nextInt(5000); log.info("延迟:"+t); Thread.sleep(t); } List
- items=itemService.getItems(orderId);//根据订单id获取商品列表 returnJsonResult.ok(items).msg("port="+port); } //减少商品库存 /** *@RequestBody完整接收请求协议体中的数据 *@paramitems *@return */@PostMapping("/decreaseNumber") publicJsonResultdecreaseNumber(@RequestBodyList
- items){ for(Itemitem:items){ log.info("减少商品库存:"+item); } itemService.decreaseNumbers(items); returnJsonResult.ok(); } }
第五步:测试ribbon重试机制
通过ribbon访问item-service,当超时,ribbon会重试请求集群中其他服务器
http://localhost:3001/item-service/35
到此这篇关于深入学习SpringCloud-Ribbon的文章就介绍到这了,更多相关SpringCloud-Ribbon内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!