├── .gitignore ├── src ├── main │ ├── resources │ │ ├── META-INF │ │ │ └── assembly │ │ │ │ └── bin │ │ │ │ ├── restart.sh │ │ │ │ ├── server.sh │ │ │ │ ├── stop.sh │ │ │ │ ├── dump.sh │ │ │ │ └── start.sh │ │ └── log4j.properties │ └── java │ │ └── com │ │ └── lenzhao │ │ └── framework │ │ ├── server │ │ ├── IRpcServer.java │ │ ├── NettyRpcServerHandler.java │ │ ├── ExtensionLoader.java │ │ └── RpcServerBootstrap.java │ │ ├── util │ │ ├── Sequence.java │ │ ├── NamedTheadFactory.java │ │ ├── LRUMap.java │ │ ├── SchemaCache.java │ │ ├── IOUtil.java │ │ ├── ReflectionCache.java │ │ ├── ClassUtil.java │ │ └── FileUtil.java │ │ ├── protocol │ │ ├── MySerializerFactory.java │ │ ├── ISerializer.java │ │ ├── RpcResponse.java │ │ ├── RpcRequestEncode.java │ │ ├── RpcRequestDecode.java │ │ ├── RpcResponseEncode.java │ │ ├── RpcResponseDecode.java │ │ ├── FutureAdapter.java │ │ ├── HessianSerializer.java │ │ ├── RpcRequest.java │ │ ├── ProtobufSerializer.java │ │ └── InvokeFuture.java │ │ ├── annotation │ │ └── ServiceAnnotation.java │ │ ├── client │ │ ├── Filter.java │ │ ├── ILoadBalance.java │ │ ├── TpsFilter.java │ │ ├── TimeOutFilter.java │ │ ├── GenericFilter.java │ │ ├── RpcClientProxy.java │ │ └── LeastActiveLoadBalance.java │ │ ├── common │ │ ├── RpcContext.java │ │ └── Constants.java │ │ ├── exception │ │ └── RpcException.java │ │ ├── connect │ │ ├── IRpcConnection.java │ │ └── NettyRpcConnection.java │ │ └── config │ │ ├── ServerConfig.java │ │ ├── ServiceConfig.java │ │ └── ClientConfig.java └── test │ └── java │ └── com │ └── lenzhao │ └── framework │ ├── IServer.java │ ├── Message.java │ ├── MyServer1.java │ ├── MyServer2.java │ └── Client.java ├── README.md ├── pom.xml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | voyage.iml 3 | target 4 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/assembly/bin/restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd `dirname $0` 3 | ./stop.sh 4 | ./start.sh 5 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/server/IRpcServer.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.server; 2 | 3 | public interface IRpcServer { 4 | 5 | public void start(int port); 6 | 7 | public void stop(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/com/lenzhao/framework/IServer.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework; 2 | 3 | public interface IServer { 4 | public String getMsg(); 5 | 6 | public Message echoMsg(String msg); 7 | 8 | public Message echoMsg(int msg); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/Sequence.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | /** 6 | *序列类 7 | */ 8 | public class Sequence { 9 | private static AtomicInteger sequence = new AtomicInteger(1000); 10 | 11 | public static int next() 12 | { 13 | return sequence.addAndGet(1); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/MySerializerFactory.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | public class MySerializerFactory { 4 | 5 | public final static ISerializer getInstance(String mode) 6 | { 7 | if("hessian".equals(mode)) 8 | { 9 | return HessianSerializer.getInstance(); 10 | } 11 | else if("protobuf".equals(mode)) 12 | { 13 | return ProtobufSerializer.getInstance(); 14 | } 15 | else 16 | { 17 | return null; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/annotation/ServiceAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | *标准rcp服务的注解,name如果不写默认的是类名 10 | */ 11 | @Target({ElementType.TYPE}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ServiceAnnotation { 14 | /** 15 | * 缺省服务名 16 | */ 17 | String name() default ""; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/assembly/bin/server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd `dirname $0` 3 | if [ "$1" = "start" ]; then 4 | ./start.sh 5 | else 6 | if [ "$1" = "stop" ]; then 7 | ./stop.sh 8 | else 9 | if [ "$1" = "debug" ]; then 10 | ./start.sh debug 11 | else 12 | if [ "$1" = "restart" ]; then 13 | ./restart.sh 14 | else 15 | if [ "$1" = "dump" ]; then 16 | ./dump.sh 17 | else 18 | echo "ERROR: Please input argument: start or stop or debug or restart or dump" 19 | exit 1 20 | fi 21 | fi 22 | fi 23 | fi 24 | fi 25 | -------------------------------------------------------------------------------- /src/test/java/com/lenzhao/framework/Message.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | public class Message implements Serializable { 7 | private String msg; 8 | 9 | private Date data; 10 | 11 | public String getMsg() { 12 | return msg; 13 | } 14 | 15 | public void setMsg(String msg) { 16 | this.msg = msg; 17 | } 18 | 19 | public Date getData() { 20 | return data; 21 | } 22 | 23 | public void setData(Date data) { 24 | this.data = data; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/client/Filter.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.client; 2 | 3 | import com.lenzhao.framework.protocol.RpcResponse; 4 | import com.lenzhao.framework.protocol.RpcRequest; 5 | 6 | /** 7 | *服务调用链过滤器,可以自定义过滤器来实现一些AOP功能 8 | */ 9 | 10 | public interface Filter { 11 | 12 | /** 13 | * 服务调用 14 | * @param request 15 | * @param loadBlance 16 | * @param serviceName 17 | * @return 18 | * @throws Throwable 19 | */ 20 | RpcResponse sendRequest(RpcRequest request, ILoadBalance loadBlance, String serviceName) throws Throwable; 21 | 22 | } -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/ISerializer.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | public interface ISerializer { 8 | 9 | public RpcRequest decodeRequest(InputStream inputStream) 10 | throws IOException ; 11 | 12 | public void encodeResponse(OutputStream outputStream, RpcResponse result) 13 | throws IOException ; 14 | 15 | public RpcResponse decodeResponse(InputStream inputStream) 16 | throws IOException ; 17 | 18 | public void encodeRequest(OutputStream outputStream, RpcRequest request) 19 | throws IOException ; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/common/RpcContext.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.common; 2 | 3 | import java.util.concurrent.Future; 4 | 5 | /** 6 | *上下文对象 7 | */ 8 | public class RpcContext { 9 | 10 | private static final ThreadLocal LOCAL = new ThreadLocal() { 11 | @Override 12 | protected RpcContext initialValue() { 13 | return new RpcContext(); 14 | } 15 | }; 16 | 17 | public static RpcContext getContext() { 18 | return LOCAL.get(); 19 | } 20 | 21 | private Future future = null; 22 | 23 | public Future getFuture() { 24 | return (Future)future; 25 | } 26 | 27 | public void setFuture(Future future) { 28 | this.future = future; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/exception/RpcException.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.exception; 2 | 3 | /** 4 | *所有的异常都统一封装成这个类 5 | */ 6 | public class RpcException extends RuntimeException { 7 | 8 | public RpcException() { 9 | super(); 10 | } 11 | 12 | public RpcException(String message, Throwable cause, 13 | boolean enableSuppression, boolean writableStackTrace) { 14 | super(message, cause, enableSuppression, writableStackTrace); 15 | } 16 | 17 | public RpcException(String message, Throwable cause) { 18 | super(message, cause); 19 | } 20 | 21 | public RpcException(String message) { 22 | super(message); 23 | } 24 | 25 | public RpcException(Throwable cause) { 26 | super(cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/client/ILoadBalance.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.client; 2 | 3 | import com.lenzhao.framework.connect.IRpcConnection; 4 | 5 | /** 6 | *负载均衡 7 | */ 8 | public interface ILoadBalance { 9 | /** 10 | * 根据服务名得到服务连接字符串,从多个备选连接串中按照负载均衡选择一个连接串 11 | * @param serviceName 12 | * @return 13 | */ 14 | String getLoadBalance(String serviceName); 15 | 16 | /** 17 | * 服务支持完毕,归还连接并计数 18 | * @param serviceName 19 | * @param connStr 20 | */ 21 | void finishLoadBalance(String serviceName, String connStr); 22 | 23 | /** 24 | * 根据连接串得到长连接 25 | * @param conn 26 | * @return 27 | */ 28 | IRpcConnection getConnection(String conn); 29 | 30 | 31 | /** 32 | * 得到当前客户端连接的并发量 33 | * @return 34 | */ 35 | int getCurTotalCount(); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/NamedTheadFactory.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | /** 7 | *可命名的线程工厂 8 | */ 9 | public class NamedTheadFactory implements ThreadFactory { 10 | 11 | private String threadName = null; 12 | 13 | public NamedTheadFactory(String threadName) { 14 | this.threadName = threadName; 15 | } 16 | 17 | private final AtomicInteger threadId = new AtomicInteger(0); 18 | 19 | @Override 20 | public Thread newThread(Runnable r) { 21 | Thread ret = new Thread(Thread.currentThread().getThreadGroup(), r, threadName + "-Thread-" + threadId.getAndIncrement(), 0); 22 | ret.setDaemon(false); 23 | if (threadId.get() == Integer.MAX_VALUE) { 24 | threadId.set(0); 25 | } 26 | return ret; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/client/TpsFilter.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.client; 2 | 3 | import com.lenzhao.framework.protocol.RpcResponse; 4 | import com.lenzhao.framework.common.Constants; 5 | import com.lenzhao.framework.exception.RpcException; 6 | import com.lenzhao.framework.protocol.RpcRequest; 7 | 8 | /** 9 | *控制客户端调用服务的最大并发量,超过最大并发量直接抛异常 10 | */ 11 | public class TpsFilter implements Filter { 12 | 13 | private Filter next; 14 | 15 | public TpsFilter(Filter next) { 16 | this.next = next; 17 | } 18 | 19 | @Override 20 | public RpcResponse sendRequest(RpcRequest request, ILoadBalance loadBlance,String serviceName) throws Throwable { 21 | int maxConcurrentNum = Constants.CLIENT_CONCURRENT_NUM; 22 | if(loadBlance.getCurTotalCount() > maxConcurrentNum) { 23 | throw new RpcException("total invoke is bigger than " + maxConcurrentNum); 24 | } else { 25 | return next.sendRequest(request, loadBlance, serviceName); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/connect/IRpcConnection.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.connect; 2 | 3 | import com.lenzhao.framework.protocol.RpcRequest; 4 | import com.lenzhao.framework.protocol.RpcResponse; 5 | 6 | /** 7 | *客户端连接 8 | */ 9 | public interface IRpcConnection { 10 | 11 | /** 12 | * 发送请求 13 | * @param request 14 | * @param async 标示是否异步发送请求 15 | * @return 16 | * @throws Throwable 17 | */ 18 | RpcResponse sendRequest(RpcRequest request, boolean async) throws Throwable; 19 | 20 | /** 21 | * 初始化连接 22 | * @throws Throwable 23 | */ 24 | void open() throws Throwable; 25 | 26 | /** 27 | * 重新连接 28 | * @throws Throwable 29 | */ 30 | void connect() throws Throwable; 31 | 32 | /** 33 | * 关闭连接 34 | * @throws Throwable 35 | */ 36 | void close() throws Throwable; 37 | 38 | /** 39 | * 连接是否已经关闭 40 | * @return 41 | */ 42 | boolean isClosed(); 43 | 44 | /** 45 | * 心跳是否正常 46 | * @return 47 | */ 48 | boolean isConnected(); 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/assembly/bin/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd `dirname $0` 3 | BIN_DIR=`pwd` 4 | cd .. 5 | DEPLOY_DIR=`pwd` 6 | CONF_DIR=$DEPLOY_DIR/conf 7 | 8 | SERVER_NAME=`sed '/voyage.application.name/!d;s/.*=//' conf/voyage.properties | tr -d '\r'` 9 | 10 | if [ -z "$SERVER_NAME" ]; then 11 | SERVER_NAME=`hostname` 12 | fi 13 | 14 | PIDS=`ps -f | grep java | grep "$CONF_DIR" |awk '{print $2}'` 15 | if [ -z "$PIDS" ]; then 16 | echo "ERROR: The $SERVER_NAME does not started!" 17 | exit 1 18 | fi 19 | 20 | if [ "$1" != "skip" ]; then 21 | $BIN_DIR/dump.sh 22 | fi 23 | 24 | echo -e "Stopping the $SERVER_NAME ...\c" 25 | for PID in $PIDS ; do 26 | kill $PID > /dev/null 2>&1 27 | done 28 | 29 | COUNT=0 30 | while [ $COUNT -lt 1 ]; do 31 | echo -e ".\c" 32 | sleep 1 33 | COUNT=1 34 | for PID in $PIDS ; do 35 | PID_EXIST=`ps -f -p $PID | grep java` 36 | if [ -n "$PID_EXIST" ]; then 37 | COUNT=0 38 | break 39 | fi 40 | done 41 | done 42 | 43 | echo "OK!" 44 | echo "PID: $PIDS" 45 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | *协议响应实体 7 | */ 8 | public class RpcResponse implements Serializable{ 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private String requestID; 13 | 14 | private Throwable exception; 15 | 16 | private Object result; 17 | 18 | public RpcResponse() { 19 | super(); 20 | } 21 | 22 | public RpcResponse(String requestID) { 23 | super(); 24 | this.requestID = requestID; 25 | } 26 | 27 | public String getRequestID() { 28 | return requestID; 29 | } 30 | 31 | public void setRequestID(String requestID) { 32 | this.requestID = requestID; 33 | } 34 | 35 | public Throwable getException() { 36 | return exception; 37 | } 38 | 39 | public void setException(Throwable exception) { 40 | this.exception = exception; 41 | } 42 | 43 | public Object getResult() { 44 | return result; 45 | } 46 | 47 | public void setResult(Object result) { 48 | this.result = result; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/lenzhao/framework/MyServer1.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework; 2 | 3 | import java.util.Date; 4 | 5 | import com.lenzhao.framework.annotation.ServiceAnnotation; 6 | import com.lenzhao.framework.server.RpcServerBootstrap; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | @ServiceAnnotation 11 | public class MyServer1 implements IServer { 12 | 13 | private static final Logger logger = LoggerFactory.getLogger(MyServer1.class); 14 | 15 | public String getMsg() { 16 | logger.info("getMsg echo"); 17 | return "Hello"; 18 | } 19 | 20 | public static void main(String[] args) { 21 | RpcServerBootstrap bootstrap = new RpcServerBootstrap(); 22 | bootstrap.start(8080); 23 | } 24 | 25 | @Override 26 | public Message echoMsg(String msg) { 27 | Message result=new Message(); 28 | result.setMsg(msg); 29 | result.setData(new Date()); 30 | return result; 31 | } 32 | 33 | @Override 34 | public Message echoMsg(int msg) { 35 | Message result=new Message(); 36 | result.setMsg("int:" + msg); 37 | result.setData(new Date()); 38 | return result; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/lenzhao/framework/MyServer2.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework; 2 | 3 | import java.util.Date; 4 | 5 | import com.lenzhao.framework.annotation.ServiceAnnotation; 6 | import com.lenzhao.framework.server.RpcServerBootstrap; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | 11 | @ServiceAnnotation 12 | public class MyServer2 implements IServer { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(MyServer2.class); 15 | 16 | public String getMsg() { 17 | logger.info("getMsg echo"); 18 | return "Hello"; 19 | } 20 | 21 | public static void main(String[] args) { 22 | RpcServerBootstrap bootstrap = new RpcServerBootstrap(); 23 | bootstrap.start(9090); 24 | } 25 | 26 | @Override 27 | public Message echoMsg(String msg) { 28 | Message result=new Message(); 29 | result.setMsg(msg); 30 | result.setData(new Date()); 31 | return result; 32 | } 33 | 34 | @Override 35 | public Message echoMsg(int msg) { 36 | Message result=new Message(); 37 | result.setMsg("int:"+msg); 38 | result.setData(new Date()); 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/config/ServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.config; 2 | 3 | /** 4 | *服务启动配置,参数有线程数目,超时时间,端口等 5 | */ 6 | public class ServerConfig { 7 | private int threadCnt = 100; 8 | 9 | private int readTimeout = 0; 10 | 11 | private int connectTimeout = 1000; 12 | 13 | private int port = 9090; 14 | 15 | public ServerConfig(int port) 16 | { 17 | this.port=port; 18 | } 19 | 20 | public int getThreadCnt() { 21 | return threadCnt; 22 | } 23 | 24 | public void setThreadCnt(int threadCnt) { 25 | this.threadCnt = threadCnt; 26 | } 27 | 28 | 29 | public int getConnectTimeout() { 30 | return connectTimeout; 31 | } 32 | 33 | public void setConnectTimeout(int connectTimeout) { 34 | this.connectTimeout = connectTimeout; 35 | } 36 | 37 | public int getPort() { 38 | return port; 39 | } 40 | 41 | public void setPort(int port) { 42 | this.port = port; 43 | } 44 | 45 | public int getReadTimeout() { 46 | return readTimeout; 47 | } 48 | 49 | public void setReadTimeout(int readTimeout) { 50 | this.readTimeout = readTimeout; 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/RpcRequestEncode.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | import org.jboss.netty.buffer.ChannelBuffer; 6 | import org.jboss.netty.buffer.ChannelBuffers; 7 | import org.jboss.netty.channel.ChannelHandlerContext; 8 | import org.jboss.netty.channel.Channels; 9 | import org.jboss.netty.channel.MessageEvent; 10 | import org.jboss.netty.channel.SimpleChannelHandler; 11 | 12 | import com.lenzhao.framework.common.Constants; 13 | 14 | /** 15 | *请求编码 16 | */ 17 | public class RpcRequestEncode extends SimpleChannelHandler{ 18 | 19 | @Override 20 | public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) 21 | throws Exception { 22 | RpcRequest request = (RpcRequest) e.getMessage(); 23 | ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); 24 | //先写入标示的魔数 25 | baos.write(Constants.MAGIC_BYTES); 26 | MySerializerFactory.getInstance(Constants.DEFAULT_RPC_CODE_MODE).encodeRequest(baos, request); 27 | ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(baos.toByteArray()); 28 | Channels.write(ctx, e.getFuture(), buffer); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/RpcRequestDecode.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import org.jboss.netty.buffer.ChannelBuffer; 4 | import org.jboss.netty.buffer.ChannelBufferInputStream; 5 | import org.jboss.netty.channel.Channel; 6 | import org.jboss.netty.channel.ChannelHandlerContext; 7 | import org.jboss.netty.handler.codec.frame.FrameDecoder; 8 | 9 | import com.lenzhao.framework.common.Constants; 10 | 11 | /** 12 | *请求解码 13 | */ 14 | public class RpcRequestDecode extends FrameDecoder { 15 | 16 | @Override 17 | protected Object decode(ChannelHandlerContext ctx, Channel channel, 18 | ChannelBuffer buffer) throws Exception { 19 | if (buffer.readableBytes() < 2) { 20 | return null; 21 | } 22 | byte byte1 = buffer.readByte(); 23 | byte byte2 = buffer.readByte(); 24 | if (byte1!=Constants.MAGIC_HIGH || byte2!=Constants.MAGIC_LOW) { 25 | throw new RuntimeException("magic number not right"); 26 | } 27 | ChannelBufferInputStream in = new ChannelBufferInputStream(buffer); 28 | RpcRequest request = MySerializerFactory.getInstance(Constants.DEFAULT_RPC_CODE_MODE).decodeRequest(in); 29 | return request; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/RpcResponseEncode.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | import org.jboss.netty.buffer.ChannelBuffer; 6 | import org.jboss.netty.buffer.ChannelBuffers; 7 | import org.jboss.netty.channel.ChannelHandlerContext; 8 | import org.jboss.netty.channel.Channels; 9 | import org.jboss.netty.channel.MessageEvent; 10 | import org.jboss.netty.channel.SimpleChannelHandler; 11 | 12 | import com.lenzhao.framework.common.Constants; 13 | 14 | /** 15 | *响应编码 16 | */ 17 | public class RpcResponseEncode extends SimpleChannelHandler { 18 | 19 | @Override 20 | public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) 21 | throws Exception { 22 | RpcResponse response = (RpcResponse) e.getMessage(); 23 | ByteArrayOutputStream baos = new ByteArrayOutputStream(16384); 24 | //先写入标示的魔数 25 | baos.write(Constants.MAGIC_BYTES); 26 | MySerializerFactory.getInstance(Constants.DEFAULT_RPC_CODE_MODE).encodeResponse(baos, response); 27 | ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(baos.toByteArray()); 28 | Channels.write(ctx, e.getFuture(), buffer); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/client/TimeOutFilter.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.client; 2 | 3 | import com.lenzhao.framework.protocol.RpcRequest; 4 | import com.lenzhao.framework.protocol.RpcResponse; 5 | 6 | import com.lenzhao.framework.common.Constants; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | *监控服务性能,如果超时则输出监控日志 12 | */ 13 | public class TimeOutFilter implements Filter{ 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(GenericFilter.class); 16 | 17 | private Filter next; 18 | 19 | public TimeOutFilter(Filter next) { 20 | this.next = next; 21 | } 22 | @Override 23 | public RpcResponse sendRequest(RpcRequest request, ILoadBalance loadBlance,String serviceName) throws Throwable { 24 | long start = System.currentTimeMillis(); 25 | RpcResponse response = next.sendRequest(request, loadBlance, serviceName); 26 | long spendTime = System.currentTimeMillis() - start; 27 | if(spendTime > Constants.TIMEOUT_LOG_MILLSECOND) { 28 | logger.warn("spend time is bigger than "+ Constants.TIMEOUT_LOG_MILLSECOND +",the serviceName is:"+serviceName); 29 | } 30 | return response; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/RpcResponseDecode.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import org.jboss.netty.buffer.ChannelBuffer; 4 | import org.jboss.netty.buffer.ChannelBufferInputStream; 5 | import org.jboss.netty.channel.Channel; 6 | import org.jboss.netty.channel.ChannelHandlerContext; 7 | import org.jboss.netty.handler.codec.frame.FrameDecoder; 8 | 9 | import com.lenzhao.framework.common.Constants; 10 | 11 | /** 12 | *响应解码 13 | */ 14 | public class RpcResponseDecode extends FrameDecoder { 15 | 16 | @Override 17 | protected Object decode(ChannelHandlerContext context, Channel channel, 18 | ChannelBuffer buffer) throws Exception { 19 | if (buffer.readableBytes() < 2) { 20 | return null; 21 | } 22 | byte byte1 = buffer.readByte(); 23 | byte byte2 = buffer.readByte(); 24 | if (byte1 != Constants.MAGIC_HIGH || byte2 != Constants.MAGIC_LOW) { 25 | throw new RuntimeException("magic number not right"); 26 | } 27 | ChannelBufferInputStream in = new ChannelBufferInputStream(buffer); 28 | RpcResponse response = MySerializerFactory.getInstance(Constants.DEFAULT_RPC_CODE_MODE).decodeResponse(in); 29 | return response; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootCategory=INFO,stdout,R 2 | 3 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | 6 | # Pattern to output the caller's file name and line number. 7 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss} %c.%t(:%L) %p %m %n 8 | 9 | log4j.appender.R=org.apache.log4j.DailyRollingFileAppender 10 | ##------------------------------------------需配置参数开始------------------------------------------------------ 11 | log4j.appender.R.File=/var/log/voyage.log 12 | ##------------------------------------------需配置参数结束------------------------------------------------------ 13 | log4j.appender.R.DatePattern = '.'yyyyMMdd 14 | 15 | log4j.appender.R.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.R.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss} %c.%t(:%L) %p %m %n 17 | # If programmed properly the most messages would be at DEBUG 18 | # and the least at FATAL. 19 | log4j.logger.org.apache=INFO 20 | # All hibernate log output of "info" level or higher goes to stdout. 21 | # For more verbose logging, change the "info" to "debug" on the last line. 22 | log4j.logger.net.sf.hibernate=INFO 23 | log4j.logger.org.quartz=INFO -------------------------------------------------------------------------------- /src/test/java/com/lenzhao/framework/Client.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework; 2 | 3 | import com.lenzhao.framework.client.RpcClientProxy; 4 | import com.lenzhao.framework.common.RpcContext; 5 | 6 | import java.util.concurrent.Future; 7 | 8 | 9 | public class Client { 10 | 11 | public static void main(String[] args) { 12 | try { 13 | final IServer server1= RpcClientProxy.proxy(IServer.class, "server", "MyServer1"); 14 | long startMillis = System.currentTimeMillis(); 15 | for(int i=0;i<10;i++) { 16 | final int f_i=i; 17 | send(server1,f_i); 18 | } 19 | long endMillis = System.currentTimeMillis(); 20 | System.out.println("spend time:"+(endMillis-startMillis)); 21 | } catch (Throwable e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | public static void send(IServer server1,int f_i) { 27 | Message msg = null; 28 | try { 29 | //由于客户端配置的async="true",我们用异步方式来获取结果,如果是同步方式,直接msg=server1.echoMsg(f_i)即可 30 | server1.echoMsg(f_i); 31 | Future future = RpcContext.getContext().getFuture(); 32 | msg = future.get(); 33 | System.out.println("msg:"+msg.getMsg()+","+msg.getData()); 34 | } catch(Throwable e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/config/ServiceConfig.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.config; 2 | 3 | /** 4 | *客户端单个服务配置实体 5 | */ 6 | public class ServiceConfig { 7 | 8 | private String name = null; 9 | 10 | private String connectStr = null; 11 | 12 | private String maxConnection = null; 13 | 14 | private boolean async = false; 15 | 16 | public ServiceConfig() { 17 | super(); 18 | } 19 | 20 | public ServiceConfig(String name, String connectStr, String maxConnection,String async) { 21 | super(); 22 | this.name = name; 23 | this.connectStr = connectStr; 24 | this.maxConnection = maxConnection; 25 | this.async = "true".equals(async); 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public String getConnectStr() { 37 | return connectStr; 38 | } 39 | 40 | public void setConnectStr(String connectStr) { 41 | this.connectStr = connectStr; 42 | } 43 | 44 | public String getMaxConnection() { 45 | return maxConnection; 46 | } 47 | 48 | public void setMaxConnection(String maxConnection) { 49 | this.maxConnection = maxConnection; 50 | } 51 | 52 | public boolean getAsync() { 53 | return async; 54 | } 55 | 56 | public void setAsync(boolean async) { 57 | this.async = async; 58 | } 59 | 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/FutureAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | import java.util.concurrent.ExecutionException; 3 | import java.util.concurrent.Future; 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.concurrent.TimeoutException; 6 | 7 | import com.lenzhao.framework.common.Constants; 8 | 9 | /** 10 | *适配器,客户端只需要依赖Future接口,而服务端依赖的是InvokeFuture 11 | */ 12 | public class FutureAdapter implements Future { 13 | 14 | private InvokeFuture invokeFuture = null; 15 | 16 | public FutureAdapter(InvokeFuture invokeFuture) { 17 | this.invokeFuture = invokeFuture; 18 | } 19 | 20 | @Override 21 | public boolean cancel(boolean mayInterruptIfRunning) { 22 | return false; 23 | } 24 | 25 | @Override 26 | public boolean isCancelled() { 27 | return false; 28 | } 29 | 30 | @Override 31 | public boolean isDone() { 32 | return invokeFuture.isDone(); 33 | } 34 | 35 | @Override 36 | public V get() throws InterruptedException, ExecutionException { 37 | return (V)invokeFuture.get(Constants.TIMEOUT_INVOKE_MILLSECOND).getResult(); 38 | } 39 | 40 | @Override 41 | public V get(long timeout, TimeUnit unit) throws InterruptedException, 42 | ExecutionException, TimeoutException { 43 | int timeoutInMillis = (int) unit.convert(timeout, TimeUnit.MILLISECONDS); 44 | return (V)invokeFuture.get(timeoutInMillis).getResult(); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Voyage 2 | # Overview 3 | 采用Java实现的基于netty轻量的高性能分布式RPC服务框架。实现了RPC的基本功能,开发者也可以自定义扩展,简单,易用,高效。 4 | # Features 5 | * 服务端支持注解配置 6 | * 客户端实现Filter机制,可以自定义Filter 7 | * 基于netty3.x实现,后期会升级至netty4.x,充分利用netty的高性能 8 | * 数据层提供protostuff和hessian的实现,可以自定义扩展ISerializer接口 9 | * 负载均衡算法采用LRU算法,可以自定义扩展ILoadBlance接口 10 | * 客户端支持服务的同步或异步调用 11 | # Protocol 12 | magic + body 13 | # Quick Start 14 | Add dependencies to pom. 15 | ``` 16 | 17 | com.lenzhao 18 | voyage-framework 19 | 0.0.1 20 | 21 | ``` 22 | 1. 定义接口(样例) 23 | ``` 24 | git clone https://github.com/zhaoshiling1017/VoyageApi.git 25 | npm install 26 | ``` 27 | 2. 服务端开发(样例) 28 | ``` 29 | git clone https://github.com/zhaoshiling1017/VoyageServer.git 30 | npm install 31 | cd VoyageServer/target 32 | tar -xzvf voyage-server-1.0-SNAPSHOT-assembly.tar.gz 33 | cd voyage-server-1.0-SNAPSHOT 34 | bin/start.sh 35 | ``` 36 | 3. 客户端开发(样例) 37 | ``` 38 | git clone https://github.com/zhaoshiling1017/VoyageClient.git 39 | npm install 40 | cd VoyageClient/target 41 | tar -xzvf voyage-client-1.0-SNAPSHOT-assembly.tar.gz 42 | cd voyage-client-1.0-SNAPSHOT 43 | bin/start.sh 44 | ``` 45 | # Documents 46 | 暂无 47 | # TODOS 48 | * 增加注册中心(zookeeper) 49 | * 增加服务治理管理 50 | * 引入asm、javassit等java字节码工具 51 | * 完善消息传递协议 52 | # License 53 | Voyage is released under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). 54 | 55 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/common/Constants.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.common; 2 | 3 | 4 | /** 5 | * 常量类 6 | */ 7 | public class Constants { 8 | 9 | public final static String ROOT_PATH = System.getProperty("user.dir"); 10 | 11 | public final static String FILE_SEPARATOR = System.getProperty("file.separator"); 12 | 13 | public final static String PATH_SEPARATOR = System.getProperty("path.separator"); 14 | 15 | public final static String SERVER_CONNECT_SEPARATOR = ";"; 16 | 17 | //默认的序列化方式,系统自带两种方式:hessian或者protobuf 18 | public final static String DEFAULT_RPC_CODE_MODE = "protobuf"; 19 | 20 | //单客户端调用服务的最大并发量 21 | public final static int CLIENT_CONCURRENT_NUM = 100; 22 | 23 | //服务调用timeout阈值,超过阈值会记录日志 24 | public final static long TIMEOUT_LOG_MILLSECOND = 1000; 25 | 26 | //客户端连接服务的超时时间 27 | public final static long TIMEOUT_CONNECTION_MILLSECOND = 1000; 28 | 29 | //客户端心跳超时时间 30 | public final static long TIMEOUT_HEARTBEAT_MILLSECOND = 5000; 31 | 32 | //服务调用的默认超时时间 33 | public final static long TIMEOUT_INVOKE_MILLSECOND = 1000; 34 | 35 | //总并发量报警信息 36 | public final static int TOTAL_CONCURRENT_ALARAM_NUM = 10; 37 | 38 | // magic header. 39 | public static final short MAGIC = (short) 0xca80; 40 | 41 | public static final byte MAGIC_HIGH = (byte) 0xca; 42 | 43 | public static final byte MAGIC_LOW = (byte) 0x80; 44 | 45 | public static final byte[] MAGIC_BYTES = new byte[]{MAGIC_HIGH,MAGIC_LOW}; 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/HessianSerializer.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | import com.caucho.hessian.io.Hessian2Input; 8 | import com.caucho.hessian.io.Hessian2Output; 9 | 10 | public class HessianSerializer implements ISerializer{ 11 | private static final HessianSerializer INSTANCE = new HessianSerializer(); 12 | 13 | private HessianSerializer() 14 | { 15 | 16 | } 17 | 18 | public static ISerializer getInstance() { 19 | return INSTANCE; 20 | } 21 | 22 | @Override 23 | public RpcRequest decodeRequest(InputStream inputStream) throws IOException { 24 | Hessian2Input in = new Hessian2Input(inputStream); 25 | RpcRequest obj = (RpcRequest)in.readObject(); 26 | return obj; 27 | } 28 | 29 | @Override 30 | public void encodeResponse(OutputStream outputStream, RpcResponse result) 31 | throws IOException { 32 | Hessian2Output out = new Hessian2Output(outputStream); 33 | out.writeObject(result); 34 | out.flush(); 35 | } 36 | 37 | @Override 38 | public RpcResponse decodeResponse(InputStream inputStream) 39 | throws IOException { 40 | Hessian2Input in = new Hessian2Input(inputStream); 41 | RpcResponse obj = (RpcResponse)in.readObject(); 42 | return obj; 43 | } 44 | 45 | @Override 46 | public void encodeRequest(OutputStream outputStream, RpcRequest request) 47 | throws IOException { 48 | Hessian2Output out = new Hessian2Output(outputStream); 49 | out.writeObject(request); 50 | out.flush(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/LRUMap.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.concurrent.locks.ReadWriteLock; 5 | import java.util.concurrent.locks.ReentrantReadWriteLock; 6 | 7 | /** 8 | *LRU缓存类 9 | */ 10 | public class LRUMap extends LinkedHashMap { 11 | 12 | private static final long serialVersionUID = 1L; 13 | private final int maxCapacity; 14 | //加载因子 15 | private static final float LOAD_FACTOR = 0.99f; 16 | private final ReadWriteLock lock = new ReentrantReadWriteLock(); 17 | 18 | public LRUMap(int maxCapacity) { 19 | super(maxCapacity, LOAD_FACTOR, false); 20 | this.maxCapacity = maxCapacity; 21 | } 22 | 23 | @Override 24 | protected boolean removeEldestEntry(java.util.Map.Entry eldest) { 25 | return size() > maxCapacity; 26 | } 27 | 28 | @Override 29 | public V get(Object key) { 30 | try { 31 | lock.readLock().lock(); 32 | return super.get(key); 33 | } finally { 34 | lock.readLock().unlock(); 35 | } 36 | } 37 | 38 | @Override 39 | public V put(K key, V value) { 40 | try { 41 | lock.writeLock().lock(); 42 | return super.put(key, value); 43 | } finally { 44 | lock.writeLock().unlock(); 45 | } 46 | } 47 | 48 | @Override 49 | public V remove(Object key) { 50 | try { 51 | lock.writeLock().lock(); 52 | return super.remove(key); 53 | } finally { 54 | lock.writeLock().unlock(); 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/client/GenericFilter.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.client; 2 | 3 | import com.lenzhao.framework.config.ClientConfig; 4 | import com.lenzhao.framework.config.ServiceConfig; 5 | import com.lenzhao.framework.connect.IRpcConnection; 6 | import com.lenzhao.framework.exception.RpcException; 7 | import com.lenzhao.framework.protocol.RpcRequest; 8 | import com.lenzhao.framework.protocol.RpcResponse; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | *最终执行的Filter,得到socket长连接并发送请求 14 | */ 15 | public class GenericFilter implements Filter { 16 | 17 | private static final Logger logger = LoggerFactory.getLogger(GenericFilter.class); 18 | 19 | @Override 20 | public RpcResponse sendRequest(RpcRequest request, ILoadBalance loadBlance, String serviceName) throws Throwable { 21 | IRpcConnection connection = null; 22 | RpcResponse response = null; 23 | String connStr = loadBlance.getLoadBalance(serviceName); 24 | ClientConfig clientConfig = ClientConfig.getInstance(); 25 | ServiceConfig serviceConfig = clientConfig.getService(serviceName); 26 | try { 27 | connection = loadBlance.getConnection(connStr); 28 | if(connection.isConnected() && connection.isClosed()) { 29 | connection.connect(); 30 | } 31 | if(connection.isConnected() && !connection.isClosed()) { 32 | response = connection.sendRequest(request, serviceConfig.getAsync()); 33 | } else { 34 | throw new RpcException("send rpc request fail"); 35 | } 36 | return response; 37 | } catch(RpcException e) { 38 | throw e; 39 | } catch (Throwable t) { 40 | logger.warn("send rpc request fail! request: " + request, t); 41 | throw new RpcException(t); 42 | } finally { 43 | loadBlance.finishLoadBalance(serviceName, connStr); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/SchemaCache.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import com.lenzhao.framework.protocol.RpcRequest; 4 | import com.lenzhao.framework.protocol.RpcResponse; 5 | import io.protostuff.Schema; 6 | import io.protostuff.runtime.RuntimeSchema; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | *protostuff的schama缓存 12 | */ 13 | public class SchemaCache { 14 | 15 | private static final Map> SCHEMA_CACHE = new LRUMap>( 16 | 4096); 17 | 18 | @SuppressWarnings("unchecked") 19 | public static Schema getSchema(Class clazz) { 20 | String className = clazz.getName(); 21 | Schema schema = (Schema) SCHEMA_CACHE.get(className); 22 | if (null != schema) { 23 | return schema; 24 | } 25 | synchronized (SCHEMA_CACHE) { 26 | if (null == SCHEMA_CACHE.get(className)) { 27 | schema = RuntimeSchema.getSchema(clazz); 28 | SCHEMA_CACHE.put(className, schema); 29 | return schema; 30 | } else { 31 | return (Schema) SCHEMA_CACHE.get(className); 32 | } 33 | } 34 | } 35 | 36 | public static Schema getSchema(RpcRequest request) { 37 | Schema schema = getSchema(RpcRequest.class); 38 | Object[] parameters = request.getParameters(); 39 | if (null != parameters && parameters.length > 0) { 40 | for (Object param : parameters) { 41 | if (null != param) { 42 | getSchema(param.getClass()); 43 | } 44 | } 45 | } 46 | return schema; 47 | } 48 | 49 | public static Schema getSchema(RpcResponse response) { 50 | Schema schema = getSchema(RpcResponse.class); 51 | if (response.getException() != null) { 52 | getSchema(response.getException().getClass()); 53 | } 54 | if (response.getResult() != null) { 55 | getSchema(response.getResult().getClass()); 56 | } 57 | return schema; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/assembly/bin/dump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd `dirname $0` 3 | BIN_DIR=`pwd` 4 | cd .. 5 | DEPLOY_DIR=`pwd` 6 | CONF_DIR=$DEPLOY_DIR/conf 7 | 8 | SERVER_NAME=`sed '/voyage.application.name/!d;s/.*=//' conf/voyage.properties | tr -d '\r'` 9 | LOGS_FILE=`sed '/voyage.log4j.file/!d;s/.*=//' conf/voyage.properties | tr -d '\r'` 10 | SERVER_NAME=`sed '/voyage.application.name/!d;s/.*=//' conf/voyage.properties | tr -d '\r'` 11 | 12 | if [ -z "$SERVER_NAME" ]; then 13 | SERVER_NAME=`hostname` 14 | fi 15 | 16 | PIDS=`ps -f | grep java | grep "$CONF_DIR" |awk '{print $2}'` 17 | if [ -z "$PIDS" ]; then 18 | echo "ERROR: The $SERVER_NAME does not started!" 19 | exit 1 20 | fi 21 | 22 | LOGS_DIR="" 23 | if [ -n "$LOGS_FILE" ]; then 24 | LOGS_DIR=`dirname $LOGS_FILE` 25 | else 26 | LOGS_DIR=$DEPLOY_DIR/logs 27 | fi 28 | if [ ! -d $LOGS_DIR ]; then 29 | mkdir $LOGS_DIR 30 | fi 31 | DUMP_DIR=$LOGS_DIR/dump 32 | if [ ! -d $DUMP_DIR ]; then 33 | mkdir $DUMP_DIR 34 | fi 35 | DUMP_DATE=`date +%Y%m%d%H%M%S` 36 | DATE_DIR=$DUMP_DIR/$DUMP_DATE 37 | if [ ! -d $DATE_DIR ]; then 38 | mkdir $DATE_DIR 39 | fi 40 | 41 | echo -e "Dumping the $SERVER_NAME ...\c" 42 | for PID in $PIDS ; do 43 | jstack $PID > $DATE_DIR/jstack-$PID.dump 2>&1 44 | echo -e ".\c" 45 | jinfo $PID > $DATE_DIR/jinfo-$PID.dump 2>&1 46 | echo -e ".\c" 47 | jstat -gcutil $PID > $DATE_DIR/jstat-gcutil-$PID.dump 2>&1 48 | echo -e ".\c" 49 | jstat -gccapacity $PID > $DATE_DIR/jstat-gccapacity-$PID.dump 2>&1 50 | echo -e ".\c" 51 | jmap $PID > $DATE_DIR/jmap-$PID.dump 2>&1 52 | echo -e ".\c" 53 | jmap -heap $PID > $DATE_DIR/jmap-heap-$PID.dump 2>&1 54 | echo -e ".\c" 55 | jmap -histo $PID > $DATE_DIR/jmap-histo-$PID.dump 2>&1 56 | echo -e ".\c" 57 | if [ -r /usr/sbin/lsof ]; then 58 | /usr/sbin/lsof -p $PID > $DATE_DIR/lsof-$PID.dump 59 | echo -e ".\c" 60 | fi 61 | done 62 | 63 | if [ -r /bin/netstat ]; then 64 | /bin/netstat -an > $DATE_DIR/netstat.dump 2>&1 65 | echo -e ".\c" 66 | fi 67 | if [ -r /usr/bin/iostat ]; then 68 | /usr/bin/iostat > $DATE_DIR/iostat.dump 2>&1 69 | echo -e ".\c" 70 | fi 71 | if [ -r /usr/bin/mpstat ]; then 72 | /usr/bin/mpstat > $DATE_DIR/mpstat.dump 2>&1 73 | echo -e ".\c" 74 | fi 75 | if [ -r /usr/bin/vmstat ]; then 76 | /usr/bin/vmstat > $DATE_DIR/vmstat.dump 2>&1 77 | echo -e ".\c" 78 | fi 79 | if [ -r /usr/bin/free ]; then 80 | /usr/bin/free -t > $DATE_DIR/free.dump 2>&1 81 | echo -e ".\c" 82 | fi 83 | if [ -r /usr/bin/sar ]; then 84 | /usr/bin/sar > $DATE_DIR/sar.dump 2>&1 85 | echo -e ".\c" 86 | fi 87 | if [ -r /usr/bin/uptime ]; then 88 | /usr/bin/uptime > $DATE_DIR/uptime.dump 2>&1 89 | echo -e ".\c" 90 | fi 91 | 92 | echo "OK!" 93 | echo "DUMP: $DATE_DIR" 94 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/IOUtil.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import java.io.Closeable; 4 | import java.io.EOFException; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.net.Socket; 9 | 10 | /** 11 | *IO util类 12 | */ 13 | public class IOUtil { 14 | 15 | /** 16 | * block until exactly length bytes read into 17 | * bytes 18 | * 19 | * @param in 20 | * @param bytes 21 | * @param offset 22 | * @param length 23 | * @throws java.io.IOException 24 | */ 25 | public static void readFully(InputStream in, byte[] bytes, int offset, 26 | int length) throws IOException { 27 | if (length < 0) { 28 | throw new IndexOutOfBoundsException(); 29 | } 30 | int n = 0; 31 | while (n < length) { 32 | int count = in.read(bytes, offset + n, length - n); 33 | if (count < 0) { 34 | throw new EOFException(); 35 | } 36 | n += count; 37 | } 38 | } 39 | 40 | /** 41 | * write an integer to the output stream 42 | * 43 | * @param out 44 | * @param value 45 | * @throws java.io.IOException 46 | */ 47 | public static void writeInt(OutputStream out, int value) throws IOException { 48 | out.write((value >>> 24) & 0xFF); 49 | out.write((value >>> 16) & 0xFF); 50 | out.write((value >>> 8) & 0xFF); 51 | out.write((value >>> 0) & 0xFF); 52 | } 53 | 54 | /** 55 | * read an integer from the input stream 56 | * 57 | * @param in 58 | * @return 59 | * @throws java.io.IOException 60 | */ 61 | public static int readInt(InputStream in) throws IOException { 62 | int ch1 = in.read(); 63 | int ch2 = in.read(); 64 | int ch3 = in.read(); 65 | int ch4 = in.read(); 66 | if ((ch1 | ch2 | ch3 | ch4) < 0) { 67 | throw new EOFException(); 68 | } 69 | return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); 70 | } 71 | 72 | public static void closeQuietly(Closeable closeable) { 73 | if (null == closeable) { 74 | return; 75 | } 76 | try { 77 | closeable.close(); 78 | } catch (Throwable t) { 79 | } 80 | } 81 | 82 | public static void closeQuietly(Socket socket) { 83 | if (null == socket) { 84 | return; 85 | } 86 | if (!socket.isInputShutdown()) { 87 | try { 88 | socket.shutdownInput(); 89 | } catch (IOException e) { 90 | } 91 | } 92 | if (!socket.isOutputShutdown()) { 93 | try { 94 | socket.shutdownOutput(); 95 | } catch (IOException e) { 96 | } 97 | } 98 | try { 99 | socket.close(); 100 | } catch (Throwable t) { 101 | } 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | /** 7 | *协议请求实体 8 | */ 9 | public class RpcRequest implements Serializable { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | /** 14 | * 请求唯一标示 15 | */ 16 | private String requestID; 17 | 18 | /** 19 | * 接口名 20 | * @eg: com.lenzhao.framework.IServer1 21 | */ 22 | private String interfaceName; 23 | 24 | /** 25 | * 服务名,也就是@ServiceAnnotation的name 26 | * @eg: MyServer1 27 | */ 28 | private String className; 29 | 30 | /** 31 | * 方法名 32 | */ 33 | private String methodName; 34 | 35 | /** 36 | * 请求参数类型 37 | */ 38 | private String[] parameterTypes; 39 | 40 | /** 41 | * 请求参数值 42 | */ 43 | private Object[] parameters; 44 | 45 | /** 46 | * 请求时间 47 | */ 48 | private Date addTime; 49 | 50 | public RpcRequest() { 51 | super(); 52 | } 53 | 54 | public RpcRequest(String requestID, String interfaceName, String className, 55 | String methodName, String[] parameterTypes, Object[] parameters) { 56 | super(); 57 | this.requestID = requestID; 58 | this.interfaceName = interfaceName; 59 | this.className = className; 60 | this.methodName = methodName; 61 | this.parameterTypes = parameterTypes; 62 | this.parameters = parameters; 63 | this.addTime = new Date(); 64 | } 65 | 66 | public String getRequestID() { 67 | return requestID; 68 | } 69 | 70 | public void setRequestID(String requestID) { 71 | this.requestID = requestID; 72 | } 73 | 74 | public String getInterfaceName() { 75 | return interfaceName; 76 | } 77 | 78 | public void setInterfaceName(String interfaceName) { 79 | this.interfaceName = interfaceName; 80 | } 81 | 82 | public String getClassName() { 83 | return className; 84 | } 85 | 86 | public void setClassName(String className) { 87 | this.className = className; 88 | } 89 | 90 | public String getMethodName() { 91 | return methodName; 92 | } 93 | 94 | public void setMethodName(String methodName) { 95 | this.methodName = methodName; 96 | } 97 | 98 | public String[] getParameterTypes() { 99 | return parameterTypes; 100 | } 101 | 102 | public void setParameterTypes(String[] parameterTypes) { 103 | this.parameterTypes = parameterTypes; 104 | } 105 | 106 | public Object[] getParameters() { 107 | return parameters; 108 | } 109 | 110 | public void setParameters(Object[] parameters) { 111 | this.parameters = parameters; 112 | } 113 | 114 | public Date getAddTime() { 115 | return addTime; 116 | } 117 | 118 | public void setAddTime(Date addTime) { 119 | this.addTime = addTime; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/ReflectionCache.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.lang.reflect.Method; 7 | import java.util.Map; 8 | 9 | /** 10 | * 反射缓存 11 | */ 12 | public class ReflectionCache { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(ReflectionCache.class); 15 | 16 | private static final Map> PRIMITIVE_CLASS = new LRUMap>(1024); 17 | 18 | private static final Map> CLASS_CACHE = new LRUMap>(1024); 19 | 20 | private static final Map METHOD_CACHE = new LRUMap(1024); 21 | static { 22 | PRIMITIVE_CLASS.put("boolean", boolean.class); 23 | PRIMITIVE_CLASS.put("byte", byte.class); 24 | PRIMITIVE_CLASS.put("short", short.class); 25 | PRIMITIVE_CLASS.put("int", int.class); 26 | PRIMITIVE_CLASS.put("long", long.class); 27 | PRIMITIVE_CLASS.put("float", float.class); 28 | PRIMITIVE_CLASS.put("double", double.class); 29 | PRIMITIVE_CLASS.put("char", char.class); 30 | PRIMITIVE_CLASS.put("void", void.class); 31 | 32 | CLASS_CACHE.putAll(PRIMITIVE_CLASS); 33 | } 34 | 35 | public static Class getClass(String className) throws ClassNotFoundException { 36 | Class clazz = CLASS_CACHE.get(className); 37 | if (null != clazz) { 38 | return clazz; 39 | } 40 | synchronized (CLASS_CACHE) { 41 | if (null == CLASS_CACHE.get(className)) { 42 | clazz = PRIMITIVE_CLASS.get(className); 43 | if (null == clazz) { 44 | clazz = Class.forName(className); 45 | } 46 | CLASS_CACHE.put(className, clazz); 47 | return clazz; 48 | } else { 49 | return CLASS_CACHE.get(className); 50 | } 51 | } 52 | } 53 | 54 | public static Method getMethod(String className, String methodName, String[] parameterTypes) throws ClassNotFoundException, SecurityException, NoSuchMethodException { 55 | String key = className + "-" + methodName + "-" + join(parameterTypes, ";"); 56 | //logger.info("key: {}", key); 57 | Method method = METHOD_CACHE.get(key); 58 | if (null != method) { 59 | return method; 60 | } 61 | synchronized (METHOD_CACHE) { 62 | if (null == METHOD_CACHE.get(key)) { 63 | Class clazz = getClass(className); 64 | Class[] parameterClasses = new Class[parameterTypes.length]; 65 | for (int i = 0; i < parameterClasses.length; i++) { 66 | parameterClasses[i] = getClass(parameterTypes[i]); 67 | } 68 | 69 | method = clazz.getMethod(methodName, parameterClasses); 70 | METHOD_CACHE.put(key, method); 71 | return method; 72 | } else { 73 | return METHOD_CACHE.get(key); 74 | } 75 | } 76 | } 77 | 78 | private static String join(String[] strs, String seperator) { 79 | if (null == strs || 0 == strs.length) { 80 | return ""; 81 | } 82 | //速度快,线程非安全,替代方案:StringBuffer. 83 | StringBuilder sb = new StringBuilder(1024); 84 | sb.append(strs[0]); 85 | for (int i = 1; i < strs.length; i++) { 86 | sb.append(seperator).append(strs[i]); 87 | } 88 | return sb.toString(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/assembly/bin/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd `dirname $0` 3 | BIN_DIR=`pwd` 4 | cd .. 5 | DEPLOY_DIR=`pwd` 6 | CONF_DIR=$DEPLOY_DIR/conf 7 | 8 | SERVER_PORT=`sed '/voyage.server.port/!d;s/.*=//' conf/voyage.properties | tr -d '\r'` 9 | LOGS_FILE=`sed '/voyage.log4j.file/!d;s/.*=//' conf/voyage.properties | tr -d '\r'` 10 | SERVER_NAME=`sed '/voyage.application.name/!d;s/.*=//' conf/voyage.properties | tr -d '\r'` 11 | 12 | if [ -z "$SERVER_NAME" ]; then 13 | SERVER_NAME=`hostname` 14 | fi 15 | 16 | PIDS=`ps -f | grep java | grep "$CONF_DIR" |awk '{print $2}'` 17 | if [ -n "$PIDS" ]; then 18 | echo "ERROR: The $SERVER_NAME already started!" 19 | echo "PID: $PIDS" 20 | exit 1 21 | fi 22 | 23 | if [ -n "$SERVER_PORT" ]; then 24 | SERVER_PORT_COUNT=`netstat -tln | grep $SERVER_PORT | wc -l` 25 | if [ $SERVER_PORT_COUNT -gt 0 ]; then 26 | echo "ERROR: The $SERVER_NAME port $SERVER_PORT already used!" 27 | exit 1 28 | fi 29 | fi 30 | 31 | LOGS_DIR="" 32 | if [ -n "$LOGS_FILE" ]; then 33 | LOGS_DIR=`dirname $LOGS_FILE` 34 | else 35 | LOGS_DIR=$DEPLOY_DIR/logs 36 | fi 37 | if [ ! -d $LOGS_DIR ]; then 38 | mkdir $LOGS_DIR 39 | fi 40 | STDOUT_FILE=$LOGS_DIR/stdout.log 41 | 42 | LIB_DIR=$DEPLOY_DIR/lib 43 | LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"` 44 | 45 | CLS_DIR=$DEPLOY_DIR/classes 46 | 47 | JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true " 48 | JAVA_DEBUG_OPTS="" 49 | if [ "$1" = "debug" ]; then 50 | JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n " 51 | fi 52 | JAVA_JMX_OPTS="" 53 | if [ "$1" = "jmx" ]; then 54 | JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false " 55 | fi 56 | JAVA_MEM_OPTS="" 57 | BITS=`java -version 2>&1 | grep -i 64-bit` 58 | if [ -n "$BITS" ]; then 59 | JAVA_MEM_OPTS=" -server -Xmx1g -Xms512m -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 " 60 | else 61 | JAVA_MEM_OPTS=" -server -Xms512m -Xmx512m -XX:PermSize=128m -XX:SurvivorRatio=2 -XX:+UseParallelGC " 62 | fi 63 | 64 | echo -e "Starting the $SERVER_NAME ...\c" 65 | nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS -classpath $CLS_DIR:$CONF_DIR:$LIB_JARS com.lenzhao.bootstrap.Main $SERVER_PORT > $STDOUT_FILE 2>&1 & 66 | 67 | COUNT=0 68 | while [ $COUNT -lt 1 ]; do 69 | echo -e ".\c" 70 | sleep 1 71 | if [ -n "$SERVER_PORT" ]; then 72 | COUNT=`netstat -an | grep $SERVER_PORT | wc -l` 73 | else 74 | COUNT=`ps -f | grep java | grep "$DEPLOY_DIR" | awk '{print $2}' | wc -l` 75 | fi 76 | if [ $COUNT -gt 0 ]; then 77 | break 78 | fi 79 | done 80 | 81 | echo "OK!" 82 | PIDS=`ps -f | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'` 83 | echo "PID: $PIDS" 84 | echo "STDOUT: $STDOUT_FILE" 85 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/ProtobufSerializer.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import io.protostuff.LinkedBuffer; 4 | import io.protostuff.ProtobufIOUtil; 5 | import io.protostuff.Schema; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | 11 | import com.lenzhao.framework.util.SchemaCache; 12 | 13 | /** 14 | *Protobuf序列化封装的辅助类 15 | */ 16 | public class ProtobufSerializer implements ISerializer{ 17 | 18 | private static final ProtobufSerializer INSTANCE = new ProtobufSerializer(); 19 | 20 | private ProtobufSerializer() { 21 | } 22 | 23 | public static ISerializer getInstance() { 24 | return INSTANCE; 25 | } 26 | 27 | protected int writeObject(LinkedBuffer buffer, T object, 28 | Schema schema) { 29 | return ProtobufIOUtil.writeTo(buffer, object, schema); 30 | } 31 | 32 | protected void parseObject(byte[] bytes, T template, Schema schema) { 33 | ProtobufIOUtil.mergeFrom(bytes, template, schema); 34 | } 35 | 36 | protected void parseObject(InputStream in, T template, Schema schema) { 37 | try { 38 | ProtobufIOUtil.mergeFrom(in, template, schema); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | 45 | public RpcRequest decodeRequest(InputStream inputStream) 46 | throws IOException { 47 | return decode(inputStream, new RpcRequest()); 48 | } 49 | 50 | public void encodeResponse(OutputStream outputStream, RpcResponse result) 51 | throws IOException { 52 | encode(outputStream, result); 53 | } 54 | 55 | public RpcResponse decodeResponse(InputStream inputStream) 56 | throws IOException { 57 | return decode(inputStream, new RpcResponse()); 58 | } 59 | 60 | public void encodeRequest(OutputStream outputStream, RpcRequest request) 61 | throws IOException { 62 | encode(outputStream, request); 63 | } 64 | 65 | /** 66 | * 将对象序列化为二进制流 67 | * @param out 68 | * @param object 69 | * @throws java.io.IOException 70 | */ 71 | @SuppressWarnings({ "unchecked", "rawtypes" }) 72 | private void encode(OutputStream out, T object) throws IOException { 73 | LinkedBuffer buffer = LinkedBuffer.allocate(); 74 | Schema schema = null; 75 | if (null == object) { 76 | schema = SchemaCache.getSchema(Object.class); 77 | } else { 78 | schema = SchemaCache.getSchema(object.getClass()); 79 | } 80 | int length = writeObject(buffer, object, schema); 81 | // IOUtils.writeInt(out, length); 82 | LinkedBuffer.writeTo(out, buffer); 83 | } 84 | 85 | /** 86 | * 将二进制流解析为对象 87 | * @param in 88 | * @param template 89 | * @return 90 | * @throws java.io.IOException 91 | */ 92 | @SuppressWarnings({ "unchecked", "rawtypes" }) 93 | private T decode(InputStream in, T template) throws IOException { 94 | Schema schema = SchemaCache.getSchema(template.getClass()); 95 | // int length = IOUtils.readInt(in); 96 | // byte[] bytes = new byte[length]; 97 | // IOUtils.readFully(in, bytes, 0, length); 98 | parseObject(in, template, schema); 99 | return template; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/server/NettyRpcServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.server; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import org.jboss.netty.channel.ChannelHandlerContext; 6 | import org.jboss.netty.channel.ChannelStateEvent; 7 | import org.jboss.netty.channel.ExceptionEvent; 8 | import org.jboss.netty.channel.MessageEvent; 9 | import org.jboss.netty.channel.SimpleChannelUpstreamHandler; 10 | import org.jboss.netty.channel.group.ChannelGroup; 11 | 12 | import com.lenzhao.framework.protocol.RpcRequest; 13 | import com.lenzhao.framework.protocol.RpcResponse; 14 | import com.lenzhao.framework.util.ReflectionCache; 15 | import org.jboss.netty.handler.timeout.ReadTimeoutException; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | /** 20 | *RPC具体处理类 21 | */ 22 | public class NettyRpcServerHandler extends SimpleChannelUpstreamHandler { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(NettyRpcServerHandler.class); 25 | 26 | private final ChannelGroup channelGroups; 27 | 28 | public NettyRpcServerHandler() { 29 | this.channelGroups = null; 30 | } 31 | 32 | public NettyRpcServerHandler(ChannelGroup channelGroups) { 33 | this.channelGroups = channelGroups; 34 | } 35 | 36 | @Override 37 | public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) 38 | throws Exception { 39 | if (null != channelGroups) { 40 | channelGroups.add(e.getChannel()); 41 | } 42 | } 43 | 44 | @Override 45 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) 46 | throws Exception { 47 | RpcRequest request = (RpcRequest) ctx.getAttachment(); 48 | if (e.getCause() instanceof ReadTimeoutException) { 49 | // The connection was OK but there was no traffic for last period. 50 | logger.warn("Disconnecting due to no inbound traffic"); 51 | } else { 52 | logger.error("", e); 53 | } 54 | e.getChannel().close().awaitUninterruptibly(); 55 | } 56 | 57 | @Override 58 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { 59 | Object msg = e.getMessage(); 60 | if (!(msg instanceof RpcRequest)) { 61 | logger.error("not RpcRequest received!"); 62 | return; 63 | } 64 | RpcRequest request = (RpcRequest) msg; 65 | ctx.setAttachment(request); 66 | 67 | RpcResponse response = new RpcResponse(request.getRequestID()); 68 | try { 69 | Object result = handle(request); 70 | response.setResult(result); 71 | } catch (Throwable t) { 72 | logger.error("handle rpc request fail! request:"+request, t); 73 | response.setException(t); 74 | } 75 | e.getChannel().write(response); 76 | } 77 | 78 | private Object handle(RpcRequest request) throws Throwable { 79 | String className = request.getClassName(); 80 | Object rpcService = ExtensionLoader.getProxy(className); 81 | if (null == rpcService) { 82 | throw new NullPointerException("server interface config is null"); 83 | } 84 | Method method = ReflectionCache.getMethod(request.getInterfaceName(), request.getMethodName(), request.getParameterTypes()); 85 | Object[] parameters = request.getParameters(); 86 | //invoke 87 | Object result = method.invoke(rpcService, parameters); 88 | return result; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/protocol/InvokeFuture.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.protocol; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import org.jboss.netty.channel.Channel; 9 | import org.jboss.netty.channel.ChannelFuture; 10 | 11 | import com.lenzhao.framework.exception.RpcException; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | *封装请求和响应的桥梁 17 | */ 18 | public class InvokeFuture { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(InvokeFuture.class); 21 | 22 | private Channel channel; 23 | private RpcRequest request; 24 | private RpcResponse response; 25 | 26 | private static Map invokeMap = new ConcurrentHashMap(); 27 | 28 | private CountDownLatch cdl = new CountDownLatch(1); 29 | 30 | /** 31 | * 发送请求 32 | */ 33 | public void send() { 34 | ChannelFuture writeFuture = channel.write(request); 35 | //阻塞等待,若超时则返回已完成和失败 36 | boolean ret = writeFuture.awaitUninterruptibly(1000, TimeUnit.MILLISECONDS); 37 | if (ret && writeFuture.isSuccess()) { 38 | return; 39 | } else if(writeFuture.getCause() != null) { 40 | invokeMap.remove(request.getRequestID()); 41 | throw new RpcException(writeFuture.getCause()); 42 | } else { 43 | invokeMap.remove(request.getRequestID()); 44 | throw new RpcException("sendRequest error"); 45 | } 46 | } 47 | 48 | public RpcResponse get(long awaitTime) { 49 | boolean isOverTime = false; 50 | try { 51 | isOverTime = cdl.await(awaitTime, TimeUnit.MILLISECONDS); 52 | } catch (InterruptedException e) { 53 | invokeMap.remove(request.getRequestID()); 54 | throw new RpcException("InterruptedException", e); 55 | } 56 | if(isOverTime) { 57 | invokeMap.remove(request.getRequestID()); 58 | return response; 59 | } else { 60 | invokeMap.remove(request.getRequestID()); 61 | throw new RpcException("sendRequest overtime"); 62 | } 63 | } 64 | 65 | public boolean isDone() { 66 | return cdl.getCount() == 0; 67 | } 68 | 69 | public void doReceive(RpcResponse response) { 70 | this.response = response; 71 | cdl.countDown(); 72 | } 73 | 74 | public static void receive(Channel channel, RpcResponse response) { 75 | InvokeFuture future = invokeMap.remove(response.getRequestID()); 76 | if(null != future) { 77 | future.doReceive(response); 78 | } else { 79 | throw new RpcException("TimeOut"); 80 | } 81 | } 82 | 83 | public InvokeFuture(Channel channel, RpcRequest request) { 84 | this.channel = channel; 85 | this.request = request; 86 | invokeMap.put(request.getRequestID(), this); 87 | } 88 | 89 | public Channel getChannel() { 90 | return channel; 91 | } 92 | public void setChannel(Channel channel) { 93 | this.channel = channel; 94 | } 95 | public RpcRequest getRequest() { 96 | return request; 97 | } 98 | public void setRequest(RpcRequest request) { 99 | this.request = request; 100 | } 101 | public RpcResponse getResponse() { 102 | return response; 103 | } 104 | public void setResponse(RpcResponse response) { 105 | this.response = response; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/client/RpcClientProxy.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.client; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | import java.lang.reflect.Proxy; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | import com.lenzhao.framework.protocol.RpcResponse; 12 | 13 | import com.lenzhao.framework.protocol.RpcRequest; 14 | import com.lenzhao.framework.util.Sequence; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | *rpc客户端动态代理类,一般为了提高性能会使用javassist、asm等工具直接操作Java字节码 20 | */ 21 | public class RpcClientProxy implements InvocationHandler { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(RpcClientProxy.class); 24 | 25 | //根据serviceName得到是哪个服务 26 | private final String serviceName; 27 | //要调用的接口 28 | private final Class interfaceClass; 29 | //要调用的具体接口实现 30 | private final String className; 31 | //负载均衡策略 32 | private final ILoadBalance loadBlance = new LeastActiveLoadBalance(); 33 | //proxy缓存 34 | private final static Map proxyMap = new ConcurrentHashMap(); 35 | //服务调用链过滤器 36 | private final Filter lastFilter; 37 | 38 | /** 39 | *初始化客户端动态代理类 40 | * @param interfaceClass 接口类 41 | * @param serviceName 服务名 42 | * @param className 实现名 43 | */ 44 | private RpcClientProxy(Class interfaceClass,String serviceName,String className) { 45 | super(); 46 | this.serviceName = serviceName; 47 | this.interfaceClass = interfaceClass; 48 | this.className = className; 49 | GenericFilter genericFilter = new GenericFilter(); 50 | TimeOutFilter timeOutFilter = new TimeOutFilter(genericFilter); 51 | //TPS控制 52 | TpsFilter tpsFilter = new TpsFilter(timeOutFilter); 53 | lastFilter = tpsFilter; 54 | } 55 | 56 | protected static final String generateRequestID() { 57 | return Sequence.next() + ""; 58 | } 59 | 60 | public static T proxy(Class interfaceClass,String serviceName,String className) throws Throwable { 61 | if (!interfaceClass.isInterface()) { 62 | throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface"); 63 | } 64 | String key = interfaceClass.getName() + "_" + serviceName + "_" + className; 65 | Object proxy = proxyMap.get(key); 66 | if(null == proxy) { 67 | proxy = Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { interfaceClass }, new RpcClientProxy(interfaceClass, serviceName, className)); 68 | proxyMap.put(key,proxy); 69 | logger.info("proxy generated,serviceName: {}, className: {}", serviceName, className); 70 | } 71 | return (T)proxy; 72 | } 73 | 74 | @Override 75 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 76 | 77 | String interfaceName = interfaceClass.getName(); 78 | List parameterTypes = new LinkedList(); 79 | for (Class parameterType : method.getParameterTypes()) { 80 | parameterTypes.add(parameterType.getName()); 81 | } 82 | //requestID用来唯一标识一次请求 83 | String requestID = generateRequestID(); 84 | final RpcRequest request = new RpcRequest(requestID, interfaceName, className, method.getName(), parameterTypes.toArray(new String[0]), args); 85 | RpcResponse response = lastFilter.sendRequest(request, loadBlance, serviceName); 86 | return response.getResult(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/server/ExtensionLoader.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.server; 2 | 3 | import java.util.HashSet; 4 | import java.util.List; 5 | import java.util.Set; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.ConcurrentMap; 8 | 9 | import com.lenzhao.framework.annotation.ServiceAnnotation; 10 | import com.lenzhao.framework.util.ClassUtil; 11 | import com.lenzhao.framework.util.FileUtil; 12 | 13 | import com.lenzhao.framework.common.Constants; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | /** 18 | *加载所有的服务(重点) 19 | */ 20 | public class ExtensionLoader { 21 | 22 | private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class); 23 | 24 | //服务实体缓存 25 | private static final ConcurrentMap cachedInstances = new ConcurrentHashMap(); 26 | 27 | /** 28 | * 标示是否已经初始化 29 | */ 30 | private static volatile boolean isInit = false; 31 | 32 | public static void init() { 33 | init(null); 34 | } 35 | 36 | public static void init(String path) { 37 | if(!isInit) { 38 | isInit = true; 39 | //logger.info("init root path: {}", path); 40 | try { 41 | scan(path); 42 | } catch (Exception e) { 43 | logger.error("init error", e); 44 | } 45 | } 46 | else { 47 | logger.warn("the scan have already inited"); 48 | } 49 | } 50 | 51 | public static Object getProxy(String serviceName) { 52 | if(!isInit) { 53 | init(); 54 | } 55 | return cachedInstances.get(serviceName); 56 | } 57 | 58 | /** 59 | * scan jars create ContractInfo 60 | * @param path 61 | * @return 62 | * @throws Exception 63 | */ 64 | private static void scan(String path) throws Exception { 65 | //logger.info("begin scan jar from path: {}", path); 66 | List jarPathList = null; 67 | if(null == path) { 68 | String classpath = System.getProperty("java.class.path"); 69 | //logger.info("java.class.path {}", classpath); 70 | String[] paths = classpath.split(Constants.PATH_SEPARATOR); 71 | jarPathList = FileUtil.getFirstPath(paths); 72 | } 73 | else { 74 | jarPathList = FileUtil.getFirstPath(path); 75 | } 76 | if(null == jarPathList || jarPathList.size() < 1) { 77 | throw new Exception("no jar fonded from path: " + path); 78 | } 79 | 80 | for (String jpath : jarPathList) { 81 | Set clsSet = new HashSet(); 82 | if(jpath.endsWith(".class")) { 83 | // 添加到classes 84 | String className = jpath.substring(0,jpath.length()-6).replace(Constants.FILE_SEPARATOR, "."); 85 | try { 86 | //here is SystemClassLoader. 87 | Class c = Thread.currentThread().getContextClassLoader().loadClass(className); 88 | clsSet.add(c); 89 | } 90 | catch(Exception e) { 91 | logger.error("class not found,class:"+jpath, e); 92 | } 93 | } else { 94 | try { 95 | clsSet = ClassUtil.getClassFromJar(jpath); 96 | } catch (Exception ex) { 97 | logger.error("getClassFromJar", ex); 98 | } 99 | } 100 | if (clsSet == null) { 101 | continue; 102 | } 103 | 104 | for (Class cls : clsSet) { 105 | try { 106 | ServiceAnnotation behavior = cls.getAnnotation(ServiceAnnotation.class); 107 | if(behavior != null) { 108 | Object instance = cls.newInstance(); 109 | String serviceName = behavior.name(); 110 | if(null == serviceName || "".equals(serviceName)) { 111 | serviceName = cls.getSimpleName(); 112 | } 113 | cachedInstances.put(serviceName, instance); 114 | } 115 | } catch (Exception ex) { 116 | logger.error("", ex); 117 | } 118 | } 119 | } 120 | //logger.info("finish scan jar: {}", cachedInstances.size()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/config/ClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.config; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import org.dom4j.Document; 10 | import org.dom4j.Element; 11 | import org.dom4j.io.SAXReader; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | *客户端配置参数,包括ip,端口,服务名,接口等信息 17 | */ 18 | public class ClientConfig { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(ClientConfig.class); 21 | 22 | private int maxThreadCount = Runtime.getRuntime().availableProcessors(); 23 | 24 | private int readTimeout = 0; 25 | 26 | private String tcpNoDelay = "true"; 27 | 28 | private String reuseAddress = "true"; 29 | 30 | private static ClientConfig clientConfig = new ClientConfig(); 31 | 32 | private Map services = null; 33 | 34 | private ClientConfig() { 35 | 36 | } 37 | 38 | public static ClientConfig getInstance() { 39 | return clientConfig; 40 | } 41 | 42 | public ServiceConfig getService(String name) { 43 | return services.get(name); 44 | } 45 | 46 | private volatile boolean isInit = false; 47 | //非静态代码块 48 | { 49 | if(!isInit) { 50 | this.root = getRoot(); 51 | services= this.parseService(); 52 | isInit = true; 53 | } 54 | } 55 | 56 | private Document document = null; 57 | 58 | private Element root = null; 59 | 60 | private Element getRoot() { 61 | Document doc = getDocument(); 62 | List list = doc.selectNodes("//application"); 63 | if (list.size() > 0) { 64 | Element aroot = list.get(0); 65 | return aroot; 66 | } 67 | return null; 68 | } 69 | 70 | private InputStream getFileStream(String fileName) { 71 | InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); 72 | return is; 73 | } 74 | 75 | private Document getDocument() { 76 | InputStream is = getFileStream("config.xml"); 77 | try { 78 | if (document == null) { 79 | SAXReader sr = new SAXReader(); 80 | sr.setValidation(false); 81 | if (is == null) { 82 | throw new RuntimeException("can not find config file..."); 83 | } 84 | document = sr.read(is); 85 | } 86 | } catch (Exception e) { 87 | logger.error(e.getMessage(), e); 88 | throw new RuntimeException("get xml file failed"); 89 | } finally { 90 | if (is != null) 91 | try { 92 | is.close(); 93 | } catch (IOException e) { 94 | e.printStackTrace(); 95 | } 96 | } 97 | return document; 98 | } 99 | 100 | public Map parseService() { 101 | 102 | Map result = new HashMap(); 103 | List serviceList = root.selectNodes("//service"); 104 | for (Element serviceNode : serviceList) { 105 | String name = serviceNode.attributeValue("name");// service name 106 | String connectStr = serviceNode.attributeValue("connectStr"); 107 | String maxConnection = serviceNode.attributeValue("maxConnection"); 108 | String async = serviceNode.attributeValue("async"); 109 | 110 | if (name.equals("")) { 111 | logger.warn("configFile :a rpcservice's name is empty."); 112 | continue; 113 | } 114 | if (connectStr.equals("")) { 115 | logger.warn("configFile:rpcservice[" + name 116 | + "] has an empty interface configure."); 117 | continue; 118 | } 119 | ServiceConfig service = new ServiceConfig(name,connectStr,maxConnection,async); 120 | result.put(name, service); 121 | } 122 | return result; 123 | } 124 | 125 | public int getMaxThreadCount() { 126 | return maxThreadCount; 127 | } 128 | 129 | public void setMaxThreadCount(int maxThreadCount) { 130 | this.maxThreadCount = maxThreadCount; 131 | } 132 | 133 | public String getTcpNoDelay() { 134 | return tcpNoDelay; 135 | } 136 | 137 | public void setTcpNoDelay(String tcpNoDelay) { 138 | this.tcpNoDelay = tcpNoDelay; 139 | } 140 | 141 | public String getReuseAddress() { 142 | return reuseAddress; 143 | } 144 | 145 | public void setReuseAddress(String reuseAddress) { 146 | this.reuseAddress = reuseAddress; 147 | } 148 | 149 | public int getReadTimeout() { 150 | return readTimeout; 151 | } 152 | 153 | public void setReadTimeout(int readTimeout) { 154 | this.readTimeout = readTimeout; 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/server/RpcServerBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.server; 2 | 3 | import java.io.IOException; 4 | import java.net.DatagramSocket; 5 | import java.net.InetSocketAddress; 6 | import java.net.ServerSocket; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | 11 | import com.lenzhao.framework.config.ServerConfig; 12 | import com.lenzhao.framework.protocol.RpcRequestDecode; 13 | import org.jboss.netty.bootstrap.ServerBootstrap; 14 | import org.jboss.netty.channel.Channel; 15 | import org.jboss.netty.channel.ChannelPipeline; 16 | import org.jboss.netty.channel.ChannelPipelineFactory; 17 | import org.jboss.netty.channel.Channels; 18 | import org.jboss.netty.channel.group.ChannelGroup; 19 | import org.jboss.netty.channel.group.ChannelGroupFuture; 20 | import org.jboss.netty.channel.group.DefaultChannelGroup; 21 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 22 | import org.jboss.netty.handler.timeout.ReadTimeoutHandler; 23 | import org.jboss.netty.util.HashedWheelTimer; 24 | import org.jboss.netty.util.Timer; 25 | 26 | import com.lenzhao.framework.protocol.RpcResponseEncode; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | /** 31 | *rpc服务实现类,在此开启长连接 32 | */ 33 | public class RpcServerBootstrap implements IRpcServer { 34 | 35 | private static final Logger logger = LoggerFactory.getLogger(RpcServerBootstrap.class); 36 | 37 | private ServerBootstrap bootstrap = null; 38 | 39 | private AtomicBoolean stopped = new AtomicBoolean(false); 40 | 41 | //处理超时事件 42 | private Timer timer=null; 43 | 44 | private void initHttpBootstrap(int myport) { 45 | logger.info("initHttpBootstrap..........."); 46 | final ServerConfig serverConfig = new ServerConfig(myport); 47 | final ChannelGroup channelGroup = new DefaultChannelGroup(getClass().getName()); 48 | bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory( 49 | //建议用ThreadPoolExecutor代替 50 | Executors.newCachedThreadPool(), 51 | Executors.newCachedThreadPool(), serverConfig.getThreadCnt())); 52 | //设置常见参数 53 | bootstrap.setOption("tcpNoDelay","true");//禁用nagle算法 54 | bootstrap.setOption("reuseAddress", "true"); 55 | bootstrap.setOption("SO_RCVBUF",1024*128); 56 | bootstrap.setOption("SO_SNDBUF",1024*128); 57 | timer = new HashedWheelTimer(); 58 | bootstrap.setPipelineFactory(new ChannelPipelineFactory() { 59 | public ChannelPipeline getPipeline() throws Exception { 60 | ChannelPipeline pipeline = Channels.pipeline(); 61 | int readTimeout = serverConfig.getReadTimeout(); 62 | if (readTimeout > 0) { 63 | pipeline.addLast("timeout", new ReadTimeoutHandler(timer, readTimeout, TimeUnit.MILLISECONDS)); 64 | } 65 | pipeline.addLast("decoder", new RpcRequestDecode()); 66 | pipeline.addLast("encoder", new RpcResponseEncode()); 67 | pipeline.addLast("handler", new NettyRpcServerHandler(channelGroup)); 68 | return pipeline; 69 | } 70 | }); 71 | 72 | int port = serverConfig.getPort(); 73 | if (!checkPortConfig(port)) { 74 | throw new IllegalStateException("port: " + port + " already in use!"); 75 | } 76 | 77 | Channel channel = bootstrap.bind(new InetSocketAddress(port)); 78 | channelGroup.add(channel); 79 | logger.info("voyage server started"); 80 | 81 | waitForShutdownCommand(); 82 | ChannelGroupFuture future = channelGroup.close(); 83 | future.awaitUninterruptibly(); 84 | bootstrap.releaseExternalResources(); 85 | timer.stop(); 86 | timer = null; 87 | 88 | logger.info("voyage server stoped"); 89 | 90 | } 91 | 92 | public void start(int port) { 93 | ExtensionLoader.init(); 94 | initHttpBootstrap(port); 95 | } 96 | 97 | public void stop() { 98 | stopped.set(true); 99 | synchronized (stopped) { 100 | stopped.notifyAll(); 101 | } 102 | } 103 | 104 | private void waitForShutdownCommand() { 105 | synchronized (stopped) { 106 | while (!stopped.get()) { 107 | try { 108 | stopped.wait(); 109 | } catch (InterruptedException e) { 110 | } 111 | } 112 | } 113 | } 114 | 115 | private boolean checkPortConfig(int listenPort) { 116 | if (listenPort < 0 || listenPort > 65536) { 117 | throw new IllegalArgumentException("Invalid start port: " + listenPort); 118 | } 119 | ServerSocket ss = null; 120 | DatagramSocket ds = null; 121 | try { 122 | ss = new ServerSocket(listenPort); 123 | ss.setReuseAddress(true); 124 | ds = new DatagramSocket(listenPort); 125 | ds.setReuseAddress(true); 126 | return true; 127 | } catch (IOException e) { 128 | } finally { 129 | if (ds != null) { 130 | ds.close(); 131 | } 132 | if (ss != null) { 133 | try { 134 | ss.close(); 135 | } catch (IOException e) { 136 | } 137 | } 138 | } 139 | return false; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/client/LeastActiveLoadBalance.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Random; 7 | import java.util.Map.Entry; 8 | import java.util.TreeMap; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | import java.util.concurrent.locks.ReadWriteLock; 12 | import java.util.concurrent.locks.ReentrantReadWriteLock; 13 | 14 | import com.lenzhao.framework.common.Constants; 15 | import com.lenzhao.framework.config.ServiceConfig; 16 | import com.lenzhao.framework.connect.IRpcConnection; 17 | import com.lenzhao.framework.connect.NettyRpcConnection; 18 | 19 | import com.lenzhao.framework.config.ClientConfig; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | /** 24 | *最少活跃调用数(LRU),相同活跃数的随机,活跃数指调用前后计数差。 25 | *使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。 26 | *常见的负载均衡策略有很多,比如:随机,轮循,最少活跃调用等,开发者可以自己扩展实现 27 | */ 28 | public class LeastActiveLoadBalance implements ILoadBalance { 29 | 30 | private static final Logger logger = LoggerFactory.getLogger(LeastActiveLoadBalance.class); 31 | //当前并发量 32 | private final AtomicInteger curTotalCount = new AtomicInteger(0); 33 | 34 | private final static Random RANDOM_NUM = new Random(); 35 | 36 | //修改curTotalMap需要加锁 37 | private final ReadWriteLock lockTotalMap = new ReentrantReadWriteLock(); 38 | 39 | private final ReadWriteLock lockConnectionMap = new ReentrantReadWriteLock(); 40 | 41 | //key代表serviceName,value Map中的key代表连接串,value代表该连接的当前并发数 42 | private final Map> curTotalMap = new ConcurrentHashMap>(); 43 | 44 | //长连接池 45 | private final Map connectionMap = new ConcurrentHashMap(); 46 | 47 | public int getCurTotalCount() { 48 | return curTotalCount.get(); 49 | } 50 | 51 | public IRpcConnection getConnection(String conn) { 52 | IRpcConnection connection = connectionMap.get(conn); 53 | if (null == connection) { 54 | try { 55 | lockConnectionMap.writeLock().lock(); 56 | connection = connectionMap.get(conn); 57 | //双重检查,避免重复创建连接 58 | if(null == connection) { 59 | try { 60 | connection = new NettyRpcConnection(conn); 61 | connection.open(); 62 | connection.connect(); 63 | connectionMap.put(conn, connection); 64 | } catch(Throwable e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | } finally { 69 | lockConnectionMap.writeLock().unlock(); 70 | } 71 | } 72 | return connection; 73 | } 74 | 75 | @Override 76 | public String getLoadBalance(String serviceName) { 77 | if(null == curTotalMap.get(serviceName)) { 78 | lockTotalMap.writeLock().lock(); 79 | try { 80 | if(null == curTotalMap.get(serviceName)) { 81 | ClientConfig clientConfig = ClientConfig.getInstance(); 82 | ServiceConfig serviceConfig = clientConfig.getService(serviceName); 83 | Map map = new ConcurrentHashMap(); 84 | String[] connStrs = serviceConfig.getConnectStr().split(Constants.SERVER_CONNECT_SEPARATOR); 85 | for(String connStr : connStrs) { 86 | map.put(connStr,new AtomicInteger(0)); 87 | } 88 | //service1 => {"127.0.0.1:8080":0, "127.0.0.1:8081":0} 89 | curTotalMap.put(serviceName, map); 90 | } 91 | } finally { 92 | lockTotalMap.writeLock().unlock(); 93 | } 94 | } 95 | //获取最优方案 96 | String connStr = getMin(curTotalMap.get(serviceName)); 97 | if(null != connStr) { 98 | curTotalCount.incrementAndGet(); 99 | } else { 100 | throw new RuntimeException("the service have no alive connection,service: " + serviceName); 101 | } 102 | return connStr; 103 | } 104 | 105 | @Override 106 | public void finishLoadBalance(String serviceName,String connStr) { 107 | curTotalCount.decrementAndGet(); 108 | curTotalMap.get(serviceName).get(connStr).decrementAndGet(); 109 | } 110 | 111 | /** 112 | * 得到存活的并且tps最少的连接 113 | * @param map 114 | * @return 115 | */ 116 | private String getMin(Map map) { 117 | if(map.size() <= 0) { 118 | return null; 119 | } 120 | String result = null; 121 | TreeMap sortedMap = new TreeMap(); 122 | List zeroResults = new ArrayList(); 123 | for(Entry entry : map.entrySet()) { 124 | IRpcConnection connection = connectionMap.get(entry.getKey()); 125 | if(null == connection || (connection != null && connection.isConnected())) { 126 | int cnt = entry.getValue().get(); 127 | if(cnt == 0) { 128 | String tmpResult = entry.getKey(); 129 | zeroResults.add(tmpResult); 130 | } 131 | else { 132 | sortedMap.put(entry.getValue().get(), entry.getKey()); 133 | } 134 | } 135 | } 136 | int zsize = zeroResults.size(); 137 | if(zsize > 0) { 138 | if(zsize == 1) { 139 | result=zeroResults.get(0); 140 | } else { 141 | result = zeroResults.get(RANDOM_NUM.nextInt(zsize)); 142 | } 143 | return result; 144 | } else if(sortedMap.size() >= 1) { 145 | result = sortedMap.firstEntry().getValue(); 146 | } else { 147 | return null; 148 | } 149 | int lessCnt = map.get(result).incrementAndGet(); 150 | int totalCnt = curTotalCount.get(); 151 | // 152 | if (totalCnt >= Constants.TOTAL_CONCURRENT_ALARAM_NUM) { 153 | logger.warn("the concurrent connection: {},lessCnt: {},totalCnt: {}", result, lessCnt, totalCnt); 154 | } 155 | //127.0.0.1:8080 156 | return result; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/ClassUtil.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.Enumeration; 8 | import java.util.LinkedHashSet; 9 | import java.util.Set; 10 | import java.util.jar.JarEntry; 11 | import java.util.jar.JarFile; 12 | 13 | /** 14 | *util类 15 | */ 16 | public class ClassUtil { 17 | 18 | public static Set getClassFromJar(String jarPath) throws IOException, ClassNotFoundException { 19 | JarFile jarFile = new JarFile(jarPath); // 读入jar文件 20 | return getClassFromJar(jarFile, "", ""); 21 | } 22 | 23 | public static Set getClassFromJar(String jarPath, String keyword) throws IOException, ClassNotFoundException { 24 | JarFile jarFile = new JarFile(jarPath); // 读入jar文件 25 | return getClassFromJar(jarFile, keyword, ""); 26 | } 27 | 28 | public static Set getClassFromJar(JarFile jarFile, String keyword, String basePakage) throws IOException { 29 | Boolean recursive = true;//是否递归 30 | String packageName = basePakage; 31 | String packageDirName = basePakage.replace('.', '/'); 32 | Enumeration entries = jarFile.entries(); 33 | Set classes = new LinkedHashSet(); 34 | while (entries.hasMoreElements()) { 35 | try { 36 | // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 37 | JarEntry entry = entries.nextElement(); 38 | String name = entry.getName(); 39 | // 如果是以/开头的 40 | if (name.charAt(0) == '/') { 41 | // 获取后面的字符串 42 | name = name.substring(1); 43 | } 44 | 45 | // 如果前半部分和定义的包名相同 46 | if (name.startsWith(packageDirName)) { 47 | int idx = name.lastIndexOf('/'); 48 | // 如果以"/"结尾 是一个包 49 | if (idx != -1) { 50 | // 获取包名 把"/"替换成"." 51 | packageName = name.substring(0, idx).replace('/', '.'); 52 | } 53 | // 如果可以迭代下去 并且是一个包 54 | if ((idx != -1) || recursive) { 55 | // 如果是一个.class文件 而且不是目录 56 | if (name.endsWith(".class") 57 | && !entry.isDirectory()) { 58 | //检测entry是否符合要求 59 | if (!ClassUtil.checkJarEntry(jarFile, entry, keyword)) { 60 | continue; 61 | } 62 | // 去掉后面的".class" 获取真正的类名 63 | String className = name.substring( 64 | packageName.length() + 1, name.length() - 6); 65 | try { 66 | // 添加到classes 67 | Class c = Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className); 68 | classes.add(c); 69 | } catch (ClassNotFoundException e) { 70 | e.printStackTrace(); 71 | } catch (NoClassDefFoundError e) { 72 | e.printStackTrace(); 73 | } 74 | } 75 | } 76 | } 77 | } catch (Throwable e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | return classes; 82 | } 83 | 84 | public static boolean checkJarEntry(JarFile jarFile, JarEntry entry, String keyWord) throws IOException { 85 | if (keyWord == null || keyWord.equals("")) { 86 | return true; 87 | } 88 | InputStream input = null; 89 | InputStreamReader isr = null; 90 | BufferedReader reader = null; 91 | 92 | try { 93 | input = jarFile.getInputStream(entry); 94 | isr = new InputStreamReader(input); 95 | reader = new BufferedReader(isr); 96 | StringBuffer sb = new StringBuffer(); 97 | boolean result = false; 98 | while (true) { 99 | String line = reader.readLine(); 100 | if (line == null) { 101 | break; 102 | } 103 | sb.append(line); 104 | if (sb.indexOf(keyWord) > -1) { 105 | result = true; 106 | } 107 | } 108 | return result; 109 | } finally { 110 | if (input != null) { 111 | input.close(); 112 | } 113 | if (isr != null) { 114 | isr.close(); 115 | } 116 | if (reader != null) { 117 | reader.close(); 118 | } 119 | } 120 | } 121 | 122 | 123 | public static Class getClassForName(String name) throws ClassNotFoundException { 124 | if (name.equals("boolean")) { 125 | return Boolean.class; 126 | } else if (name.equals("char")) { 127 | return Character.class; 128 | } else if (name.equals("byte")) { 129 | return Byte.class; 130 | } else if (name.equals("short")) { 131 | return Short.class; 132 | } else if (name.equals("int")) { 133 | return Integer.class; 134 | } else if (name.equals("long")) { 135 | return Long.class; 136 | } else if (name.equals("float")) { 137 | return Float.class; 138 | } else if (name.equals("double")) { 139 | return Double.class; 140 | } else { 141 | return Class.forName(name); 142 | } 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | import java.io.FileWriter; 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import com.lenzhao.framework.common.Constants; 12 | 13 | /** 14 | *文件util类 15 | */ 16 | public class FileUtil { 17 | 18 | 19 | /** 20 | * create file 21 | * @param filePath 22 | * @param content 23 | * @throws java.io.IOException 24 | */ 25 | public static void createFile(String filePath, String content) throws IOException { 26 | FileWriter writer = null; 27 | try { 28 | writer = new FileWriter(filePath); 29 | writer.write(content); 30 | } catch(IOException ex) { 31 | throw ex; 32 | } finally { 33 | if(writer != null) { 34 | writer.close(); 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * create multilevel folder 41 | * @param path 42 | */ 43 | public static void createFolder(String path){ 44 | File file = new File(path); 45 | file.mkdirs(); 46 | } 47 | 48 | /** 49 | * move file 50 | * @param oldPath 51 | * @param newPath 52 | */ 53 | public static void moveFile(String oldPath, String newPath) { 54 | File fileOld = new File(oldPath); 55 | if(fileOld.exists()) { 56 | File fileNew = new File(newPath); 57 | fileOld.renameTo(fileNew); 58 | } 59 | } 60 | 61 | /** 62 | * delete file 63 | * @param path 64 | */ 65 | public static void deleteFile(String path) { 66 | File file = new File(path); 67 | file.deleteOnExit(); 68 | } 69 | 70 | /** 71 | * get all file which in dir 72 | * @param dir 73 | * @param extension 74 | * @return 75 | */ 76 | public static List getFiles(String dir, String... extension) { 77 | File f = new File(dir); 78 | List fileList = new ArrayList(); 79 | if (!f.isDirectory()) { 80 | return null; 81 | } else { 82 | getFiles(f, fileList, extension); 83 | } 84 | return fileList; 85 | } 86 | 87 | /** 88 | * 得到第一层目录结构中的jar包,得到所有的class文件 89 | * @param dirs 90 | * @return 91 | * @throws java.io.IOException 92 | */ 93 | public static List getFirstPath(String... dirs) throws IOException{ 94 | 95 | List jarList = new ArrayList(); 96 | List fileNameList = new ArrayList(); 97 | 98 | for(String dir : dirs) { 99 | List fileList = FileUtil.getFiles(dir, "rar", "jar", "war", "ear","class"); 100 | if(fileList != null) { 101 | for(File file : fileList) { 102 | if(!fileNameList.contains(file.getName()) && file.getCanonicalPath().startsWith(Constants.ROOT_PATH)) { 103 | if(file.getName().endsWith(".class")) { 104 | jarList.add(file.getCanonicalPath().substring(dir.length()+1)); 105 | fileNameList.add(file.getName()); 106 | } 107 | else { 108 | jarList.add(file.getCanonicalPath()); 109 | fileNameList.add(file.getName()); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | return jarList; 116 | } 117 | 118 | 119 | /** 120 | * get all jar/war/ear which in dir 121 | * @param dirs 122 | * @return 123 | * @throws java.io.IOException 124 | */ 125 | public static List getUniqueLibPath(String... dirs) throws IOException{ 126 | 127 | List jarList = new ArrayList(); 128 | List fileNameList = new ArrayList(); 129 | 130 | for(String dir : dirs) { 131 | List fileList = FileUtil.getFiles(dir, "rar", "jar", "war", "ear","class"); 132 | if(fileList != null) { 133 | for(File file : fileList) { 134 | if(!fileNameList.contains(file.getName())) { 135 | jarList.add(file.getCanonicalPath()); 136 | fileNameList.add(file.getName()); 137 | } 138 | } 139 | } 140 | } 141 | 142 | return jarList; 143 | } 144 | 145 | /** 146 | * 147 | * @param file 148 | * @param fileList 149 | * @param extension 150 | */ 151 | private static void getFiles(File f, List fileList, String... extension) { 152 | File[] files = f.listFiles(); 153 | if(files==null) 154 | return; 155 | for (int i = 0; i < files.length; i++) { 156 | if (files[i].isDirectory()) { 157 | getFiles(files[i], fileList, extension); 158 | } else if (files[i].isFile()) { 159 | String fileName = files[i].getName().toLowerCase(); 160 | boolean isAdd = false; 161 | if(extension != null) { 162 | for(String ext : extension) { 163 | if (fileName.lastIndexOf(ext) == fileName.length() - ext.length()){ 164 | isAdd = true; 165 | break; 166 | } 167 | } 168 | } 169 | 170 | if(isAdd) { 171 | fileList.add(files[i]); 172 | } 173 | } 174 | } 175 | } 176 | 177 | /** 178 | * 按行读取文件 179 | * @param path 180 | * @throws java.io.IOException 181 | */ 182 | public static String getContentByLines(String path) throws IOException { 183 | File file = new File(path); 184 | if(!file.exists()) { 185 | throw new IOException("file not exist:" + path); 186 | } 187 | BufferedReader reader = null; 188 | StringBuilder sbContent = new StringBuilder(); 189 | try { 190 | reader = new BufferedReader(new FileReader(file)); 191 | String line = null; 192 | while ((line = reader.readLine()) != null) { 193 | sbContent.append(line); 194 | } 195 | } catch (IOException e) { 196 | e.printStackTrace(); 197 | } finally { 198 | if (reader != null) { 199 | try { 200 | reader.close(); 201 | } catch (IOException e) { 202 | e.printStackTrace(); 203 | } 204 | } 205 | } 206 | 207 | return sbContent.toString(); 208 | } 209 | 210 | 211 | } 212 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.lenzhao 8 | voyage-framework 9 | 0.0.1 10 | jar 11 | Voyage RPC 12 | Voyage RPC 13 | https://github.com/zhaoshiling1017/voyage 14 | 15 | 16 | 17 | ossrh 18 | https://oss.sonatype.org/content/repositories/snapshots 19 | 20 | 21 | ossrh 22 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 23 | 24 | 25 | 26 | 27 | 28 | lenzhao 29 | lenzhao@yahoo.com 30 | lenzhao 31 | https://www.lenzhao.com 32 | 33 | 34 | 35 | 36 | scm:git:https://github.com/zhaoshiling1017/voyage.git 37 | https://github.com/zhaoshiling1017/voyage 38 | scm:git:https://github.com/zhaoshiling1017/voyage.git 39 | 40 | 41 | 42 | 43 | The Apache License, Version 2.0 44 | http://www.apache.org/licenses/LICENSE-2.0.txt 45 | 46 | 47 | 48 | 49 | 1.7.7 50 | 3.9.9.Final 51 | 1.3.5 52 | 4.0.38 53 | 1.6.1 54 | 1.1.6 55 | 4.9 56 | UTF-8 57 | UTF-8 58 | 59 | 60 | 61 | 62 | io.netty 63 | netty 64 | ${netty.version} 65 | 66 | 67 | 68 | 69 | io.protostuff 70 | protostuff-core 71 | ${protostuff.version} 72 | 73 | 74 | 75 | io.protostuff 76 | protostuff-runtime 77 | ${protostuff.version} 78 | 79 | 80 | 81 | com.caucho 82 | hessian 83 | ${hessian.version} 84 | 85 | 86 | 87 | dom4j 88 | dom4j 89 | ${dom4j.version} 90 | 91 | 92 | 93 | jaxen 94 | jaxen 95 | ${jaxen.version} 96 | 97 | 98 | 99 | org.slf4j 100 | slf4j-api 101 | ${slf4j.version} 102 | 103 | 104 | 105 | org.slf4j 106 | jcl-over-slf4j 107 | ${slf4j.version} 108 | 109 | 110 | org.slf4j 111 | slf4j-log4j12 112 | ${slf4j.version} 113 | 114 | 115 | 116 | junit 117 | junit 118 | ${junit.version} 119 | test 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-source-plugin 129 | 2.2.1 130 | 131 | 132 | attach-sources 133 | 134 | jar-no-fork 135 | 136 | 137 | 138 | 139 | 140 | org.apache.maven.plugins 141 | maven-javadoc-plugin 142 | 2.9.1 143 | 144 | 145 | attach-javadocs 146 | 147 | jar 148 | 149 | 150 | 151 | 152 | 153 | org.apache.maven.plugins 154 | maven-gpg-plugin 155 | 1.5 156 | 157 | 158 | sign-artifacts 159 | verify 160 | 161 | sign 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/java/com/lenzhao/framework/connect/NettyRpcConnection.java: -------------------------------------------------------------------------------- 1 | package com.lenzhao.framework.connect; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.ScheduledThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.atomic.AtomicBoolean; 8 | 9 | import com.lenzhao.framework.common.RpcContext; 10 | import com.lenzhao.framework.protocol.RpcResponse; 11 | import org.jboss.netty.bootstrap.ClientBootstrap; 12 | import org.jboss.netty.channel.Channel; 13 | import org.jboss.netty.channel.ChannelFactory; 14 | import org.jboss.netty.channel.ChannelFuture; 15 | import org.jboss.netty.channel.ChannelHandlerContext; 16 | import org.jboss.netty.channel.ChannelPipeline; 17 | import org.jboss.netty.channel.ChannelPipelineFactory; 18 | import org.jboss.netty.channel.Channels; 19 | import org.jboss.netty.channel.ExceptionEvent; 20 | import org.jboss.netty.channel.MessageEvent; 21 | import org.jboss.netty.channel.SimpleChannelHandler; 22 | import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; 23 | 24 | import com.lenzhao.framework.common.Constants; 25 | import com.lenzhao.framework.config.ClientConfig; 26 | import com.lenzhao.framework.exception.RpcException; 27 | import com.lenzhao.framework.protocol.FutureAdapter; 28 | import com.lenzhao.framework.protocol.InvokeFuture; 29 | import com.lenzhao.framework.protocol.RpcRequest; 30 | import com.lenzhao.framework.protocol.RpcRequestEncode; 31 | import com.lenzhao.framework.protocol.RpcResponseDecode; 32 | import com.lenzhao.framework.util.NamedTheadFactory; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | /** 37 | *netty客户端长连接 38 | */ 39 | public class NettyRpcConnection extends SimpleChannelHandler implements IRpcConnection { 40 | 41 | private static final Logger logger = LoggerFactory.getLogger(NettyRpcConnection.class); 42 | 43 | private static final ScheduledThreadPoolExecutor executorService = new ScheduledThreadPoolExecutor(1, new NamedTheadFactory("ConnectionHeart")); 44 | 45 | private volatile long lastConnectedTime = System.currentTimeMillis(); 46 | 47 | private InetSocketAddress inetAddr; 48 | 49 | private volatile Channel channel; 50 | 51 | //是否已经连接的标示,初始化打开和周期检测时会设置该标示 52 | private volatile AtomicBoolean connected = new AtomicBoolean(false); 53 | //客户端配置文件 54 | private static final ClientConfig clientConfig = ClientConfig.getInstance(); 55 | 56 | private ClientBootstrap bootstrap = null; 57 | 58 | //处理超时事件 59 | // private Timer timer=null; 60 | 61 | private static final ChannelFactory factory = new NioClientSocketChannelFactory( 62 | Executors.newCachedThreadPool(), 63 | Executors.newCachedThreadPool(),clientConfig.getMaxThreadCount()); 64 | 65 | public NettyRpcConnection(String connStr) { 66 | this.inetAddr = new InetSocketAddress(connStr.split(":")[0],Integer.parseInt(connStr.split(":")[1])); 67 | initReconnect(); 68 | } 69 | 70 | public NettyRpcConnection(String host, int port) { 71 | this.inetAddr = new InetSocketAddress(host, port); 72 | initReconnect(); 73 | } 74 | 75 | public RpcResponse sendRequest(RpcRequest request, boolean async) throws Throwable { 76 | if (!isConnected() || !channel.isConnected()) { 77 | throw new RpcException("not connected"); 78 | } 79 | //如果request已经超时,直接抛弃 80 | if(System.currentTimeMillis() - request.getAddTime().getTime() > Constants.TIMEOUT_INVOKE_MILLSECOND) { 81 | logger.error("request timeout exception"); 82 | throw new RpcException("request timeout exception"); 83 | } 84 | //异步发送请求 85 | InvokeFuture invokeFuture = new InvokeFuture(channel,request); 86 | invokeFuture.send(); 87 | if(async) { 88 | //如果是异步,则封装context 89 | RpcContext.getContext().setFuture(new FutureAdapter(invokeFuture)); 90 | return new RpcResponse(); 91 | } 92 | else { 93 | //如果是同步,则阻塞调用get方法 94 | RpcContext.getContext().setFuture(null); 95 | return invokeFuture.get(Constants.TIMEOUT_INVOKE_MILLSECOND); 96 | } 97 | } 98 | 99 | /** 100 | * 初始化连接 101 | */ 102 | public void open() throws Throwable { 103 | open(true); 104 | } 105 | 106 | /** 107 | * @param connectStatus 心跳检测状态是否正常 108 | * @throws Throwable 109 | */ 110 | public void open(boolean connectStatus) throws Throwable { 111 | logger.info("open start,"+getConnStr()); 112 | bootstrap = new ClientBootstrap(factory); 113 | // timer = new HashedWheelTimer(); 114 | { 115 | bootstrap.setOption("tcpNoDelay", Boolean.parseBoolean(clientConfig.getTcpNoDelay())); 116 | bootstrap.setOption("reuseAddress", Boolean.parseBoolean(clientConfig.getReuseAddress())); 117 | bootstrap.setOption("SO_RCVBUF",1024*128); 118 | bootstrap.setOption("SO_SNDBUF",1024*128); 119 | bootstrap.setPipelineFactory(new ChannelPipelineFactory() { 120 | public ChannelPipeline getPipeline() { 121 | ChannelPipeline pipeline = Channels.pipeline(); 122 | // int readTimeout = clientConfig.getReadTimeout(); 123 | // if (readTimeout > 0) { 124 | // pipeline.addLast("timeout", new ReadTimeoutHandler(timer, 125 | // readTimeout, TimeUnit.MILLISECONDS)); 126 | // } 127 | pipeline.addLast("encoder", new RpcRequestEncode()); 128 | pipeline.addLast("decoder", new RpcResponseDecode()); 129 | pipeline.addLast("handler", NettyRpcConnection.this); 130 | return pipeline; 131 | } 132 | }); 133 | } 134 | connected.set(connectStatus); 135 | logger.info("open finish,"+getConnStr()); 136 | } 137 | 138 | public void initReconnect() { 139 | Runnable connectStatusCheckCommand = new Runnable() { 140 | @Override 141 | public void run() { 142 | try { 143 | if(!isConnected()) { 144 | try { 145 | open(false); 146 | connect(); 147 | connected.set(true); 148 | } catch (Throwable e) { 149 | logger.error("connect open error,conn: {}", getConnStr()); 150 | } 151 | } 152 | if (isConnected() && isClosed()) { 153 | try { 154 | connect(); 155 | } catch (Throwable e) { 156 | logger.error("connect error,conn: {}", getConnStr()); 157 | } 158 | } 159 | if(isConnected() && !isClosed()) { 160 | lastConnectedTime = System.currentTimeMillis(); 161 | } 162 | if (System.currentTimeMillis() - lastConnectedTime > Constants.TIMEOUT_HEARTBEAT_MILLSECOND) { 163 | if (connected.get()) { 164 | connected.set(false); 165 | logger.error("connected has loss heartbeat,conn: {}", getConnStr()); 166 | } 167 | } 168 | } 169 | catch(Throwable e) { 170 | logger.error("connectStatusCheckCommand error"); 171 | } 172 | } 173 | }; 174 | //1秒后每隔3秒发送一次心跳 175 | executorService.scheduleAtFixedRate(connectStatusCheckCommand, 1000, 3000, TimeUnit.MILLISECONDS); 176 | } 177 | 178 | /** 179 | * 尝试连接 180 | */ 181 | public void connect() { 182 | ChannelFuture future = bootstrap.connect(inetAddr); 183 | try{ 184 | boolean ret = future.awaitUninterruptibly(Constants.TIMEOUT_CONNECTION_MILLSECOND, TimeUnit.MILLISECONDS); 185 | if (ret && future.isSuccess()) { 186 | Channel newChannel = future.getChannel(); 187 | newChannel.setInterestOps(Channel.OP_READ_WRITE); 188 | try { 189 | // 关闭旧的连接 190 | Channel oldChannel = NettyRpcConnection.this.channel; 191 | if (oldChannel != null) { 192 | logger.info("Close old netty channel {} on create new netty channel {}", oldChannel, newChannel); 193 | oldChannel.close(); 194 | } 195 | } finally { 196 | if (!isConnected()) { 197 | try { 198 | logger.info("Close new netty channel {}, because the client closed.", newChannel); 199 | newChannel.close(); 200 | } finally { 201 | NettyRpcConnection.this.channel = null; 202 | } 203 | } else { 204 | NettyRpcConnection.this.channel = newChannel; 205 | } 206 | } 207 | } else if (null != future.getCause()) { 208 | logger.error("connect fail", future.getCause()); 209 | throw new RuntimeException("connect error", future.getCause()); 210 | } else { 211 | logger.error("connect fail,connstr: "+this.getConnStr()); 212 | throw new RuntimeException("connect error"); 213 | } 214 | }finally{ 215 | if (! isConnected()) { 216 | future.cancel(); 217 | } 218 | } 219 | } 220 | 221 | /** 222 | * 客户端接受并处理消息 223 | */ 224 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { 225 | RpcResponse response = (RpcResponse) e.getMessage(); 226 | InvokeFuture.receive(channel, response); 227 | } 228 | 229 | 230 | public void close() throws Throwable { 231 | connected.set(false); 232 | // if (null != timer) { 233 | // timer.stop(); 234 | // timer = null; 235 | // } 236 | if (null != channel) { 237 | channel.close().awaitUninterruptibly(); 238 | channel.getFactory().releaseExternalResources(); 239 | 240 | synchronized (channel) { 241 | channel.notifyAll(); 242 | } 243 | channel = null; 244 | } 245 | } 246 | 247 | public boolean isConnected() { 248 | return connected.get(); 249 | } 250 | 251 | public boolean isClosed() { 252 | return (null == channel) || !channel.isConnected() || !channel.isReadable() || !channel.isWritable(); 253 | } 254 | 255 | public String getConnStr() { 256 | return inetAddr.getHostName()+":"+inetAddr.getPort(); 257 | } 258 | 259 | @Override 260 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) 261 | throws Exception { 262 | super.exceptionCaught(ctx, e); 263 | logger.error("exceptionCaught", e.getCause()); 264 | } 265 | 266 | } 267 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------