在spring boot中集成微服务网关系统Spring Cloud Zuul

栏目: Java · 发布时间: 5年前

内容简介:spring cloud zuul由大名鼎鼎的netflix公司开发,已经超越spring cloud gateway微服务网关系统,成为了Spring Cloud全家桶里排名第一的微服务网关系统了网关作为所有应用系统的最前端,可以提供以下的价值完整代码已上传GITHUB,参考:

spring cloud zuul由大名鼎鼎的netflix公司开发,已经超越spring cloud gateway微服务网关系统,成为了Spring Cloud全家桶里排名第一的微服务网关系统了

网关作为所有应用系统的最前端,可以提供以下的价值

  1. 为后端微服务系统提供统一的入口
  2. 为后端微服务系统提供统一的授权机制
  3. 为后端微服务系统提供统一的认证机制
  4. 为后端微服务系统api提供统一签名校验机制
  5. 为流量入口添加日志记录
  6. qps统计
  7. 限流

完整代码已上传GITHUB,参考: github.com/neatlife/my…

创建网关项目

可以在https://start.spring.io/创建新的spring boot项目作为网关的骨架,比如

在spring boot中集成微服务网关系统Spring Cloud Zuul

注意需要把Zuul网关组件添加进来,也可以手动添加,pom依赖如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
复制代码

代理后端服务

编辑Spring Boot main方法所在的主类,添加EnableZuulProxy注解,核心代码如下

@SpringBootApplication
@EnableZuulProxy
public class MyzuulApplication {

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

}
复制代码

然后在application.properties配置文件中配置代理的后端服务,比如代理这个服务 jsonplaceholder.typicode.com/todos/

在spring boot中集成微服务网关系统Spring Cloud Zuul
这个服务可以直接访问效果如下: jsonplaceholder.typicode.com/todos/3
在spring boot中集成微服务网关系统Spring Cloud Zuul

使用zuul代理访问效果如下

在spring boot中集成微服务网关系统Spring Cloud Zuul

可以看到数据是完全一样的,网关只是起到了一个代理的作用

可以结合apollo配置中心实现自动刷新后端路由列表,参考: ZuulPropertiesRefresher.java

核心代码如下:

@ApolloConfigChangeListener(interestedKeyPrefixes = "zuul.")
 public void onChange(ConfigChangeEvent changeEvent) {
   refreshZuulProperties(changeEvent);
 }

 private void refreshZuulProperties(ConfigChangeEvent changeEvent) {
   logger.info("Refreshing zuul properties!");

   /**
    * rebind configuration beans, e.g. ZuulProperties
    * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent
    */
   this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));

   /**
    * refresh routes
    * @see org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration.ZuulRefreshListener#onApplicationEvent
    */
   this.applicationContext.publishEvent(new RoutesRefreshedEvent(routeLocator));

   logger.info("Zuul properties refreshed!");
 }
复制代码

过滤器系统

过滤器类型 过滤器执行时机 常见用法
PRE 在请求被路由之前调用 比如记录请求参数,认证
ROUTING 将请求路由到后端 比如请求dubbo搭建的后端服务
POST 在请求被路由之后调用 比如记录响应数据
ERROR 发生错误时执行该过滤器 比如给前端返回统一的报错json格式

web 系统

这个网关系统本身也是一个完整spring boot项目 可以编写控制器api,调用hibernate操作数据库等 比如在网关里编写登陆功能实现统一的授权功能

授权与认证

授权一般在网关里的控制器里实现授权的逻辑,授权一般可以指代登陆操作 认证一般在网关里的前置过滤器里实现,一般是检查是否登陆来决定是否允许访问网关后面的服务

网关登陆接口编写思路

流程代码如下

@PostMapping("/login")
public String login(@RequestParam(name = "username") String username,
                    @RequestParam(name = "password") String password
) {

    String token;

    // 到数据中检查用户名和密码是否合法
    if ("admin".equals(username) && "admin".equals(password)) {
        // 生成token,保存到 redis 中
        token = "21232f297a57a5a743894a0e4a801fc3";
    } else {
        throw new RuntimeException("用户名或密码错误");
    }

    // 返回token给前端,用来认证使用
    return token;
}

复制代码

认证流程

使用pre类型的过滤器 流程代码如下

/**
 * 认证
 */
@Slf4j
public class CertificationFilter extends ZuulFilter {

    @Override
    public Object run() {
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        // 到redis中检查token是否存在
        if  ("21232f297a57a5a743894a0e4a801fc3".equals(request.getParameter("token"))) {
            return null;
        }
        throw new ZuulRuntimeException(new ZuulException("未授权用户禁止访问", 403, "token校验失败"));
    }

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

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

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
}
复制代码

就是普通的登陆接口,不过是把这个逻辑放到了网关里面

将请求的参数记录到日志

