转一篇很不错的关于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);
} }

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

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

  1. Spring Cloud Zuul网关 Filter、熔断、重试、高可用的使用方式。

    时间过的很快,写springcloud(十):服务网关zuul初级篇还在半年前,现在已经是2018年了,我们继续探讨Zuul更高级的使用方式. 上篇文章主要介绍了Zuul网关使用模式,以及自动转发机制 ...

  2. SpringCloud---API网关服务---Spring Cloud Zuul

    1.概述 1.1 微服务架构出现的问题   及  解决: 1.1.1 前言 每个微服务应用都提供对外的Restful API服务,它通过F5.Nginx等网络设备或工具软件实现对各个微服务的路由与负载 ...

  3. Spring Cloud Zuul API服务网关之请求路由

    目录 一.Zuul 介绍 二.构建Spring Cloud Zuul网关 构建网关 请求路由 请求过滤 三.路由详解 一.Zuul 介绍 ​ 通过前几篇文章的介绍,我们了解了Spring Cloud ...

  4. 服务网关Spring Cloud Zuul

    Spring Cloud Zuul 开发环境 idea 2019.1.2 jdk1.8.0_201 Spring Boot 2.1.9.RELEASE Spring Cloud Greenwich S ...

  5. Spring Cloud Zuul 添加 ZuulFilter

    紧接着上篇随笔Spring Cloud Zuul写,添加过滤器,进行权限验证 1.添加过滤器 package com.dzpykj.filter; import java.io.IOException ...

  6. Spring Cloud Zuul 限流详解(附源码)(转)

    在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud中如何实现限流. 在 Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法. ...

  7. Spring cloud Zuul网关异常处理

    Spring cloud Zuul网关异常处理 一 异常测试: 1> 创建一个pre类型的过滤器,并在该过滤器的run方法实现中抛出一个异常.比如下面的实现,在run方法中调用的doSometh ...

  8. 第七章 API网关服务:Spring Cloud Zuul

    API网关是一个更为智能的应用服务器, 它的定义类似于面向对象设计模式中的Facade模式, 它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤.它除了要实现 ...

  9. Spring Cloud Zuul实现IP访问控制

    接着上篇文章 https://www.cnblogs.com/mxmbk/p/9569438.html IP访问限制和黑白名单如何做,需要解决以下几个问题: 1.如何识别正常访问和异常访问?(一段时间 ...

随机推荐

  1. C# 常用方法——生成验证码

    其他常用方法详见:https://www.cnblogs.com/zhuanjiao/p/12060937.html 原文链接:https://www.cnblogs.com/morang/p/405 ...

  2. python-加密算法

    #!/usr/bin/python3 # coding:utf-8 # Auther:AlphaPanda # Description: 使用hashlib模块的md5和sha系列加密算法对字符串进行 ...

  3. Java中 DecimalFormat 用法详解

    我们经常要将数字进行格式化,比如取2位小数,这是最常见的.Java 提供DecimalFormat类,帮你用最快的速度将数字格式化为你需要的样子.下面是一个例子: import java.text.D ...

  4. mybatis invalid bound statement (not found)

    Spring boot + Mybatis : Invalid bound statement (not found) 如果只在启动类上配置@MapperScan注解,默认只扫描和mapper接口同名 ...

  5. JavaWeb-SpringBoot_一个类实现腾讯云SDK发送短信

    腾讯云应用列表 传送门 使用Gradle编译项目 传送门 SDK&API:qcloudsms_java 传送门 package com.Gary.sms.controller; import ...

  6. JavaWeb_(session和application)用户登录注册模板_进阶版

    用户登录注册模板_基础版 传送门 用户登录注册模板进阶版 添加了获得获得当前登录用户信息及存储登录人数 用户登录后,在首页.注册页.登录页显示登录用户信息和存储登录人数信息 目录结构 <%@pa ...

  7. Unity3D_(游戏)甜品消消乐02_游戏核心算法

    甜品消消乐01_游戏基础界面 传送门 甜品消消乐02_游戏核心算法 传送门 甜品消消乐03_游戏UI设计    传送门 GameManager脚本上修改Fill Time可以改变消消乐移动速度 实现过 ...

  8. eclipse安装错误的解决办法

    eclipse安装错误的解决办法 Eclipse 是一个集成开发环境,如Java,C,C++,PHP等安装Eclipse首先得先安装java的Jdk或者Jre 首先访问https://www.ecli ...

  9. elk5.0 版本遇到的安装问题

    问题1:max_map_count不够大 max virtual memory areas vm.max_map_count [65536] likely too low, increase to a ...

  10. 【转】JS正则验证邮手机、箱等格式

    function test() { var temp = document.getElementById("text1"); //对电子邮件的验证 var myreg = /^([ ...