✨ 添加新特性。 扩展ribbon 支持IP有限策略

This commit is contained in:
冷冷 committed 2019-06-25 18:05:36 +08:00
1 parent 002f7d41a2
commit 796872022c
20 files changed
+412 -26

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
# 使用说明 V3.1.0
# 使用说明 V3.2.0
# 1. 使用docker-compose 宿主机不需要配置host来发现
# 2. 无需修改源码,根目录 docker-compose up 即可
# 3. 静静等待服务启动
@@ -66,7 +66,7 @@ import java.util.Map;
public class PigxTokenEndpoint {
private static final String PIGX_OAUTH_ACCESS = SecurityConstants.PIGX_PREFIX + SecurityConstants.OAUTH_PREFIX + "auth_to_access:";
private final ClientDetailsService clientDetailsService;
private final RedisTemplate pigxRedisTemplate;
private final RedisTemplate redisTemplate;
private final TokenStore tokenStore;
private final CacheManager cacheManager;
@@ -167,19 +167,19 @@ public class PigxTokenEndpoint {
List<String> pages = findKeysForPage(key, MapUtil.getInt(params, PaginationConstants.CURRENT)
, MapUtil.getInt(params, PaginationConstants.SIZE));
pigxRedisTemplate.setKeySerializer(new StringRedisSerializer());
pigxRedisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
Page result = new Page(MapUtil.getInt(params, PaginationConstants.CURRENT), MapUtil.getInt(params, PaginationConstants.SIZE));
result.setRecords(pigxRedisTemplate.opsForValue().multiGet(pages));
result.setTotal(Long.valueOf(pigxRedisTemplate.keys(key).size()));
result.setRecords(redisTemplate.opsForValue().multiGet(pages));
result.setTotal(Long.valueOf(redisTemplate.keys(key).size()));
return R.ok(result);
}
private List<String> findKeysForPage(String patternKey, int pageNum, int pageSize) {
ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
RedisSerializer<String> redisSerializer = (RedisSerializer<String>) pigxRedisTemplate.getKeySerializer();
Cursor cursor = (Cursor) pigxRedisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
RedisSerializer<String> redisSerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
List<String> result = new ArrayList<>();
int tmpIndex = 0;
int startIndex = (pageNum - 1) * pageSize;
+7
View File
@@ -22,6 +22,7 @@
<mysql.connector.version>8.0.16</mysql.connector.version>
<swagger.core.version>1.5.22</swagger.core.version>
<mp.weixin.version>3.3.0</mp.weixin.version>
<mica.version>1.1.3</mica.version>
</properties>
<dependencyManagement>
@@ -132,6 +133,12 @@
<artifactId>weixin-java-common</artifactId>
<version>${mp.weixin.version}</version>
</dependency>
<!--路由增强包 -->
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>mica-plus-ribbon</artifactId>
<version>${mica.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
@@ -18,7 +18,9 @@
package com.pig4cloud.pigx.common.data.cache;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -34,13 +36,14 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
*/
@EnableCaching
@Configuration
@ConditionalOnBean(RedisConnectionFactory.class)
@AllArgsConstructor
@AutoConfigureBefore(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
public class RedisTemplateConfig {
private final RedisConnectionFactory redisConnectionFactory;
@Bean
public RedisTemplate<String, Object> pigxRedisTemplate() {
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+5
View File
@@ -45,5 +45,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -42,4 +42,5 @@ public class DynamicRouteAutoConfiguration {
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator() {
return new PropertiesRouteDefinitionLocator(new GatewayProperties());
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.gateway.configuration;
import com.pig4cloud.pigx.common.gateway.rule.DiscoveryEnabledRule;
import com.pig4cloud.pigx.common.gateway.rule.MetadataAwareRule;
import com.pig4cloud.pigx.common.gateway.support.MicaRibbonRuleProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
* Mica ribbon rule auto configuration.
*
* @author L.cm
*/
@Configuration
@RequiredArgsConstructor
@ConditionalOnClass(NacosServer.class)
@AutoConfigureBefore(RibbonClientConfiguration.class)
@EnableConfigurationProperties(MicaRibbonRuleProperties.class)
@ConditionalOnProperty(value = "mica.ribbon.rule.enabled", matchIfMissing = true)
public class MicaRibbonRuleAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DiscoveryEnabledRule metadataAwareRule() {
return new MetadataAwareRule();
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.gateway.predicate;
import com.netflix.loadbalancer.AbstractServerPredicate;
import com.netflix.loadbalancer.PredicateKey;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import org.springframework.lang.Nullable;
/**
* 过滤服务
*
* @author L.cm
*/
public abstract class DiscoveryEnabledPredicate extends AbstractServerPredicate {
/**
* {@inheritDoc}
*/
@Override
public boolean apply(@Nullable PredicateKey input) {
return input != null
&& input.getServer() instanceof NacosServer
&& apply((NacosServer) input.getServer());
}
/**
* Returns whether the specific {@link NacosServer} matches this predicate.
*
* @param server the discovered server
* @return whether the server matches the predicate
*/
protected abstract boolean apply(NacosServer server);
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.gateway.predicate;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import com.pig4cloud.pigx.common.gateway.support.MicaRibbonRuleProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import java.util.Map;
/**
* 基于 Metadata 的服务筛选
*
* @author L.cm
*/
public class MetadataAwarePredicate extends DiscoveryEnabledPredicate {
@Override
protected boolean apply(NacosServer server) {
final Map<String, String> metadata = server.getMetadata();
// 获取配置
MicaRibbonRuleProperties properties = SpringContextHolder.getBean(MicaRibbonRuleProperties.class);
// 服务里的配置
String localTag = properties.getTag();
if (StrUtil.isBlank(localTag)) {
return true;
}
// 本地有 tag,服务没有,返回 false
String metaDataTag = metadata.get("tag");
if (StrUtil.isBlank(metaDataTag)) {
return false;
}
return metaDataTag.equals(localTag);
}
}
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.gateway.rule;
import com.google.common.base.Optional;
import com.netflix.loadbalancer.*;
import com.pig4cloud.pigx.common.gateway.predicate.DiscoveryEnabledPredicate;
import org.springframework.util.Assert;
import java.util.List;
/**
* ribbon 路由规则
*
* @author L.cm
*/
public abstract class DiscoveryEnabledRule extends PredicateBasedRule {
private final CompositePredicate predicate;
public DiscoveryEnabledRule(DiscoveryEnabledPredicate discoveryEnabledPredicate) {
Assert.notNull(discoveryEnabledPredicate, "Parameter 'discoveryEnabledPredicate' can't be null");
this.predicate = createCompositePredicate(discoveryEnabledPredicate, new AvailabilityPredicate(this, null));
}
/**
* {@inheritDoc}
*/
@Override
public AbstractServerPredicate getPredicate() {
return predicate;
}
@Override
public Server choose(Object key) {
ILoadBalancer lb = getLoadBalancer();
List<Server> allServers = lb.getAllServers();
// 过滤服务列表
List<Server> serverList = filterServers(allServers);
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(serverList, key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
/**
* 过滤服务
*
* @param serverList 服务列表
* @return 服务列表
*/
public abstract List<Server> filterServers(List<Server> serverList);
private CompositePredicate createCompositePredicate(DiscoveryEnabledPredicate discoveryEnabledPredicate, AvailabilityPredicate availabilityPredicate) {
return CompositePredicate.withPredicates(discoveryEnabledPredicate, availabilityPredicate)
.build();
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.gateway.rule;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.system.HostInfo;
import cn.hutool.system.SystemUtil;
import com.netflix.loadbalancer.Server;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import com.pig4cloud.pigx.common.gateway.predicate.DiscoveryEnabledPredicate;
import com.pig4cloud.pigx.common.gateway.predicate.MetadataAwarePredicate;
import com.pig4cloud.pigx.common.gateway.support.MicaRibbonRuleProperties;
import org.springframework.util.PatternMatchUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* ribbon 路由规则器
*
* @author dream.lu
*/
public class MetadataAwareRule extends DiscoveryEnabledRule {
public MetadataAwareRule() {
this(new MetadataAwarePredicate());
}
public MetadataAwareRule(DiscoveryEnabledPredicate predicate) {
super(predicate);
}
@Override
public List<Server> filterServers(List<Server> serverList) {
MicaRibbonRuleProperties ribbonProperties = SpringContextHolder.getBean(MicaRibbonRuleProperties.class);
List<String> priorIpPattern = ribbonProperties.getPriorIpPattern();
// 1. 查找是否有本机 ip
HostInfo hostInfo = SystemUtil.getHostInfo();
String hostIp = hostInfo.getAddress();
// 优先的 ip 规则
boolean hasPriorIpPattern = !priorIpPattern.isEmpty();
String[] priorIpPatterns = priorIpPattern.toArray(new String[0]);
List<Server> priorServerList = new ArrayList<>();
for (Server server : serverList) {
String host = server.getHost();
// 2. 优先本地 ip 的服务
if (ObjectUtil.equal(hostIp, host)) {
return Collections.singletonList(server);
}
// 3. 优先的 ip 服务
if (hasPriorIpPattern && PatternMatchUtils.simpleMatch(priorIpPatterns, host)) {
priorServerList.add(server);
}
}
// 4. 如果优先的有数据直接返回
if (!priorServerList.isEmpty()) {
return priorServerList;
}
return Collections.unmodifiableList(serverList);
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.gateway.support;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.ArrayList;
import java.util.List;
/**
* Ribbon 配置
*
* @author L.cm
*/
@Getter
@Setter
@RefreshScope
@ConfigurationProperties("ribbon.rule")
public class PigxRibbonRuleProperties {
/**
* 是否开启,默认:true
*/
private Boolean enabled = Boolean.TRUE;
/**
* 服务的tag,用于灰度,匹配:nacos.discovery.metadata.tag
*/
private String tag;
/**
* 优先的ip列表,支持通配符,例如:10.20.0.8*、10.20.0.*
*/
private List<String> priorIpPattern = new ArrayList<>();
}
@@ -17,7 +17,7 @@ import org.springframework.stereotype.Component;
*/
@Data
@Component
@ConfigurationProperties(prefix = "xsequence.redis")
@ConfigurationProperties(prefix = "pigx.xsequence.redis")
public class SequenceRedisProperties extends BaseSequenceProperties {
}
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
*/
@Data
@Component
@ConfigurationProperties(prefix = "xsequence.snowflake")
@ConfigurationProperties(prefix = "pigx.xsequence.snowflake")
public class SequenceSnowflakeProperties extends BaseSequenceProperties {
/**
@@ -8,6 +8,7 @@ spring:
nacos:
discovery:
server-addr: ${NACOS-HOST:pigx-register}:${NACOS-PORT:8848}
network-interface: ppp0
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yml
@@ -50,14 +50,14 @@ import java.net.URI;
@Configuration
@AllArgsConstructor
public class DynamicRouteInitRunner {
private final RedisTemplate pigxRedisTemplate;
private final RedisTemplate redisTemplate;
private final SysRouteConfService routeConfService;
@Async
@Order
@EventListener({WebServerInitializedEvent.class, DynamicRouteInitEvent.class})
public void initRoute() {
Boolean result = pigxRedisTemplate.delete(CacheConstants.ROUTE_KEY);
Boolean result = redisTemplate.delete(CacheConstants.ROUTE_KEY);
log.info("初始化网关路由 {} ", result);
routeConfService.routes().forEach(route -> {
@@ -73,8 +73,8 @@ public class DynamicRouteInitRunner {
vo.setPredicates(predicateObj.toList(PredicateDefinition.class));
log.info("加载路由ID:{},{}", route.getRouteId(), vo);
pigxRedisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));
pigxRedisTemplate.opsForHash().put(CacheConstants.ROUTE_KEY, route.getRouteId(), vo);
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));
redisTemplate.opsForHash().put(CacheConstants.ROUTE_KEY, route.getRouteId(), vo);
});
log.debug("初始化网关路由结束 ");
}
@@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit;
@Service
@AllArgsConstructor
public class MobileServiceImpl implements MobileService {
private final RedisTemplate pigxRedisTemplate;
private final RedisTemplate redisTemplate;
private final SysUserMapper userMapper;
@@ -67,7 +67,7 @@ public class MobileServiceImpl implements MobileService {
return R.ok(Boolean.FALSE, "手机号未注册");
}
Object codeObj = pigxRedisTemplate.opsForValue().get(CommonConstants.DEFAULT_CODE_KEY + mobile);
Object codeObj = redisTemplate.opsForValue().get(CommonConstants.DEFAULT_CODE_KEY + mobile);
if (codeObj != null) {
log.info("手机号验证码未过期:{},{}", mobile, codeObj);
@@ -76,7 +76,7 @@ public class MobileServiceImpl implements MobileService {
String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE));
log.debug("手机号生成验证码成功:{},{}", mobile, code);
pigxRedisTemplate.opsForValue().set(
redisTemplate.opsForValue().set(
CommonConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + "@" + mobile
, code, SecurityConstants.CODE_TIME, TimeUnit.SECONDS);
return R.ok(Boolean.TRUE, code);
@@ -57,7 +57,7 @@ import java.util.stream.Collectors;
@AllArgsConstructor
@Service("sysRouteConfService")
public class SysRouteConfServiceImpl extends ServiceImpl<SysRouteConfMapper, SysRouteConf> implements SysRouteConfService {
private final RedisTemplate pigxRedisTemplate;
private final RedisTemplate redisTemplate;
private final ApplicationEventPublisher applicationEventPublisher;
@@ -84,7 +84,7 @@ public class SysRouteConfServiceImpl extends ServiceImpl<SysRouteConfMapper, Sys
@Transactional(rollbackFor = Exception.class)
public Mono<Void> updateRoutes(JSONArray routes) {
// 清空Redis 缓存
Boolean result = pigxRedisTemplate.delete(CacheConstants.ROUTE_KEY);
Boolean result = redisTemplate.delete(CacheConstants.ROUTE_KEY);
log.info("清空网关路由 {} ", result);
// 遍历修改的routes,保存到Redis
@@ -131,8 +131,8 @@ public class SysRouteConfServiceImpl extends ServiceImpl<SysRouteConfMapper, Sys
if (order != null) {
vo.setOrder(Integer.parseInt(String.valueOf(order)));
}
pigxRedisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));
pigxRedisTemplate.opsForHash().put(CacheConstants.ROUTE_KEY, vo.getId(), vo);
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));
redisTemplate.opsForHash().put(CacheConstants.ROUTE_KEY, vo.getId(), vo);
routeDefinitionVoList.add(vo);
});
@@ -55,7 +55,7 @@ public class WxMpConfiguration {
, LocationHandler locationHandler, MenuHandler menuHandler
, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler
, SubscribeHandler subscribeHandler, ScanHandler scanHandler
, RedisTemplate pigxRedisTemplate) {
, RedisTemplate redisTemplate) {
this.accountService = accountService;
this.logHandler = logHandler;
this.nullHandler = nullHandler;
@@ -67,7 +67,7 @@ public class WxMpConfiguration {
this.unsubscribeHandler = unsubscribeHandler;
this.subscribeHandler = subscribeHandler;
this.scanHandler = scanHandler;
this.redisTemplate = pigxRedisTemplate;
this.redisTemplate = redisTemplate;
}
@PostConstruct
+1 -1
View File
@@ -51,7 +51,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<spring-boot-admin.version>2.1.5</spring-boot-admin.version>
<easyexcel.version>1.1.2-beta5</easyexcel.version>
<hutool.version>4.5.12</hutool.version>
<hutool.version>4.5.13</hutool.version>
<kaptcha.version>0.0.9</kaptcha.version>
<swagger.fox.version>2.9.2</swagger.fox.version>
<swagger-bootstrap-ui.version>1.9.4</swagger-bootstrap-ui.version>