SpringCloud(六)——微服务集群

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud(六)——微服务集群相关的知识,希望对你有一定的参考价值。

参考技术A 为了避免Eureka的单点故障,我们搭建了Eureka集群。
要是微服务发生了单点故障怎么办呢?
那么就要搭建微服务集群了。

我们再新建一个服务提供者,之前有了一个8001的提供者,我们用同样的方式创建一个8002端口的提供者,可以参考我之前写的:
https://www.jianshu.com/p/e1b08eeb1d1e

8001工程里面的类和配置文件在8002里都要有一份,只不过我们要将端口改成8002。

因为之前只有一个服务提供者,我们也就无需区分请求发到了哪个服务。现在我们有两个服务提供者了,所以我们要区分一下发到了哪个服务,需要再controller里改一下。

添加端口号的属性,并且作为数据返回。
改造号了之后我们要先分别单独发送一下8001和8002的服务看看是否可以返回,确保微服务没问题。

服务提供者没问题了接下来改造消费者。

消费者里面需要改的有两处controller和config

在消费者的controller里,我们通过restTemplate来和提供者进行通讯,之前的写法是写死了一个url来供restTemplate发送,因为提供者是集群模式了,所以我们就不能写死发往8001的url了。
我们现在要使用服务名来发送服务。
这里的服务名就是注册到Eureka的服务名。

所以我们要在controller里面使用服务名做url。

因为我们使用了服务名,就不能直接解析成ip地址+端口号的形式了,需要我们在restTemplat上面添加@LoadBalanced注解,这个注解同时可以开启我们默认的负载均衡策略。

先看一下我们启动的工程:

Eureka集群和Payment提供者集群都已经搭建好了。并且注册中心中也有实例信息。
那么我们多发送几个请求看看结果。

我们可以看到发送到消费者的服务被依次分发到8001和8002的两个服务上了。
说明我们的负载均衡也生效了!

微服务架构整理-(六SpringCloud实战之Ribbon)

SpringCloud实战之Ribbon

负责均衡介绍

负载均衡分为 硬件负载均衡和软件负载均衡。常用见的如下:
硬件负载均衡: F5 ,深信服, Array 等;
软件负载均衡: Nginx,LVS,HAProxy,Ribbon 等;

它们的原理是维护一个可用的服务列表,通过心跳机制来剔除不可用节点,从而保证可用列表的有效性。当请求经过负载均衡设备(软件)时,设备会按照设定的算法从可用的列表中选择一个服务进行转发。

Ribbon概念

Ribbon是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算
法, 是 一 个基于 HTTP 和 TCP 的客户端负载均衡工具,这里可以理解成服务消费者端的负载均衡工具。
SpringCloud对Ribbon进行了二次封闭,可以让开发人员通过RestTemplate(在注入时,直接在上方加入注解 @LoadBalanced 即可)的实例调用接口时,可以自动进行服务选择。

其主要特点包括:

  • 负责均衡算法多样,且支持定制化
  • 小巧,使用方便,无需独立部署,直接在代码里使用即可
  • 不同于服务端负载均衡,Ribbon的可用服务列表直接从注册中心中读取

Ribbon中的负载均衡算法

Ribbon中提供了一系列的负载均衡算法,这些算法封装了相应的java类,使用时只需注入到Spring容器中即可。

算法作用
RandomRule随机选择一个 Server
RoundRobinRule轮询选择
RetryRule先按照RoundRobinRule 策略选择,如果选择到的服务不能访问,则在指定时间内进行重试,选择其他可用的服务
BestAvailableRule先过滤掉由于多次访问故障的服务,然后选择一个并发量最小的服务
ResponseTimeWeightedRule/WeightedResponseTimeRule根据响应时间分配一个 Weight(权重),响应时间越长,Weight 越小,被选中的可能性越低
AvailabilityFilteringRule先过滤掉由于多次访问故障的服务,以及并发连接数超过阈值的服务,然后对剩下的服务按照轮询策略进行访问
ZoneAvoidanceRule综合判断服务节点所在区域的性能和服务节点的 可用性,来 决定选择哪个服务

Ribbon使用

因为SpringCloud对Ribbon进行了二次封闭,所以使用起来很方便。这里在上一篇博客的基础上将Ribbon加进来,所以只给出改动的代码。

添加一个服务提供者

