当前位置:首页 > 技术分析 > 正文内容

微服务 Spring Cloud 实战 Eureka+Gateway+Feign+Hystrix

ruisui883个月前 (02-03)技术分析27

前言

我所在项目组刚接到一个微服务改造需求,技术选型为 Spring Cloud,具体需求是把部分项目使用 Spring Cloud 技术进行重构。本篇文章 中介绍了 Eureka、Gateway、Feign 和 Hystrix 这些组件的用途,我整合这几个组件写了一个 demo,涉及四个工程分别是:注册中心 Eureka、网关 Gateway、服务 1、服务 2,涉及的功能分别有:使用 Zuul 配置动态路由、使用 ZuulFilter 过滤器实现 IP 白名单、使用 Feign 实现负载均衡的服务调用、使用 Hystrix 实现服务隔离、熔断、降级。

概念介绍

Eureka

Eureka 是 Spring Cloud 集合的重要组件之一,作为服务注册及发现的中心,即全部服务都会注册到 Eureka,其他工程需要使用服务时也是从 Eureka 得到(即服务发现)。Eureka 区分 server 和 client 两个性质,一般来说除 Eureka 工程外都是 client,比如:Gateway 工程是作为 Eureka 的 client。

Eureka 是可以集群部署,防止单个 Eureka 场景下宕机后导致 client 获取不了最新的服务列表(每个 client 都会缓存一份服务列表到本地),也可以防止 Eureka 宕机后 client 工程重启后获取不了服务列表,而导致无法使用服务。生产环境一般都是部署两个或者三个 Eureka 节点。

Gateway

Gateway 也是 Spring Cloud 集合的组件之一,主要是做动态路由(类似 Nginx)和请求过滤。动态路由是根据自定义的配置,把请求转发到指定的工程,类似于 Nginx;请求过滤基于 Filter 链的方式实现鉴权(比如:调用接口时校验是否携带 token)、限流(比如:固定哪些 IP 才可以调用接口)、监控(记录所有请求记录加以分析)等功能。

Feign

Feign 也是 Spring Cloud 集合的组件之一,是用来实现负载均衡的服务调用。提起 Feign 往往会想起 Ribbon 这个组件,Ribbon 也是实现负载均衡的服务调用,Ribbon 需要结合 RestTemplate 模板使用,每个服务调用的类都要注入 RestTemplate,用起来比较麻烦,Feign 是在 Ribbon 的基础上再进行封装,只需创建一个接口并使用注解方式配置对应的哪个实例及路径即可。

Hystrix

Hystrix 也是 Spring Cloud 集合的组件之一,是用来实现服务隔离、熔断、降级。隔离是把每个请求在不同的线程上执行,从而实现服务调用过程中出现问题也不影响其他服务的调用;熔断是在服务调用过程中,如果失败次数比例超过阈值(默认 50%),此时熔断器会切换到 open 状态(即所有请求都直接失败),熔断器在 open 状态保持一段时间后(默认 5 秒),会自动切换到 half-open 状态,此时如果有请求过来,则会根据请求结果来切换熔断器的状态,如果请求成功则把状态切换到 close,如果失败则切换到 open;降级是在请求过程中出现超时或者异常的情况下,直接中断请求避免拖垮整个微服务,返回自定义的信息(比如:服务繁忙),从而达到服务降级的效果。

Eureka 工程代码分析

代码结构

pom.xml 文件配置

下面配置是工程需要使用的所有 jar 和 maven 打包策略。



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.3.RELEASE
         
    
    com.example
    demo-Eureka
    0.0.1-SNAPSHOT
    demo-Eureka
    Demo project for Spring Boot


    
        1.8
        Dalston.SR1
    

    
        
            org.springframework.boot
            spring-boot-starter
        
        
            org.springframework.cloud
            spring-cloud-starter-Eureka-server
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    true
                
            
        
    

启动类的配置

启动类代码非常简单,加上 @SpringBootApplication 和 @EnableEurekaServer 即可,@SpringBootApplication 是开启自动配置;@EnableEurekaServer 是开启 EurekaServer 服务,即此工程作为 Eureka 的服务端,即服务注册及发现的中心。

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

application.yml 配置

application.yml 文件中配置了注册中心的实例名为 registry-server,端口为 8888,主机名为 127.0.0.1(也可以是域名),不优先使用 ip,唯一 id 是由 ip 地址 + 端口(比如:127.0.0.1:8888/)等等,具体配置说明请看下面的代码。

spring:
  application:
    name: registry-server

server:
  port: 8888

