Spring Cloud Zuul Filter 和熔断

转一篇很不错的关于Spring Cloud Zuul 相关用法的文章,基本包含常用的一些场景,另外附上实际项目中的熔断、打印请求日志和登录验证的实例。

原文地址:https://www.cnblogs.com/shihaiming/p/8489006.html ,作者:https://www.cnblogs.com/shihaiming/

1.服务熔断

package com.ftk.hjs.zuul.server.hystrix;

import com.alibaba.fastjson.JSON;
import com.ftk.hjs.common.WebConstants;
import com.ftk.hjs.common.common.Response;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class ServiceFallbackProvider implements FallbackProvider {

    private static final Logger logger = LoggerFactory.getLogger(ServiceFallbackProvider.class);
    @Autowired
    private RouteLocator routeLocator;
    @Autowired
    private UrlPathHelper urlPathHelper;

    //服务id,可以用* 或者 null 代表所有服务都过滤
    @Override
    public String getRoute() {
        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 {
                RequestContext ctx = RequestContext.getCurrentContext();
                Route route = route(ctx.getRequest());
                logger.error(" >>>触发zuul-server断溶;zuulServletContextPath={{}}", route.getLocation());
                Response response = new Response(false);
                response.setMessage(WebConstants.SYSTEM_ERROR_MESSAGE);
                return new ByteArrayInputStream(JSON.toJSONString(response).getBytes("UTF-8")); //返回前端的内容
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); //设置头
                return httpHeaders;
            }
        };
    }

    //核心逻辑,获取请求路径,利用RouteLocator返回路由信息
    protected Route route(HttpServletRequest request) {
        String requestURI = urlPathHelper.getPathWithinApplication(request);
        return routeLocator.getMatchingRoute(requestURI);
    }

}

2.打印日志的拦截器

package com.ftk.hjs.zuul.server.filter;

import com.alibaba.fastjson.JSONObject;
import com.ftk.framework.redis.clients.collections.builder.RedisStrutureBuilder;
import com.ftk.framework.redis.clients.collections.keyValueRedisStructure;
import com.ftk.hjs.common.WebConstants;
import com.ftk.hjs.common.constant.RedisConstants;
import com.ftk.hjs.common.data.LoginUserData;
import com.ftk.hjs.common.utils.StringUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.util.StreamUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

