0
点赞
收藏
分享

微信扫一扫

Llama2 的内部工作机理

穿裙子的程序员 2024-07-24 阅读 16
springjava

第三章 网关zuul搭建


前言:

1、主要功能

        zuul主要提供动态路由(内置ribbon实现)和过滤(可以做统一鉴权过滤器、灰度发布过滤器、黑白名单IP过滤器、服务限流过滤器(可以配合Sentinel实现))功能;

2、和Spring Cloud GateWay的区别

        属于两个不同开源组织提供的网关解决方案。spring cloud GateWay使用非阻塞API,内置限流过滤器,支持长连接(比如 websockets),在高并发和后端服务响应慢的场景下比Zuul1的表现要好;zuul无内置限流过滤器,但是可以与Sentinel(是阿里开源的一款高性能的限流框架)集成使用。


一、创建zuul服务module

参考第二章eureka的创建

二、配置文件

1、pom文件配置

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.hq</groupId>
<artifactId>taxi-online</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>taxi-online-zuul</artifactId>
<name>taxi-online-zuul</name>
<description>personal test for zuul</description>
<url>org.apache</url>

<packaging>jar</packaging>

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

2、application.yml文件配置

#服务端口
server:
port: 9100
#服务名
spring:
application:
name: taxi-online-zuul
#服务注册
eureka:
client:
service-url:
defaultZone: http://localhost:9001/eureka/,http://localhost:9002/eureka/
enabled: true # 是否启用Eureka client
instance:
hostname: localhost
instance-id: taxi-online-zuul # 实例id

3、启动类

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

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

三、zuul功能

1、动态路由

2、统一鉴权

3、过滤器

4、灰度发布

5、限流

四、底层源码

1、启动原理

类似注册中心:

1)boot启动类

boot启动类上添加@EnableZuulProxy注解;

2)EnableZuulProxy注解

EnableZuulProxy注解中导入ZuulProxyMarkerConfiguration配置类

@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {

}
3)ZuulProxyMarkerConfiguration配置类

ZuulProxyMarkerConfiguration配置类往容器添加了一个空的Marker对象,这个对象是作为容器是否添加ZuulProxyAutoConfiguration类的条件,因为ZuulProxyAutoConfiguration类上面加了@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)的注解

@Configuration(proxyBeanMethods = false)
public class ZuulProxyMarkerConfiguration {

@Bean
public Marker zuulProxyMarkerBean() {
return new Marker();
}

class Marker {

}

}
4)spring.factories文件

zuul包下面的spring.factories文件指定了两个引入类,只要导包就会被引进去(由SpringFactoriesLoader 进行读取)--见下方截图。如果没有这个文件,boot的自动扫描不会扫描这些外部包的类。这两个引入类被注入容器的条件分别是:

@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)

@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)

,分别可以通过在启动类上添加下面注解来引入相应的Marker类:

@EnableZuulProxy

@EnableZuulServer。


@EnableZuulServer、@EnableZuulProxy两个注解的区别:
@EnableZuulServer - 普通Zuul Server,只支持基本的route与filter功能.
@EnableZuulProxy - 普通Zuul Server+服务发现与熔断等功能的增强版,具有反向代理功能。
@EnableZuulProxy简单理解为@EnableZuulServer的增强版,当Zuul与Eureka、Ribbon等组件配合使用时,我们使用@EnableZuulProxy。所以我们做SpringCloud微服务系统一般都是使用@EnableZuulProxy。

5)ZuulServerAutoConfiguration配置类

ZuulServerAutoConfiguration配置类(源码见下方)主要做的事情有:

1、往容器注入了ZuulServlet,该类是zuul的核心类。

2、往容器注入了 一些前置过滤器和后置过滤器。

3、注入ZuulFilterInitializer类并初始化,初始化逻辑中将所有过滤器交给FilterRegistry类进行管理。FilterRegistry类源码如下:

/**
* @author mhawthorne
*/

public class FilterRegistry {

private static final FilterRegistry INSTANCE = new FilterRegistry();

public static final FilterRegistry instance() {
return INSTANCE;
}

private final ConcurrentHashMap<String, ZuulFilter> filters = new ConcurrentHashMap<String, ZuulFilter>();

private FilterRegistry() {
}

public ZuulFilter remove(String key) {
return this.filters.remove(key);
}

public ZuulFilter get(String key) {
return this.filters.get(key);
}

public void put(String key, ZuulFilter filter) {
this.filters.putIfAbsent(key, filter);
}

public int size() {
return this.filters.size();
}

public Collection<ZuulFilter> getAllFilters() {
return this.filters.values();
}

}

ZuulServerAutoConfiguration源码如下(有删减):

/**
* @author Spencer Gibb
* @author Dave Syer
* @author Biju Kunjummen
*/

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass({ ZuulServlet.class, ZuulServletFilter.class })
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
// Make sure to get the ServerProperties from the same place as a normal web app would
// FIXME @Import(ServerPropertiesAutoConfiguration.class)
public class ZuulServerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false",
matchIfMissing = true)

