Multi service registry center

This commit is contained in:
hongqiaowei
2021-12-23 16:05:27 +08:00
committed by GitHub
parent c503e02ab1
commit 0268eb00e0
35 changed files with 1126 additions and 361 deletions

View File

@@ -4,7 +4,7 @@ English | [简体中文](./README.md)
<a href="https://www.fizzgate.com"><img src="https://raw.githubusercontent.com/wiki/wehotel/fizz-gateway-community/img/icon-color.png" width="70%"></a>
</p>
<p>
<img alt="Version" src="https://img.shields.io/badge/version-2.4.0-blue.svg?cacheSeconds=2592000" />
<img alt="Version" src="https://img.shields.io/badge/version-2.4.1-blue.svg?cacheSeconds=2592000" />
<a href="http://www.fizzgate.com/fizz-gateway-community/" target="_blank">
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
</a>
@@ -111,6 +111,7 @@ Starting from v1.3.0, the frontend and backend of the management backend are mer
| v2.3.2 | v2.3.2 |
| v2.3.3 | v2.3.3 |
| v2.4.0 | v2.4.0 |
| v2.4.1 | v2.4.1 |
Please download the corresponding management backend version according to the version of the community version

View File

@@ -3,7 +3,7 @@
<a href="https://www.fizzgate.com"><img src="https://raw.githubusercontent.com/wiki/wehotel/fizz-gateway-community/img/icon-color.png" width="70%"></a>
</p>
<p>
<img alt="Version" src="https://img.shields.io/badge/version-2.4.0-blue.svg?cacheSeconds=2592000" />
<img alt="Version" src="https://img.shields.io/badge/version-2.4.1-blue.svg?cacheSeconds=2592000" />
<a href="http://www.fizzgate.com/fizz-gateway-community/" target="_blank">
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
</a>
@@ -113,6 +113,7 @@ API地址http://demo.fizzgate.com/proxy/[服务名]/[API_Path]
| v2.3.2 | v2.3.2 |
| v2.3.3 | v2.3.3 |
| v2.4.0 | v2.4.0 |
| v2.4.1 | v2.4.1 |
请根据社区版的版本下载对应的管理后台版本

View File

