Spring Cloud 入门(二)Zuul 服务网关、Ribbon 负载均衡、Feign 声明式接口调用


代码在 https://github.com/betterGa/SpringCloudDemo

一、Zuul 服务网关

在这里插入图片描述

    可以看到,如果没有服务网关,比如说有 4 个微服务,客户端就要知道每个微服务的 IP 地址和端口号;而如果有了网关,只需要和网关进行交互,这样可以减少客户端 和 微服务的交互次数,降低耦合性。由网关映射到各个微服务,相当于有一个 统一的访问入口,它会对请求进行 统一管理,可以把公共逻辑放在网关中进行处理,比如说,验证身份这种操作也可以放到网关里。
     Spring Cloud 集成了 Zuul 组件,实现服务网关。
     Zuul 是 Netflix 提供的⼀个开源的 API 网关服务器,是 客户端 和 ⽹站后端所有请求 的中间层,对外开放 ⼀个 API,将 所有请求 导入 统⼀的入口,屏蔽了服务端的具体实现逻辑,Zuul 可以实现 反向代理 的功能,在网关内部实现动态路由、身份认证、IP 过滤、数据监控等。Zuul 也是 Spring Cloud 集成的组件,通过它来实现服务网关。

首先。创建新 module ,并引入依赖:

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
    </dependencies>

提供配置文件:

server:
  port: 8030
