在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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

社交红利

社交红利

徐志斌 / 北京联合出版公司 / 2013-8 / 42

如今的互联网,社交网络已占据了主要的位置。如腾讯微博、微信、QQ空间、人人网、新浪微博、唱吧、美丽说、啪啪等等,都可以算是社交网络,将大部分活跃的人们聚集起来,通过文字、图片、语音等形式分享着身边的事。这些社交网络吸引着更多兴趣相投的陌生人成为朋友结成圈子,也衍生出的海量流量和机会,为业界和创业者提供着源源不绝的新机会。可以这样说,社交网络在将散落在人们中的需求汇聚起来,等待着企业来提供服务。因此......一起来看看 《社交红利》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

URL 编码/解码
URL 编码/解码

URL 编码/解码