Eureka:
  instance:
    hostname: 127.0.0.1
    prefer-ip-address: false   #是否优先使用 ip 地址(与 hostname 相比)
    instance-id: ${spring.cloud.client.ipAddress}:${server.port}
  client:
    register-with-Eureka: false   #实例是否在 Eureka 服务器上注册自己的信息以供其他服务发现,默认为 true
    fetch-registry: false         #此客户端是否获取 Eureka 服务器注册表上的注册信息,默认为 true
    service-url:
      defaultZone: http://${Eureka.instance.hostname}:${server.port}/Eureka/    #与 Eureka 注册服务中心的通信地址
  server:
    enable-self-preservation: false          #服务端开启自我保护模式。无论什么情况,服务端都会保持一定数量的服务。避免 client 与 server 的网络问题,而出现大量的服务被清除。
    response-cache-update-interval-ms: 1000  #多长时间更新一次缓存中的服务注册数据(单位:毫秒)
    eviction-interval-timer-in-ms: 3000      #开启清除无效服务的定时任务并设置时间间隔(单位:毫秒),默认 1 分钟

Gateway 工程代码分析

代码结构

pom.xml 文件配置

下面配置是工程需要使用的所有 jar 和 maven 打包策略。



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
         
    
    com.example
    demo-Gateway
    0.0.1-SNAPSHOT
    demo-Gateway
    Demo project for Spring Boot

    
        UTF-8
        UTF-8
        1.8
        Greenwich.SR2
    

    
        
            org.springframework.cloud
            spring-cloud-starter-netflix-Eureka-client
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-zuul
        
        
            io.springfox
            springfox-swagger2
            2.6.1
        
        
            io.springfox
            springfox-swagger-ui
            2.6.1
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    true
                
            
        
    

启动类的配置

启动类代码非常简单,加上 @SpringBootApplication 和 @EnableEurekaServer 即可,@SpringBootApplication 是开启自动配置;@EnableEurekaServer 是开启网关服务。

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

application.yml 配置

application.yml 文件中配置了注册中心的实例名为 Gateway,端口为 9999,Eureka 地址(比如:127.0.0.1:8888/Eureka/),路由规则等等,具体配置说明请看下面的代码。

spring:
  application:
    name: Gateway

server:
  port: 9999

Eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/Eureka/
  instance:
    hostname: com.example.Gateway
    prefer-ip-address: true        #是否优先使用 ip 地址
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
    non-secure-port-enabled: true  #是否启用 http 通信端口
    secure-port-enabled: false     #是否启用 https 通信端口

zuul:
  routes:
    local:          #本地工程路由配置
      path: /**
      stripPrefix: false
      serviceId: forward:/
    user1:         #service-user1 工程路由配置
      path: /user1/**
      stripPrefix: false
      serviceId: service-user1
    user2:         #service-user2 工程路由配置
      path: /user2/**
      stripPrefix: false
      serviceId: service-user2

IP 白名单代码分析

下面代码是使用 ZuulFilter 过滤器机制实现 IP 限流功能,在把请求转发到具体实例之前,判断请求的 ip 是否在白名单内,如果是则允许访问,否则禁止访问,以达到 IP 限流效果。

package com.example.demoGateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

/**
 * IP 白名单,即 IP 限流
 */
@Component
public class IpWhitelistFilter extends ZuulFilter {

    private Logger logger = LogManager.getLogger(getClass());
    private static List ipWhiteList = new ArrayList<>();

    /**
     * 为了方便演示,此处固定写死白名单内容,正常来说是读外部数据库,比如 mysql、redis 等
     */
    static {
        ipWhiteList.add("127.0.0.1");
        ipWhiteList.add("localhost");
    }

