[feat] 初始化数据中台框架_02
This commit is contained in:
parent
5775e932a1
commit
ee5cc55a76
@ -1,67 +0,0 @@
|
||||
package com.czsj.rpc.registry;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: service registry
|
||||
*
|
||||
* /xxl-rpc/dev/
|
||||
* - key01(service01)
|
||||
* - value01 (ip:port01)
|
||||
* - value02 (ip:port02)
|
||||
**/
|
||||
public abstract class ServiceRegistry {
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public abstract void start(Map<String, String> param);
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public abstract void stop();
|
||||
|
||||
|
||||
/**
|
||||
* registry service, for mult
|
||||
*
|
||||
* @param keys service key
|
||||
* @param value service value/ip:port
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean registry(Set<String> keys, String value);
|
||||
|
||||
|
||||
/**
|
||||
* remove service, for mult
|
||||
*
|
||||
* @param keys
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean remove(Set<String> keys, String value);
|
||||
|
||||
/**
|
||||
* discovery services, for mult
|
||||
*
|
||||
* @param keys
|
||||
* @return
|
||||
*/
|
||||
public abstract Map<String, TreeSet<String>> discovery(Set<String> keys);
|
||||
|
||||
/**
|
||||
* discovery service, for one
|
||||
*
|
||||
* @param key service key
|
||||
* @return service value/ip:port
|
||||
*/
|
||||
public abstract TreeSet<String> discovery(String key);
|
||||
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package com.czsj.rpc.registry.impl;
|
||||
|
||||
import com.czsj.rpc.registry.ServiceRegistry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: service registry for "local"
|
||||
**/
|
||||
public class LocalServiceRegistry extends ServiceRegistry {
|
||||
|
||||
/**
|
||||
* registry data
|
||||
*/
|
||||
private Map<String, TreeSet<String>> registryData;
|
||||
|
||||
|
||||
/**
|
||||
* @param param ignore, not use
|
||||
*/
|
||||
@Override
|
||||
public void start(Map<String, String> param) {
|
||||
registryData = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
registryData.clear();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean registry(Set<String> keys, String value) {
|
||||
if (keys == null || keys.size() == 0 || value == null || value.trim().length() == 0) {
|
||||
return false;
|
||||
}
|
||||
for (String key : keys) {
|
||||
TreeSet<String> values = registryData.get(key);
|
||||
if (values == null) {
|
||||
values = new TreeSet<>();
|
||||
registryData.put(key, values);
|
||||
}
|
||||
values.add(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Set<String> keys, String value) {
|
||||
if (keys == null || keys.size() == 0 || value == null || value.trim().length() == 0) {
|
||||
return false;
|
||||
}
|
||||
for (String key : keys) {
|
||||
TreeSet<String> values = registryData.get(key);
|
||||
if (values != null) {
|
||||
values.remove(value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, TreeSet<String>> discovery(Set<String> keys) {
|
||||
if (keys == null || keys.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
Map<String, TreeSet<String>> registryDataTmp = new HashMap<String, TreeSet<String>>();
|
||||
for (String key : keys) {
|
||||
TreeSet<String> valueSetTmp = discovery(key);
|
||||
if (valueSetTmp != null) {
|
||||
registryDataTmp.put(key, valueSetTmp);
|
||||
}
|
||||
}
|
||||
return registryDataTmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeSet<String> discovery(String key) {
|
||||
return registryData.get(key);
|
||||
}
|
||||
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker;
|
||||
|
||||
import com.czsj.rpc.registry.ServiceRegistry;
|
||||
import com.czsj.rpc.registry.impl.LocalServiceRegistry;
|
||||
import com.czsj.rpc.remoting.net.params.BaseCallback;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcFutureResponse;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* xxl-rpc invoker factory, init service-registry
|
||||
*
|
||||
* @author xuxueli 2018-10-19
|
||||
*/
|
||||
public class XxlRpcInvokerFactory {
|
||||
private static Logger logger = LoggerFactory.getLogger(XxlRpcInvokerFactory.class);
|
||||
|
||||
// ---------------------- default instance ----------------------
|
||||
|
||||
private static volatile XxlRpcInvokerFactory instance = new XxlRpcInvokerFactory(LocalServiceRegistry.class, null);
|
||||
|
||||
public static XxlRpcInvokerFactory getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- config ----------------------
|
||||
|
||||
private Class<? extends ServiceRegistry> serviceRegistryClass; // class.forname
|
||||
private Map<String, String> serviceRegistryParam;
|
||||
|
||||
|
||||
public XxlRpcInvokerFactory() {
|
||||
}
|
||||
|
||||
public XxlRpcInvokerFactory(Class<? extends ServiceRegistry> serviceRegistryClass, Map<String, String> serviceRegistryParam) {
|
||||
this.serviceRegistryClass = serviceRegistryClass;
|
||||
this.serviceRegistryParam = serviceRegistryParam;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- start / stop ----------------------
|
||||
|
||||
public void start() throws Exception {
|
||||
// start registry
|
||||
if (serviceRegistryClass != null) {
|
||||
serviceRegistry = serviceRegistryClass.newInstance();
|
||||
serviceRegistry.start(serviceRegistryParam);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() throws Exception {
|
||||
// stop registry
|
||||
if (serviceRegistry != null) {
|
||||
serviceRegistry.stop();
|
||||
}
|
||||
|
||||
// stop callback
|
||||
if (stopCallbackList.size() > 0) {
|
||||
for (BaseCallback callback : stopCallbackList) {
|
||||
try {
|
||||
callback.run();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop CallbackThreadPool
|
||||
stopCallbackThreadPool();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- service registry ----------------------
|
||||
|
||||
private ServiceRegistry serviceRegistry;
|
||||
|
||||
public ServiceRegistry getServiceRegistry() {
|
||||
return serviceRegistry;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- service registry ----------------------
|
||||
|
||||
private List<BaseCallback> stopCallbackList = new ArrayList<BaseCallback>();
|
||||
|
||||
public void addStopCallBack(BaseCallback callback) {
|
||||
stopCallbackList.add(callback);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- future-response pool ----------------------
|
||||
|
||||
// XxlRpcFutureResponseFactory
|
||||
|
||||
private ConcurrentMap<String, XxlRpcFutureResponse> futureResponsePool = new ConcurrentHashMap<String, XxlRpcFutureResponse>();
|
||||
|
||||
public void setInvokerFuture(String requestId, XxlRpcFutureResponse futureResponse) {
|
||||
futureResponsePool.put(requestId, futureResponse);
|
||||
}
|
||||
|
||||
public void removeInvokerFuture(String requestId) {
|
||||
futureResponsePool.remove(requestId);
|
||||
}
|
||||
|
||||
public void notifyInvokerFuture(String requestId, final XxlRpcResponse xxlRpcResponse) {
|
||||
|
||||
// get
|
||||
final XxlRpcFutureResponse futureResponse = futureResponsePool.get(requestId);
|
||||
if (futureResponse == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// notify
|
||||
if (futureResponse.getInvokeCallback() != null) {
|
||||
|
||||
// callback type
|
||||
try {
|
||||
executeResponseCallback(() -> {
|
||||
if (xxlRpcResponse.getErrorMsg() != null) {
|
||||
futureResponse.getInvokeCallback().onFailure(new XxlRpcException(xxlRpcResponse.getErrorMsg()));
|
||||
} else {
|
||||
futureResponse.getInvokeCallback().onSuccess(xxlRpcResponse.getResult());
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
|
||||
// other nomal type
|
||||
futureResponse.setResponse(xxlRpcResponse);
|
||||
}
|
||||
|
||||
// do remove
|
||||
futureResponsePool.remove(requestId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- response callback ThreadPool ----------------------
|
||||
|
||||
private ThreadPoolExecutor responseCallbackThreadPool = null;
|
||||
|
||||
public void executeResponseCallback(Runnable runnable) {
|
||||
|
||||
if (responseCallbackThreadPool == null) {
|
||||
synchronized (this) {
|
||||
if (responseCallbackThreadPool == null) {
|
||||
responseCallbackThreadPool = new ThreadPoolExecutor(
|
||||
10,
|
||||
100,
|
||||
60L,
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(1000),
|
||||
r -> new Thread(r, "xxl-rpc, XxlRpcInvokerFactory-responseCallbackThreadPool-" + r.hashCode()),
|
||||
(r, executor) -> {
|
||||
throw new XxlRpcException("xxl-rpc Invoke Callback Thread pool is EXHAUSTED!");
|
||||
}); // default maxThreads 300, minThreads 60
|
||||
}
|
||||
}
|
||||
}
|
||||
responseCallbackThreadPool.execute(runnable);
|
||||
}
|
||||
|
||||
public void stopCallbackThreadPool() {
|
||||
if (responseCallbackThreadPool != null) {
|
||||
responseCallbackThreadPool.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.annotation;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.call.CallType;
|
||||
import com.czsj.rpc.remoting.invoker.route.LoadBalance;
|
||||
import com.czsj.rpc.remoting.net.Client;
|
||||
import com.czsj.rpc.remoting.net.impl.netty.client.NettyClient;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import com.czsj.rpc.serialize.impl.HessianSerializer;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: rpc service annotation, skeleton of stub ("@Inherited" allow service use "Transactional")
|
||||
**/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface XxlRpcReference {
|
||||
|
||||
Class<? extends Client> client() default NettyClient.class;
|
||||
|
||||
Class<? extends Serializer> serializer() default HessianSerializer.class;
|
||||
|
||||
CallType callType() default CallType.SYNC;
|
||||
|
||||
LoadBalance loadBalance() default LoadBalance.ROUND;
|
||||
|
||||
//Class<?> iface;
|
||||
String version() default "";
|
||||
|
||||
long timeout() default 1000;
|
||||
|
||||
String address() default "";
|
||||
|
||||
String accessToken() default "";
|
||||
|
||||
//XxlRpcInvokeCallback invokeCallback() ;
|
||||
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.call;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: 远程调用的类型
|
||||
**/
|
||||
public enum CallType {
|
||||
|
||||
|
||||
SYNC,
|
||||
|
||||
FUTURE,
|
||||
|
||||
CALLBACK,
|
||||
|
||||
ONEWAY;
|
||||
|
||||
|
||||
public static CallType match(String name, CallType defaultCallType) {
|
||||
for (CallType item : CallType.values()) {
|
||||
if (item.name().equals(name)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return defaultCallType;
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.call;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description:
|
||||
**/
|
||||
public abstract class XxlRpcInvokeCallback<T> {
|
||||
|
||||
public abstract void onSuccess(T result);
|
||||
|
||||
public abstract void onFailure(Throwable exception);
|
||||
|
||||
|
||||
// ---------------------- thread invoke callback ----------------------
|
||||
|
||||
private static ThreadLocal<XxlRpcInvokeCallback> threadInvokerFuture = new ThreadLocal<XxlRpcInvokeCallback>();
|
||||
|
||||
/**
|
||||
* get callback
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static XxlRpcInvokeCallback getCallback() {
|
||||
XxlRpcInvokeCallback invokeCallback = threadInvokerFuture.get();
|
||||
threadInvokerFuture.remove();
|
||||
return invokeCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* set future
|
||||
*
|
||||
* @param invokeCallback
|
||||
*/
|
||||
public static void setCallback(XxlRpcInvokeCallback invokeCallback) {
|
||||
threadInvokerFuture.set(invokeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove future
|
||||
*/
|
||||
public static void removeCallback() {
|
||||
threadInvokerFuture.remove();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.call;
|
||||
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcFutureResponse;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description:
|
||||
**/
|
||||
public class XxlRpcInvokeFuture implements Future {
|
||||
|
||||
|
||||
private XxlRpcFutureResponse futureResponse;
|
||||
|
||||
public XxlRpcInvokeFuture(XxlRpcFutureResponse futureResponse) {
|
||||
this.futureResponse = futureResponse;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
// remove-InvokerFuture
|
||||
futureResponse.removeInvokerFuture();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return futureResponse.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return futureResponse.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return futureResponse.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get() throws ExecutionException, InterruptedException {
|
||||
try {
|
||||
return get(-1, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
throw new XxlRpcException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
try {
|
||||
// future get
|
||||
XxlRpcResponse xxlRpcResponse = futureResponse.get(timeout, unit);
|
||||
if (xxlRpcResponse.getErrorMsg() != null) {
|
||||
throw new XxlRpcException(xxlRpcResponse.getErrorMsg());
|
||||
}
|
||||
return xxlRpcResponse.getResult();
|
||||
} finally {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- thread invoke future ----------------------
|
||||
|
||||
private static ThreadLocal<XxlRpcInvokeFuture> threadInvokerFuture = new ThreadLocal<XxlRpcInvokeFuture>();
|
||||
|
||||
/**
|
||||
* get future
|
||||
*
|
||||
* @param type
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> Future<T> getFuture(Class<T> type) {
|
||||
Future<T> future = (Future<T>) threadInvokerFuture.get();
|
||||
threadInvokerFuture.remove();
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* set future
|
||||
*
|
||||
* @param future
|
||||
*/
|
||||
public static void setFuture(XxlRpcInvokeFuture future) {
|
||||
threadInvokerFuture.set(future);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove future
|
||||
*/
|
||||
public static void removeFuture() {
|
||||
threadInvokerFuture.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.generic;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description:
|
||||
**/
|
||||
public interface XxlRpcGenericService {
|
||||
|
||||
/**
|
||||
* generic invoke
|
||||
*
|
||||
* @param iface iface name
|
||||
* @param version iface version
|
||||
* @param method method name
|
||||
* @param parameterTypes parameter types, limit base type like "int、java.lang.Integer、java.util.List、java.util.Map ..."
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
Object invoke(String iface, String version, String method, String[] parameterTypes, Object[] args);
|
||||
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.impl;
|
||||
|
||||
import com.czsj.rpc.registry.ServiceRegistry;
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.invoker.annotation.XxlRpcReference;
|
||||
import com.czsj.rpc.remoting.invoker.reference.XxlRpcReferenceBean;
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xuxueli 2018-10-19
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description:
|
||||
**/
|
||||
public class XxlRpcSpringInvokerFactory extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean, DisposableBean, BeanFactoryAware {
|
||||
private Logger logger = LoggerFactory.getLogger(XxlRpcSpringInvokerFactory.class);
|
||||
|
||||
// ---------------------- config ----------------------
|
||||
|
||||
private Class<? extends ServiceRegistry> serviceRegistryClass; // class.forname
|
||||
private Map<String, String> serviceRegistryParam;
|
||||
|
||||
|
||||
public void setServiceRegistryClass(Class<? extends ServiceRegistry> serviceRegistryClass) {
|
||||
this.serviceRegistryClass = serviceRegistryClass;
|
||||
}
|
||||
|
||||
public void setServiceRegistryParam(Map<String, String> serviceRegistryParam) {
|
||||
this.serviceRegistryParam = serviceRegistryParam;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- util ----------------------
|
||||
|
||||
private XxlRpcInvokerFactory xxlRpcInvokerFactory;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// start invoker factory
|
||||
xxlRpcInvokerFactory = new XxlRpcInvokerFactory(serviceRegistryClass, serviceRegistryParam);
|
||||
xxlRpcInvokerFactory.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
|
||||
|
||||
// collection
|
||||
final Set<String> serviceKeyList = new HashSet<>();
|
||||
|
||||
// parse XxlRpcReferenceBean
|
||||
ReflectionUtils.doWithFields(bean.getClass(), field -> {
|
||||
if (field.isAnnotationPresent(XxlRpcReference.class)) {
|
||||
// valid
|
||||
Class iface = field.getType();
|
||||
if (!iface.isInterface()) {
|
||||
throw new XxlRpcException("xxl-rpc, reference(XxlRpcReference) must be interface.");
|
||||
}
|
||||
|
||||
XxlRpcReference rpcReference = field.getAnnotation(XxlRpcReference.class);
|
||||
|
||||
// init reference bean
|
||||
XxlRpcReferenceBean referenceBean = new XxlRpcReferenceBean();
|
||||
referenceBean.setClient(rpcReference.client());
|
||||
referenceBean.setSerializer(rpcReference.serializer());
|
||||
referenceBean.setCallType(rpcReference.callType());
|
||||
referenceBean.setLoadBalance(rpcReference.loadBalance());
|
||||
referenceBean.setIface(iface);
|
||||
referenceBean.setVersion(rpcReference.version());
|
||||
referenceBean.setTimeout(rpcReference.timeout());
|
||||
referenceBean.setAddress(rpcReference.address());
|
||||
referenceBean.setAccessToken(rpcReference.accessToken());
|
||||
referenceBean.setInvokeCallback(null);
|
||||
referenceBean.setInvokerFactory(xxlRpcInvokerFactory);
|
||||
|
||||
|
||||
// get proxyObj
|
||||
Object serviceProxy;
|
||||
try {
|
||||
serviceProxy = referenceBean.getObject();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// set bean
|
||||
field.setAccessible(true);
|
||||
field.set(bean, serviceProxy);
|
||||
|
||||
logger.info(">>>>>>>>>>> xxl-rpc, invoker factory init reference bean success. serviceKey = {}, bean.field = {}.{}",
|
||||
XxlRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version()), beanName, field.getName());
|
||||
|
||||
// collection
|
||||
String serviceKey = XxlRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version());
|
||||
serviceKeyList.add(serviceKey);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// mult discovery
|
||||
if (xxlRpcInvokerFactory.getServiceRegistry() != null) {
|
||||
try {
|
||||
xxlRpcInvokerFactory.getServiceRegistry().discovery(serviceKeyList);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return super.postProcessAfterInstantiation(bean, beanName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
|
||||
// stop invoker factory
|
||||
xxlRpcInvokerFactory.stop();
|
||||
}
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
}
|
@ -1,320 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.reference;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.invoker.call.CallType;
|
||||
import com.czsj.rpc.remoting.invoker.call.XxlRpcInvokeCallback;
|
||||
import com.czsj.rpc.remoting.invoker.call.XxlRpcInvokeFuture;
|
||||
import com.czsj.rpc.remoting.invoker.generic.XxlRpcGenericService;
|
||||
import com.czsj.rpc.remoting.invoker.route.LoadBalance;
|
||||
import com.czsj.rpc.remoting.net.Client;
|
||||
import com.czsj.rpc.remoting.net.impl.netty.client.NettyClient;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcFutureResponse;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import com.czsj.rpc.serialize.impl.HessianSerializer;
|
||||
import com.czsj.rpc.util.ClassUtil;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* rpc reference bean, use by api
|
||||
*
|
||||
* @author xuxueli 2015-10-29 20:18:32
|
||||
*/
|
||||
public class XxlRpcReferenceBean {
|
||||
private static final Logger logger = LoggerFactory.getLogger(XxlRpcReferenceBean.class);
|
||||
// [tips01: save 30ms/100invoke. why why why??? with this logger, it can save lots of time.]
|
||||
|
||||
|
||||
// ---------------------- config ----------------------
|
||||
|
||||
private Class<? extends Client> client = NettyClient.class;
|
||||
private Class<? extends Serializer> serializer = HessianSerializer.class;
|
||||
private CallType callType = CallType.SYNC;
|
||||
private LoadBalance loadBalance = LoadBalance.ROUND;
|
||||
|
||||
private Class<?> iface = null;
|
||||
private String version = null;
|
||||
|
||||
private long timeout = 10000;
|
||||
|
||||
private String address = null;
|
||||
private String accessToken = null;
|
||||
|
||||
private XxlRpcInvokeCallback invokeCallback = null;
|
||||
|
||||
private XxlRpcInvokerFactory invokerFactory = null;
|
||||
|
||||
|
||||
// set
|
||||
public void setClient(Class<? extends Client> client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void setSerializer(Class<? extends Serializer> serializer) {
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public void setCallType(CallType callType) {
|
||||
this.callType = callType;
|
||||
}
|
||||
|
||||
public void setLoadBalance(LoadBalance loadBalance) {
|
||||
this.loadBalance = loadBalance;
|
||||
}
|
||||
|
||||
public void setIface(Class<?> iface) {
|
||||
this.iface = iface;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public void setInvokeCallback(XxlRpcInvokeCallback invokeCallback) {
|
||||
this.invokeCallback = invokeCallback;
|
||||
}
|
||||
|
||||
public void setInvokerFactory(XxlRpcInvokerFactory invokerFactory) {
|
||||
this.invokerFactory = invokerFactory;
|
||||
}
|
||||
|
||||
|
||||
// get
|
||||
public Serializer getSerializerInstance() {
|
||||
return serializerInstance;
|
||||
}
|
||||
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public XxlRpcInvokerFactory getInvokerFactory() {
|
||||
return invokerFactory;
|
||||
}
|
||||
|
||||
public Class<?> getIface() {
|
||||
return iface;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- initClient ----------------------
|
||||
|
||||
private Client clientInstance = null;
|
||||
private Serializer serializerInstance = null;
|
||||
|
||||
public XxlRpcReferenceBean initClient() throws Exception {
|
||||
|
||||
// valid
|
||||
if (this.client == null) {
|
||||
throw new XxlRpcException("xxl-rpc reference client missing.");
|
||||
}
|
||||
if (this.serializer == null) {
|
||||
throw new XxlRpcException("xxl-rpc reference serializer missing.");
|
||||
}
|
||||
if (this.callType == null) {
|
||||
throw new XxlRpcException("xxl-rpc reference callType missing.");
|
||||
}
|
||||
if (this.loadBalance == null) {
|
||||
throw new XxlRpcException("xxl-rpc reference loadBalance missing.");
|
||||
}
|
||||
if (this.iface == null) {
|
||||
throw new XxlRpcException("xxl-rpc reference iface missing.");
|
||||
}
|
||||
if (this.timeout < 0) {
|
||||
this.timeout = 0;
|
||||
}
|
||||
if (this.invokerFactory == null) {
|
||||
this.invokerFactory = XxlRpcInvokerFactory.getInstance();
|
||||
}
|
||||
|
||||
// init serializerInstance
|
||||
this.serializerInstance = serializer.newInstance();
|
||||
|
||||
// init Client
|
||||
clientInstance = client.newInstance();
|
||||
clientInstance.init(this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- util ----------------------
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
|
||||
// initClient
|
||||
initClient();
|
||||
|
||||
// newProxyInstance
|
||||
return Proxy.newProxyInstance(Thread.currentThread()
|
||||
.getContextClassLoader(), new Class[]{iface},
|
||||
(proxy, method, args) -> {
|
||||
|
||||
// method param
|
||||
String className = method.getDeclaringClass().getName(); // iface.getName()
|
||||
String varsion_ = version;
|
||||
String methodName = method.getName();
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
Object[] parameters = args;
|
||||
|
||||
// filter for generic
|
||||
if (className.equals(XxlRpcGenericService.class.getName()) && methodName.equals("invoke")) {
|
||||
|
||||
Class<?>[] paramTypes = null;
|
||||
if (args[3] != null) {
|
||||
String[] paramTypes_str = (String[]) args[3];
|
||||
if (paramTypes_str.length > 0) {
|
||||
paramTypes = new Class[paramTypes_str.length];
|
||||
for (int i = 0; i < paramTypes_str.length; i++) {
|
||||
paramTypes[i] = ClassUtil.resolveClass(paramTypes_str[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
className = (String) args[0];
|
||||
varsion_ = (String) args[1];
|
||||
methodName = (String) args[2];
|
||||
parameterTypes = paramTypes;
|
||||
parameters = (Object[]) args[4];
|
||||
}
|
||||
|
||||
// filter method like "Object.toString()"
|
||||
if (className.equals(Object.class.getName())) {
|
||||
logger.info(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}#{}]", className, methodName);
|
||||
throw new XxlRpcException("xxl-rpc proxy class-method not support");
|
||||
}
|
||||
|
||||
// address
|
||||
String finalAddress = address;
|
||||
if (finalAddress == null || finalAddress.trim().length() == 0) {
|
||||
if (invokerFactory != null && invokerFactory.getServiceRegistry() != null) {
|
||||
// discovery
|
||||
String serviceKey = XxlRpcProviderFactory.makeServiceKey(className, varsion_);
|
||||
TreeSet<String> addressSet = invokerFactory.getServiceRegistry().discovery(serviceKey);
|
||||
// load balance
|
||||
if (addressSet == null || addressSet.size() == 0) {
|
||||
// pass
|
||||
} else if (addressSet.size() == 1) {
|
||||
finalAddress = addressSet.first();
|
||||
} else {
|
||||
finalAddress = loadBalance.xxlRpcInvokerRouter.route(serviceKey, addressSet);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (finalAddress == null || finalAddress.trim().length() == 0) {
|
||||
throw new XxlRpcException("xxl-rpc reference bean[" + className + "] address empty");
|
||||
}
|
||||
|
||||
// request
|
||||
XxlRpcRequest xxlRpcRequest = new XxlRpcRequest();
|
||||
xxlRpcRequest.setRequestId(UUID.randomUUID().toString());
|
||||
xxlRpcRequest.setCreateMillisTime(System.currentTimeMillis());
|
||||
xxlRpcRequest.setAccessToken(accessToken);
|
||||
xxlRpcRequest.setClassName(className);
|
||||
xxlRpcRequest.setMethodName(methodName);
|
||||
xxlRpcRequest.setParameterTypes(parameterTypes);
|
||||
xxlRpcRequest.setParameters(parameters);
|
||||
xxlRpcRequest.setVersion(version);
|
||||
|
||||
// send
|
||||
if (CallType.SYNC == callType) {
|
||||
// future-response set
|
||||
XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, null);
|
||||
try {
|
||||
// do invoke
|
||||
clientInstance.asyncSend(finalAddress, xxlRpcRequest);
|
||||
|
||||
// future get
|
||||
XxlRpcResponse xxlRpcResponse = futureResponse.get(timeout, TimeUnit.MILLISECONDS);
|
||||
if (xxlRpcResponse.getErrorMsg() != null) {
|
||||
throw new XxlRpcException(xxlRpcResponse.getErrorMsg());
|
||||
}
|
||||
return xxlRpcResponse.getResult();
|
||||
} catch (Exception e) {
|
||||
logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest);
|
||||
|
||||
throw (e instanceof XxlRpcException) ? e : new XxlRpcException(e);
|
||||
} finally {
|
||||
// future-response remove
|
||||
futureResponse.removeInvokerFuture();
|
||||
}
|
||||
} else if (CallType.FUTURE == callType) {
|
||||
// future-response set
|
||||
XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, null);
|
||||
try {
|
||||
// invoke future set
|
||||
XxlRpcInvokeFuture invokeFuture = new XxlRpcInvokeFuture(futureResponse);
|
||||
XxlRpcInvokeFuture.setFuture(invokeFuture);
|
||||
|
||||
// do invoke
|
||||
clientInstance.asyncSend(finalAddress, xxlRpcRequest);
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest);
|
||||
|
||||
// future-response remove
|
||||
futureResponse.removeInvokerFuture();
|
||||
|
||||
throw (e instanceof XxlRpcException) ? e : new XxlRpcException(e);
|
||||
}
|
||||
|
||||
} else if (CallType.CALLBACK == callType) {
|
||||
|
||||
// get callback
|
||||
XxlRpcInvokeCallback finalInvokeCallback = invokeCallback;
|
||||
XxlRpcInvokeCallback threadInvokeCallback = XxlRpcInvokeCallback.getCallback();
|
||||
if (threadInvokeCallback != null) {
|
||||
finalInvokeCallback = threadInvokeCallback;
|
||||
}
|
||||
if (finalInvokeCallback == null) {
|
||||
throw new XxlRpcException("xxl-rpc XxlRpcInvokeCallback(CallType=" + CallType.CALLBACK.name() + ") cannot be null.");
|
||||
}
|
||||
|
||||
// future-response set
|
||||
XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, finalInvokeCallback);
|
||||
try {
|
||||
clientInstance.asyncSend(finalAddress, xxlRpcRequest);
|
||||
} catch (Exception e) {
|
||||
logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest);
|
||||
|
||||
// future-response remove
|
||||
futureResponse.removeInvokerFuture();
|
||||
|
||||
throw (e instanceof XxlRpcException) ? e : new XxlRpcException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
} else if (CallType.ONEWAY == callType) {
|
||||
clientInstance.asyncSend(finalAddress, xxlRpcRequest);
|
||||
return null;
|
||||
} else {
|
||||
throw new XxlRpcException("xxl-rpc callType[" + callType + "] invalid");
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.reference.impl;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.reference.XxlRpcReferenceBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* rpc reference bean, use by spring xml and annotation (for spring)
|
||||
*
|
||||
* @author xuxueli 2015-10-29 20:18:32
|
||||
*/
|
||||
public class XxlRpcSpringReferenceBean implements FactoryBean<Object>, InitializingBean {
|
||||
|
||||
|
||||
// ---------------------- util ----------------------
|
||||
|
||||
private XxlRpcReferenceBean xxlRpcReferenceBean;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
// init config
|
||||
this.xxlRpcReferenceBean = new XxlRpcReferenceBean();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
return xxlRpcReferenceBean.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return xxlRpcReferenceBean.getIface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* public static <T> ClientProxy ClientProxy<T> getFuture(Class<T> type) {
|
||||
* <T> ClientProxy proxy = (<T>) new ClientProxy();
|
||||
* return proxy;
|
||||
* }
|
||||
*/
|
||||
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.route;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.route.impl.*;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2018-12-04
|
||||
*/
|
||||
public enum LoadBalance {
|
||||
|
||||
RANDOM(new XxlRpcLoadBalanceRandomStrategy()),
|
||||
ROUND(new XxlRpcLoadBalanceRoundStrategy()),
|
||||
LRU(new XxlRpcLoadBalanceLRUStrategy()),
|
||||
LFU(new XxlRpcLoadBalanceLFUStrategy()),
|
||||
CONSISTENT_HASH(new XxlRpcLoadBalanceConsistentHashStrategy());
|
||||
|
||||
|
||||
public final XxlRpcLoadBalance xxlRpcInvokerRouter;
|
||||
|
||||
private LoadBalance(XxlRpcLoadBalance xxlRpcInvokerRouter) {
|
||||
this.xxlRpcInvokerRouter = xxlRpcInvokerRouter;
|
||||
}
|
||||
|
||||
|
||||
public static LoadBalance match(String name, LoadBalance defaultRouter) {
|
||||
for (LoadBalance item : LoadBalance.values()) {
|
||||
if (item.equals(name)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return defaultRouter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*public static void main(String[] args) {
|
||||
String serviceKey = "service";
|
||||
TreeSet<String> addressSet = new TreeSet<String>(){{
|
||||
add("1");
|
||||
add("2");
|
||||
add("3");
|
||||
add("4");
|
||||
add("5");
|
||||
}};
|
||||
|
||||
for (LoadBalance item : LoadBalance.values()) {
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
String address = LoadBalance.LFU.xxlRpcInvokerRouter.route(serviceKey, addressSet);
|
||||
//System.out.println(address);;
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println(item.name() + " --- " + (end-start));
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.route;
|
||||
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* 分组下机器地址相同,不同JOB均匀散列在不同机器上,保证分组下机器分配JOB平均;且每个JOB固定调度其中一台机器;
|
||||
* a、virtual node:解决不均衡问题
|
||||
* b、hash method replace hashCode:String的hashCode可能重复,需要进一步扩大hashCode的取值范围
|
||||
*
|
||||
* @author xuxueli 2018-12-04
|
||||
*/
|
||||
public abstract class XxlRpcLoadBalance {
|
||||
|
||||
public abstract String route(String serviceKey, TreeSet<String> addressSet);
|
||||
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.route.impl;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.route.XxlRpcLoadBalance;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* consustent hash
|
||||
*
|
||||
* 单个JOB对应的每个执行器,使用频率最低的优先被选举
|
||||
* a(*)、LFU(Least Frequently Used):最不经常使用,频率/次数
|
||||
* b、LRU(Least Recently Used):最近最久未使用,时间
|
||||
*
|
||||
* @author xuxueli 2018-12-04
|
||||
*/
|
||||
public class XxlRpcLoadBalanceConsistentHashStrategy extends XxlRpcLoadBalance {
|
||||
|
||||
private int VIRTUAL_NODE_NUM = 5;
|
||||
|
||||
/**
|
||||
* get hash code on 2^32 ring (md5散列的方式计算hash值)
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private long hash(String key) {
|
||||
|
||||
// md5 byte
|
||||
MessageDigest md5;
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("MD5 not supported", e);
|
||||
}
|
||||
md5.reset();
|
||||
byte[] keyBytes = null;
|
||||
try {
|
||||
keyBytes = key.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("Unknown string :" + key, e);
|
||||
}
|
||||
|
||||
md5.update(keyBytes);
|
||||
byte[] digest = md5.digest();
|
||||
|
||||
// hash code, Truncate to 32-bits
|
||||
long hashCode = ((long) (digest[3] & 0xFF) << 24)
|
||||
| ((long) (digest[2] & 0xFF) << 16)
|
||||
| ((long) (digest[1] & 0xFF) << 8)
|
||||
| (digest[0] & 0xFF);
|
||||
|
||||
long truncateHashCode = hashCode & 0xffffffffL;
|
||||
return truncateHashCode;
|
||||
}
|
||||
|
||||
public String doRoute(String serviceKey, TreeSet<String> addressSet) {
|
||||
|
||||
// ------A1------A2-------A3------
|
||||
// -----------J1------------------
|
||||
TreeMap<Long, String> addressRing = new TreeMap<Long, String>();
|
||||
for (String address: addressSet) {
|
||||
for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
|
||||
long addressHash = hash("SHARD-" + address + "-NODE-" + i);
|
||||
addressRing.put(addressHash, address);
|
||||
}
|
||||
}
|
||||
|
||||
long jobHash = hash(serviceKey);
|
||||
SortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);
|
||||
if (!lastRing.isEmpty()) {
|
||||
return lastRing.get(lastRing.firstKey());
|
||||
}
|
||||
return addressRing.firstEntry().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String route(String serviceKey, TreeSet<String> addressSet) {
|
||||
String finalAddress = doRoute(serviceKey, addressSet);
|
||||
return finalAddress;
|
||||
}
|
||||
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.route.impl;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.route.XxlRpcLoadBalance;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* lru
|
||||
*
|
||||
* @author xuxueli 2018-12-04
|
||||
*/
|
||||
public class XxlRpcLoadBalanceLFUStrategy extends XxlRpcLoadBalance {
|
||||
|
||||
private ConcurrentMap<String, HashMap<String, Integer>> jobLfuMap = new ConcurrentHashMap<String, HashMap<String, Integer>>();
|
||||
private long CACHE_VALID_TIME = 0;
|
||||
|
||||
public String doRoute(String serviceKey, TreeSet<String> addressSet) {
|
||||
|
||||
// cache clear
|
||||
if (System.currentTimeMillis() > CACHE_VALID_TIME) {
|
||||
jobLfuMap.clear();
|
||||
CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*24;
|
||||
}
|
||||
|
||||
// lfu item init
|
||||
HashMap<String, Integer> lfuItemMap = jobLfuMap.get(serviceKey); // Key排序可以用TreeMap+构造入参Compare;Value排序暂时只能通过ArrayList;
|
||||
if (lfuItemMap == null) {
|
||||
lfuItemMap = new HashMap<String, Integer>();
|
||||
jobLfuMap.putIfAbsent(serviceKey, lfuItemMap); // 避免重复覆盖
|
||||
}
|
||||
|
||||
// put new
|
||||
for (String address: addressSet) {
|
||||
if (!lfuItemMap.containsKey(address) || lfuItemMap.get(address) >1000000 ) {
|
||||
lfuItemMap.put(address, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// remove old
|
||||
List<String> delKeys = new ArrayList<>();
|
||||
for (String existKey: lfuItemMap.keySet()) {
|
||||
if (!addressSet.contains(existKey)) {
|
||||
delKeys.add(existKey);
|
||||
}
|
||||
}
|
||||
if (delKeys.size() > 0) {
|
||||
for (String delKey: delKeys) {
|
||||
lfuItemMap.remove(delKey);
|
||||
}
|
||||
}
|
||||
|
||||
// load least userd count address
|
||||
List<Map.Entry<String, Integer>> lfuItemList = new ArrayList<Map.Entry<String, Integer>>(lfuItemMap.entrySet());
|
||||
Collections.sort(lfuItemList, new Comparator<Map.Entry<String, Integer>>() {
|
||||
@Override
|
||||
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
|
||||
return o1.getValue().compareTo(o2.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
Map.Entry<String, Integer> addressItem = lfuItemList.get(0);
|
||||
String minAddress = addressItem.getKey();
|
||||
addressItem.setValue(addressItem.getValue() + 1);
|
||||
|
||||
return minAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String route(String serviceKey, TreeSet<String> addressSet) {
|
||||
String finalAddress = doRoute(serviceKey, addressSet);
|
||||
return finalAddress;
|
||||
}
|
||||
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.route.impl;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.route.XxlRpcLoadBalance;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* lru
|
||||
*
|
||||
* @author xuxueli 2018-12-04
|
||||
*/
|
||||
public class XxlRpcLoadBalanceLRUStrategy extends XxlRpcLoadBalance {
|
||||
|
||||
private ConcurrentMap<String, LinkedHashMap<String, String>> jobLRUMap = new ConcurrentHashMap<String, LinkedHashMap<String, String>>();
|
||||
private long CACHE_VALID_TIME = 0;
|
||||
|
||||
public String doRoute(String serviceKey, TreeSet<String> addressSet) {
|
||||
|
||||
// cache clear
|
||||
if (System.currentTimeMillis() > CACHE_VALID_TIME) {
|
||||
jobLRUMap.clear();
|
||||
CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*24;
|
||||
}
|
||||
|
||||
// init lru
|
||||
LinkedHashMap<String, String> lruItem = jobLRUMap.get(serviceKey);
|
||||
if (lruItem == null) {
|
||||
/**
|
||||
* LinkedHashMap
|
||||
* a、accessOrder:ture=访问顺序排序(get/put时排序)/ACCESS-LAST;false=插入顺序排期/FIFO;
|
||||
* b、removeEldestEntry:新增元素时将会调用,返回true时会删除最老元素;可封装LinkedHashMap并重写该方法,比如定义最大容量,超出是返回true即可实现固定长度的LRU算法;
|
||||
*/
|
||||
lruItem = new LinkedHashMap<String, String>(16, 0.75f, true){
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
|
||||
if(super.size() > 1000){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
jobLRUMap.putIfAbsent(serviceKey, lruItem);
|
||||
}
|
||||
|
||||
// put new
|
||||
for (String address: addressSet) {
|
||||
if (!lruItem.containsKey(address)) {
|
||||
lruItem.put(address, address);
|
||||
}
|
||||
}
|
||||
// remove old
|
||||
List<String> delKeys = new ArrayList<>();
|
||||
for (String existKey: lruItem.keySet()) {
|
||||
if (!addressSet.contains(existKey)) {
|
||||
delKeys.add(existKey);
|
||||
}
|
||||
}
|
||||
if (delKeys.size() > 0) {
|
||||
for (String delKey: delKeys) {
|
||||
lruItem.remove(delKey);
|
||||
}
|
||||
}
|
||||
|
||||
// load
|
||||
String eldestKey = lruItem.entrySet().iterator().next().getKey();
|
||||
String eldestValue = lruItem.get(eldestKey);
|
||||
return eldestValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String route(String serviceKey, TreeSet<String> addressSet) {
|
||||
String finalAddress = doRoute(serviceKey, addressSet);
|
||||
return finalAddress;
|
||||
}
|
||||
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.route.impl;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.route.XxlRpcLoadBalance;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* random
|
||||
*
|
||||
* @author xuxueli 2018-12-04
|
||||
*/
|
||||
public class XxlRpcLoadBalanceRandomStrategy extends XxlRpcLoadBalance {
|
||||
|
||||
private Random random = new Random();
|
||||
|
||||
@Override
|
||||
public String route(String serviceKey, TreeSet<String> addressSet) {
|
||||
// arr
|
||||
String[] addressArr = addressSet.toArray(new String[addressSet.size()]);
|
||||
|
||||
// random
|
||||
String finalAddress = addressArr[random.nextInt(addressSet.size())];
|
||||
return finalAddress;
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package com.czsj.rpc.remoting.invoker.route.impl;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.route.XxlRpcLoadBalance;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* round
|
||||
*
|
||||
* @author xuxueli 2018-12-04
|
||||
*/
|
||||
public class XxlRpcLoadBalanceRoundStrategy extends XxlRpcLoadBalance {
|
||||
|
||||
private ConcurrentMap<String, Integer> routeCountEachJob = new ConcurrentHashMap<String, Integer>();
|
||||
private long CACHE_VALID_TIME = 0;
|
||||
private int count(String serviceKey) {
|
||||
// cache clear
|
||||
if (System.currentTimeMillis() > CACHE_VALID_TIME) {
|
||||
routeCountEachJob.clear();
|
||||
CACHE_VALID_TIME = System.currentTimeMillis() + 24*60*60*1000;
|
||||
}
|
||||
|
||||
// count++
|
||||
Integer count = routeCountEachJob.get(serviceKey);
|
||||
count = (count==null || count>1000000)?(new Random().nextInt(100)):++count; // 初始化时主动Random一次,缓解首次压力
|
||||
routeCountEachJob.put(serviceKey, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String route(String serviceKey, TreeSet<String> addressSet) {
|
||||
// arr
|
||||
String[] addressArr = addressSet.toArray(new String[addressSet.size()]);
|
||||
|
||||
// round
|
||||
String finalAddress = addressArr[count(serviceKey)%addressArr.length];
|
||||
return finalAddress;
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.reference.XxlRpcReferenceBean;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* i client
|
||||
* @author xuxueli 2015-11-24 22:18:10
|
||||
*/
|
||||
public abstract class Client {
|
||||
protected static final Logger logger = LoggerFactory.getLogger(Client.class);
|
||||
|
||||
|
||||
// ---------------------- init ----------------------
|
||||
|
||||
protected volatile XxlRpcReferenceBean xxlRpcReferenceBean;
|
||||
|
||||
public void init(XxlRpcReferenceBean xxlRpcReferenceBean) {
|
||||
this.xxlRpcReferenceBean = xxlRpcReferenceBean;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- send ----------------------
|
||||
|
||||
/**
|
||||
* async send, bind requestId and future-response
|
||||
*
|
||||
* @param address
|
||||
* @param xxlRpcRequest
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void asyncSend(String address, XxlRpcRequest xxlRpcRequest) throws Exception;
|
||||
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net;
|
||||
|
||||
import com.czsj.rpc.remoting.net.params.BaseCallback;
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* server
|
||||
*
|
||||
* @author xuxueli 2015-11-24 20:59:49
|
||||
*/
|
||||
public abstract class Server {
|
||||
protected static final Logger logger = LoggerFactory.getLogger(Server.class);
|
||||
|
||||
|
||||
private BaseCallback startedCallback;
|
||||
private BaseCallback stopedCallback;
|
||||
|
||||
public void setStartedCallback(BaseCallback startedCallback) {
|
||||
this.startedCallback = startedCallback;
|
||||
}
|
||||
|
||||
public void setStopedCallback(BaseCallback stopedCallback) {
|
||||
this.stopedCallback = stopedCallback;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* start server
|
||||
*
|
||||
* @param xxlRpcProviderFactory
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void start(final XxlRpcProviderFactory xxlRpcProviderFactory) throws Exception;
|
||||
|
||||
/**
|
||||
* callback when started
|
||||
*/
|
||||
public void onStarted() {
|
||||
if (startedCallback != null) {
|
||||
try {
|
||||
startedCallback.run();
|
||||
} catch (Exception e) {
|
||||
logger.error(">>>>>>>>>>> xxl-rpc, server startedCallback error.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stop server
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void stop() throws Exception;
|
||||
|
||||
/**
|
||||
* callback when stoped
|
||||
*/
|
||||
public void onStopped() {
|
||||
if (stopedCallback != null) {
|
||||
try {
|
||||
stopedCallback.run();
|
||||
} catch (Exception e) {
|
||||
logger.error(">>>>>>>>>>> xxl-rpc, server stopedCallback error.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.common;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.invoker.reference.XxlRpcReferenceBean;
|
||||
import com.czsj.rpc.remoting.net.params.BaseCallback;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2018-10-19
|
||||
*/
|
||||
public abstract class ConnectClient {
|
||||
protected static transient Logger logger = LoggerFactory.getLogger(ConnectClient.class);
|
||||
|
||||
// ---------------------- iface ----------------------
|
||||
|
||||
public abstract void init(String address, final Serializer serializer, final XxlRpcInvokerFactory xxlRpcInvokerFactory) throws Exception;
|
||||
|
||||
public abstract void close();
|
||||
|
||||
public abstract boolean isValidate();
|
||||
|
||||
public abstract void send(XxlRpcRequest xxlRpcRequest) throws Exception;
|
||||
|
||||
|
||||
// ---------------------- client pool map ----------------------
|
||||
|
||||
/**
|
||||
* async send
|
||||
*/
|
||||
public static void asyncSend(XxlRpcRequest xxlRpcRequest, String address,
|
||||
Class<? extends ConnectClient> connectClientImpl,
|
||||
final XxlRpcReferenceBean xxlRpcReferenceBean) throws Exception {
|
||||
|
||||
// client pool [tips03 : may save 35ms/100invoke if move it to constructor, but it is necessary. cause by ConcurrentHashMap.get]
|
||||
ConnectClient clientPool = ConnectClient.getPool(address, connectClientImpl, xxlRpcReferenceBean);
|
||||
|
||||
try {
|
||||
// do invoke
|
||||
clientPool.send(xxlRpcRequest);
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static volatile ConcurrentMap<String, ConnectClient> connectClientMap; // (static) alread addStopCallBack
|
||||
private static volatile ConcurrentMap<String, Object> connectClientLockMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static ConnectClient getPool(String address, Class<? extends ConnectClient> connectClientImpl,
|
||||
final XxlRpcReferenceBean xxlRpcReferenceBean) throws Exception {
|
||||
|
||||
// init base compont, avoid repeat init
|
||||
if (connectClientMap == null) {
|
||||
synchronized (ConnectClient.class) {
|
||||
if (connectClientMap == null) {
|
||||
// init
|
||||
connectClientMap = new ConcurrentHashMap<String, ConnectClient>();
|
||||
// stop callback
|
||||
xxlRpcReferenceBean.getInvokerFactory().addStopCallBack(new BaseCallback() {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (connectClientMap.size() > 0) {
|
||||
for (String key : connectClientMap.keySet()) {
|
||||
ConnectClient clientPool = connectClientMap.get(key);
|
||||
clientPool.close();
|
||||
}
|
||||
connectClientMap.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get-valid client
|
||||
ConnectClient connectClient = connectClientMap.get(address);
|
||||
if (connectClient != null && connectClient.isValidate()) {
|
||||
return connectClient;
|
||||
}
|
||||
|
||||
// lock
|
||||
Object clientLock = connectClientLockMap.get(address);
|
||||
if (clientLock == null) {
|
||||
connectClientLockMap.putIfAbsent(address, new Object());
|
||||
clientLock = connectClientLockMap.get(address);
|
||||
}
|
||||
|
||||
// remove-create new client
|
||||
synchronized (clientLock) {
|
||||
|
||||
// get-valid client, avlid repeat
|
||||
connectClient = connectClientMap.get(address);
|
||||
if (connectClient != null && connectClient.isValidate()) {
|
||||
return connectClient;
|
||||
}
|
||||
|
||||
// remove old
|
||||
if (connectClient != null) {
|
||||
connectClient.close();
|
||||
connectClientMap.remove(address);
|
||||
}
|
||||
|
||||
// set pool
|
||||
ConnectClient connectClient_new = connectClientImpl.newInstance();
|
||||
try {
|
||||
connectClient_new.init(address, xxlRpcReferenceBean.getSerializerInstance(), xxlRpcReferenceBean.getInvokerFactory());
|
||||
connectClientMap.put(address, connectClient_new);
|
||||
} catch (Exception e) {
|
||||
connectClient_new.close();
|
||||
throw e;
|
||||
}
|
||||
|
||||
return connectClient_new;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.common;
|
||||
|
||||
public class NettyConstant {
|
||||
|
||||
public static int MAX_LENGTH = 20 * 1024 * 1024;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty.client;
|
||||
|
||||
import com.czsj.rpc.remoting.net.Client;
|
||||
import com.czsj.rpc.remoting.net.common.ConnectClient;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
|
||||
/**
|
||||
* netty client
|
||||
*
|
||||
* @author xuxueli 2015-11-24 22:25:15
|
||||
*/
|
||||
public class NettyClient extends Client {
|
||||
|
||||
private Class<? extends ConnectClient> connectClientImpl = NettyConnectClient.class;
|
||||
|
||||
@Override
|
||||
public void asyncSend(String address, XxlRpcRequest xxlRpcRequest) throws Exception {
|
||||
ConnectClient.asyncSend(xxlRpcRequest, address, connectClientImpl, xxlRpcReferenceBean);
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty.client;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* rpc netty client handler
|
||||
*
|
||||
* @author xuxueli 2015-10-31 18:00:27
|
||||
*/
|
||||
public class NettyClientHandler extends SimpleChannelInboundHandler<XxlRpcResponse> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(NettyClientHandler.class);
|
||||
|
||||
|
||||
private XxlRpcInvokerFactory xxlRpcInvokerFactory;
|
||||
private NettyConnectClient nettyConnectClient;
|
||||
public NettyClientHandler(final XxlRpcInvokerFactory xxlRpcInvokerFactory, NettyConnectClient nettyConnectClient) {
|
||||
this.xxlRpcInvokerFactory = xxlRpcInvokerFactory;
|
||||
this.nettyConnectClient = nettyConnectClient;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, XxlRpcResponse xxlRpcResponse) throws Exception {
|
||||
|
||||
// notify response
|
||||
xxlRpcInvokerFactory.notifyInvokerFuture(xxlRpcResponse.getRequestId(), xxlRpcResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
logger.error(">>>>>>>>>>> xxl-rpc netty client caught exception", cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (evt instanceof IdleStateEvent){
|
||||
/*ctx.channel().close(); // close idle channel
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty client close an idle channel.");*/
|
||||
|
||||
nettyConnectClient.send(Beat.BEAT_PING); // beat N, close if fail(may throw error)
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty client send beat-ping.");
|
||||
|
||||
} else {
|
||||
super.userEventTriggered(ctx, evt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty.client;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.net.common.ConnectClient;
|
||||
import com.czsj.rpc.remoting.net.impl.netty.codec.NettyDecoder;
|
||||
import com.czsj.rpc.remoting.net.impl.netty.codec.NettyEncoder;
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import com.czsj.rpc.util.IpUtil;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* netty pooled client
|
||||
*
|
||||
* @author xuxueli
|
||||
*/
|
||||
public class NettyConnectClient extends ConnectClient {
|
||||
|
||||
|
||||
private EventLoopGroup group;
|
||||
private Channel channel;
|
||||
|
||||
|
||||
@Override
|
||||
public void init(String address, final Serializer serializer, final XxlRpcInvokerFactory xxlRpcInvokerFactory) throws Exception {
|
||||
final NettyConnectClient thisClient = this;
|
||||
|
||||
Object[] array = IpUtil.parseIpPort(address);
|
||||
String host = (String) array[0];
|
||||
int port = (int) array[1];
|
||||
|
||||
|
||||
this.group = new NioEventLoopGroup();
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
bootstrap.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel channel) throws Exception {
|
||||
channel.pipeline()
|
||||
.addLast(new IdleStateHandler(0,0, Beat.BEAT_INTERVAL, TimeUnit.SECONDS)) // beat N, close if fail
|
||||
.addLast(new NettyEncoder(XxlRpcRequest.class, serializer))
|
||||
.addLast(new NettyDecoder(XxlRpcResponse.class, serializer))
|
||||
.addLast(new NettyClientHandler(xxlRpcInvokerFactory, thisClient));
|
||||
}
|
||||
})
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
|
||||
this.channel = bootstrap.connect(host, port).sync().channel();
|
||||
|
||||
// valid
|
||||
if (!isValidate()) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty client proxy, connect to server success at host:{}, port:{}", host, port);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValidate() {
|
||||
if (this.channel != null) {
|
||||
return this.channel.isActive();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.channel != null && this.channel.isActive()) {
|
||||
this.channel.close(); // if this.channel.isOpen()
|
||||
}
|
||||
if (this.group != null && !this.group.isShutdown()) {
|
||||
this.group.shutdownGracefully();
|
||||
}
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty client close.");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void send(XxlRpcRequest xxlRpcRequest) throws Exception {
|
||||
this.channel.writeAndFlush(xxlRpcRequest).sync();
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty.codec;
|
||||
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* decoder
|
||||
*
|
||||
* @author xuxueli 2015-10-29 19:02:36
|
||||
*/
|
||||
public class NettyDecoder extends ByteToMessageDecoder {
|
||||
|
||||
private Class<?> genericClass;
|
||||
private Serializer serializer;
|
||||
|
||||
public NettyDecoder(Class<?> genericClass, final Serializer serializer) {
|
||||
this.genericClass = genericClass;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
if (in.readableBytes() < 4) {
|
||||
return;
|
||||
}
|
||||
in.markReaderIndex();
|
||||
int dataLength = in.readInt();
|
||||
if (dataLength < 0) {
|
||||
ctx.close();
|
||||
}
|
||||
if (in.readableBytes() < dataLength) {
|
||||
in.resetReaderIndex();
|
||||
return; // fix 1024k buffer splice limix
|
||||
}
|
||||
byte[] data = new byte[dataLength];
|
||||
in.readBytes(data);
|
||||
|
||||
Object obj = serializer.deserialize(data, genericClass);
|
||||
out.add(obj);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty.codec;
|
||||
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
/**
|
||||
* encoder
|
||||
*
|
||||
* @author xuxueli 2015-10-29 19:43:00
|
||||
*/
|
||||
public class NettyEncoder extends MessageToByteEncoder<Object> {
|
||||
|
||||
private Class<?> genericClass;
|
||||
private Serializer serializer;
|
||||
|
||||
public NettyEncoder(Class<?> genericClass, final Serializer serializer) {
|
||||
this.genericClass = genericClass;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {
|
||||
if (genericClass.isInstance(in)) {
|
||||
byte[] data = serializer.serialize(in);
|
||||
out.writeInt(data.length);
|
||||
out.writeBytes(data);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty.server;
|
||||
|
||||
import com.czsj.rpc.remoting.net.Server;
|
||||
import com.czsj.rpc.remoting.net.impl.netty.codec.NettyDecoder;
|
||||
import com.czsj.rpc.remoting.net.impl.netty.codec.NettyEncoder;
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import com.czsj.rpc.util.ThreadPoolUtil;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* netty rpc server
|
||||
*
|
||||
* @author xuxueli 2015-10-29 18:17:14
|
||||
*/
|
||||
public class NettyServer extends Server {
|
||||
|
||||
private Thread thread;
|
||||
|
||||
@Override
|
||||
public void start(final XxlRpcProviderFactory xxlRpcProviderFactory) throws Exception {
|
||||
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// param
|
||||
final ThreadPoolExecutor serverHandlerPool = ThreadPoolUtil.makeServerThreadPool(
|
||||
NettyServer.class.getSimpleName(),
|
||||
xxlRpcProviderFactory.getCorePoolSize(),
|
||||
xxlRpcProviderFactory.getMaxPoolSize());
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
try {
|
||||
// start server
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel channel) throws Exception {
|
||||
channel.pipeline()
|
||||
.addLast(new IdleStateHandler(0,0, Beat.BEAT_INTERVAL*3, TimeUnit.SECONDS)) // beat 3N, close if idle
|
||||
.addLast(new NettyDecoder(XxlRpcRequest.class, xxlRpcProviderFactory.getSerializerInstance()))
|
||||
.addLast(new NettyEncoder(XxlRpcResponse.class, xxlRpcProviderFactory.getSerializerInstance()))
|
||||
.addLast(new NettyServerHandler(xxlRpcProviderFactory, serverHandlerPool));
|
||||
}
|
||||
})
|
||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true);
|
||||
|
||||
// bind
|
||||
ChannelFuture future = bootstrap.bind(xxlRpcProviderFactory.getPort()).sync();
|
||||
|
||||
logger.info(">>>>>>>>>>> xxl-rpc remoting server start success, nettype = {}, port = {}", NettyServer.class.getName(), xxlRpcProviderFactory.getPort());
|
||||
onStarted();
|
||||
|
||||
// wait util stop
|
||||
future.channel().closeFuture().sync();
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e instanceof InterruptedException) {
|
||||
logger.info(">>>>>>>>>>> xxl-rpc remoting server stop.");
|
||||
} else {
|
||||
logger.error(">>>>>>>>>>> xxl-rpc remoting server error.", e);
|
||||
}
|
||||
} finally {
|
||||
|
||||
// stop
|
||||
try {
|
||||
serverHandlerPool.shutdown(); // shutdownNow
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
try {
|
||||
workerGroup.shutdownGracefully();
|
||||
bossGroup.shutdownGracefully();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
|
||||
// destroy server thread
|
||||
if (thread != null && thread.isAlive()) {
|
||||
thread.interrupt();
|
||||
}
|
||||
|
||||
// on stop
|
||||
onStopped();
|
||||
logger.info(">>>>>>>>>>> xxl-rpc remoting server destroy success.");
|
||||
}
|
||||
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty.server;
|
||||
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import com.czsj.rpc.util.ThrowableUtil;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
|
||||
/**
|
||||
* netty server handler
|
||||
*
|
||||
* @author xuxueli 2015-10-29 20:07:37
|
||||
*/
|
||||
public class NettyServerHandler extends SimpleChannelInboundHandler<XxlRpcRequest> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
|
||||
|
||||
private XxlRpcProviderFactory xxlRpcProviderFactory;
|
||||
private ThreadPoolExecutor serverHandlerPool;
|
||||
|
||||
public NettyServerHandler(final XxlRpcProviderFactory xxlRpcProviderFactory, final ThreadPoolExecutor serverHandlerPool) {
|
||||
this.xxlRpcProviderFactory = xxlRpcProviderFactory;
|
||||
this.serverHandlerPool = serverHandlerPool;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void channelRead0(final ChannelHandlerContext ctx, final XxlRpcRequest xxlRpcRequest) throws Exception {
|
||||
|
||||
// filter beat
|
||||
if (Beat.BEAT_ID.equalsIgnoreCase(xxlRpcRequest.getRequestId())){
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc provider netty server read beat-ping.");
|
||||
return;
|
||||
}
|
||||
|
||||
// do invoke
|
||||
try {
|
||||
serverHandlerPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// invoke + response
|
||||
XxlRpcResponse xxlRpcResponse = xxlRpcProviderFactory.invokeService(xxlRpcRequest);
|
||||
|
||||
ctx.writeAndFlush(xxlRpcResponse);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// catch error
|
||||
XxlRpcResponse xxlRpcResponse = new XxlRpcResponse();
|
||||
xxlRpcResponse.setRequestId(xxlRpcRequest.getRequestId());
|
||||
xxlRpcResponse.setErrorMsg(ThrowableUtil.toString(e));
|
||||
|
||||
ctx.writeAndFlush(xxlRpcResponse);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
logger.error(">>>>>>>>>>> xxl-rpc provider netty server caught exception", cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (evt instanceof IdleStateEvent){
|
||||
ctx.channel().close(); // beat 3N, close if idle
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc provider netty server close an idle channel.");
|
||||
} else {
|
||||
super.userEventTriggered(ctx, evt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty_http.client;
|
||||
|
||||
import com.czsj.rpc.remoting.net.Client;
|
||||
import com.czsj.rpc.remoting.net.common.ConnectClient;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
|
||||
/**
|
||||
* netty_http client
|
||||
*
|
||||
* @author xuxueli 2015-11-24 22:25:15
|
||||
*/
|
||||
public class NettyHttpClient extends Client {
|
||||
|
||||
private Class<? extends ConnectClient> connectClientImpl = NettyHttpConnectClient.class;
|
||||
|
||||
@Override
|
||||
public void asyncSend(String address, XxlRpcRequest xxlRpcRequest) throws Exception {
|
||||
ConnectClient.asyncSend(xxlRpcRequest, address, connectClientImpl, xxlRpcReferenceBean);
|
||||
}
|
||||
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty_http.client;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* netty_http
|
||||
*
|
||||
* @author xuxueli 2015-11-24 22:25:15
|
||||
*/
|
||||
public class NettyHttpClientHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(NettyHttpClientHandler.class);
|
||||
|
||||
|
||||
private XxlRpcInvokerFactory xxlRpcInvokerFactory;
|
||||
private Serializer serializer;
|
||||
private NettyHttpConnectClient nettyHttpConnectClient;
|
||||
public NettyHttpClientHandler(final XxlRpcInvokerFactory xxlRpcInvokerFactory, Serializer serializer, final NettyHttpConnectClient nettyHttpConnectClient) {
|
||||
this.xxlRpcInvokerFactory = xxlRpcInvokerFactory;
|
||||
this.serializer = serializer;
|
||||
this.nettyHttpConnectClient = nettyHttpConnectClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
|
||||
|
||||
// valid status
|
||||
if (!HttpResponseStatus.OK.equals(msg.status())) {
|
||||
throw new XxlRpcException("xxl-rpc response status invalid.");
|
||||
}
|
||||
|
||||
// response parse
|
||||
byte[] responseBytes = ByteBufUtil.getBytes(msg.content());
|
||||
|
||||
// valid length
|
||||
if (responseBytes.length == 0) {
|
||||
throw new XxlRpcException("xxl-rpc response data empty.");
|
||||
}
|
||||
|
||||
// response deserialize
|
||||
XxlRpcResponse xxlRpcResponse = (XxlRpcResponse) serializer.deserialize(responseBytes, XxlRpcResponse.class);
|
||||
|
||||
// notify response
|
||||
xxlRpcInvokerFactory.notifyInvokerFuture(xxlRpcResponse.getRequestId(), xxlRpcResponse);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
//super.exceptionCaught(ctx, cause);
|
||||
logger.error(">>>>>>>>>>> xxl-rpc netty_http client caught exception", cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
// retry
|
||||
super.channelInactive(ctx);
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (evt instanceof IdleStateEvent){
|
||||
/*ctx.channel().close(); // close idle channel
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty_http client close an idle channel.");*/
|
||||
|
||||
nettyHttpConnectClient.send(Beat.BEAT_PING); // beat N, close if fail(may throw error)
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty_http client send beat-ping.");
|
||||
} else {
|
||||
super.userEventTriggered(ctx, evt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty_http.client;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.net.common.ConnectClient;
|
||||
import com.czsj.rpc.remoting.net.common.NettyConstant;
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* netty_http
|
||||
*
|
||||
* @author xuxueli 2015-11-24 22:25:15
|
||||
*/
|
||||
public class NettyHttpConnectClient extends ConnectClient {
|
||||
|
||||
private EventLoopGroup group;
|
||||
private Channel channel;
|
||||
|
||||
private Serializer serializer;
|
||||
private String address;
|
||||
private String host;
|
||||
private DefaultFullHttpRequest beatRequest;
|
||||
|
||||
@Override
|
||||
public void init(String address, final Serializer serializer, final XxlRpcInvokerFactory xxlRpcInvokerFactory) throws Exception {
|
||||
final NettyHttpConnectClient thisClient = this;
|
||||
|
||||
if (!address.toLowerCase().startsWith("http")) {
|
||||
address = "http://" + address; // IP:PORT, need parse to url
|
||||
}
|
||||
|
||||
this.address = address;
|
||||
URL url = new URL(address);
|
||||
this.host = url.getHost();
|
||||
int port = url.getPort() > -1 ? url.getPort() : 80;
|
||||
|
||||
|
||||
this.group = new NioEventLoopGroup();
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
bootstrap.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel channel) throws Exception {
|
||||
channel.pipeline()
|
||||
.addLast(new IdleStateHandler(0, 0, Beat.BEAT_INTERVAL, TimeUnit.SECONDS)) // beat N, close if fail
|
||||
.addLast(new HttpClientCodec())
|
||||
.addLast(new HttpObjectAggregator(NettyConstant.MAX_LENGTH))
|
||||
.addLast(new NettyHttpClientHandler(xxlRpcInvokerFactory, serializer, thisClient));
|
||||
}
|
||||
})
|
||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
|
||||
this.channel = bootstrap.connect(host, port).sync().channel();
|
||||
|
||||
this.serializer = serializer;
|
||||
|
||||
// valid
|
||||
if (!isValidate()) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty client proxy, connect to server success at host:{}, port:{}", host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidate() {
|
||||
if (this.channel != null) {
|
||||
return this.channel.isActive();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.channel != null && this.channel.isActive()) {
|
||||
this.channel.close(); // if this.channel.isOpen()
|
||||
}
|
||||
if (this.group != null && !this.group.isShutdown()) {
|
||||
this.group.shutdownGracefully();
|
||||
}
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc netty client close.");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void send(XxlRpcRequest xxlRpcRequest) throws Exception {
|
||||
byte[] requestBytes = serializer.serialize(xxlRpcRequest);
|
||||
|
||||
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, new URI(address).getRawPath(), Unpooled.wrappedBuffer(requestBytes));
|
||||
request.headers().set(HttpHeaderNames.HOST, host);
|
||||
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
|
||||
|
||||
this.channel.writeAndFlush(request).sync();
|
||||
}
|
||||
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty_http.server;
|
||||
|
||||
import com.czsj.rpc.remoting.net.Server;
|
||||
import com.czsj.rpc.remoting.net.common.NettyConstant;
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import com.czsj.rpc.util.ThreadPoolUtil;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* netty_http
|
||||
*
|
||||
* @author xuxueli 2015-11-24 22:25:15
|
||||
*/
|
||||
public class NettyHttpServer extends Server {
|
||||
|
||||
private Thread thread;
|
||||
|
||||
@Override
|
||||
public void start(final XxlRpcProviderFactory xxlRpcProviderFactory) {
|
||||
|
||||
thread = new Thread(() -> {
|
||||
// param
|
||||
final ThreadPoolExecutor serverHandlerPool = ThreadPoolUtil.makeServerThreadPool(
|
||||
NettyHttpServer.class.getSimpleName(),
|
||||
xxlRpcProviderFactory.getCorePoolSize(),
|
||||
xxlRpcProviderFactory.getMaxPoolSize());
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
try {
|
||||
// start server
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel channel) {
|
||||
channel.pipeline()
|
||||
.addLast(new IdleStateHandler(0, 0, Beat.BEAT_INTERVAL * 3, TimeUnit.SECONDS)) // beat 3N, close if idle
|
||||
.addLast(new HttpServerCodec())
|
||||
.addLast(new HttpObjectAggregator(NettyConstant.MAX_LENGTH)) // merge request & reponse to FULL
|
||||
.addLast(new NettyHttpServerHandler(xxlRpcProviderFactory, serverHandlerPool));
|
||||
}
|
||||
})
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true);
|
||||
|
||||
// bind
|
||||
ChannelFuture future = bootstrap.bind(xxlRpcProviderFactory.getPort()).sync();
|
||||
|
||||
logger.info(">>>>>>>>>>> xxl-rpc remoting server start success, nettype = {}, port = {}", NettyHttpServer.class.getName(), xxlRpcProviderFactory.getPort());
|
||||
onStarted();
|
||||
|
||||
// wait util stop
|
||||
future.channel().closeFuture().sync();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
if (e instanceof InterruptedException) {
|
||||
logger.info(">>>>>>>>>>> xxl-rpc remoting server stop.");
|
||||
} else {
|
||||
logger.error(">>>>>>>>>>> xxl-rpc remoting server error.", e);
|
||||
}
|
||||
} finally {
|
||||
|
||||
// stop
|
||||
try {
|
||||
serverHandlerPool.shutdown(); // shutdownNow
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
try {
|
||||
workerGroup.shutdownGracefully();
|
||||
bossGroup.shutdownGracefully();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
thread.setDaemon(true); // daemon, service jvm, user thread leave >>> daemon leave >>> jvm leave
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// destroy server thread
|
||||
if (thread != null && thread.isAlive()) {
|
||||
thread.interrupt();
|
||||
}
|
||||
|
||||
// on stop
|
||||
onStopped();
|
||||
logger.info(">>>>>>>>>>> xxl-rpc remoting server destroy success.");
|
||||
}
|
||||
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.impl.netty_http.server;
|
||||
|
||||
import com.czsj.rpc.remoting.net.params.Beat;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import com.czsj.rpc.util.ThrowableUtil;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
|
||||
/**
|
||||
* netty_http
|
||||
*
|
||||
* @author xuxueli 2015-11-24 22:25:15
|
||||
*/
|
||||
public class NettyHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(NettyHttpServerHandler.class);
|
||||
|
||||
|
||||
private XxlRpcProviderFactory xxlRpcProviderFactory;
|
||||
private ThreadPoolExecutor serverHandlerPool;
|
||||
|
||||
public NettyHttpServerHandler(final XxlRpcProviderFactory xxlRpcProviderFactory, final ThreadPoolExecutor serverHandlerPool) {
|
||||
this.xxlRpcProviderFactory = xxlRpcProviderFactory;
|
||||
this.serverHandlerPool = serverHandlerPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(final ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
|
||||
|
||||
// request parse
|
||||
final byte[] requestBytes = ByteBufUtil.getBytes(msg.content()); // byteBuf.toString(io.netty.util.CharsetUtil.UTF_8);
|
||||
final String uri = msg.uri();
|
||||
final boolean keepAlive = HttpUtil.isKeepAlive(msg);
|
||||
|
||||
// do invoke
|
||||
serverHandlerPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
process(ctx, uri, requestBytes, keepAlive);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void process(ChannelHandlerContext ctx, String uri, byte[] requestBytes, boolean keepAlive){
|
||||
String requestId = null;
|
||||
try {
|
||||
if ("/services".equals(uri)) { // services mapping
|
||||
|
||||
// request
|
||||
StringBuilder stringBuffer = new StringBuilder("<ui>");
|
||||
for (String serviceKey: xxlRpcProviderFactory.getServiceData().keySet()) {
|
||||
stringBuffer.append("<li>").append(serviceKey).append(": ").append(xxlRpcProviderFactory.getServiceData().get(serviceKey)).append("</li>");
|
||||
}
|
||||
stringBuffer.append("</ui>");
|
||||
|
||||
// response serialize
|
||||
byte[] responseBytes = stringBuffer.toString().getBytes("UTF-8");
|
||||
|
||||
// response-write
|
||||
writeResponse(ctx, keepAlive, responseBytes);
|
||||
|
||||
} else {
|
||||
System.out.println("=================");
|
||||
// valid
|
||||
if (requestBytes.length == 0) {
|
||||
throw new XxlRpcException("xxl-rpc request data empty.");
|
||||
}
|
||||
|
||||
// request deserialize
|
||||
XxlRpcRequest xxlRpcRequest = (XxlRpcRequest) xxlRpcProviderFactory.getSerializerInstance().deserialize(requestBytes, XxlRpcRequest.class);
|
||||
requestId = xxlRpcRequest.getRequestId();
|
||||
|
||||
// filter beat
|
||||
if (Beat.BEAT_ID.equalsIgnoreCase(xxlRpcRequest.getRequestId())){
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc provider netty_http server read beat-ping.");
|
||||
return;
|
||||
}
|
||||
|
||||
// invoke + response
|
||||
XxlRpcResponse xxlRpcResponse = xxlRpcProviderFactory.invokeService(xxlRpcRequest);
|
||||
|
||||
// response serialize
|
||||
byte[] responseBytes = xxlRpcProviderFactory.getSerializerInstance().serialize(xxlRpcResponse);
|
||||
|
||||
// response-write
|
||||
writeResponse(ctx, keepAlive, responseBytes);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
|
||||
// response error
|
||||
XxlRpcResponse xxlRpcResponse = new XxlRpcResponse();
|
||||
xxlRpcResponse.setRequestId(requestId);
|
||||
xxlRpcResponse.setErrorMsg(ThrowableUtil.toString(e));
|
||||
|
||||
// response serialize
|
||||
byte[] responseBytes = xxlRpcProviderFactory.getSerializerInstance().serialize(xxlRpcResponse);
|
||||
|
||||
// response-write
|
||||
writeResponse(ctx, keepAlive, responseBytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* write response
|
||||
*/
|
||||
private void writeResponse(ChannelHandlerContext ctx, boolean keepAlive, byte[] responseBytes){
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(responseBytes));
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8"); // HttpHeaderValues.TEXT_PLAIN.toString()
|
||||
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
|
||||
if (keepAlive) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
}
|
||||
ctx.writeAndFlush(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
logger.error(">>>>>>>>>>> xxl-rpc provider netty_http server caught exception", cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (evt instanceof IdleStateEvent){
|
||||
ctx.channel().close(); // beat 3N, close if idle
|
||||
logger.debug(">>>>>>>>>>> xxl-rpc provider netty_http server close an idle channel.");
|
||||
} else {
|
||||
super.userEventTriggered(ctx, evt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.params;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2018-10-19
|
||||
*/
|
||||
public abstract class BaseCallback {
|
||||
|
||||
public abstract void run() throws Exception;
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.params;
|
||||
|
||||
/**
|
||||
* beat for keep-alive
|
||||
*
|
||||
* @author xuxueli 2019-09-27
|
||||
*/
|
||||
public final class Beat {
|
||||
|
||||
public static final int BEAT_INTERVAL = 30;
|
||||
public static final String BEAT_ID = "BEAT_PING_PONG";
|
||||
|
||||
public static XxlRpcRequest BEAT_PING;
|
||||
|
||||
static {
|
||||
BEAT_PING = new XxlRpcRequest(){};
|
||||
BEAT_PING.setRequestId(BEAT_ID);
|
||||
}
|
||||
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.params;
|
||||
|
||||
import com.czsj.rpc.remoting.invoker.XxlRpcInvokerFactory;
|
||||
import com.czsj.rpc.remoting.invoker.call.XxlRpcInvokeCallback;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* call back future
|
||||
*
|
||||
* @author xuxueli 2015-11-5 14:26:37
|
||||
*/
|
||||
public class XxlRpcFutureResponse implements Future<XxlRpcResponse> {
|
||||
|
||||
private XxlRpcInvokerFactory invokerFactory;
|
||||
|
||||
// net data
|
||||
private XxlRpcRequest request;
|
||||
private XxlRpcResponse response;
|
||||
|
||||
// future lock
|
||||
private boolean done = false;
|
||||
private Object lock = new Object();
|
||||
|
||||
// callback, can be null
|
||||
private XxlRpcInvokeCallback invokeCallback;
|
||||
|
||||
|
||||
public XxlRpcFutureResponse(final XxlRpcInvokerFactory invokerFactory, XxlRpcRequest request, XxlRpcInvokeCallback invokeCallback) {
|
||||
this.invokerFactory = invokerFactory;
|
||||
this.request = request;
|
||||
this.invokeCallback = invokeCallback;
|
||||
|
||||
// set-InvokerFuture
|
||||
setInvokerFuture();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- response pool ----------------------
|
||||
|
||||
public void setInvokerFuture() {
|
||||
this.invokerFactory.setInvokerFuture(request.getRequestId(), this);
|
||||
}
|
||||
|
||||
public void removeInvokerFuture() {
|
||||
this.invokerFactory.removeInvokerFuture(request.getRequestId());
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- get ----------------------
|
||||
|
||||
public XxlRpcRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public XxlRpcInvokeCallback getInvokeCallback() {
|
||||
return invokeCallback;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- for invoke back ----------------------
|
||||
|
||||
public void setResponse(XxlRpcResponse response) {
|
||||
this.response = response;
|
||||
synchronized (lock) {
|
||||
done = true;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- for invoke ----------------------
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XxlRpcResponse get() throws InterruptedException {
|
||||
try {
|
||||
return get(-1, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
throw new XxlRpcException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XxlRpcResponse get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
|
||||
if (!done) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
if (timeout < 0) {
|
||||
lock.wait();
|
||||
} else {
|
||||
long timeoutMillis = (TimeUnit.MILLISECONDS == unit) ? timeout : TimeUnit.MILLISECONDS.convert(timeout, unit);
|
||||
lock.wait(timeoutMillis);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
throw new XxlRpcException("xxl-rpc, request timeout at:" + System.currentTimeMillis() + ", request:" + request.toString());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.params;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* request
|
||||
*
|
||||
* @author xuxueli 2015-10-29 19:39:12
|
||||
*/
|
||||
public class XxlRpcRequest implements Serializable {
|
||||
private static final long serialVersionUID = 42L;
|
||||
|
||||
private String requestId;
|
||||
private long createMillisTime;
|
||||
private String accessToken;
|
||||
|
||||
private String className;
|
||||
private String methodName;
|
||||
private Class<?>[] parameterTypes;
|
||||
private Object[] parameters;
|
||||
|
||||
private String version;
|
||||
|
||||
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public void setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public long getCreateMillisTime() {
|
||||
return createMillisTime;
|
||||
}
|
||||
|
||||
public void setCreateMillisTime(long createMillisTime) {
|
||||
this.createMillisTime = createMillisTime;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public void setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public Class<?>[] getParameterTypes() {
|
||||
return parameterTypes;
|
||||
}
|
||||
|
||||
public void setParameterTypes(Class<?>[] parameterTypes) {
|
||||
this.parameterTypes = parameterTypes;
|
||||
}
|
||||
|
||||
public Object[] getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public void setParameters(Object[] parameters) {
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "XxlRpcRequest{" +
|
||||
"requestId='" + requestId + '\'' +
|
||||
", createMillisTime=" + createMillisTime +
|
||||
", accessToken='" + accessToken + '\'' +
|
||||
", className='" + className + '\'' +
|
||||
", methodName='" + methodName + '\'' +
|
||||
", parameterTypes=" + Arrays.toString(parameterTypes) +
|
||||
", parameters=" + Arrays.toString(parameters) +
|
||||
", version='" + version + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package com.czsj.rpc.remoting.net.params;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* response
|
||||
*
|
||||
* @author xuxueli 2015-10-29 19:39:54
|
||||
*/
|
||||
public class XxlRpcResponse implements Serializable {
|
||||
private static final long serialVersionUID = 42L;
|
||||
|
||||
|
||||
private String requestId;
|
||||
private String errorMsg;
|
||||
private Object result;
|
||||
|
||||
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public void setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public String getErrorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
public void setErrorMsg(String errorMsg) {
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(Object result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "XxlRpcResponse{" +
|
||||
"requestId='" + requestId + '\'' +
|
||||
", errorMsg='" + errorMsg + '\'' +
|
||||
", result=" + result +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
package com.czsj.rpc.remoting.provider;
|
||||
|
||||
import com.czsj.rpc.registry.ServiceRegistry;
|
||||
import com.czsj.rpc.remoting.net.Server;
|
||||
import com.czsj.rpc.remoting.net.impl.netty.server.NettyServer;
|
||||
import com.czsj.rpc.remoting.net.params.BaseCallback;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcRequest;
|
||||
import com.czsj.rpc.remoting.net.params.XxlRpcResponse;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import com.czsj.rpc.serialize.impl.HessianSerializer;
|
||||
import com.czsj.rpc.util.IpUtil;
|
||||
import com.czsj.rpc.util.NetUtil;
|
||||
import com.czsj.rpc.util.ThrowableUtil;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* provider
|
||||
*
|
||||
* @author xuxueli 2015-10-31 22:54:27
|
||||
*/
|
||||
public class XxlRpcProviderFactory {
|
||||
private static final Logger logger = LoggerFactory.getLogger(XxlRpcProviderFactory.class);
|
||||
|
||||
// ---------------------- config ----------------------
|
||||
|
||||
private Class<? extends Server> server = NettyServer.class;
|
||||
private Class<? extends Serializer> serializer = HessianSerializer.class;
|
||||
|
||||
private int corePoolSize = 60;
|
||||
private int maxPoolSize = 300;
|
||||
|
||||
private String ip = null; // for registry
|
||||
private int port = 7080; // default port
|
||||
private String accessToken = null;
|
||||
|
||||
private Class<? extends ServiceRegistry> serviceRegistry = null;
|
||||
private Map<String, String> serviceRegistryParam = null;
|
||||
|
||||
// set
|
||||
public void setServer(Class<? extends Server> server) {
|
||||
this.server = server;
|
||||
}
|
||||
public void setSerializer(Class<? extends Serializer> serializer) {
|
||||
this.serializer = serializer;
|
||||
}
|
||||
public void setCorePoolSize(int corePoolSize) {
|
||||
this.corePoolSize = corePoolSize;
|
||||
}
|
||||
public void setMaxPoolSize(int maxPoolSize) {
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
}
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public void setServiceRegistry(Class<? extends ServiceRegistry> serviceRegistry) {
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
}
|
||||
|
||||
public void setServiceRegistryParam(Map<String, String> serviceRegistryParam) {
|
||||
this.serviceRegistryParam = serviceRegistryParam;
|
||||
}
|
||||
|
||||
// get
|
||||
public Serializer getSerializerInstance() {
|
||||
return serializerInstance;
|
||||
}
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
public int getCorePoolSize() {
|
||||
return corePoolSize;
|
||||
}
|
||||
public int getMaxPoolSize() {
|
||||
return maxPoolSize;
|
||||
}
|
||||
|
||||
// ---------------------- start / stop ----------------------
|
||||
|
||||
private Server serverInstance;
|
||||
private Serializer serializerInstance;
|
||||
private ServiceRegistry serviceRegistryInstance;
|
||||
private String serviceAddress;
|
||||
|
||||
public void start() throws Exception {
|
||||
|
||||
// valid
|
||||
if (this.server == null) {
|
||||
throw new XxlRpcException("xxl-rpc provider server missing.");
|
||||
}
|
||||
if (this.serializer==null) {
|
||||
throw new XxlRpcException("xxl-rpc provider serializer missing.");
|
||||
}
|
||||
if (!(this.corePoolSize>0 && this.maxPoolSize>0 && this.maxPoolSize>=this.corePoolSize)) {
|
||||
this.corePoolSize = 60;
|
||||
this.maxPoolSize = 300;
|
||||
}
|
||||
if (this.ip == null) {
|
||||
this.ip = IpUtil.getIp();
|
||||
}
|
||||
if (this.port <= 0) {
|
||||
this.port = 7080;
|
||||
}
|
||||
if (NetUtil.isPortUsed(this.port)) {
|
||||
throw new XxlRpcException("xxl-rpc provider port["+ this.port +"] is used.");
|
||||
}
|
||||
|
||||
// init serializerInstance
|
||||
this.serializerInstance = serializer.newInstance();
|
||||
|
||||
// start server
|
||||
serviceAddress = IpUtil.getIpPort(this.ip, port);
|
||||
serverInstance = server.newInstance();
|
||||
serverInstance.setStartedCallback(new BaseCallback() { // serviceRegistry started
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
// start registry
|
||||
if (serviceRegistry != null) {
|
||||
serviceRegistryInstance = serviceRegistry.newInstance();
|
||||
serviceRegistryInstance.start(serviceRegistryParam);
|
||||
if (serviceData.size() > 0) {
|
||||
serviceRegistryInstance.registry(serviceData.keySet(), serviceAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
serverInstance.setStopedCallback(new BaseCallback() { // serviceRegistry stoped
|
||||
@Override
|
||||
public void run() {
|
||||
// stop registry
|
||||
if (serviceRegistryInstance != null) {
|
||||
if (serviceData.size() > 0) {
|
||||
serviceRegistryInstance.remove(serviceData.keySet(), serviceAddress);
|
||||
}
|
||||
serviceRegistryInstance.stop();
|
||||
serviceRegistryInstance = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
serverInstance.start(this);
|
||||
}
|
||||
|
||||
public void stop() throws Exception {
|
||||
// stop server
|
||||
serverInstance.stop();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- server invoke ----------------------
|
||||
|
||||
/**
|
||||
* init local rpc service map
|
||||
*/
|
||||
private Map<String, Object> serviceData = new HashMap<String, Object>();
|
||||
public Map<String, Object> getServiceData() {
|
||||
return serviceData;
|
||||
}
|
||||
|
||||
/**
|
||||
* make service key
|
||||
*
|
||||
* @param iface
|
||||
* @param version
|
||||
* @return
|
||||
*/
|
||||
public static String makeServiceKey(String iface, String version){
|
||||
String serviceKey = iface;
|
||||
if (version!=null && version.trim().length()>0) {
|
||||
serviceKey += "#".concat(version);
|
||||
}
|
||||
return serviceKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* add service
|
||||
*
|
||||
* @param iface
|
||||
* @param version
|
||||
* @param serviceBean
|
||||
*/
|
||||
public void addService(String iface, String version, Object serviceBean){
|
||||
String serviceKey = makeServiceKey(iface, version);
|
||||
serviceData.put(serviceKey, serviceBean);
|
||||
|
||||
logger.info(">>>>>>>>>>> xxl-rpc, provider factory add service success. serviceKey = {}, serviceBean = {}", serviceKey, serviceBean.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* invoke service
|
||||
*
|
||||
* @param xxlRpcRequest
|
||||
* @return
|
||||
*/
|
||||
public XxlRpcResponse invokeService(XxlRpcRequest xxlRpcRequest) {
|
||||
|
||||
// make response
|
||||
XxlRpcResponse xxlRpcResponse = new XxlRpcResponse();
|
||||
xxlRpcResponse.setRequestId(xxlRpcRequest.getRequestId());
|
||||
|
||||
// match service bean
|
||||
String serviceKey = makeServiceKey(xxlRpcRequest.getClassName(), xxlRpcRequest.getVersion());
|
||||
Object serviceBean = serviceData.get(serviceKey);
|
||||
|
||||
// valid
|
||||
if (serviceBean == null) {
|
||||
xxlRpcResponse.setErrorMsg("The serviceKey["+ serviceKey +"] not found.");
|
||||
return xxlRpcResponse;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - xxlRpcRequest.getCreateMillisTime() > 3*60*1000) {
|
||||
xxlRpcResponse.setErrorMsg("The timestamp difference between admin and executor exceeds the limit.");
|
||||
return xxlRpcResponse;
|
||||
}
|
||||
if (accessToken!=null && accessToken.trim().length()>0 && !accessToken.trim().equals(xxlRpcRequest.getAccessToken())) {
|
||||
xxlRpcResponse.setErrorMsg("The access token[" + xxlRpcRequest.getAccessToken() + "] is wrong.");
|
||||
return xxlRpcResponse;
|
||||
}
|
||||
|
||||
try {
|
||||
// invoke
|
||||
Class<?> serviceClass = serviceBean.getClass();
|
||||
String methodName = xxlRpcRequest.getMethodName();
|
||||
Class<?>[] parameterTypes = xxlRpcRequest.getParameterTypes();
|
||||
Object[] parameters = xxlRpcRequest.getParameters();
|
||||
|
||||
Method method = serviceClass.getMethod(methodName, parameterTypes);
|
||||
method.setAccessible(true);
|
||||
Object result = method.invoke(serviceBean, parameters);
|
||||
|
||||
/*FastClass serviceFastClass = FastClass.create(serviceClass);
|
||||
FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
|
||||
Object result = serviceFastMethod.invoke(serviceBean, parameters);*/
|
||||
|
||||
xxlRpcResponse.setResult(result);
|
||||
} catch (Throwable t) {
|
||||
// catch error
|
||||
logger.error("xxl-rpc provider invokeService error.", t);
|
||||
xxlRpcResponse.setErrorMsg(ThrowableUtil.toString(t));
|
||||
}
|
||||
|
||||
return xxlRpcResponse;
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package com.czsj.rpc.remoting.provider.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* rpc service annotation, skeleton of stub ("@Inherited" allow service use "Transactional")
|
||||
*
|
||||
* @author 2015-10-29 19:44:33
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface XxlRpcService {
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
String version() default "";
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package com.czsj.rpc.remoting.provider.impl;
|
||||
|
||||
import com.czsj.rpc.remoting.provider.XxlRpcProviderFactory;
|
||||
import com.czsj.rpc.remoting.provider.annotation.XxlRpcService;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* xxl-rpc provider (for spring)
|
||||
*
|
||||
* @author xuxueli 2018-10-18 18:09:20
|
||||
*/
|
||||
public class XxlRpcSpringProviderFactory extends XxlRpcProviderFactory
|
||||
implements ApplicationContextAware, InitializingBean,DisposableBean {
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
||||
Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(XxlRpcService.class);
|
||||
if (serviceBeanMap!=null && serviceBeanMap.size()>0) {
|
||||
for (Object serviceBean : serviceBeanMap.values()) {
|
||||
// valid
|
||||
if (serviceBean.getClass().getInterfaces().length ==0) {
|
||||
throw new XxlRpcException("xxl-rpc, service(XxlRpcService) must inherit interface.");
|
||||
}
|
||||
// add service
|
||||
XxlRpcService xxlRpcService = serviceBean.getClass().getAnnotation(XxlRpcService.class);
|
||||
|
||||
String iface = serviceBean.getClass().getInterfaces()[0].getName();
|
||||
String version = xxlRpcService.version();
|
||||
|
||||
super.addService(iface, version, serviceBean);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO,addServices by api + prop
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
super.stop();
|
||||
}
|
||||
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package com.czsj.rpc.serialize;
|
||||
|
||||
/**
|
||||
* serializer
|
||||
*
|
||||
* Tips:模板方法模式:定义一个操作中算法的骨架(或称为顶级逻辑),将一些步骤(或称为基本方法)的执行延迟到其子类中;
|
||||
* Tips:基本方法:抽象方法 + 具体方法final + 钩子方法;
|
||||
* Tips:Enum 时最好的单例方案;枚举单例会初始化全部实现,此处改为托管Class,避免无效的实例化;
|
||||
*
|
||||
* @author xuxueli 2015-10-30 21:02:55
|
||||
*/
|
||||
public abstract class Serializer {
|
||||
|
||||
public abstract <T> byte[] serialize(T obj);
|
||||
|
||||
public abstract <T> Object deserialize(byte[] bytes, Class<T> clazz);
|
||||
|
||||
/*public enum SerializeEnum {
|
||||
HESSIAN(HessianSerializer.class),
|
||||
HESSIAN1(Hessian1Serializer.class);
|
||||
|
||||
private Class<? extends Serializer> serializerClass;
|
||||
private SerializeEnum (Class<? extends Serializer> serializerClass) {
|
||||
this.serializerClass = serializerClass;
|
||||
}
|
||||
|
||||
public Serializer getSerializer() {
|
||||
try {
|
||||
return serializerClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new XxlRpcException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static SerializeEnum match(String name, SerializeEnum defaultSerializer){
|
||||
for (SerializeEnum item : SerializeEnum.values()) {
|
||||
if (item.name().equals(name)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return defaultSerializer;
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package com.czsj.rpc.serialize.impl;
|
||||
|
||||
import com.caucho.hessian.io.Hessian2Input;
|
||||
import com.caucho.hessian.io.Hessian2Output;
|
||||
import com.czsj.rpc.serialize.Serializer;
|
||||
import com.czsj.rpc.util.XxlRpcException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* hessian serialize
|
||||
* @author xuxueli 2015-9-26 02:53:29
|
||||
*/
|
||||
public class HessianSerializer extends Serializer {
|
||||
|
||||
@Override
|
||||
public <T> byte[] serialize(T obj) {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
Hessian2Output ho = new Hessian2Output(os);
|
||||
try {
|
||||
ho.writeObject(obj);
|
||||
ho.flush();
|
||||
byte[] result = os.toByteArray();
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new XxlRpcException(e);
|
||||
} finally {
|
||||
try {
|
||||
ho.close();
|
||||
} catch (IOException e) {
|
||||
throw new XxlRpcException(e);
|
||||
}
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
throw new XxlRpcException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Object deserialize(byte[] bytes, Class<T> clazz) {
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
|
||||
Hessian2Input hi = new Hessian2Input(is);
|
||||
try {
|
||||
Object result = hi.readObject();
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new XxlRpcException(e);
|
||||
} finally {
|
||||
try {
|
||||
hi.close();
|
||||
} catch (Exception e) {
|
||||
throw new XxlRpcException(e);
|
||||
}
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
throw new XxlRpcException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package com.czsj.rpc.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: Class的工具类
|
||||
**/
|
||||
public class ClassUtil {
|
||||
|
||||
private static final HashMap<String, Class<?>> primClasses = new HashMap<>();
|
||||
|
||||
static {
|
||||
primClasses.put("boolean", boolean.class);
|
||||
primClasses.put("byte", byte.class);
|
||||
primClasses.put("char", char.class);
|
||||
primClasses.put("short", short.class);
|
||||
primClasses.put("int", int.class);
|
||||
primClasses.put("long", long.class);
|
||||
primClasses.put("float", float.class);
|
||||
primClasses.put("double", double.class);
|
||||
primClasses.put("void", void.class);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param className 类名
|
||||
* @return 反射得到类的Class
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static Class<?> resolveClass(String className) throws ClassNotFoundException {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Class<?> cl = primClasses.get(className);
|
||||
if (cl != null) {
|
||||
return cl;
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
package com.czsj.rpc.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: IP的工具类
|
||||
**/
|
||||
public class IpUtil {
|
||||
private static final Logger logger = LoggerFactory.getLogger(IpUtil.class);
|
||||
|
||||
private static final String ANYHOST_VALUE = "0.0.0.0";
|
||||
private static final String LOCALHOST_VALUE = "127.0.0.1";
|
||||
private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");
|
||||
|
||||
|
||||
private static volatile InetAddress LOCAL_ADDRESS = null;
|
||||
|
||||
|
||||
private static InetAddress toValidAddress(InetAddress address) {
|
||||
if (address instanceof Inet6Address) {
|
||||
Inet6Address v6Address = (Inet6Address) address;
|
||||
if (isPreferIPV6Address()) {
|
||||
return normalizeV6Address(v6Address);
|
||||
}
|
||||
}
|
||||
if (isValidV4Address(address)) {
|
||||
return address;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static boolean isPreferIPV6Address() {
|
||||
return Boolean.getBoolean("java.net.preferIPv6Addresses");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param address
|
||||
* @return 判断是不是有效的IPV4的地址
|
||||
*/
|
||||
private static boolean isValidV4Address(InetAddress address) {
|
||||
if (address == null || address.isLoopbackAddress()) {
|
||||
return false;
|
||||
}
|
||||
String name = address.getHostAddress();
|
||||
return (name != null && IP_PATTERN.matcher(name).matches() && !ANYHOST_VALUE.equals(name) && !LOCALHOST_VALUE
|
||||
.equals(name));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param address
|
||||
* @return 返回IPV6的地址
|
||||
*/
|
||||
private static InetAddress normalizeV6Address(Inet6Address address) {
|
||||
String addr = address.getHostAddress();
|
||||
int i = addr.lastIndexOf('%');
|
||||
if (i > 0) {
|
||||
try {
|
||||
return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId());
|
||||
} catch (UnknownHostException e) {
|
||||
logger.debug("Unknown IPV6 address: ", e);
|
||||
}
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return 返回本地的IP
|
||||
*/
|
||||
private static InetAddress getLocalAddress0() {
|
||||
InetAddress localAddress = null;
|
||||
try {
|
||||
localAddress = InetAddress.getLocalHost();
|
||||
InetAddress addressItem = toValidAddress(localAddress);
|
||||
if (addressItem != null) {
|
||||
return addressItem;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||
if (null == interfaces) {
|
||||
return localAddress;
|
||||
}
|
||||
while (interfaces.hasMoreElements()) {
|
||||
try {
|
||||
NetworkInterface network = interfaces.nextElement();
|
||||
if (network.isLoopback() || network.isVirtual() || !network.isUp()) {
|
||||
continue;
|
||||
}
|
||||
Enumeration<InetAddress> addresses = network.getInetAddresses();
|
||||
while (addresses.hasMoreElements()) {
|
||||
try {
|
||||
InetAddress addressItem = toValidAddress(addresses.nextElement());
|
||||
if (addressItem != null) {
|
||||
try {
|
||||
if (addressItem.isReachable(100)) {
|
||||
return addressItem;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return 返回本地的IP
|
||||
*/
|
||||
public static InetAddress getLocalAddress() {
|
||||
if (LOCAL_ADDRESS != null) {
|
||||
return LOCAL_ADDRESS;
|
||||
}
|
||||
InetAddress localAddress = getLocalAddress0();
|
||||
LOCAL_ADDRESS = localAddress;
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return 返回本地的IP
|
||||
*/
|
||||
public static String getIp() {
|
||||
return getLocalAddress().getHostAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param port
|
||||
* @return 返回本地IP:port
|
||||
*/
|
||||
public static String getIpPort(int port) {
|
||||
String ip = getIp();
|
||||
return getIpPort(ip, port);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ip
|
||||
* @param port
|
||||
* @return ip:port
|
||||
*/
|
||||
public static String getIpPort(String ip, int port) {
|
||||
if (ip == null) {
|
||||
return null;
|
||||
}
|
||||
return ip.concat(":").concat(String.valueOf(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address
|
||||
* @return ip和port的数组
|
||||
*/
|
||||
public static Object[] parseIpPort(String address) {
|
||||
String[] array = address.split(":");
|
||||
|
||||
String host = array[0];
|
||||
int port = Integer.parseInt(array[1]);
|
||||
|
||||
return new Object[]{host, port};
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package com.czsj.rpc.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: 端口的工具类
|
||||
**/
|
||||
public class NetUtil {
|
||||
private static Logger logger = LoggerFactory.getLogger(NetUtil.class);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param defaultPort
|
||||
* @return 可用的端口
|
||||
*/
|
||||
public static int findAvailablePort(int defaultPort) {
|
||||
int portTmp = defaultPort;
|
||||
while (portTmp < 65535) {
|
||||
if (!isPortUsed(portTmp)) {
|
||||
return portTmp;
|
||||
} else {
|
||||
portTmp++;
|
||||
}
|
||||
}
|
||||
portTmp = defaultPort--;
|
||||
while (portTmp > 0) {
|
||||
if (!isPortUsed(portTmp)) {
|
||||
return portTmp;
|
||||
} else {
|
||||
portTmp--;
|
||||
}
|
||||
}
|
||||
throw new XxlRpcException("no available port.");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param port
|
||||
* @return 可用返回true, 否则返回false
|
||||
*/
|
||||
public static boolean isPortUsed(int port) {
|
||||
boolean used;
|
||||
ServerSocket serverSocket = null;
|
||||
try {
|
||||
serverSocket = new ServerSocket(port);
|
||||
used = false;
|
||||
} catch (IOException e) {
|
||||
logger.info(">>>>>>>>>>> xxl-rpc, port[{}] is in use.", port);
|
||||
used = true;
|
||||
} finally {
|
||||
if (serverSocket != null) {
|
||||
try {
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {
|
||||
logger.info("");
|
||||
}
|
||||
}
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package com.czsj.rpc.util;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: 自定义线程池
|
||||
**/
|
||||
public class ThreadPoolUtil {
|
||||
|
||||
public static ThreadPoolExecutor makeServerThreadPool(final String serverType, int corePoolSize, int maxPoolSize) {
|
||||
ThreadPoolExecutor serverHandlerPool = new ThreadPoolExecutor(corePoolSize, maxPoolSize, 60L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(1000),
|
||||
r -> new Thread(r, "xxl-rpc, " + serverType + "-serverHandlerPool-" + r.hashCode()), (r, executor) -> {
|
||||
throw new XxlRpcException("xxl-rpc " + serverType + " Thread pool is EXHAUSTED!");
|
||||
}); // default maxThreads 300, minThreads 60
|
||||
|
||||
return serverHandlerPool;
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.czsj.rpc.util;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: 将异常转换为String
|
||||
**/
|
||||
public class ThrowableUtil {
|
||||
|
||||
public static String toString(Throwable e) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(stringWriter));
|
||||
String errorMsg = stringWriter.toString();
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package com.czsj.rpc.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: 自定义异常类
|
||||
**/
|
||||
public class XxlRpcException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 42L;
|
||||
|
||||
public XxlRpcException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public XxlRpcException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
public XxlRpcException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package com.czsj.rpc.util.json;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: Json的父类
|
||||
**/
|
||||
public class BasicJson {
|
||||
|
||||
private static final BasicJsonReader basicJsonReader = new BasicJsonReader();
|
||||
private static final BasicJsonwriter basicJsonwriter = new BasicJsonwriter();
|
||||
|
||||
/**
|
||||
* object to json
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static String toJson(Object object) {
|
||||
return basicJsonwriter.toJson(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse json to map
|
||||
*
|
||||
* @param json
|
||||
* @return only for filed type "null、ArrayList、LinkedHashMap、String、Long、Double、..."
|
||||
*/
|
||||
public static Map<String, Object> parseMap(String json) {
|
||||
return basicJsonReader.parseMap(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* json to List
|
||||
*
|
||||
* @param json
|
||||
* @return
|
||||
*/
|
||||
public static List<Object> parseList(String json) {
|
||||
return basicJsonReader.parseList(json);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("msg", "success");
|
||||
result.put("arr", Arrays.asList("111", "222"));
|
||||
result.put("float", 1.11f);
|
||||
result.put("temp", null);
|
||||
|
||||
String json = toJson(result);
|
||||
System.out.println(json);
|
||||
|
||||
Map<String, Object> mapObj = parseMap(json);
|
||||
System.out.println(mapObj);
|
||||
|
||||
List<Object> listInt = parseList("[111,222,33]");
|
||||
System.out.println(listInt);
|
||||
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
package com.czsj.rpc.util.json;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: JsonReader的父类
|
||||
**/
|
||||
public class BasicJsonReader {
|
||||
private static Logger logger = LoggerFactory.getLogger(BasicJsonwriter.class);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param json 类型{}
|
||||
* @return 将JSON转换为Map
|
||||
*/
|
||||
public Map<String, Object> parseMap(String json) {
|
||||
if (json != null) {
|
||||
json = json.trim();
|
||||
if (json.startsWith("{")) {
|
||||
return parseMapInternal(json);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot parse JSON");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param json []
|
||||
* @return 将Json数组转换为List集合
|
||||
*/
|
||||
public List<Object> parseList(String json) {
|
||||
if (json != null) {
|
||||
json = json.trim();
|
||||
if (json.startsWith("[")) {
|
||||
return parseListInternal(json);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot parse JSON");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param json
|
||||
* @return
|
||||
*/
|
||||
private List<Object> parseListInternal(String json) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '[');
|
||||
for (String value : tokenize(json)) {
|
||||
list.add(parseInternal(value));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private Object parseInternal(String json) {
|
||||
if (json.equals("null")) {
|
||||
return null;
|
||||
}
|
||||
if (json.startsWith("[")) {
|
||||
return parseListInternal(json);
|
||||
}
|
||||
if (json.startsWith("{")) {
|
||||
return parseMapInternal(json);
|
||||
}
|
||||
if (json.startsWith("\"")) {
|
||||
return trimTrailingCharacter(trimLeadingCharacter(json, '"'), '"');
|
||||
}
|
||||
try {
|
||||
return Long.valueOf(json);
|
||||
} catch (NumberFormatException ex) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
return Double.valueOf(json);
|
||||
} catch (NumberFormatException ex) {
|
||||
// ignore
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
private Map<String, Object> parseMapInternal(String json) {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
json = trimLeadingCharacter(trimTrailingCharacter(json, '}'), '{');
|
||||
for (String pair : tokenize(json)) {
|
||||
String[] values = trimArrayElements(split(pair, ":"));
|
||||
String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"');
|
||||
Object value = parseInternal(values[1]);
|
||||
map.put(key, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// append start
|
||||
private static String[] split(String toSplit, String delimiter) {
|
||||
if (toSplit != null && !toSplit.isEmpty() && delimiter != null && !delimiter.isEmpty()) {
|
||||
int offset = toSplit.indexOf(delimiter);
|
||||
if (offset < 0) {
|
||||
return null;
|
||||
} else {
|
||||
String beforeDelimiter = toSplit.substring(0, offset);
|
||||
String afterDelimiter = toSplit.substring(offset + delimiter.length());
|
||||
return new String[]{beforeDelimiter, afterDelimiter};
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] trimArrayElements(String[] array) {
|
||||
if (array == null || array.length == 0) {
|
||||
return new String[0];
|
||||
} else {
|
||||
String[] result = new String[array.length];
|
||||
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
String element = array[i];
|
||||
result[i] = element != null ? element.trim() : null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<String> tokenize(String json) {
|
||||
List<String> list = new ArrayList<>();
|
||||
int index = 0;
|
||||
int inObject = 0;
|
||||
int inList = 0;
|
||||
boolean inValue = false;
|
||||
boolean inEscape = false;
|
||||
StringBuilder build = new StringBuilder();
|
||||
while (index < json.length()) {
|
||||
char current = json.charAt(index);
|
||||
if (inEscape) {
|
||||
build.append(current);
|
||||
index++;
|
||||
inEscape = false;
|
||||
continue;
|
||||
}
|
||||
if (current == '{') {
|
||||
inObject++;
|
||||
}
|
||||
if (current == '}') {
|
||||
inObject--;
|
||||
}
|
||||
if (current == '[') {
|
||||
inList++;
|
||||
}
|
||||
if (current == ']') {
|
||||
inList--;
|
||||
}
|
||||
if (current == '"') {
|
||||
inValue = !inValue;
|
||||
}
|
||||
if (current == ',' && inObject == 0 && inList == 0 && !inValue) {
|
||||
list.add(build.toString());
|
||||
build.setLength(0);
|
||||
} else if (current == '\\') {
|
||||
inEscape = true;
|
||||
} else {
|
||||
build.append(current);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if (build.length() > 0) {
|
||||
list.add(build.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// plugin util
|
||||
private static String trimTrailingCharacter(String string, char c) {
|
||||
if (string.length() > 0 && string.charAt(string.length() - 1) == c) {
|
||||
return string.substring(0, string.length() - 1);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
private static String trimLeadingCharacter(String string, char c) {
|
||||
if (string.length() > 0 && string.charAt(0) == c) {
|
||||
return string.substring(1);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
package com.czsj.rpc.util.json;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author: czsj
|
||||
* @Date: 2022/9/16 11:14
|
||||
* @Description: JsonWriter的父类
|
||||
**/
|
||||
public class BasicJsonwriter {
|
||||
private static Logger logger = LoggerFactory.getLogger(BasicJsonwriter.class);
|
||||
|
||||
|
||||
private static final String STR_SLASH = "\"";
|
||||
private static final String STR_SLASH_STR = "\":";
|
||||
private static final String STR_COMMA = ",";
|
||||
private static final String STR_OBJECT_LEFT = "{";
|
||||
private static final String STR_OBJECT_RIGHT = "}";
|
||||
private static final String STR_ARRAY_LEFT = "[";
|
||||
private static final String STR_ARRAY_RIGHT = "]";
|
||||
|
||||
private static final Map<String, Field[]> cacheFields = new HashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @return 将JSON转换为String字符串
|
||||
*/
|
||||
public String toJson(Object object) {
|
||||
StringBuilder json = new StringBuilder();
|
||||
try {
|
||||
writeObjItem(null, object, json);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
String str = json.toString();
|
||||
if (str.contains("\n")) {
|
||||
str = str.replaceAll("\\n", "\\\\n");
|
||||
}
|
||||
if (str.contains("\t")) {
|
||||
str = str.replaceAll("\\t", "\\\\t");
|
||||
}
|
||||
if (str.contains("\r")) {
|
||||
str = str.replaceAll("\\r", "\\\\r");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回json格式的键值对
|
||||
* @param key :键
|
||||
* @param value :值
|
||||
* @param json :返回值 "key":value or value
|
||||
*/
|
||||
private void writeObjItem(String key, Object value, StringBuilder json) {
|
||||
|
||||
// "key:"
|
||||
if (key != null) {
|
||||
json.append(STR_SLASH).append(key).append(STR_SLASH_STR);
|
||||
}
|
||||
|
||||
// val
|
||||
if (value == null) {
|
||||
json.append("null");
|
||||
} else if (value instanceof String || value instanceof Byte || value instanceof CharSequence) {
|
||||
// string
|
||||
json.append(STR_SLASH).append(value.toString()).append(STR_SLASH);
|
||||
} else if (value instanceof Boolean || value instanceof Short || value instanceof Integer
|
||||
|| value instanceof Long || value instanceof Float || value instanceof Double) {
|
||||
// number
|
||||
json.append(value);
|
||||
} else if (value instanceof Object[] || value instanceof Collection) {
|
||||
// collection | array // Array.getLength(array); // Array.get(array, i);
|
||||
Collection valueColl = null;
|
||||
if (value instanceof Object[]) {
|
||||
Object[] valueArr = (Object[]) value;
|
||||
valueColl = Arrays.asList(valueArr);
|
||||
} else if (value instanceof Collection) {
|
||||
valueColl = (Collection) value;
|
||||
}
|
||||
|
||||
json.append(STR_ARRAY_LEFT);
|
||||
if (valueColl.size() > 0) {
|
||||
for (Object obj : valueColl) {
|
||||
writeObjItem(null, obj, json);
|
||||
json.append(STR_COMMA);
|
||||
}
|
||||
json.delete(json.length() - 1, json.length());
|
||||
}
|
||||
json.append(STR_ARRAY_RIGHT);
|
||||
|
||||
} else if (value instanceof Map) {
|
||||
// map
|
||||
|
||||
Map<?, ?> valueMap = (Map<?, ?>) value;
|
||||
|
||||
json.append(STR_OBJECT_LEFT);
|
||||
if (!valueMap.isEmpty()) {
|
||||
Set<?> keys = valueMap.keySet();
|
||||
for (Object valueMapItemKey : keys) {
|
||||
writeObjItem(valueMapItemKey.toString(), valueMap.get(valueMapItemKey), json);
|
||||
json.append(STR_COMMA);
|
||||
}
|
||||
json.delete(json.length() - 1, json.length());
|
||||
}
|
||||
json.append(STR_OBJECT_RIGHT);
|
||||
} else {
|
||||
json.append(STR_OBJECT_LEFT);
|
||||
Field[] fields = getDeclaredFields(value.getClass());
|
||||
if (fields.length > 0) {
|
||||
for (Field field : fields) {
|
||||
Object fieldObj = getFieldObject(field, value);
|
||||
writeObjItem(field.getName(), fieldObj, json);
|
||||
json.append(STR_COMMA);
|
||||
}
|
||||
json.delete(json.length() - 1, json.length());
|
||||
}
|
||||
|
||||
json.append(STR_OBJECT_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clazz 类
|
||||
* @return 返回类的字段名数组,并缓存
|
||||
*/
|
||||
public synchronized Field[] getDeclaredFields(Class<?> clazz) {
|
||||
String cacheKey = clazz.getName();
|
||||
if (cacheFields.containsKey(cacheKey)) {
|
||||
return cacheFields.get(cacheKey);
|
||||
}
|
||||
Field[] fields = getAllDeclaredFields(clazz); //clazz.getDeclaredFields();
|
||||
cacheFields.put(cacheKey, fields);
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clazz
|
||||
* @return 通过反射返回类的字段名数组
|
||||
*/
|
||||
private Field[] getAllDeclaredFields(Class<?> clazz) {
|
||||
List<Field> list = new ArrayList<Field>();
|
||||
Class<?> current = clazz;
|
||||
|
||||
while (current != null && current != Object.class) {
|
||||
Field[] fields = current.getDeclaredFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
list.add(field);
|
||||
}
|
||||
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
|
||||
return list.toArray(new Field[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param field
|
||||
* @param obj
|
||||
* @return 反射获取字段对象
|
||||
*/
|
||||
private synchronized Object getFieldObject(Field field, Object obj) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return null;
|
||||
} finally {
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user