以案例聊聊SpringCloud分布式系统的架构者(三)

一般的系统中存在一个很棘手的问题,就是一个服务器同时访问的量比较大,逼近甚至超过了台服务器的最大负载量,然后服务器崩了,再比如:由于网路方面的问题,用户可能在访问一个网站或者使用网站的某个热点功能的时候,突然访问不成就像网站卡顿到了那,让用户退也不是不退也不是。一般的系统中存在这个问题,在我们分布式系统中也存在这种问题,尤其是在高并发访问的情况下,同一时刻用户访问量几倍甚至几百倍的超过服务器最大承受量,那么这种情况也会发生。此时在理想的情况下就是,一旦某台服务器上的服务,出现故障。那么立即把这台服务器上的服务断开,返回一个断开的标志,让这种故障不再向上传导。

归纳起来就是:在微服务场景中,通常会有很多层的服务调用。如果一个底层服务出现问题,故障会被向上传播给用户。我们需要一种机制,当底层服务不可用时,可以阻断故障的传播。这就是断路器的作用。他是系统服务稳定性的最后一重保障。

在springcloud中断路器组件就是Hystrix。Hystrix也是Netflix套件的一部分。他的功能是,当对某个服务的调用在一定的时间内(默认10s,由metrics.rollingStats.timeInMilliseconds配置),有超过一定次数(默认20次,由circuitBreaker.requestVolumeThreshold参数配置)并且失败率超过一定值(默认50%,由circuitBreaker.errorThresholdPercentage配置),该服务的断路器会打开。返回一个由开发者设定的fallback。

fallback可以是另一个由Hystrix保护的服务调用,也可以是固定的值。fallback也可以设计成链式调用,先执行某些逻辑,再返回fallback。

在SpringCloud中有一个负载均衡的概念和实现的方式。具体的表现形式是这样的:有一个服务,这一个服务运行在不同节点的机器上。注意两台机器上运行的服务本质上是一个服务。也可以这么理解一个jar包运行在了两个机器中(连里面的配置都是一样的,除了端口号以外)。虽然两个机器中运行的是一个jar包,那么这两个jar包无论如何都需要向注册中心注册,那么在注册中心的表现就是服务提供者的名称公用一个,但这个名称下面对应两台机器的服务。在服务消费者在调用的时候就会出现负载均衡的情况。它会的访问会在在两台机器上来回的切换。具体表现形式是:

上面就是SpringCloud提供的负载均衡的体现。SpringCloud实现均衡负载只需要在RestTemplter对象注入的时候在@Bean上加上@LoadBalanced注解就可以了。

那么有了这种负载均衡的机制,其实是很好的。同样它也存在隐患,那就是一台机器发生了故障的时候。有的人看到这会说,在调用量不大的时候一台机器发生故障了那就故障呗不是还有一台机器吗。那么问题来了,一台机器故障了那你的告诉它的调用者啊,万一一个服务的调用者恰好调用了这个台故障的机器呢,那么因为你的故障就会给服务的调用者返回不可预期的错误。所以这里需要断路器。一旦某台机器发生故障,将这台服务器中断,然后服务的消费者如果调用故障机器那么故障机器中的服务提供者就会调用预先处理故障的方法。调用者这边只需要判断服务提供者里是否返回给了特定的错误标志,没有证明机器就是好的,只要有一条说明机器故障。可再次调用服务的提供者或者有其他的操作。好了接下来我就以之前的案例为准进行拓展,将断路器加进去。

这里需要说明一下,SpringCloud对RestTempletr调用服务提供者的方式和Feign调用提供服务者的方式都实现了相应的断路器,但本质上其实是同一个底层实现。先来RestTempletr对象的:

第一步:新创建一个maven项目也就是model

第二步:添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

第三步:设置配置文件

#配置端口号
server.port=6050

#配置服务消费者的名称,以便依赖计算关系
spring.application.name=server-consumer-Hystrix-ribbon

#配置注册中心的地址
eureka.client.service-url.defaultZone=http://localhost:6010/eureka/

第四步:在启动的main方法上面注入RestTempletr对象和加入注解。如下:

第五步:提供controller供web访问、再提供个service和其实现类
package com.alibaba.controller;

import com.alibaba.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    UserService userService;

    @RequestMapping(value="/getUserMassage")
    public String getUserMassgae(String username){
      return userService.getUserMassage(username);
    }
}

service的实现类:

第六步:直接启动main方法。然后观察到,当web浏览器中调用在上面定义的UserController中的getUserMassage方法后刚开始是能正确的在两台机器上来回切换的,当我们把其中的一个机器停了以后,再不断的访问getUserMassage时就会发现一个可以继续返回正常的值,一个就会返回error。这个error的返回值就是服务提供者返回给服务消费者的标志。

接下来,我说下一种Feign方式调用服务提供者时的断路器。

第一步:在创建的项目的时候添加依赖


第二步:配置服务的信息

#配置服务消费者的端口号
server.port=6070

#激活Hystrix
feign.hystrix.enabled=true

#配置服务消费者的名称,以便计算依赖
spring.application.name=server-consumer-Hystrix-Feige

#配置服务注册中心的地址
eureka.client.service-url.defaultZone=http://localhost:6010/eureka/

第三步:创建用于web浏览器访问的Controller和调用消费提供者的service

Controller:

package com.alibaba.controller;

import com.alibaba.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;


@RestController
public class UserController {

    @Resource
    UserService userService;

    @RequestMapping("/getUserMassage")
    public String getUserMassage(@RequestParam("username") String username){
      return userService.getUserMassage(username);
    }
}

Service:

package com.alibaba.service;

import com.alibaba.service.Impl.UserServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "SERVER-PROVIDER",fallback = UserServiceImpl.class)
public interface UserService {

    @RequestMapping("/getUserMassage")
    String getUserMassage(@RequestParam("username")  String username);
}

ServiceImpl:

package com.alibaba.service.Impl;

import com.alibaba.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public String getUserMassage(String username) {
        return "error is";
    }
}

第四步:在启动类中添加注解

package com.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class ServerConsumer03Application {

    public static void main(String[] args) {
        SpringApplication.run(ServerConsumer03Application.class, args);
    }

}

在Feign的服务调用方式中,我们将服务提供者的服务名和如果服务提供者发生异常后断开服务的标志(就是发生异常后,需要调用一个同名方法的返回值,这个放回值是断开服务的标志)需要的关联对象都定义到了@FeignClient注解里面。这个断开服务的标志一般是这个interface的实现类。在service接口中的某一个方法去调用服务提供者的服务的时候(注意:接口方法上面@RequestRapping注解里的路径是服务提供者的方法,它和接口上面@FeignClient注解里面定义的服务提供者的名称组合到一起就相当于是RestTemplater调用的URL路径),如果服务提供者发生错误了以后那么开始对服务提供者进行断路,返回给服务消费者的时候就会调用服务消费者service接口中当前方法的实现类中的实现方法,这个实现方法的返回值就是服务提供者返回给服务消费者的标志。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值