├── .gitattributes ├── .gitignore ├── .travis.yml ├── DESIGN.md ├── LICENSE ├── README.md ├── bom └── pom.xml ├── child-rpc-core ├── pom.xml └── src │ └── main │ └── java │ └── cn │ └── whforever │ └── core │ ├── cache │ ├── ReflectCache.java │ └── RpcCacheManager.java │ ├── coedc │ └── netty │ │ ├── NettyDecoder.java │ │ └── NettyEncoder.java │ ├── common │ ├── Destroyable.java │ └── Initializable.java │ ├── config │ ├── AbstractIdConfig.java │ ├── ClientConfig.java │ ├── Config.java │ ├── RegistryConfig.java │ └── ServerConfig.java │ ├── exception │ └── ChildRpcRuntimeException.java │ ├── log │ ├── Logger.java │ ├── LoggerFactory.java │ └── SLF4JLoggerImpl.java │ ├── protocol │ ├── bolt │ │ └── server │ │ │ └── BoltServer.java │ └── netty │ │ ├── client │ │ ├── NettyClient.java │ │ ├── NettyClientAbstract.java │ │ └── NettyClientHandler.java │ │ └── server │ │ ├── NettyServerAbstract.java │ │ └── NettyServerHandler.java │ ├── proxy │ ├── ClientProxy.java │ ├── JDKInvocationHandler.java │ ├── Proxy.java │ └── ServerProxy.java │ ├── register │ ├── Registry.java │ └── RegistryFactory.java │ ├── registry │ ├── consul │ │ └── ConsulRegistry.java │ └── zk │ │ ├── ZookeeperRegistry.java │ │ └── ZookeeperRegistryHelper.java │ ├── remote │ ├── client │ │ └── AbstractChildClient.java │ └── server │ │ └── AbstractChildServer.java │ ├── rpc │ ├── RpcCallbackFuture.java │ ├── RpcConstants.java │ ├── RpcInvokerHandler.java │ ├── RpcRequest.java │ ├── RpcResponse.java │ └── RpcRuntimeContext.java │ ├── serialize │ ├── Serializer.java │ └── impl │ │ ├── HessianSerializer.java │ │ ├── JacksonSerializer.java │ │ └── ProtostuffSerializer.java │ └── util │ ├── BeanUtils.java │ ├── ClassLoaderUtils.java │ ├── ClassTypeUtils.java │ ├── ClassUtils.java │ ├── CommonUtils.java │ ├── ExceptionUtils.java │ ├── IOUtils.java │ ├── NetUtils.java │ ├── ReflectUtils.java │ ├── RegistryUtils.java │ ├── StringUtils.java │ └── SystemInfo.java ├── child-rpc-example ├── pom.xml └── src │ └── test │ ├── java │ └── cn │ │ └── whforever │ │ └── example │ │ ├── local │ │ ├── ClientTest.java │ │ └── ServerTest.java │ │ ├── log │ │ └── SLF4JLoggerImplTest.java │ │ ├── service │ │ ├── HelloService.java │ │ └── impl │ │ │ └── HelloServiceImpl.java │ │ └── zookeeper │ │ ├── ZkRegistryTest.java │ │ ├── ZookeeperClientMainTest.java │ │ ├── ZookeeperServerMainTest.java │ │ └── ZookeeperTest.java │ └── resources │ └── log4j.xml ├── pom.xml ├── rpc-1.md └── rpc-2.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Java 2 | *.css linguist-language=Java 3 | *.html linguist-language=Java 4 | *.ftl linguist-language=Java -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | !.mvn/wrapper/* 5 | *.war 6 | *.zip 7 | *.tar 8 | *.tar.gz 9 | 10 | # eclipse ignore 11 | .settings/ 12 | .project 13 | .classpath 14 | 15 | # idea ignore 16 | .idea/ 17 | *.ipr 18 | *.iml 19 | *.iws 20 | 21 | # temp ignore 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk9 5 | script: mvn cobertura:cobertura -DskipTests 6 | 7 | after_success: 8 | - bash <(curl -s https://codecov.io/bash) -t e0747db2-51ac-4ae4-a030-d51f6afeb576 -------------------------------------------------------------------------------- /DESIGN.md: -------------------------------------------------------------------------------- 1 | ## 注册中心 2 | ### 本地直连实现 3 | ### zookeeper 注册中心实现 4 | 5 | ## 序列化 6 | ### json 序列化实现 7 | ### hission 序列化实现 8 | ### JDK 序列化实现 9 | 10 | ## RPC 通信 11 | ### Netty 通信实现 12 | 13 | ## 代理模式 14 | ### JDK动态代理的实现 15 | ### cglib 动态代理实现 16 | ### javassist 动态代理的实现 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 下面是开发该框架的时候产出的两篇博客:
2 | [实现自己的 RPC 框架 (一)](./rpc-1.md)
3 | [实现自己的 RPC 框架 (二)](./rpc-2.md) -------------------------------------------------------------------------------- /child-rpc-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | child-rpc 7 | cn.whforever 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | child-rpc-core 13 | 14 | 15 | 1.6.2 16 | 2.6 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 8 25 | 8 26 | 27 | 28 | 29 | org.codehaus.mojo 30 | cobertura-maven-plugin 31 | 2.7 32 | 33 | true 34 | 35 | xml 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 44 | package 45 | 46 | cobertura 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | io.netty 57 | netty-all 58 | 59 | 60 | 61 | com.ecwid.consul 62 | consul-api 63 | 64 | 65 | 66 | org.slf4j 67 | slf4j-api 68 | 69 | 70 | 71 | org.jboss.resteasy 72 | resteasy-jackson-provider 73 | 74 | 75 | 76 | com.alibaba 77 | fastjson 78 | 1.2.47 79 | 80 | 81 | com.fasterxml.jackson.core 82 | jackson-databind 83 | 84 | 85 | com.caucho 86 | hessian 87 | 4.0.38 88 | 89 | 90 | org.apache.curator 91 | curator-framework 92 | 2.9.1 93 | 94 | 95 | 96 | org.apache.curator 97 | curator-recipes 98 | 2.9.1 99 | 100 | 101 | 102 | 103 | io.protostuff 104 | protostuff-core 105 | ${protostuff.version} 106 | 107 | 108 | io.protostuff 109 | protostuff-runtime 110 | ${protostuff.version} 111 | 112 | 113 | org.objenesis 114 | objenesis 115 | ${objenesis.version} 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/cache/ReflectCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.cache; 18 | 19 | import cn.whforever.core.util.ClassLoaderUtils; 20 | 21 | import java.lang.reflect.Method; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | import java.util.concurrent.ConcurrentMap; 24 | 25 | /** 26 | * 业务要支持多ClassLoader,需要缓存ClassLoader或者方法等相关信息 27 | *

28 | * // TODO 统一的回收实效策略,例如大小限制、时间限制、哪些可以被回收 29 | * 30 | * @author GengZhang 31 | */ 32 | public final class ReflectCache { 33 | 34 | /*----------- ClassLoader Cache ------------*/ 35 | /** 36 | * 应用对应的ClassLoader 37 | */ 38 | static final ConcurrentMap APPNAME_CLASSLOADER_MAP = new ConcurrentHashMap(); 39 | 40 | /** 41 | * 服务对应的ClassLoader 42 | */ 43 | static final ConcurrentMap SERVICE_CLASSLOADER_MAP = new ConcurrentHashMap(); 44 | /** 45 | * String-->Class 缓存 46 | */ 47 | static final ConcurrentMap CLASS_CACHE = new ConcurrentHashMap(); 48 | /** 49 | * Class-->String 缓存 50 | */ 51 | static final ConcurrentMap TYPE_STR_CACHE = new ConcurrentHashMap(); 52 | /** 53 | * 不支持重载的方法对象缓存 {service:{方法名:Method}} 54 | */ 55 | static final ConcurrentMap> NOT_OVERLOAD_METHOD_CACHE = new ConcurrentHashMap>(); 56 | /** 57 | * 不支持重载的方法对象参数签名缓存 {service:{方法名:对象参数签名}} 58 | */ 59 | static final ConcurrentMap> NOT_OVERLOAD_METHOD_SIGS_CACHE = new ConcurrentHashMap>(); 60 | 61 | /*----------- Class Cache ------------*/ 62 | /** 63 | * 方法对象缓存 {service:{方法名#(参数列表):Method}}
64 | * 用于缓存参数列表,不是按接口,是按ServiceUniqueName 65 | */ 66 | final static ConcurrentMap> OVERLOAD_METHOD_CACHE = new ConcurrentHashMap>(); 67 | 68 | /** 69 | * 注册服务所在的ClassLoader 70 | * 71 | * @param appName 应用名 72 | * @param classloader 应用级别ClassLoader 73 | */ 74 | public static void registerAppClassLoader(String appName, ClassLoader classloader) { 75 | APPNAME_CLASSLOADER_MAP.put(appName, classloader); 76 | } 77 | 78 | /** 79 | * 得到服务的自定义ClassLoader 80 | * 81 | * @param appName 应用名 82 | * @return 应用级别ClassLoader 83 | */ 84 | public static ClassLoader getAppClassLoader(String appName) { 85 | ClassLoader appClassLoader = APPNAME_CLASSLOADER_MAP.get(appName); 86 | if (appClassLoader == null) { 87 | return ClassLoaderUtils.getCurrentClassLoader(); 88 | } else { 89 | return appClassLoader; 90 | } 91 | } 92 | 93 | /** 94 | * 注册服务所在的ClassLoader 95 | * 96 | * @param serviceUniqueName 服务唯一名称 97 | * @param classloader 服务级别ClassLoader 98 | */ 99 | public static void registerServiceClassLoader(String serviceUniqueName, ClassLoader classloader) { 100 | SERVICE_CLASSLOADER_MAP.put(serviceUniqueName, classloader); 101 | } 102 | 103 | /** 104 | * 得到服务的自定义ClassLoader 105 | * 106 | * @param serviceUniqueName 服务唯一名称 107 | * @return 服务级别ClassLoader 108 | */ 109 | public static ClassLoader getServiceClassLoader(String serviceUniqueName) { 110 | ClassLoader appClassLoader = SERVICE_CLASSLOADER_MAP.get(serviceUniqueName); 111 | if (appClassLoader == null) { 112 | return ClassLoaderUtils.getCurrentClassLoader(); 113 | } else { 114 | return appClassLoader; 115 | } 116 | } 117 | 118 | /** 119 | * 放入Class缓存 120 | * 121 | * @param typeStr 对象描述 122 | * @param clazz 类 123 | */ 124 | public static void putClassCache(String typeStr, Class clazz) { 125 | CLASS_CACHE.put(typeStr, clazz); 126 | } 127 | 128 | /*----------- Method Cache NOT support overload ------------*/ 129 | 130 | /** 131 | * 得到Class缓存 132 | * 133 | * @param typeStr 对象描述 134 | * @return 类 135 | */ 136 | public static Class getClassCache(String typeStr) { 137 | return CLASS_CACHE.get(typeStr); 138 | } 139 | 140 | /** 141 | * 放入类描述缓存 142 | * 143 | * @param clazz 类 144 | * @param typeStr 对象描述 145 | */ 146 | public static void putTypeStrCache(Class clazz, String typeStr) { 147 | TYPE_STR_CACHE.put(clazz, typeStr); 148 | } 149 | 150 | /** 151 | * 得到类描述缓存 152 | * 153 | * @param clazz 类 154 | * @return 类描述 155 | */ 156 | public static String getTypeStrCache(Class clazz) { 157 | return TYPE_STR_CACHE.get(clazz); 158 | } 159 | 160 | /** 161 | * 往缓存里放入方法 162 | * 163 | * @param serviceName 服务名(非接口名) 164 | * @param method 方法 165 | */ 166 | public static void putMethodCache(String serviceName, Method method) { 167 | ConcurrentHashMap cache = NOT_OVERLOAD_METHOD_CACHE.get(serviceName); 168 | if (cache == null) { 169 | cache = new ConcurrentHashMap(); 170 | ConcurrentHashMap old = NOT_OVERLOAD_METHOD_CACHE.putIfAbsent(serviceName, cache); 171 | if (old != null) { 172 | cache = old; 173 | } 174 | } 175 | cache.putIfAbsent(method.getName(), method); 176 | } 177 | 178 | /** 179 | * 从缓存里获取方法 180 | * 181 | * @param serviceName 服务名(非接口名) 182 | * @param methodName 方法名 183 | * @return 方法 184 | */ 185 | public static Method getMethodCache(String serviceName, String methodName) { 186 | ConcurrentHashMap methods = NOT_OVERLOAD_METHOD_CACHE.get(serviceName); 187 | return methods == null ? null : methods.get(methodName); 188 | } 189 | 190 | /** 191 | * 根据服务名使方法缓存失效 192 | * 193 | * @param serviceName 服务名(非接口名) 194 | */ 195 | public static void invalidateMethodCache(String serviceName) { 196 | NOT_OVERLOAD_METHOD_CACHE.remove(serviceName); 197 | } 198 | 199 | /** 200 | * 往缓存里放入方法参数签名 201 | * 202 | * @param serviceName 服务名(非接口名) 203 | * @param methodName 方法名 204 | * @param argSigs 方法参数签名 205 | */ 206 | public static void putMethodSigsCache(String serviceName, String methodName, String[] argSigs) { 207 | ConcurrentHashMap cacheSigs = NOT_OVERLOAD_METHOD_SIGS_CACHE.get(serviceName); 208 | if (cacheSigs == null) { 209 | cacheSigs = new ConcurrentHashMap(); 210 | ConcurrentHashMap old = NOT_OVERLOAD_METHOD_SIGS_CACHE 211 | .putIfAbsent(serviceName, cacheSigs); 212 | if (old != null) { 213 | cacheSigs = old; 214 | } 215 | } 216 | cacheSigs.putIfAbsent(methodName, argSigs); 217 | } 218 | 219 | /** 220 | * 从缓存里获取方法参数签名 221 | * 222 | * @param serviceName 服务名(非接口名) 223 | * @param methodName 方法名 224 | * @return 方法参数签名 225 | */ 226 | public static String[] getMethodSigsCache(String serviceName, String methodName) { 227 | ConcurrentHashMap methods = NOT_OVERLOAD_METHOD_SIGS_CACHE.get(serviceName); 228 | return methods == null ? null : methods.get(methodName); 229 | } 230 | 231 | /*----------- Method Cache support overload ------------*/ 232 | 233 | /** 234 | * 根据服务名使方法缓存失效 235 | * 236 | * @param serviceName 服务名(非接口名) 237 | */ 238 | public static void invalidateMethodSigsCache(String serviceName) { 239 | NOT_OVERLOAD_METHOD_SIGS_CACHE.remove(serviceName); 240 | } 241 | 242 | /** 243 | * 往缓存里放入方法 244 | * 245 | * @param serviceName 服务名(非接口名) 246 | * @param method 方法 247 | */ 248 | public static void putOverloadMethodCache(String serviceName, Method method) { 249 | ConcurrentHashMap cache = OVERLOAD_METHOD_CACHE.get(serviceName); 250 | if (cache == null) { 251 | cache = new ConcurrentHashMap(); 252 | ConcurrentHashMap old = OVERLOAD_METHOD_CACHE.putIfAbsent(serviceName, cache); 253 | if (old != null) { 254 | cache = old; 255 | } 256 | } 257 | StringBuilder mSigs = new StringBuilder(128); 258 | mSigs.append(method.getName()); 259 | for (Class paramType : method.getParameterTypes()) { 260 | mSigs.append(paramType.getName()); 261 | } 262 | cache.putIfAbsent(mSigs.toString(), method); 263 | } 264 | 265 | /** 266 | * 从缓存里获取方法 267 | * 268 | * @param serviceName 服务名(非接口名) 269 | * @param methodName 方法名 270 | * @param methodSigs 方法描述 271 | * @return 方法 272 | */ 273 | public static Method getOverloadMethodCache(String serviceName, String methodName, String[] methodSigs) { 274 | ConcurrentHashMap methods = OVERLOAD_METHOD_CACHE.get(serviceName); 275 | if (methods == null) { 276 | return null; 277 | } 278 | StringBuilder mSigs = new StringBuilder(128); 279 | mSigs.append(methodName); 280 | for (String methodSign : methodSigs) { 281 | mSigs.append(methodSign); 282 | } 283 | return methods.get(mSigs.toString()); 284 | } 285 | 286 | /** 287 | * 取消缓存服务的公共方法 288 | * 289 | * @param serviceName 服务名(非接口名) 290 | */ 291 | public static void invalidateOverloadMethodCache(String serviceName) { 292 | OVERLOAD_METHOD_CACHE.remove(serviceName); 293 | } 294 | 295 | /*----------- Cache Management ------------*/ 296 | 297 | /** 298 | * 清理方法 299 | */ 300 | static void clearAll() { 301 | CLASS_CACHE.clear(); 302 | TYPE_STR_CACHE.clear(); 303 | APPNAME_CLASSLOADER_MAP.clear(); 304 | SERVICE_CLASSLOADER_MAP.clear(); 305 | NOT_OVERLOAD_METHOD_CACHE.clear(); 306 | NOT_OVERLOAD_METHOD_SIGS_CACHE.clear(); 307 | OVERLOAD_METHOD_CACHE.clear(); 308 | } 309 | 310 | } 311 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/cache/RpcCacheManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.cache; 18 | 19 | /** 20 | * @author GengZhang 21 | */ 22 | public class RpcCacheManager { 23 | 24 | /** 25 | * 清理缓存 26 | */ 27 | public static void clearAll() { 28 | ReflectCache.clearAll(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/coedc/netty/NettyDecoder.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.coedc.netty; 2 | 3 | import cn.whforever.core.serialize.Serializer; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.ByteToMessageDecoder; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * netty的解码器. 12 | * 13 | * @author wuhaifei 14 | */ 15 | public class NettyDecoder extends ByteToMessageDecoder { 16 | 17 | private Class genericClass; 18 | private Serializer serializer; 19 | 20 | public NettyDecoder(Class genericClass, Serializer serializer) { 21 | this.genericClass = genericClass; 22 | this.serializer = serializer; 23 | } 24 | 25 | @Override 26 | protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception { 27 | if (byteBuf.readableBytes() < 4) { 28 | return; 29 | } 30 | 31 | byteBuf.markReaderIndex(); 32 | int dataLength = byteBuf.readInt(); 33 | if (dataLength < 0) { 34 | channelHandlerContext.close(); 35 | } 36 | 37 | if (byteBuf.readableBytes() < dataLength) { 38 | byteBuf.resetReaderIndex(); 39 | return; 40 | } 41 | 42 | try { 43 | byte[] data = new byte[dataLength]; 44 | byteBuf.readBytes(data); 45 | Object object = serializer.deserialize(data, genericClass); 46 | list.add(object); 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/coedc/netty/NettyEncoder.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.coedc.netty; 2 | 3 | import cn.whforever.core.serialize.Serializer; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.MessageToByteEncoder; 7 | 8 | /** 9 | * netty的编码器. 10 | * 11 | * @author wuhf 12 | * @Date 2018/8/31 19:18 13 | **/ 14 | public class NettyEncoder extends MessageToByteEncoder { 15 | 16 | private Class genericClass; 17 | private Serializer serializer; 18 | 19 | public NettyEncoder(Class genericClass, Serializer serializer) { 20 | this.serializer = serializer; 21 | this.genericClass = genericClass; 22 | } 23 | 24 | @Override 25 | protected void encode(ChannelHandlerContext channelHandlerContext, Object object, ByteBuf byteBuf) throws Exception { 26 | if (genericClass.isInstance(object)) { 27 | byte[] data = serializer.serialize(object); 28 | byteBuf.writeInt(data.length); 29 | byteBuf.writeBytes(data); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/common/Destroyable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.common; 18 | 19 | /** 20 | *

可销毁的接口

21 | * 22 | * @author GengZhang 23 | */ 24 | public interface Destroyable { 25 | 26 | /** 27 | * 销毁接口 28 | */ 29 | void destroy(); 30 | 31 | /** 32 | * Do destroy with hook. 33 | * 34 | * @param hook DestroyHook 35 | */ 36 | void destroy(DestroyHook hook); 37 | 38 | /** 39 | * 销毁钩子 40 | */ 41 | interface DestroyHook { 42 | /** 43 | * 销毁前要做的事情 44 | */ 45 | void preDestroy(); 46 | 47 | /** 48 | * 銷毀后要做的事情 49 | */ 50 | void postDestroy(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/common/Initializable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.common; 18 | 19 | /** 20 | * 可初始化的接口 21 | * 22 | * @author GengZhang 23 | */ 24 | public interface Initializable { 25 | 26 | /** 27 | * 初始化 28 | */ 29 | void init(); 30 | } 31 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/config/AbstractIdConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.config; 18 | 19 | import java.io.Serializable; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | /** 23 | * 默认配置带ID 24 | * 25 | * @param the sub class of AbstractIdConfig 26 | * @author GengZhang 27 | */ 28 | public abstract class AbstractIdConfig implements Serializable { 29 | 30 | private static final long serialVersionUID = -1932911135229369183L; 31 | 32 | /** 33 | * Id生成器 34 | */ 35 | private final static AtomicInteger ID_GENERATOR = new AtomicInteger(0); 36 | 37 | // static { 38 | // RpcRuntimeContext.now(); 39 | // } 40 | 41 | /** 42 | * config id 43 | */ 44 | private String id; 45 | 46 | /** 47 | * Gets id. 48 | * 49 | * @return the id 50 | */ 51 | public String getId() { 52 | if (id == null) { 53 | synchronized (this) { 54 | if (id == null) { 55 | id = "rpc-cfg-" + ID_GENERATOR.getAndIncrement(); 56 | } 57 | } 58 | } 59 | return id; 60 | } 61 | 62 | /** 63 | * Sets id. 64 | * 65 | * @param id the id 66 | */ 67 | public S setId(String id) { 68 | this.id = id; 69 | return castThis(); 70 | } 71 | 72 | protected S castThis() { 73 | return (S) this; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/config/ClientConfig.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.config; 2 | 3 | import cn.whforever.core.serialize.Serializer; 4 | 5 | /** 6 | * @author wuhf 7 | * @Date 2018/9/1 15:02 8 | **/ 9 | public class ClientConfig extends Config { 10 | 11 | protected long timeoutMillis; 12 | private String host; 13 | private int port; 14 | private Serializer serializer; 15 | 16 | /** 17 | * 协议 18 | */ 19 | private String protocol; 20 | 21 | public String getHost() { 22 | return host; 23 | } 24 | 25 | public ClientConfig setHost(String host) { 26 | this.host = host; 27 | return this; 28 | } 29 | 30 | public int getPort() { 31 | return port; 32 | } 33 | 34 | public ClientConfig setPort(int port) { 35 | this.port = port; 36 | return this; 37 | } 38 | 39 | public Serializer getSerializer() { 40 | return serializer; 41 | } 42 | 43 | public ClientConfig setSerializer(Serializer serializer) { 44 | this.serializer = serializer; 45 | return this; 46 | } 47 | 48 | public long getTimeoutMillis() { 49 | return timeoutMillis; 50 | } 51 | 52 | public ClientConfig setTimeoutMillis(long timeoutMillis) { 53 | this.timeoutMillis = timeoutMillis; 54 | return this; 55 | } 56 | 57 | public String getProtocol() { 58 | return protocol; 59 | } 60 | 61 | public ClientConfig setProtocol(String protocol) { 62 | this.protocol = protocol; 63 | return this; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/config/Config.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.config; 2 | 3 | /** 4 | * @author wuhaifei 5 | */ 6 | public abstract class Config { 7 | /** 8 | * 是否注册,如果是false只订阅不注册 9 | */ 10 | protected boolean register = true; 11 | 12 | /** 13 | * 是否订阅服务 14 | */ 15 | protected boolean subscribe = true; 16 | 17 | /** 18 | * 服务接口:做为服务唯一标识的组成部分
19 | */ 20 | protected String interfaceId; 21 | 22 | public String getInterfaceId() { 23 | return interfaceId; 24 | } 25 | 26 | public Config setInterfaceId(String interfaceId) { 27 | this.interfaceId = interfaceId; 28 | return this; 29 | } 30 | 31 | public boolean isRegister() { 32 | return register; 33 | } 34 | 35 | public Config setRegister(boolean register) { 36 | this.register = register; 37 | return this; 38 | } 39 | 40 | public boolean isSubscribe() { 41 | return subscribe; 42 | } 43 | 44 | public Config setSubscribe(boolean subscribe) { 45 | this.subscribe = subscribe; 46 | return this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/config/RegistryConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.config; 18 | 19 | import java.io.Serializable; 20 | import java.util.Map; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | 23 | /** 24 | * 注册中心配置 25 | * 26 | * @author wuhaifei 27 | */ 28 | public class RegistryConfig extends AbstractIdConfig implements Serializable { 29 | /** 30 | * The constant serialVersionUID. 31 | */ 32 | private static final long serialVersionUID = -2921019924557602234L; 33 | /** 34 | * Consumer给Provider发心跳的间隔 35 | */ 36 | protected int heartbeatPeriod = 5; 37 | /** 38 | * Consumer给Provider重连的间隔 39 | */ 40 | protected int reconnectPeriod = 5; 41 | /** 42 | * The Parameters. 自定义参数 43 | */ 44 | protected Map parameters; 45 | /** 46 | * 协议 47 | */ 48 | private String protocol = ""; 49 | /** 50 | * 指定注册中心的地址, 和index必须填一个,address优先 51 | */ 52 | private String address; 53 | /** 54 | * 指定注册中心寻址服务的地址, 和address必须填一个 55 | */ 56 | private String index = ""; 57 | /** 58 | * 是否注册,如果是false只订阅不注册 59 | */ 60 | private boolean register = false; 61 | /** 62 | * 是否订阅服务 63 | */ 64 | private boolean subscribe = false; 65 | /** 66 | * 调用注册中心超时时间 67 | */ 68 | private int timeout = 2000; 69 | /** 70 | * 连接注册中心超时时间 71 | */ 72 | private int connectTimeout = 2000; 73 | /** 74 | * 保存到本地文件的位置,默认$HOME下 75 | */ 76 | private String file; 77 | /** 78 | * 是否批量操作 79 | */ 80 | private boolean batch = false; 81 | /** 82 | * 定时批量检查时的条目数 83 | */ 84 | private int batchSize = 10; 85 | 86 | /** 87 | * Gets protocol. 88 | * 89 | * @return the protocol 90 | */ 91 | public String getProtocol() { 92 | return protocol; 93 | } 94 | 95 | /** 96 | * Sets protocol. 97 | * 98 | * @param protocol the protocol 99 | * @return the protocol 100 | */ 101 | public RegistryConfig setProtocol(String protocol) { 102 | this.protocol = protocol; 103 | return this; 104 | } 105 | 106 | /** 107 | * Gets address. 108 | * 109 | * @return the address 110 | */ 111 | public String getAddress() { 112 | return address; 113 | } 114 | 115 | /** 116 | * Sets address. 117 | * 118 | * @param address the address 119 | * @return the address 120 | */ 121 | public RegistryConfig setAddress(String address) { 122 | this.address = address; 123 | return this; 124 | } 125 | 126 | /** 127 | * Gets index. 128 | * 129 | * @return the index 130 | */ 131 | public String getIndex() { 132 | return index; 133 | } 134 | 135 | /** 136 | * Sets index. 137 | * 138 | * @param index the index 139 | * @return the index 140 | */ 141 | public RegistryConfig setIndex(String index) { 142 | this.index = index; 143 | return this; 144 | } 145 | 146 | /** 147 | * Is register boolean. 148 | * 149 | * @return the boolean 150 | */ 151 | public boolean isRegister() { 152 | return register; 153 | } 154 | 155 | /** 156 | * Sets register. 157 | * 158 | * @param register the register 159 | * @return the register 160 | */ 161 | public RegistryConfig setRegister(boolean register) { 162 | this.register = register; 163 | return this; 164 | } 165 | 166 | /** 167 | * Is subscribe boolean. 168 | * 169 | * @return the boolean 170 | */ 171 | public boolean isSubscribe() { 172 | return subscribe; 173 | } 174 | 175 | /** 176 | * Sets subscribe. 177 | * 178 | * @param subscribe the subscribe 179 | * @return the subscribe 180 | */ 181 | public RegistryConfig setSubscribe(boolean subscribe) { 182 | this.subscribe = subscribe; 183 | return this; 184 | } 185 | 186 | /** 187 | * Gets timeout. 188 | * 189 | * @return the timeout 190 | */ 191 | public int getTimeout() { 192 | return timeout; 193 | } 194 | 195 | /** 196 | * Sets timeout. 197 | * 198 | * @param timeout the timeout 199 | * @return the timeout 200 | */ 201 | public RegistryConfig setTimeout(int timeout) { 202 | this.timeout = timeout; 203 | return this; 204 | } 205 | 206 | /** 207 | * Gets connect timeout. 208 | * 209 | * @return the connect timeout 210 | */ 211 | public int getConnectTimeout() { 212 | return connectTimeout; 213 | } 214 | 215 | /** 216 | * Sets connect timeout. 217 | * 218 | * @param connectTimeout the connect timeout 219 | * @return the connect timeout 220 | */ 221 | public RegistryConfig setConnectTimeout(int connectTimeout) { 222 | this.connectTimeout = connectTimeout; 223 | return this; 224 | } 225 | 226 | /** 227 | * Gets file. 228 | * 229 | * @return the file 230 | */ 231 | public String getFile() { 232 | return file; 233 | } 234 | 235 | /** 236 | * Sets file. 237 | * 238 | * @param file the file 239 | * @return the file 240 | */ 241 | public RegistryConfig setFile(String file) { 242 | this.file = file; 243 | return this; 244 | } 245 | 246 | /** 247 | * Is batch boolean. 248 | * 249 | * @return the boolean 250 | */ 251 | public boolean isBatch() { 252 | return batch; 253 | } 254 | 255 | /** 256 | * Sets batch. 257 | * 258 | * @param batch the batch 259 | * @return the batch 260 | */ 261 | public RegistryConfig setBatch(boolean batch) { 262 | this.batch = batch; 263 | return this; 264 | } 265 | 266 | /** 267 | * Gets batch check. 268 | * 269 | * @return the batch check 270 | */ 271 | public int getBatchSize() { 272 | return batchSize; 273 | } 274 | 275 | /** 276 | * Sets batch check. 277 | * 278 | * @param batchSize the batch check 279 | * @return the batch check 280 | */ 281 | public RegistryConfig setBatchSize(int batchSize) { 282 | this.batchSize = batchSize; 283 | return this; 284 | } 285 | 286 | /** 287 | * Gets heartbeatPeriod. 288 | * 289 | * @return the heartbeatPeriod 290 | */ 291 | public int getHeartbeatPeriod() { 292 | return heartbeatPeriod; 293 | } 294 | 295 | /** 296 | * Sets heartbeatPeriod. 297 | * 298 | * @param heartbeatPeriod the heartbeatPeriod 299 | * @return the heartbeatPeriod 300 | */ 301 | public RegistryConfig setHeartbeatPeriod(int heartbeatPeriod) { 302 | this.heartbeatPeriod = heartbeatPeriod; 303 | return this; 304 | } 305 | 306 | /** 307 | * Gets reconnectPeriod. 308 | * 309 | * @return the reconnectPeriod 310 | */ 311 | public int getReconnectPeriod() { 312 | return reconnectPeriod; 313 | } 314 | 315 | /** 316 | * Sets reconnectPeriod. 317 | * 318 | * @param reconnectPeriod the reconnectPeriod 319 | * @return the reconnectPeriod 320 | */ 321 | public RegistryConfig setReconnectPeriod(int reconnectPeriod) { 322 | this.reconnectPeriod = reconnectPeriod; 323 | return this; 324 | } 325 | 326 | /** 327 | * Gets parameters. 328 | * 329 | * @return the parameters 330 | */ 331 | public Map getParameters() { 332 | return parameters; 333 | } 334 | 335 | /** 336 | * Sets parameters. 337 | * 338 | * @param parameters the parameters 339 | * @return the RegistryConfig 340 | */ 341 | public RegistryConfig setParameters(Map parameters) { 342 | if (this.parameters == null) { 343 | this.parameters = new ConcurrentHashMap(); 344 | this.parameters.putAll(parameters); 345 | } 346 | return this; 347 | } 348 | 349 | /** 350 | * Sets parameter. 351 | * 352 | * @param key the key 353 | * @param value the value 354 | * @return the RegistryConfig 355 | */ 356 | public RegistryConfig setParameter(String key, String value) { 357 | if (parameters == null) { 358 | parameters = new ConcurrentHashMap(); 359 | } 360 | parameters.put(key, value); 361 | return this; 362 | } 363 | 364 | /** 365 | * Gets parameter. 366 | * 367 | * @param key the key 368 | * @return the value 369 | */ 370 | public String getParameter(String key) { 371 | return parameters == null ? null : parameters.get(key); 372 | } 373 | 374 | @Override 375 | public String toString() { 376 | return "RegistryConfig{" + 377 | "protocol='" + protocol + '\'' + 378 | ", address='" + address + '\'' + 379 | ", index='" + index + '\'' + 380 | ", register=" + register + 381 | ", subscribe=" + subscribe + 382 | ", timeout=" + timeout + 383 | ", connectTimeout=" + connectTimeout + 384 | ", file='" + file + '\'' + 385 | ", batch=" + batch + 386 | ", batchSize=" + batchSize + 387 | ", heartbeatPeriod=" + heartbeatPeriod + 388 | ", reconnectPeriod=" + reconnectPeriod + 389 | ", parameters=" + parameters + 390 | '}'; 391 | } 392 | 393 | @Override 394 | public boolean equals(Object o) { 395 | if (this == o) { 396 | return true; 397 | } 398 | if (!(o instanceof RegistryConfig)) { 399 | return false; 400 | } 401 | 402 | RegistryConfig that = (RegistryConfig) o; 403 | 404 | if (register != that.register) { 405 | return false; 406 | } 407 | if (subscribe != that.subscribe) { 408 | return false; 409 | } 410 | if (timeout != that.timeout) { 411 | return false; 412 | } 413 | if (connectTimeout != that.connectTimeout) { 414 | return false; 415 | } 416 | if (batch != that.batch) { 417 | return false; 418 | } 419 | if (batchSize != that.batchSize) { 420 | return false; 421 | } 422 | if (heartbeatPeriod != that.heartbeatPeriod) { 423 | return false; 424 | } 425 | if (reconnectPeriod != that.reconnectPeriod) { 426 | return false; 427 | } 428 | if (!protocol.equals(that.protocol)) { 429 | return false; 430 | } 431 | if (address != null ? !address.equals(that.address) : that.address != null) { 432 | return false; 433 | } 434 | if (index != null ? !index.equals(that.index) : that.index != null) { 435 | return false; 436 | } 437 | if (file != null ? !file.equals(that.file) : that.file != null) { 438 | return false; 439 | } 440 | return parameters != null ? parameters.equals(that.parameters) : that.parameters == null; 441 | 442 | } 443 | 444 | @Override 445 | public int hashCode() { 446 | int result = protocol.hashCode(); 447 | result = 31 * result + (address != null ? address.hashCode() : 0); 448 | result = 31 * result + (index != null ? index.hashCode() : 0); 449 | result = 31 * result + (register ? 1 : 0); 450 | result = 31 * result + (subscribe ? 1 : 0); 451 | result = 31 * result + timeout; 452 | result = 31 * result + connectTimeout; 453 | result = 31 * result + (file != null ? file.hashCode() : 0); 454 | result = 31 * result + (batch ? 1 : 0); 455 | result = 31 * result + batchSize; 456 | result = 31 * result + heartbeatPeriod; 457 | result = 31 * result + reconnectPeriod; 458 | result = 31 * result + (parameters != null ? parameters.hashCode() : 0); 459 | return result; 460 | } 461 | } -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/config/ServerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.config; 2 | 3 | import cn.whforever.core.serialize.Serializer; 4 | 5 | /** 6 | * @author wuhf 7 | * @Date 2018/9/1 15:05 8 | **/ 9 | public class ServerConfig extends Config { 10 | 11 | /** 12 | * 接口实现类引用 13 | */ 14 | protected Object ref; 15 | private int port; 16 | private String host; 17 | private Serializer serializer; 18 | /** 19 | * 协议 20 | */ 21 | private String protocol = ""; 22 | 23 | private boolean register = true; 24 | 25 | public int getPort() { 26 | return port; 27 | } 28 | 29 | public ServerConfig setPort(int port) { 30 | this.port = port; 31 | return this; 32 | } 33 | 34 | public Serializer getSerializer() { 35 | return serializer; 36 | } 37 | 38 | public ServerConfig setSerializer(Serializer serializer) { 39 | this.serializer = serializer; 40 | return this; 41 | } 42 | 43 | /** 44 | * Gets ref. 45 | * 46 | * @return the ref 47 | */ 48 | public Object getRef() { 49 | return ref; 50 | } 51 | 52 | /** 53 | * Sets ref. 54 | * 55 | * @param ref the ref 56 | * @return the ref 57 | */ 58 | public ServerConfig setRef(Object ref) { 59 | this.ref = ref; 60 | return this; 61 | } 62 | 63 | public String getProtocol() { 64 | return protocol; 65 | } 66 | 67 | public ServerConfig setProtocol(String protocol) { 68 | this.protocol = protocol; 69 | return this; 70 | } 71 | 72 | public String getHost() { 73 | return host; 74 | } 75 | 76 | public ServerConfig setHost(String host) { 77 | this.host = host; 78 | return this; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/exception/ChildRpcRuntimeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.exception; 18 | 19 | /** 20 | * RPC的自定义异常. 21 | * 22 | * @author wuhaifei 23 | */ 24 | public class ChildRpcRuntimeException extends RuntimeException { 25 | 26 | protected ChildRpcRuntimeException() { 27 | 28 | } 29 | 30 | public ChildRpcRuntimeException(String message) { 31 | super(message); 32 | } 33 | 34 | public ChildRpcRuntimeException(String message, Throwable cause) { 35 | super(message, cause); 36 | } 37 | 38 | public ChildRpcRuntimeException(Throwable cause) { 39 | super(cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/log/Logger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.log; 18 | 19 | /** 20 | * Just Logger. 21 | * 22 | * @author wuhf 23 | */ 24 | public interface Logger { 25 | 26 | /** 27 | * Gets name. 28 | * 29 | * @return the name 30 | */ 31 | String getName(); 32 | 33 | /** 34 | * Is debug enabled boolean. 35 | * 36 | * @return the boolean 37 | */ 38 | boolean isDebugEnabled(); 39 | 40 | /** 41 | * Debug. 42 | * 43 | * @param message the message 44 | */ 45 | void debug(String message); 46 | 47 | /** 48 | * Debug. 49 | * 50 | * @param format the format 51 | * @param args the args 52 | */ 53 | void debug(String format, Object... args); 54 | 55 | /** 56 | * Debug. 57 | * 58 | * @param message the message 59 | * @param t the t 60 | */ 61 | void debug(String message, Throwable t); 62 | 63 | /** 64 | * Is debug enabled boolean. 65 | * 66 | * @param appName the app name 67 | * @return the boolean 68 | */ 69 | boolean isDebugEnabled(String appName); 70 | 71 | /** 72 | * Debug with app. 73 | * 74 | * @param appName the app name 75 | * @param message the message 76 | */ 77 | void debugWithApp(String appName, String message); 78 | 79 | /** 80 | * Debug with app. 81 | * 82 | * @param appName the app name 83 | * @param format the format 84 | * @param args the args 85 | */ 86 | void debugWithApp(String appName, String format, Object... args); 87 | 88 | /** 89 | * Debug with app. 90 | * 91 | * @param appName the app name 92 | * @param message the message 93 | * @param t the t 94 | */ 95 | void debugWithApp(String appName, String message, Throwable t); 96 | 97 | /** 98 | * Is info enabled boolean. 99 | * 100 | * @return the boolean 101 | */ 102 | boolean isInfoEnabled(); 103 | 104 | /** 105 | * Info. 106 | * 107 | * @param message the message 108 | */ 109 | void info(String message); 110 | 111 | /** 112 | * Info. 113 | * 114 | * @param format the format 115 | * @param args the args 116 | */ 117 | void info(String format, Object... args); 118 | 119 | /** 120 | * Info. 121 | * 122 | * @param message the message 123 | * @param t the t 124 | */ 125 | void info(String message, Throwable t); 126 | 127 | /** 128 | * Is info enabled boolean. 129 | * 130 | * @param appName the app name 131 | * @return the boolean 132 | */ 133 | boolean isInfoEnabled(String appName); 134 | 135 | /** 136 | * Info with app. 137 | * 138 | * @param appName the app name 139 | * @param message the message 140 | */ 141 | void infoWithApp(String appName, String message); 142 | 143 | /** 144 | * Info with app. 145 | * 146 | * @param appName the app name 147 | * @param format the format 148 | * @param args the args 149 | */ 150 | void infoWithApp(String appName, String format, Object... args); 151 | 152 | /** 153 | * Info with app. 154 | * 155 | * @param appName the app name 156 | * @param message the message 157 | * @param t the t 158 | */ 159 | void infoWithApp(String appName, String message, Throwable t); 160 | 161 | /** 162 | * Is warn enabled boolean. 163 | * 164 | * @return the boolean 165 | */ 166 | boolean isWarnEnabled(); 167 | 168 | /** 169 | * Warn. 170 | * 171 | * @param message the message 172 | */ 173 | void warn(String message); 174 | 175 | /** 176 | * Warn. 177 | * 178 | * @param format the format 179 | * @param args the args 180 | */ 181 | void warn(String format, Object... args); 182 | 183 | /** 184 | * Warn. 185 | * 186 | * @param message the message 187 | * @param t the t 188 | */ 189 | void warn(String message, Throwable t); 190 | 191 | /** 192 | * Is warn enabled boolean. 193 | * 194 | * @param appName the app name 195 | * @return the boolean 196 | */ 197 | boolean isWarnEnabled(String appName); 198 | 199 | /** 200 | * Warn with app. 201 | * 202 | * @param appName the app name 203 | * @param message the message 204 | */ 205 | void warnWithApp(String appName, String message); 206 | 207 | /** 208 | * Warn with app. 209 | * 210 | * @param appName the app name 211 | * @param format the format 212 | * @param args the args 213 | */ 214 | void warnWithApp(String appName, String format, Object... args); 215 | 216 | /** 217 | * Warn with app. 218 | * 219 | * @param appName the app name 220 | * @param message the message 221 | * @param t the t 222 | */ 223 | void warnWithApp(String appName, String message, Throwable t); 224 | 225 | /** 226 | * Is error enabled boolean. 227 | * 228 | * @return the boolean 229 | */ 230 | boolean isErrorEnabled(); 231 | 232 | /** 233 | * Error. 234 | * 235 | * @param message the message 236 | */ 237 | void error(String message); 238 | 239 | /** 240 | * Error. 241 | * 242 | * @param format the format 243 | * @param args the args 244 | */ 245 | void error(String format, Object... args); 246 | 247 | /** 248 | * Error. 249 | * 250 | * @param message the message 251 | * @param t the t 252 | */ 253 | void error(String message, Throwable t); 254 | 255 | /** 256 | * Is error enabled boolean. 257 | * 258 | * @param appName the app name 259 | * @return the boolean 260 | */ 261 | boolean isErrorEnabled(String appName); 262 | 263 | /** 264 | * Error with app. 265 | * 266 | * @param appName the app name 267 | * @param message the message 268 | */ 269 | void errorWithApp(String appName, String message); 270 | 271 | /** 272 | * Error with app. 273 | * 274 | * @param appName the app name 275 | * @param format the format 276 | * @param args the args 277 | */ 278 | void errorWithApp(String appName, String format, Object... args); 279 | 280 | /** 281 | * Error with app. 282 | * 283 | * @param appName the app name 284 | * @param message the message 285 | * @param t the t 286 | */ 287 | void errorWithApp(String appName, String message, Throwable t); 288 | 289 | } 290 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/log/LoggerFactory.java: -------------------------------------------------------------------------------- 1 | ///* 2 | // * Licensed to the Apache Software Foundation (ASF) under one or more 3 | // * contributor license agreements. See the NOTICE file distributed with 4 | // * this work for additional information regarding copyright ownership. 5 | // * The ASF licenses this file to You under the Apache License, Version 2.0 6 | // * (the "License"); you may not use this file except in compliance with 7 | // * the License. You may obtain a copy of the License at 8 | // * 9 | // * http://www.apache.org/licenses/LICENSE-2.0 10 | // * 11 | // * Unless required by applicable law or agreed to in writing, software 12 | // * distributed under the License is distributed on an "AS IS" BASIS, 13 | // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // * See the License for the specific language governing permissions and 15 | // * limitations under the License. 16 | // */ 17 | //package cn.whforever.core.log; 18 | // 19 | //import cn.whforever.core.exception.ChildRpcRuntimeException; 20 | //import cn.whforever.core.util.ClassUtils; 21 | // 22 | ///** 23 | // * Factory of logger. 24 | // * 25 | // * @author GengZhang 26 | // */ 27 | //public class LoggerFactory { 28 | // 29 | // /** 30 | // * 配置的实现类 31 | // */ 32 | // private static String implClass = "logger.impl"; 33 | // 34 | // public static Logger getLogger(String name) { 35 | // try { 36 | // return (Logger) ClassUtils.forName(implClass).getConstructor(String.class).newInstance(name); 37 | // } catch (Exception e) { 38 | // throw new ChildRpcRuntimeException("Error when getLogger of " + name 39 | // + ", implement is " + implClass + "", e); 40 | // } 41 | // } 42 | // 43 | // public static Logger getLogger(Class clazz) { 44 | // try { 45 | // return (Logger) ClassUtils.forName(implClass).getConstructor(Class.class).newInstance(clazz); 46 | // } catch (Exception e) { 47 | // throw new ChildRpcRuntimeException("Error when getLogger of " + clazz.getName() 48 | // + ", implement is " + implClass + "", e); 49 | // } 50 | // } 51 | //} 52 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/log/SLF4JLoggerImpl.java: -------------------------------------------------------------------------------- 1 | ///* 2 | // * Licensed to the Apache Software Foundation (ASF) under one or more 3 | // * contributor license agreements. See the NOTICE file distributed with 4 | // * this work for additional information regarding copyright ownership. 5 | // * The ASF licenses this file to You under the Apache License, Version 2.0 6 | // * (the "License"); you may not use this file except in compliance with 7 | // * the License. You may obtain a copy of the License at 8 | // * 9 | // * http://www.apache.org/licenses/LICENSE-2.0 10 | // * 11 | // * Unless required by applicable law or agreed to in writing, software 12 | // * distributed under the License is distributed on an "AS IS" BASIS, 13 | // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // * See the License for the specific language governing permissions and 15 | // * limitations under the License. 16 | // */ 17 | //package cn.whforever.core.log; 18 | // 19 | // 20 | //import cn.whforever.core.util.StringUtils; 21 | // 22 | ///** 23 | // * Logger base on native slf4j 24 | // * 25 | // * @author GengZhang 26 | // */ 27 | //public class SLF4JLoggerImpl implements Logger { 28 | // private final org.slf4j.Logger logger; 29 | // 30 | // public SLF4JLoggerImp/**/l(String name) { 31 | // logger = org.slf4j.LoggerFactory.getLogger(name); 32 | // } 33 | // 34 | // public SLF4JLoggerImpl(Class clazz) { 35 | // logger = org.slf4j.LoggerFactory.getLogger(clazz); 36 | // } 37 | // 38 | // protected org.slf4j.Logger getLogger() { 39 | // return logger; 40 | // } 41 | // 42 | // protected org.slf4j.Logger getLogger(String appName) { 43 | // return logger; 44 | // } 45 | // 46 | // @Override 47 | // public String getName() { 48 | // return getLogger().getName(); 49 | // } 50 | // 51 | // @Override 52 | // public boolean isDebugEnabled() { 53 | // return getLogger().isDebugEnabled(); 54 | // } 55 | // 56 | // @Override 57 | // public void debug(String message) { 58 | // getLogger().debug(message); 59 | // } 60 | // 61 | // @Override 62 | // public void debug(String format, Object... args) { 63 | // getLogger().debug(format, args); 64 | // } 65 | // 66 | // @Override 67 | // public void debug(String message, Throwable t) { 68 | // getLogger().debug(message, t); 69 | // } 70 | // 71 | // @Override 72 | // public boolean isDebugEnabled(String appName) { 73 | // return getLogger(appName).isDebugEnabled(); 74 | // } 75 | // 76 | // @Override 77 | // public void debugWithApp(String appName, String message) { 78 | // getLogger(appName).debug(prefix(appName) + message); 79 | // } 80 | // 81 | // @Override 82 | // public void debugWithApp(String appName, String format, Object... args) { 83 | // getLogger(appName).debug(prefix(appName) + format, args); 84 | // } 85 | // 86 | // @Override 87 | // public void debugWithApp(String appName, String message, Throwable t) { 88 | // getLogger(appName).debug(prefix(appName) + message, t); 89 | // } 90 | // 91 | // @Override 92 | // public boolean isInfoEnabled() { 93 | // return getLogger().isInfoEnabled(); 94 | // } 95 | // 96 | // @Override 97 | // public void info(String message) { 98 | // getLogger().info(message); 99 | // } 100 | // 101 | // @Override 102 | // public void info(String format, Object... args) { 103 | // getLogger().info(format, args); 104 | // } 105 | // 106 | // @Override 107 | // public void info(String message, Throwable t) { 108 | // getLogger().info(message, t); 109 | // } 110 | // 111 | // @Override 112 | // public boolean isInfoEnabled(String appName) { 113 | // return getLogger(appName).isInfoEnabled(); 114 | // } 115 | // 116 | // @Override 117 | // public void infoWithApp(String appName, String message) { 118 | // getLogger(appName).info(prefix(appName) + message); 119 | // } 120 | // 121 | // @Override 122 | // public void infoWithApp(String appName, String format, Object... args) { 123 | // getLogger(appName).info(prefix(appName) + format, args); 124 | // } 125 | // 126 | // @Override 127 | // public void infoWithApp(String appName, String message, Throwable t) { 128 | // getLogger(appName).info(prefix(appName) + message, t); 129 | // } 130 | // 131 | // @Override 132 | // public boolean isWarnEnabled() { 133 | // return getLogger().isWarnEnabled(); 134 | // } 135 | // 136 | // @Override 137 | // public void warn(String message) { 138 | // getLogger().warn(message); 139 | // } 140 | // 141 | // @Override 142 | // public void warn(String format, Object... args) { 143 | // getLogger().warn(format, args); 144 | // } 145 | // 146 | // @Override 147 | // public void warn(String message, Throwable t) { 148 | // getLogger().warn(message, t); 149 | // } 150 | // 151 | // @Override 152 | // public boolean isWarnEnabled(String appName) { 153 | // return getLogger(appName).isWarnEnabled(); 154 | // } 155 | // 156 | // @Override 157 | // public void warnWithApp(String appName, String message) { 158 | // getLogger(appName).warn((prefix(appName)) + message); 159 | // } 160 | // 161 | // @Override 162 | // public void warnWithApp(String appName, String format, Object... args) { 163 | // getLogger(appName).warn(prefix(appName) + format, args); 164 | // } 165 | // 166 | // @Override 167 | // public void warnWithApp(String appName, String message, Throwable t) { 168 | // getLogger(appName).warn(prefix(appName) + message, t); 169 | // } 170 | // 171 | // @Override 172 | // public boolean isErrorEnabled() { 173 | // return getLogger().isErrorEnabled(); 174 | // } 175 | // 176 | // @Override 177 | // public void error(String message) { 178 | // getLogger().error(message); 179 | // } 180 | // 181 | // @Override 182 | // public void error(String format, Object... args) { 183 | // getLogger().error(format, args); 184 | // } 185 | // 186 | // @Override 187 | // public void error(String message, Throwable t) { 188 | // getLogger().error(message, t); 189 | // } 190 | // 191 | // @Override 192 | // public boolean isErrorEnabled(String appName) { 193 | // return getLogger(appName).isErrorEnabled(); 194 | // } 195 | // 196 | // @Override 197 | // public void errorWithApp(String appName, String message) { 198 | // getLogger(appName).error(prefix(appName) + message); 199 | // } 200 | // 201 | // @Override 202 | // public void errorWithApp(String appName, String format, Object... args) { 203 | // getLogger(appName).error(prefix(appName) + format, args); 204 | // } 205 | // 206 | // @Override 207 | // public void errorWithApp(String appName, String message, Throwable t) { 208 | // getLogger(appName).error(prefix(appName) + message, t); 209 | // } 210 | // 211 | // private String prefix(String appName) { 212 | // return appName == null ? StringUtils.EMPTY : "[" + appName + "]"; 213 | // } 214 | //} -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/protocol/bolt/server/BoltServer.java: -------------------------------------------------------------------------------- 1 | //package cn.whforever.core.protocol.bolt.server; 2 | // 3 | //import cn.whforever.core.config.Config; 4 | // 5 | //public class BoltServer extends ChildServer { 6 | // @Override 7 | // public void start(Config config) throws Exception { 8 | // 9 | // } 10 | // 11 | // @Override 12 | // public void destroy() throws Exception { 13 | // 14 | // } 15 | //} 16 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/protocol/netty/client/NettyClient.java: -------------------------------------------------------------------------------- 1 | //package cn.whforever.core.protocol.netty.client; 2 | // 3 | //import cn.whforever.core.coedc.netty.NettyDecoder; 4 | //import cn.whforever.core.coedc.netty.NettyEncoder; 5 | //import cn.whforever.core.config.ClientConfig; 6 | //import cn.whforever.core.rpc.RpcCallbackFuture; 7 | //import cn.whforever.core.rpc.RpcRequest; 8 | //import cn.whforever.core.rpc.RpcResponse; 9 | //import io.netty.bootstrap.Bootstrap; 10 | //import io.netty.channel.Channel; 11 | //import io.netty.channel.ChannelInitializer; 12 | //import io.netty.channel.ChannelOption; 13 | //import io.netty.channel.EventLoopGroup; 14 | //import io.netty.channel.nio.NioEventLoopGroup; 15 | //import io.netty.channel.socket.SocketChannel; 16 | //import io.netty.channel.socket.nio.NioSocketChannel; 17 | // 18 | //public class NettyClient extends ChildClient { 19 | // 20 | // private Channel channel; 21 | // 22 | // @Override 23 | // public void init(ClientConfigConfig config) { 24 | // super.init(config); 25 | // try { 26 | // initNettyClient(); 27 | // } catch (Exception e) { 28 | // e.printStackTrace(); 29 | // } 30 | // } 31 | // 32 | // @Override 33 | // public RpcResponse send(RpcRequest request) throws Exception { 34 | // ClientConfig clientConfig = (ClientConfig) this.config; 35 | // RpcCallbackFuture future = new RpcCallbackFuture(request); 36 | // RpcCallbackFuture.futurePool.put(request.getRequestId(), future); 37 | // // rpc invoke 38 | // this.writeMsg(request); 39 | // // future get 40 | // return future.get(clientConfig.getTimeoutMillis()); 41 | // } 42 | // 43 | // private void initNettyClient() throws Exception { 44 | // ClientConfig clientConfig = (ClientConfig) this.config; 45 | // EventLoopGroup group = new NioEventLoopGroup(); 46 | // Bootstrap bootstrap = new Bootstrap(); 47 | // bootstrap.group(group).channel(NioSocketChannel.class) 48 | // .handler(new ChannelInitializer() { 49 | // @Override 50 | // public void initChannel(SocketChannel channel) throws Exception { 51 | // channel.pipeline() 52 | // .addLast(new NettyEncoder(RpcRequest.class, clientConfig.getSerializer())) 53 | // .addLast(new NettyDecoder(RpcResponse.class, clientConfig.getSerializer())) 54 | // .addLast(new NettyClientHandler()); 55 | // } 56 | // }) 57 | // .option(ChannelOption.TCP_NODELAY, true) 58 | // .option(ChannelOption.SO_REUSEADDR, true) 59 | // .option(ChannelOption.SO_KEEPALIVE, true); 60 | // this.channel = bootstrap.connect(clientConfig.getHost(), clientConfig.getPort()).sync().channel(); 61 | // } 62 | // 63 | // private void writeMsg(RpcRequest request) throws Exception { 64 | // this.channel.writeAndFlush(request).sync(); 65 | // } 66 | //} 67 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/protocol/netty/client/NettyClientAbstract.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.protocol.netty.client; 2 | 3 | import cn.whforever.core.coedc.netty.NettyDecoder; 4 | import cn.whforever.core.coedc.netty.NettyEncoder; 5 | import cn.whforever.core.config.ClientConfig; 6 | import cn.whforever.core.config.Config; 7 | import cn.whforever.core.remote.client.AbstractChildClient; 8 | import cn.whforever.core.rpc.RpcCallbackFuture; 9 | import cn.whforever.core.rpc.RpcRequest; 10 | import cn.whforever.core.rpc.RpcResponse; 11 | import io.netty.bootstrap.Bootstrap; 12 | import io.netty.channel.Channel; 13 | import io.netty.channel.ChannelInitializer; 14 | import io.netty.channel.ChannelOption; 15 | import io.netty.channel.EventLoopGroup; 16 | import io.netty.channel.nio.NioEventLoopGroup; 17 | import io.netty.channel.socket.SocketChannel; 18 | import io.netty.channel.socket.nio.NioSocketChannel; 19 | 20 | /** 21 | * @author wuhaifei 22 | */ 23 | public class NettyClientAbstract extends AbstractChildClient { 24 | 25 | private Channel channel; 26 | 27 | @Override 28 | public void init(Config config) { 29 | super.init(config); 30 | try { 31 | initNettyClient(); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | 37 | @Override 38 | public RpcResponse send(RpcRequest request) throws Exception { 39 | ClientConfig clientConfig = (ClientConfig) this.config; 40 | RpcCallbackFuture future = new RpcCallbackFuture(request); 41 | RpcCallbackFuture.futurePool.put(request.getRequestId(), future); 42 | // rpc invoke 43 | this.writeMsg(request); 44 | // future get 45 | return future.get(clientConfig.getTimeoutMillis()); 46 | } 47 | 48 | private void initNettyClient() throws Exception { 49 | final ClientConfig clientConfig = (ClientConfig) this.config; 50 | EventLoopGroup group = new NioEventLoopGroup(); 51 | Bootstrap bootstrap = new Bootstrap(); 52 | bootstrap.group(group).channel(NioSocketChannel.class) 53 | .handler(new ChannelInitializer() { 54 | @Override 55 | public void initChannel(SocketChannel channel) throws Exception { 56 | channel.pipeline() 57 | .addLast(new NettyEncoder(RpcRequest.class, clientConfig.getSerializer())) 58 | .addLast(new NettyDecoder(RpcResponse.class, clientConfig.getSerializer())) 59 | .addLast(new NettyClientHandler()); 60 | } 61 | }) 62 | .option(ChannelOption.TCP_NODELAY, true) 63 | .option(ChannelOption.SO_REUSEADDR, true) 64 | .option(ChannelOption.SO_KEEPALIVE, true); 65 | this.channel = bootstrap.connect(clientConfig.getHost(), clientConfig.getPort()).sync().channel(); 66 | } 67 | 68 | private void writeMsg(RpcRequest request) throws Exception { 69 | this.channel.writeAndFlush(request).sync(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/protocol/netty/client/NettyClientHandler.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.protocol.netty.client; 2 | 3 | import cn.whforever.core.rpc.RpcCallbackFuture; 4 | import cn.whforever.core.rpc.RpcResponse; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.SimpleChannelInboundHandler; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * 客户端业务处理类。 12 | * 13 | * @author wuhf 14 | */ 15 | public class NettyClientHandler extends SimpleChannelInboundHandler { 16 | private static final Logger logger = LoggerFactory.getLogger(NettyClientHandler.class); 17 | 18 | @Override 19 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcResponse rpcResponse) throws Exception { 20 | RpcCallbackFuture future = RpcCallbackFuture.futurePool.get(rpcResponse.getRequestId()); 21 | future.setResponse(rpcResponse); 22 | RpcCallbackFuture.futurePool.put(rpcResponse.getRequestId(), future); 23 | } 24 | 25 | @Override 26 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 27 | logger.error(">>>>>>>>>>> child-rpc netty client caught exception", cause); 28 | ctx.close(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/protocol/netty/server/NettyServerAbstract.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.protocol.netty.server; 2 | 3 | import cn.whforever.core.coedc.netty.NettyDecoder; 4 | import cn.whforever.core.coedc.netty.NettyEncoder; 5 | import cn.whforever.core.config.Config; 6 | import cn.whforever.core.config.ServerConfig; 7 | import cn.whforever.core.remote.server.AbstractChildServer; 8 | import cn.whforever.core.rpc.RpcRequest; 9 | import cn.whforever.core.rpc.RpcResponse; 10 | import io.netty.bootstrap.ServerBootstrap; 11 | import io.netty.channel.*; 12 | import io.netty.channel.nio.NioEventLoopGroup; 13 | import io.netty.channel.socket.SocketChannel; 14 | import io.netty.channel.socket.nio.NioServerSocketChannel; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | public class NettyServerAbstract extends AbstractChildServer { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(NettyServerAbstract.class); 21 | private Thread thread; 22 | 23 | @Override 24 | public void start(Config config) throws Exception { 25 | final ServerConfig serverConfig = (ServerConfig) config; 26 | thread = new Thread(new Runnable() { 27 | @Override 28 | public void run() { 29 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 30 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 31 | try { 32 | ServerBootstrap bootstrap = new ServerBootstrap(); 33 | bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) 34 | .childHandler(new ChannelInitializer() { 35 | @Override 36 | public void initChannel(SocketChannel channel) throws Exception { 37 | channel.pipeline() 38 | .addLast(new NettyDecoder(RpcRequest.class, serverConfig.getSerializer())) 39 | .addLast(new NettyEncoder(RpcResponse.class, serverConfig.getSerializer())) 40 | .addLast(new NettyServerHandler()); 41 | } 42 | }) 43 | .option(ChannelOption.SO_TIMEOUT, 100) 44 | .option(ChannelOption.SO_BACKLOG, 128) 45 | .option(ChannelOption.TCP_NODELAY, true) 46 | .option(ChannelOption.SO_REUSEADDR, true) 47 | .childOption(ChannelOption.SO_KEEPALIVE, true); 48 | ChannelFuture future = bootstrap.bind(serverConfig.getPort()).sync(); 49 | logger.info(">>>>>>>>>>> child-rpc server start success, appName={}, port={}", NettyServerAbstract.class.getName(), serverConfig.getPort()); 50 | Channel serviceChannel = future.channel().closeFuture().sync().channel(); 51 | } catch (InterruptedException e) { 52 | logger.error(">>>>>> ", e); 53 | } finally { 54 | workerGroup.shutdownGracefully(); 55 | bossGroup.shutdownGracefully(); 56 | } 57 | } 58 | }); 59 | thread.setDaemon(true); 60 | thread.start(); 61 | } 62 | 63 | @Override 64 | public void destroy() throws Exception { 65 | thread.interrupt(); 66 | logger.info(">>>>>> netty server interrupt"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/protocol/netty/server/NettyServerHandler.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.protocol.netty.server; 2 | 3 | import cn.whforever.core.rpc.RpcInvokerHandler; 4 | import cn.whforever.core.rpc.RpcRequest; 5 | import cn.whforever.core.rpc.RpcResponse; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.SimpleChannelInboundHandler; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * server端业务逻辑处理. 13 | * 14 | * @author wuhf 15 | * @data 2018/07/09/03 16 | */ 17 | public class NettyServerHandler extends SimpleChannelInboundHandler { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class); 20 | 21 | @Override 22 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcRequest rpcRequest) throws Exception { 23 | // invoke 24 | RpcResponse response = RpcInvokerHandler.invokeService(rpcRequest); 25 | channelHandlerContext.writeAndFlush(response); 26 | } 27 | 28 | @Override 29 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 30 | logger.error(">>>>>>>>>>> child-rpc provider netty server caught exception", cause); 31 | ctx.close(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/proxy/ClientProxy.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.proxy; 2 | 3 | import cn.whforever.core.config.ClientConfig; 4 | import cn.whforever.core.config.Config; 5 | import cn.whforever.core.config.RegistryConfig; 6 | import cn.whforever.core.exception.ChildRpcRuntimeException; 7 | import cn.whforever.core.register.Registry; 8 | import cn.whforever.core.register.RegistryFactory; 9 | import cn.whforever.core.remote.client.AbstractChildClient; 10 | import cn.whforever.core.rpc.RpcRequest; 11 | import cn.whforever.core.rpc.RpcResponse; 12 | 13 | import java.util.List; 14 | import java.util.UUID; 15 | import java.util.concurrent.ThreadLocalRandom; 16 | 17 | /** 18 | * @author wuhf 19 | * @Date 2018/9/1 17:04 20 | **/ 21 | public class ClientProxy implements Proxy { 22 | private AbstractChildClient childClient; 23 | private Config config; 24 | private ClientConfig clientConfig; 25 | private Class iface; 26 | private RegistryConfig registryConfig; 27 | 28 | public ClientProxy(Config config, AbstractChildClient childClient, Class iface) { 29 | this.childClient = childClient; 30 | this.config = config; 31 | this.config.setInterfaceId(iface.getName()); 32 | this.iface = iface; 33 | } 34 | 35 | public T refer() { 36 | try { 37 | this.clientConfig = (ClientConfig) config; 38 | if (clientConfig.isSubscribe()) { 39 | subscribe(); 40 | } 41 | childClient.init(this.clientConfig); 42 | return invoke(); 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | return null; 47 | } 48 | 49 | public void unRef() { 50 | } 51 | 52 | /** 53 | * 订阅zk的服务列表. 54 | */ 55 | private void subscribe() { 56 | Registry registry = RegistryFactory.getRegistry(this.getRegistryConfig()); 57 | registry.init(); 58 | registry.start(); 59 | 60 | List providerList = registry.subscribe(this.clientConfig); 61 | 62 | if (null == providerList) { 63 | throw new ChildRpcRuntimeException("无可用服务供订阅!"); 64 | } 65 | 66 | // 使用随机算法,随机选择一个provider 67 | int index = ThreadLocalRandom.current().nextInt(providerList.size()); 68 | String providerInfo = providerList.get(index); 69 | String[] providerArr = providerInfo.split(":"); 70 | clientConfig = (ClientConfig) this.config; 71 | clientConfig.setHost(providerArr[0]); 72 | clientConfig.setPort(Integer.parseInt(providerArr[1])); 73 | } 74 | 75 | // private void unSubscribe() { 76 | // 77 | // } 78 | 79 | public T invoke() { 80 | return (T) java.lang.reflect.Proxy.newProxyInstance(Thread.currentThread() 81 | .getContextClassLoader(), new Class[]{iface}, 82 | (proxy, method, args) -> { 83 | 84 | // request 85 | RpcRequest request = new RpcRequest(); 86 | request.setRequestId(UUID.randomUUID().toString()); 87 | request.setCreateMillisTime(System.currentTimeMillis()); 88 | request.setClassName(method.getDeclaringClass().getName()); 89 | request.setMethodName(method.getName()); 90 | request.setParameterTypes(method.getParameterTypes()); 91 | request.setParameters(args); 92 | 93 | // send 94 | RpcResponse response = childClient.send(request); 95 | 96 | // valid response 97 | if (response == null) { 98 | throw new ChildRpcRuntimeException(">>>>>>>>>>> child-rpc netty response not found."); 99 | } 100 | 101 | if (null != response.getError()) { 102 | throw new ChildRpcRuntimeException(response.getError()); 103 | } 104 | 105 | return response.getResult(); 106 | }); 107 | } 108 | 109 | public RegistryConfig getRegistryConfig() { 110 | return registryConfig; 111 | } 112 | 113 | public ClientProxy setRegistryConfig(RegistryConfig registryConfig) { 114 | this.registryConfig = registryConfig; 115 | return this; 116 | } 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/proxy/JDKInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.proxy; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * @author wuhf 8 | * @Date 2018/9/1 14:20 9 | **/ 10 | public class JDKInvocationHandler implements InvocationHandler { 11 | @Override 12 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 13 | return null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/proxy/Proxy.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.proxy; 2 | 3 | public interface Proxy { 4 | } 5 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/proxy/ServerProxy.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.proxy; 2 | 3 | import cn.whforever.core.config.Config; 4 | import cn.whforever.core.config.RegistryConfig; 5 | import cn.whforever.core.config.ServerConfig; 6 | import cn.whforever.core.exception.ChildRpcRuntimeException; 7 | import cn.whforever.core.register.Registry; 8 | import cn.whforever.core.register.RegistryFactory; 9 | import cn.whforever.core.remote.server.AbstractChildServer; 10 | import cn.whforever.core.rpc.RpcInvokerHandler; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | * @author wuhf 16 | * @Date 2018/9/1 17:21 17 | **/ 18 | public class ServerProxy { 19 | 20 | private final static Logger LOGGER = LoggerFactory.getLogger(ServerProxy.class); 21 | protected transient volatile boolean exported; 22 | private AbstractChildServer childServer; 23 | private Config config; 24 | private ServerConfig serverConfig; 25 | private RegistryConfig registryConfig; 26 | 27 | 28 | public ServerProxy(AbstractChildServer childServer, Config config) { 29 | this.childServer = childServer; 30 | this.config = config; 31 | serverConfig = (ServerConfig) this.config; 32 | } 33 | 34 | public ServerProxy(AbstractChildServer nettyServerAbstract) { 35 | this.childServer = nettyServerAbstract; 36 | } 37 | 38 | public void export() { 39 | try { 40 | Object serviceBean = Class.forName((String) serverConfig.getRef()).newInstance(); 41 | RpcInvokerHandler.serviceMap.put(serverConfig.getInterfaceId(), serviceBean); 42 | this.childServer.start(this.getServerConfig()); 43 | 44 | if (serverConfig.isRegister()) { 45 | // 将服务注册到zookeeper 46 | register(); 47 | } 48 | } catch (Exception e) { 49 | // 取消服务注册 50 | unregister(); 51 | if (e instanceof ChildRpcRuntimeException) { 52 | throw (ChildRpcRuntimeException) e; 53 | } else { 54 | throw new ChildRpcRuntimeException("Build provider proxy error!", e); 55 | } 56 | } 57 | exported = true; 58 | } 59 | 60 | /** 61 | * 注册服务 62 | */ 63 | protected void register() { 64 | if (serverConfig.isRegister()) { 65 | Registry registry = RegistryFactory.getRegistry(this.getRegistryConfig()); 66 | registry.init(); 67 | registry.start(); 68 | try { 69 | registry.register(this.serverConfig); 70 | } catch (ChildRpcRuntimeException e) { 71 | throw e; 72 | } catch (Throwable e) { 73 | String appName = serverConfig.getInterfaceId(); 74 | LOGGER.info(appName, "Catch exception when register to registry: " 75 | + registryConfig.getId(), e); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * 反注册服务 82 | */ 83 | protected void unregister() { 84 | if (serverConfig.isRegister()) { 85 | Registry registry = RegistryFactory.getRegistry(this.getRegistryConfig()); 86 | try { 87 | registry.unRegister(serverConfig); 88 | } catch (Exception e) { 89 | String appName = serverConfig.getInterfaceId(); 90 | LOGGER.info(appName, "Catch exception when unRegister from registry: " + 91 | registryConfig.getId() 92 | + ", but you can ignore if it's called by JVM shutdown hook", e); 93 | } 94 | } 95 | } 96 | 97 | public RegistryConfig getRegistryConfig() { 98 | return registryConfig; 99 | } 100 | 101 | public ServerProxy setRegistryConfig(RegistryConfig registryConfig) { 102 | this.registryConfig = registryConfig; 103 | return this; 104 | } 105 | 106 | public ServerConfig getServerConfig() { 107 | return serverConfig; 108 | } 109 | 110 | public ServerProxy setServerConfig(ServerConfig serverConfig) { 111 | this.serverConfig = serverConfig; 112 | return this; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/register/Registry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.register; 18 | 19 | import cn.whforever.core.common.Destroyable; 20 | import cn.whforever.core.common.Initializable; 21 | import cn.whforever.core.config.ClientConfig; 22 | import cn.whforever.core.config.RegistryConfig; 23 | import cn.whforever.core.config.ServerConfig; 24 | 25 | import java.util.List; 26 | 27 | /** 28 | * Registry SPI 29 | * 30 | * @author wuhaifei 31 | */ 32 | public abstract class Registry implements Initializable, Destroyable { 33 | 34 | /** 35 | * 注册中心服务配置 36 | */ 37 | protected RegistryConfig registryConfig; 38 | 39 | /** 40 | * 注册中心配置 41 | * 42 | * @param registryConfig 注册中心配置 43 | */ 44 | protected Registry(RegistryConfig registryConfig) { 45 | this.registryConfig = registryConfig; 46 | } 47 | 48 | /** 49 | * 启动 50 | * 51 | * @return is started 52 | */ 53 | public abstract boolean start(); 54 | 55 | /** 56 | * 注册服务提供者 57 | * 58 | * @param config Provider配置 59 | */ 60 | public abstract void register(ServerConfig config); 61 | 62 | /** 63 | * 反注册服务提供者 64 | * 65 | * @param config Provider配置 66 | */ 67 | public abstract void unRegister(ServerConfig config); 68 | 69 | /** 70 | * 反注册服务提供者 71 | * 72 | * @param configs Provider配置 73 | */ 74 | public abstract void batchUnRegister(List configs); 75 | 76 | /** 77 | * 订阅服务列表 78 | * 79 | * @param config Consumer配置 80 | * @return 当前Provider列表,返回null表示未同步获取到地址 81 | */ 82 | public abstract List subscribe(ClientConfig config); 83 | 84 | /** 85 | * 反订阅服务调用者相关配置 86 | * 87 | * @param config Consumer配置 88 | */ 89 | public abstract void unSubscribe(ClientConfig config); 90 | 91 | /** 92 | * 反订阅服务调用者相关配置 93 | * 94 | * @param configs Consumer配置 95 | */ 96 | public abstract void batchUnSubscribe(List configs); 97 | 98 | @Override 99 | public void destroy(DestroyHook hook) { 100 | if (hook != null) { 101 | hook.preDestroy(); 102 | } 103 | destroy(); 104 | if (hook != null) { 105 | hook.postDestroy(); 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/register/RegistryFactory.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.register; 2 | 3 | import cn.whforever.core.config.RegistryConfig; 4 | import cn.whforever.core.exception.ChildRpcRuntimeException; 5 | import cn.whforever.core.registry.consul.ConsulRegistry; 6 | import cn.whforever.core.registry.zk.ZookeeperRegistry; 7 | import cn.whforever.core.rpc.RpcConstants; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | import java.util.concurrent.ConcurrentMap; 16 | 17 | /** 18 | * Factory of Registry. 19 | * 20 | * @author wuhaifei 21 | */ 22 | public class RegistryFactory { 23 | 24 | /** 25 | * 保存全部的配置和注册中心实例 26 | */ 27 | private final static ConcurrentMap ALL_REGISTRIES = new ConcurrentHashMap(); 28 | 29 | /** 30 | * slf4j Logger for this class 31 | */ 32 | private final static Logger LOGGER = LoggerFactory.getLogger(RegistryFactory.class); 33 | 34 | /** 35 | * 得到注册中心对象 36 | * 37 | * @param registryConfig RegistryConfig类 38 | * @return Registry实现 39 | */ 40 | public static synchronized Registry getRegistry(RegistryConfig registryConfig) { 41 | if (ALL_REGISTRIES.size() > 3) { // 超过3次 是不是配错了? 42 | if (LOGGER.isWarnEnabled()) { 43 | LOGGER.warn("Size of registry is greater than 3, Please check it!"); 44 | } 45 | } 46 | try { 47 | // 注意:RegistryConfig重写了equals方法,如果多个RegistryConfig属性一样,则认为是一个对象 48 | Registry registry = ALL_REGISTRIES.get(registryConfig); 49 | if (registry == null) { 50 | // TODO 使用SPI的方式生成服务注册 51 | // ExtensionClass ext =ext ExtensionLoaderFactory.getExtensionLoader(Registry.class) 52 | // .getExtensionClass(registryConfig.getProtocol()); 53 | // if (ext == null) { 54 | // throw ExceptionUtils.buildRuntime("registry.protocol", registryConfig.getProtocol(), 55 | // "Unsupported protocol of registry config !"); 56 | // } 57 | // registry = ext.getExtInstance(new Class[]{RegistryConfig.class}, new Object[]{registryConfig}); 58 | 59 | // ServiceLoader services = ServiceLoader.load(Registry.class); 60 | // Iterator iterator = services.iterator(); 61 | // while (iterator.hasNext()) { 62 | // registry = (Registry) iterator.next(); 63 | // } 64 | 65 | if (RpcConstants.ZOOKEEPER.equalsIgnoreCase(registryConfig.getProtocol())) { 66 | registry = new ZookeeperRegistry(registryConfig); 67 | } else if (RpcConstants.CONSUL.equalsIgnoreCase(registryConfig.getProtocol())) { 68 | registry = new ConsulRegistry(registryConfig); 69 | } 70 | ALL_REGISTRIES.put(registryConfig, registry); 71 | } 72 | return registry; 73 | } catch (ChildRpcRuntimeException e) { 74 | throw e; 75 | } catch (Throwable e) { 76 | throw new ChildRpcRuntimeException(e.getMessage(), e); 77 | } 78 | } 79 | 80 | /** 81 | * 得到全部注册中心配置 82 | * 83 | * @return 注册中心配置 84 | */ 85 | public static List getRegistryConfigs() { 86 | return new ArrayList(ALL_REGISTRIES.keySet()); 87 | } 88 | 89 | /** 90 | * 得到全部注册中心 91 | * 92 | * @return 注册中心 93 | */ 94 | public static List getRegistries() { 95 | return new ArrayList(ALL_REGISTRIES.values()); 96 | } 97 | 98 | /** 99 | * 关闭全部注册中心 100 | */ 101 | public static void destroyAll() { 102 | for (Map.Entry entry : ALL_REGISTRIES.entrySet()) { 103 | RegistryConfig config = entry.getKey(); 104 | Registry registry = entry.getValue(); 105 | try { 106 | // registry.destroy(); 107 | ALL_REGISTRIES.remove(config); 108 | } catch (Exception e) { 109 | LOGGER.error("Error when destroy registry :" + config 110 | + ", but you can ignore if it's called by JVM shutdown hook", e); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/registry/consul/ConsulRegistry.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.registry.consul; 2 | 3 | import cn.whforever.core.config.ClientConfig; 4 | import cn.whforever.core.config.RegistryConfig; 5 | import cn.whforever.core.config.ServerConfig; 6 | import cn.whforever.core.register.Registry; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * consul 注册中心. 12 | * 13 | * @author wuhaifei 2019-08-02 14 | */ 15 | public class ConsulRegistry extends Registry { 16 | 17 | /** 18 | * 注册中心配置 19 | * 20 | * @param registryConfig 注册中心配置 21 | */ 22 | public ConsulRegistry(RegistryConfig registryConfig) { 23 | super(registryConfig); 24 | } 25 | 26 | @Override 27 | public boolean start() { 28 | return false; 29 | } 30 | 31 | @Override 32 | public void register(ServerConfig config) { 33 | 34 | } 35 | 36 | @Override 37 | public void unRegister(ServerConfig config) { 38 | 39 | } 40 | 41 | @Override 42 | public void batchUnRegister(List configs) { 43 | 44 | } 45 | 46 | @Override 47 | public List subscribe(ClientConfig config) { 48 | return null; 49 | } 50 | 51 | @Override 52 | public void unSubscribe(ClientConfig config) { 53 | 54 | } 55 | 56 | @Override 57 | public void batchUnSubscribe(List configs) { 58 | 59 | } 60 | 61 | @Override 62 | public void destroy() { 63 | 64 | } 65 | 66 | @Override 67 | public void init() { 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/registry/zk/ZookeeperRegistry.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.registry.zk; 2 | 3 | import cn.whforever.core.config.ClientConfig; 4 | import cn.whforever.core.config.RegistryConfig; 5 | import cn.whforever.core.config.ServerConfig; 6 | import cn.whforever.core.exception.ChildRpcRuntimeException; 7 | import cn.whforever.core.register.Registry; 8 | import cn.whforever.core.util.CommonUtils; 9 | import cn.whforever.core.util.StringUtils; 10 | import org.apache.curator.RetryPolicy; 11 | import org.apache.curator.framework.CuratorFramework; 12 | import org.apache.curator.framework.CuratorFrameworkFactory; 13 | import org.apache.curator.framework.imps.CuratorFrameworkState; 14 | import org.apache.curator.framework.recipes.cache.PathChildrenCache; 15 | import org.apache.curator.retry.ExponentialBackoffRetry; 16 | import org.apache.zookeeper.CreateMode; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import java.net.URLEncoder; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | 26 | import static cn.whforever.core.util.RegistryUtils.buildConsumerPath; 27 | import static cn.whforever.core.util.RegistryUtils.buildProviderPath; 28 | import static cn.whforever.core.util.StringUtils.CONTEXT_SEP; 29 | 30 | /** 31 | *
 32 |  *  在zookeeper上存放的数据结构为:
 33 |  *  -$rootPath (根路径)
 34 |  *         └--child-rpc
 35 |  *             |--cn.whforever.rpc.example.HelloService (服务)
 36 |  *             |       |-providers (服务提供者列表)
 37 |  *             |       |     |--192.168.1.100:22000
 38 |  *             |       |     |--192.168.1.110:23000
 39 |  *             |       |     └--192.168.1.120:2400
 40 |  *             |       |-consumers (服务调用者列表)
 41 |  *             |       |     |--192.168.3.100
 42 |  *             |       |     |--192.168.3.110
 43 |  *             |       |     └--192.168.3.120
 44 |  *             |--cn.whforever.rpc.example.EchoService (下一个服务)
 45 |  *             | ......
 46 |  *  
47 | * 48 | * @author wuhf 49 | * @Date 2018/9/12 16:27 50 | **/ 51 | public class ZookeeperRegistry extends Registry { 52 | /** 53 | * 配置项:是否本地优先 54 | */ 55 | public final static String PARAM_PREFER_LOCAL_FILE = "preferLocalFile"; 56 | /** 57 | * 配置项:是否使用临时节点。
58 | * 如果使用临时节点:那么断开连接的时候,将zookeeper将自动消失。好处是如果服务端异常关闭,也不会有垃圾数据。
59 | * 坏处是如果和zookeeper的网络闪断也通知客户端,客户端以为是服务端下线
60 | * 如果使用永久节点:好处:网络闪断时不会影响服务端,而是由客户端进行自己判断长连接
61 | * 坏处:服务端如果是异常关闭(无反注册),那么数据里就由垃圾节点,得由另外的哨兵程序进行判断 62 | */ 63 | public final static String PARAM_CREATE_EPHEMERAL = "createEphemeral"; 64 | /** 65 | * slf4j Logger for this class 66 | */ 67 | private final static Logger LOGGER = LoggerFactory.getLogger(ZookeeperRegistry.class); 68 | /** 69 | * 服务被下线 70 | */ 71 | private final static byte[] PROVIDER_OFFLINE = new byte[]{0}; 72 | /** 73 | * 正常在线服务 74 | */ 75 | private final static byte[] PROVIDER_ONLINE = new byte[]{1}; 76 | /** 77 | * Zookeeper zkClient 78 | */ 79 | private CuratorFramework zkClient; 80 | /** 81 | * Root path of registry data 82 | */ 83 | private String rootPath; 84 | /** 85 | * Prefer get data from local file to remote zk cluster. 86 | * 87 | * @see ZookeeperRegistry#PARAM_PREFER_LOCAL_FILE 88 | */ 89 | private boolean preferLocalFile = false; 90 | /** 91 | * Create EPHEMERAL node when true, otherwise PERSISTENT 92 | * 93 | * @see ZookeeperRegistry#PARAM_CREATE_EPHEMERAL 94 | * @see CreateMode#PERSISTENT 95 | * @see CreateMode#EPHEMERAL 96 | */ 97 | private boolean ephemeralNode = true; 98 | /** 99 | * 保存服务发布者的url 100 | */ 101 | private Map providerUrls = new ConcurrentHashMap<>(); 102 | /** 103 | * 保存服务消费者的url 104 | */ 105 | private Map consumerUrls = new ConcurrentHashMap<>(); 106 | 107 | /** 108 | * 注册中心配置 109 | * 110 | * @param registryConfig 注册中心配置 111 | */ 112 | public ZookeeperRegistry(RegistryConfig registryConfig) { 113 | super(registryConfig); 114 | } 115 | 116 | /** 117 | * 初始化zookeeper 118 | */ 119 | @Override 120 | public synchronized void init() { 121 | if (zkClient != null) { 122 | return; 123 | } 124 | 125 | // xxx:2181,yyy:2181/path1/paht2 126 | String addressInput = registryConfig.getAddress(); 127 | if (StringUtils.isEmpty(addressInput)) { 128 | throw new ChildRpcRuntimeException("Address of zookeeper registry is empty."); 129 | } 130 | 131 | int idx = addressInput.indexOf(CONTEXT_SEP); 132 | String address; // IP地址 133 | if (idx > 0) { 134 | address = addressInput.substring(0, idx); 135 | rootPath = addressInput.substring(idx); 136 | if (!rootPath.endsWith(CONTEXT_SEP)) { 137 | // 保证以"/"结尾 138 | rootPath += CONTEXT_SEP; 139 | } 140 | } else { 141 | address = addressInput; 142 | rootPath = CONTEXT_SEP; 143 | } 144 | preferLocalFile = !CommonUtils.isFalse(registryConfig.getParameter(PARAM_PREFER_LOCAL_FILE)); 145 | ephemeralNode = !CommonUtils.isFalse(registryConfig.getParameter(PARAM_CREATE_EPHEMERAL)); 146 | 147 | if (LOGGER.isInfoEnabled()) { 148 | LOGGER.info( 149 | "Init ZookeeperRegistry with address {}, root path is {}. preferLocalFile:{}, ephemeralNode:{}", 150 | address, rootPath, preferLocalFile, ephemeralNode); 151 | } 152 | 153 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); 154 | zkClient = CuratorFrameworkFactory.builder() 155 | .connectString(address) 156 | .sessionTimeoutMs(registryConfig.getConnectTimeout() * 3) 157 | .connectionTimeoutMs(registryConfig.getConnectTimeout()) 158 | .sessionTimeoutMs(1000) 159 | .connectionTimeoutMs(1000) 160 | .canBeReadOnly(false) 161 | .retryPolicy(retryPolicy) 162 | .defaultData(null) 163 | .build(); 164 | } 165 | 166 | @Override 167 | public synchronized boolean start() { 168 | if (zkClient == null) { 169 | LOGGER.warn("Start zookeeper registry must be do init first!"); 170 | return false; 171 | } 172 | if (zkClient.getState() == CuratorFrameworkState.STARTED) { 173 | return true; 174 | } 175 | try { 176 | zkClient.start(); 177 | } catch (Exception e) { 178 | throw new ChildRpcRuntimeException("Failed to start zookeeper zkClient", e); 179 | } 180 | return zkClient.getState() == CuratorFrameworkState.STARTED; 181 | } 182 | 183 | @Override 184 | public void destroy() { 185 | if (zkClient != null && zkClient.getState() == CuratorFrameworkState.STARTED) { 186 | zkClient.close(); 187 | } 188 | providerUrls.clear(); 189 | consumerUrls.clear(); 190 | } 191 | 192 | @Override 193 | public void destroy(DestroyHook hook) { 194 | hook.preDestroy(); 195 | destroy(); 196 | hook.postDestroy(); 197 | } 198 | 199 | @Override 200 | public void register(ServerConfig config) { 201 | String appName = (String) config.getInterfaceId(); 202 | if (!registryConfig.isRegister()) { 203 | // do some log 204 | return; 205 | } 206 | if (config.isRegister()) { 207 | // 注册服务端节点 208 | try { 209 | String url = ZookeeperRegistryHelper.convertProviderToUrls(config); 210 | if (StringUtils.isNotBlank(url)) { 211 | String providerPath = buildProviderPath(rootPath, config); 212 | // do some logger 213 | url = URLEncoder.encode(url, "UTF-8"); 214 | String providerUrl = providerPath + CONTEXT_SEP + url; 215 | getAndCheckZkClient().create().creatingParentContainersIfNeeded() 216 | .withMode(ephemeralNode ? CreateMode.EPHEMERAL : CreateMode.PERSISTENT) // 是否永久节点 217 | // .forPath(providerUrl, config.isDynamic() ? PROVIDER_ONLINE : PROVIDER_OFFLINE); // 是否默认上下线 218 | .forPath(providerUrl, PROVIDER_ONLINE); 219 | // do some log 220 | providerUrls.put(config, url); 221 | // do some log 222 | } 223 | } catch (Exception e) { 224 | throw new ChildRpcRuntimeException("Failed to register provider to zookeeperRegistry!", e); 225 | } 226 | } 227 | } 228 | 229 | @Override 230 | public void unRegister(ServerConfig config) { 231 | String appName = (String) config.getInterfaceId(); 232 | if (!registryConfig.isRegister()) { 233 | // 注册中心不注册 do some log 234 | return; 235 | } 236 | // 反注册服务端节点 237 | if (config.isRegister()) { 238 | try { 239 | String url = providerUrls.remove(config); 240 | String providerPath = buildProviderPath(rootPath, config); 241 | url = URLEncoder.encode(url, "UTF-8"); 242 | getAndCheckZkClient().delete().forPath(providerPath + CONTEXT_SEP + url); 243 | // do some log 244 | } catch (Exception e) { 245 | throw new ChildRpcRuntimeException("Failed to unregister provider to zookeeperRegistry!", e); 246 | } 247 | } 248 | } 249 | 250 | @Override 251 | public void batchUnRegister(List configs) { 252 | // // 一个一个来,后续看看要不要使用curator的事务 253 | for (ServerConfig config : configs) { 254 | unRegister(config); 255 | } 256 | } 257 | 258 | @Override 259 | public List subscribe(final ClientConfig config) { 260 | String appName = config.getInterfaceId(); 261 | if (!registryConfig.isSubscribe()) { 262 | // 注册中心不订阅 263 | LOGGER.info("服务未订阅 [{}]", appName); 264 | return new ArrayList<>(); 265 | } 266 | // 注册Consumer节点 267 | if (config.isRegister()) { 268 | try { 269 | if (StringUtils.isBlank(consumerUrls.get(config))) { 270 | String consumerPath = buildConsumerPath(rootPath, config); 271 | String url = ZookeeperRegistryHelper.convertConsumerToUrl(config); 272 | String encodeUrl = URLEncoder.encode(url, "UTF-8"); 273 | getAndCheckZkClient().create().creatingParentContainersIfNeeded() 274 | // Consumer临时节点 275 | .withMode(CreateMode.EPHEMERAL) 276 | .forPath(consumerPath + CONTEXT_SEP + encodeUrl); 277 | consumerUrls.put(config, url); 278 | } 279 | } catch (Exception e) { 280 | throw new ChildRpcRuntimeException("Failed to register consumer to zookeeperRegistry!", e); 281 | } 282 | } 283 | if (config.isSubscribe()) { 284 | // 订阅Providers节点 285 | try { 286 | final String providerPath = buildProviderPath(rootPath, config); 287 | // 监听配置节点下 子节点增加、子节点删除、子节点Data修改事件 288 | PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient, providerPath, true); 289 | pathChildrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); 290 | List providerInfos = ZookeeperRegistryHelper.convertUrlsToProviders(providerPath, pathChildrenCache.getCurrentData()); 291 | return providerInfos; 292 | } catch (Exception e) { 293 | throw new ChildRpcRuntimeException("Failed to subscribe provider from zookeeperRegistry!", e); 294 | } 295 | } 296 | return null; 297 | } 298 | 299 | @Override 300 | public void unSubscribe(ClientConfig config) { 301 | // 反注册服务端节点 302 | if (config.isRegister()) { 303 | try { 304 | String url = consumerUrls.remove(config); 305 | if (url != null) { 306 | String consumerPath = buildConsumerPath(rootPath, config); 307 | url = URLEncoder.encode(url, "UTF-8"); 308 | getAndCheckZkClient().delete().forPath(consumerPath + CONTEXT_SEP + url); 309 | } 310 | } catch (Exception e) { 311 | // if (!RpcRunningState.isShuttingDown()) { 312 | throw new ChildRpcRuntimeException("Failed to unregister consumer to zookeeperRegistry!", e); 313 | // } 314 | } 315 | } 316 | } 317 | 318 | @Override 319 | public void batchUnSubscribe(List configs) { 320 | // 一个一个来,后续看看要不要使用curator的事务 321 | for (ClientConfig config : configs) { 322 | unSubscribe(config); 323 | } 324 | } 325 | 326 | protected CuratorFramework getZkClient() { 327 | return zkClient; 328 | } 329 | 330 | private CuratorFramework getAndCheckZkClient() { 331 | if (zkClient == null || zkClient.getState() != CuratorFrameworkState.STARTED) { 332 | throw new ChildRpcRuntimeException("Zookeeper client is not available"); 333 | } 334 | return zkClient; 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/registry/zk/ZookeeperRegistryHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.registry.zk; 18 | 19 | import cn.whforever.core.util.CommonUtils; 20 | import cn.whforever.core.util.RegistryUtils; 21 | import org.apache.curator.framework.recipes.cache.ChildData; 22 | 23 | import java.io.UnsupportedEncodingException; 24 | import java.net.URLDecoder; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * Helper for ZookeeperRegistry 30 | * 31 | * @author GengZhang 32 | */ 33 | public class ZookeeperRegistryHelper extends RegistryUtils { 34 | 35 | /** 36 | * Convert url to provider list. 37 | * 38 | * @param providerPath 39 | * @param currentData the current data 40 | * @return the list 41 | * @throws UnsupportedEncodingException decode exception 42 | */ 43 | static List convertUrlsToProviders(String providerPath, 44 | List currentData) throws UnsupportedEncodingException { 45 | List providerInfos = new ArrayList<>(); 46 | 47 | if (CommonUtils.isEmpty(currentData)) { 48 | return providerInfos; 49 | } 50 | 51 | for (ChildData childData : currentData) { 52 | providerInfos.add(convertUrlToProvider(providerPath, childData)); 53 | } 54 | return providerInfos; 55 | } 56 | 57 | static String convertUrlToProvider(String providerPath, 58 | ChildData childData) throws UnsupportedEncodingException { 59 | String url = childData.getPath(); 60 | url = URLDecoder.decode(url, "UTF-8"); 61 | url = url.replace(providerPath + "/", ""); 62 | return url; 63 | } 64 | 65 | // /** 66 | // * Convert child data to attribute list. 67 | // * 68 | // * @param configPath the config path 69 | // * @param currentData the current data 70 | // * @return the attribute list 71 | // */ 72 | // static List> convertConfigToAttributes(String configPath, 73 | // List currentData) { 74 | // List> attributes = new ArrayList>(); 75 | // if (CommonUtils.isEmpty(currentData)) { 76 | // return attributes; 77 | // } 78 | // 79 | // for (ChildData childData : currentData) { 80 | // attributes.add(convertConfigToAttribute(configPath, childData, false)); 81 | // } 82 | // return attributes; 83 | // } 84 | // 85 | // /** 86 | // * Convert child data to attribute. 87 | // * 88 | // * @param configPath the config path 89 | // * @param childData the child data 90 | // * @param removeType is remove type 91 | // * @return the attribute 92 | // */ 93 | // static Map convertConfigToAttribute(String configPath, ChildData childData, 94 | // boolean removeType) { 95 | // String attribute = childData.getPath().substring(configPath.length() + 1); 96 | // //If event type is CHILD_REMOVED, attribute should return to default value 97 | // return Collections.singletonMap(attribute, removeType ? RpcConfigs.getStringValue(attribute) 98 | // : StringSerializer.decode(childData.getData())); 99 | // } 100 | 101 | // /** 102 | // * Convert child data to attribute list. 103 | // * 104 | // * @param config the interface config 105 | // * @param overridePath the override path 106 | // * @param currentData the current data 107 | // * @return the attribute list 108 | // * @throws UnsupportedEncodingException decode exception 109 | // */ 110 | // static List> convertOverrideToAttributes(AbstractInterfaceConfig config, 111 | // String overridePath, 112 | // List currentData) 113 | // throws UnsupportedEncodingException { 114 | // List> attributes = new ArrayList>(); 115 | // if (CommonUtils.isEmpty(currentData)) { 116 | // return attributes; 117 | // } 118 | // 119 | // for (ChildData childData : currentData) { 120 | // String url = URLDecoder.decode(childData.getPath().substring(overridePath.length() + 1), 121 | // "UTF-8"); 122 | // if (config instanceof ConsumerConfig) { 123 | // //If child data contains system local host, convert config to attribute 124 | // if (StringUtils.isNotEmpty(url) && StringUtils.isNotEmpty(SystemInfo.getLocalHost()) 125 | // && url.contains("://" + SystemInfo.getLocalHost() + "?")) { 126 | // attributes.add(convertConfigToAttribute(overridePath, childData, false)); 127 | // } 128 | // } 129 | // } 130 | // return attributes; 131 | // } 132 | 133 | // /** 134 | // * Convert child data to attribute. 135 | // * 136 | // * @param overridePath the override path 137 | // * @param childData the child data 138 | // * @param removeType is remove type 139 | // * @param interfaceConfig register provider/consumer config 140 | // * @return the attribute 141 | // * @throws Exception decode exception 142 | // */ 143 | // static Map convertOverrideToAttribute(String overridePath, ChildData childData, 144 | // boolean removeType, 145 | // AbstractInterfaceConfig interfaceConfig) throws Exception { 146 | // String url = URLDecoder.decode(childData.getPath().substring(overridePath.length() + 1), 147 | // "UTF-8"); 148 | // Map attribute = new ConcurrentHashMap(); 149 | // for (String keyPairs : url.substring(url.indexOf('?') + 1).split("&")) { 150 | // String[] overrideAttrs = keyPairs.split("="); 151 | // // TODO 这个列表待确认,不少字段是不支持的 152 | // List configKeys = Arrays.asList(RpcConstants.CONFIG_KEY_TIMEOUT, 153 | // RpcConstants.CONFIG_KEY_SERIALIZATION, RpcConstants.CONFIG_KEY_LOADBALANCER); 154 | // if (configKeys.contains(overrideAttrs[0])) { 155 | // if (removeType) { 156 | // Class clazz = null; 157 | // if (interfaceConfig instanceof ProviderConfig) { 158 | // // TODO 服务端也生效? 159 | // clazz = ProviderConfig.class; 160 | // } else if (interfaceConfig instanceof ConsumerConfig) { 161 | // clazz = ConsumerConfig.class; 162 | // } 163 | // if (clazz != null) { 164 | // Method getMethod = ReflectUtils.getPropertyGetterMethod(clazz, 165 | // overrideAttrs[0]); 166 | // Class propertyClazz = getMethod.getReturnType(); 167 | // //If event type is CHILD_REMOVED, attribute should return to register value 168 | // attribute.put(overrideAttrs[0], StringUtils.toString(BeanUtils 169 | // .getProperty(interfaceConfig, overrideAttrs[0], propertyClazz))); 170 | // } 171 | // } else { 172 | // attribute.put(overrideAttrs[0], overrideAttrs[1]); 173 | // } 174 | // } 175 | // } 176 | // return attribute; 177 | // } 178 | 179 | // static String buildOverridePath(String rootPath, AbstractInterfaceConfig config) { 180 | // return rootPath + "sofa-rpc/" + config.getInterfaceId() + "/overrides"; 181 | // } 182 | } 183 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/remote/client/AbstractChildClient.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.remote.client; 2 | 3 | import cn.whforever.core.config.Config; 4 | import cn.whforever.core.rpc.RpcRequest; 5 | import cn.whforever.core.rpc.RpcResponse; 6 | 7 | /** 8 | * @author wuhf 9 | * @data 2018-10-24 10 | */ 11 | public abstract class AbstractChildClient { 12 | // ---------------------- config ---------------------- 13 | protected Config config; 14 | 15 | public void init(Config config) { 16 | this.config = config; 17 | } 18 | 19 | // ---------------------- operate ---------------------- 20 | 21 | public abstract RpcResponse send(RpcRequest request) throws Exception; 22 | } 23 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/remote/server/AbstractChildServer.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.remote.server; 2 | 3 | import cn.whforever.core.config.Config; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * @author wuhf 9 | */ 10 | public abstract class AbstractChildServer { 11 | private static final Logger logger = LoggerFactory.getLogger(AbstractChildServer.class); 12 | 13 | public abstract void start(Config config) throws Exception; 14 | 15 | public abstract void destroy() throws Exception; 16 | } 17 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/rpc/RpcCallbackFuture.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.rpc; 2 | 3 | import java.text.MessageFormat; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.ConcurrentMap; 6 | import java.util.concurrent.TimeoutException; 7 | 8 | /** 9 | * netty的连接超时操作. 10 | * 11 | * @author wuhf 12 | * @Date 2018/8/31 19:06 13 | **/ 14 | public class RpcCallbackFuture { 15 | 16 | /** 17 | * 过期,失效 18 | */ 19 | public static ConcurrentMap futurePool = new ConcurrentHashMap(); 20 | 21 | /** 22 | * net codec 23 | */ 24 | private RpcRequest request; 25 | private RpcResponse response; 26 | /** 27 | * future lock 28 | */ 29 | private boolean isDone = false; 30 | private Object lock = new Object(); 31 | 32 | public RpcCallbackFuture(RpcRequest request) { 33 | this.request = request; 34 | futurePool.put(request.getRequestId(), this); 35 | } 36 | 37 | public RpcResponse getResponse() { 38 | return response; 39 | } 40 | 41 | public void setResponse(RpcResponse response) { 42 | this.response = response; 43 | // notify future lock 44 | synchronized (lock) { 45 | isDone = true; 46 | lock.notifyAll(); 47 | } 48 | } 49 | 50 | public RpcResponse get(long timeoutMillis) throws InterruptedException, TimeoutException { 51 | if (!isDone) { 52 | synchronized (lock) { 53 | try { 54 | lock.wait(timeoutMillis); 55 | } catch (InterruptedException e) { 56 | e.printStackTrace(); 57 | throw e; 58 | } 59 | } 60 | } 61 | 62 | if (!isDone) { 63 | throw new TimeoutException(MessageFormat.format(">>>>>>>>>>>> child-rpc, netty request timeout at:{0}, request:{1}", System.currentTimeMillis(), request.toString())); 64 | } 65 | return response; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/rpc/RpcConstants.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.rpc; 2 | 3 | /** 4 | * RPC常量类 5 | * 6 | * @author wuhaifei 7 | * @date 2018/08/30 8 | */ 9 | public class RpcConstants { 10 | 11 | /** 12 | * 网络直连. 13 | */ 14 | public static final String DIRECT_CONN = "DIRECT_CONN"; 15 | 16 | /** 17 | * zookeeper 18 | */ 19 | public static final String ZOOKEEPER = "ZOOKEEPER"; 20 | public static final String CONSUL = "CONSUL"; 21 | 22 | public static final String CONFIG_KEY_PID = "pid"; 23 | public static final String CONFIG_KEY_INTERFACE = "interface"; 24 | public static final String CONFIG_KEY_PROTOCOL = "protocol"; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/rpc/RpcInvokerHandler.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.rpc; 2 | 3 | import cn.whforever.core.exception.ChildRpcRuntimeException; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author wuhf 11 | * @Date 2018/9/1 18:10 12 | **/ 13 | public class RpcInvokerHandler { 14 | public static Map serviceMap = new HashMap(); 15 | 16 | public static RpcResponse invokeService(RpcRequest request) { 17 | Object serviceBean = serviceMap.get(request.getClassName()); 18 | 19 | RpcResponse response = new RpcResponse(); 20 | response.setRequestId(request.getRequestId()); 21 | try { 22 | 23 | Class serviceClass = serviceBean.getClass(); 24 | String methodName = request.getMethodName(); 25 | Class[] parameterTypes = request.getParameterTypes(); 26 | Object[] parameters = request.getParameters(); 27 | 28 | Method method = serviceClass.getMethod(methodName, parameterTypes); 29 | method.setAccessible(true); 30 | Object result = method.invoke(serviceBean, parameters); 31 | 32 | response.setResult(result); 33 | } catch (Throwable t) { 34 | t.printStackTrace(); 35 | response.setError(new ChildRpcRuntimeException(t)); 36 | } 37 | 38 | return response; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/rpc/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.rpc; 2 | 3 | import java.io.Serializable; 4 | 5 | public class RpcRequest implements Serializable { 6 | private static final long serialVersionUID = -4364536436151723421L; 7 | 8 | private String requestId; 9 | private long createMillisTime; 10 | private String className; 11 | private String methodName; 12 | private Class[] parameterTypes; 13 | private Object[] parameters; 14 | 15 | public String getRequestId() { 16 | return requestId; 17 | } 18 | 19 | public void setRequestId(String requestId) { 20 | this.requestId = requestId; 21 | } 22 | 23 | public long getCreateMillisTime() { 24 | return createMillisTime; 25 | } 26 | 27 | public void setCreateMillisTime(long createMillisTime) { 28 | this.createMillisTime = createMillisTime; 29 | } 30 | 31 | public String getClassName() { 32 | return className; 33 | } 34 | 35 | public void setClassName(String className) { 36 | this.className = className; 37 | } 38 | 39 | public String getMethodName() { 40 | return methodName; 41 | } 42 | 43 | public void setMethodName(String methodName) { 44 | this.methodName = methodName; 45 | } 46 | 47 | public Class[] getParameterTypes() { 48 | return parameterTypes; 49 | } 50 | 51 | public void setParameterTypes(Class[] parameterTypes) { 52 | this.parameterTypes = parameterTypes; 53 | } 54 | 55 | public Object[] getParameters() { 56 | return parameters; 57 | } 58 | 59 | public void setParameters(Object[] parameters) { 60 | this.parameters = parameters; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/rpc/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.rpc; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * RPC常量类 7 | * 8 | * @author wuhaifei 9 | * @date 2018/08/30 10 | */ 11 | public class RpcResponse implements Serializable { 12 | private static final long serialVersionUID = 7329530374415722876L; 13 | 14 | private String requestId; 15 | private Throwable error; 16 | private Object result; 17 | 18 | public String getRequestId() { 19 | return requestId; 20 | } 21 | 22 | public void setRequestId(String requestId) { 23 | this.requestId = requestId; 24 | } 25 | 26 | public Throwable getError() { 27 | return error; 28 | } 29 | 30 | public void setError(Throwable error) { 31 | this.error = error; 32 | } 33 | 34 | public Object getResult() { 35 | return result; 36 | } 37 | 38 | public void setResult(Object result) { 39 | this.result = result; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | StringBuilder sb = new StringBuilder(128); 45 | sb.append("RpcResponse["); 46 | sb.append("child-rpc exception=").append(error).append(", "); 47 | sb.append("child-rpc requestId=").append(requestId).append(", "); 48 | sb.append("RpcResponse=").append(result).append("]"); 49 | return sb.toString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/rpc/RpcRuntimeContext.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.rpc; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.lang.management.ManagementFactory; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * 全局的运行时上下文 11 | * 12 | * @author wuhaifei 13 | * @date 2018/08/30 14 | */ 15 | public class RpcRuntimeContext { 16 | /** 17 | * 当前进程Id 18 | */ 19 | public static final String PID = ManagementFactory 20 | .getRuntimeMXBean() 21 | .getName().split("@")[0]; 22 | /** 23 | * 当前应用启动时间(用这个类加载时间为准) 24 | */ 25 | public static final long START_TIME = now(); 26 | /** 27 | * slf4j Logger for this class 28 | */ 29 | private final static Logger LOGGER = LoggerFactory.getLogger(RpcRuntimeContext.class); 30 | /** 31 | * 上下文信息,例如instancekey,本机ip等信息 32 | */ 33 | private final static ConcurrentHashMap CONTEXT = new ConcurrentHashMap(); 34 | 35 | /** 36 | * 发布的服务配置 37 | */ 38 | // private final static ConcurrentHashSet EXPORTED_PROVIDER_CONFIGS = new ConcurrentHashSet(); 39 | // 40 | // /** 41 | // * 发布的订阅配置 42 | // */ 43 | // private final static ConcurrentHashSet REFERRED_CONSUMER_CONFIGS = new ConcurrentHashSet(); 44 | // 45 | // /** 46 | // * 关闭资源的钩子 47 | // */ 48 | // private final static List DESTROY_HOOKS = new CopyOnWriteArrayList(); 49 | 50 | 51 | static { 52 | if (LOGGER.isInfoEnabled()) { 53 | LOGGER.info("Welcome! Loading SOFA RPC Framework : {}, PID is:{}", "version 0.1", PID); 54 | } 55 | // put(RpcConstants.CONFIG_KEY_RPC_VERSION, Version.RPC_VERSION); 56 | // // 初始化一些上下文 57 | // initContext(); 58 | // // 初始化其它模块 59 | // ModuleFactory.installModules(); 60 | // // 增加jvm关闭事件 61 | // if (RpcConfigs.getOrDefaultValue(RpcOptions.JVM_SHUTDOWN_HOOK, true)) { 62 | // Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 63 | // @Override 64 | // public void run() { 65 | // if (LOGGER.isWarnEnabled()) { 66 | // LOGGER.warn("SOFA RPC Framework catch JVM shutdown event, Run shutdown hook now."); 67 | // } 68 | // destroy(false); 69 | // } 70 | // }, "SOFA-RPC-ShutdownHook")); 71 | // } 72 | } 73 | 74 | /** 75 | * 初始化一些上下文 76 | */ 77 | private static void initContext() { 78 | // putIfAbsent(KEY_APPID, RpcConfigs.getOrDefaultValue(APP_ID, null)); 79 | // putIfAbsent(KEY_APPNAME, RpcConfigs.getOrDefaultValue(APP_NAME, null)); 80 | // putIfAbsent(KEY_APPINSID, RpcConfigs.getOrDefaultValue(INSTANCE_ID, null)); 81 | // putIfAbsent(KEY_APPAPTH, System.getProperty("user.dir")); 82 | } 83 | 84 | /** 85 | * 获取当前时间,此处可以做优化 86 | * 87 | * @return 当前时间 88 | */ 89 | public static long now() { 90 | return System.currentTimeMillis(); 91 | } 92 | 93 | /** 94 | * 得到上下文信息 95 | * 96 | * @param key the key 97 | * @return the object 98 | * @see ConcurrentHashMap#get(Object) 99 | */ 100 | public static Object get(String key) { 101 | return CONTEXT.get(key); 102 | } 103 | 104 | /** 105 | * 设置上下文信息(不存在才设置成功) 106 | * 107 | * @param key the key 108 | * @param value the value 109 | * @return the object 110 | * @see ConcurrentHashMap#putIfAbsent(Object, Object) 111 | */ 112 | public static Object putIfAbsent(String key, Object value) { 113 | return value == null ? CONTEXT.remove(key) : CONTEXT.putIfAbsent(key, value); 114 | } 115 | 116 | /** 117 | * 设置上下文信息 118 | * 119 | * @param key the key 120 | * @param value the value 121 | * @return the object 122 | * @see ConcurrentHashMap#put(Object, Object) 123 | */ 124 | public static Object put(String key, Object value) { 125 | return value == null ? CONTEXT.remove(key) : CONTEXT.put(key, value); 126 | } 127 | 128 | /** 129 | * 得到全部上下文信息 130 | * 131 | * @return the CONTEXT 132 | */ 133 | public static ConcurrentHashMap getContext() { 134 | return new ConcurrentHashMap(CONTEXT); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/serialize/Serializer.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.serialize; 2 | 3 | import cn.whforever.core.serialize.impl.HessianSerializer; 4 | import cn.whforever.core.serialize.impl.JacksonSerializer; 5 | import cn.whforever.core.serialize.impl.ProtostuffSerializer; 6 | 7 | /** 8 | * @author wuhf 9 | * @Date 2018/8/31 19:46 10 | **/ 11 | public abstract class Serializer { 12 | public abstract byte[] serialize(T obj); 13 | 14 | public abstract Object deserialize(byte[] bytes, Class clazz); 15 | 16 | public enum SerializeEnum { 17 | 18 | /** 19 | * hession 序列化. 20 | */ 21 | HESSIAN(new HessianSerializer()), 22 | PROTOSTUFF(new ProtostuffSerializer()), 23 | 24 | /** 25 | * json 序列化. 26 | */ 27 | JSON(new JacksonSerializer()); 28 | 29 | public final Serializer serializer; 30 | 31 | private SerializeEnum(Serializer serializer) { 32 | this.serializer = serializer; 33 | } 34 | 35 | public static SerializeEnum match(String name, SerializeEnum defaultSerializer) { 36 | for (SerializeEnum item : SerializeEnum.values()) { 37 | if (item.name().equals(name)) { 38 | return item; 39 | } 40 | } 41 | return defaultSerializer; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/serialize/impl/HessianSerializer.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.serialize.impl; 2 | 3 | import cn.whforever.core.serialize.Serializer; 4 | import com.caucho.hessian.io.HessianInput; 5 | import com.caucho.hessian.io.HessianOutput; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | 11 | /** 12 | * hessian serialize 13 | * 14 | * @author haifeiwugm.@gmail.com 15 | */ 16 | public class HessianSerializer extends Serializer { 17 | 18 | @Override 19 | public byte[] serialize(T obj) { 20 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 21 | HessianOutput ho = new HessianOutput(os); 22 | try { 23 | ho.writeObject(obj); 24 | } catch (IOException e) { 25 | throw new IllegalStateException(e.getMessage(), e); 26 | } 27 | return os.toByteArray(); 28 | } 29 | 30 | @Override 31 | public Object deserialize(byte[] bytes, Class clazz) { 32 | ByteArrayInputStream is = new ByteArrayInputStream(bytes); 33 | HessianInput hi = new HessianInput(is); 34 | try { 35 | return hi.readObject(); 36 | } catch (IOException e) { 37 | throw new IllegalStateException(e.getMessage(), e); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/serialize/impl/JacksonSerializer.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.serialize.impl; 2 | 3 | import cn.whforever.core.serialize.Serializer; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * json serialize 11 | * 12 | * @author haifeiwugm.@gmail.com 13 | */ 14 | public class JacksonSerializer extends Serializer { 15 | private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 16 | 17 | @Override 18 | public byte[] serialize(T obj) { 19 | try { 20 | return OBJECT_MAPPER.writeValueAsBytes(obj); 21 | } catch (IOException e) { 22 | throw new IllegalStateException(e.getMessage(), e); 23 | } 24 | } 25 | 26 | /** 27 | * string --> bean、Map、List(array) 28 | */ 29 | @Override 30 | public Object deserialize(byte[] bytes, Class clazz) { 31 | try { 32 | OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); 33 | return OBJECT_MAPPER.readValue(bytes, clazz); 34 | } catch (IOException e) { 35 | throw new IllegalStateException(e.getMessage(), e); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/serialize/impl/ProtostuffSerializer.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.serialize.impl; 2 | 3 | import cn.whforever.core.exception.ChildRpcRuntimeException; 4 | import cn.whforever.core.serialize.Serializer; 5 | import io.protostuff.LinkedBuffer; 6 | import io.protostuff.ProtostuffIOUtil; 7 | import io.protostuff.Schema; 8 | import io.protostuff.runtime.RuntimeSchema; 9 | import org.objenesis.Objenesis; 10 | import org.objenesis.ObjenesisStd; 11 | 12 | import java.util.Map; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | /** 16 | * Protostuff util 17 | * 18 | * @author haifeiwugm@gmail.com 19 | */ 20 | public class ProtostuffSerializer extends Serializer { 21 | 22 | private static Objenesis objenesis = new ObjenesisStd(true); 23 | private static Map, Schema> cachedSchema = new ConcurrentHashMap, Schema>(); 24 | 25 | private static Schema getSchema(Class cls) { 26 | @SuppressWarnings("unchecked") 27 | Schema schema = (Schema) cachedSchema.get(cls); 28 | if (schema == null) { 29 | schema = RuntimeSchema.createFrom(cls); 30 | if (schema != null) { 31 | cachedSchema.put(cls, schema); 32 | } 33 | } 34 | return schema; 35 | } 36 | 37 | @Override 38 | public byte[] serialize(T obj) { 39 | @SuppressWarnings("unchecked") 40 | Class cls = (Class) obj.getClass(); 41 | LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); 42 | try { 43 | Schema schema = getSchema(cls); 44 | return ProtostuffIOUtil.toByteArray(obj, schema, buffer); 45 | } catch (Exception e) { 46 | throw new ChildRpcRuntimeException(e); 47 | } finally { 48 | buffer.clear(); 49 | } 50 | } 51 | 52 | @Override 53 | public Object deserialize(byte[] bytes, Class clazz) { 54 | try { 55 | T message = (T) objenesis.newInstance(clazz); 56 | Schema schema = getSchema(clazz); 57 | ProtostuffIOUtil.mergeFrom(bytes, message, schema); 58 | return message; 59 | } catch (Exception e) { 60 | throw new ChildRpcRuntimeException(e); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/BeanUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.util; 18 | 19 | 20 | import cn.whforever.core.exception.ChildRpcRuntimeException; 21 | 22 | import java.lang.reflect.Field; 23 | import java.lang.reflect.InvocationTargetException; 24 | import java.lang.reflect.Method; 25 | import java.lang.reflect.Modifier; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * Bean的一些操作 33 | * 34 | * @author GengZhang 35 | */ 36 | public class BeanUtils { 37 | 38 | /** 39 | * 设置属性 40 | * 41 | * @param bean 对象 42 | * @param name 属性名 43 | * @param clazz 设置值的类 44 | * @param value 属性值 45 | * @param 和值对应的类型 46 | * @throws Exception 设值异常 47 | */ 48 | public static void setProperty(Object bean, String name, Class clazz, T value) throws Exception { 49 | Method method = ReflectUtils.getPropertySetterMethod(bean.getClass(), name, clazz); 50 | if (method.isAccessible()) { 51 | method.invoke(bean, value); 52 | } else { 53 | try { 54 | method.setAccessible(true); 55 | method.invoke(bean, value); 56 | } finally { 57 | method.setAccessible(false); 58 | } 59 | } 60 | } 61 | 62 | /** 63 | * 得到属性的值 64 | * 65 | * @param bean 对象 66 | * @param name 属性名 67 | * @param clazz 设置值的类 68 | * @param 和返回值对应的类型 69 | * @return 属性值 70 | * @throws Exception 取值异常 71 | */ 72 | public static T getProperty(Object bean, String name, Class clazz) throws Exception { 73 | Method method = ReflectUtils.getPropertyGetterMethod(bean.getClass(), name); 74 | if (method.isAccessible()) { 75 | return (T) method.invoke(bean); 76 | } else { 77 | try { 78 | method.setAccessible(true); 79 | return (T) method.invoke(bean); 80 | } finally { 81 | method.setAccessible(false); 82 | } 83 | } 84 | } 85 | 86 | /** 87 | * 复制属性到map,可以自定义前缀 88 | * 89 | * @param bean 对象 90 | * @param prefix 放入key的前缀 91 | * @param map 要写入的map 92 | */ 93 | public static void copyPropertiesToMap(Object bean, String prefix, Map map) { 94 | Class clazz = bean.getClass(); 95 | Method[] methods = bean.getClass().getMethods(); 96 | for (Method method : methods) { 97 | // 复制属性 98 | Class returnc = method.getReturnType(); 99 | if (ReflectUtils.isBeanPropertyReadMethod(method)) { 100 | String propertyName = ReflectUtils.getPropertyNameFromBeanReadMethod(method); 101 | try { 102 | if (ReflectUtils.getPropertySetterMethod(clazz, propertyName, returnc) == null) { 103 | continue; // 还需要有set方法 104 | } 105 | } catch (Exception e) { 106 | continue; 107 | } 108 | Object val; 109 | try { 110 | val = method.invoke(bean); 111 | } catch (InvocationTargetException e) { 112 | throw new ChildRpcRuntimeException("Can't access copy " + propertyName, e.getCause()); 113 | } catch (IllegalAccessException e) { 114 | throw new ChildRpcRuntimeException("Can't access copy " + propertyName, e); 115 | } 116 | if (val != null) { // 值不为空,放入缓存 117 | map.put(prefix + propertyName, val); 118 | } 119 | } 120 | } 121 | Field[] fields = bean.getClass().getFields(); 122 | for (Field field : fields) { 123 | String fieldName = field.getName(); 124 | if (map.containsKey(prefix + fieldName)) { 125 | continue; 126 | } 127 | int m = field.getModifiers(); 128 | if (!Modifier.isStatic(m) && !Modifier.isTransient(m)) { 129 | Object val = null; 130 | try { 131 | if (field.isAccessible()) { 132 | val = field.get(bean); 133 | } else { 134 | try { 135 | field.setAccessible(true); 136 | val = field.get(bean); 137 | } finally { 138 | field.setAccessible(false); 139 | } 140 | } 141 | } catch (IllegalAccessException e) { 142 | // LOGGER.warn("Can't access field" + fieldName + "when copy value to context", e); 143 | } 144 | if (val != null) { 145 | map.put(prefix + fieldName, val); 146 | } 147 | } 148 | } 149 | } 150 | 151 | /** 152 | * 从一个对象复制相同字段到另一个对象,(只写有getter/setter方法都有的值) 153 | * 154 | * @param src 原始对象 155 | * @param dst 目标对象 156 | * @param ignoreFields 忽略的字段 157 | */ 158 | public static void copyProperties(Object src, Object dst, String... ignoreFields) { 159 | Class srcClazz = src.getClass(); 160 | Class distClazz = dst.getClass(); 161 | Method[] methods = distClazz.getMethods(); 162 | List ignoreFiledList = Arrays.asList(ignoreFields); 163 | for (Method dstMethod : methods) { // 遍历目标对象的方法 164 | if (Modifier.isStatic(dstMethod.getModifiers()) 165 | || !ReflectUtils.isBeanPropertyReadMethod(dstMethod)) { 166 | // 不是static方法, 是getter方法 167 | continue; 168 | } 169 | String propertyName = ReflectUtils.getPropertyNameFromBeanReadMethod(dstMethod); 170 | if (ignoreFiledList.contains(propertyName)) { 171 | // 忽略字段 172 | continue; 173 | } 174 | Class dstReturnType = dstMethod.getReturnType(); 175 | try { // 同时目标字段还需要有set方法 176 | Method dstSetterMethod = ReflectUtils.getPropertySetterMethod(distClazz, propertyName, dstReturnType); 177 | if (dstSetterMethod != null) { 178 | // 再检查原始对象方法 179 | Method srcGetterMethod = ReflectUtils.getPropertyGetterMethod(srcClazz, propertyName); 180 | // 原始字段有getter方法 181 | Class srcReturnType = srcGetterMethod.getReturnType(); 182 | if (srcReturnType.equals(dstReturnType)) { // 原始字段和目标字段返回类型一样 183 | Object val = srcGetterMethod.invoke(src); // 从原始对象读取值 184 | if (val != null) { 185 | dstSetterMethod.invoke(dst, val); // 设置到目标对象 186 | } 187 | } 188 | } 189 | } catch (Exception ignore) { 190 | // ignore 下一循环 191 | } 192 | } 193 | } 194 | 195 | /** 196 | * 检查一个类的一个对象和另一个对象哪些属性被修改了(只写有getter/setter方法都有的值) 197 | * 198 | * @param src 修改前对象 199 | * @param dst 修改后对象 200 | * @param ignoreFields 忽略的字段 201 | * @param 对象 202 | * @return 修改过的字段列表 203 | */ 204 | public static List getModifiedFields(T src, T dst, String... ignoreFields) { 205 | Class clazz = src.getClass(); 206 | Method[] methods = clazz.getMethods(); 207 | List ignoreFiledList = Arrays.asList(ignoreFields); 208 | List modifiedFields = new ArrayList(); 209 | for (Method getterMethod : methods) { // 遍历目标对象的方法 210 | if (Modifier.isStatic(getterMethod.getModifiers()) 211 | || !ReflectUtils.isBeanPropertyReadMethod(getterMethod)) { 212 | // 不是static方法, 是getter方法 213 | continue; 214 | } 215 | String propertyName = ReflectUtils.getPropertyNameFromBeanReadMethod(getterMethod); 216 | if (ignoreFiledList.contains(propertyName)) { 217 | // 忽略字段 218 | continue; 219 | } 220 | Class returnType = getterMethod.getReturnType(); 221 | try { // 同时目标字段还需要有set方法 222 | Method setterMethod = ReflectUtils.getPropertySetterMethod(clazz, propertyName, returnType); 223 | if (setterMethod != null) { 224 | Object srcVal = getterMethod.invoke(src); // 原始值 225 | Object dstVal = getterMethod.invoke(dst); // 修改后值 226 | if (srcVal == null) { // 左边为空 227 | if (dstVal != null) { 228 | modifiedFields.add(propertyName); 229 | } 230 | } else { 231 | if (dstVal == null) { // 右边为空 232 | modifiedFields.add(propertyName); 233 | } else { 234 | if (!srcVal.equals(dstVal)) { // 都不为空且不同 235 | modifiedFields.add(propertyName); 236 | } 237 | } 238 | } 239 | } 240 | } catch (Exception ignore) { 241 | // ignore 下一循环 242 | } 243 | } 244 | return modifiedFields; 245 | } 246 | } -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/ClassLoaderUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.util; 18 | 19 | /** 20 | * 缓存了一些ClassLoader的对应关系,让应用和服务可以找到对应的ClassLoader。 21 | * 22 | * @author GengZhang 23 | */ 24 | public class ClassLoaderUtils { 25 | 26 | /** 27 | * 得到当前ClassLoader,先找线程池的,找不到就找中间件所在的ClassLoader 28 | * 29 | * @return ClassLoader 30 | */ 31 | public static ClassLoader getCurrentClassLoader() { 32 | ClassLoader cl = Thread.currentThread().getContextClassLoader(); 33 | if (cl == null) { 34 | cl = ClassLoaderUtils.class.getClassLoader(); 35 | } 36 | return cl == null ? ClassLoader.getSystemClassLoader() : cl; 37 | } 38 | 39 | /** 40 | * 得到当前ClassLoader 41 | * 42 | * @param clazz 某个类 43 | * @return ClassLoader 44 | */ 45 | public static ClassLoader getClassLoader(Class clazz) { 46 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 47 | if (loader != null) { 48 | return loader; 49 | } 50 | if (clazz != null) { 51 | loader = clazz.getClassLoader(); 52 | if (loader != null) { 53 | return loader; 54 | } 55 | } 56 | return ClassLoader.getSystemClassLoader(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/CommonUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.util; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collection; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.concurrent.ConcurrentMap; 24 | 25 | /** 26 | * 一些通用方法 27 | * 28 | * @author GengZhang 29 | */ 30 | public class CommonUtils { 31 | 32 | /** 33 | * 将值放入ConcurrentMap,已经考虑第一次并发问题 34 | * 35 | * @param map ConcurrentMap 36 | * @param key 关键字 37 | * @param value 值 38 | * @param 关键字类型 39 | * @param 值类型 40 | * @return 旧值 41 | */ 42 | public static V putToConcurrentMap(ConcurrentMap map, K key, V value) { 43 | V old = map.putIfAbsent(key, value); 44 | return old != null ? old : value; 45 | } 46 | 47 | /** 48 | * 不为空,且为“true” 49 | * 50 | * @param b Boolean对象 51 | * @return 不为空,且为true 52 | */ 53 | public static boolean isTrue(String b) { 54 | return b != null && ClassTypeUtils.StringUtils.TRUE.equalsIgnoreCase(b); 55 | } 56 | 57 | /** 58 | * 不为空,且为true 59 | * 60 | * @param b Boolean对象 61 | * @return 不为空,且为true 62 | */ 63 | public static boolean isTrue(Boolean b) { 64 | return b != null && b; 65 | } 66 | 67 | /** 68 | * 不为空,且为false 69 | * 70 | * @param b Boolean对象 71 | * @return 不为空,且为true 72 | */ 73 | public static boolean isFalse(Boolean b) { 74 | return b != null && !b; 75 | } 76 | 77 | /** 78 | * 不为空,且为“false” 79 | * 80 | * @param b Boolean对象 81 | * @return 不为空,且为true 82 | */ 83 | public static boolean isFalse(String b) { 84 | return b != null && ClassTypeUtils.StringUtils.FALSE.equalsIgnoreCase(b); 85 | } 86 | 87 | /** 88 | * 判断一个集合是否为空 89 | * 90 | * @param collection 集合 91 | * @return 是否为空 92 | */ 93 | public static boolean isEmpty(Collection collection) { 94 | return collection == null || collection.isEmpty(); 95 | } 96 | 97 | /** 98 | * 判断一个集合是否为非空 99 | * 100 | * @param collection 集合 101 | * @return 是否为非空 102 | */ 103 | public static boolean isNotEmpty(Collection collection) { 104 | return collection != null && !collection.isEmpty(); 105 | } 106 | 107 | /** 108 | * 判断一个Map是否为空 109 | * 110 | * @param map Map 111 | * @return 是否为空 112 | */ 113 | public static boolean isEmpty(Map map) { 114 | return map == null || map.isEmpty(); 115 | } 116 | 117 | /** 118 | * 判断一个Map是否为非空 119 | * 120 | * @param map Map 121 | * @return 是否为非空 122 | */ 123 | public static boolean isNotEmpty(Map map) { 124 | return map != null && !map.isEmpty(); 125 | } 126 | 127 | /** 128 | * 判断一个Array是否为空 129 | * 130 | * @param array 数组 131 | * @return 是否为空 132 | */ 133 | public static boolean isEmpty(Object[] array) { 134 | return array == null || array.length == 0; 135 | } 136 | 137 | /** 138 | * 判断一个Array是否为非空 139 | * 140 | * @param array 数组 141 | * @return 是否为非空 142 | */ 143 | public static boolean isNotEmpty(Object[] array) { 144 | return array != null && array.length > 0; 145 | } 146 | 147 | /** 148 | * 取数值 149 | * 150 | * @param num 数字 151 | * @param defaultInt 默认值 152 | * @param 数字的子类 153 | * @return int 154 | */ 155 | public static T parseNum(T num, T defaultInt) { 156 | return num == null ? defaultInt : num; 157 | } 158 | 159 | /** 160 | * 字符串转数值 161 | * 162 | * @param num 数字 163 | * @param defaultInt 默认值 164 | * @return int 165 | */ 166 | public static int parseInt(String num, int defaultInt) { 167 | if (num == null) { 168 | return defaultInt; 169 | } else { 170 | try { 171 | return Integer.parseInt(num); 172 | } catch (Exception e) { 173 | return defaultInt; 174 | } 175 | } 176 | } 177 | 178 | /** 179 | * String Long turn number. 180 | * 181 | * @param num The number of strings. 182 | * @param defaultLong The default value 183 | * @return long 184 | */ 185 | public static long parseLong(String num, long defaultLong) { 186 | if (num == null) { 187 | return defaultLong; 188 | } else { 189 | try { 190 | return Long.parseLong(num); 191 | } catch (Exception e) { 192 | return defaultLong; 193 | } 194 | } 195 | } 196 | 197 | /** 198 | * 字符串转布尔 199 | * 200 | * @param bool 数字 201 | * @param defaultInt 默认值 202 | * @return int 203 | */ 204 | public static boolean parseBoolean(String bool, boolean defaultInt) { 205 | if (bool == null) { 206 | return defaultInt; 207 | } else { 208 | return Boolean.parseBoolean(bool); 209 | } 210 | } 211 | 212 | /** 213 | * 字符串转值 214 | * 215 | * @param nums 多个数字 216 | * @param sperator 分隔符 217 | * @return int[] 218 | */ 219 | public static int[] parseInts(String nums, String sperator) { 220 | String[] ss = ClassTypeUtils.StringUtils.split(nums, sperator); 221 | int[] ints = new int[ss.length]; 222 | for (int i = 0; i < ss.length; i++) { 223 | ints[i] = Integer.parseInt(ss[i]); 224 | } 225 | return ints; 226 | } 227 | 228 | /** 229 | * 比较list元素是否一致,忽略顺序 230 | * 231 | * @param left 左边List 232 | * @param right 右边List 233 | * @param 元素类型 234 | * @return 是否一致 235 | */ 236 | public static boolean listEquals(List left, List right) { 237 | if (left == null) { 238 | return right == null; 239 | } else { 240 | if (right == null) { 241 | return false; 242 | } 243 | if (left.size() != right.size()) { 244 | return false; 245 | } 246 | 247 | List ltmp = new ArrayList(left); 248 | List rtmp = new ArrayList(right); 249 | for (T t : ltmp) { 250 | rtmp.remove(t); 251 | } 252 | return rtmp.isEmpty(); 253 | } 254 | } 255 | 256 | /** 257 | * 连接集合类为字符串 258 | * 259 | * @param collection 集合 260 | * @param separator 分隔符 261 | * @return 分隔符连接的字符串 262 | */ 263 | public static String join(Collection collection, String separator) { 264 | if (isEmpty(collection)) { 265 | return ClassTypeUtils.StringUtils.EMPTY; 266 | } 267 | StringBuilder sb = new StringBuilder(); 268 | for (Object object : collection) { 269 | if (object != null) { 270 | String string = ClassTypeUtils.StringUtils.toString(object); 271 | if (string != null) { 272 | sb.append(string).append(separator); 273 | } 274 | } 275 | } 276 | return sb.length() > 0 ? sb.substring(0, sb.length() - separator.length()) : ClassTypeUtils.StringUtils.EMPTY; 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/ExceptionUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.util; 18 | 19 | import cn.whforever.core.exception.ChildRpcRuntimeException; 20 | 21 | /** 22 | * 异常工具类 23 | * 24 | * @author GengZhang 25 | */ 26 | public final class ExceptionUtils { 27 | 28 | public static ChildRpcRuntimeException buildRuntime(String configKey, String configValue) { 29 | String msg = "The value of config " + configKey + " [" + configValue + "] is illegal, please check it"; 30 | return new ChildRpcRuntimeException(msg); 31 | } 32 | 33 | public static ChildRpcRuntimeException buildRuntime(String configKey, String configValue, String message) { 34 | String msg = "The value of config " + configKey + " [" + configValue + "] is illegal, " + message; 35 | return new ChildRpcRuntimeException(msg); 36 | } 37 | 38 | public static boolean isServerException(ChildRpcRuntimeException exception) { 39 | // int errorType = exception.getErrorType(); 40 | // return errorType >= 100 && errorType < 200; 41 | return false; 42 | } 43 | 44 | public static boolean isClientException(ChildRpcRuntimeException exception) { 45 | // int errorType = exception.getErrorType(); 46 | // return errorType >= 200 && errorType < 300; 47 | return false; 48 | } 49 | 50 | /** 51 | * 返回堆栈信息(e.printStackTrace()的内容) 52 | * 53 | * @param e Throwable 54 | * @return 异常堆栈信息 55 | */ 56 | public static String toString(Throwable e) { 57 | StackTraceElement[] traces = e.getStackTrace(); 58 | StringBuilder sb = new StringBuilder(1024); 59 | sb.append(e.toString()).append("\n"); 60 | if (traces != null) { 61 | for (StackTraceElement trace : traces) { 62 | sb.append("\tat ").append(trace).append("\n"); 63 | } 64 | } 65 | return sb.toString(); 66 | } 67 | 68 | /** 69 | * 返回消息+简短堆栈信息(e.printStackTrace()的内容) 70 | * 71 | * @param e Throwable 72 | * @param stackLevel 堆栈层级 73 | * @return 异常堆栈信息 74 | */ 75 | public static String toShortString(Throwable e, int stackLevel) { 76 | StackTraceElement[] traces = e.getStackTrace(); 77 | StringBuilder sb = new StringBuilder(1024); 78 | sb.append(e.toString()).append("\t"); 79 | if (traces != null) { 80 | for (int i = 0; i < traces.length; i++) { 81 | if (i < stackLevel) { 82 | sb.append("\tat ").append(traces[i]).append("\t"); 83 | } else { 84 | break; 85 | } 86 | } 87 | } 88 | return sb.toString(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/IOUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.util; 18 | 19 | import java.io.Closeable; 20 | import java.net.ServerSocket; 21 | import java.net.Socket; 22 | 23 | /** 24 | * IO工具类 25 | * 26 | * @author GengZhang 27 | */ 28 | public class IOUtils { 29 | 30 | /** 31 | * 静默关闭 32 | * 33 | * @param closeable 可关闭的 34 | */ 35 | public static void closeQuietly(Closeable closeable) { 36 | if (closeable != null) { 37 | try { 38 | closeable.close(); 39 | } catch (Exception ignore) { 40 | // NOPMD 41 | } 42 | } 43 | } 44 | 45 | /** 46 | * 静默关闭 for jdk6 47 | * 48 | * @param closeable 可关闭的 49 | */ 50 | public static void closeQuietly(ServerSocket closeable) { 51 | if (closeable != null) { 52 | try { 53 | closeable.close(); 54 | } catch (Exception ignore) { 55 | // NOPMD 56 | } 57 | } 58 | } 59 | 60 | /** 61 | * 静默关闭 for jdk6 62 | * 63 | * @param closeable 可关闭的 64 | */ 65 | public static void closeQuietly(Socket closeable) { 66 | if (closeable != null) { 67 | try { 68 | closeable.close(); 69 | } catch (Exception ignore) { 70 | // NOPMD 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/ReflectUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.util; 18 | 19 | 20 | import cn.whforever.core.exception.ChildRpcRuntimeException; 21 | 22 | import java.lang.reflect.Field; 23 | import java.lang.reflect.Method; 24 | import java.lang.reflect.Modifier; 25 | import java.net.URL; 26 | import java.security.CodeSource; 27 | import java.security.ProtectionDomain; 28 | import java.util.Date; 29 | 30 | /** 31 | * 反射工具类 32 | * 33 | * @author GengZhang 34 | */ 35 | public class ReflectUtils { 36 | 37 | /** 38 | * 是否默认类型,基本类型+string+date 39 | * 40 | * @param clazz the cls 41 | * @return the boolean 42 | */ 43 | public static boolean isPrimitives(Class clazz) { 44 | if (clazz.isArray()) { // 数组,检查数组类型 45 | return isPrimitiveType(clazz.getComponentType()); 46 | } 47 | return isPrimitiveType(clazz); 48 | } 49 | 50 | private static boolean isPrimitiveType(Class clazz) { 51 | return clazz.isPrimitive() // 基本类型 52 | // 基本类型的对象 53 | || 54 | Boolean.class == clazz 55 | || Character.class == clazz 56 | || Number.class.isAssignableFrom(clazz) 57 | // string 或者 date 58 | || String.class == clazz 59 | || Date.class.isAssignableFrom(clazz); 60 | } 61 | 62 | /** 63 | * 得到类所在地址,可以是文件,也可以是jar包 64 | * 65 | * @param cls the cls 66 | * @return the code base 67 | */ 68 | public static String getCodeBase(Class cls) { 69 | 70 | if (cls == null) { 71 | return null; 72 | } 73 | ProtectionDomain domain = cls.getProtectionDomain(); 74 | if (domain == null) { 75 | return null; 76 | } 77 | CodeSource source = domain.getCodeSource(); 78 | if (source == null) { 79 | return null; 80 | } 81 | URL location = source.getLocation(); 82 | if (location == null) { 83 | return null; 84 | } 85 | return location.getFile(); 86 | } 87 | 88 | /** 89 | * 加载Method方法 90 | * 91 | * @param clazzName 类名 92 | * @param methodName 方法名 93 | * @param argsType 参数列表 94 | * @return Method对象 95 | */ 96 | public static Method getMethod(String clazzName, String methodName, String[] argsType) { 97 | Class clazz = ClassUtils.forName(clazzName); 98 | Class[] classes = ClassTypeUtils.getClasses(argsType); 99 | return getMethod(clazz, methodName, classes); 100 | } 101 | 102 | /** 103 | * 加载Method方法 104 | * 105 | * @param clazz 类 106 | * @param methodName 方法名 107 | * @param argsType 参数列表 108 | * @return Method对象 109 | * @since 5.4.0 110 | */ 111 | public static Method getMethod(Class clazz, String methodName, Class... argsType) { 112 | try { 113 | return clazz.getMethod(methodName, argsType); 114 | } catch (NoSuchMethodException e) { 115 | throw new ChildRpcRuntimeException(e.getMessage(), e); 116 | } 117 | } 118 | 119 | /** 120 | * 得到set方法 121 | * 122 | * @param clazz 类 123 | * @param property 属性 124 | * @param propertyClazz 属性 125 | * @return Method 方法对象 126 | */ 127 | public static Method getPropertySetterMethod(Class clazz, String property, Class propertyClazz) { 128 | String methodName = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); 129 | try { 130 | return clazz.getMethod(methodName, propertyClazz); 131 | } catch (NoSuchMethodException e) { 132 | throw new ChildRpcRuntimeException("No setter method for " + clazz.getName() + "#" + property, e); 133 | } 134 | } 135 | 136 | /** 137 | * 得到get/is方法 138 | * 139 | * @param clazz 类 140 | * @param property 属性 141 | * @return Method 方法对象 142 | */ 143 | public static Method getPropertyGetterMethod(Class clazz, String property) { 144 | String methodName = "get" + property.substring(0, 1).toUpperCase() + property.substring(1); 145 | Method method; 146 | try { 147 | method = clazz.getMethod(methodName); 148 | } catch (NoSuchMethodException e) { 149 | try { 150 | methodName = "is" + property.substring(0, 1).toUpperCase() + property.substring(1); 151 | method = clazz.getMethod(methodName); 152 | } catch (NoSuchMethodException e1) { 153 | throw new ChildRpcRuntimeException("No getter method for " + clazz.getName() + "#" + property, e); 154 | } 155 | } 156 | return method; 157 | } 158 | 159 | protected static boolean isBeanPropertyReadMethod(Method method) { 160 | return method != null 161 | && Modifier.isPublic(method.getModifiers()) 162 | && !Modifier.isStatic(method.getModifiers()) 163 | && method.getReturnType() != void.class 164 | && method.getDeclaringClass() != Object.class 165 | && method.getParameterTypes().length == 0 166 | && (method.getName().startsWith("get") || method.getName().startsWith("is")) 167 | // 排除就叫get和is的方法 168 | && (!"get".equals(method.getName()) && !"is".equals(method.getName())); 169 | } 170 | 171 | protected static String getPropertyNameFromBeanReadMethod(Method method) { 172 | if (isBeanPropertyReadMethod(method)) { 173 | if (method.getName().startsWith("get")) { 174 | return method.getName().substring(3, 4).toLowerCase() 175 | + method.getName().substring(4); 176 | } 177 | if (method.getName().startsWith("is")) { 178 | return method.getName().substring(2, 3).toLowerCase() 179 | + method.getName().substring(3); 180 | } 181 | } 182 | return null; 183 | } 184 | 185 | protected static boolean isBeanPropertyWriteMethod(Method method) { 186 | return method != null 187 | && Modifier.isPublic(method.getModifiers()) 188 | && !Modifier.isStatic(method.getModifiers()) 189 | && method.getDeclaringClass() != Object.class 190 | && method.getParameterTypes().length == 1 191 | && method.getName().startsWith("set") 192 | // 排除就叫set的方法 193 | && !"set".equals(method.getName()); 194 | } 195 | 196 | protected static boolean isPublicInstanceField(Field field) { 197 | return Modifier.isPublic(field.getModifiers()) 198 | && !Modifier.isStatic(field.getModifiers()) 199 | && !Modifier.isFinal(field.getModifiers()) 200 | && !field.isSynthetic(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/RegistryUtils.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.core.util; 2 | 3 | import cn.whforever.core.config.ClientConfig; 4 | import cn.whforever.core.config.Config; 5 | import cn.whforever.core.config.ServerConfig; 6 | import cn.whforever.core.rpc.RpcConstants; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class RegistryUtils { 12 | 13 | /** 14 | * Convert provider to url. 15 | * 16 | * @return the url list 17 | */ 18 | public static String convertProviderToUrls(ServerConfig serverConfig) { 19 | @SuppressWarnings("unchecked") 20 | String url; 21 | StringBuilder sb = new StringBuilder(); 22 | String host = serverConfig.getHost(); 23 | if (host == null) { 24 | host = serverConfig.getHost(); 25 | if (NetUtils.isLocalHost(host) || NetUtils.isAnyHost(host)) { 26 | host = SystemInfo.getLocalHost(); 27 | } 28 | } 29 | 30 | Map metaData = convertProviderToMap(serverConfig); 31 | //noinspection unchecked 32 | sb.append(host).append(":") 33 | .append(serverConfig.getPort()); 34 | url = sb.toString(); 35 | return url; 36 | } 37 | 38 | // 39 | // public static List matchProviderInfos(ClientConfig consumerConfig, List providerInfos) { 40 | // String protocol = consumerConfig.getProtocol(); 41 | // List result = new ArrayList(); 42 | // for (ProviderInfo providerInfo : providerInfos) { 43 | // if (providerInfo.getProtocolType().equalsIgnoreCase(protocol) 44 | // && StringUtils.equals(consumerConfig.getUniqueId(), 45 | // providerInfo.getAttr(ProviderInfoAttrs.ATTR_UNIQUEID))) { 46 | // result.add(providerInfo); 47 | // } 48 | // } 49 | // return result; 50 | // } 51 | // 52 | public static Map convertProviderToMap(ServerConfig providerConfig) { 53 | Map metaData = new HashMap<>(16); 54 | metaData.put(RpcConstants.CONFIG_KEY_INTERFACE, providerConfig.getInterfaceId()); 55 | metaData.put(RpcConstants.CONFIG_KEY_PROTOCOL, providerConfig.getProtocol()); 56 | return metaData; 57 | } 58 | 59 | /** 60 | * Convert consumer to url. 61 | * 62 | * @param consumerConfig the ConsumerConfig 63 | * @return the url list 64 | */ 65 | public static String convertConsumerToUrl(ClientConfig consumerConfig) { 66 | String host = SystemInfo.getLocalHost(); 67 | return host; 68 | } 69 | 70 | /** 71 | * Gets key pairs. 72 | * 73 | * @param key the key 74 | * @param value the value 75 | * @return the key pairs 76 | */ 77 | public static String getKeyPairs(String key, Object value) { 78 | if (value != null) { 79 | return "&" + key + "=" + value.toString(); 80 | } else { 81 | return ""; 82 | } 83 | } 84 | 85 | /** 86 | * 转换 map to url pair 87 | * 88 | * @param map 属性 89 | */ 90 | private static String convertMap2Pair(Map map) { 91 | 92 | if (CommonUtils.isEmpty(map)) { 93 | return StringUtils.EMPTY; 94 | } 95 | 96 | StringBuilder sb = new StringBuilder(128); 97 | for (Map.Entry entry : map.entrySet()) { 98 | sb.append(getKeyPairs(entry.getKey(), entry.getValue())); 99 | } 100 | 101 | return sb.toString(); 102 | } 103 | 104 | public static String buildProviderPath(String rootPath, Config config) { 105 | return rootPath + "child-rpc/" + config.getInterfaceId() + "/providers"; 106 | } 107 | 108 | public static String buildConsumerPath(String rootPath, Config config) { 109 | return rootPath + "child-rpc/" + config.getInterfaceId() + "/consumers"; 110 | } 111 | 112 | // 113 | // /** 114 | // * Read the warmUp weight parameter, 115 | // * decide whether to switch the state to the preheating period, 116 | // * and set the corresponding parameters during the preheating period. 117 | // * 118 | // * @param providerInfo the provider info 119 | // */ 120 | // public static void processWarmUpWeight(ProviderInfo providerInfo) { 121 | // 122 | // String warmupTimeStr = providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_WARMUP_TIME); 123 | // String warmupWeightStr = providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_WARMUP_WEIGHT); 124 | // String startTimeStr = providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_START_TIME); 125 | // 126 | // if (StringUtils.isNotBlank(warmupTimeStr) && StringUtils.isNotBlank(warmupWeightStr) && 127 | // StringUtils.isNotBlank(startTimeStr)) { 128 | // 129 | // long warmupTime = CommonUtils.parseLong(warmupTimeStr, 0); 130 | // int warmupWeight = CommonUtils.parseInt(warmupWeightStr, 131 | // Integer.parseInt(providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_WEIGHT))); 132 | // long startTime = CommonUtils.parseLong(startTimeStr, 0); 133 | // long warmupEndTime = startTime + warmupTime; 134 | // 135 | // // set for dynamic 136 | // providerInfo.setDynamicAttr(ProviderInfoAttrs.ATTR_WARMUP_WEIGHT, warmupWeight); 137 | // providerInfo.setDynamicAttr(ProviderInfoAttrs.ATTR_WARM_UP_END_TIME, warmupEndTime); 138 | // providerInfo.setStatus(ProviderStatus.WARMING_UP); 139 | // } 140 | // 141 | // // remove from static 142 | // providerInfo.getStaticAttrs().remove(ProviderInfoAttrs.ATTR_WARMUP_TIME); 143 | // providerInfo.getStaticAttrs().remove(ProviderInfoAttrs.ATTR_WARMUP_WEIGHT); 144 | // 145 | // } 146 | // 147 | // /** 148 | // * Init or add list. 149 | // * 150 | // * @param 151 | // * the key parameter 152 | // * @param 153 | // * the value parameter 154 | // * @param orginMap 155 | // * the orgin map 156 | // * @param key 157 | // * the key 158 | // * @param needAdd 159 | // * the need add 160 | // */ 161 | // public static void initOrAddList(Map> orginMap, K key, V needAdd) { 162 | // List listeners = orginMap.get(key); 163 | // if (listeners == null) { 164 | // listeners = new CopyOnWriteArrayList(); 165 | // listeners.add(needAdd); 166 | // orginMap.put(key, listeners); 167 | // } else { 168 | // listeners.add(needAdd); 169 | // } 170 | // } 171 | // 172 | // public static String convertInstanceToUrl(String host, int port, Map metaData) { 173 | // if (metaData == null) { 174 | // metaData = new HashMap(); 175 | // } 176 | // String uri = ""; 177 | // String protocol = metaData.get(RpcConstants.CONFIG_KEY_PROTOCOL); 178 | // if (StringUtils.isNotEmpty(protocol)) { 179 | // uri = protocol + "://"; 180 | // } 181 | // uri += host + ":" + port; 182 | // 183 | // StringBuilder sb = new StringBuilder(); 184 | // for (Map.Entry entry : metaData.entrySet()) { 185 | // sb.append("&").append(entry.getKey()).append("=").append(entry.getValue()); 186 | // } 187 | // if (sb.length() > 0) { 188 | // uri += sb.replace(0, 1, "?").toString(); 189 | // } 190 | // return uri; 191 | // } 192 | // 193 | // public static String getServerHost(ServerConfig server) { 194 | // String host = server.getVirtualHost(); 195 | // if (host == null) { 196 | // host = server.getHost(); 197 | // if (NetUtils.isLocalHost(host) || NetUtils.isAnyHost(host)) { 198 | // host = SystemInfo.getLocalHost(); 199 | // } 200 | // } 201 | // return host; 202 | // } 203 | // 204 | // public static String buildUniqueName(AbstractInterfaceConfig config, String protocol) { 205 | // if (RpcConstants.PROTOCOL_TYPE_BOLT.equals(protocol) || RpcConstants.PROTOCOL_TYPE_TR.equals(protocol)) { 206 | // return ConfigUniqueNameGenerator.getUniqueName(config) + "@DEFAULT"; 207 | // } else { 208 | // return ConfigUniqueNameGenerator.getUniqueName(config) + "@" + protocol; 209 | // } 210 | // } 211 | } 212 | -------------------------------------------------------------------------------- /child-rpc-core/src/main/java/cn/whforever/core/util/SystemInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.whforever.core.util; 18 | 19 | /** 20 | * 系统相关信息 21 | * 22 | * @author GengZhang 23 | */ 24 | public class SystemInfo { 25 | 26 | /** 27 | * 缓存了本机地址 28 | */ 29 | private static String LOCALHOST; 30 | /** 31 | * 缓存了物理机地址 32 | */ 33 | private static String HOSTMACHINE; 34 | /** 35 | * 是否Windows系统 36 | */ 37 | private static boolean IS_WINDOWS; 38 | /** 39 | * 是否Linux系统 40 | */ 41 | private static boolean IS_LINUX; 42 | /** 43 | * 是否MAC系统 44 | */ 45 | private static boolean IS_MAC; 46 | 47 | static { 48 | boolean[] os = parseOSName(); 49 | IS_WINDOWS = os[0]; 50 | IS_LINUX = os[1]; 51 | IS_MAC = os[2]; 52 | 53 | LOCALHOST = NetUtils.getLocalIpv4(); 54 | HOSTMACHINE = parseHostMachine(); 55 | } 56 | 57 | /** 58 | * 解析物理机地址 59 | * 60 | * @return 物理机地址 61 | */ 62 | static boolean[] parseOSName() { 63 | boolean[] result = new boolean[]{false, false, false}; 64 | String osName = System.getProperty("os.name").toLowerCase(); 65 | if (osName.contains("windows")) { 66 | result[0] = true; 67 | } else if (osName.contains("linux")) { 68 | result[1] = true; 69 | } else if (osName.contains("mac")) { 70 | result[2] = true; 71 | } 72 | return result; 73 | } 74 | 75 | /** 76 | * 是否Windows系统 77 | */ 78 | public static boolean isWindows() { 79 | return IS_WINDOWS; 80 | } 81 | 82 | /** 83 | * 是否Linux系统 84 | */ 85 | public static Boolean isLinux() { 86 | return IS_LINUX; 87 | } 88 | 89 | /** 90 | * 是否Mac系统 91 | */ 92 | public static boolean isMac() { 93 | return IS_MAC; 94 | } 95 | 96 | /** 97 | * 得到CPU核心数(dock特殊处理) 98 | * 99 | * @return 可用的cpu内核数 100 | */ 101 | public static int getCpuCores() { 102 | return Runtime.getRuntime().availableProcessors(); 103 | } 104 | 105 | /** 106 | * 得到缓存的本机地址 107 | * 108 | * @return 本机地址 109 | */ 110 | public static String getLocalHost() { 111 | return LOCALHOST; 112 | } 113 | 114 | /** 115 | * 设置本机地址到缓存(一般是多网卡由外部选择后设置) 116 | * 117 | * @param localhost 本机地址 118 | */ 119 | public static void setLocalHost(String localhost) { 120 | LOCALHOST = localhost; 121 | } 122 | 123 | /** 124 | * 解析物理机地址 125 | * 126 | * @return 物理机地址 127 | */ 128 | static String parseHostMachine() { 129 | String hostMachine = System.getProperty("host_machine"); 130 | return StringUtils.isNotEmpty(hostMachine) ? hostMachine : null; 131 | } 132 | 133 | /** 134 | * 物理机地址 135 | * 136 | * @return 物理机地址 137 | */ 138 | public static String getHostMachine() { 139 | return HOSTMACHINE; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /child-rpc-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | child-rpc 7 | cn.whforever 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | child-rpc-example 13 | 14 | 15 | 16 | org.apache.maven.plugins 17 | maven-compiler-plugin 18 | 19 | 8 20 | 8 21 | 22 | 23 | 24 | 25 | 26 | 27 | cn.whforever 28 | child-rpc-core 29 | 1.0-SNAPSHOT 30 | test 31 | 32 | 33 | junit 34 | junit 35 | test 36 | 37 | 38 | 39 | org.slf4j 40 | slf4j-nop 41 | 1.7.2 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/local/ClientTest.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.local; 2 | 3 | import cn.whforever.core.config.ClientConfig; 4 | import cn.whforever.core.protocol.netty.client.NettyClientAbstract; 5 | import cn.whforever.core.proxy.ClientProxy; 6 | import cn.whforever.core.rpc.RpcConstants; 7 | import cn.whforever.core.serialize.Serializer; 8 | import cn.whforever.example.service.HelloService; 9 | 10 | /** 11 | * @author wuhf 12 | * @Date 2018/9/1 18:31 13 | **/ 14 | public class ClientTest { 15 | 16 | public static void main(String[] args) { 17 | ClientConfig clientConfig = new ClientConfig(); 18 | clientConfig.setHost("127.0.0.1") 19 | .setPort(5201) 20 | .setProtocol(RpcConstants.DIRECT_CONN) 21 | .setTimeoutMillis(100000) 22 | .setSerializer(Serializer.SerializeEnum.HESSIAN.serializer) 23 | .setRegister(false) 24 | .setSubscribe(false); 25 | ClientProxy clientProxy = new ClientProxy(clientConfig, new NettyClientAbstract(), HelloService.class); 26 | for (int i = 0; i < 10; i++) { 27 | HelloService helloService = clientProxy.refer(); 28 | System.out.println(helloService.sayHi()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/local/ServerTest.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.local; 2 | 3 | import cn.whforever.core.config.ServerConfig; 4 | import cn.whforever.core.protocol.netty.server.NettyServerAbstract; 5 | import cn.whforever.core.proxy.ServerProxy; 6 | import cn.whforever.core.serialize.Serializer; 7 | import cn.whforever.example.service.HelloService; 8 | import cn.whforever.example.service.impl.HelloServiceImpl; 9 | 10 | /** 11 | * @author wuhf 12 | * @Date 2018/9/1 18:30 13 | **/ 14 | public class ServerTest { 15 | 16 | public static void main(String[] args) { 17 | ServerConfig serverConfig = new ServerConfig(); 18 | serverConfig.setSerializer(Serializer.SerializeEnum.HESSIAN.serializer) 19 | .setPort(5201) 20 | .setRef(HelloServiceImpl.class.getName()) 21 | .setRegister(false) 22 | .setInterfaceId(HelloService.class.getName()); 23 | ServerProxy serverProxy = new ServerProxy(new NettyServerAbstract(), serverConfig); 24 | try { 25 | serverProxy.export(); 26 | while (true) { 27 | 28 | } 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/log/SLF4JLoggerImplTest.java: -------------------------------------------------------------------------------- 1 | ///* 2 | // * Licensed to the Apache Software Foundation (ASF) under one or more 3 | // * contributor license agreements. See the NOTICE file distributed with 4 | // * this work for additional information regarding copyright ownership. 5 | // * The ASF licenses this file to You under the Apache License, Version 2.0 6 | // * (the "License"); you may not use this file except in compliance with 7 | // * the License. You may obtain a copy of the License at 8 | // * 9 | // * http://www.apache.org/licenses/LICENSE-2.0 10 | // * 11 | // * Unless required by applicable law or agreed to in writing, software 12 | // * distributed under the License is distributed on an "AS IS" BASIS, 13 | // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // * See the License for the specific language governing permissions and 15 | // * limitations under the License. 16 | // */ 17 | //package cn.whforever.example.log; 18 | // 19 | //import cn.whforever.core.log.Logger; 20 | //import cn.whforever.core.log.SLF4JLoggerImpl; 21 | //import org.junit.Assert; 22 | //import org.junit.Test; 23 | // 24 | ///** 25 | // * 26 | // * 27 | // * @author GengZhang 28 | // */ 29 | //public class SLF4JLoggerImplTest { 30 | // 31 | // @Test 32 | // public void getName() throws Exception { 33 | // Logger loggerFromClass = new SLF4JLoggerImpl(SLF4JLoggerImplTest.class); 34 | // Logger logger = new SLF4JLoggerImpl(SLF4JLoggerImplTest.class.getCanonicalName()); 35 | // Assert.assertEquals(loggerFromClass.getName(), logger.getName()); 36 | // String appName = "app"; 37 | // if (logger.isDebugEnabled()) { 38 | // logger.debug("debug"); 39 | // logger.debug("debug {}", "1"); 40 | // logger.debug("debug {} {} {}", "1", "2", "3"); 41 | // logger.debug("debug", new RuntimeException("runtime")); 42 | // } 43 | // if (logger.isDebugEnabled(appName)) { 44 | // logger.debugWithApp(appName, "debug"); 45 | // logger.debugWithApp(appName, "debug {}", "1"); 46 | // logger.debugWithApp(appName, "debug {} {} {}", "1", "2", "3"); 47 | // logger.debugWithApp(appName, "debug", new RuntimeException("runtime")); 48 | // } 49 | // 50 | // if (logger.isInfoEnabled()) { 51 | // logger.info("info"); 52 | // logger.info("info {}", "1"); 53 | // logger.info("info {} {} {}", "1", "2", "3"); 54 | // logger.info("info", new RuntimeException("runtime")); 55 | // } 56 | // if (logger.isInfoEnabled(appName)) { 57 | // logger.infoWithApp(appName, "info"); 58 | // logger.infoWithApp(appName, "info {}", "1"); 59 | // logger.infoWithApp(appName, "info {} {} {}", "1", "2", "3"); 60 | // logger.infoWithApp(appName, "info", new RuntimeException("runtime")); 61 | // } 62 | // 63 | // if (logger.isWarnEnabled()) { 64 | // logger.warn("warn"); 65 | // logger.warn("warn {}", "1"); 66 | // logger.warn("warn {} {} {}", "1", "2", "3"); 67 | // logger.warn("warn", new RuntimeException("runtime")); 68 | // } 69 | // if (logger.isWarnEnabled(appName)) { 70 | // logger.warn(appName, "warn"); 71 | // logger.warnWithApp(appName, "warn {}", "1"); 72 | // logger.warnWithApp(appName, "warn {} {} {}", "1", "2", "3"); 73 | // logger.warnWithApp(appName, "warn", new RuntimeException("runtime")); 74 | // } 75 | // 76 | // if (logger.isErrorEnabled()) { 77 | // logger.error("error"); 78 | // logger.error("error {}", "1"); 79 | // logger.error("error {} {} {}", "1", "2", "3"); 80 | // logger.error("error", new RuntimeException("runtime")); 81 | // } 82 | // if (logger.isErrorEnabled(appName)) { 83 | // logger.errorWithApp(appName, "error"); 84 | // logger.errorWithApp(appName, "error {}", "1"); 85 | // logger.errorWithApp(appName, "error {} {} {}", "1", "2", "3"); 86 | // logger.errorWithApp(appName, "error", new RuntimeException("runtime")); 87 | // } 88 | // } 89 | // 90 | //} -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/service/HelloService.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.service; 2 | 3 | /** 4 | * @author wuhf 5 | * @Date 2018/9/1 18:41 6 | **/ 7 | public interface HelloService { 8 | String sayHi(); 9 | } 10 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/service/impl/HelloServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.service.impl; 2 | 3 | import cn.whforever.example.service.HelloService; 4 | 5 | /** 6 | * @author wuhf 7 | * @Date 2018/9/1 18:41 8 | **/ 9 | public class HelloServiceImpl implements HelloService { 10 | @Override 11 | public String sayHi() { 12 | System.out.println("hello data from client"); 13 | return "this is server"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/zookeeper/ZkRegistryTest.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.zookeeper; 2 | 3 | import cn.whforever.core.config.RegistryConfig; 4 | import cn.whforever.core.registry.zk.ZookeeperRegistry; 5 | import cn.whforever.core.rpc.RpcConstants; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * @author wuhaifei 2019-08-01 11 | */ 12 | public class ZkRegistryTest { 13 | 14 | @Test 15 | public void initTest() { 16 | RegistryConfig registryConfig = new RegistryConfig() 17 | .setProtocol(RpcConstants.ZOOKEEPER) 18 | .setAddress("127.0.0.1:2181"); 19 | 20 | ZookeeperRegistry registry = new ZookeeperRegistry(registryConfig); 21 | registry.init(); 22 | boolean started = registry.start(); 23 | Assert.assertEquals(true, started); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/zookeeper/ZookeeperClientMainTest.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.zookeeper; 2 | 3 | import cn.whforever.core.config.ClientConfig; 4 | import cn.whforever.core.config.RegistryConfig; 5 | import cn.whforever.core.protocol.netty.client.NettyClientAbstract; 6 | import cn.whforever.core.proxy.ClientProxy; 7 | import cn.whforever.core.rpc.RpcConstants; 8 | import cn.whforever.core.serialize.Serializer; 9 | import cn.whforever.example.service.HelloService; 10 | 11 | /** 12 | * @author wuhaifei 2019-08-02 13 | */ 14 | public class ZookeeperClientMainTest { 15 | public static void main(String[] args) { 16 | ClientConfig clientConfig = new ClientConfig(); 17 | clientConfig.setProtocol(RpcConstants.ZOOKEEPER) 18 | .setTimeoutMillis(100000) 19 | .setSerializer(Serializer.SerializeEnum.HESSIAN.serializer); 20 | 21 | RegistryConfig registryConfig = new RegistryConfig() 22 | .setAddress("127.0.0.1:2181") 23 | .setProtocol(RpcConstants.ZOOKEEPER) 24 | .setRegister(true) 25 | .setSubscribe(true); 26 | ClientProxy clientProxy = new ClientProxy(clientConfig, new NettyClientAbstract(), HelloService.class) 27 | .setRegistryConfig(registryConfig); 28 | for (int i = 0; i < 10; i++) { 29 | HelloService helloService = clientProxy.refer(); 30 | System.out.println(helloService.sayHi()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/zookeeper/ZookeeperServerMainTest.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.zookeeper; 2 | 3 | import cn.whforever.core.config.RegistryConfig; 4 | import cn.whforever.core.config.ServerConfig; 5 | import cn.whforever.core.protocol.netty.server.NettyServerAbstract; 6 | import cn.whforever.core.proxy.ServerProxy; 7 | import cn.whforever.core.rpc.RpcConstants; 8 | import cn.whforever.core.serialize.Serializer; 9 | import cn.whforever.example.service.HelloService; 10 | import cn.whforever.example.service.impl.HelloServiceImpl; 11 | 12 | /** 13 | * @author wuhaifei 2019-08-02 14 | */ 15 | public class ZookeeperServerMainTest { 16 | 17 | public static void main(String[] args) { 18 | ServerConfig serverConfig = new ServerConfig(); 19 | serverConfig.setSerializer(Serializer.SerializeEnum.HESSIAN.serializer) 20 | .setHost("172.16.30.114") 21 | .setPort(5201) 22 | .setRef(HelloServiceImpl.class.getName()) 23 | .setRegister(true) 24 | .setInterfaceId(HelloService.class.getName()); 25 | 26 | RegistryConfig registryConfig = new RegistryConfig().setAddress("127.0.0.1:2181") 27 | .setSubscribe(true) 28 | .setRegister(true) 29 | .setProtocol(RpcConstants.ZOOKEEPER); 30 | ServerProxy serverProxy = new ServerProxy(new NettyServerAbstract()) 31 | .setServerConfig(serverConfig) 32 | .setRegistryConfig(registryConfig); 33 | try { 34 | serverProxy.export(); 35 | while (true) { 36 | 37 | } 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/java/cn/whforever/example/zookeeper/ZookeeperTest.java: -------------------------------------------------------------------------------- 1 | package cn.whforever.example.zookeeper; 2 | 3 | import org.apache.curator.RetryPolicy; 4 | import org.apache.curator.framework.CuratorFramework; 5 | import org.apache.curator.framework.CuratorFrameworkFactory; 6 | import org.apache.curator.retry.ExponentialBackoffRetry; 7 | import org.apache.zookeeper.CreateMode; 8 | import org.junit.Assert; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | /** 13 | * @author wuhaifei 2019-08-01 14 | */ 15 | public class ZookeeperTest { 16 | //ZooKeeper服务地址 17 | private static final String SERVER = "127.0.0.1:2181"; 18 | //会话超时时间 19 | private final int SESSION_TIMEOUT = 30 * 1000; 20 | //连接超时时间 21 | private final int CONNECTION_TIMEOUT = 3 * 1000; 22 | /** 23 | * baseSleepTimeMs:初始的重试等待时间 24 | * maxRetries:最多重试次数 25 | */ 26 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); 27 | //创建连接实例 28 | private CuratorFramework client = null; 29 | 30 | @Before 31 | public void init() { 32 | //创建 CuratorFrameworkImpl实例 33 | client = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, retryPolicy); 34 | 35 | //启动 36 | client.start(); 37 | } 38 | 39 | /** 40 | * 测试创建节点 41 | * 42 | * @throws Exception 43 | */ 44 | @Test 45 | public void testCreate() throws Exception { 46 | //创建永久节点 47 | // client.create().forPath("/curator","/curator data".getBytes()); 48 | 49 | //创建永久有序节点 50 | // client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/curator_sequential","/curator_sequential data".getBytes()); 51 | 52 | //创建临时节点 53 | client.create().withMode(CreateMode.EPHEMERAL) 54 | .forPath("/curator/ephemeral", "/curator/ephemeral data".getBytes()); 55 | 56 | //创建临时有序节点 57 | // client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL) 58 | // .forPath("/curator/ephemeral_path1","/curator/ephemeral_path1 data".getBytes()); 59 | // 60 | // client.create().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL) 61 | // .forPath("/curator/ephemeral_path2","/curator/ephemeral_path2 data".getBytes()); 62 | 63 | client.setData().forPath("/zookeeper/quota", "123123".getBytes()); 64 | String pathData = new String(client.getData().forPath("/zookeeper/quota")); 65 | Assert.assertEquals("123123", pathData); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /child-rpc-example/src/test/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | cn.whforever 8 | child-rpc 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | 13 | child-rpc-core 14 | bom 15 | child-rpc-example 16 | 17 | 18 | 19 | 20 | 21 | cn.whforever 22 | child-rpc-bom 23 | ${project.version} 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /rpc-1.md: -------------------------------------------------------------------------------- 1 | > 原文地址: [haifeiWu和他朋友们的博客](https://www.hchstudio.cn/article/2018/b674/?_ref=github)
博客地址:[www.hchstudio.cn](https://www.hchstudio.cn/article/2018/b674/?_ref=github)
欢迎转载,转载请注明作者及出处,谢谢! 2 | 3 | 服务端开发都会或多或少的涉及到 RPC 的使用,当然如果止步于会用,对自己的成长很是不利,所以楼主今天本着知其然,且知其所以然的精神来探讨一下 RPC 这个东西。 4 | 5 | 6 | ## child-rpc模型 7 | child-rpc 采用 socket 直连的方式来实现服务的远程调用,然后使用 jdk 动态代理的方式让调用者感知不到远程调用。 8 | ![child-rpc模型]( http://img.whforever.cn/rpc-model.png "") 9 | 10 | ## child-rpc 开箱使用 11 | 12 | ### 发布服务 13 | RPC 服务类要监听指定IP端口,设置要发布的服务的实现及其接口的引用,并指定序列化的方式,目前 child-rpc 支持 Hessian,JACKSON 两种序列化方式。 14 | 15 | ```java 16 | /** 17 | * @author wuhf 18 | * @Date 2018/9/1 18:30 19 | **/ 20 | public class ServerTest { 21 | 22 | public static void main(String[] args) { 23 | ServerConfig serverConfig = new ServerConfig(); 24 | serverConfig.setSerializer(Serializer.SerializeEnum.HESSIAN.serializer) 25 | .setPort(5201) 26 | .setInterfaceId(HelloService.class.getName()) 27 | .setRef(HelloServiceImpl.class.getName()); 28 | ServerProxy serverProxy = new ServerProxy(new NettyServer(),serverConfig); 29 | try { 30 | serverProxy.export(); 31 | while (true){ 32 | 33 | } 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | } 39 | ``` 40 | 41 | ### 引用服务 42 | RPC 客户端要链接远程 IP 端口,并注册要引用的服务,然后调用 sayHi 方法,输出结果 43 | ```java 44 | /** 45 | * @author wuhf 46 | * @Date 2018/9/1 18:31 47 | **/ 48 | public class ClientTest { 49 | 50 | public static void main(String[] args) { 51 | ClientConfig clientConfig = new ClientConfig(); 52 | clientConfig.setHost("127.0.0.1") 53 | .setPort(5201) 54 | .setTimeoutMillis(100000) 55 | .setSerializer(Serializer.SerializeEnum.HESSIAN.serializer); 56 | ClientProxy clientProxy = new ClientProxy(clientConfig,new NettyClient(),HelloService.class); 57 | for (int i = 0; i < 10; i++) { 58 | HelloService helloService = (HelloService) clientProxy.refer(); 59 | System.out.println(helloService.sayHi()); 60 | } 61 | } 62 | } 63 | ``` 64 | ### 运行 65 | server 端输出 66 | ![rpc-srever]( http://img.whforever.cn/rpc-server.png "") 67 | 68 | client 端输出 69 | ![rpc-client]( http://img.whforever.cn/rpc-client.png "") 70 | 71 | ## child-rpc 具体实现 72 | 73 | ### RPC 请求,响应消息实体定义 74 | 定义消息请求响应格式,消息类型、消息唯一 ID 和消息的 json 序列化字符串内容。消息唯一 ID 是用来客户端验证服务器请求和响应是否匹配。 75 | 76 | ```java 77 | // rpc 请求 78 | public class RpcRequest implements Serializable { 79 | private static final long serialVersionUID = -4364536436151723421L; 80 | 81 | private String requestId; 82 | private long createMillisTime; 83 | private String className; 84 | private String methodName; 85 | private Class[] parameterTypes; 86 | private Object[] parameters; 87 | 88 | // set get 方法省略掉 89 | } 90 | // rpc 响应 91 | public class RpcResponse implements Serializable { 92 | private static final long serialVersionUID = 7329530374415722876L; 93 | 94 | private String requestId; 95 | private Throwable error; 96 | private Object result; 97 | // set get 方法省略掉 98 | } 99 | ``` 100 | 101 | ### 网络传输过程中的编码解码 102 | 103 | 消息编码解码使用自定义的编解码器,根据服务初始化是使用的序列化器来将数据序列化成字节流,拆包的策略是设定指定长度的数据包,对 socket 粘包,拆包感兴趣的小伙伴请移步 [Socket 中粘包问题浅析及其解决方案](http://www.hchstudio.cn/article/2018/d5b3/ ) 104 | 105 | 下面是解码器代码实现 : 106 | ```java 107 | public class NettyDecoder extends ByteToMessageDecoder { 108 | 109 | private Class genericClass; 110 | private Serializer serializer; 111 | 112 | public NettyDecoder(Class genericClass, Serializer serializer) { 113 | this.genericClass = genericClass; 114 | this.serializer = serializer; 115 | } 116 | 117 | @Override 118 | protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception { 119 | if (byteBuf.readableBytes() < 4) { 120 | return; 121 | } 122 | 123 | byteBuf.markReaderIndex(); 124 | // 读取消息长度 125 | int dataLength = byteBuf.readInt(); 126 | 127 | if (dataLength < 0) { 128 | channelHandlerContext.close(); 129 | } 130 | 131 | if (byteBuf.readableBytes() < dataLength) { 132 | byteBuf.resetReaderIndex(); 133 | return; 134 | } 135 | 136 | try { 137 | byte[] data = new byte[dataLength]; 138 | byteBuf.readBytes(data); 139 | Object object = serializer.deserialize(data,genericClass); 140 | list.add(object); 141 | } catch (Exception e) { 142 | e.printStackTrace(); 143 | } 144 | } 145 | } 146 | ``` 147 | 148 | 下面是编码器的实现: 149 | 150 | ```java 151 | public class NettyEncoder extends MessageToByteEncoder { 152 | 153 | private Class genericClass; 154 | private Serializer serializer; 155 | 156 | public NettyEncoder(Class genericClass,Serializer serializer) { 157 | this.serializer = serializer; 158 | this.genericClass = genericClass; 159 | } 160 | 161 | @Override 162 | protected void encode(ChannelHandlerContext channelHandlerContext, Object object, ByteBuf byteBuf) throws Exception { 163 | if (genericClass.isInstance(object)) { 164 | byte[] data = serializer.serialize(object); 165 | byteBuf.writeInt(data.length); 166 | byteBuf.writeBytes(data); 167 | } 168 | } 169 | } 170 | ``` 171 | ### RPC 业务逻辑处理 handler 172 | 173 | server 端业务处理 handler 实现 : 主要业务逻辑是 通过 java 的反射实现方法的调用。 174 | 175 | ```java 176 | public class NettyServerHandler extends SimpleChannelInboundHandler { 177 | 178 | private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class); 179 | 180 | @Override 181 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcRequest rpcRequest) throws Exception { 182 | // invoke 通过调用反射方法获取 rpcResponse 183 | RpcResponse response = RpcInvokerHandler.invokeService(rpcRequest); 184 | channelHandlerContext.writeAndFlush(response); 185 | } 186 | 187 | @Override 188 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 189 | logger.error(">>>>>>>>>>> child-rpc provider netty server caught exception", cause); 190 | ctx.close(); 191 | } 192 | } 193 | 194 | public class RpcInvokerHandler { 195 | public static Map serviceMap = new HashMap(); 196 | public static RpcResponse invokeService(RpcRequest request) throws ClassNotFoundException, IllegalAccessException, InstantiationException { 197 | Object serviceBean = serviceMap.get(request.getClassName()); 198 | 199 | RpcResponse response = new RpcResponse(); 200 | response.setRequestId(request.getRequestId()); 201 | try { 202 | Class serviceClass = serviceBean.getClass(); 203 | String methodName = request.getMethodName(); 204 | Class[] parameterTypes = request.getParameterTypes(); 205 | Object[] parameters = request.getParameters(); 206 | 207 | Method method = serviceClass.getMethod(methodName, parameterTypes); 208 | method.setAccessible(true); 209 | Object result = method.invoke(serviceBean, parameters); 210 | 211 | response.setResult(result); 212 | } catch (Throwable t) { 213 | t.printStackTrace(); 214 | response.setError(t); 215 | } 216 | return response; 217 | } 218 | } 219 | ``` 220 | 221 | client 端主要业务实现是等待 server 响应返回。代码比较简单就不贴代码了,详情请看下面给出的 github 链接。 222 | ### RPC 服务端与客户端启动 223 | 因为服务端与客户端启动都是 Netty 的模板代码,因为篇幅原因就不贴出来了,感兴趣的伙伴请移步 [造个轮子---RPC动手实现]( https://github.com/haifeiWu/child-rpc )。 224 | 225 | ## 小结 226 | 因为只是为了理解 RPC 的本质,所以在实现细节上还有好多没有仔细去雕琢的地方。不过 RPC 的目的就是允许像调用本地服务一样调用远程服务,对调用者透明,于是我们使用了动态代理。并使用 Netty 的 handler 发送数据和响应数据,总的来说该框架实现了简单的 RPC 调用。代码比较简单,主要是思路,以及了解 RPC 底层的实现。 227 | 228 | ## 参考文章 229 | 230 | - [造个轮子---RPC动手实现]( https://github.com/haifeiWu/child-rpc ) 231 | - [Socket 中粘包问题浅析及其解决方案](https://www.hchstudio.cn/article/2018/d5b3/ ) 232 | 233 | 234 | ![关注我们](https://img.hchstudio.cn/CodePig-QRCode.jpg "关注我们") 235 | -------------------------------------------------------------------------------- /rpc-2.md: -------------------------------------------------------------------------------- 1 | > 原文地址: [haifeiWu和他朋友们的博客](https://www.hchstudio.cn/article/2019/ba29/?_ref=github)
博客地址:[www.hchstudio.cn](https://www.hchstudio.cn/article/2019/ba29/?_ref=github)
欢迎转载,转载请注明作者及出处,谢谢! 2 | 3 | 前段时间自己搞了个 RPC 的轮子,不过相对来说比较简单,最近在原来的基础上加以改造,使用 Zookeeper 实现了 provider 自动寻址以及消费者的简单负载均衡,对之前的感兴趣的请转 [造个轮子---RPC动手实现]( https://www.hchstudio.cn/article/2018/b674/ )。 4 | 5 | ## RPC 模型 6 | 在原来使用 TCP 直连的基础上实现基于 Zookeeper 的服务的注册与发现,改造后的依赖关系是这样的。 7 | 8 | ![child-rpc](https://img.hchstudio.cn/child-rpc2.png) 9 | 10 | ## 怎么用 11 | 话不多说,我们来看下如何发布和引用服务。 12 | 服务端我们将服务的 IP 和端口号基础信息注册到 Zookeeper 上。 13 | ```java 14 | /** 15 | * @author wuhaifei 2019-08-02 16 | */ 17 | public class ZookeeperServerMainTest { 18 | 19 | public static void main(String[] args) { 20 | ServerConfig serverConfig = new ServerConfig(); 21 | serverConfig.setSerializer(AbstractSerializer.SerializeEnum.HESSIAN.serializer) 22 | .setHost("172.16.30.114") 23 | .setPort(5201) 24 | .setRef(HelloServiceImpl.class.getName()) 25 | .setRegister(true) 26 | .setInterfaceId(HelloService.class.getName()); 27 | 28 | RegistryConfig registryConfig = new RegistryConfig().setAddress("127.0.0.1:2181") 29 | .setSubscribe(true) 30 | .setRegister(true) 31 | .setProtocol(RpcConstants.ZOOKEEPER); 32 | ServerProxy serverProxy = new ServerProxy(new NettyServerAbstract()) 33 | .setServerConfig(serverConfig) 34 | .setRegistryConfig(registryConfig); 35 | try { 36 | serverProxy.export(); 37 | while (true){ 38 | 39 | } 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | 通过 Zookeeper 引用注册在其上的服务。 48 | ```java 49 | /** 50 | * @author wuhaifei 2019-08-02 51 | */ 52 | public class ZookeeperClientMainTest { 53 | public static void main(String[] args) { 54 | ClientConfig clientConfig = new ClientConfig(); 55 | clientConfig.setProtocol(RpcConstants.ZOOKEEPER) 56 | .setTimeoutMillis(100000) 57 | .setSerializer(AbstractSerializer.SerializeEnum.HESSIAN.serializer); 58 | 59 | RegistryConfig registryConfig = new RegistryConfig() 60 | .setAddress("127.0.0.1:2181") 61 | .setProtocol(RpcConstants.ZOOKEEPER) 62 | .setRegister(true) 63 | .setSubscribe(true); 64 | ClientProxy clientProxy = new ClientProxy(clientConfig, new NettyClientAbstract(), HelloService.class) 65 | .setRegistryConfig(registryConfig); 66 | for (int i = 0; i < 10; i++) { 67 | HelloService helloService = clientProxy.refer(); 68 | System.out.println(helloService.sayHi()); 69 | } 70 | } 71 | } 72 | ``` 73 | 74 | 运行结果就不一一贴出了,感兴趣的小伙伴可以查看楼主传到 github 上的源码[这是一个rpc的轮子](https://github.com/haifeiWu/child-rpc.git)。 75 | 76 | ## 服务的发布与订阅 77 | 楼主在原来代码的基础上添加了 Zookeeper 的注册的逻辑,原来的代码相关介绍请转 [造个轮子---RPC动手实现]( https://www.hchstudio.cn/article/2018/b674/ )。 78 | 79 | ### 服务的发布 80 | ```java 81 | /** 82 | * 发布服务 83 | */ 84 | public void export() { 85 | try { 86 | Object serviceBean = Class.forName((String) serverConfig.getRef()).newInstance(); 87 | RpcInvokerHandler.serviceMap.put(serverConfig.getInterfaceId(), serviceBean); 88 | this.childServer.start(this.getServerConfig()); 89 | 90 | if (serverConfig.isRegister()) { 91 | // 将服务注册到zookeeper 92 | register(); 93 | } 94 | } catch (Exception e) { 95 | // 取消服务注册 96 | unregister(); 97 | if (e instanceof ChildRpcRuntimeException) { 98 | throw (ChildRpcRuntimeException) e; 99 | } else { 100 | throw new ChildRpcRuntimeException("Build provider proxy error!", e); 101 | } 102 | } 103 | exported = true; 104 | } 105 | 106 | /** 107 | * 注册服务 108 | */ 109 | protected void register() { 110 | if (serverConfig.isRegister()) { 111 | Registry registry = RegistryFactory.getRegistry(this.getRegistryConfig()); 112 | registry.init(); 113 | registry.start(); 114 | try { 115 | registry.register(this.serverConfig); 116 | } catch (ChildRpcRuntimeException e) { 117 | throw e; 118 | } catch (Throwable e) { 119 | String appName = serverConfig.getInterfaceId(); 120 | LOGGER.info(appName, "Catch exception when register to registry: " 121 | + registryConfig.getId(), e); 122 | } 123 | } 124 | } 125 | 126 | ``` 127 | ### 服务的订阅 128 | 129 | ```java 130 | /** 131 | * 服务的引用. 132 | */ 133 | public T refer() { 134 | try { 135 | if (config.isSubscribe()) { 136 | subscribe(); 137 | } 138 | childClient.init(this.clientConfig); 139 | return invoke(); 140 | } catch (Exception e) { 141 | e.printStackTrace(); 142 | } 143 | return null; 144 | } 145 | 146 | /** 147 | * 订阅zk的服务列表. 148 | */ 149 | private void subscribe() { 150 | Registry registry = RegistryFactory.getRegistry(this.getRegistryConfig()); 151 | registry.init(); 152 | registry.start(); 153 | 154 | this.clientConfig = (ClientConfig) config; 155 | List providerList = registry.subscribe(this.clientConfig); 156 | 157 | if (null == providerList) { 158 | throw new ChildRpcRuntimeException("无可用服务供订阅!"); 159 | } 160 | 161 | // 使用随机算法,随机选择一个provider 162 | int index = ThreadLocalRandom.current().nextInt(providerList.size()); 163 | String providerInfo = providerList.get(index); 164 | String[] providerArr = providerInfo.split(":"); 165 | clientConfig = (ClientConfig) this.config; 166 | clientConfig.setHost(providerArr[0]); 167 | clientConfig.setPort(Integer.parseInt(providerArr[1])); 168 | } 169 | ``` 170 | 171 | 上面代码比较简单,就是在原来直连的基础上添加 zk 的操作,在发布服务的时候将 provider 的 IP 和端口号基础信息注册到 zk 上,在引用服务的时候使用随机算法从 zk 上选取可用的 provider 信息,然后进行 invoke 调用。 172 | 173 | 174 | ## 小结 175 | RPC(Remote procedure call)底层逻辑相对来说比较简单,楼主在实现的过程中参考了其他 RPC 框架的部分代码,受益匪浅~ --------------------------------------------------------------------------------