SpringCloud網(wǎng)關(guān)Zuul底層實(shí)現(xiàn)原理詳解
Zuul現(xiàn)在應(yīng)用的已經(jīng)非常少了,至少都在使用Spring Cloud Gateway。Zuul實(shí)現(xiàn)是基于Servlet這種阻塞是IO這種機(jī)制是通過(guò)創(chuàng)建更多的線程來(lái)彌補(bǔ)其不足;而Cloud Gateway則是基于反應(yīng)式非阻塞式的,使用少量的線程來(lái)做更多的事。以下是官方對(duì)阻塞與非阻塞的對(duì)比圖:
圖片
1 收集路由
public class ZuulServerAutoConfiguration {
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
// 主
@Bean
@Primary
public CompositeRouteLocator primaryRouteLocator(
Collection<RouteLocator> routeLocators) {
return new CompositeRouteLocator(routeLocators);
}
@Bean
@ConditionalOnMissingBean(SimpleRouteLocator.class)
public SimpleRouteLocator simpleRouteLocator() {
return new SimpleRouteLocator(this.server.getServlet().getContextPath(), this.zuulProperties);
}
}
SimpleRouteLocator
public class SimpleRouteLocator implements RouteLocator, Ordered {
private ZuulProperties properties;
private String dispatcherServletPath = "/";
private String zuulServletPath;
private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();
// servletPath = server.servlet.contextPath 配置屬性值
public SimpleRouteLocator(String servletPath, ZuulProperties properties) {
this.properties = properties;
if (StringUtils.hasText(servletPath)) {
this.dispatcherServletPath = servletPath;
}
// 默認(rèn)為:/zuul
this.zuulServletPath = properties.getServletPath();
}
// 該方法會(huì)在CompositeRouteLocator中調(diào)用,而本例的作用就是將ZuulRoute轉(zhuǎn)換為Route對(duì)象
@Override
public List<Route> getRoutes() {
List<Route> values = new ArrayList<>();
for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) {
ZuulRoute route = entry.getValue();
String path = route.getPath();
try {
// 將配置文件中配置的ZuulRoute路由轉(zhuǎn)換為Route對(duì)象
values.add(getRoute(route, path));
}
}
return values;
}
protected Map<String, ZuulRoute> getRoutesMap() {
if (this.routes.get() == null) {
this.routes.set(locateRoutes());
}
return this.routes.get();
}
protected Map<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
// properties.getRouets獲取配置文件中配置的所有路由
for (ZuulRoute route : this.properties.getRoutes().values()) {
routesMap.put(route.getPath(), route);
}
return routesMap;
}
protected Route getRoute(ZuulRoute route, String path) {
String targetPath = path;
// 獲取配置文件的zuul.prefix屬性值
String prefix = this.properties.getPrefix();
if (prefix.endsWith("/")) {
// 刪除 '/' 結(jié)尾的字符
prefix = prefix.substring(0, prefix.length() - 1);
}
// 判斷配置的路由path屬性值的開(kāi)始字符串是不是以 '/prefix/'開(kāi)頭,如:/api/
// 如果配置的path:/api/api-1/**則匹配第一個(gè)條件,再判斷zuul.strip-prefix屬性值是否為true
if (path.startsWith(prefix + "/") && this.properties.isStripPrefix()) {
// 成立則會(huì)截取字符串刪除'/prefix/'開(kāi)頭的字符,則最終targetPath = /api-1/**
targetPath = path.substring(prefix.length());
}
// 如果配置為true
if (route.isStripPrefix()) {
// 知道path中第一個(gè)'*'
int index = route.getPath().indexOf("*") - 1;
// 如果存在
if (index > 0) {
// 截取第一個(gè)'*'之前的字符串
// 如上:最終routePrefix = /api-1
String routePrefix = route.getPath().substring(0, index);
// 結(jié)合上面:targetPath = /**
targetPath = targetPath.replaceFirst(routePrefix, "");
// 結(jié)合上面:prefix = /api + /api-1 = /api/api-1
prefix = prefix + routePrefix;
}
}
// 上面的路徑處理就是將配置的zuul.prefix + zuul.routes.xx.path
Boolean retryable = this.properties.getRetryable();
if (route.getRetryable() != null) {
retryable = route.getRetryable();
}
// 構(gòu)建Route對(duì)象
return new Route(route.getId(), targetPath, route.getLocation(), prefix,
retryable,
route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null,
route.isStripPrefix());
}
}
CompositeRouteLocator
public class CompositeRouteLocator implements RefreshableRouteLocator {
private final Collection<? extends RouteLocator> routeLocators;
private ArrayList<RouteLocator> rl;
// 收集所有RouteLocator,如上面的SimpleRouteLocator
public CompositeRouteLocator(Collection<? extends RouteLocator> routeLocators) {
rl = new ArrayList<>(routeLocators);
AnnotationAwareOrderComparator.sort(rl);
this.routeLocators = rl;
}
public List<Route> getRoutes() {
List<Route> route = new ArrayList<>();
for (RouteLocator locator : routeLocators) {
route.addAll(locator.getRoutes());
}
return route;
}
}
這里的RouteLocator有何用,下面會(huì)見(jiàn)到。
2 注冊(cè)HandlerMapping
Zuul會(huì)注冊(cè)自身特定的ZuulHandlerMapping對(duì)象。
public class ZuulServerAutoConfiguration {
// Controller接口;一個(gè)controller接口可以通過(guò)實(shí)現(xiàn)Controller或AbstractController接口來(lái)實(shí)現(xiàn)
@Bean
public ZuulController zuulController() {
return new ZuulController();
}
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,
ZuulController zuulController) {
// HandlerMapping中注入了RouteLocator,也就是上面的CompositeRouteLocator對(duì)象
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController);
mapping.setErrorController(this.errorController);
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}
}
zuulController
public class ZuulController extends ServletWrappingController {
public ZuulController() {
// 調(diào)用父類(lèi)的方法設(shè)置父類(lèi)的成員變量servletClass 該Class是個(gè)Servlet
// 在下面會(huì)看到如何使用ZuulServlet對(duì)象
setServletClass(ZuulServlet.class);
setServletName("zuul");
// 支持任意的請(qǐng)求方法
setSupportedMethods((String[]) null); // Allow all
}
// 該方法是Controller接口的核心方法
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
try {
return super.handleRequestInternal(request, response);
}
}
}
ZuulHandlerMapping
HandlerMapping作用就是從通過(guò)請(qǐng)求的URI查找到合適的處理句柄,找到就之間返回,接下來(lái)就是由HandlerAdapter進(jìn)行實(shí)際的處理了
public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {
private volatile boolean dirty = true;
private final ZuulController zuul;
public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) {
this.routeLocator = routeLocator;
this.zuul = zuul;
// 優(yōu)先級(jí)就很高了,值越小優(yōu)先級(jí)越高
setOrder(-200);
}
protected Object lookupHandler(String urlPath, HttpServletRequest request)
throws Exception {
if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
return null;
}
if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) {
return null;
}
RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.containsKey("forward.to")) {
return null;
}
if (this.dirty) {
synchronized (this) {
if (this.dirty) {
// 首次時(shí)dirty為true,這時(shí)候就會(huì)注冊(cè)處理句柄了
registerHandlers();
this.dirty = false;
}
}
}
// 調(diào)用父類(lèi)的方法查找合適的請(qǐng)求處理器,從父類(lèi)handlerMap屬性(Map)集合中查找
return super.lookupHandler(urlPath, request);
}
private void registerHandlers() {
// 這里就調(diào)用了上面CompositeRouteLocator#getRoutes,這樣就得到了所有的路由信息
Collection<Route> routes = this.routeLocator.getRoutes();
if (routes.isEmpty()) {
this.logger.warn("No routes found from RouteLocator");
} else {
for (Route route : routes) {
// 注意:所有的請(qǐng)求路徑都對(duì)應(yīng)同一個(gè)Handler也就是上面的ZuulController對(duì)象
// 到這就知道了所有的路由請(qǐng)求都會(huì)被ZuulController處理
// 調(diào)用父類(lèi)方法,將路由信息添加到父類(lèi)handlerMap屬性中(Map)集合
registerHandler(route.getFullPath(), this.zuul);
}
}
}
}
3 路由調(diào)用
一個(gè)請(qǐng)求過(guò)來(lái)時(shí),通過(guò)上面的ZuulHandlerMapping對(duì)象找到了合適的處理句柄也就是ZuulController,接下來(lái)就是由合適的HandlerAdapter進(jìn)行真正的調(diào)用了。
現(xiàn)在確定了handler對(duì)象是ZuulController,接下來(lái)就是查看那個(gè)HandlerAdatper能夠進(jìn)行處理了。
ZuulController實(shí)現(xiàn)了Controller接口,能夠處理該類(lèi)型的是:SimpleControllerHandlerAdaper。
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 調(diào)用ZuulController#handleRequest方法
return ((Controller) handler).handleRequest(request, response);
}
}
回到ZuulController
public class ZuulController extends ServletWrappingController {
public ZuulController() {
// 記住這里我們給父類(lèi)屬性servletClass 設(shè)置為ZuulServlet
setServletClass(ZuulServlet.class);
setServletName("zuul");
// 支持任意方法
setSupportedMethods((String[]) null); // Allow all
}
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
try {
// 調(diào)用父類(lèi)方法
return super.handleRequestInternal(request, response);
} finally {
// @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter
RequestContext.getCurrentContext().unset();
}
}
}
public class ServletWrappingController extends AbstractController
implements BeanNameAware, InitializingBean, DisposableBean {
private Class<? extends Servlet> servletClass;
public void setServletClass(Class<? extends Servlet> servletClass) {
this.servletClass = servletClass;
}
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 調(diào)用ZuulServlet#service方法(具體的處理還是通過(guò)ZuulServlet處理)
this.servletInstance.service(request, response);
return null;
}
// 該Bean在初始化解決會(huì)通過(guò)serveltClass實(shí)例化ZuulServlet對(duì)象。
public void afterPropertiesSet() throws Exception {
if (this.servletClass == null) {
throw new IllegalArgumentException("'servletClass' is required");
}
if (this.servletName == null) {
this.servletName = this.beanName;
}
// 實(shí)例化
this.servletInstance = ReflectionUtils.accessibleConstructor(this.servletClass).newInstance();
// servlet初始化
this.servletInstance.init(new DelegatingServletConfig());
}
}
ZuulServlet
public class ZuulServlet extends HttpServlet {
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);
}
// 任意階段發(fā)生了異常都會(huì)執(zhí)行error,post階段
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
// pre節(jié)點(diǎn)的ZuulFilter執(zhí)行
preRoute();
} catch (ZuulException e) {
// error階段
error(e);
postRoute();
return;
}
try {
// route階段的路由執(zhí)行
// 這里其實(shí)就是通過(guò)網(wǎng)絡(luò)api調(diào)用目標(biāo)服務(wù)的接口了
// 我們可以實(shí)現(xiàn)自己的處理api接口的調(diào)用
// 如果你真的自己實(shí)現(xiàn)了,那么請(qǐng)記得最后執(zhí)行這行代碼
// context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running
// 這樣就阻止默認(rèn)的執(zhí)行了
// SimpleHostRoutingFilter 網(wǎng)絡(luò)請(qǐng)求處理使用的Apache Client
// 在該節(jié)點(diǎn)得到數(shù)據(jù)后我們可以通過(guò)ProxyRequestHelper工具類(lèi)將結(jié)果(InputStream)保存到
// RequestContext中,一切順利在post階段就會(huì)由SendResponseFilter從RequestContext中獲取InputStream,然后寫(xiě)到客戶端
route();
} catch (ZuulException e) {
error(e);
// 發(fā)生了異常也會(huì)執(zhí)行
postRoute();
return;
}
try {
// post階段
// 比如:系統(tǒng)提供的SendResponseFilter過(guò)濾器就是真正往客戶端開(kāi)始寫(xiě)數(shù)據(jù)了。
postRoute();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
// 將request,response都進(jìn)行了Wrapper(HttpServletRequestWrapper, HttpServletResponseWrapper)
zuulRunner.init(servletRequest, servletResponse);
}
}
在上面就看到了各個(gè)階段的Filter的執(zhí)行。這里需要注意一點(diǎn),在每個(gè)階段執(zhí)行的時(shí)候都會(huì)通過(guò)FilterLoader來(lái)獲取對(duì)應(yīng)階段的ZuulFilter。
這個(gè)FilterLoader對(duì)象并不是一個(gè)被容器管理的對(duì)象,下面來(lái)看這個(gè)FilterLoader是如何收集所有ZuulFilter。
public class ZuulServerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
protected static class ZuulFilterConfiguration {
// 獲取容器中所有的ZuulFilter
@Autowired
private Map<String, ZuulFilter> filters;
// 該Bean中有通過(guò)注解@PostConstruct標(biāo)注的Bean在初始化階段執(zhí)行的方法。
@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);
}
}
}
public class ZuulFilterInitializer {
private final Map<String, ZuulFilter> filters;
private final FilterLoader filterLoader;
private final FilterRegistry filterRegistry;
public ZuulFilterInitializer(Map<String, ZuulFilter> filters,
CounterFactory counterFactory, TracerFactory tracerFactory,
FilterLoader filterLoader, FilterRegistry filterRegistry) {
this.filters = filters;
this.counterFactory = counterFactory;
this.tracerFactory = tracerFactory;
this.filterLoader = filterLoader;
this.filterRegistry = filterRegistry;
}
// 該方法作用就是將所有的ZuulFilter添加到FilterRegistry中
@PostConstruct
public void contextInitialized() {
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
// 將所有的ZuulFilter注冊(cè)到FilterRegistry中
filterRegistry.put(entry.getKey(), entry.getValue());
}
}
}
FilterLoader
public class FilterLoader {
public Object runFilters(String sType) throws Throwable {
boolean bResult = false;
// 獲取對(duì)應(yīng)階段的ZuulFilter
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 List<ZuulFilter> getFiltersByType(String filterType) {
List<ZuulFilter> list = hashFiltersByType.get(filterType);
if (list != null) return list;
list = new ArrayList<ZuulFilter>();
// 從FilterRegistry中獲取所有的ZuulFilter;在上面看到了所有的ZuulFilter都被添加到了FilterRegistry中
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;
}
}
以上就是Zuul網(wǎng)關(guān)的底層實(shí)現(xiàn)原理。