当一个系统中有多个服务提供者微服务的时候,服务消费者应该如何去选择哪个提供者去访问呢。Spring Cloud提供了Ribbon来提供客户端的负载均衡功能,本文将通过搭建的方式来简单了解如何使用Ribbon实现负载均衡功能。
系列文章
SpringCloud(一)-手把手教你创建springcloud微服务父子项目
SpringCloud(二)-手把手教你搭建Eureka Server和Eureka Client
SpringCloud(三)-手把手教你通过Rinbbon实现客户端负载均衡
SpringCloud(四)-手把手教你使用OpenFeign
SpringCloud(五)-手把手教你使用Hystrix配置服务熔断和降级以及Hystrix Dashboard
SpringCloud(六)-手把手教你搭建SpringCloud Config配置中心
SpringCloud(七)-手把手教你使用消息总线Bus实现动态刷新
SpringCloud(八)-手把手教你使用Stream消息驱动
负载均衡分为客户端负载均衡和服务端负载均衡,客户端负载均衡就是客户端去选择具体的微服务进行访问,而服务端负载均衡则是通过中间件拦截客户端的请求转发给对应的微服务。而Ribbon提供的则是客户端的负载均衡功能,因此我们要在消费端去装配负载均衡功能。
1. Ribbon配置
1.1 消费端配置Ribbon
找到我们的服务提供者springcloud-product-consumer-8200
1.1.1 修改pom.xml
<!--ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
1.1.2 添加注解
我们在配置restTemplate的时候加上 @LoadBalanced 注解
package com.elio.springcloud.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.RoundRobinRule;
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 RestConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
1.1.3 配置负载均衡规则
新增MyRule配置类,注意千万不要建在主启动类同一级目录下,不然会有问题。
package rule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyRule {
@Bean
public IRule getLoadBalancedRule(){
return new RoundRobinRule();
}
}
MyRule位置
1.2 新增访问服务信息的API
现在服务消费者和服务提供者的API不能很明确的显示我们访问的是哪个微服务,因此需要新增一个查询当前访问微服务信息的api
1.2.1 服务提供者8100
我们找到服务提供者8100的controller新增geServieInfo方法
package com.elio.springcloud.controller;
import com.elio.springcloud.dto.Result;
import com.elio.springcloud.entity.Product;
import com.elio.springcloud.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@Slf4j
@RequestMapping("/")
public class ProductProviderController {
@Resource
private ProductService productService;
@Value("${spring.application.name}")
private String instantName;
@Value("${server.port}")
private String port;
@GetMapping("product/provider/get/info")
public Result geServieInfo(){
return new Result(200, "查询成功", "当前服务名:"+instantName + " 当前端口:"+port);
}
/**
* 查询
* @param id
* @return
*/
@GetMapping("product/provider/get/{id}")
public Result selectById(@PathVariable("id") Long id){
return new Result(200, "查询成功", productService.selectById(id));
}
/**
* 删除
* @param id
* @return
*/
@GetMapping("product/provider/delete/{id}")
public Result deleteById(@PathVariable("id") Long id){
return new Result(200, "删除成功", productService.deleteById(id));
}
/**
* 修改
* @param product
* @return
*/
@PostMapping("product/provider/update")
public Result updateById(@RequestBody Product product){
return new Result(200, "修改成功", productService.updateById(product.getId(), product.getName()));
}
/**
* 新增
* @return
*/
@PutMapping( "product/provider/add")
public Result insertById(@RequestBody Product product){
return new Result(200, "修改成功", productService.insertOne(product));
}
}
1.2.2 服务消费者8200
找到服务消费者项目,修改controller,新增geServieInfo方法,然后注意将我们写死的地址换成服务提供者微服务名
package com.elio.springcloud.controller;
import com.elio.springcloud.dto.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class ProductConsumerController {
@Resource
RestTemplate restTemplate;
//public static String url = "http://localhost:8100/";
public static String url = "http://springcloud-product-provider/";
/**
* 查询
* @return
*/
@GetMapping("product/consumer/get/info")
public Result selectById(){
return new Result(200, "查询成功",
restTemplate.getForObject(url+"product/provider/get/info", Result.class));
}
/**
* 查询
* @param id
* @return
*/
@GetMapping("product/consumer/get/{id}")
public Result selectById(@PathVariable("id") Long id){
return new Result(200, "查询成功",
restTemplate.getForObject(url+"product/provider/get/"+id, Result.class));
}
}
1.3 服务提供者启动多个实例
在上面的两篇文章中我们都是通过新增子项目来配置多个实例的,这种方法无脑而且麻烦,因此接下来介绍第二种方法。通过idea的启动配置来设置一个微服务在不同端口启动。
下拉 run的按钮选中服务提供者项目,点击左上角的复制按钮
点击复制如图为复制后的配置名, 我们修改名称为ProductProvider8103
image.png
在VM options中,添加参数 -Dserver.port=8103,表示这个实例将在8103上运行。
修改端口号点击了ok后,我们再下拉run按钮,就可以看到配置的 ProductProvider8103了,然后点击右边的debug按钮,可以在8103端口上启动服务提供者了。
ProductProvider8103
最后我们要注意的是服务提供者的实例名要不同,不然的话只能注册成功一个,由于端口是不一样的所以保证了实例名不一样,服务名是一样的。
eureka:
instance:
instance-id: ${spring.application.name}:${server.port}
然后依次启动服务注册中8300,服务提供者8100,8101,8103,服务消费者8200,然后Eureka 服务中心中服务提供者会有三个实例。
成功注册1.4 测试
在配置的过程中注意我提出的两点注意,不然的话要么注册不成功,要么就是实现不了负载均衡。接下来就是测试负载均衡了,访问消费者的api http://localhost:8200/product/consumer/get/info, 然后多刷新几次,发现按照8100,8101,8103的顺序访问服务提供者
8100 8101 81032. 总结
本文通过一个简单的配置例子来实现了Spring Cloud项目配置Ribbon来实现客户端负载均衡的,其工作原理和工作流程也没有涉及,这将在其它文章中解析。
上篇文章中,我们抛出了两个问题,在这篇文章中我们已经解决了,一个是解决服务地址写死的问题,另一个则是负载均衡的实现。但是现有的项目还是存在一个问题,就是我们访问服务提供者的接口时,需要在方法中指出具体的地址,如果这个接口被多处访问以后需要修改的话就需要修改多次,这个问题我们留在下篇文章中解决。
网友评论