SpringCloud(2)

Eureka管理机制

心跳检测

1.Eureka Client会定期(默认30秒)向Eureka Server端发送信息刷新注册信息,表示续约。若在指定时间(默认90秒)没有向服务器发送信息,则服务端将其从服务列表中删除。

  • 控制发送心跳间隔

eureka.instance.lease-renewal-interval-in-seconds 表示eureka client发送心跳给server端的频率,默认30秒。 如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance。

  • 控制超时清理间隔

eureka.instance.lease-expiration-duration-in-seconds 表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳, 则将移除该instance。默认为90秒 如果该值太大,则很可能将流量转发过去的时候,该instance已经不存活了。 如果该值设置太小了,则instance则很可能因为临时的网络抖动而被摘除掉。 建议该值至少应该大于leaseRenewalIntervalInSeconds

2.Eureka Server定期(默认60秒)检测注册列表,清理无效服务节点。但是由于Server默认开启了自我保护机制,即使有无效节点也不会剔除。

  • 控制清理无效节点间隔

eureka.server.eviction-interval-timer-in-ms eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒

自我保护机制

默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。

Eureka通过“自我保护模式”来解决上述问题,一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

自我保护的工作机制是如果在15分钟内正常心跳的客户端节点低于85%,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:

  • Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
  • Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然
    可用。
  • 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
    控制启用/关闭自我保护配置如下,开发测试可关闭,发布运行时建议启用。
eureka.server.enable-self-preservation是否开启自我保护模式,默认为true。

Eureka的自我保护机制的意义在于当EurekaServer由于自身发生网络故障等原因无法接收到EurekaClient端发送的心跳(续约)时,不会将未收到心跳(续约)请求的服务下线,虽然这样短时间内可能造成EurekaServer维护的注册列表信息不是完全准确的,但保证了EurekaServer可用性。它底层源码主要通过expectedNumberOfRenewsPerMinnumberOfRenewsPerMinThreshold这两个值判断是否进入自我保护模式,当每分钟收到的心跳数量小于期望收到的心跳数量,EurekaServer便会进入自我保护模式,不会剔除任何一个服务,直到心跳回复正常后退出自我保护模式。

服务注册源码:

if (this.expectedNumberOfRenewsPerMin > 0) {
    // Since the client wants to cancel it, reduce the threshold
    // (1
    // for 30 seconds, 2 for a minute)
    // 每分钟期望收到的心跳(续约)次数增加两次
    this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;
    // 每分钟期望最小心跳(续约 )次数 = 修改后的expectedNumberOfRenewsPerMin * 默认的0.85
    this.numberOfRenewsPerMinThreshold =
        (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
}

服务下线源码:

synchronized (lock) {
    if (this.expectedNumberOfRenewsPerMin > 0) {
        // Since the client wants to cancel it, reduce the threshold (1 for 30 seconds, 2 for a minute)
        // 每分钟期望收到的心跳(续约)次数减少两次
        this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin - 2;
        // 每分钟期望最小心跳(续约 )次数 = 修改后的expectedNumberOfRenewsPerMin * 默认的0.85
        this.numberOfRenewsPerMinThreshold =
            (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
    }
}

Eureka Server控制台有Renews threshold和Renews (last min)两个参数

Renews threshold:Eureka Server 期望每分钟收到客户端实例续约的总数。 Renews (last min):Eureka Server 最后 1 分钟收到客户端实例续约的总数。

当自我保护模式被激活时会发现Renews (last min)*85% < Renews threshold。

服务注册列表

通过心跳机制Eureka Server端定时更新注册列表。在Eureka Client端也提供了服务注册列表缓存,Client中启动了一个定时任务,周期性的通过Http请求去Server端拉取最新的注册表信息,并缓存到本地,默认30秒。

eureka.client.registry-fetch-interval-seconds //更新列表缓存时间,默认30秒
eureka.client.fetch-registry //默认开启,抓取注册列表

当Eureka Server端出现故障时,Eureka Client也可以通过缓存的注册列表实现服务查找和调用。

SpringCloud Hystrix断路器

SpringCloud提供了熔断机制,当多个服务互相关联,某个服务坏掉,会影响关联的服务导致大面积失败,称为雪崩效应。
Hystrix断路器提供了服务降级、服务隔离等机制保护系统。

1.服务降级

  • 默认情况下,Cloud Hystrix要求1秒钟响应,超过1秒被认为失败,会自动调用“备胎”方法。

2.服务隔离机制

  • 当服务在某个段时间内(默认5S),如果处理失败的频率达到指定比率(默认50%),这是Hystrix就要将断路器阀Open(断开服务),又过5S会将阀半开状态,如果处理成功率达到指定标准,会将断路器阀Close,未达到标准再Open。

服务降级使用方法

1.普通服务降级使用

  • 在pom.xml导入spring-cloud-starter-netflix-hystrix
  • 在启动类前使用@EnableCircuitBreaker@EnableHystrix
  • 在服务处理方法前使用@HystrixCommand(fallbackMethod="loadDirectionFallBack")
@GetMapping("/direction/{id}")
@HystrixCommand(fallbackMethod="loadDirectionFallBack")
public YdmaResult loadDirection(@PathVariable("id")int id) {
    return directionService.loadDirection(id);
}
//fallback方法参数和原服务方法保持一致
public YdmaResult loadDirectionFallBack(int id) {
    YdmaResult result = new YdmaResult();
    result.setCode(YdmaConstant.ERROR1);
    result.setMsg(YdmaConstant.ERROR_MSG);
    return result;
}
  • 默认处理时间1S,可以通过下面配置改变
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000

2.Feign服务降级使用

通过feign接口模式调用服务,发生延迟或异常,默认是不会触发Hystrix服务降级的,需要进行设置和开启。

  • 编写Feign接口实现类,在实现类方法中写默认FallBack返回逻辑.

提示:实现组件要追加@Component扫描标记

  • 将Feign接口实现类给@FeignClient的fallback属性指定
@FeignClient(name="YDMA-DIRECTION",fallback=DirectionFeignRemoteFallBack.class)
public interface DirectionFeignRemote {

    @GetMapping("/direction/get")
    public YdmaResult loadDirection(int id);

    @GetMapping("/direction/{id}")
    public YdmaResult loadDirection1(@PathVariable("id")int id);

    @GetMapping("/direction/all")
    public YdmaResult loadAllDirections();
}
  • 在application.properties配置中打开Feign启用Hystrix设置

在A、B、C默认结合启用,但是从D以后默认关闭,需要打开设置

feign.hystrix.enabled=true

SpringCloud Zuul

网关,外界环境访问Cloud服务中心的服务时,都需要通过网关组件进行访问,相当于代理作用。微服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能。

  • 在Spring cloud体系中,一般上选择zuul或者Gateway网关技术。

Spring Cloud Zuul:Zuul是Netflix开源的微服务网关
可以和Eureka、Ribbon、Hystrix等组件配合使用,
Spring Cloud对Zuul进行了整合与增强,Zuul的主要功能是路由转发和过滤器。

Spring Cloud Gateway:是由spring官方基于Spring5.0,Spring Boot2.0,Project Reactor等技术开发的网关,
提供了一个构建在Spring Ecosystem之上的API网关,旨在提供一种简单而有效的途径来发送API,
并向他们提供交叉关注点,例如:安全性,监控/指标和弹性。
目的是为了替换Spring Cloud Netfilx Zuul的。


SpringCloud Zuul网关的搭建过程如下:

1.创建一个项目,在pom.xml导入spring-boot-starter-parentspring-cloud-denpendencesspring-cloud-starter-netflix-eureka-clientspring-cloud-starter-netflix-zuul


    org.springframework.boot
    spring-boot-starter-parent
    2.0.5.RELEASE


    
        
        
          org.springframework.cloud
          spring-cloud-dependencies
          Finchley.RELEASE
          pom
          import
        
    
    
    
        
            org.springframework.cloud
            spring-cloud-starter-netflix-zuul
        
        
            org.springframework.cloud
            
                spring-cloud-starter-netflix-eureka-client
            
        
    

2.在application.properties配置文件定义eureka参数

server.port=9999
spring.application.name=ydma-zuul
eureka.client.serviceUrl.defaultZone=http://localhost:3333/eureka

3.在启动类前使用@EnableZuulProxy@EnableDiscoveryClient

@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy//启用zuul代理
public class ZuulRunBoot {

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

4.外界访问,通过zuul网关访问eureka注册其他的集群服务,访问格式

http://localhost:9999/服务名/请求

提示:服务名用小写


Zuul Filter

SpringCloud提供了很多内置的filter,进行请求处理。


image.png

Zuul过滤器适合做一些权限检查、身份认证等处理。
编写实现类,继承ZuulFilter,重写约定方法

@Component
public class CheckLoginFilter extends ZuulFilter{

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public boolean shouldFilter() {
        //获取请求URI,不需要登录的就return false;需要登录的return true
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String uri = request.getRequestURI();
        List checkUris = new ArrayList();
        checkUris.add("/ydma-video/chapters");
        System.out.println("检查"+uri+"是否需要进行拦截检查");
        if(checkUris.contains(uri)) {
            return true;//true调用filter
        }
        return false;//false不调用filter
    }

    @Override
    public Object run() throws ZuulException {
        //过滤器执行逻辑
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();
        String ticket = request.getParameter("ticket");
        if(ticket != null&&!"".equals(ticket)) {
            YdmaResult checkResult = restTemplate.getForObject(
                    "http://YDMA-USER/user/ticket?ticket="+ticket, YdmaResult.class);
            if(checkResult.getCode()==YdmaConstant.SUCCESS) {
                ctx.setSendZuulResponse(true);//允许继续路由调用服务
                ctx.setResponseStatusCode(200);
                return null;
            }
        }
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET,POST");
        response.setContentType("text/html;charset=utf-8");
        ctx.setSendZuulResponse(false);//阻止继续路由调用服务
        ctx.setResponseStatusCode(401);//response.setStatus(401);
        ctx.setResponseBody("{\"code\":-1,\"msg\":\"用户身份不合法,未登录\"}");//out.println("{\"code\":-1,\"msg\":\"用户身份不合法,未登录\"}");
        return null;
    }

    @Override
    public String filterType() {
        return "pre";//过滤器类型,pre、post、error、route
    }

    @Override
    public int filterOrder() {
        return 0;//过滤器执行顺序... -3、-2、-1、0、1、2、3
    }
}

Zuul Fallback回退机制

我们在项目中使用Spring cloud zuul的时候,在zuul进行路由分发时,如果后端服务没有启动,或者调用超时,这时候我们希望Zuul提供一种hystrix服务降级处理,而不是将异常暴露出来。

Zuul集成了Hystrix,提供一个FallbackProvider回退机制当路由后面的服务发生故障时进行降级处理。

@Component
public class ServiceFallbackProvider implements FallbackProvider {
    @Override
    // 返回值表示需要针对此微服务做回退处理(该名称一定要是注册进入 eureka 微服务中的那个 serviceId 名称)
    public String getRoute() {
        return "*"; // api服务id,如果需要所有调用都支持回退,则return "*"或return null
    }
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK; // 请求网关成功了,所以是ok
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.OK.value();
            }
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.OK.getReasonPhrase();
            }
            @Override
            public void close() {

            }
            @Override
            public InputStream getBody() throws IOException {
                String msg = "{\"code\":-1,\"msg\":\"服务调用失败\"}";
                return new ByteArrayInputStream(msg.getBytes("UTF-8")); // 返回前端的内容
            }
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); // 设置头
                return httpHeaders;
            }
        };
    }
}

你可能感兴趣的:(SpringCloud(2))