public class PrintRequestLogFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(PrintRequestLogFilter.class);

    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;//要打印返回信息,必须得用"post"
    }

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

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

    @Override
    public Object run() {
        try {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            InputStream in = request.getInputStream();
            String reqBbody = StreamUtils.copyToString(in, Charset.forName("UTF-8"));

            String principal = request.getHeader(WebConstants.TOKEN_KEY);

            if (StringUtil.isNotBlank(principal)) {
                String userToken = RedisConstants.UserNP.USER_TOKEN_KEY + principal;
                keyValueRedisStructure<String> structure = RedisStrutureBuilder.ofKeyValue(String.class)
                        .withNameSpace(RedisConstants.UserNP.USER_NAMESPACE)
                        .withttl(RedisConstants.UserNP.USER_TOKEN_EXPIRE).build();
                String token = structure.get(userToken);
                if (StringUtil.isNotBlank(token)) {
                    LoginUserData userData = JSONObject.parseObject(token, LoginUserData.class);
                    log.info("request token:{} , userNum:{} , mobilePhone:{} , channelNum:{}", principal, userData.getUserNum(), userData.getMobilePhone(), userData.getChannelNum());
                }

            }
            log.info("request url:{} , requestUrl:{}", request.getMethod(), request.getRequestURL().toString());

            if (reqBbody != null) {
                log.info("request body:{}", reqBbody);
            }
            String outBody = ctx.getResponseBody();
            if (outBody != null) {
                log.info("response body:{}", outBody);
            }
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
            ctx.setResponseBody(outBody);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

}

3.登录验证

package com.ftk.hjs.zuul.server.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ftk.framework.redis.clients.collections.MapStructure;
import com.ftk.framework.redis.clients.collections.builder.RedisStrutureBuilder;
import com.ftk.framework.redis.clients.collections.keyValueRedisStructure;
import com.ftk.hjs.common.WebConstants;
import com.ftk.hjs.common.common.Request;
import com.ftk.hjs.common.common.Response;
import com.ftk.hjs.common.constant.ErrCodeConstants;
import com.ftk.hjs.common.constant.RedisConstants;
import com.ftk.hjs.common.data.LoginUserData;
import com.ftk.hjs.common.utils.StringUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import lombok.Data;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.nio.charset.Charset;
import java.util.*;

import static com.alibaba.fastjson.JSON.parseObject;

/**
 * 登录验证
 * Created by Frank on 2016/12/8.
 */
public class LoginFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(LoginFilter.class);

    private final RouteLocator routeLocator;
    private final UrlPathHelper urlPathHelper;

    private static List<String> oldServers = new ArrayList<>();
    private static List<String> newServers = new ArrayList<>();

    private List<String> excludeSuffixs = new ArrayList<>();

    public LoginFilter(RouteLocator routeLocator, UrlPathHelper urlPathHelper) {
        this.routeLocator = routeLocator;
        this.urlPathHelper = urlPathHelper;
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_FINANCIAL);
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_COMMON);
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_FUND);
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_INSURANCE);

        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_BANK);
        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_MESSAGE);
        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_USER);
        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_ROUT);

        excludeSuffixs.addAll(Arrays.asList(".png", ".js", ".css"));
    }

    //核心逻辑,获取请求路径,利用RouteLocator返回路由信息
    protected Route route(HttpServletRequest request) {
        String requestURI = urlPathHelper.getPathWithinApplication(request);
        return routeLocator.getMatchingRoute(requestURI);
    }

    @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 userNum = null;
        String body = "";
        String principal = request.getHeader(WebConstants.TOKEN_KEY);
        String osTypeKey = request.getHeader(WebConstants.OSTYPE_KEY);
        String channelNum = request.getHeader(WebConstants.CHANNEL_NUM);
        ctx.addZuulRequestHeader(WebConstants.OSTYPE_KEY, osTypeKey);
        ctx.addZuulRequestHeader(WebConstants.CHANNEL_NUM, channelNum);
        try {
            body = StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"));
//            log.info(">>> zuul LoginFilter body={}", body);
            if(StringUtil.isBlank(principal)){
                principal = request.getParameter(WebConstants.TOKEN_KEY);
            }
            if (StringUtil.isNotBlank(principal)) {
                String userToken = RedisConstants.UserNP.USER_TOKEN_KEY + principal;
                //如果用户token为空,则肯定是没有登录
                if (StringUtil.isBlank(userToken)) {
                    return null;
                }

                keyValueRedisStructure<String> structure = RedisStrutureBuilder.ofKeyValue(String.class)
                        .withNameSpace(RedisConstants.UserNP.USER_NAMESPACE)
                        .withttl(RedisConstants.UserNP.USER_TOKEN_EXPIRE).build();
                String token = structure.get(userToken);
                if (StringUtil.isNotBlank(token)) {
                    LoginUserData userData = JSONObject.parseObject(token, LoginUserData.class);
                    if (userData != null) {
                        userNum = userData.getUserNum();
                        channelNum = userData.getChannelNum();
                        //延长用户token登录时间
                        structure.set(userToken, token);
                        ctx.addZuulRequestHeader(WebConstants.USER_NUM, userNum);
                        ctx.addZuulRequestHeader(WebConstants.CHANNEL_NUM, channelNum);
                        JSONObject jsonObject = new JSONObject();
                        if (!StringUtil.isEmpty(body)) {
                            jsonObject = JSONObject.parseObject(body);
                        }
                        jsonObject.put("userNum", userNum);
                        request.setAttribute("userNum", userNum);
                        final byte[] reqBodyBytes = jsonObject.toJSONString().getBytes();
                        ctx.setRequest(new HttpServletRequestWrapper(request) {
                            @Override
                            public ServletInputStream getInputStream() {
                                return new ServletInputStreamWrapper(reqBodyBytes);
                            }

                            @Override
                            public int getContentLength() {
                                return reqBodyBytes.length;
                            }

                            @Override
                            public long getContentLengthLong() {
                                return reqBodyBytes.length;
                            }
                        });
                    }
                }
            }

//            log.info(" >>> gateWay url={}, userTokenKey={}, userNum={}", request.getRequestURI(), principal, userNum);

            Route route = route(ctx.getRequest());
            String requestURI = request.getRequestURI();
            String zuulServletContextPath = route.getLocation().replace("-server", "");
            //验证接口是否需要登录
            MapStructure<Boolean> structure = RedisStrutureBuilder.ofMap(Boolean.class).withNameSpace(RedisConstants.SystemNP.SYSTEM_NAMESPACE).build();
            Map<String, Boolean> serviceMethod = structure.get(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD.concat(zuulServletContextPath));
            NeedLoginBean needLoginBean = adaptServiceMethod(requestURI, body, serviceMethod, zuulServletContextPath);
            log.info(">>> zuul LoginFilter needLoginBean={}", needLoginBean);
            //static 静态资源不进行接口验证

            for (String suffix : excludeSuffixs) {
                if (requestURI.endsWith(suffix)) {
                    return null;
                }
            }

            //选判断此接口是否存在zuul网关中
            if (!needLoginBean.isHasMethod()) {
                log.error(">>> 未知接口。requestType={}", requestURI);
                ctx.setSendZuulResponse(false); //不进行路由
                ctx.getResponse().setContentType("text/html;charset=UTF-8");
                Response response = new Response(needLoginBean.getRequestURI(), false);
                response.setMessage(WebConstants.SYSTEM_ERROR_MESSAGE);
                ctx.setResponseBody(JSON.toJSONString(response));
                return null;
            }
            boolean needLogin = needLoginBean.needLogin;
            if (needLogin && StringUtil.isBlank(userNum)) {
                log.error(">>> 当前接口需要登录,请先登录。requestType={}", requestURI);
                ctx.setSendZuulResponse(false); //不进行路由
                ctx.getResponse().setContentType("text/html;charset=UTF-8");
                Response response = new Response(needLoginBean.getRequestURI(), false);
                response.setErrorCode(ErrCodeConstants.NEED_LOGIN.getCode());
                response.setMessage(ErrCodeConstants.NEED_LOGIN.getMessage());
                ctx.setResponseBody(JSON.toJSONString(response));
            }
        } catch ( Exception e) {
            log.error(e.getMessage(), e);
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            Response response = new Response(request.getRequestURI(), false);
            response.setMessage(WebConstants.SYSTEM_ERROR_MESSAGE);
            ctx.setResponseBody(JSON.toJSONString(response));
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
        }
        return null;
    }

    public NeedLoginBean adaptServiceMethod(String requestURI, String req, Map<String, Boolean> serviceMethod, String zuulServletContextPath) {
        NeedLoginBean needLoginBean = new NeedLoginBean();//兼容老的服务调用方式
        if (oldServers.contains(zuulServletContextPath)) {
            Request bizRequest = parseObject(req, Request.class);
            needLoginBean.setRequestURI(bizRequest.getRequestType());
            Boolean needLogin = serviceMethod.get(bizRequest.getRequestType());
            if (needLogin == null) {
                //false说明此接口不在网关注册接口范围内
                needLoginBean.setHasMethod(false);
            } else {
                needLoginBean.setHasMethod(true);
                needLoginBean.setNeedLogin(needLogin);
            }

        } else if (newServers.contains(zuulServletContextPath)) {
            needLoginBean.setRequestURI(requestURI);
            //false说明此接口不在网关注册接口范围内
            PathMatcher matcher = new AntPathMatcher();
            Iterator it = serviceMethod.keySet().iterator();
            while (it.hasNext()) {
                String key = (String) it.next();
                boolean result = matcher.match(key, requestURI);
                if (result) {
                    needLoginBean.setHasMethod(true);
                    needLoginBean.setNeedLogin(serviceMethod.get(key));
                    break;
                }
            }

        } else {
            throw new RuntimeException(" >>>请求接口不存在");
        }
        return needLoginBean;
    }

    @ToString
    @Data
    private static class NeedLoginBean {
        String requestURI;
        boolean hasMethod;
        boolean needLogin;
    }
}