public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(
new ZuulServlet(), this.zuulProperties.getServletPattern());
// The whole point of exposing this servlet is to provide a route that doesn't
// buffer requests.
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}

// pre filters

@Bean
public ServletDetectionFilter servletDetectionFilter() {
return new ServletDetectionFilter();
}

@Bean
@ConditionalOnMissingBean
public FormBodyWrapperFilter formBodyWrapperFilter() {
return new FormBodyWrapperFilter();
}

@Bean
@ConditionalOnMissingBean
public DebugFilter debugFilter() {
return new DebugFilter();
}

@Bean
@ConditionalOnMissingBean
public Servlet30WrapperFilter servlet30WrapperFilter() {
return new Servlet30WrapperFilter();
}

// post filters

@Bean
public SendResponseFilter sendResponseFilter(ZuulProperties properties) {
return new SendResponseFilter(zuulProperties);
}

@Bean
public SendErrorFilter sendErrorFilter() {
return new SendErrorFilter();
}

@Bean
public SendForwardFilter sendForwardFilter() {
return new SendForwardFilter();
}

//
@Configuration(proxyBeanMethods = false)
protected static class ZuulFilterConfiguration {

@Autowired
private Map<String, ZuulFilter> filters;

@Bean
public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory,
TracerFactory tracerFactory)
{
FilterLoader filterLoader = FilterLoader.getInstance();
FilterRegistry filterRegistry = FilterRegistry.instance();
return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory,
filterLoader, filterRegistry);
}

}
}
6)ZuulProxyAutoConfiguration配置类

继承了ZuulServerAutoConfiguration类,是ZuulServerAutoConfiguration的增强版,在ZuulServerAutoConfiguration的基础上增加eureka、ribbon、hystrix等功能。源码如下:

/**
* @author Spencer Gibb
* @author Dave Syer
* @author Biju Kunjummen
*/

@Configuration(proxyBeanMethods = false)
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
HttpClientConfiguration.class })

@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {

// pre filters
@Bean
@ConditionalOnMissingBean(PreDecorationFilter.class)
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
ProxyRequestHelper proxyRequestHelper)
{
return new PreDecorationFilter(routeLocator,
this.server.getServlet().getContextPath(), this.zuulProperties,
proxyRequestHelper);
}

// route filters
@Bean
@ConditionalOnMissingBean(RibbonRoutingFilter.class)
public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
RibbonCommandFactory<?> ribbonCommandFactory)
{
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
this.requestCustomizers);
return filter;
}

@Bean
@ConditionalOnMissingBean({ SimpleHostRoutingFilter.class,
CloseableHttpClient.class })

public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory)
{
return new SimpleHostRoutingFilter(helper, zuulProperties,
connectionManagerFactory, httpClientFactory);
}

@Bean
@ConditionalOnMissingBean({ SimpleHostRoutingFilter.class })
public SimpleHostRoutingFilter simpleHostRoutingFilter2(ProxyRequestHelper helper,
ZuulProperties zuulProperties, CloseableHttpClient httpClient)
{
return new SimpleHostRoutingFilter(helper, zuulProperties, httpClient);
}

@Bean
@ConditionalOnMissingBean(ServiceRouteMapper.class)
public ServiceRouteMapper serviceRouteMapper() {
return new SimpleServiceRouteMapper();
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.boot.actuate.health.Health")
protected static class NoActuatorConfiguration {

@Bean
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
ProxyRequestHelper helper = new ProxyRequestHelper(zuulProperties);
return helper;
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Health.class)
protected static class EndpointConfiguration {

@Autowired(required = false)
private HttpTraceRepository traces;

@Bean
@ConditionalOnAvailableEndpoint
public RoutesEndpoint routesEndpoint(RouteLocator routeLocator) {
return new RoutesEndpoint(routeLocator);
}

@ConditionalOnAvailableEndpoint
@Bean
public FiltersEndpoint filtersEndpoint() {
FilterRegistry filterRegistry = FilterRegistry.instance();
return new FiltersEndpoint(filterRegistry);
}

@Bean
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
TraceProxyRequestHelper helper = new TraceProxyRequestHelper(zuulProperties);
if (this.traces != null) {
helper.setTraces(this.traces);
}
return helper;
}

}


}

2、核心类ZuulServlet

1)作用

调度不同阶段的filters,处理异常。所有的Request都要经过ZuulServlet的处理。三个核心的方法preRoute(),route(), postRoute(),zuul对request处理逻辑都在这三个方法里。源码如下:

public class ZuulServlet extends HttpServlet {

private static final long serialVersionUID = -3374242278843351500L;
private ZuulRunner zuulRunner;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);

String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;

zuulRunner = new ZuulRunner(bufferReqs);
}

@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

