当前位置:网站首页>Zuul 實現動態路由
Zuul 實現動態路由
2022-06-26 04:52:00 【Hejjon】
需求:SpringCloud 整合 zuul路由,實現在數據庫中修改serviceId 字段的值即可切換調用服務。譬如:假設在本地環境,zuul的運行端口是 8081。現在有ServiceA, ServiceB在正常運行,在zuul裏配置的路由地址分別是 /service-a/** 和 /service-b/** 。現在訪問A,訪問地址可以寫成 http://localhost:8081/service-a/xxx。現在我要實現只在數據庫的路由錶中改一下 serviceId,使得原來訪問服務A的地址去訪問到B服務上。
實現步驟如下:
1. 搭建zuul 項目
1.1 導入依賴
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>1.2 核心代碼
自定義路由處理器是實現動態路由的核心,需要繼承
org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator 類
和實現 org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator 接口
package com.atguigu.springcloud.filter;
import com.atguigu.springcloud.entity.ZuulRouteEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Created by caoshi at 21:21 2022-05-04
*/
public class CustomRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
private static Logger logger = LoggerFactory.getLogger(CustomRouteLocator.class);
private JdbcTemplate jdbcTemplate;
private ZuulProperties properties;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public CustomRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.properties = properties;
logger.info("servletPath:{}", servletPath);
}
@Override
public void refresh() {
doRefresh();
}
@Override
protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>();
routesMap.putAll(super.locateRoutes());
routesMap.putAll(locateRoutesFromDB());
Map<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routesMap.entrySet()) {
String path = entry.getKey();
if (!path.startsWith("/")) {
path = "/" + path;
}
if (StringUtils.hasText(this.properties.getPrefix())) {
path = this.properties.getPrefix() + path;
if (!path.startsWith("/")) {
path = "/" + path;
}
}
values.put(path, entry.getValue());
}
return values;
}
private Map<String, ZuulProperties.ZuulRoute> locateRoutesFromDB() {
logger.info("============= 開始加載db路由錶配置 ==============");
Map<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<>();
try {
List<ZuulRouteEntity> results =
this.jdbcTemplate.query("select * from x_gw_route where enabled = 1",
new BeanPropertyRowMapper<>(ZuulRouteEntity.class));
for (ZuulRouteEntity result : results) {
if (StringUtils.isEmpty(result.getPath())) {
continue;
}
ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
copy2ZuulRoute(result, zuulRoute);
routes.put(zuulRoute.getPath(), zuulRoute);
}
} catch (DataAccessException e) {
logger.error("=============加載db中路由錶配置出錯==============", e);
}
return routes;
}
/**
*
* @param zuulRouteEntity
* @param zuulRoute
*/
private void copy2ZuulRoute(ZuulRouteEntity zuulRouteEntity, ZuulProperties.ZuulRoute zuulRoute) {
if (zuulRouteEntity == null || zuulRoute == null) {
return;
}
zuulRoute.setId(zuulRouteEntity.getId());
zuulRoute.setServiceId(zuulRouteEntity.getService_id());
zuulRoute.setPath(zuulRouteEntity.getPath());
zuulRoute.setStripPrefix(zuulRouteEntity.getStrip_prefix() == 1);
zuulRoute.setUrl(zuulRouteEntity.getUrl());
zuulRoute.setRetryable(zuulRouteEntity.getRetryable() == 1);
}
}
路由錶映射實體類:
package com.atguigu.springcloud.entity;
/**
* Created by caoshi at 20:53 2022-06-16
*/
public class ZuulRouteEntity {
private String id; // 主鍵id
private String path; // 訪問路徑 /xxx/**
private String service_id; // 服務id 一般配成 spring.application.name
private String url; //
private int strip_prefix; // 是否去除前綴,zuul自帶前綴 /zuul,所以設置成1就是去掉前綴
private int retryable; // 是否失敗後重新請求
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getService_id() {
return service_id;
}
public void setService_id(String service_id) {
this.service_id = service_id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getStrip_prefix() {
return strip_prefix;
}
public void setStrip_prefix(int strip_prefix) {
this.strip_prefix = strip_prefix;
}
public int getRetryable() {
return retryable;
}
public void setRetryable(int retryable) {
this.retryable = retryable;
}
}
配置類,配置 CustomRouteLocator 交由Spring容器管理。
package com.atguigu.springcloud.config;
import com.atguigu.springcloud.filter.CustomRouteLocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Created by caoshi at 20:51 2022-06-16
*/
@Configuration
public class CustomZuulConfig {
private static Logger logger = LoggerFactory.getLogger(CustomZuulConfig.class);
private ZuulProperties zuulProperties = new ZuulProperties();
@Autowired
private ServerProperties server;
@Autowired
private JdbcTemplate jdbcTemplate;
@Bean
public CustomRouteLocator routeLocator() {
String contextPath = server.getServlet().getContextPath();
logger.info("=========== servlet contextPath {}", contextPath);
CustomRouteLocator routeLocator = new CustomRouteLocator(contextPath, zuulProperties);
routeLocator.setJdbcTemplate(jdbcTemplate);
return routeLocator;
}
}
數據庫路由錶結構
CREATE TABLE `x_gw_route` (
`id` varchar(50) NOT NULL COMMENT '主鍵',
`path` varchar(255) NOT NULL COMMENT '訪問路徑',
`service_id` varchar(50) DEFAULT NULL COMMENT '服務id',
`url` varchar(255) DEFAULT NULL,
`retryable` tinyint(1) DEFAULT NULL COMMENT '是否失敗後重新請求1是0否',
`enabled` tinyint(1) NOT NULL COMMENT '是否使用1正常0不使用',
`strip_prefix` int(11) DEFAULT NULL COMMENT '是否去除前綴1是0否',
`api_name` varchar(255) DEFAULT NULL COMMENT '服務名稱',
`c_desc` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '描述',
`c_createuserid` varchar(60) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '創建用戶id',
`c_createtime` datetime DEFAULT NULL,
`c_updateuserid` varchar(60) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '最後修改者id',
`c_updatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;至此動態路由的功能,zuul的配置完成。
如果還要實現請求過濾,比如校驗token信息等。可以自定義過濾器
package com.atguigu.springcloud.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/**
* Created by caoshi at 22:43 2022-06-04
*/
@Component
public class AuthHeaderFilter extends ZuulFilter {
private static Logger logger = LoggerFactory.getLogger(AuthHeaderFilter.class);
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
Object serviceIdKey = requestContext.get(FilterConstants.SERVICE_ID_KEY);
Object request_uri_key = requestContext.get(FilterConstants.REQUEST_URI_KEY);
System.out.println(serviceIdKey + " " + request_uri_key);
String servletPath = requestContext.getRequest().getServletPath();
System.out.println("servletPath = " + servletPath);
logger.info("網關接收到路由請求 {} ", requestContext.getRequest().getRequestURI());
return null;
}
}
可以重寫 run() 方法,可以取到 RequestContext 對象對請求進行過濾處理。
踩坑記錄:
默認在路由錶裏 strip_prefix 字段的值是0,導致zull不會去除前綴,而zuul本身默認就包含一個前綴 /zuul


導致在真實的請求前面加上這個前綴 /zuul。而我在服務提供者方的控制器方法上設置的 匹配路徑中是不包含 前綴/zuul的,所以導致請求失敗,報 404 錯誤。
最後的解决辦法是 將路由錶裏 strip_prefix 設置為 1,錶示去除前綴。
边栏推荐
- Condition query
- Motivational skills for achieving goals
- Nabicat连接:本地Mysql&&云服务Mysql以及报错
- JWT token authentication verification
- A new paradigm for large model application: unified feature representation optimization (UFO)
- #微信小程序# 在小程序里面退出退出小程序(navigator以及API--wx.exitMiniProgram)
- What is the best way to store chat messages in a database? [Close] - best way to store chat messages in a database? [closed]
- Sort query
- Dameng database backup and restore
- 5. <tag-栈和常规问题>补充: lt.946. 验证栈序列(同剑指 Offer 31. 栈的压入、弹出序列)
猜你喜欢

Sixtool- source code of multi-functional and all in one generation hanging assistant

1.19 learning summary

企业的产品服务怎么进行口碑营销?口碑营销可以找人代做吗?

Zhongshanshan: engineers after being blasted will take off | ONEFLOW u

1.24 learning summary

ModuleNotFoundError: No module named ‘numpy‘

UWB超高精度定位系统原理图

Introduction to markdown grammar

Use of better scroll

Multipass中文文档-远程使用Multipass
随机推荐
Why do many Shopify independent station sellers use chat robots? Read industry secrets in one minute!
NVM installation and use and NPM package installation failure record
记录一次循环引用的问题
torchvision_ Transform (image enhancement)
mysql高级学习(跟着尚硅谷老师周阳学习)
Multipass Chinese document - use multipass service to authorize the client
Is education important or ability important in software testing
Multipass Chinese document - use instance command alias
2.< tag-动态规划和常规问题>lt.343. 整数拆分
Yolov5 super parameter setting and data enhancement analysis
1.13 learning summary
#微信小程序# 在小程序里面退出退出小程序(navigator以及API--wx.exitMiniProgram)
ModuleNotFoundError: No module named ‘numpy‘
基础查询
date_ Range creation date range freq parameter value table and creation example
Genius makers: lone Rangers, technology giants and AI | ten years of the rise of in-depth learning
Sklearn Library -- linear regression model
Motivational skills for achieving goals
PHP installation SSH2 extension
LISP programming language