3.启动类

package com.ftk.hjs.zuul.server;

import com.ftk.framework.redis.clients.collections.factory.RedisConfig;
import com.ftk.framework.redis.clients.collections.factory.RedisConnection;
import com.ftk.hjs.zuul.server.filter.LoginFilter;
import com.ftk.hjs.zuul.server.filter.PrintRequestLogFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.util.UrlPathHelper;

import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@SpringCloudApplication
@EnableFeignClients
@ComponentScan(basePackages = {"com.ftk.hjs","com.ftk.framework"})
public class ZuulServerLauncher implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(ZuulServerLauncher.class);

    @Autowired
    private RedisConfig redisConfig;

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

    @Override
    public void run(String... strings){
        //初始化redis
//        RedisConnection.init(redisConfig);
        logger.info("ZuulServerLauncher has run !!! {} ", strings);

    }

    @Bean
    public LoginFilter accessFilter(RouteLocator routeLocator) {
        return new LoginFilter(routeLocator,new UrlPathHelper());
    }

    @Bean
    public PrintRequestLogFilter printRequestLogFilter() {
        return new PrintRequestLogFilter();
    }

    private CorsConfiguration addcorsConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        List<String> list = new ArrayList<>();
        list.add("*");
        corsConfiguration.setAllowedOrigins(list);
        /*
        // 请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,只能是post方式等等)
        */
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(3600L);
        corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", addcorsConfig());
        return new CorsFilter(source);
    }

}

最后,以上代码均为部分代码,参照转载文章的说明和实例即可实现自己的网关功能。

原文地址:https://www.cnblogs.com/li-zhi-long/p/10871012.html

时间: 05-15

Spring Cloud Zuul Filter 和熔断的相关文章

笔记:Spring Cloud Zuul 快速入门

