前奏
最近公司需求网关,就选择了zuul。就使用了一下,在这里记录下使用过程中,遇到的一些常见的
问题和用法。并且本次使用注册中心是基于zk,所以和大路上使用eureka是不同的,并且网上关于
使用zk的资料相对较少!鉴于spring cloud 已经不再持续,建议大家选择gateway。
pom依赖
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
主要依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<optional>true</optional>
</dependency>
<!-- 除此外还需要springboot web 、mysql、mybatis、lombok等依赖自行添加 -->
注册中心
application.properties
spring.cloud.zookeeper.connect-string=zk地址
spring.cloud.zookeeper.discovery.instance-port=${server.port}
zuul.host.connection-request-timeout-millis=3000
#spring.cloud.zookeeper.base-sleep-time-ms=10
#不向zookeeper注册
#spring.cloud.zookeeper.discovery.register=false
# retry
zuul.retryable=false
# 超时设置,url生效
zuul.host.connect-timeout-millis=10000
zuul.host.socket-timeout-millis=10000
# 超时设置,serviceId生效
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000
ribbon.ReadTimeout=30000
ribbon.SocketTimeout=30000
zuul.add-host-header=true
zuul.strip-prefix=true
动态加载路由规则
实体对象:
@Data
public class ZuulRouteRule implements Serializable {
/**
* 主键ID
*/
private Integer id;
/**
* 请求路径
*/
private String path;
/**
* 服务提供者编码
*/
private String serviceId;
/**
* 路由地址
*/
private String url;
/**
* 是否增加前缀,0:否 1:是
*/
private Boolean stripPrefix;
/**
* 是否重试, 0:否 1:是
*/
private Boolean retryable;
/**
* 是否生效,0:生效 1:失效
*/
private Boolean enabled;
/**
* 规则说明
*/
private String description;
/**
* 创建时间
*/
private Date createTime;
/**
* 创建人
*/
private String createUser;
/**
* 更新时间
*/
private Date updateTime;
/**
* 更新人
*/
private String updateUser;
private static final long serialVersionUID = 1L;
}
DAO层:
public interface ZuulRouteRuleMapper{
List<ZuulRouteRule> queryZuulRulesByCondition(ZuulRouteRule param);
}
Service接口
public interface ZuulRouteRuleService {
Map<String, ZuulProperties.ZuulRoute> getAllEnabledRules();
}
@Service
public class ZuulRouteRuleServiceImpl implements ZuulRouteRuleService {
@Autowired
private ZuulRouteRuleMapper zuulRouteRuleMapper;
@Override
public Map<String, ZuulProperties.ZuulRoute> getAllEnabledRules() {
Map<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<>();
ZuulRouteRule param = new ZuulRouteRule();
param.setEnabled(true);
List<ZuulRouteRule> zuulRouteRuleList = zuulRouteRuleMapper.queryZuulRulesByCondition(param);
zuulRouteRuleList.forEach(routeRule -> {
if (StringUtils.isEmpty(routeRule.getPath())) {
return;
}
ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
BeanUtils.copyProperties(routeRule, zuulRoute);
zuulRoute.setId(String.valueOf(routeRule.getId()));
routes.put(zuulRoute.getPath(), zuulRoute);
});
return routes;
}
}
动态路由加载
@Slf4j
public class DynamicZuulRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
private static final String SLASH = "/";
@Autowired
private ZuulProperties properties;
@Autowired
private ZuulRouteRuleService zuulRouteRuleService;
public DynamicZuulRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.properties = properties;
}
@Override
public void refresh() {
doRefresh();
}
@Override
protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>();
routesMap.putAll(super.locateRoutes());
Map<String, ZuulProperties.ZuulRoute> dbRoutesMap = zuulRouteRuleService.getAllEnabledRules();
if(log.isInfoEnabled()){
log.info("加载数据条目:{}",dbRoutesMap.size());
}
if (null != dbRoutesMap && !dbRoutesMap.isEmpty()) {
routesMap.putAll(dbRoutesMap);
}
LinkedHashMap<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
routesMap.forEach((key, value) -> {
String path = key;
if (!path.startsWith(SLASH)) {
path = SLASH + path;
}
if (StringUtils.hasText(this.properties.getPrefix())) {
path = this.properties.getPrefix() + path;
if (!path.startsWith(SLASH)) {
path = SLASH + path;
}
}
values.put(path, value);
});
return values;
}
}
JavaConfig 加载bean
@Configuration
public class DynamicZuulConfig {
@Bean
public DynamicZuulRouteLocator routeLocator(ZuulProperties zuulProperties,ServerProperties serverProperties) {
return new DynamicZuulRouteLocator(
serverProperties.getServlet().getContextPath(), zuulProperties);
}
}
springboot启动类:
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
@MapperScan("com.demo.gateway.dao")
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public AuthenticationFilter getAuthFilter(){
return new AuthenticationFilter();
}
}
自动刷新
定时刷新
@Configuration
@Slf4j
public class ScheduleZuulRulesListener implements ApplicationListener<ContextRefreshedEvent> {
private static ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
@Autowired
private ZuulHandlerMapping zuulHandlerMapping;
@Override
public void onApplicationEvent(ContextRefreshedEvent applicationEvent) {
service.scheduleAtFixedRate(() -> {
if(log.isInfoEnabled()) {
log.info("刷新zuul配置信息开始");
}
try {
zuulHandlerMapping.setDirty(true);
} catch (Exception e) {
log.error("刷新Zuul规则配置报错:{}",e.getMessage());
}
if(log.isInfoEnabled()) {
log.info("刷新zuul配置信息结束");
}
},10,10,TimeUnit.SECONDS);
}
}
其他服务集成网关
待补充
集成swagger
待补充