SpringCloud五大常用组件Eureka/Zuul/Ribbon/Feign/Hystrix

Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

SpringCloud是Spring旗下的项目之一,它是微服务架构的一种实现方式。

官网地址:http://projects.spring.io/spring-cloud/

Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

  1. Eureka:注册中心
  2. Zuul:服务网关
  3. Ribbon:负载均衡
  4. Feign:服务调用
  5. Hystrix:熔断器

场景模拟:

现在来使用springboot创建两个微服务,为了方便直观,将服务的提供者命名为user-service,将服务的消费者命名为consumer-demo,我们利用这两个微服务之间的调用,来感受一下pringcloud这个全局的治理框架的作用。

1Eureka

Eureka
Eureka 结构图
  • Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址;
  • Provider(提供者):启动后向Eureka注册自己信息(地址,服务名称等),并且定期进行服务续约;
  • Consumer(消费者):服务调用方,会定期去Eureka拉取服务列表,然后使用负载均衡算法选出一个服务进行调用;
  • Renewal(续约):服务调用方,会定期去Eureka拉取服务列表,然后使用负载均衡算法选出一个服务进行调用。

首先,Eureka是一个注册中心,和zookeeper功能类似,其中不同的一点是zookeeper是一个软件,启动直接可以使用,而Eureka是需要我们自己开发的。

下面对开发一个Eureka注册中心进项简单的解析:

1.首先一个简单的Eureka注册中心只需要一个依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
</dependencies>

2.其次,编写一个启动类,该类上加上一个注解

@SpringBootApplication
@EnableEurekaServer 		//开启Eureka注册服务
public class EurekaServer {
	public static void main(String[] args) {
		SpringApplication.run(EurekaServer.class, args);
	}
}

3.最后,编写该项目基本的配置文件

server:
  port: 10086					#端口号
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中作为服务的id标识(serviceId)
eureka:
  client:
    service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
      defaultZone: http://127.0.0.1:10086/eureka	#注册的地址,如果是集群,应该用,隔开
    register-with-eureka: false # 不注册自己
    fetch-registry: false #不拉取服务

这样一个简单的Eureka就完成了,可以启动服务,访问http://127.0.0.1:10086,查看在该注册中心上注册的以及服务的基本信息。

服务的注册(客户端的开发):

Eureka开发完成之后,访问发现没有服务注册到Eureka中,现在我们要让user-serviceconsumer-demo注册到Eureka上。

第一步:在需要注册的微服务中加入依赖

<!-- Eureka客户端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

第二步:在启动类上添加@EnableDiscoveryClient来开启Eureka客户端的功能

@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端发现功能
public class UserServiceDemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(UserServiceDemoApplication.class, args);
	}
}

 第三步:再次访问http://127.0.0.1:10086可以看到有两个服务注册到Eureka上

2Zuul

在微服务架构中,Zuul就是守门的大Boss!一夫当关,万夫莫开!

不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。

再来一张图:

Zuul 结构图
Zuul 结构图

这可以看出Zuul在整个架构中扮演着什么样的角色,其实zuul的主要作用有两个:过滤(鉴权)和路由和Eureka相同,Zuul同样需要我们自己来开发。

1.创建一个model,一个Zuul的的服务端应该导入下面这个依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
</dependencies>

2.编写启动类,这里要注意的是要打上@EnableZuulProxy开启zuul的网关功能