    /**
     * pre:在请求被路由(转发)之前调用
     * route:在路由(请求)转发时被调用
     * error:服务网关发生异常时被调用
     * post:在路由(转发)请求后调用
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String remoteHost = request.getRemoteHost();    //获取请求 IP
        logger.log(Level.INFO, request.getMethod() + " request ip : {}", remoteHost);
        if (!(ipWhiteList.contains(remoteHost))) {
            logger.log(Level.WARN, request.getMethod() + " request from " + remoteHost + " is unauthorized.");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            return null;
        }
        logger.log(Level.INFO, "access ip ok");
        return null;
    }
}

service-user1 服务工程(Feign + Hystrix)代码分析

代码结构

pom.xml 文件配置

下面配置是工程需要使用的所有 jar 和 maven 打包策略。



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
         
    
    com.example
    demo-service-user1
    0.0.1-SNAPSHOT
    demo-service-user1
    Demo project for Spring Boot

    
        UTF-8
        UTF-8
        1.8
        Greenwich.SR2
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.cloud
            spring-cloud-starter-netflix-Eureka-client
        

        
            org.springframework.cloud
            spring-cloud-starter-openFeign
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-Hystrix
        

    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    true
                
            
        
    

启动类的配置

启动类代码非常简单,加上 @SpringBootApplication、@EnableFeignClients 和 @EnableHystrix 即可,@SpringBootApplication 是开启自动配置;@EnableFeignClients 是开启 Feign 服务,即负载均衡的服务调用;@EnableHystrix 是开启熔断服务。

@EnableHystrix         //开启熔断服务
@EnableFeignClients    //开启 Feign 服务,即负载均衡的服务调用
@SpringBootApplication
public class DemoServiceUser1Application {
    public static void main(String[] args) {
        SpringApplication.run(DemoServiceUser1Application.class, args);
    }
}

application.yml 配置

application.yml 文件中配置了注册中心的实例名为 service-user1,端口为 8081,Eureka 地址(比如:127.0.0.1:8888/Eureka/),是否开启 Hystrix 服务等等,具体配置说明请看下面的代码。

spring:
  application:
    name: service-user1
server:
  port: 8081

Eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/Eureka/
  instance:
    hostname: com.example.user1
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
    non-secure-port-enabled: true
    secure-port-enabled: false

Feign:
  Hystrix:
    enabled: true

Controller 类和 Service 类

下面代码是微服务中的 service-user1 工程调用 service-user2 工程接口,使用 Feign 组件进行服务调用,使用 Hystrix 组件实现熔断功能。

package com.example.demo.controller;

import com.example.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @Autowired
    private DemoService demoService;

    /**
     * 本工程接口
     * @return
     */
    @GetMapping(value = "/user1/test1")
    public String test1() {
        return "this is service-user1 service test1 method";
    }

    /**
     * 调用 service-user2 服务接口
     * @return
     */
    @GetMapping(value = "/user1/test2")
    public String test2() {
        return demoService.user2test2();
    }

    /**
     * 调用 service-user2 服务接口,验证熔断功能
     * @return
     */
    @RequestMapping(value = "/user1/test3")
    public String test3() {
        return demoService.user2test3();
    }

}


package com.example.demo.service;

import org.springframework.cloud.openFeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * service-user2 服务接口
 */
@Component
@FeignClient(name = "service-user2", fallback = DemoServiceFallback.class)
public interface DemoService {

    @RequestMapping(value = "/user2/test2", method = RequestMethod.GET)
    String user2test2();

    @RequestMapping(value = "/user2/test3", method = RequestMethod.GET)
    String user2test3();
}


package com.example.demo.service;

import org.springframework.stereotype.Component;

/**
 * DemoService 熔断实现
 */
@Component
public class DemoServiceFallback implements DemoService{

    @Override
    public String user2test2() {
        return "this is service-user2 service test2 fallback method";
    }

    @Override
    public String user2test3() {
        return "this is service-user2 service test3 fallback method";
    }
}

service-user2 服务工程代码分析

代码结构

pom.xml 文件配置

下面配置是工程需要使用的所有 jar 和 maven 打包策略。



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
         
    
    com.example
    demo-service-user2
    0.0.1-SNAPSHOT
    demo-service-user2
    Demo project for Spring Boot

    
        UTF-8
        UTF-8
        1.8
        Greenwich.SR2
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.cloud
            spring-cloud-starter-netflix-Eureka-client
        

        
            org.springframework.cloud
            spring-cloud-starter-openFeign
        

        
            org.springframework.boot
            spring-boot-starter-web
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    true
                
            
        
    

启动类的配置

启动类代码非常简单,加上 @SpringBootApplication 即可,@SpringBootApplication 是开启自动配置。

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

application.yml 配置

application.yml 文件中配置了注册中心的实例名为 service-user2,端口为 8082,Eureka 地址(比如:127.0.0.1:8888/Eureka/)等等,具体配置说明请看下面的代码。

spring:
  application:
    name: service-user2
server:
  port: 8082

Eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/Eureka/
  instance:
    hostname: com.example.user2
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
    non-secure-port-enabled: true
    secure-port-enabled: false

Controller 类

下面代码是两个非常简单的接口,单纯用来测试服务调用是否成功及熔断是否起效。

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping(value = "/user2/test2")
    public String test2() {
        return "this is service-user2 service test2 method";
    }

    @GetMapping(value = "/user2/test3")
    public String test3() {
        int i = 1/0;  //故意抛异常,测试熔断功能
        return "this is service-user2 service test3 method";
    }
}

接口测试

1. 查看 Eureka 基本信息和注册实例信息。

url:http://127.0.0.1:8888/