@@ -16,13 +16,13 @@
<properties>
<java.version>1.8</java.version>
<spring-framework.version>5.2.18.RELEASE</spring-framework.version>
<spring-framework.version>5.2.19.RELEASE</spring-framework.version>
<spring-session-bom.version>Dragonfruit-SR3</spring-session-bom.version>
<reactor-bom.version>Dysprosium-SR25</reactor-bom.version>
<lettuce.version>5.3.7.RELEASE</lettuce.version>
<netty.version>4.1.70.Final</netty.version>
<httpcore.version>4.4.14</httpcore.version>
<log4j2.version>2.14.1</log4j2.version>
<netty.version>4.1.72.Final</netty.version>
<httpcore.version>4.4.15</httpcore.version>
<log4j2.version>2.17.0</log4j2.version>
<slf4j.version>1.7.32</slf4j.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<lombok.version>1.18.22</lombok.version>
@@ -37,6 +37,7 @@
<netty-tcnative.version>2.0.46.Final</netty-tcnative.version>
<spring-cloud.version>2.2.9.RELEASE</spring-cloud.version>
<resilience4j.version>1.7.1</resilience4j.version>
<snakeyaml.version>1.30</snakeyaml.version>
</properties>
<dependencies>
@@ -99,6 +100,12 @@
<artifactId>resilience4j-reactor</artifactId>
<version>${resilience4j.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-classes</artifactId>
<version>${netty-tcnative.version}</version>
</dependency>
</dependencies>
<profiles>

View File

@@ -187,6 +187,7 @@ public class FizzBootstrapApplication {
public static void main(String[] args) {
System.setProperty("log4j2.contextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
System.setProperty("log4j2.formatMsgNoLookups", "true");
SpringApplication springApplication = new SpringApplication(FizzBootstrapApplication.class);
springApplication.setApplicationContextClass(CustomReactiveWebServerApplicationContext.class);

View File

@@ -20,7 +20,7 @@ eureka:
enabled: false #use Eureka? (default:false)
serviceUrl:
# need replace
defaultZone: http://localhost:6600/eureka/ #please input the eureka client serviceUrl defaultZone (default:http://localhost:6600/eureka/)
defaultZone: http://6.6.6.6:6600/eureka/ #please input the eureka client serviceUrl defaultZone (default:http://localhost:6600/eureka/)
instance:
prefer-ip-address: true
################################################### Eureka config end ###################################################
@@ -52,7 +52,7 @@ spring:
aggregate:
redis:
# need replace
host: 6.6.6.6 #please input the redis host (default:localhost)
host: 1.1.1.1 #please input the redis host (default:localhost)
# need replace
port: 6379 #please input the redis port (default:6379)
# need replace
@@ -127,15 +127,22 @@ fizz:
retry-count: 0 # default no retry
retry-interval: 0 # default no retry interval
crypto: true # if true, client will encrypt request body and decrypt response body
# service-registration:
# type: eureka # service registration type, can be eureka or nacos
# application: ax # register the name of this application to server
# port: 8601 # the port to be registered
# service-url: http://6.6.6.6:6600/eureka/ # server address
# type: nacos
# application: ax
service-registration:
# eureka:
# server-port: 8601
# client:
# enabled: true
# serviceUrl:
# defaultZone: http://3.3.3.3:6600/eureka
# instance:
# appname: fizz-dedicated-line
# prefer-ip-address: true
# nacos:
# discovery:
# enabled: true
# service: fizz-dedicated-line
# port: 8601
# service-url: 6.6.6.6:8848
# server-addr: 2.2.2.2:8848
fizz-trace-id:
header: X-Trace-Id

View File

@@ -7,10 +7,10 @@
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<!--<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level [%-29t] %30c{1}.%41M:%4L %m %ex%n"/>-->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %30c{1}.%41M:%4L %m %ex%n"/>
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %30c{1}.%41M:%4L %m{nolookups} %ex%n"/>
</Console>
<LogSend name="LogSend">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg%n"/>
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg{nolookups}%n"/>
</LogSend>
</Appenders>
<Loggers>

View File

@@ -27,6 +27,7 @@ public final class Consts {
}
public static final class S {
public static final String DEFAULT = "default";
public static final String TRUE = "true";
public static final String FALSE = "false";
public static final String TRUE1 = "1";

View File

@@ -0,0 +1,34 @@
package we.util;
import java.io.File;
import java.util.Objects;
/**
* @author hongqiaowei
*/
public abstract class FileUtils {
private FileUtils() {
}
public static String getAppRootDir() {
return new File(Consts.S.EMPTY).getAbsolutePath();
}
public static String getAbsoluteDir(Class<?> cls) {
return new File(Objects.requireNonNull(cls.getResource(Consts.S.EMPTY)).getPath()).getAbsolutePath();
}
public static String getAbsolutePath(Class<?> cls) {
String absoluteDir = getAbsoluteDir(cls);
return absoluteDir + File.separatorChar + cls.getSimpleName() + ".class";
}
/**
* @param file relative to src or resource dir, eg: we/util/FileUtils.class and application.yml
*/
public static String getAbsolutePath(String file) {
return new File(Objects.requireNonNull(FileUtils.class.getClassLoader().getResource(file)).getPath()).getAbsolutePath();
}
}

View File

@@ -0,0 +1,70 @@
package we.util;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.PropertyAccessor;
import java.util.Map;
import java.util.Properties;
/**
* @author hongqiaowei
*/
public abstract class PropertiesUtils {
private PropertiesUtils() {
}
public static String normalize(String propertyName) {
char[] chars = propertyName.toCharArray();
StringBuilder b = new StringBuilder(chars.length);
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (c == Consts.S.DASH) {
b.append(Character.toUpperCase(chars[++i]));
} else {
b.append(c);
}
}
return b.toString();
}
public static Properties remove(Properties properties, String prefix) {
Properties result = new Properties();
properties.forEach(
(k, v) -> {
String s = k.toString();
int idx = s.indexOf(prefix);
if (idx > -1) {
s = s.substring(prefix.length() + 1);
}
result.setProperty(s, v.toString());
}
);
return result;
}
public static void setBeanPropertyValue(Object bean, Properties properties) {
setBeanPropertyValue(bean, properties, null);
}
public static void setBeanPropertyValue(Object bean, Properties properties, Map<String, Class<?>> propertyTypeHint) {
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean);
for (String propertyName : properties.stringPropertyNames()) {
if (beanWrapper.isWritableProperty(propertyName)) {
beanWrapper.setPropertyValue(propertyName, properties.get(propertyName));
} else if (propertyTypeHint != null) {
int dotPos = propertyName.lastIndexOf(Consts.S.DOT);
if (dotPos > -1) {
String prefix = propertyName.substring(0, dotPos);
Class<?> aClass = propertyTypeHint.get(prefix);
if (aClass != null && Map.class.isAssignableFrom(aClass)) {
String newPropertyName = prefix + PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR + propertyName.substring(dotPos + 1) + PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR;
beanWrapper.setPropertyValue(newPropertyName, properties.get(propertyName));
}
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
package we.util;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.util.Properties;
/**
* @author hongqiaowei
*/
public abstract class YmlUtils {
private YmlUtils() {
}
public static Properties file2properties(String file) {
Resource resource = new ClassPathResource(file);
return getProperties(resource);
}
public static Properties string2properties(String config) {
Resource resource = new ByteArrayResource(config.getBytes());
return getProperties(resource);
}
private static Properties getProperties(Resource resource) {
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(resource);
return yamlPropertiesFactoryBean.getObject();
}
}

View File

@@ -329,6 +329,10 @@
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-classes</artifactId>
</dependency>
</dependencies>
<repositories>

View File

@@ -19,27 +19,25 @@ package we.dedicated_line;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import we.config.SystemConfig;
import we.service_registry.FizzServiceRegistration;
import we.service_registry.eureka.FizzEurekaHelper;
import we.service_registry.eureka.FizzEurekaProperties;
import we.service_registry.eureka.FizzEurekaServiceRegistration;
import we.service_registry.nacos.FizzNacosHelper;
import we.service_registry.nacos.FizzNacosProperties;
import we.service_registry.nacos.FizzNacosServiceRegistration;
import javax.annotation.PreDestroy;
import java.util.Properties;
/**
* @author hongqiaowei
@@ -51,9 +49,9 @@ import javax.annotation.PreDestroy;
@AutoConfigureAfter({EurekaClientAutoConfiguration.class, NacosDiscoveryAutoConfiguration.class})
public class DedicatedLineServiceRegistration implements ApplicationListener<DedicatedLineWebServerInitializedEvent> {
private ServiceRegistry serviceRegistry;
private static final Logger log = LoggerFactory.getLogger(DedicatedLineServiceRegistration.class);
private Registration registration;
private FizzServiceRegistration fizzServiceRegistration;
@SneakyThrows
@Override
@@ -63,69 +61,74 @@ public class DedicatedLineServiceRegistration implements ApplicationListener<Ded
ConfigurableEnvironment env = applicationContext.getEnvironment();
String prefix = SystemConfig.FIZZ_DEDICATED_LINE_CLIENT_PREFIX + ".service-registration";
String type = env.getProperty(prefix + ".type");
boolean eureka = env.containsProperty((prefix + ".eureka.instance.appname"));
boolean nacos = env.containsProperty((prefix + ".nacos.discovery.server-addr"));
if (StringUtils.isNotBlank(type)) {
if ("eureka".equals(type)) {
String application = env.getProperty(prefix + ".application");
String ipAddress = env.getProperty(prefix + ".ip-address");
String port = env.getProperty(prefix + ".port");
String preferIpAddress = env.getProperty(prefix + ".prefer-ip-address", "true");
String serviceUrl = env.getProperty(prefix + ".service-url");
FizzEurekaProperties fizzEurekaProperties = new FizzEurekaProperties().applicationContext(applicationContext)
.appName(application)
.ipAddress(ipAddress)
.nonSecurePort(Integer.parseInt(port))
.preferIpAddress(Boolean.parseBoolean(preferIpAddress))
.serviceUrl(serviceUrl);
FizzEurekaServiceRegistration fizzEurekaServiceRegistration = FizzEurekaHelper.getServiceRegistration(fizzEurekaProperties);
serviceRegistry = fizzEurekaServiceRegistration.serviceRegistry;
registration = fizzEurekaServiceRegistration.registration;
if (eureka || nacos) {
if (eureka) {
Properties eurekaProperties = new Properties();
boolean find = false;
for (PropertySource<?> propertySource : env.getPropertySources()) {
if (propertySource instanceof OriginTrackedMapPropertySource) {
OriginTrackedMapPropertySource originTrackedMapPropertySource = (OriginTrackedMapPropertySource) propertySource;
String[] propertyNames = originTrackedMapPropertySource.getPropertyNames();
for (String propertyName : propertyNames) {
if (propertyName.length() > 55) {
int eurekaPos = propertyName.indexOf("eureka");
if (eurekaPos > -1) {
eurekaProperties.setProperty(propertyName.substring(eurekaPos), originTrackedMapPropertySource.getProperty(propertyName).toString());
find = true;
}
}
}
if (find) {
break;
}
}
}
if (!find) {
log.error("no eureka config");
return;
}
fizzServiceRegistration = FizzEurekaHelper.getServiceRegistration(applicationContext, eurekaProperties);
}
if ("nacos".equals(type)) {
String application = env.getProperty(prefix + ".application");
String ipAddress = env.getProperty(prefix + ".ip-address");
String port = env.getProperty(prefix + ".port");
String serviceUrl = env.getProperty(prefix + ".service-url");
String namespace = env.getProperty(prefix + ".namespace", "");
String group = env.getProperty(prefix + ".group", "DEFAULT_GROUP");
String clusterName = env.getProperty(prefix + ".clusterName", "DEFAULT");
FizzNacosProperties fizzNacosProperties = new FizzNacosProperties();
fizzNacosProperties.setApplicationContext(applicationContext);
fizzNacosProperties.setId(application + ':' + serviceUrl);
fizzNacosProperties.setService(application);
fizzNacosProperties.setIp(ipAddress == null ? applicationContext.getBean(InetUtils.class).findFirstNonLoopbackAddress().getHostAddress() : ipAddress);
fizzNacosProperties.setPort(Integer.parseInt(port));
fizzNacosProperties.setNamespace(namespace.equals("") ? null : namespace);
fizzNacosProperties.setGroup(group);
fizzNacosProperties.setClusterName(clusterName);
fizzNacosProperties.setNamespace("");
fizzNacosProperties.setSecretKey("");
fizzNacosProperties.setAccessKey("");
fizzNacosProperties.setUsername("");
fizzNacosProperties.setPassword("");
fizzNacosProperties.setEndpoint("");
fizzNacosProperties.setLogName("");
fizzNacosProperties.setNamingLoadCacheAtStart("false");
fizzNacosProperties.setServerAddr(serviceUrl);
FizzNacosServiceRegistration fizzNacosServiceRegistration = FizzNacosHelper.getServiceRegistration(fizzNacosProperties);
serviceRegistry = fizzNacosServiceRegistration.serviceRegistry;
registration = fizzNacosServiceRegistration.registration;
if (nacos) {
Properties nacosProperties = new Properties();
boolean find = false;
for (PropertySource<?> propertySource : env.getPropertySources()) {
if (propertySource instanceof OriginTrackedMapPropertySource) {
OriginTrackedMapPropertySource originTrackedMapPropertySource = (OriginTrackedMapPropertySource) propertySource;
String[] propertyNames = originTrackedMapPropertySource.getPropertyNames();
for (String propertyName : propertyNames) {
if (propertyName.length() > 64) {
int naocsPos = propertyName.indexOf("nacos");
if (naocsPos > -1) {
nacosProperties.setProperty(propertyName.substring(naocsPos), originTrackedMapPropertySource.getProperty(propertyName).toString());
find = true;
}
}
}
if (find) {
break;
}
}
}
if (!find) {
log.error("no nacos config");
return;
}
fizzServiceRegistration = FizzNacosHelper.getServiceRegistration(applicationContext, nacosProperties);
}
serviceRegistry.register(registration);
fizzServiceRegistration.register();
}
}
@PreDestroy
public void stop() {
if (serviceRegistry != null) {
serviceRegistry.deregister(registration);
if (fizzServiceRegistration != null) {
fizzServiceRegistration.deregister();
}
}
}

View File

@@ -43,6 +43,7 @@ import we.plugin.auth.Receiver;
import we.proxy.CallbackService;
import we.proxy.DiscoveryClientUriSelector;
import we.proxy.ServiceInstance;
import we.service_registry.RegistryCenterService;
import we.util.Consts;
import we.util.NettyDataBufferUtils;
import we.util.ThreadContext;
@@ -75,6 +76,9 @@ public class CallbackFilter extends FizzWebFilter {
@Resource
private CallbackFilterProperties callbackFilterProperties;
@Resource
private RegistryCenterService registryCenterService;
@Resource(name = AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE)
private ReactiveStringRedisTemplate rt;
@@ -142,7 +146,14 @@ public class CallbackFilter extends FizzWebFilter {
List<Receiver> receivers = ac.callbackConfig.receivers;
for (Receiver r : receivers) {
if (r.type == ApiConfig.Type.SERVICE_DISCOVERY) {
ServiceInstance inst = discoveryClientSelector.getNextInstance(r.service);
ServiceInstance inst = null;
if (r.registryCenter == null) {
inst = discoveryClientSelector.getNextInstance(r.service);
} else {
String instance = registryCenterService.getInstance(r.registryCenter, r.service);
String[] ipAndPort = StringUtils.split(instance, Consts.S.COLON);
inst = new ServiceInstance(ipAndPort[0], Integer.parseInt(ipAndPort[1]));
}
service2instMap.put(r.service, inst);
}
}

View File

@@ -115,7 +115,11 @@ public class RouteFilter extends FizzWebFilter {
} else if (route.type == ApiConfig.Type.SERVICE_DISCOVERY) {
String pathQuery = getBackendPathQuery(req, route);
return fizzWebClient.send2service(traceId, route.method, route.backendService, pathQuery, hdrs, req.getBody(), route.timeout, route.retryCount, route.retryInterval)
String svc = route.backendService;
if (route.registryCenter != null) {
svc = route.registryCenter + Consts.S.COMMA + route.backendService;
}
return fizzWebClient.send2service(traceId, route.method, svc, pathQuery, hdrs, req.getBody(), route.timeout, route.retryCount, route.retryInterval)
.flatMap(genServerResponse(exchange));
} else if (route.type == ApiConfig.Type.REVERSE_PROXY) {

View File

@@ -25,6 +25,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import we.plugin.PluginConfig;
import we.proxy.Route;
import we.util.Consts;
import we.util.JacksonUtils;
import we.util.UrlTransformUtils;
@@ -71,6 +72,8 @@ public class ApiConfig {
public String service;
public String registryCenter;
public String backendService;
public Object fizzMethod = ALL_METHOD;
@@ -141,6 +144,17 @@ public class ApiConfig {
firstGatewayGroup = gatewayGroups.iterator().next();
}
@JsonProperty("registryName")
public void setRegistryCenter(String rc) {
if (StringUtils.isNotBlank(rc)) {
if (rc.equals(Consts.S.DEFAULT)) {
registryCenter = Consts.S.DEFAULT;
} else {
registryCenter = rc;
}
}
}
public void setPath(String p) {
if (StringUtils.isNotBlank(p)) {
if ("/".equals(p)) {
@@ -212,6 +226,7 @@ public class ApiConfig {
Route r = new Route().dedicatedLine( this.dedicatedLine)
.type( this.type)
.method( request.getMethod())
.registryCenter( this.registryCenter)
.backendService( this.backendService)
.backendPath( this.backendPath)
.rpcMethod( this.rpcMethod)

View File

@@ -17,6 +17,9 @@
package we.plugin.auth;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import we.util.Consts;
import we.util.JacksonUtils;
/**
@@ -27,10 +30,23 @@ public class Receiver {
public String service;
public String registryCenter;
public int type;
public String path;
@JsonProperty("registryName")
public void setRegistryCenter(String rc) {
if (StringUtils.isNotBlank(rc)) {
if (rc.equals(Consts.S.DEFAULT)) {
registryCenter = Consts.S.DEFAULT;
} else {
registryCenter = rc;
}
}
}
@Override
public String toString() {
return JacksonUtils.writeValueAsString(this);

View File

@@ -242,7 +242,13 @@ public class CallbackService {
for (ServiceTypePath stp : req.assignServices) {
if (stp.type == ApiConfig.Type.SERVICE_DISCOVERY) {
send = fizzWebClient.send2service(req.id, req.method, stp.service, stp.path, req.headers, req.body)
String svc = null;
if (stp.registryCenter == null) {
svc = stp.service;
} else {
svc = stp.registryCenter + Consts.S.COMMA + stp.service;
}
send = fizzWebClient.send2service(req.id, req.method, svc, stp.path, req.headers, req.body)
.onErrorResume( crError(req, stp.service, stp.path) );
} else {
String traceId = CommonConstants.TRACE_ID_PREFIX + req.id;

View File

@@ -17,12 +17,6 @@
package we.proxy;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +29,6 @@ import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;
@@ -44,10 +37,16 @@ import we.config.SystemConfig;
import we.exception.ExternalService4xxException;
import we.fizz.exception.FizzRuntimeException;
import we.flume.clients.log4j2appender.LogService;
import we.service_registry.RegistryCenterService;
import we.util.Consts;
import we.util.ThreadContext;
import we.util.WebUtils;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
/**
* @author hongqiaowei
*/
@@ -67,6 +66,9 @@ public class FizzWebClient {
@Resource
private DiscoveryClientUriSelector discoveryClientUriSelector;
@Resource
private RegistryCenterService registryCenterService;
@Resource(name = ProxyWebClientConfig.proxyWebClient)
private WebClient webClient;
@@ -85,7 +87,16 @@ public class FizzWebClient {
Mono<ClientResponse> cr = Mono.just(Consts.S.EMPTY).flatMap(dummy -> {
if (isService(s)) {
String path = uriOrSvc.substring(uriOrSvc.indexOf(Consts.S.FORWARD_SLASH, 10));
String uri = discoveryClientUriSelector.getNextUri(s, path);
String uri = null;
int commaPos = s.indexOf(Consts.S.COMMA);
if (commaPos > -1) {
String rc = s.substring(0, commaPos);
String svc = s.substring(commaPos + 1);
String instance = registryCenterService.getInstance(rc, svc);
uri = ThreadContext.getStringBuilder().append(Consts.S.HTTP_PROTOCOL_PREFIX).append(instance).append(path).toString();
} else {
uri = discoveryClientUriSelector.getNextUri(s, path);
}
return send2uri(traceId, method, uri, headers, body, timeout);
} else {
return send2uri(traceId, method, uriOrSvc, headers, body, timeout);
@@ -123,7 +134,16 @@ public class FizzWebClient {
long timeout, long numRetries, long retryInterval) {
Mono<ClientResponse> cr = Mono.just(Consts.S.EMPTY).flatMap(dummy -> {
String uri = discoveryClientUriSelector.getNextUri(service, relativeUri);
String uri = null;
int commaPos = service.indexOf(Consts.S.COMMA);
if (commaPos > -1) {
String rc = service.substring(0, commaPos);
String s = service.substring(commaPos + 1);
String instance = registryCenterService.getInstance(rc, s);
uri = ThreadContext.getStringBuilder().append(Consts.S.HTTP_PROTOCOL_PREFIX).append(instance).append(relativeUri).toString();
} else {
uri = discoveryClientUriSelector.getNextUri(service, relativeUri);
}
return send2uri(traceId, method, uri, headers, body, timeout);
});
if (numRetries > 0) {

View File

@@ -36,6 +36,8 @@ public class Route {
public HttpMethod method;
public String registryCenter;
public String backendService;
public String backendPath;
@@ -81,6 +83,11 @@ public class Route {
return this;
}
public Route registryCenter(String rc) {
registryCenter = rc;
return this;
}
public Route backendService(String bs) {
backendService = bs;
return this;

View File

@@ -17,6 +17,9 @@
package we.proxy;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import we.util.Consts;
import we.util.JacksonUtils;
/**
@@ -25,12 +28,25 @@ import we.util.JacksonUtils;
public class ServiceTypePath {
public String registryCenter;
public String service;
public String path;
public int type;
@JsonProperty("registryName")
public void setRegistryCenter(String rc) {
if (StringUtils.isNotBlank(rc)) {
if (rc.equals(Consts.S.DEFAULT)) {
registryCenter = Consts.S.DEFAULT;
} else {
registryCenter = rc;
}
}
}
@Override
public String toString() {
return JacksonUtils.writeValueAsString(this);

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2020 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package we.service_registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
/**
* @author hongqiaowei
*/
public abstract class FizzServiceRegistration {
protected static final Logger log = LoggerFactory.getLogger(FizzServiceRegistration.class);
protected String id;
private Registration registration;
private ServiceRegistry serviceRegistry;
public FizzServiceRegistration(String id, Registration registration, ServiceRegistry serviceRegistry) {
this.id = id;
this.registration = registration;
this.serviceRegistry = serviceRegistry;
}
public void register() {
serviceRegistry.register(registration);
}
public void deregister() {
serviceRegistry.deregister(registration);
}
public abstract RegistryCenter.Status getRegistryCenterStatus();
public abstract String getInstance(String service);
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2020 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package we.service_registry;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import we.Fizz;
import we.service_registry.eureka.FizzEurekaHelper;
import we.service_registry.nacos.FizzNacosHelper;
import we.util.JacksonUtils;
import we.util.YmlUtils;
import java.io.IOException;
import java.util.Properties;
/**
* @author hongqiaowei
*/
public class RegistryCenter {
public enum Status {
UP,
DOWN,
STARTING,
OUT_OF_SERVICE,
UNKNOWN;
}
public static final int EUREKA = 1;
public static final int NACOS = 2;
public static final int YML = 1;
public static final int PROPERTIES = 2;
@JsonProperty(
access = JsonProperty.Access.WRITE_ONLY
)
public boolean isDeleted = false;
public long id;
public String name;
public int type;
public int clientConfigFormat;
public String clientConfig;
private FizzServiceRegistration fizzServiceRegistration;
@JsonCreator
public RegistryCenter(
@JsonProperty("isDeleted") int isDeleted,
@JsonProperty("id") long id,
@JsonProperty("name") String name,
@JsonProperty("type") int type,
@JsonProperty("format") int clientConfigFormat,
@JsonProperty("content") String clientConfig
) {
if (isDeleted == 1) {
this.isDeleted = true;
}
this.id = id;
this.name = name;
this.type = type;
this.clientConfigFormat = clientConfigFormat;
this.clientConfig = clientConfig;
}
@JsonIgnore
public FizzServiceRegistration getFizzServiceRegistration() {
if (fizzServiceRegistration == null) {
Properties properties;
if (this.clientConfigFormat == YML) {
properties = YmlUtils.string2properties(clientConfig);
} else {
Resource resource = new ByteArrayResource(clientConfig.getBytes());
try {
properties = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (type == EUREKA) {
fizzServiceRegistration = FizzEurekaHelper.getServiceRegistration(Fizz.context, properties);
} else {
fizzServiceRegistration = FizzNacosHelper.getServiceRegistration(Fizz.context, properties);
}
}
return fizzServiceRegistration;
}
@JsonIgnore
public String getInstance(String service) {
return fizzServiceRegistration.getInstance(service);
}
@Override
public String toString() {
return JacksonUtils.writeValueAsString(this);
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2020 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package we.service_registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import we.config.AggregateRedisConfig;
import we.util.JacksonUtils;
import we.util.Result;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* @author hongqiaowei
*/
@Service
public class RegistryCenterService {
private static final Logger log = LoggerFactory.getLogger(RegistryCenterService.class);
private Map<String, RegistryCenter> registryCenterMap = new HashMap<>();
@Resource(name = AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE)
private ReactiveStringRedisTemplate rt;
@PostConstruct
public void init() {
Result<?> result = initRegistryCenter();
if (result.code == Result.FAIL) {
throw new RuntimeException(result.msg, result.t);
}
result = lsnRegistryCenterChange();
if (result.code == Result.FAIL) {
throw new RuntimeException(result.msg, result.t);
}
}
private Result<?> initRegistryCenter() {
Result<?> result = Result.succ();
Flux<Map.Entry<Object, Object>> registryCenterEntries = rt.opsForHash().entries("fizz_registry");
registryCenterEntries.collectList()
.defaultIfEmpty(Collections.emptyList())
.flatMap(
es -> {
if (!es.isEmpty()) {
String json = null;
try {
for (Map.Entry<Object, Object> e : es) {
json = (String) e.getValue();
RegistryCenter rc = JacksonUtils.readValue(json, RegistryCenter.class);
registryCenterMap.put(rc.name, rc);
log.info("init registry center {}", rc.name);
}
} catch (Throwable t) {
result.code = Result.FAIL;
result.msg = "init registry center error, json: " + json;
result.t = t;
}
} else {
log.info("no registry center");
}
return Mono.empty();
}
)
.onErrorReturn(
throwable -> {
result.code = Result.FAIL;
result.msg = "init registry center error";
result.t = throwable;
return true;
},
result
)
.block();
return result;
}
private Result<?> lsnRegistryCenterChange() {
Result<?> result = Result.succ();
String channel = "fizz_registry_channel";
rt.listenToChannel(channel)
.doOnError(
t -> {
result.code = Result.FAIL;
result.msg = "lsn error, channel: " + channel;
result.t = t;
log.error("lsn channel {} error", channel, t);
}
)
.doOnSubscribe(
s -> {
log.info("success to lsn on {}", channel);
}
)
.doOnNext(
msg -> {
String message = msg.getMessage();
try {
RegistryCenter rc = JacksonUtils.readValue(message, RegistryCenter.class);
if (rc.isDeleted) {
registryCenterMap.remove(rc.name);
log.info("remove registry center {}", rc.name);
} else {
registryCenterMap.put(rc.name, rc);
log.info("update registry center {}", rc.name);
}
} catch (Throwable t) {
log.error("update registry center error, {}", message, t);
}
}
)
.subscribe();
return result;
}
public RegistryCenter getRegistryCenter(String name) {
return registryCenterMap.get(name);
}
public String getInstance(String registryCenter, String service) {
return registryCenterMap.get(registryCenter).getInstance(service);
}
}

View File

@@ -20,7 +20,9 @@ package we.service_registry.eureka;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtilsProperties;
import org.springframework.cloud.loadbalancer.support.SimpleObjectProvider;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
@@ -28,10 +30,13 @@ import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.eureka.InstanceInfoFactory;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;
import org.springframework.context.ApplicationContext;
import we.util.Consts;
import we.util.PropertiesUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author hongqiaowei
@@ -39,61 +44,88 @@ import java.util.Map;
public abstract class FizzEurekaHelper {
private static final int el = "eureka." .length();
private static final int ecl = "eureka.client." .length();
private static final int eil = "eureka.instance." .length();
private FizzEurekaHelper() {
}
public static FizzEurekaServiceRegistration getServiceRegistration(FizzEurekaProperties fizzEurekaProperties) {
public static FizzEurekaServiceRegistration getServiceRegistration(ApplicationContext applicationContext, Properties eurekaProperties) {
InetUtils inetUtils = fizzEurekaProperties.applicationContext.getBean(InetUtils.class);
EurekaInstanceConfigBean eurekaInstanceConfig = new EurekaInstanceConfigBean(inetUtils);
eurekaInstanceConfig.setAppname(fizzEurekaProperties.appName);
eurekaInstanceConfig.setVirtualHostName(fizzEurekaProperties.getVirtualHostName());
eurekaInstanceConfig.setNonSecurePort(fizzEurekaProperties.nonSecurePort);
String ip = fizzEurekaProperties.ipAddress;
String instanceId;
if (ip == null) {
ip = inetUtils.findFirstNonLoopbackAddress().getHostAddress();
instanceId = ip + ':' + fizzEurekaProperties.appName + ':' + fizzEurekaProperties.nonSecurePort;
Properties eurekaProps = new Properties();
for (String propertyName : eurekaProperties.stringPropertyNames()) {
String pn = null;
if (propertyName.charAt(ecl - 1) == Consts.S.DOT) {
pn = propertyName.substring(ecl);
} else if (propertyName.charAt(eil - 1) == Consts.S.DOT) {
pn = propertyName.substring(eil);
} else {
instanceId = ip + ':' + fizzEurekaProperties.appName + ':' + fizzEurekaProperties.nonSecurePort;
fizzEurekaProperties.instanceId(instanceId);
pn = propertyName.substring(el);
}
if (pn.indexOf(Consts.S.DASH) > -1) {
pn = PropertiesUtils.normalize(pn);
}
eurekaProps.setProperty(pn, eurekaProperties.getProperty(propertyName));
}
eurekaInstanceConfig.setIpAddress(ip);
eurekaInstanceConfig.setInstanceId(instanceId);
eurekaInstanceConfig.setPreferIpAddress(fizzEurekaProperties.preferIpAddress);
eurekaInstanceConfig.setSecurePortEnabled(fizzEurekaProperties.securePortEnabled);
String healthCheckUrl = fizzEurekaProperties.getHealthCheckUrl();
if (healthCheckUrl != null) {
eurekaInstanceConfig.setHealthCheckUrl(healthCheckUrl);
InetUtils inetUtils = null;
try {
inetUtils = applicationContext.getBean(InetUtils.class);
} catch (NoSuchBeanDefinitionException e) {
inetUtils = new InetUtils(new InetUtilsProperties());
}
EurekaInstanceConfigBean eurekaInstanceConfig = new EurekaInstanceConfigBean(inetUtils);
PropertiesUtils.setBeanPropertyValue(eurekaInstanceConfig, eurekaProps);
String appname = eurekaInstanceConfig.getAppname();
if (appname == null) {
appname = applicationContext.getApplicationName();
eurekaInstanceConfig.setAppname(appname);
}
// VirtualHostName
String virtualHostName = eurekaInstanceConfig.getVirtualHostName();
if (virtualHostName.equals("unknown")) {
eurekaInstanceConfig.setVirtualHostName(appname);
}
String serverPort = eurekaProps.getProperty("serverPort");
if (serverPort == null) {
serverPort = applicationContext.getEnvironment().getProperty("server.port");
}
assert serverPort != null;
eurekaInstanceConfig.setNonSecurePort(Integer.parseInt(serverPort));
String ipAddress = eurekaInstanceConfig.getIpAddress();
if (ipAddress == null) {
ipAddress = inetUtils.findFirstNonLoopbackAddress().getHostAddress();
eurekaInstanceConfig.setIpAddress(ipAddress);
}
String instanceId = eurekaInstanceConfig.getInstanceId();
if (instanceId == null) {
eurekaInstanceConfig.setInstanceId(ipAddress + ':' + appname + ':' + serverPort);
}
eurekaInstanceConfig.setDataCenterInfo(fizzEurekaProperties.dataCenterInfo);
InstanceInfo instanceInfo = new InstanceInfoFactory().create(eurekaInstanceConfig);
ApplicationInfoManager applicationInfoManager = new ApplicationInfoManager(eurekaInstanceConfig, instanceInfo);
EurekaClientConfigBean eurekaClientConfig = new EurekaClientConfigBean();
eurekaClientConfig.setRegion(fizzEurekaProperties.region);
Map<String, String> serviceUrlMap = new HashMap<>();
serviceUrlMap.put(fizzEurekaProperties.zone, fizzEurekaProperties.serviceUrl);
eurekaClientConfig.setServiceUrl(serviceUrlMap);
Map<String, Class<?>> propertyTypeHint = new HashMap<>();
propertyTypeHint.put("serviceUrl", Map.class);
PropertiesUtils.setBeanPropertyValue(eurekaClientConfig, eurekaProps, propertyTypeHint);
CloudEurekaClient eurekaClient = new CloudEurekaClient(applicationInfoManager, eurekaClientConfig, null, fizzEurekaProperties.applicationContext);
CloudEurekaClient eurekaClient = new CloudEurekaClient(applicationInfoManager, eurekaClientConfig, null, applicationContext);
SimpleObjectProvider<HealthCheckHandler> healthCheckHandler = new SimpleObjectProvider<>(null);
EurekaRegistration eurekaRegistration = EurekaRegistration.builder(eurekaInstanceConfig).with(applicationInfoManager).with(healthCheckHandler).with(eurekaClient).build();
EurekaServiceRegistry serviceRegistry = new EurekaServiceRegistry();
return new FizzEurekaServiceRegistration(fizzEurekaProperties.getId(), eurekaRegistration, serviceRegistry, eurekaClient);
String registerCenter = eurekaProps.getProperty("register-center");
if (registerCenter == null) {
registerCenter = eurekaClientConfig.getServiceUrl().get(EurekaClientConfigBean.DEFAULT_ZONE);
}
public static Map<String, FizzEurekaServiceRegistration> getServiceRegistration(List<FizzEurekaProperties> fizzEurekaPropertiesList) {
Map<String, FizzEurekaServiceRegistration> result = new HashMap<>();
for (FizzEurekaProperties properties : fizzEurekaPropertiesList) {
FizzEurekaServiceRegistration fizzEurekaServiceRegistration = getServiceRegistration(properties);
result.put(properties.getId(), fizzEurekaServiceRegistration);
}
return result;
return new FizzEurekaServiceRegistration(registerCenter, eurekaRegistration, serviceRegistry, eurekaClient);
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright (C) 2020 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package we.service_registry.eureka;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.MyDataCenterInfo;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author hongqiaowei
*/
public class FizzEurekaProperties {
public ConfigurableApplicationContext applicationContext;
private String id;
public String appName;
private String virtualHostName;
public String ipAddress;
public int nonSecurePort = 80;
private String instanceId;
public boolean preferIpAddress = true;
public boolean securePortEnabled = false;
private String healthCheckUrl;
public DataCenterInfo dataCenterInfo = new MyDataCenterInfo(
DataCenterInfo.Name.MyOwn);
public String region = "default";
public String zone = EurekaClientConfigBean.DEFAULT_ZONE;
public String serviceUrl;
public int securePort = 443;
public FizzEurekaProperties applicationContext(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
return this;
}
public FizzEurekaProperties id(String id) {
this.id = id;
return this;
}
public String getId() {
if (id == null) {
id = appName + ':' + serviceUrl;
}
return id;
}
public FizzEurekaProperties appName(String appName) {
this.appName = appName;
return this;
}
public FizzEurekaProperties virtualHostName(String virtualHostName) {
this.virtualHostName = virtualHostName;
return this;
}
public String getVirtualHostName() {
if (virtualHostName == null) {
virtualHostName = appName;
}
return virtualHostName;
}
public FizzEurekaProperties ipAddress(String ipAddress) {
this.ipAddress = ipAddress;
return this;
}
public FizzEurekaProperties nonSecurePort(int nonSecurePort) {
this.nonSecurePort = nonSecurePort;
return this;
}
public FizzEurekaProperties instanceId(String instanceId) {
this.instanceId = instanceId;
return this;
}
public String getInstanceId() {
/*if (instanceId == null) {
instanceId = ipAddress + ':' + appName + ':' + nonSecurePort;
}*/
return instanceId;
}
public FizzEurekaProperties preferIpAddress(boolean preferIpAddress) {
this.preferIpAddress = preferIpAddress;
return this;
}
public FizzEurekaProperties securePortEnabled(boolean securePortEnabled) {
this.securePortEnabled = securePortEnabled;
return this;
}
public FizzEurekaProperties healthCheckUrl(String healthCheckUrl) {
this.healthCheckUrl = healthCheckUrl;
return this;
}
public String getHealthCheckUrl() {
/*if (healthCheckUrl == null) {
healthCheckUrl = "http://" + ipAddress + ':' + nonSecurePort + "/actuator/info";
}*/
return healthCheckUrl;
}
public FizzEurekaProperties region(String region) {
this.region = region;
return this;
}
public FizzEurekaProperties zone(String zone) {
this.zone = zone;
return this;
}
public FizzEurekaProperties serviceUrl(String serviceUrl) {
this.serviceUrl = serviceUrl;
return this;
}
public FizzEurekaProperties securePort(int securePort) {
this.securePort = securePort;
return this;
}
}

View File

@@ -17,28 +17,122 @@
package we.service_registry.eureka;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;
import org.springframework.util.CollectionUtils;
import we.service_registry.FizzServiceRegistration;
import we.service_registry.RegistryCenter;
import we.util.Consts;
import we.util.Utils;
import java.util.List;
/**
* @author hongqiaowei
*/
public class FizzEurekaServiceRegistration {
public class FizzEurekaServiceRegistration extends FizzServiceRegistration {
public String id;
public EurekaRegistration registration;
public EurekaServiceRegistry serviceRegistry;
public CloudEurekaClient client;
private final CloudEurekaClient client;
public FizzEurekaServiceRegistration(String id, EurekaRegistration registration, EurekaServiceRegistry serviceRegistry, CloudEurekaClient client) {
this.id = id;
this.registration = registration;
this.serviceRegistry = serviceRegistry;
super(id, registration, serviceRegistry);
this.client = client;
}
public DiscoveryClient getDiscoveryClient() {
return client;
}
@Override
public RegistryCenter.Status getRegistryCenterStatus() {
EurekaClientConfig eurekaClientConfig = client.getEurekaClientConfig();
List<String> eurekaServerServiceUrls = eurekaClientConfig.getEurekaServerServiceUrls(EurekaClientConfigBean.DEFAULT_ZONE);
boolean f = false;
for (String serviceUrl : eurekaServerServiceUrls) {
String vip;
int port;
int begin = serviceUrl.indexOf('p') + 4;
int colon = serviceUrl.indexOf(':', begin);
if (colon > -1) {
int end = serviceUrl.indexOf('/', colon);
vip = serviceUrl.substring(begin, colon);
port = Integer.parseInt(serviceUrl.substring(colon + 1, end));
} else {
int end = serviceUrl.indexOf('/', begin);
vip = serviceUrl.substring(begin, end);
port = 80;
}
Applications applications = client.getApplications(serviceUrl);
for (Application registeredApplication : applications.getRegisteredApplications()) {
List<InstanceInfo> instances = registeredApplication.getInstances();
for (InstanceInfo instance : instances) {
String vipAddress = instance.getVIPAddress();
String ipAddr = instance.getIPAddr();
if (vipAddress.equals(vip) || ipAddr.equals(vip)) {
int p = instance.getPort();
if (p == port) {
f = true;
break;
}
}
}
if (f) {
for (InstanceInfo instance : instances) {
InstanceInfo.InstanceStatus status = instance.getStatus();
if (status != InstanceInfo.InstanceStatus.UP) {
return transfrom(status);
}
}
return transfrom(InstanceInfo.InstanceStatus.UP);
}
}
}
String join = StringUtils.join(eurekaServerServiceUrls, ',');
throw Utils.runtimeExceptionWithoutStack("can't find any server with " + join);
}
private RegistryCenter.Status transfrom(InstanceInfo.InstanceStatus status) {
if ( status == InstanceInfo.InstanceStatus.UP) {
return RegistryCenter.Status.UP;
} else if (status == InstanceInfo.InstanceStatus.DOWN) {
return RegistryCenter.Status.DOWN;
} else if (status == InstanceInfo.InstanceStatus.OUT_OF_SERVICE) {
return RegistryCenter.Status.OUT_OF_SERVICE;
} else if (status == InstanceInfo.InstanceStatus.STARTING) {
return RegistryCenter.Status.STARTING;
} else {
return RegistryCenter.Status.UNKNOWN;
}
}
@Override
public String getInstance(String service) {
InstanceInfo inst = getInstanceInfo(service);
return inst.getIPAddr() + Consts.S.COLON + inst.getPort();
}
public InstanceInfo getInstanceInfo(String service) {
List<InstanceInfo> insts = client.getInstancesByVipAddress(service, false);
if (CollectionUtils.isEmpty(insts)) {
throw Utils.runtimeExceptionWithoutStack(id + " eureka no " + service);
}
Applications apps = client.getApplications();
int index = (int) (apps.getNextIndex(service.toUpperCase(), false).incrementAndGet() % insts.size());
return insts.get(index);
}
}

View File

@@ -21,11 +21,13 @@ import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import com.alibaba.nacos.api.naming.NamingService;
import org.springframework.context.ApplicationContext;
import we.util.Consts;
import we.util.JacksonUtils;
import we.util.PropertiesUtils;
import we.util.ReflectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author hongqiaowei
@@ -33,25 +35,44 @@ import java.util.Map;
public abstract class FizzNacosHelper {
private static final int ndl = "nacos.discovery.".length();
private FizzNacosHelper() {
}
public static FizzNacosServiceRegistration getServiceRegistration(FizzNacosProperties fizzNacosProperties) {
public static FizzNacosServiceRegistration getServiceRegistration(ApplicationContext applicationContext, Properties nacosProperties) {
Properties ps = new Properties();
for (String propertyName : nacosProperties.stringPropertyNames()) {
String pn = propertyName.substring(ndl);
if (pn.indexOf(Consts.S.DASH) > -1) {
pn = PropertiesUtils.normalize(pn);
}
ps.setProperty(pn, nacosProperties.getProperty(propertyName));
}
FizzNacosProperties fizzNacosProperties = new FizzNacosProperties();
PropertiesUtils.setBeanPropertyValue(fizzNacosProperties, ps);
fizzNacosProperties.setApplicationContext(applicationContext);
if (fizzNacosProperties.getId() == null) {
fizzNacosProperties.setId(fizzNacosProperties.getServerAddr());
}
if (fizzNacosProperties.getService() == null) {
fizzNacosProperties.setService(applicationContext.getApplicationName());
}
if (fizzNacosProperties.getPort() == -1) {
fizzNacosProperties.setPort(Integer.parseInt(applicationContext.getEnvironment().getProperty("server.port")));
}
fizzNacosProperties.setNamingLoadCacheAtStart("false");
fizzNacosProperties.init();
NacosServiceRegistry nacosServiceRegistry = new NacosServiceRegistry(fizzNacosProperties);
NacosServiceManager nacosServiceManager = new NacosServiceManager();
ReflectionUtils.set(nacosServiceRegistry, "nacosServiceManager", nacosServiceManager);
NacosRegistration nacosRegistration = new NacosRegistration(null, fizzNacosProperties, fizzNacosProperties.getApplicationContext());
NacosRegistration nacosRegistration = new NacosRegistration(null, fizzNacosProperties, applicationContext);
NamingService namingService = nacosServiceManager.getNamingService(fizzNacosProperties.getNacosProperties());
return new FizzNacosServiceRegistration(fizzNacosProperties.getId(), nacosRegistration, nacosServiceRegistry, namingService);
}
public static Map<String, FizzNacosServiceRegistration> getServiceRegistration(List<FizzNacosProperties> fizzNacosPropertiesList) {
Map<String, FizzNacosServiceRegistration> result = new HashMap<>();
for (FizzNacosProperties properties : fizzNacosPropertiesList) {
FizzNacosServiceRegistration fizzNacosServiceRegistration = getServiceRegistration(properties);
result.put(properties.getId(), fizzNacosServiceRegistration);
}
return result;
}
}

View File

@@ -21,6 +21,7 @@ import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.PreservedMetadataKeys;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils;
@@ -38,23 +39,23 @@ import static com.alibaba.nacos.api.PropertyKeyConst.*;
public class FizzNacosProperties extends NacosDiscoveryProperties {
private ConfigurableApplicationContext applicationContext;
private ApplicationContext applicationContext;
private String id;
private String serverAddr;
private String username;
private String username = "";
private String password;
private String password = "";
private String endpoint;
private String endpoint = "";
private String namespace;
private long watchDelay = 30000;
private String logName;
private String logName = "";
private String service;
@@ -78,9 +79,9 @@ public class FizzNacosProperties extends NacosDiscoveryProperties {
private boolean secure = false;
private String accessKey;
private String accessKey = "";
private String secretKey;
private String secretKey = "";
private Integer heartBeatInterval;
@@ -143,11 +144,11 @@ public class FizzNacosProperties extends NacosDiscoveryProperties {
init = true;
}
public void setApplicationContext(ConfigurableApplicationContext applicationContext) {
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public ConfigurableApplicationContext getApplicationContext() {
public ApplicationContext getApplicationContext() {
return applicationContext;
}

View File

@@ -17,28 +17,99 @@
package we.service_registry.nacos;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.springframework.util.StringUtils;
import we.service_registry.FizzServiceRegistration;
import we.service_registry.RegistryCenter;
import we.util.Consts;
import we.util.Utils;
import java.util.Collections;
import java.util.List;
/**
* @author hongqiaowei
*/
public class FizzNacosServiceRegistration {
public class FizzNacosServiceRegistration extends FizzServiceRegistration {
public String id;
private NamingService namingService;
public NacosRegistration registration;
private final String groupName;
public NacosServiceRegistry serviceRegistry;
private List<String> clusterNameList;
public NamingService namingService;
private boolean useGroupName;
private boolean userClusterName;
public FizzNacosServiceRegistration(String id, NacosRegistration registration, NacosServiceRegistry serviceRegistry, NamingService namingService) {
this.id = id;
this.registration = registration;
this.serviceRegistry = serviceRegistry;
super(id, registration, serviceRegistry);
this.namingService = namingService;
NacosDiscoveryProperties discoveryProperties = registration.getNacosDiscoveryProperties();
groupName = discoveryProperties.getGroup();
if (StringUtils.hasText(groupName)) {
useGroupName = true;
}
String clusterName = discoveryProperties.getClusterName();
if (StringUtils.hasText(clusterName)) {
userClusterName = true;
clusterNameList = Collections.singletonList(clusterName);
}
}
public NamingService getNamingService() {
return namingService;
}
@Override
public RegistryCenter.Status getRegistryCenterStatus() {
String status = namingService.getServerStatus();
return transfrom(status);
}
private RegistryCenter.Status transfrom(String status) {
if (status.equals("UP")) {
return RegistryCenter.Status.UP;
} else if (status.equals("DOWN")) {
return RegistryCenter.Status.DOWN;
} else {
log.warn("{} status is {}", id, status);
return RegistryCenter.Status.UNKNOWN;
}
}
@Override
public String getInstance(String service) {
Instance instance = getInstanceInfo(service);
return instance.getIp() + Consts.S.COLON + instance.getPort();
}
public Instance getInstanceInfo(String service) {
Instance instance = null;
try {
if (useGroupName && userClusterName) {
instance = namingService.selectOneHealthyInstance(service, groupName, clusterNameList);
} else if (useGroupName) {
instance = namingService.selectOneHealthyInstance(service, groupName);
} else if (userClusterName) {
instance = namingService.selectOneHealthyInstance(service, clusterNameList);
} else {
instance = namingService.selectOneHealthyInstance(service);
}
} catch (NacosException e) {
throw new RuntimeException(e);
}
if (instance == null) {
throw Utils.runtimeExceptionWithoutStack(id + " nacos no " + service);
}
return instance;
}
}

View File

@@ -0,0 +1,63 @@
package we.service_registry;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import we.Fizz;
import we.redis.RedisProperties;
import we.redis.RedisServerConfiguration;
import we.redis.RedisTemplateConfiguration;
import we.service_registry.eureka.FizzEurekaServiceRegistration;
import we.util.ReflectionUtils;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* @author hongqiaowei
*/
@TestPropertySource("/application.properties")
@SpringJUnitConfig(classes = {RedisProperties.class, RedisTemplateConfiguration.class, RedisServerConfiguration.class})
public class RegistryCenterServiceTests {
@Resource
StringRedisTemplate stringRedisTemplate;
@Resource
ReactiveStringRedisTemplate reactiveStringRedisTemplate;
RegistryCenterService registryCenterService;
// @BeforeEach
void beforeEach() throws NoSuchFieldException {
registryCenterService = new RegistryCenterService();
ReflectionUtils.set(registryCenterService, "rt", reactiveStringRedisTemplate);
}
// @Test
void initTest() throws Throwable {
System.setProperty("server.port", "8866");
Fizz.context = new GenericApplicationContext();
Fizz.context.refresh();
Map<String, String> registryCenterServiceMap = new HashMap<>();
String yml = FileUtil.readString("eureka.yml", CharsetUtil.CHARSET_UTF_8);
registryCenterServiceMap.put("1", "{\"id\":1,\"name\":\"default\",\"type\":1,\"format\":1,\"content\":\"" + yml + "\",\"isDeleted\":0}");
stringRedisTemplate.opsForHash().putAll("fizz_registry", registryCenterServiceMap);
registryCenterService.init();
RegistryCenter def = registryCenterService.getRegistryCenter("default");
FizzServiceRegistration fizzServiceRegistration = def.getFizzServiceRegistration();
fizzServiceRegistration.register();
// Thread.currentThread().join();
}
}

View File

@@ -0,0 +1,30 @@
package we.util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class YmlUtilsTest {
@Test
void config2bean() throws IOException {
String s = FileUtil.readString("eureka.yml", CharsetUtil.CHARSET_UTF_8);
Properties properties = YmlUtils.string2properties(s);
// System.err.println("properties: \n" + properties);
properties = PropertiesUtils.remove(properties, "eureka.client");
EurekaClientConfigBean eurekaClientConfig = new EurekaClientConfigBean();
Map<String, Class<?>> propertyTypeHint = new HashMap<>();
propertyTypeHint.put("serviceUrl", Map.class);
PropertiesUtils.setBeanPropertyValue(eurekaClientConfig, properties, propertyTypeHint);
// System.err.println(JacksonUtils.writeValueAsString(eurekaClientConfig));
// ClassPathResource resource = new ClassPathResource("application.properties");
// properties = PropertiesLoaderUtils.loadProperties(resource);
}
}

View File

@@ -0,0 +1,10 @@
eureka:
client:
enabled: true
serviceUrl:
defaultZone: http://6.6.6.6:6600/eureka,http://8.8.8.8:6600/eureka
instance:
appname: abc
prefer-ip-address: true
lease-renewal-interval-in-seconds: 66

View File

@@ -179,6 +179,12 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<exclusions>
<exclusion>
<artifactId>netty-tcnative-classes</artifactId>
<groupId>io.netty</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -218,6 +224,10 @@
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<artifactId>netty-tcnative-classes</artifactId>
<groupId>io.netty</groupId>
</exclusion>
</exclusions>
</dependency>
@@ -245,6 +255,12 @@
<dependency>
<groupId>com.fizzgate</groupId>
<artifactId>fizz-core</artifactId>
<exclusions>
<exclusion>
<artifactId>netty-tcnative-classes</artifactId>
<groupId>io.netty</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fizzgate</groupId>

16
pom.xml
View File

@@ -6,13 +6,13 @@
<properties>
<!--<java.version>1.8</java.version>-->
<spring-boot.version>2.2.13.RELEASE</spring-boot.version>
<spring-framework.version>5.2.18.RELEASE</spring-framework.version>
<spring-framework.version>5.2.19.RELEASE</spring-framework.version>
<reactor-bom.version>Dysprosium-SR25</reactor-bom.version>
<lettuce.version>5.3.7.RELEASE</lettuce.version>
<nacos.cloud.version>2.2.6.RELEASE</nacos.cloud.version>
<netty.version>4.1.70.Final</netty.version>
<httpcore.version>4.4.14</httpcore.version>
<log4j2.version>2.14.1</log4j2.version>
<netty.version>4.1.72.Final</netty.version>
<httpcore.version>4.4.15</httpcore.version>
<log4j2.version>2.17.0</log4j2.version>
<slf4j.version>1.7.32</slf4j.version>
<apache.dubbo.version>2.7.5</apache.dubbo.version>
<grpc.version>1.16.1</grpc.version>
@@ -25,6 +25,7 @@
<netty-tcnative.version>2.0.46.Final</netty-tcnative.version>
<spring-cloud.version>2.2.9.RELEASE</spring-cloud.version>
<resilience4j.version>1.7.1</resilience4j.version>
<snakeyaml.version>1.30</snakeyaml.version>
</properties>
<parent>
@@ -433,11 +434,16 @@
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${netty-tcnative.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-classes</artifactId>
<version>${netty-tcnative.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.7.16</version>
<version>5.7.17</version>
</dependency>
</dependencies>
</dependencyManagement>