Spring Cloud Zuul网关服务

本文深入探讨了Spring Cloud Zuul作为微服务网关的角色,解析其在路由、鉴权、监控、缓存及限流等方面的功能,通过实例展示了Zuul的配置与请求过滤,为微服务架构下的API统一管理提供了实践指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近也是有了闲鱼的时间,可以出来写写博客,由于买的房子最近交房再加上公司的一些乱七八糟的事情,导致最近心情比较荡漾,所以如何能把自己的时间挤满,如何能转变思维、想法,只有学习哇。

本人写文章有个特点,喜欢一边看优秀的博客,一边写,这样既可以把别人优秀的点拿过来作为参考,也可以加入自己的想法,自己的理解,这样岂不是更加好,完美?........

最近也是好事坏事一箩筐,有个好兄弟拉着我跟他一起去创业公司,然后工资不涨,可能会降,当时说的时候,还有点兴趣,后期跟着几个朋友聊了聊现在的情况,他们都果断的不想让我过去,理由很简单,钱不给到位,怎么可能放弃现有安稳的工作,跑过去呢?

纠结了一段时间后,果断拒绝了,几年后会不会后悔呢?几年后就见分晓了.....

 

大家可以看看文章,有些段落是借鉴别人优秀的人的语言。可以提供大家交流学习,一起进步~~~

 

首先看看zuul是个什么鬼?

spring cloud zuul简述

1、spring cloud zuul是netflix提供的一个组件,提供路由请求、鉴权、监控、缓存、限流等功能。
2、微服务场景下,每一个微服务对外暴露了一组细粒度的服务。客户端的请求会涉及到一串的服务调用,如果将这些微服务都暴露给客户端,那么会增加客户端代码的复杂度。
3、参考GOF设计模式中的Facade模式,将细粒度的服务组合起来提供一个粗粒度的服务,所有请求都导入一个统一的入口,那么整个服务只需要暴露一个api,对外屏蔽了服务端的实现细节,也减少了客户端与服务器的网络调用次数。这就是api gateway。

我们为什么要用网关呢?网关到底解决了什么问题?

不同的微服务一般会有不同的网络地址,而客户端可能需要调用多个服务接口才能完成一个业务需求,若让客户端直接与各个微服务通信,会有以下问题:

(1)客户端会多次请求不同微服务,增加了客户端复杂性

(2)存在跨域请求,处理相对复杂

(3)认证复杂,每个服务都需要独立认证

(4)难以重构,多个服务可能将会合并成一个或拆分成多个

微服务网关介于服务端与客户端的中间层,所有外部服务请求都会先经过微服务网关客户只能跟微服务网关进行交互,无需调用特定微服务接口,使得开发得到简化,同时也利于负载均衡。

API网关主要为了服务本身对外的调用该怎么调用来解决的,还有解决权限校验的问题,你可以在这里整合调用一系列过滤器的,例如整合shiro,springsecurity之类的东西。

引入服务网关后的微服务架构如上,总体包含三部分:服务网关、open-service和service。

服务网关技术选型:

(1)总体流程

       服务网关、open-service和service启动时注册到注册中心上去;用户请求时直接请求网关,网关做智能路由转发(包括服务发现,负载均衡)到open-service,这其中包含权限校验、监控、限流等操作,open-service聚合内部service响应,返回给网关,网关再返回给用户

(2)引入网关的注意点

   增加了网关,多了一层转发(原本用户请求直接访问open-service即可),性能会下降一些(但是下降不大,通常,网关机器性能会很好,而且网关与open-service的访问通常是内网访问,速度很快);

(3)服务网关基本功能

智能路由:接收外部一切请求,并转发到后端的对外服务open-service上去;

     注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;

       当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。

权限校验:可在微服务网关上进行认证,然后在将请求转发给微服务,无须每个微服务都进行认证,不校验服务内部的请求。服务内部的请求有必要校验吗?

 API监控:只监控经过网关的请求,以及网关本身的一些性能指标(例如,gc等);

 限流:与监控配合,进行限流操作;

 API日志统一收集:类似于一个aspect切面,记录接口的进入和出去时的相关日志。

 

以上就是网关涉及到的方方面面,大家可以参阅阅读~~~~

 

接下来我们一起看看怎么玩zuul?

第一步,新建一个Zuul项目,引入依赖,代码如下:

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-zuul</artifactId>
		</dependency>
	</dependencies>

接着在启动类上打上@EnableZuulProxy注解,代码如下:

package com.inspire.efficacy.zuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
public class ZuulApp {
	public static void main(String[] args) {
		SpringApplication.run(ZuulApp.class, args);
	}
}