// Marks this request as having passed through the "Zuul engine", as opposed to servlets
// explicitly bound in web.xml, for which requests will not have the same data attached
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();

try {
preRoute();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
}

} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}

/**
* executes "post" ZuulFilters
*
* @throws ZuulException
*/

void postRoute() throws ZuulException {
zuulRunner.postRoute();
}

/**
* executes "route" filters
*
* @throws ZuulException
*/

void route() throws ZuulException {
zuulRunner.route();
}

/**
* executes "pre" filters
*
* @throws ZuulException
*/

void preRoute() throws ZuulException {
zuulRunner.preRoute();
}

/**
* initializes request
*
* @param servletRequest
* @param servletResponse
*/

void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}

/**
* sets error context info and executes "error" filters
*
* @param e
*/

void error(ZuulException e) {
RequestContext.getCurrentContext().setThrowable(e);
zuulRunner.error();
}
}
2)调用链路:

以preRoute()为例:

1、ZuulServlet——》ZuulRunner,源码见四-2-1);

2、ZuulRunner——》FilterProcessor,源码如下:

public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}

3、FilterProcessor调自己的runFilters方法,涉及调用其他类:

FilterProcessor——》FilterLoader,源码见下方4

实际执行过滤器逻辑是在调用自己processZuulFilter方法中的下方的代码逻辑。

ZuulFilterResult result = filter.runFilter();(runFilter里面调用了子类的run方法)

public void preRoute() throws ZuulException {
try {
runFilters("pre");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
}
}

/**
* runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
*
* @param sType the filterType.
* @return
* @throws Throwable throws up an arbitrary exception
*/

public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}

public Object processZuulFilter(ZuulFilter filter) throws ZuulException {

RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
final String metricPrefix = "zuul.filter-";
long execTime = 0;
String filterName = "";
try {
long ltime = System.currentTimeMillis();
filterName = filter.getClass().getSimpleName();

RequestContext copy = null;
Object o = null;
Throwable t = null;

if (bDebug) {
Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
copy = ctx.copy();
}

ZuulFilterResult result = filter.runFilter();
ExecutionStatus s = result.getStatus();
execTime = System.currentTimeMillis() - ltime;

switch (s) {
case FAILED:
t = result.getException();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
break;
case SUCCESS:
o = result.getResult();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
if (bDebug) {
Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
Debug.compareContextState(filterName, copy);
}
break;
default:
break;
}

if (t != null) throw t;

usageNotifier.notify(filter, s);
return o;

} catch (Throwable e) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage());
}
usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (e instanceof ZuulException) {
throw (ZuulException) e;
} else {
ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}

4、FilterLoader——》filterRegistry,通过filterRegistry拿到所有的过滤器并筛选返回符合要求类型的集合并按优先级排序,放入缓存。

 /**
* Returns a list of filters by the filterType specified
*
* @param filterType
* @return a List<ZuulFilter>
*/

public List<ZuulFilter> getFiltersByType(String filterType) {

List<ZuulFilter> list = hashFiltersByType.get(filterType);
if (list != null) return list;

list = new ArrayList<ZuulFilter>();

Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
ZuulFilter filter = iterator.next();
if (filter.filterType().equals(filterType)) {
list.add(filter);
}
}
Collections.sort(list); // sort by priority

hashFiltersByType.putIfAbsent(filterType, list);
return list;
}

5、FilterRegistry调用自己的getAllFilters方法返回所有的过滤器

private final ConcurrentHashMap<String, ZuulFilter> filters = new ConcurrentHashMap<String, ZuulFilter>();
public Collection<ZuulFilter> getAllFilters() {
return this.filters.values();
}

3、顶级抽象父类ZuulFilter

所有的过滤器都应继承该抽象类,重写shouldFilter、filterType、filterOrder、run方法。

shouldFilter:该过滤器是否生效  返回true:有效;false:无效

filterType:指定过滤器类型,FilterConstants包含下面图中几种类型:filterOrder:在过滤器链中的顺序,返回值越小越靠前;

run:过滤器的逻辑。

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {

abstract boolean shouldFilter();
abstract Object run() throws ZuulException;
abstract public String filterType();

/**
* filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not
* important for a filter. filterOrders do not need to be sequential.
*
* @return the int order of a filter
*/

abstract public int filterOrder();

public int compareTo(ZuulFilter filter) {
return Integer.compare(this.filterOrder(), filter.filterOrder());
}
}

4、过滤器热加载

参考下方博客:

zuul 1.x 源码解析 (结合本人过去2年工作经历整理,超详细)_zuulproxyautoconfiguration.predecorationfilter-CSDN博客文章浏览阅读428次,点赞6次,收藏4次。zuul 1.x 源码解析,全程断点调试,超详细(结合本人过去2年工作经历,花了1周时间整理并记录)_zuulproxyautoconfiguration.predecorationfilterhttps://blog.csdn.net/qq_22270363/article/details/132052278

举报

相关推荐

0 条评论