之前只有一个服务提供者,为了能够使用负载均衡,至少需要两个服务提供者。所以这里将之前的product_service 复制一份,稍加修改就可以了。涉及以下几个方面:
端口号与服务名
在application.yml中修改端口号,服务名保持不动,服务消费者根据服务名获取服务提供者的集合,然后在集合中选择(由Ribbon完成)一个服务提供者。

server:
  # port: 9001 #端口
  port: 9003 #端口
spring:
  favicon:
    enabled: false
  application:
    name: service-product #服务名称,与之前保持一样

工程添加到父工程里面
在父工程pom.xml中添加新的module

<modules>
        <module>product_service</module>
        <!--添加product_service_2 module -->
        <module>product_service_2</module>
        <module>order_service</module>
        <module>eureka_service</module>
        <module>eureka_service_1</module>
    </modules>

工程id
product_service_2的pom.xml中修改artifactId

<artifactId>product_service_2</artifactId>

添加Ribbon

在消费者服务(service-order)的配置类中通过添加 @LoadBalanced 方式引进Ribbon, 负载均衡算法通过bean的方式注入,如果不注入算法,默认使用轮询算法。

@Configuration
public class Config 
	//引入Ribbon,此标签引入后,当服务启动的时候会自动读取注册中心的服务提供者
    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate() 
        return new RestTemplate();
    
	//注入负责均衡算法,这里只列了两种,大家可以根据自己的需求自行添加
    @Bean
    public IRule getIRule() 
        // return new RandomRule();
        return new BestAvailableRule();
    

区别服务提供者

因为两个服务提供者返回的内容都一样,所以这里为了区分是由哪个服务者返回的内容,在返回值上稍加修改,真正的生产环境不需要这样。
product_service
在name后面加上1

 @GetMapping(value = "/id")
    public Product findById(@PathVariable Long id) 
        Product product = productService.findById(id);
        product.setName(product.getName() + "1");
        return product;
    

product_service_2
在name后面加上2

 @GetMapping(value = "/id")
    public Product findById(@PathVariable Long id) 
        Product product = productService.findById(id);
        product.setName(product.getName() + "2");
        return product;
    

访问服务提供者

order_service controller中不能再使用服务提供者的实例,需要将其改成服务名,因为这里会有多个实例,每个实例拥有自己的端口,如果用实例的话,这里拼服务的地址的时候,又需要写个算法来判断使用哪个端口,所以直接使用服务名就可以,具体用哪个服务,交给Ribbon来处理。

/**
 * @author :浦江之猿
 * @date :Created in 2022/05/19 21:48
 */
@RestController
@RequestMapping("/order")
public class OrderController 

    @Autowired
    RestTemplate restTemplate;
    @Autowired
    DiscoveryClient discoveryClient;

    @GetMapping(value = "/buy/id")
    public Product findById(@PathVariable Long id) 
        // 获取元数据
        /* List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
        ServiceInstance instance = instances.get(0);
        restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,Product.class); */
        Product product = null;
        //直接将服务名拼接到地址中
        product = restTemplate.getForObject("http://service-product/product/" + id, Product.class);
        return product;
    

运行结果

注册中心
访问http://localhost:9006/,可以看到service-product启了两个

访问订单
看一下使用轮询算法的结果,http://localhost:9002/order/buy/1 返回值会在下面两条中来回切换。

//product_service 返回的结果
"id":1,"name":"apple1","price":8000.00
//product_service_2返回的结果 
"id":1,"name":"apple2","price":8000.00

总结

关于Ribbon的使用基本就介绍完了,因为SpringCloud对其进行了二次封装,所以使用其来非常方便。最后,希望本文能帮助大家,祝大家在IT之路上少走弯路,一路绿灯不堵车,测试一性通过,bug秒解!
源码下载

以上是关于SpringCloud(六)——微服务集群的主要内容,如果未能解决你的问题,请参考以下文章

springCloud搭建微服务集群+Zuul服务器端负载均衡

SpringCloud微服务电商系统在Kubernetes集群中上线详细教程

Jenkins—— Jenkins+Docker+SpringCloud微服务持续集成项目优化和集群

云原生微服务SpringCloud-eureka(server)集群搭建

SpringCloud02_Eureka概述单机案例集群案例微服务服务完善服务发现Discovery自我保护机制

SpringCloud微服务之Nacos集群搭建