前言:该文章只是记录自己平时的点点滴滴.来激励自己成长.长成自己喜欢的人!
环境:
- 电脑 Mac 10.13.1
- IntelliJ IDEA 2018.2 (Ultimate Edition)
- Spring boot 2.0.1.RELEASE
实现方案:
- 对外提供接口只暴漏一个接口名称,通过入参的形式路由服务对象
- 要求数据加密
- 要求传输每一步有日志输出
实现细节:
- Zuul
- Feign
- ZuulFilter
- ErrorController
- RefreshableRouteLocator
这里记录 RefreshableRouteLocator 实现db路由配置.
- DB - table
CREATE TABLE `saas_zuul_route` (
`id` varchar(64) NOT NULL COMMENT 'zuul routes id',
`path` varchar(64) DEFAULT NULL COMMENT 'zuul path',
`service_id` varchar(64) DEFAULT NULL COMMENT 'zuul serviceId',
`url` varchar(64) DEFAULT NULL COMMENT 'zuul url',
`enabled` tinyint(1) DEFAULT '1' COMMENT '是否启用,默认启用',
`retryable` tinyint(1) DEFAULT '1' COMMENT '是否重复使用',
`strip_prefix` tinyint(1) DEFAULT '1' COMMENT '是否添加前缀',
`prefix` varchar(32) DEFAULT NULL,
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_update` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`state` tinyint(1) DEFAULT '1' COMMENT '数据状态',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='zuul 动态路由主表'
CREATE TABLE `saas_zuul_route_detail` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`zuul_route_id` varchar(64) DEFAULT NULL COMMENT 'zuul routes id',
`service_path` varchar(64) DEFAULT NULL COMMENT '微服务controller path',
`service_name` varchar(64) DEFAULT NULL COMMENT '对外service',
`zuul_name` varchar(64) DEFAULT NULL COMMENT '微服务中文名称',
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
`gmt_update` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`state` tinyint(1) DEFAULT '1' COMMENT '数据状态',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='zuul 动态路由详情表'
业务逻辑代码(借鉴 https://blog.csdn.net/tianyaleixiaowu/article/details/77933295 )
public class DBRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
private JdbcTemplate jdbcTemplate;
private ZuulProperties properties;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public DBRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.properties = properties;
log.info("servletPath:{}", servletPath);
}
//父类已经提供了这个方法,这里写出来只是为了说明这一个方法很重要!!!
// @Override
// protected void doRefresh() {
// super.doRefresh();
// }
@Override
public void refresh() {
doRefresh();
}
@Override
protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>();
routesMap.putAll(super.locateRoutes());
//从db中加载路由信息
routesMap.putAll(locateRoutesFromDB());
LinkedHashMap<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routesMap.entrySet()) {
log.info("数据集合 + {}",entry);
String path = entry.getKey();
if (!path.startsWith("/")) {
path = "/" + path;
}
if (StringUtil.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() {
Map<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<>();
List<ZuulRouteDO> results = jdbcTemplate.query("select * from saas_zuul_route where enabled = true ", new BeanPropertyRowMapper<>(ZuulRouteDO.class));
for (ZuulRouteDO result : results) {
if (StringUtil.isEmptys(result.getPath()) ) {
continue;
}
if (StringUtil.isEmptys(result.getServiceId()) && StringUtil.isEmptys(result.getUrl())) {
continue;
}
ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
try {
BeanUtils.copyProperties(result, zuulRoute);
} catch (Exception e) {
log.error("=============load zuul route info from db with error==============", e);
}
routes.put(zuulRoute.getPath(), zuulRoute);
}
return routes;
}
@Data
private static class ZuulRouteDO {
private String id;
private String path;
private String serviceId;
private String url;
private Boolean stripPrefix = true;
private Boolean retryable;
private Boolean enabled;
private String prefix;
private Date gmtCreate;
private Date gmtUpdate;
private String state;
private String remark;
}
zuul 启动时加载配置
@Configuration
public class ZuulGatewayConfig {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
ZuulProperties zuulProperties;
@Autowired
ServerProperties server;
/**
* @return DBRouteLocator 动态路由
*/
@Bean
public DBRouteLocator routeLocator() {
DBRouteLocator routeLocator = new DBRouteLocator(server.getServlet().getServletPrefix(), this.zuulProperties);
routeLocator.setJdbcTemplate(jdbcTemplate);
return routeLocator;
}
}
刷新缓存.我这里用的是db链接.所以不需要刷新缓存.毕竟spring boot 2.0 提供了定时刷新任务
bootstrap.yml
server:
port: 8080
spring:
application:
name: zuul
cloud:
config:
enabled: false #关闭加载远程配置
logging:
level:
root: INFO
application.yml 配置,因为db读取routes . 所以这里不配置任何routes
spring:
datasource:
name: mysql_dev
type: com.alibaba.druid.pool.DruidDataSource
#druid相关配置
druid:
#监控统计拦截的filters
#配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙,防止sql注入
filters: stat
driver-class-name: com.mysql.jdbc.Driver
#基本属性
url: jdbc:mysql://localhost/abc
username: root
password: dasd
#配置初始化大小/最小/最大
initial-size: 1
min-idle: 1
max-active: 20
#获取连接等待超时时间
max-wait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
time-between-eviction-runs-millis: 60000
#一个连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
filter:
stat:
log-slow-sql: true
eureka:
client:
service-url:
defaultZone: http://admin:admin@localhost:1111/eureka
instance:
prefer-ip-address: true #开启ip访问. (默认主机名访问)
instance-id: ${spring.application.name}:${server.port} #主机名:主机ip:port
zuul:
ignoredServices: '*' # 所有的微服务 ignored, 拒绝