这样做的目的是在异常时可以通过日志找到请求参数 编写一个过滤器,获取请求参数并记录到日志中,核心代码如下

@Override
public Object run() {
    HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
    Map<String, Object> parameters = getParametersFromRequest(request);
    log.info("网关有新的访问, url: {}, method: {}, parameters: {}", request.getRequestURL(), request.getMethod(), parameters);
    return null;
}

private Map<String, Object> getParametersFromRequest(HttpServletRequest request) {
    Enumeration<?> parameterNames = request.getParameterNames();
    Map<String, Object> parameters = new HashMap<>(16);
    while (parameterNames.hasMoreElements()) {
        String pName = (String) parameterNames.nextElement();
        Object pValue = request.getParameter(pName);
        parameters.put(pName, pValue);
    }
    return parameters;
}
复制代码

完整代码参考:

统一错误返回格式

使用error过滤器可以实现 核心代码如下

@Slf4j
public class CustomErrorFilter extends ZuulFilter {

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        // Remove error code to prevent further error handling in follow up filters
        ctx.remove("error.status_code");
        // block the SendErrorFilter from running
        ctx.set("sendErrorFilter.ran");

        ctx.setSendZuulResponse(false);
        ctx.getResponse().setContentType("application/json");
        ctx.getResponse().setCharacterEncoding("utf-8");
        ctx.getResponse().setHeader("Access-Control-Allow-Origin", "*");
        ctx.getResponse().setHeader("Access-Control-Allow-Methods", "*");
        ctx.getResponse().setHeader("Access-Control-Allow-Age", "86400");
        ctx.getResponse().setHeader("Access-Control-Allow-Headers", "*");
        ctx.setResponseStatusCode(200);
        StringWriter sw = new StringWriter();
        ctx.getThrowable().printStackTrace(new PrintWriter(sw, true));
        try {
            ZuulException zuulException = (ZuulException) ctx.getThrowable().getCause().getCause();

            ctx.getResponse().getWriter().write(
            "{\"message\": \"" +
                    zuulException.getMessage() +
                    "\", \"code\" :" +
                    zuulException.nStatusCode
                    + "}"
            );
        } catch (Exception e) {
            log.error("写入异常到客户端异常, estring: {}", e.toString());
        }
        return null;
    }

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

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

    @Override
    public String filterType() {
        return FilterConstants.ERROR_TYPE;
    }

}
复制代码

注意需要加上以下设置

// 阻止别的错误处理过滤器再次对错误进行处理
ctx.remove("error.status_code");
// 阻止别的发送错误的过滤器对错误响应结果再次进行处理
ctx.set("sendErrorFilter.ran");
复制代码

在浏览器中查看效果

在spring boot中集成微服务网关系统Spring Cloud Zuul

zuul设置与调优

常用的调优参数如下:

连接池最大连接,默认是200 zuul.host.maxTotalConnections=1000

每个route可用的最大连接数,默认值是20 zuul.host.maxPerRouteConnections=1000

Hystrix最大的并发请求 默认值是100 zuul.semaphore.maxSemaphores=1000

hystrix熔断设置与调优

网关属于整个系统最前端的应用,同时又属于基础服务,和redis, mysql等基础服务一样,一般是不允许宕机的,可用性应该得到保证,保证可用性常用的技术就是使用降级熔断技术hystrix了

Hystrix 超时时间配置 配置默认的hystrix超时时间

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
复制代码

配置特定方法的超时时间

hystrix.command.<hystrixcommandkey>.execution.isolation.thread.timeoutInMilliseconds=10000
复制代码

的format为FeignClassName#methodSignature,下面是示例配置

hystrix.command.PressureService#getBalance(int).execution.isolation.thread.timeoutInMilliseconds=10000
复制代码

一些注意的点

可以在前置过滤器中校验接口的签名,参考:简单API接口签名验证

网关里面登陆的用户需要和其它服务共享用户的登陆信息,可以把用户的信息存放到redis中进行共享

如果在开发过程中遇到问题,可加作者微信探讨

在spring boot中集成微服务网关系统Spring Cloud Zuul

以上所述就是小编给大家介绍的《在spring boot中集成微服务网关系统Spring Cloud Zuul》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

链接

链接

[美] 巴拉巴西 / 徐彬 / 湖南科技出版社 / 2007-04-01 / 28.00

从鸡尾酒会到恐怖分子的巢穴,从远古的细菌到国际组织——所有这一切各自都是一种网络,都是一个令人惊讶的科学革新的一部分。21世纪初,有科学家发现,网络具有深层的秩序,依据简单而强有力的规则运行。这一领域的知识帮助我们了解时尚、病毒等的传播机制,了解生态系统的稳健性,以及经济体系的脆弱性——甚至是民主的未来。 一位致力于研究“链接和节点”的科学家将首次带领我们领略网络革新的内幕。在本书中,作者生......一起来看看 《链接》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码