Spring Cloud Zuul 实现了路由规则与实例的维护问题,通过 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获取了所有其他微服务的实例信息,这样的设计非常巧妙的将服务治理体系中维护的实例信息利用起来,使得维护服务实例的工作交给了服务治理框架自动完成,而对路由规则的维护,默认会将通过以服务名作为 ContextPath 的方式来创建路由映射,也可以做一些特别的配置,对于签名校验.登录校验等在微服务架构中的冗余问题

spring cloud 学习(6) - zuul 微服务网关

微服务架构体系中,通常一个业务系统会有很多的微服务,比如:OrderService.ProductService.UserService...,为了让调用更简单,一般会在这些服务前端再封装一层,类似下面这样: 前面这一层俗称为“网关层”,其存在意义在于,将"1对N"问题 转换成了"1对1”问题,同时在请求到达真正的微服务之前,可以做一些预处理,比如:来源合法性检测,权限校验,反爬虫之类... 传统方式下,最土的办法,网关层可以人肉封装,类似以下示例代码: LoginResul

spring cloud 学习(4) - hystrix 服务熔断处理

hystrix 是一个专用于服务熔断处理的开源项目,当依赖的服务方出现故障不可用时,hystrix有一个所谓的断路器,一但打开,就会直接拦截掉对故障服务的调用,从而防止故障进一步扩大(类似中电路中的跳闸,保护家用电器). 使用步骤:(仍然在之前的示例代码上加以改造) 一.添加hystrix依赖 compile 'org.springframework.cloud:spring-cloud-starter-hystrix' 二.在需要熔断的方法上添加注解 package com.cnblogs.y

Spring cloud整体框架

研究了一段时间spring boot了准备向spirng cloud进发,公司架构和项目也全面拥抱了Spring Cloud.在使用了一段时间后发现Spring Cloud从技术架构上降低了对大型系统构建的要求,使我们以非常低的成本(技术或者硬件)搭建一套高效.分布式.容错的平台,但Spring Cloud也不是没有缺点,小型独立的项目不适合使用,另外对分布式事物的支持暂时也没有. Spring Cloud是什么鬼? Spring Cloud是一个基于Spring Boot实现的云应用开发工具,

Spring Cloud集成相关优质项目推荐

Spring Cloud Config 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储.Git以及Subversion. Spring Cloud Bus 事件.消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署. Eureka 云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移. Hystrix 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节

在阿里云容器服务上开发基于Docker的Spring Cloud微服务应用

本文为阿里云容器服务Spring Cloud应用开发系列文章的第一篇. 一.在阿里云容器服务上开发Spring Cloud微服务应用(本文) 二.部署Spring Cloud应用示例 三.服务发现 四.服务间通信与集成 五.服务智能路由 六.集中配置管理 七.高可用和容错 八.监控和日志 九.服务的部署和发布策略 微服务概述 单体应用通常指在一个程序中满足多个业务或技术领域的需求,不同的需求领域内化为模块.假定我们要开发一个Web应用,通常的MVC模式可以满足要求.针对不同领域有不少代码生成工具

Spring Cloud在国内中小型公司能用起来吗?

今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题 Spring Cloud在国内中小型公司能用起来吗?,吸引了我的注意.仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用 Spring Boot 和 Spring Cloud 近两年的程序员,看的我手痒痒不答不快呀. 好问题 好问题必须配认真的回答,仔细的看了题主的问题,发现这个问题非常具有代表性,可能是广大网友想使用 Spring Cloud 却又对 Spring Clou

Spring Cloud ZooKeeper集成Feign的坑2,服务调用了一次后第二次调用就变成了500,错误:Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.n

错误如下: 2017-09-19 15:05:24.659 INFO 9986 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.spring[email protected]56528192: startup date [Tue Sep 19 15:05:24 CST 2017]; root of context hierarchy 2017-09-19 15:05:24.858 INFO 9986 --

笔记:Spring Cloud Feign Hystrix 配置

在 Spring Cloud Feign 中,除了引入了用户客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix,默认情况下,Spring Cloud Feign 会为将所有 Feign客户端的方法都封装到 Hystrix 命令中进行服务保护,需要注意的是 Ribbon 的超时与 Hystrix 的超时是二个概念,需要让 Hystrix 的超时时间大于 Ribbon 的超时时间,否则 Hystrix 命令超时后,该命令直接熔断,重试机制就没

Spring cloud子项目

目前来说spring主要集中于spring boot(用于开发微服务)和spring cloud相关框架的开发,我们从几张图着手理解,然后再具体介绍: spring cloud子项目包括: Spring Cloud Config:配置管理开发工具包,可以让你把配置放到远程服务器,目前支持本地存储.Git以及Subversion. Spring Cloud Bus:事件.消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署. Sprin