@SpringBootApplication
@EnableZuulProxy // 开启Zuul的网关功能
@EnableDiscoveryClient//开启Eureka的客户端发现
public class ZuulApplication {

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

3.编写配置文件

server:
  port: 10010 #服务端口
spring: 
  application:  
    name: api-gateway #指定服务名
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka		#配置Eureka的地址,将zuul会代理Eureka上注册的服务
zuul:
  prefix: /api # 添加路由前缀
  routes:
    user-service: /user-service/** # 这里是映射路径

这里的user-service在Eureka上注册的一个服务,这里配置了他的映射路径。路径就要以/api/user-service开头的请求都会被分发到该服务上。

Zuul是集成了另外一个组件负载均衡的组件Ribbon,所以,如果Eureka上有两个名称为user-service的服务,可以自动的进行负载均衡,默认的策略是轮询

熔断机制

Zuul还集成了Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断的超时时间只有1S,也就是说,访问一个服务,如果1s没有响应,就判定为异常,我们也可以自己设置超时时间。

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000

3Feign

我们已经将Eureka和Zuul开发完毕,而且上面注册了两个微服务,现在我们实现两个微服务之间的调用。

String baseUrl = "http://127.0.0.1:10010/user-service/user/";
User user = this.restTemplate.getForObject(baseUrl + id, User.class)

这样虽然能访问到,但是这样的代码不太优雅,这里使用了spring提供的RestTemplate,已经简化了操作,如果使用远程的httpclient,那更是怀疑人生,怎么实现优雅的访问呢?答案是Feign。

Feign的英文含义假装,伪装,为什么叫伪装?

Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

下面为我们的demo加上feign:

1.在consumer-demo中导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.开发feign的客户端,其实就是一个接口

@FeignClient("user-service") //声明这是一个Feign客户端,同时通过value属性指定服务名称
public interface UserClient {

    @GetMapping("/user/{id}")  //这里的返回结果和 url地址一定要和提供方保持一致
    User queryById(@PathVariable("id") Long id);
}

注意:访问路径和返回结果一定要与服务端的提供方一致,因为feign会根据这个路径为我们生成代理对象。

3.在启动类上开启feign的功能

@SpringCloudApplication
@EnableFeignClients // 开启Feign功能
public class ConsumerDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerDemoApplication.class, args);
    }
}

4.现在就可以使用feign的方式进行访问了

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private UserClient userClient;

    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id){
        return userClient.queryById(id);
    }
}

到这里feign的功能就已经实现了,单独看controller是看不出使用了远程调用的,这就是feign的作用。

最后强调:Feign中本身已经集成了Ribbon依赖和自动配置,不用再次引入依赖,就可以直接配置ribbon相关的参数。

4Ribbon

说完了feign,可以知道,feign是通过服务在Eureka上的serviceId来找寻服务的,也就是user-service,那么如果我们现在有两个user-service的服务:

user-service: 192.168.100.1
user-serivice: 192.168.100.2

那么feign怎么知道你要请求的是哪个服务器呢?

​ 这Ribbon就派上用场了。Ribbon就是专门解决这个问题的。它的作用是负载均衡,会帮你在每次请求时选择一台机器,均匀的把请求分发到各个机器上

Ribbon的负载均衡默认使用的最经典的Round Robin轮询算法。这是啥?简单来说,就是如果订单服务对库存服务发起10次请求,那就先让你请求第1台机器、然后是第2台机器、第3台机器、第4台机器、第5台机器,接着再来—个循环,第1台机器、第2台机器…………以此类推

我们也可以通过配置文件更改它默认的负载均衡算法:

#这是将算法更改为了随机
user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

5Hystrix

首先我们来了解一下什么是雪崩问题

​ 服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,形成雪崩效应。

解决这个问题,我们就需要来学习一下hystrix:

1 线程隔离

Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,解释一下,假如现在有两个服务:user-serviceorder-service,现在hystrix会为这两个服务分配两个小的线程池,如果user-service宕机会导致它的线程池所有的线程都被卡着,但是并不会影响到order-service的线程池,这两就不会出现雪崩问题了。

2 服务熔断

在线程隔离中讲到,如果user-service宕机会导致它的线程池所有的线程都被卡着,每次对user-service的访问都会失败,既然都会失败,就不要再走网络请求了,这就用到了Hystrix的服务熔断机制

​ 假如5s中之内访问一个服务20次都失败了,那么这5s之内再去访问这个服务就直接给熔断了,也就是不再请求,直接判定失败,5秒20次是Hystrix的默认值

​ 我们可以通知配置文件修改这些参数:

circuitBreaker:
 requestVolumeThreshold: 10
 sleepWindowInMilliseconds: 10000
 errorThresholdPercentage: 50
  • requestVolumeThreshold:触发熔断的最小请求次数,默认10
  • sleepWindowInMilliseconds:休眠时长,默认是10000毫秒
  • errorThresholdPercentage:触发熔断的失败请求最小占比,默认50%

3 服务降级

熔断之后直接判定失败,直接返回时不太好的,总的做的什么操作吧,这就是降级服务,也就是失败之后执行的一个方法,在这个方法中可以去记录请求的信息,也可以返回一些友好的 提示。

我们在consumer-demo中的试一下:

1.导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2.开启熔断

将启动类上的注解换成@SpringCloudApplication

@SpringCloudApplication
public class ConsumerApplication {
    // ...
}

3.编写降级逻辑

这里我们的降级逻辑,就返回一个提示好了

@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallBack")
public String queryById(@PathVariable("id") Long id){
    @Autowired
    private UserClient userClient;

    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id){
        return userClient.queryById(id);
    }
}

public String queryByIdFallBack(Long id){
    log.error("查询用户信息失败,id:{}", id);
    return "对不起,网络太拥挤了!";
}

要注意,因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明

这样,这个服务降级功能就完成了

引用自:https://www.cnblogs.com/changchangchang/p/12083330.html