spring:
  application:
    name: gateway
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
zuul:
  routes: 
    provider: /p/**

属性说明:

  • zuul.routes.provider: 给服务提供者 provider 设置映射,可以不需要再记住服务提供者的端口,在上一篇中 服务提供者有配置 spring: application: name: provider ,所以就能通过 /p 就可以映射到。

接下来,创建启动类,@EnableZuulProxy 注解中 包含了 @EnableZuulServer ,设置该类是⽹关的启动类; @EnableAutoConfiguration 注解可以帮助 Spring Boot 应⽤将所有符合条件的 @Configuration 配置加载到当前 Spring Boot 创建并使⽤的 IOC 容器中:

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

运行结果:
在这里插入图片描述
     可以看到,网关也注册进来了,而且此时除了通过访问端口 比如,8010: http://localhost:8010/student/findAll,还可以通过访问 8030 端口 ,也就是网关,然后通过映射访问服务,访问 http://localhost:8030/p/student/findAll :
在这里插入图片描述

Zuul 还自带了负载均衡的功能,就是说 可以用多个实例来分摊压力,修改 provider 代码(也就是 StudentHandler 类):

 	@Value("${server.port}")
    private String port;
    
 	@GetMapping("/index")
    public String index(){
        return "当前端口:"+this.port;
    }

接下来,依次启动 注册中心、8010 端口的 prodiver 、再把端口改成 8011 的 prodiver(修改配置文件,并再提供个启动类,或者 在 IDEA 的 Run/Debug Configuration 对话框中 勾选 Allow parallel run)、网关,可以看到,注册了两个提供者:
在这里插入图片描述
然后访问 8010 和 8011 的 index 路径 :
在这里插入图片描述
在这里插入图片描述
接下来,通过网关的映射去访问 /index 路径:
在这里插入图片描述
在这里插入图片描述

     可以看到,这两个端口是交替响应的,(为什么在 改成 8011 后,还能访问到 8010 的端口呢?应该是因为把 8010 的配置信息存在缓存里了吧,因为我再选择 IDEA 里的 Invalidate Caches / restart 后,再重新启动这俩端口对应的服务,是会报错的:Port 8011 was already in use. )这就是 负载均衡 的方式。
    
(但是这里有点奇怪,一个服务名,对应了两个端口 ❔❔❔ )
    

二、Ribbon 负载均衡

     在大型的分布式项目中,负载均衡是必备的,那么就可以采用 Ribbon来实现。
     Ribbon 是 Sping Cloud 的一个组件,Spring Cloud Ribbon 是一个负载均衡的解决方案。Ribbon是 Netflix 发布的负载均衡器,Spring 对其进行了集成,Spring Cloud Ribbon 是基于 Netflix Ribbon实 现的,是一个用于对 HTTP 请求进行控制的负载均衡客户端
     Spring Cloud Ribbon 也是要结合 Eureka Server 来使用的,因为也要在注册中心进行注册。在注册中心对 Ribbon 进行注册之后,Ribbon 就可以基于某种负载均衡算法,如轮询、随机、加权轮询、加权随机等 自动帮助服务消费者调用接口,开发者也可以根据具体需求 自定义 Ribbon 负载均衡算法。实际开发中,Spring Cloud Eureka 来使用,Eureka Server 提供所有可以调用的服务提供者列表,Ribbon 基于特定的负载均衡算法从这些服务提供者中选择要调用的具体实例。
    
在这里插入图片描述
    可以看到,A、B、C、D 四个微服务都能提供相同的功能,我们把这四个微服务 和 Ribbon 都进行注册,然后就可以从四个微服务中选择一个来响应客户端请求。

接下来,创建 module,并导入依赖,对它进行注册:

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>

然后创建配置文件:

server:
  port: 8040
spring:
  application:
    name: ribbon
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

    
Ribbon 本身是不提供服务的,它是用负载均衡的方式调用其他服务,创建启动类。使用 @LoadBalanced 注解声明一个基于 Ribbon 的负载均衡,标注此注解后,RestTemplate 就具有了客户端负载均衡的能力 :

@SpringBootApplication
public class RibbonApplication {
    public static void main(String[] args) {
        SpringApplication.run(RibbonApplication.class,args);
    }
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

因为会调用 provider 的服务,还是需要拷贝 Student 实体类的,然后提供控制类:

@RestController
@RequestMapping("/ribbon")
public class RibbonHandler {
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/findAll")
    public Collection<Student> findAll() {
        return restTemplate.getForObject("http://provider/student/findAll",
                Collection.class);
    }

    // 实现负载均衡
    @GetMapping("/index")
    public String index() {
        return restTemplate.getForObject("http://provider/student/index",
                String.class);
    }
}

(启动类和这个控制类要放在一个包下,这样 @Autowired 才能识别到 @Bean 生成的 bean,如果不在一个包下,是要用 @ComponentScan 注解设置扫描包的。)

    然后还是把 provider 修改成 8010 和 8011 都能访问到的,再启动 Ribbon,访问 /findAll 路径 :
在这里插入图片描述
    可以看到调用正常,但是看不出来调用的端口,而如果访问 http://192.168.1.109:8040/ribbon/index:
在这里插入图片描述
刷新:
在这里插入图片描述
可以看到,是轮询的,两个端口交替响应客户端请求。
    

三、Feign 声明式接口调用

     Feign也是用于实现负载均衡,但是它的使用要比 Ribbon 更加简化, Feign 实际上是基于 Ribbon 进行了封装,让我们可以通过调用接口的方式实现负载均衡。
     Feign 和 Ribbon 都是由 Netflix 提供的,Feign 是一个声明式、模板化的 Web Service 客户端,它简化了开发者编写 Web 服务客户端的操作,开发者可以通过简单的接口 和 注解 来调用 HTTP API,使得开发变得更加简化、快捷。Spring Cloud Feign 也是基于 Netflix Feign 的二次开发,它整合了 Ribbon 和 Hystrix,具有可插拔、基于注解、负载均衡、服务熔断等一系列的便捷功能,也就是说我们 在实际开发中可以用 Feign 来取代 Ribbon。
     相比较于 Ribbon+RestTemplate 的方式,Feign 大大简化了代码的开发,Feign 支持多种注解,包括 Feign 注解、JAX-RS 注解、Spring MVC 注解等,Spring Cloud 对 Feign 进行了优化,整合了 Ribbon 和 Eureka,从而让 Feign 使用更加方便。
    

1、Ribbon 和 Feign 的区别

     Ribbon 是一个通用的 HTTP 客户端工具,Feign 是基于 Ribbon 实现的。
    

2、Feign 的优点
  • Feign 是一个声明式的 Web Service 客户端。
  • 支持 Feign 注解、Spring MVC注解、JAX-RS注解
  • Feign 是基于 Ribbon 实现,使用起来更加方便
  • Feign 集成了 Hystrix,具备服务熔断的功能
        
3、实战

     先新建 module,再导入依赖:

	<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
    </dependencies>

提供配置文件:

server:
  port: 8050
spring:
  application:
    name: feign
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka
  instance:
    prefer-ip-address: true

创建启动类:

@SpringBootApplication
@EnableFeignClients
public class FeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(FeignApplication.class,args);
    }
}

照样要拷贝 Student 实体类,然后通过声明式接口调用,直接提供接口:

@FeignClient(value = "provider")
public interface FeignProviderClient {
    
    @GetMapping("/student/findAll")
    public Collection<Student> findAll();
    
    @GetMapping("/student/index")
    public String index();
}

在控制层调用接口:

@RestController
@RequestMapping("/feign")
public class FeignHandler {
    @Autowired
    private FeignProviderClient client;

    @GetMapping("/findAll")
    public Collection<Student> findAll() {
        return client.findAll();
    }

    @GetMapping("/index")
    public String index() {
        return client.index();
    }
}

运行 feign 的启动类:
在这里插入图片描述
     访问 /index 路径也是和之前的 Ribbon 一个效果,刷新的话可以看到轮询调用 8010 、8011 两个端口。

     在大型微服务项目中,一个需求可能调用很多个微服务,如果某一个微服务出现了问题,可能导致整个业务出现延迟,或者整个崩溃的问题。熔断是说,当某一个微服务出现问题时,通过降级处理,或者应急措施,保证整个服务整体不崩溃。
     接下来,使用 Feign 的熔断机制。先把两个服务提供者都停掉,会看到:
在这里插入图片描述
    
为了不直接暴露错误信息,向用户更好地呈现,我们需要添加服务熔断机制,修改application.yml ,开启熔断机制 :

feign:
  hystrix:
    enabled: true

    
接下来,创建 FeignProviderClient 接口的实现类 FeignError ,定义容错处理机制,并且 通过 @Component 注解将它的实例注入到 IOC 容器中:

@Component
public class FeignError implements FeignProviderClient{
    @Override
    public Collection<Student> findAll() {
        return null;
    }

    @Override
    public String index() {
        return "服务维护中!";
    }
}

    
还需要在 FeignProviderClient 接口定义处定义 @FeignClient 的 fallback 属性来做降级处理,设置映射,映射到实现类 FeignError 中去。修改接口:

@FeignClient(value = "provider",fallback = FeignError.class)
public interface FeignProviderClient {

    
这样的话,如果访问的路径没问题,就会调用 provider 里的方法,如果出现了问题,就调用 FeignError 里的方法。把 两个 provider 服务都停掉,访问 feign 路径,运行结果:
在这里插入图片描述
因为 findAll 方法实现里是返回 null,所以这时如果访问 /findAll 路径,是空白页面。

    

四、总结

    Zuul、Ribbon、Feign 都能做负载均衡。

(1)网关的作用在于,当有多个微服务时,客户端只需要和网关进行交互,这样可以减少客户端 和 微服务的交互次数,降低耦合性。而且 由网关映射到各个微服务,相当于有了一个统一的访问入口,它会对请求进行统一管理。
     Spring Cloud 集成了 Zuul 组件,实现服务网关,还自带了负载均衡的功能,可以用多个实例来分摊压力,实现了反向代理。
     使用 @EnableZuulProxy 注解声明网关启动类,在配置文件中设置服务和映射路径,即可通过映射路径访问服务。
    
(2)Spring Cloud Ribbon 是一个用于对 HTTP 请求进行控制的负载均衡客户端,它本身不提供服务,而是用负载均衡的方式调用其他服务。
     使用 @LoadBalanced 注解使 RestTemplate 具有客户端负载均衡的能力。

(3)Feign 是对 Ribbon 的封装,还集成了 Hystrix ,所以,可以用于实现负载均衡,还可以实现熔断机制。
     但是它的使用要比 Ribbon 更加简化,让我们可以通过调用接口的方式实现负载均衡。
     使用 @EnableFeignClients 声明启动类,使用 @FeignClient 声明接口,在控制层直接调用接口里的方法。
    
     熔断是说 当某一个微服务出现问题时,通过降级处理,或者应急措施,保证整个服务整体不崩溃。
     要想使用熔断机制,需要先实现接口,定义容错处理机制,然后在接口定义处使用 @FeignClient 的 fallback 属性来做降级处理,映射到实现类中去,比如:

@FeignClient(value = "provider",fallback = FeignError.class)
public interface FeignProviderClient {

    

🎉补充:正向代理 和 反向代理

     简单地说,正向代理是 隐藏真实的请求客户端;反向代理是 隐藏真实的服务端。
     正向代理,服务器不知道真实的客户端是谁。比如,我们用浏览器访问 http://www.xxx.com 被 block 了,可以在国外搭建一台代理服务器,去代理,帮我们完成请求。
     而反向代理,比如打电话给 10086,我们并不知道具体是哪个客服小哥 / 姐姐;再比如,当请求 www.baidu.com 时,背后有成千上万台服务器,我们并不知道,也不需要知道具体是哪一台。 Nginx 就是性能非常好的反向代理服务器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值