配置文件,如下:

server:
    port: 1113 #服务端口号
spring:
    application:
      name: efficiency-zuul-service
    zipkin:
      base-url: http://10.2.8.42:2222/
    sleuth:
      sampler:
        percentage: 0.1
      web: #sleuth的追踪跳过的机制,默认sleuth已经禁用了如下URI,下面的URI访问系统的时候是不会被追踪的。
        skip-pattern: "/api-docs.*|/autoconfig|/configprops|/dump|/health|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream|/api"   
zuul:
  routes:
    efficiency-portal:
       service-id: efficiency-portal 
       path: /
security:
     user:
        name: admin
        password: admin
eureka:
    client:
       register-with-eureka: true
       fetchRegistry: true
       service-url.defaultZone: http://${security.user.name}:${security.user.password}@10.2.8.42:1111/eureka/   #设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka ;多个地址可使用 , 分隔。
    instance:
       appname: zuul-service #服务实例名称
management:
    security:
       enabled: false #高可用模式下配置了service-id就不用指定uri了

 可以看到zuul进行了请求分发了。

zuul还能进行请求过滤,代码如下:

package com.inspire.efficacy.zuul.filter;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.inspire.efficacy.tool.CookieUtils;
import com.inspire.efficacy.treeToJson.JsonUtil;
import com.inspire.efficacy.zuul.util.RedisUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

public class AccessFilter extends ZuulFilter {
	
	private static Logger logger = LoggerFactory.getLogger(AccessFilter.class);

	@Autowired
	RedisUtil redisUtil;

	/**
	 * 控制过滤器生效不生效,可以在里面写一串逻辑来控制 true:生效 false:不生效
	 */
	@Override
	public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		String reUrl = request.getRequestURL().toString();
		logger.info("params reUrl:{}" + reUrl);
		if (reUrl.contains("login") || reUrl.contains("mobileInterface") || reUrl.contains("forgetpwd")
				|| reUrl.contains("getidentifycode") || reUrl.contains("changepwd") || reUrl.contains("getSessionId")
				|| reUrl.contains("Signout")) {
			return false;
		} else if (reUrl.contains("alarm")) {
			// 告警服务中/authentication /sendMessage /sendWarningMsg不走网关验证session有效时间
			if (reUrl.contains("authentication") || reUrl.contains("sendMessage") || reUrl.contains("sendWarningMsg")) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 执行过滤逻辑
	 */
	@Override
	public Object run() {
		String token = "";
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		token = CookieUtils.getCookieValue(request, "SessionId");
		logger.info("获取到的cookie值是:" + token);
		Map<String, Object> resultMap = new HashMap<String, Object>();
		if (StringUtils.isBlank(token)) {
			ctx.setSendZuulResponse(false);
			ctx.setResponseStatusCode(200);
			resultMap.put("code", 401);
			resultMap.put("mesg", "没有登录,请先登录!");
			ctx.setResponseBody(JsonUtil.map2json(resultMap));
			ctx.getResponse().setContentType("application/json;charset=UTF-8");
			return null;
		}
		String json = (String) redisUtil.get(token);
		if (StringUtils.isBlank(json)) {
			ctx.setSendZuulResponse(false);
			resultMap.put("code", 401);
			resultMap.put("mesg", "Session已经过期");
			ctx.setResponseBody(JsonUtil.map2json(resultMap));
			ctx.getResponse().setContentType("application/json;charset=UTF-8");
			return null;
		}
		redisUtil.expire(token, 30 * 60);
		return null;
	}

	/**
	 * 四种类型:pre,routing,error,post pre:主要用在路由映射的阶段是寻找路由映射表的
	 * routing:具体的路由转发过滤器是在routing路由器,具体的请求转发的时候会调用 error:一旦前面的过滤器出错了,会调用error过滤器。
	 * post:当routing,error运行完后才会调用该过滤器,是在最后阶段的
	 */
	@Override
	public String filterType() {
		return "pre";
	}

	/**
	 * 自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行
	 */
	@Override
	public int filterOrder() {
		return 0;
	}

}

 

filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,

具体如下:

  pre:可以在请求被路由之前调用,用在路由映射的阶段是寻找路由映射表的

  route:在路由请求时候被调用,具体的路由转发过滤器是在routing路由器具体的请求转发的时候会调用

  error:处理请求时发生错误时被调用

  post:当routing,error运行完后才会调用该过滤器,是在最后阶段的

================================================================================

现在可以测试一下:放到测试环境,完美解决了问题!!!

 

在这里记录一下问题:

              zuul转发用serviceId  总是报错,报连接超时,无论怎么改配置超时时间都不好使~

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值