2. 测试接口 test1,经过统一网关,仅涉及 service-user1 工程。

url:http://127.0.0.1:9999/user1/test1

响应结果:

this is service-user1 service test1 method

3. 测试接口 test2,经过统一网关,涉及 service-user1 和 service-user2 工程,测试 Feign。

url:http://127.0.0.1:9999/user1/test2

响应结果:

this is service-user2 service test2 method

4. 测试接口 test3,经过统一网关,涉及 service-user1 和 service-user2 工程,测试 Feign + Hystrix。

url:http://127.0.0.1:9999/user1/test3

响应结果:

this is service-user2 service test3 fallback method

注意事项

1. Eureka 工程必须把是否在 Eureka 上注册自己标识设置为 false,该标识默认为 true,因为本身就是 Eureka 服务端,不需要自己注册自己;把是否获取 Eureka 上的服务列表标识设置为 false,该标识默认为 true,因为 Eureka 客户端才需要获取服务列表。

2. 权限控制模块应该放在网关 Gateway 工程上实现,因为网关是统一入口,所以请求都经过这里,最适合做统一鉴权。

3. 如果网关 Gateway 工程也提供接口,而且使用了 ZuulFilter 过滤器(比如:上面提到的 IP 白名单),必须配置 serviceId 为 forward:/,不然调用接口时不经过 ZuulFilter 过滤器。

    zuul:
      routes:
        local:     #本地工程路由配置,名字随意起
          path: /**
          stripPrefix: false
          serviceId: forward:/

4. 如果使用 Hystrix 组件实现熔断功能,则需要配置 Hystrix 的 enabled 属性为 true。

    Feign:
      Hystrix:
        enabled: true

总结

通过这次的微服务 Spring Cloud 实战,让我们认识了 Eureka、Gateway、Feign 和 Hystrix 是什么,可以实现什么功能;让我们掌握了如何配置注册中心 Eureka,如何配置网关 Gateway 的动态路由,如何使用 ZuulFilter 过滤器实现相关业务,如何使用 Feign 实现服务之间的负载均衡调用,如何使用 Hystrix 实现熔断机制等技术。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/1193.html

标签: feign 使用
分享给朋友:

“微服务 Spring Cloud 实战 Eureka+Gateway+Feign+Hystrix” 的相关文章

首个支持苹果 M1 Mac 的 Linux 发行版发布,面向用户开放下载

IT之家 3 月 20 日消息,Asahi Linux 是研究 Linux for Apple Silicon macs 的组织群体,3 月 18 日,Asahi Linux 宣布成功在 M1 MacBook Air 笔记本电脑上运行,并开放了 Asahi Linux 的下载安装。Asahi Lin...

「图解」父子组件通过 props 进行数据交互的方法

1.组件化开发,经常有这样的一个场景,就是父组件通过 Ajax 获取数据,传递给子组件,如何通过 props 进行数据交互来实现,便是本图解的重点。2.代码的结构3.具体代码 ①在父组件 data 中存放数据 ms。 ②将父组件 data 中的数据 ms 绑定到子组件中的属性 ms。 ③子组件在 p...

Gitlab+Jenkins通过钩子实现自动部署web项目,图文详细教程

扩展参考:Jenkins+Gitlab通过脚本自动部署回滚web项目至集群 一:基础环境介绍及准备1):Gitlab服务器:ubuntu 192.168.152.131 ---参考搭建:Linux安装gitlab,docker安装gitlab教程2):Jenkins服务器:ubunu 192.168...

Git 分支管理策略汇总

最近,团队新入职了一些小伙伴,在开发过程中,他们问我 Git 分支是如何管理的,以及应该怎么提交代码?我大概说了一些规则,但仔细想来,好像也并没有形成一个清晰规范的流程。所以查了一些资料,总结出下面这篇文章,一共包含四种常见的分支管理策略,分享给大家。Git flow在这种模式下,主要维护了两类分支...

html5+css3做的响应式企业网站前端源码

大家好,今天给大家介绍一款,html5+css3做的响应式企业网站前端源码 (图1)。送给大家哦,获取方式在本文末尾。首页banner幻灯片切换特效(图2)首页布局简约合理(图3)关于我们页面(图4)商品列表(图5)商品详情(图6)服务介绍(图7)新闻列表(图8)联系我们(图9)源码完整,需要的朋友...

数组、去重、排序、合并、过滤、删除

ES6数字去重 Array.from(new Set([1,2,3,3,4,4])) //[1,2,3,4] [...new Set([1,2,3,3,4,4])] //[1,2,3,4]2、ES6数字排序 [1,2,3,4].sort(); // [1, 2,3,4],默认是升序...