├── sailfish-channelmode.png ├── sailfish-kernel ├── src │ ├── main │ │ └── java │ │ │ └── sailfish │ │ │ └── remoting │ │ │ ├── constants │ │ │ ├── LangType.java │ │ │ ├── CompressType.java │ │ │ ├── SerializeType.java │ │ │ ├── Opcode.java │ │ │ ├── RemotingConstants.java │ │ │ └── ChannelAttrKeys.java │ │ │ ├── Server.java │ │ │ ├── configuration │ │ │ ├── ExchangeServerConfig.java │ │ │ ├── AbstractExchangeConfig.java │ │ │ └── NegotiateConfig.java │ │ │ ├── exceptions │ │ │ ├── ExceptionCode.java │ │ │ └── SailfishException.java │ │ │ ├── eventgroup │ │ │ ├── ReusedEventGroup.java │ │ │ ├── ClientEventGroup.java │ │ │ ├── ServerEventGroup.java │ │ │ └── AbstractReusedEventGroup.java │ │ │ ├── utils │ │ │ ├── PacketIdGenerator.java │ │ │ ├── CollectionUtils.java │ │ │ ├── Bytes.java │ │ │ ├── ChannelUtil.java │ │ │ ├── ArrayUtils.java │ │ │ └── ParameterChecker.java │ │ │ ├── handler │ │ │ ├── MsgHandler.java │ │ │ ├── ConcreteRequestHandler.java │ │ │ └── HeartbeatChannelHandler.java │ │ │ ├── channel │ │ │ ├── ChannelGroupMode.java │ │ │ ├── ChannelType.java │ │ │ ├── EagerExchangeChannel.java │ │ │ ├── ExchangeChannelChooserFactory.java │ │ │ ├── ExchangeChannel.java │ │ │ ├── MessageExchangePattern.java │ │ │ ├── ServerExchangeChannel.java │ │ │ ├── AbstractExchangeChannelGroup.java │ │ │ ├── ExchangeChannelGroup.java │ │ │ ├── LazyExchangeChannel.java │ │ │ ├── DefaultExchangeChannelGroup.java │ │ │ ├── EmptyExchangeChannel.java │ │ │ ├── ReadWriteServerExchangeChannelGroup.java │ │ │ ├── ServerExchangeChannelGroup.java │ │ │ ├── ReferenceCountedServerExchangeChannelGroup.java │ │ │ ├── ReadWriteExchangeChannelGroup.java │ │ │ ├── HighPerformanceChannelWriter.java │ │ │ ├── DefaultExchangeChannelChooserFactory.java │ │ │ ├── MultiConnectionsExchangeChannelGroup.java │ │ │ ├── AbstractConfigurableExchangeChannelGroup.java │ │ │ ├── SingleConnctionExchangeChannel.java │ │ │ └── AbstractExchangeChannel.java │ │ │ ├── codec │ │ │ ├── RemotingCodec.java │ │ │ ├── RemotingEncoder.java │ │ │ ├── RemotingDecoder.java │ │ │ └── DefaultRemotingCodec.java │ │ │ ├── Endpoint.java │ │ │ ├── processors │ │ │ ├── RequestProcessor.java │ │ │ ├── Processors.java │ │ │ ├── Request.java │ │ │ └── Response.java │ │ │ ├── ResponseCallback.java │ │ │ ├── future │ │ │ └── ResponseFuture.java │ │ │ ├── protocol │ │ │ ├── Protocol.java │ │ │ └── ProtocolParameterChecker.java │ │ │ ├── NettyPlatformIndependent.java │ │ │ ├── Address.java │ │ │ ├── DefaultClient.java │ │ │ ├── RequestControl.java │ │ │ ├── Exchanger.java │ │ │ ├── ReconnectManager.java │ │ │ ├── executor │ │ │ └── SimpleExecutor.java │ │ │ ├── Tracer.java │ │ │ └── DefaultServer.java │ └── test │ │ ├── resources │ │ └── logback.xml │ │ └── java │ │ └── sailfish │ │ └── remoting │ │ ├── processors │ │ ├── ServerRequestProcessor.java │ │ ├── ClientRequestProcessor.java │ │ ├── ClientServerTimeoutTestRequestProcessor.java │ │ ├── RemoteExceptionTestRequestHandler.java │ │ ├── ClientServerOnewayTestRequestProcessor.java │ │ └── ClientServerNormalRequestTestRequestProcessor.java │ │ ├── BytesTest.java │ │ ├── ServerClientTest.java │ │ ├── RemoteExceptionTest.java │ │ ├── RecycleTest.java │ │ ├── ProtocolTest.java │ │ └── ExchangeChannelChooserTest.java └── pom.xml ├── .gitignore ├── pom.xml ├── README.md └── best-practice-in-netty.md /sailfish-channelmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spccold/sailfish/HEAD/sailfish-channelmode.png -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/constants/LangType.java: -------------------------------------------------------------------------------- 1 | package sailfish.remoting.constants; 2 | 3 | public interface LangType { 4 | byte JAVA = 0; 5 | byte C = 1; 6 | byte CPP = 2; 7 | byte GO = 3; 8 | } -------------------------------------------------------------------------------- /sailfish-kernel/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | *.tar.gz 8 | 9 | # eclipse ignore 10 | .settings/ 11 | .project 12 | .classpath 13 | 14 | # idea ignore 15 | .idea/ 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # temp ignore 21 | *.log 22 | *.cache 23 | *.diff 24 | *.patch 25 | *.tmp 26 | 27 | # system ignore 28 | .DS_Store 29 | Thumbs.db 30 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/constants/CompressType.java: -------------------------------------------------------------------------------- 1 | package sailfish.remoting.constants; 2 | 3 | public interface CompressType { 4 | //TODO compressType with multiple mode, needs to perfect in future 5 | byte NON_COMPRESS = 0; 6 | byte LZ4_COMPRESS = 1; 7 | byte GZIP_COMPRESS = 2; 8 | byte DEFLATE_COMPRESS = 3; 9 | byte SNAPPY_COMPRESS = 4; 10 | } -------------------------------------------------------------------------------- /sailfish-kernel/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | sailfish 6 | sailfish 7 | 0.0.1-SNAPSHOT 8 | 9 | sailfish-kernel 10 | sailfish-kernel 11 | sailfish microkernel 12 | 13 | 14 | 15 | io.netty 16 | netty-all 17 | 18 | 19 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/constants/SerializeType.java: -------------------------------------------------------------------------------- 1 | package sailfish.remoting.constants; 2 | 3 | public interface SerializeType { 4 | //pure bytes, no need any serialize and deserialize 5 | byte NON_SERIALIZE = 0; 6 | //java platform 7 | byte JDK_SERIALIZE = 1; 8 | //java platform with high performance 9 | byte KRYO_SERIALIZE = 2; 10 | byte FST_SERIALIZE = 3; 11 | //cross-platform 12 | byte JSON_SERIALIZE = 4; 13 | //cross-platform with high performance 14 | byte HESSIAN_SERIALIZE = 5; 15 | byte AVRO_SERIALIZE = 6; 16 | byte THRIFT_SERIALIZE = 7; 17 | byte PROTOBUF_SERIALIZE = 8; 18 | byte FLATBUFFER_SERIALIZE = 9; 19 | } 20 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/constants/Opcode.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.constants; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: Opcode.java, v 0.1 2016年11月4日 下午4:01:17 jileng Exp $ 24 | */ 25 | public interface Opcode { 26 | short HEARTBEAT_WITH_NEGOTIATE = 1; 27 | } 28 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/Server.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.Collection; 21 | 22 | import sailfish.remoting.channel.ExchangeChannelGroup; 23 | 24 | /** 25 | * @author spccold 26 | * @version $Id: Server.java, v 0.1 2016年11月29日 下午9:44:51 spccold Exp $ 27 | */ 28 | public interface Server extends Endpoint{ 29 | 30 | Collection listChannelGroups(); 31 | } 32 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/configuration/ExchangeServerConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.configuration; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: ExchangeServerConfig.java, v 0.1 2016年10月27日 下午5:42:01 jileng Exp $ 24 | */ 25 | public class ExchangeServerConfig extends AbstractExchangeConfig{ 26 | 27 | @Override 28 | public void check() { 29 | super.check(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/exceptions/ExceptionCode.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.exceptions; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: ExceptionCode.java, v 0.1 2016年10月27日 下午4:25:28 jileng Exp $ 24 | */ 25 | public enum ExceptionCode { 26 | BAD_PACKAGE, 27 | RESPONSE_TIMEOUT, 28 | WRITE_TIMEOUT, 29 | EXCHANGER_NOT_AVAILABLE, 30 | UNFINISHED_REQUEST, 31 | CHANNEL_WRITE_FAIL, 32 | DEFAULT, 33 | ; 34 | } 35 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/eventgroup/ReusedEventGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.eventgroup; 19 | 20 | import io.netty.channel.EventLoopGroup; 21 | import io.netty.util.concurrent.EventExecutorGroup; 22 | 23 | /** 24 | * @author spccold 25 | * @version $Id: ReusedEventGroup.java, v 0.1 2016年11月20日 下午7:26:51 spccold Exp $ 26 | */ 27 | public interface ReusedEventGroup { 28 | EventLoopGroup getLoopGroup(); 29 | EventExecutorGroup getExecutorGroup(); 30 | void destory(); 31 | } 32 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/utils/PacketIdGenerator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.utils; 19 | 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | /** 23 | * 24 | * @author spccold 25 | * @version $Id: PacketIdGenerator.java, v 0.1 2016年10月26日 下午10:47:17 jileng Exp $ 26 | */ 27 | public class PacketIdGenerator { 28 | private static final AtomicInteger ID_GENERATOR = new AtomicInteger(0); 29 | public static int nextId(){ 30 | return ID_GENERATOR.incrementAndGet(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/handler/MsgHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.handler; 19 | 20 | import sailfish.remoting.channel.ExchangeChannelGroup; 21 | import sailfish.remoting.protocol.Protocol; 22 | 23 | /** 24 | * 25 | * @author spccold 26 | * @version $Id: MsgHandler.java, v 0.1 2016年10月26日 上午11:46:07 jileng Exp $ 27 | */ 28 | public interface MsgHandler { 29 | /** 30 | * I is request or response 31 | */ 32 | void handle(ExchangeChannelGroup channelGroup, I msg); 33 | } 34 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ChannelGroupMode.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: ChannelGroupMode.java, v 0.1 2016年10月27日 上午11:09:58 jileng Exp $ 24 | */ 25 | public enum ChannelGroupMode { 26 | /** with only one eager or lazy connection*/ 27 | simple, 28 | /** with multiple simple exchange channel*/ 29 | multiconns, 30 | /** with multiple simple exchange channel, but support read write splitting */ 31 | readwrite, 32 | ; 33 | } 34 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/codec/RemotingCodec.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.codec; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import sailfish.remoting.exceptions.SailfishException; 22 | import sailfish.remoting.protocol.Protocol; 23 | 24 | /** 25 | * 26 | * @author spccold 27 | * @version $Id: RemotingCodec.java, v 0.1 2016年10月15日 下午4:45:54 jileng Exp $ 28 | */ 29 | public interface RemotingCodec { 30 | public void encode(Protocol protocol, ByteBuf buffer) throws SailfishException; 31 | public Protocol decode(ByteBuf buffer) throws SailfishException; 32 | } 33 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/Endpoint.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | /** 21 | * Endpoint 22 | * 23 | * @author spccold 24 | * @version $Id: Endpoint.java, v 0.1 2016年10月3日 上午11:35:29 jileng Exp $ 25 | */ 26 | public interface Endpoint { 27 | /** 28 | * close this {@link Endpoint} 29 | */ 30 | void close(); 31 | 32 | /** 33 | * graceful close this {@link Endpoint} 34 | * @param timeout milliseconds 35 | */ 36 | void close(int timeout); 37 | 38 | boolean isClosed(); 39 | } 40 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/processors/RequestProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | /** 23 | * @author spccold 24 | * @version $Id: RequestProcessor.java, v 0.1 2016年11月25日 上午11:55:03 spccold Exp $ 25 | */ 26 | public interface RequestProcessor { 27 | Executor executor(); 28 | 29 | short opcode(); 30 | 31 | void handleRequest(Request request, Output output); 32 | 33 | void onRejectedExecutionException(Request request, Output output); 34 | 35 | interface Output { 36 | void response(Response response); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/ResponseCallback.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | import io.netty.util.concurrent.DefaultThreadFactory; 23 | import io.netty.util.concurrent.FastThreadLocalThread; 24 | 25 | /** 26 | * 27 | * @author spccold 28 | * @version $Id: ResponseCallback.java, v 0.1 2016年10月31日 上午10:09:15 jileng Exp $ 29 | */ 30 | public interface ResponseCallback { 31 | /** 32 | * should take advantage of {@link FastThreadLocalThread} with {@link DefaultThreadFactory} 33 | */ 34 | Executor getExecutor(); 35 | 36 | void handleResponse(T resp); 37 | void handleException(Exception cause); 38 | } 39 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ChannelType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | /** 21 | * @author spccold 22 | * @version $Id: ChannelType.java, v 0.1 2016年11月24日 下午7:04:12 spccold Exp $ 23 | */ 24 | public enum ChannelType { 25 | /** read channel for {@link ReadWriteExchangeChannelGroup} */ 26 | read((byte) 0), 27 | /** write channel for {@link ReadWriteExchangeChannelGroup} */ 28 | write((byte) 1), 29 | /** read&write channel for {@link DefaultExchangeChannelGroup} */ 30 | readwrite((byte) 2), 31 | 32 | unknow((byte) -1); 33 | ; 34 | 35 | byte code; 36 | 37 | private ChannelType(byte code) { 38 | this.code = code; 39 | } 40 | 41 | public byte code() { 42 | return code; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/future/ResponseFuture.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.future; 19 | 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import sailfish.remoting.ResponseCallback; 23 | import sailfish.remoting.exceptions.SailfishException; 24 | 25 | /** 26 | * 27 | * @author spccold 28 | * @version $Id: ResponseFuture.java, v 0.1 2016年10月4日 下午3:56:21 jileng Exp $ 29 | */ 30 | public interface ResponseFuture{ 31 | void putResponse(T resp, byte result, SailfishException cause); 32 | boolean isDone(); 33 | void setCallback(ResponseCallback callback, int timeout); 34 | T get() throws SailfishException, InterruptedException; 35 | T get(long timeout, TimeUnit unit) throws SailfishException, InterruptedException; 36 | } 37 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/EagerExchangeChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import io.netty.bootstrap.Bootstrap; 21 | import sailfish.remoting.exceptions.SailfishException; 22 | 23 | /** 24 | * @author spccold 25 | * @version $Id: EagerExchangeChannel.java, v 0.1 2016年11月21日 下午11:17:02 spccold Exp $ 26 | */ 27 | public final class EagerExchangeChannel extends SingleConnctionExchangeChannel { 28 | 29 | EagerExchangeChannel(Bootstrap bootstrap, ExchangeChannelGroup parent, int reconnectInterval) 30 | throws SailfishException { 31 | super(bootstrap, parent, reconnectInterval, true); 32 | } 33 | 34 | @Override 35 | public boolean isAvailable() { 36 | if (isClosed()) { 37 | return false; 38 | } 39 | return super.isAvailable(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/utils/CollectionUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.utils; 19 | 20 | import java.util.Collection; 21 | import java.util.Map; 22 | 23 | /** 24 | * 25 | * @author spccold 26 | * @version $Id: CollectionUtils.java, v 0.1 2016年11月10日 下午5:33:30 jileng Exp $ 27 | */ 28 | public class CollectionUtils { 29 | public static boolean isEmpty(Collection coll) { 30 | return (null == coll || coll.isEmpty()); 31 | } 32 | 33 | public static boolean isNotEmpty(Collection coll) { 34 | return !isEmpty(coll); 35 | } 36 | 37 | public static boolean isEmpty(Map map) { 38 | return (map == null || map.isEmpty()); 39 | } 40 | 41 | public static boolean isNotEmpty(Map map) { 42 | return !isEmpty(map); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/processors/ServerRequestProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | /** 23 | * @author spccold 24 | * @version $Id: ServerRequestProcessor.java, v 0.1 2016年11月29日 下午10:11:44 spccold Exp $ 25 | */ 26 | public class ServerRequestProcessor implements RequestProcessor{ 27 | public static final short OPCODE = 0; 28 | 29 | @Override 30 | public Executor executor() { 31 | return null; 32 | } 33 | 34 | @Override 35 | public short opcode() { 36 | return OPCODE; 37 | } 38 | 39 | @Override 40 | public void handleRequest(Request request, Output output) { 41 | output.response(new Response(true, request.getRequestData())); 42 | } 43 | 44 | @Override 45 | public void onRejectedExecutionException(Request request, Output output) { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/processors/ClientRequestProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | /** 23 | * @author spccold 24 | * @version $Id: ClientRequestProcessor.java, v 0.1 2016年11月29日 下午10:10:55 spccold Exp $ 25 | */ 26 | public class ClientRequestProcessor implements RequestProcessor{ 27 | public static final short OPCODE = 0; 28 | 29 | @Override 30 | public Executor executor() { 31 | return null; 32 | } 33 | 34 | @Override 35 | public short opcode() { 36 | return OPCODE; 37 | } 38 | 39 | @Override 40 | public void handleRequest(Request request, Output output) { 41 | output.response(new Response(true, request.getRequestData())); 42 | } 43 | 44 | @Override 45 | public void onRejectedExecutionException(Request request, Output output) { 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/protocol/Protocol.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.protocol; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import sailfish.remoting.exceptions.SailfishException; 22 | 23 | /** 24 | * 25 | * @author spccold 26 | * @version $Id: Protocol.java, v 0.1 2016年10月4日 下午3:01:29 jileng Exp $ 27 | */ 28 | public interface Protocol { 29 | /** 30 | * request or response direction 31 | */ 32 | boolean request(); 33 | 34 | /** 35 | * heartbeat request/response 36 | */ 37 | boolean heartbeat(); 38 | 39 | /** 40 | * serialize bytes data to channel 41 | */ 42 | void serialize(ByteBuf output) throws SailfishException; 43 | 44 | /** 45 | * deserialize bytes data from channel 46 | */ 47 | void deserialize(ByteBuf input, int totalLength) throws SailfishException; 48 | } 49 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/BytesTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | import sailfish.remoting.utils.Bytes; 24 | 25 | /** 26 | * 27 | * @author spccold 28 | * @version $Id: BytesTest.java, v 0.1 2016年11月8日 下午7:53:57 jileng Exp $ 29 | */ 30 | public class BytesTest { 31 | 32 | @Test 33 | public void test() { 34 | //test positive 35 | int integer = 1; 36 | Assert.assertTrue(integer == Bytes.bytes2int(Bytes.int2bytes(integer))); 37 | 38 | //test negative 39 | integer = -1; 40 | Assert.assertTrue(integer == Bytes.bytes2int(Bytes.int2bytes(integer))); 41 | 42 | //test zero 43 | integer = 0; 44 | Assert.assertTrue(integer == Bytes.bytes2int(Bytes.int2bytes(integer))); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/processors/ClientServerTimeoutTestRequestProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | /** 23 | * @author spccold 24 | * @version $Id: ClientServerTimeoutTestRequestProcessor.java, v 0.1 2016年11月25日 下午4:21:57 spccold Exp $ 25 | */ 26 | public class ClientServerTimeoutTestRequestProcessor implements RequestProcessor{ 27 | public static final short OPCODE = 2; 28 | @Override 29 | public Executor executor() { 30 | return null; 31 | } 32 | 33 | @Override 34 | public short opcode() { 35 | return OPCODE; 36 | } 37 | 38 | @Override 39 | public void handleRequest(Request request, Output output) { 40 | //do nothing, request from remote peer will timeout 41 | } 42 | 43 | @Override 44 | public void onRejectedExecutionException(Request request, Output output) { 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/utils/Bytes.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.utils; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: Bytes.java, v 0.1 2016年11月8日 下午7:44:46 jileng Exp $ 24 | */ 25 | public class Bytes { 26 | public static int bytes2int(byte[] bytes) { 27 | if (null == bytes || bytes.length != 4) { 28 | throw new IllegalArgumentException(); 29 | } 30 | return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3] << 0; 31 | } 32 | 33 | //high byte first 34 | public static byte[] int2bytes(int integer) { 35 | byte[] bytes = new byte[4]; 36 | bytes[0] = (byte) (integer >> 24 & 0xFF); 37 | bytes[1] = (byte) (integer >> 16 & 0xFF); 38 | bytes[2] = (byte) (integer >> 8 & 0xFF); 39 | bytes[3] = (byte) (integer >> 0 & 0xFF); 40 | return bytes; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ExchangeChannelChooserFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import sailfish.remoting.exceptions.SailfishException; 21 | 22 | /** 23 | * Factory that creates new {@link ExchangeChannelChooser}s. 24 | * 25 | * @author spccold 26 | * @version $Id: ExchangeChannelChooserFactory.java, v 0.1 2016年11月22日 下午4:36:58 spccold Exp $ 27 | */ 28 | public interface ExchangeChannelChooserFactory { 29 | /** 30 | * Returns a new {@link ExchangeChannelChooser}. 31 | */ 32 | ExchangeChannelChooser newChooser(ExchangeChannel[] channels, ExchangeChannel[] deadChannels); 33 | 34 | /** 35 | * Chooses the next {@link ExchangeChannel} to use. 36 | */ 37 | interface ExchangeChannelChooser { 38 | 39 | /** 40 | * Returns the new {@link ExchangeChannel} to use. 41 | */ 42 | ExchangeChannel next() throws SailfishException; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/codec/RemotingEncoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.codec; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import io.netty.channel.ChannelHandler; 22 | import io.netty.channel.ChannelHandlerContext; 23 | import io.netty.handler.codec.MessageToByteEncoder; 24 | import sailfish.remoting.protocol.Protocol; 25 | 26 | /** 27 | * 28 | * @author spccold 29 | * @version $Id: RemotingEncoder.java, v 0.1 2016年10月9日 下午9:44:43 jileng Exp $ 30 | */ 31 | @ChannelHandler.Sharable 32 | public class RemotingEncoder extends MessageToByteEncoder{ 33 | 34 | public static final RemotingEncoder INSTANCE = new RemotingEncoder(); 35 | 36 | private RemotingEncoder(){} 37 | 38 | //all write exceptions will be manage by Promise 39 | @Override 40 | protected void encode(ChannelHandlerContext ctx, Protocol msg, ByteBuf out) throws Exception { 41 | DefaultRemotingCodec.INSTANCE.encode(msg, out); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/processors/RemoteExceptionTestRequestHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | import io.netty.util.CharsetUtil; 23 | 24 | /** 25 | * @author spccold 26 | * @version $Id: RemoteExceptionTestRequestHandler.java, v 0.1 2016年11月29日 下午8:42:49 spccold Exp $ 27 | */ 28 | public class RemoteExceptionTestRequestHandler implements RequestProcessor{ 29 | public static final short OPCODE = 0; 30 | 31 | @Override 32 | public Executor executor() { 33 | return null; 34 | } 35 | 36 | @Override 37 | public short opcode() { 38 | return OPCODE; 39 | } 40 | 41 | @Override 42 | public void handleRequest(Request request, Output output) { 43 | throw new RuntimeException(new String(request.getRequestData(), CharsetUtil.UTF_8)); 44 | } 45 | 46 | @Override 47 | public void onRejectedExecutionException(Request request, Output output) { 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/processors/Processors.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import java.util.concurrent.ConcurrentMap; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author spccold 28 | * @version $Id: Processors.java, v 0.1 2016年11月25日 上午11:54:17 spccold Exp $ 29 | */ 30 | public class Processors { 31 | 32 | private static final Logger logger = LoggerFactory.getLogger(Processors.class); 33 | 34 | private final ConcurrentMap processors = new ConcurrentHashMap<>(); 35 | 36 | public void registerProcessor(int opcode, RequestProcessor processor){ 37 | if(null != processors.putIfAbsent(opcode, processor)){ 38 | logger.warn("repeat register request processor, opcode[{}], processor[{}]", opcode, processor); 39 | } 40 | } 41 | 42 | public RequestProcessor findProcessor(int opcode){ 43 | return processors.get(opcode); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/processors/ClientServerOnewayTestRequestProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | import sailfish.remoting.ClientServerTest; 23 | import sailfish.remoting.utils.Bytes; 24 | 25 | /** 26 | * @author spccold 27 | * @version $Id: ClientServerOnewayTestRequestProcessor.java, v 0.1 2016年11月25日 下午4:13:40 spccold Exp $ 28 | */ 29 | public class ClientServerOnewayTestRequestProcessor implements RequestProcessor{ 30 | public static final short OPCODE = 0; 31 | @Override 32 | public Executor executor() { 33 | return null; 34 | } 35 | 36 | @Override 37 | public short opcode() { 38 | return OPCODE; 39 | } 40 | 41 | @Override 42 | public void handleRequest(Request request, Output output) { 43 | ClientServerTest.RECORDS.get(Bytes.bytes2int(request.getRequestData())).countDown(); 44 | } 45 | 46 | @Override 47 | public void onRejectedExecutionException(Request request, Output output) { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/processors/ClientServerNormalRequestTestRequestProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import java.util.concurrent.Executor; 21 | 22 | import org.junit.Assert; 23 | 24 | import sailfish.remoting.ClientServerTest; 25 | 26 | /** 27 | * @author spccold 28 | * @version $Id: ClientServerNormalRequestTestRequestProcessor.java, v 0.1 2016年11月25日 下午4:24:26 spccold Exp $ 29 | */ 30 | public class ClientServerNormalRequestTestRequestProcessor implements RequestProcessor{ 31 | public static final short OPCODE = 1; 32 | @Override 33 | public Executor executor() { 34 | return null; 35 | } 36 | 37 | @Override 38 | public short opcode() { 39 | return OPCODE; 40 | } 41 | 42 | @Override 43 | public void handleRequest(Request request, Output output) { 44 | Assert.assertNotNull(request.getRequestData()); 45 | Assert.assertTrue(request.getRequestData().length > 0); 46 | Assert.assertArrayEquals(ClientServerTest.data, request.getRequestData()); 47 | output.response(new Response(true, ClientServerTest.data)); 48 | } 49 | 50 | @Override 51 | public void onRejectedExecutionException(Request request, Output output) { 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ExchangeChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.net.SocketAddress; 21 | 22 | import io.netty.channel.Channel; 23 | import sailfish.remoting.exceptions.SailfishException; 24 | 25 | /** 26 | * @author spccold 27 | * @version $Id: ExchangeChannel.java, v 0.1 2016年11月21日 下午7:26:12 spccold Exp $ 28 | */ 29 | public interface ExchangeChannel extends ExchangeChannelGroup{ 30 | /** 31 | * Returns a reference to itself. 32 | */ 33 | @Override 34 | ExchangeChannel next(); 35 | 36 | /** 37 | * Return the {@link ExchangeChannelGroup} which is the parent of this {@link ExchangeChannel}, 38 | */ 39 | ExchangeChannelGroup parent(); 40 | 41 | /** 42 | * update this {@link ExchangeChannel} underlying {@link Channel} 43 | * @param newChannel 44 | * @return old {@link Channel} 45 | */ 46 | Channel update(Channel newChannel); 47 | 48 | /** 49 | * connect to remote peer 50 | * @returnSailfishException 51 | * @throws SailfishException 52 | */ 53 | Channel doConnect() throws SailfishException; 54 | 55 | /** 56 | * recover this {@link ExchangeChannel} if {@link #isAvailable()} return false 57 | */ 58 | void recover(); 59 | 60 | SocketAddress localAddress(); 61 | SocketAddress remoteAdress(); 62 | } 63 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/MessageExchangePattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import sailfish.remoting.RequestControl; 21 | import sailfish.remoting.ResponseCallback; 22 | import sailfish.remoting.exceptions.SailfishException; 23 | import sailfish.remoting.future.ResponseFuture; 24 | import sailfish.remoting.protocol.ResponseProtocol; 25 | 26 | /** 27 | * 28 | *
29 |  * Messaging_pattern
30 |  * Request–response
31 |  * 
32 | * 33 | * @author spccold 34 | * @version $Id: MessageExchangePattern.java, v 0.1 2016年11月24日 下午5:45:03 spccold Exp $ 35 | */ 36 | public interface MessageExchangePattern { 37 | 38 | /** 39 | * one-way pattern 40 | */ 41 | void oneway(byte[] data, RequestControl requestControl) throws SailfishException; 42 | 43 | /** 44 | * request–response pattern 45 | */ 46 | ResponseFuture request(byte[] data, RequestControl requestControl) throws SailfishException; 47 | 48 | /** 49 | * callback request via request–response pattern 50 | */ 51 | void request(byte[] data, ResponseCallback callback, RequestControl requestControl) throws SailfishException; 52 | 53 | void response(ResponseProtocol response) throws SailfishException; 54 | } 55 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/eventgroup/ClientEventGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.eventgroup; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import io.netty.util.internal.SystemPropertyUtil; 24 | import sailfish.remoting.constants.RemotingConstants; 25 | 26 | /** 27 | * @author spccold 28 | * @version $Id: ClientEventGroup.java, v 0.1 2016年11月20日 下午7:29:07 spccold Exp 29 | * $ 30 | */ 31 | public class ClientEventGroup extends AbstractReusedEventGroup { 32 | private static final Logger logger = LoggerFactory.getLogger(ClientEventGroup.class); 33 | 34 | private static final int IO_THREADS; 35 | private static final int EVENT_THREADS; 36 | public static final ClientEventGroup INSTANCE; 37 | static { 38 | IO_THREADS = Math.max(1, 39 | SystemPropertyUtil.getInt("sailfish.remoting.client.ioThreads", RemotingConstants.DEFAULT_IO_THREADS)); 40 | EVENT_THREADS = Math.max(1, SystemPropertyUtil.getInt("sailfish.remoting.client.eventThreads", 41 | RemotingConstants.DEFAULT_EVENT_THREADS)); 42 | if (logger.isDebugEnabled()) { 43 | logger.debug("-Dsailfish.remoting.client.ioThreads: {}", IO_THREADS); 44 | logger.debug("-Dsailfish.remoting.client.eventThreads: {}", EVENT_THREADS); 45 | } 46 | INSTANCE = new ClientEventGroup(); 47 | } 48 | 49 | private ClientEventGroup() { 50 | super(IO_THREADS, RemotingConstants.CLIENT_IO_THREADNAME, EVENT_THREADS, 51 | RemotingConstants.CLIENT_EVENT_THREADNAME); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/eventgroup/ServerEventGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.eventgroup; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import io.netty.util.internal.SystemPropertyUtil; 24 | import sailfish.remoting.constants.RemotingConstants; 25 | 26 | /** 27 | * @author spccold 28 | * @version $Id: ServerEventGroup.java, v 0.1 2016年11月20日 下午7:29:27 spccold Exp 29 | * $ 30 | */ 31 | public class ServerEventGroup extends AbstractReusedEventGroup { 32 | public static final Logger logger = LoggerFactory.getLogger(ServerEventGroup.class); 33 | 34 | private static final int IO_THREADS; 35 | private static final int EVENT_THREADS; 36 | public static final ServerEventGroup INSTANCE; 37 | static { 38 | IO_THREADS = Math.max(1, 39 | SystemPropertyUtil.getInt("sailfish.remoting.server.ioThreads", RemotingConstants.DEFAULT_IO_THREADS)); 40 | EVENT_THREADS = Math.max(1, SystemPropertyUtil.getInt("sailfish.remoting.server.eventThreads", 41 | RemotingConstants.DEFAULT_EVENT_THREADS)); 42 | if (logger.isDebugEnabled()) { 43 | logger.debug("-Dsailfish.remoting.server.ioThreads: {}", IO_THREADS); 44 | logger.debug("-Dsailfish.remoting.server.eventThreads: {}", EVENT_THREADS); 45 | } 46 | INSTANCE = new ServerEventGroup(); 47 | } 48 | 49 | private ServerEventGroup() { 50 | super(IO_THREADS, RemotingConstants.SERVER_IO_THREADNAME, EVENT_THREADS, 51 | RemotingConstants.SERVER_EVENT_THREADNAME); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/processors/Request.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import sailfish.remoting.constants.CompressType; 21 | import sailfish.remoting.constants.SerializeType; 22 | 23 | /** 24 | * @author spccold 25 | * @version $Id: Request.java, v 0.1 2016年11月29日 下午5:13:03 spccold Exp $ 26 | */ 27 | public class Request { 28 | private boolean oneway; 29 | private byte serializeType = SerializeType.NON_SERIALIZE; 30 | private byte compressType = CompressType.NON_COMPRESS; 31 | private byte[] requestData; 32 | private byte langType; 33 | 34 | public Request(boolean oneway, byte serializeType, byte compressType, byte[] requestData, byte langType) { 35 | this.oneway = oneway; 36 | this.serializeType = serializeType; 37 | this.compressType = compressType; 38 | this.requestData = requestData; 39 | this.langType = langType; 40 | } 41 | 42 | /** 43 | * @return the oneway 44 | */ 45 | public boolean isOneway() { 46 | return oneway; 47 | } 48 | 49 | /** 50 | * @return the serializeType 51 | */ 52 | public byte getSerializeType() { 53 | return serializeType; 54 | } 55 | 56 | /** 57 | * @return the compressType 58 | */ 59 | public byte getCompressType() { 60 | return compressType; 61 | } 62 | 63 | /** 64 | * @return the requestData 65 | */ 66 | public byte[] getRequestData() { 67 | return requestData; 68 | } 69 | 70 | /** 71 | * @return the langType 72 | */ 73 | public byte getLangType() { 74 | return langType; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/handler/ConcreteRequestHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.handler; 19 | 20 | import io.netty.channel.ChannelHandlerContext; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import io.netty.channel.ChannelHandler; 26 | import io.netty.channel.SimpleChannelInboundHandler; 27 | import sailfish.remoting.channel.ExchangeChannelGroup; 28 | import sailfish.remoting.constants.ChannelAttrKeys; 29 | import sailfish.remoting.protocol.Protocol; 30 | import sailfish.remoting.utils.ChannelUtil; 31 | 32 | /** 33 | * 34 | * @author spccold 35 | * @version $Id: ConcreteRequestHandler.java, v 0.1 2016年11月1日 下午2:17:59 jileng Exp $ 36 | */ 37 | @ChannelHandler.Sharable 38 | public class ConcreteRequestHandler extends SimpleChannelInboundHandler { 39 | 40 | private static final Logger logger = LoggerFactory.getLogger(ConcreteRequestHandler.class); 41 | 42 | public static final ConcreteRequestHandler INSTANCE = new ConcreteRequestHandler(); 43 | 44 | private ConcreteRequestHandler() {} 45 | 46 | @Override 47 | protected void channelRead0(ChannelHandlerContext ctx, Protocol msg) throws Exception { 48 | ExchangeChannelGroup channelGroup = ctx.channel().attr(ChannelAttrKeys.channelGroup).get(); 49 | if(null != channelGroup){ 50 | channelGroup.getMsgHander().handle(channelGroup, msg); 51 | }else{ 52 | logger.warn("channelGroup not exist, side[{}], protocol[{}]", ChannelUtil.clientSide(ctx) ? "client" : "server", msg); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/constants/RemotingConstants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.constants; 19 | 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * different compression algorithm in java platform 24 | * 25 | * @author spccold 26 | * @version $Id: RemotingConstants.java, v 0.1 2016年10月9日 下午9:58:16 jileng Exp $ 27 | */ 28 | public interface RemotingConstants { 29 | // 压缩阀值, KB 30 | int COMPRESS_THRESHOLD = 4 * 1024; 31 | // sailfish binary protocol magic 32 | short SAILFISH_MAGIC = ByteBuffer.wrap("SH".getBytes()).getShort(); 33 | 34 | // max frame size, 8MB 35 | int DEFAULT_PAYLOAD_LENGTH = 8 * 1024 * 1024; 36 | 37 | // milliseconds 38 | int DEFAULT_CONNECT_TIMEOUT = 2000; 39 | int DEFAULT_RECONNECT_INTERVAL = 1000; 40 | // seconds 41 | byte DEFAULT_IDLE_TIMEOUT = 10; 42 | byte DEFAULT_MAX_IDLE_TIMEOUT = 3 * DEFAULT_IDLE_TIMEOUT; 43 | 44 | // result 45 | byte RESULT_SUCCESS = 0; 46 | byte RESULT_FAIL = 1; 47 | 48 | // channel type for read write splitting 49 | byte WRITE_CHANNEL = 0; 50 | byte READ_CHANNEL = 1; 51 | 52 | int DEFAULT_IO_THREADS = Runtime.getRuntime().availableProcessors() + 1; 53 | int DEFAULT_EVENT_THREADS = 1; 54 | String CLIENT_IO_THREADNAME = "sailfish-client-io"; 55 | String CLIENT_EVENT_THREADNAME = "sailfish-client-event"; 56 | String SERVER_IO_THREADNAME = "sailfish-server-io"; 57 | String SERVER_EVENT_THREADNAME = "sailfish-server-event"; 58 | String SERVER_ACCEPT_THREADNAME = "sailfish-server-accept"; 59 | } 60 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/eventgroup/AbstractReusedEventGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.eventgroup; 19 | 20 | import io.netty.channel.EventLoopGroup; 21 | import io.netty.util.concurrent.DefaultEventExecutorGroup; 22 | import io.netty.util.concurrent.DefaultThreadFactory; 23 | import io.netty.util.concurrent.EventExecutorGroup; 24 | import sailfish.remoting.NettyPlatformIndependent; 25 | 26 | /** 27 | * @author spccold 28 | * @version $Id: AbstractReusedEventGroup.java, v 0.1 2016年11月20日 下午7:29:59 29 | * spccold Exp $ 30 | */ 31 | public abstract class AbstractReusedEventGroup implements ReusedEventGroup { 32 | protected final EventLoopGroup reusedEventLoopGroup; 33 | protected final EventExecutorGroup reusedEventExecutorGroup; 34 | 35 | protected AbstractReusedEventGroup(int ioThreads, String ioThreadName, int eventThreads, String eventThreadName) { 36 | this.reusedEventLoopGroup = NettyPlatformIndependent.newEventLoopGroup(ioThreads, 37 | new DefaultThreadFactory(ioThreadName)); 38 | this.reusedEventExecutorGroup = new DefaultEventExecutorGroup(eventThreads, 39 | new DefaultThreadFactory(eventThreadName)); 40 | } 41 | 42 | @Override 43 | public void destory() { 44 | reusedEventLoopGroup.shutdownGracefully(); 45 | reusedEventExecutorGroup.shutdownGracefully(); 46 | } 47 | 48 | @Override 49 | public EventLoopGroup getLoopGroup() { 50 | return reusedEventLoopGroup; 51 | } 52 | 53 | @Override 54 | public EventExecutorGroup getExecutorGroup() { 55 | return reusedEventExecutorGroup; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/protocol/ProtocolParameterChecker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.protocol; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: ProtocolParameterChecker.java, v 0.1 2016年11月2日 下午7:29:18 jileng Exp $ 24 | */ 25 | public class ProtocolParameterChecker { 26 | public static byte checkSerializeType(byte serializeType){ 27 | if(serializeType < 0 || serializeType > 0x1F){ 28 | throw new IllegalArgumentException( 29 | "serializeType: " + serializeType + " (expected: 0 <= serializeType <= 0x1F)"); 30 | } 31 | return serializeType; 32 | } 33 | 34 | public static byte checkCompressType(byte compressType){ 35 | if(compressType < 0 || compressType > 0xF){ 36 | throw new IllegalArgumentException( 37 | "compressType: " + compressType + " (expected: 0 <= compressType <= 0xF)"); 38 | } 39 | return compressType; 40 | } 41 | 42 | public static byte checkLangType(byte langType){ 43 | if(langType < 0 || langType > 0xF){ 44 | throw new IllegalArgumentException( 45 | "langType: " + langType + " (expected: 0 <= langType <= 0xF)"); 46 | } 47 | return langType; 48 | } 49 | 50 | public static byte checkResult(byte result){ 51 | if(result < 0 || result > 0xF){ 52 | throw new IllegalArgumentException( 53 | "result: " + result + " (expected: 0 <= result <= 0xF)"); 54 | } 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ServerExchangeChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import io.netty.channel.Channel; 21 | import sailfish.remoting.Tracer; 22 | import sailfish.remoting.exceptions.SailfishException; 23 | import sailfish.remoting.handler.MsgHandler; 24 | import sailfish.remoting.protocol.Protocol; 25 | 26 | /** 27 | * @author spccold 28 | * @version $Id: ServerExchangeChannel.java, v 0.1 2016年11月24日 下午5:49:52 spccold Exp $ 29 | */ 30 | public final class ServerExchangeChannel extends AbstractExchangeChannel { 31 | 32 | public ServerExchangeChannel(ExchangeChannelGroup parent, Channel channel) { 33 | super(parent); 34 | this.channel = channel; 35 | } 36 | 37 | @Override 38 | public Channel update(Channel newChannel) { 39 | throw new UnsupportedOperationException("update"); 40 | } 41 | 42 | @Override 43 | public Channel doConnect() throws SailfishException { 44 | throw new UnsupportedOperationException("doConnect"); 45 | } 46 | 47 | @Override 48 | public void recover() { 49 | throw new UnsupportedOperationException("recover"); 50 | } 51 | 52 | @Override 53 | public void close(int timeout) { 54 | throw new UnsupportedOperationException("close with timeout: "+timeout); 55 | } 56 | 57 | @Override 58 | public boolean isAvailable() { 59 | return super.isAvailable() && super.isWritable(); 60 | } 61 | 62 | @Override 63 | public MsgHandler getMsgHander() { 64 | return parent().getMsgHander(); 65 | } 66 | 67 | @Override 68 | public Tracer getTracer() { 69 | return parent().getTracer(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/utils/ChannelUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.utils; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import io.netty.channel.Channel; 24 | import io.netty.channel.ChannelFuture; 25 | import io.netty.channel.ChannelFutureListener; 26 | import io.netty.channel.ChannelHandlerContext; 27 | import io.netty.util.Attribute; 28 | import sailfish.remoting.constants.ChannelAttrKeys; 29 | 30 | /** 31 | * @author spccold 32 | * @version $Id: ChannelUtil.java, v 0.1 2016年11月23日 下午11:13:40 spccold Exp $ 33 | */ 34 | public class ChannelUtil { 35 | 36 | private static final Logger logger = LoggerFactory.getLogger(ChannelUtil.class); 37 | 38 | public static boolean clientSide(ChannelHandlerContext ctx) { 39 | Attribute clientSideAttr = ctx.channel().attr(ChannelAttrKeys.clientSide); 40 | return (null != clientSideAttr && null != clientSideAttr.get() && clientSideAttr.get()); 41 | } 42 | 43 | public static void closeChannel(final Channel channel) { 44 | if (null == channel) { 45 | return; 46 | } 47 | channel.close().addListener(new ChannelFutureListener() { 48 | @Override 49 | public void operationComplete(ChannelFuture future) throws Exception { 50 | String log = String.format( 51 | "closeChannel: close connection to remoteAddress [%s] , localAddress [%s], ret:[%b]", 52 | null != channel.remoteAddress() ? channel.remoteAddress().toString() : "null", 53 | null != channel.localAddress() ? channel.localAddress().toString() : "null", 54 | future.isSuccess()); 55 | logger.info(log); 56 | } 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/constants/ChannelAttrKeys.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.constants; 19 | 20 | import java.util.concurrent.CountDownLatch; 21 | 22 | import io.netty.util.AttributeKey; 23 | import sailfish.remoting.DefaultServer; 24 | import sailfish.remoting.channel.ExchangeChannelGroup; 25 | import sailfish.remoting.channel.HighPerformanceChannelWriter; 26 | import sailfish.remoting.configuration.NegotiateConfig; 27 | 28 | /** 29 | * @author spccold 30 | * @version $Id: ChannelAttrKeys.java, v 0.1 2016年11月6日 上午11:22:41 spccold Exp $ 31 | */ 32 | public interface ChannelAttrKeys { 33 | AttributeKey highPerformanceWriter = AttributeKey.valueOf("sailfish.highPerformanceWriter"); 34 | 35 | // for idle handle and heart beat 36 | AttributeKey maxIdleTimeout = AttributeKey.valueOf("sailfish.maxIdleTimeout"); 37 | AttributeKey lastReadTimeMillis = AttributeKey.valueOf("sailfish.lastReadTimeMillis"); 38 | 39 | // side 40 | AttributeKey clientSide = AttributeKey.valueOf("sailfish.side"); 41 | 42 | AttributeKey channelGroup = AttributeKey.valueOf("sailfish.channelGroup"); 43 | AttributeKey exchangeServer = AttributeKey.valueOf("sailfish.exchangeServer"); 44 | AttributeKey uuidStr = AttributeKey.valueOf("sailfish.uuidStr"); 45 | 46 | interface OneTime { 47 | // for idle handle 48 | AttributeKey idleTimeout = AttributeKey.valueOf("sailfish.idleTimeout"); 49 | AttributeKey awaitNegotiate = AttributeKey.valueOf("sailfish.awaitNegotiate"); 50 | 51 | AttributeKey channelConfig = AttributeKey.valueOf("sailfish.channelConfig"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/codec/RemotingDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.codec; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import io.netty.buffer.ByteBuf; 24 | import io.netty.channel.ChannelHandlerContext; 25 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 26 | import sailfish.remoting.constants.RemotingConstants; 27 | import sailfish.remoting.exceptions.ExceptionCode; 28 | import sailfish.remoting.exceptions.SailfishException; 29 | import sailfish.remoting.utils.ChannelUtil; 30 | 31 | /** 32 | * 33 | * @author spccold 34 | * @version $Id: RemotingDecoder.java, v 0.1 2016年10月15日 上午11:20:55 jileng Exp $ 35 | */ 36 | public class RemotingDecoder extends LengthFieldBasedFrameDecoder { 37 | 38 | private static final Logger logger = LoggerFactory.getLogger(RemotingDecoder.class); 39 | 40 | public RemotingDecoder() { 41 | super(RemotingConstants.DEFAULT_PAYLOAD_LENGTH, 2, 4); 42 | } 43 | 44 | // all read exceptions will be fired by exceptionCaught() 45 | @Override 46 | protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { 47 | ByteBuf buffer = (ByteBuf) super.decode(ctx, in); 48 | if (null == buffer) { 49 | return null; 50 | } 51 | try { 52 | return DefaultRemotingCodec.INSTANCE.decode(buffer); 53 | } catch (SailfishException cause) { 54 | if (cause.code() == ExceptionCode.BAD_PACKAGE) { 55 | String log = String.format("packet invalid, begin to close channel[{}], detail msg: {}", 56 | ctx.channel().toString(), cause.getMessage()); 57 | logger.error(log); 58 | ChannelUtil.closeChannel(ctx.channel()); 59 | return null; 60 | } 61 | throw cause; 62 | } finally { 63 | buffer.release(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/AbstractExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.UUID; 21 | 22 | import sailfish.remoting.RequestControl; 23 | import sailfish.remoting.ResponseCallback; 24 | import sailfish.remoting.exceptions.SailfishException; 25 | import sailfish.remoting.future.ResponseFuture; 26 | import sailfish.remoting.protocol.ResponseProtocol; 27 | 28 | /** 29 | * @author spccold 30 | * @version $Id: AbstractExchangeChannelGroup.java, v 0.1 2016年11月22日 下午3:57:38 spccold Exp $ 31 | */ 32 | public abstract class AbstractExchangeChannelGroup implements ExchangeChannelGroup{ 33 | private final UUID id; 34 | protected volatile boolean closed = false; 35 | 36 | protected AbstractExchangeChannelGroup(UUID id) { 37 | this.id = id; 38 | } 39 | 40 | @Override 41 | public UUID id() { 42 | return id; 43 | } 44 | 45 | @Override 46 | public void close() { 47 | close(0); 48 | } 49 | 50 | @Override 51 | public boolean isClosed() { 52 | return closed; 53 | } 54 | 55 | @Override 56 | public void oneway(byte[] data, RequestControl requestControl) throws SailfishException { 57 | next().oneway(data, requestControl); 58 | } 59 | 60 | @Override 61 | public ResponseFuture request(byte[] data, RequestControl requestControl) throws SailfishException { 62 | return next().request(data, requestControl); 63 | } 64 | 65 | @Override 66 | public void request(byte[] data, ResponseCallback callback, RequestControl requestControl) 67 | throws SailfishException { 68 | next().request(data, callback, requestControl); 69 | } 70 | 71 | @Override 72 | public void response(ResponseProtocol response) throws SailfishException { 73 | next().response(response); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/NettyPlatformIndependent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.concurrent.ThreadFactory; 21 | 22 | import io.netty.channel.Channel; 23 | import io.netty.channel.EventLoopGroup; 24 | import io.netty.channel.ServerChannel; 25 | import io.netty.channel.epoll.EpollEventLoopGroup; 26 | import io.netty.channel.epoll.EpollServerSocketChannel; 27 | import io.netty.channel.epoll.EpollSocketChannel; 28 | import io.netty.channel.nio.NioEventLoopGroup; 29 | import io.netty.channel.socket.nio.NioServerSocketChannel; 30 | import io.netty.channel.socket.nio.NioSocketChannel; 31 | import sailfish.remoting.utils.PlatformUtil; 32 | 33 | /** 34 | *
35 |  * nettynative-transports 
36 |  * Why native epoll support is introduced in Netty?
37 |  * 
38 | * 39 | * @author spccold 40 | * @version $Id: NettyPlatformIndependent.java, v 0.1 2016年11月14日 下午4:51:01 41 | * jileng Exp $ 42 | */ 43 | public class NettyPlatformIndependent { 44 | 45 | public static EventLoopGroup newEventLoopGroup(int nThreads, ThreadFactory threadFactory) { 46 | if (PlatformUtil.isLinux()) { 47 | return new EpollEventLoopGroup(nThreads, threadFactory); 48 | } 49 | return new NioEventLoopGroup(nThreads, threadFactory); 50 | } 51 | 52 | public static Class channelClass() { 53 | if (PlatformUtil.isLinux()) { 54 | return EpollSocketChannel.class; 55 | } 56 | return NioSocketChannel.class; 57 | } 58 | 59 | public static Class serverChannelClass() { 60 | if (PlatformUtil.isLinux()) { 61 | return EpollServerSocketChannel.class; 62 | } 63 | return NioServerSocketChannel.class; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/utils/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.utils; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: ArrayUtils.java, v 0.1 2016年11月10日 下午5:34:03 jileng Exp $ 24 | */ 25 | 26 | public class ArrayUtils { 27 | 28 | /** 29 | *

30 | * Checks if an array of Objects is empty or {@code null}. 31 | *

32 | * 33 | * @param array 34 | * the array to test 35 | * @return {@code true} if the array is empty or {@code null} 36 | * @since 2.1 37 | */ 38 | public static boolean isEmpty(T[] array) { 39 | return array == null || array.length == 0; 40 | } 41 | 42 | /** 43 | *

44 | * Checks if an array of Objects is not empty or not {@code null}. 45 | *

46 | * 47 | * @param 48 | * the component type of the array 49 | * @param array 50 | * the array to test 51 | * @return {@code true} if the array is not empty or not {@code null} 52 | * @since 2.5 53 | */ 54 | public static boolean isNotEmpty(T[] array) { 55 | return !isEmpty(array); 56 | } 57 | 58 | /** 59 | *

60 | * Checks if an array of primitive bytes is empty or {@code null}. 61 | *

62 | * 63 | * @param array 64 | * the array to test 65 | * @return {@code true} if the array is empty or {@code null} 66 | * @since 2.1 67 | */ 68 | public static boolean isEmpty(byte[] array) { 69 | return array == null || array.length == 0; 70 | } 71 | 72 | /** 73 | * reverse element array index 74 | * 75 | * @param length array length 76 | * @param index the element index 77 | * @return 78 | */ 79 | public static int reverseArrayIndex(int length, int index){ 80 | return length - index - 1; 81 | } 82 | } -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/processors/Response.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.processors; 19 | 20 | import sailfish.remoting.constants.CompressType; 21 | import sailfish.remoting.constants.SerializeType; 22 | 23 | /** 24 | * @author spccold 25 | * @version $Id: Response.java, v 0.1 2016年11月29日 下午5:18:01 spccold Exp $ 26 | */ 27 | public class Response { 28 | private boolean success; 29 | private byte serializeType = SerializeType.NON_SERIALIZE; 30 | private byte compressType = CompressType.NON_COMPRESS; 31 | private byte[] responseData; 32 | 33 | public Response(boolean success, byte[] responseData) { 34 | this.success = success; 35 | this.responseData = responseData; 36 | } 37 | 38 | /** 39 | * @return the success 40 | */ 41 | public boolean isSuccess() { 42 | return success; 43 | } 44 | 45 | /** 46 | * @param success the success to set 47 | */ 48 | public void setSuccess(boolean success) { 49 | this.success = success; 50 | } 51 | 52 | /** 53 | * @return the serializeType 54 | */ 55 | public byte getSerializeType() { 56 | return serializeType; 57 | } 58 | 59 | /** 60 | * @param serializeType the serializeType to set 61 | */ 62 | public void setSerializeType(byte serializeType) { 63 | this.serializeType = serializeType; 64 | } 65 | 66 | /** 67 | * @return the compressType 68 | */ 69 | public byte getCompressType() { 70 | return compressType; 71 | } 72 | 73 | /** 74 | * @param compressType the compressType to set 75 | */ 76 | public void setCompressType(byte compressType) { 77 | this.compressType = compressType; 78 | } 79 | 80 | /** 81 | * @return the responseData 82 | */ 83 | public byte[] getResponseData() { 84 | return responseData; 85 | } 86 | 87 | /** 88 | * @param responseData the responseData to set 89 | */ 90 | public void setResponseData(byte[] responseData) { 91 | this.responseData = responseData; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.UUID; 21 | 22 | import io.netty.channel.Channel; 23 | import sailfish.remoting.Endpoint; 24 | import sailfish.remoting.Tracer; 25 | import sailfish.remoting.exceptions.SailfishException; 26 | import sailfish.remoting.handler.MsgHandler; 27 | import sailfish.remoting.protocol.Protocol; 28 | import sailfish.remoting.protocol.ResponseProtocol; 29 | 30 | /** 31 | * 32 | * The {@link ExchangeChannelGroup} is responsible for providing the {@link ExchangeChannel}'s to use 33 | * via its {@link #next()} method. 34 | * 35 | * @author spccold 36 | * @version $Id: ExchangeChannelGroup.java, v 0.1 2016年11月21日 下午2:18:34 spccold 37 | * Exp $ 38 | */ 39 | public interface ExchangeChannelGroup extends Endpoint, MessageExchangePattern{ 40 | /** 41 | * like {@link Channel#id()}, Returns the globally unique identifier of this {@link ExchangeChannelGroup}. 42 | */ 43 | UUID id(); 44 | 45 | /** 46 | * Return {@code true} if this {@link ExchangeChannelGroup} is available, this means that the {@link ExchangeChannelGroup} 47 | * can receive bytes from remote peer or write bytes to remote peer 48 | */ 49 | boolean isAvailable(); 50 | 51 | /** 52 | * Returns one of the {@link ExchangeChannel}s managed by this {@link ExchangeChannelGroup}. 53 | */ 54 | ExchangeChannel next() throws SailfishException; 55 | 56 | /** 57 | * Return the {@link MsgHandler} of this {@link ExchangeChannelGroup} which used for process {@link ResponseProtocol} 58 | * sent by the {@link ExchangeChannelGroup} 59 | */ 60 | MsgHandler getMsgHander(); 61 | 62 | /** 63 | * Return the {@link Tracer} of this {@link ExchangeChannelGroup} which used for trace {@link ResponseProtocol} 64 | * sent by the {@link ExchangeChannelGroup} 65 | * @return 66 | */ 67 | Tracer getTracer(); 68 | } 69 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/LazyExchangeChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import io.netty.bootstrap.Bootstrap; 21 | import sailfish.remoting.RequestControl; 22 | import sailfish.remoting.ResponseCallback; 23 | import sailfish.remoting.exceptions.SailfishException; 24 | import sailfish.remoting.future.ResponseFuture; 25 | 26 | /** 27 | * @author spccold 28 | * @version $Id: LazyExchangeChannel.java, v 0.1 2016年11月21日 下午11:18:11 spccold Exp $ 29 | */ 30 | public final class LazyExchangeChannel extends SingleConnctionExchangeChannel { 31 | 32 | private volatile boolean lazyWithOutInit = true; 33 | 34 | LazyExchangeChannel(Bootstrap bootstrap, ExchangeChannelGroup parent, int reconnectInterval) 35 | throws SailfishException { 36 | super(bootstrap, parent, reconnectInterval, false); 37 | } 38 | 39 | @Override 40 | public void oneway(byte[] data, RequestControl requestControl) throws SailfishException { 41 | initChannel(); 42 | super.oneway(data, requestControl); 43 | } 44 | 45 | @Override 46 | public ResponseFuture request(byte[] data, RequestControl requestControl) throws SailfishException { 47 | initChannel(); 48 | return super.request(data, requestControl); 49 | } 50 | 51 | @Override 52 | public void request(byte[] data, ResponseCallback callback, RequestControl requestControl) 53 | throws SailfishException { 54 | initChannel(); 55 | super.request(data, callback, requestControl); 56 | } 57 | 58 | private void initChannel() throws SailfishException { 59 | if (null != channel) { 60 | return; 61 | } 62 | synchronized (this) { 63 | if (null != channel) { 64 | return; 65 | } 66 | this.channel = doConnect(); 67 | this.lazyWithOutInit = false; 68 | } 69 | } 70 | 71 | @Override 72 | public boolean isAvailable() { 73 | if (isClosed()) { 74 | return false; 75 | } 76 | if (this.lazyWithOutInit) { 77 | return true; 78 | } 79 | return super.isAvailable(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/codec/DefaultRemotingCodec.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.codec; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import sailfish.remoting.constants.RemotingConstants; 22 | import sailfish.remoting.exceptions.ExceptionCode; 23 | import sailfish.remoting.exceptions.SailfishException; 24 | import sailfish.remoting.protocol.RequestProtocol; 25 | import sailfish.remoting.protocol.ResponseProtocol; 26 | import sailfish.remoting.protocol.Protocol; 27 | 28 | /** 29 | * 30 | * @author spccold 31 | * @version $Id: DefaultRemotingCodec.java, v 0.1 2016年10月15日 下午4:52:20 jileng Exp $ 32 | */ 33 | public class DefaultRemotingCodec implements RemotingCodec{ 34 | 35 | public static final DefaultRemotingCodec INSTANCE = new DefaultRemotingCodec(); 36 | 37 | private DefaultRemotingCodec(){} 38 | 39 | @Override 40 | public void encode(Protocol protocol, ByteBuf buffer) throws SailfishException { 41 | protocol.serialize(buffer); 42 | } 43 | 44 | @Override 45 | public Protocol decode(ByteBuf buffer) throws SailfishException { 46 | short magic = buffer.readShort(); 47 | if(RemotingConstants.SAILFISH_MAGIC != magic){ 48 | throw new SailfishException(ExceptionCode.BAD_PACKAGE, 49 | "bad packet, expected magic:"+RemotingConstants.SAILFISH_MAGIC+", but actual:"+magic+", current channel will be closed!"); 50 | } 51 | 52 | int totalLength = buffer.readInt(); 53 | byte compactByte = buffer.getByte(buffer.readerIndex()); 54 | boolean request = ((compactByte & RequestProtocol.REQUEST_FLAG) != 0); 55 | 56 | Protocol protocol; 57 | //recycle Protocol 58 | if(request){//request 59 | protocol = RequestProtocol.newInstance(); 60 | }else{//response 61 | protocol = ResponseProtocol.newInstance(); 62 | } 63 | protocol.deserialize(buffer, totalLength); 64 | return protocol; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/utils/ParameterChecker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.utils; 19 | 20 | /** 21 | * 22 | * @author spccold 23 | * @version $Id: ParameterChecker.java, v 0.1 2016年10月26日 下午11:15:06 jileng Exp $ 24 | */ 25 | public class ParameterChecker { 26 | 27 | public static T checkNotNull(T reference, String hint) { 28 | if (reference == null) { 29 | throw new NullPointerException(hint); 30 | } 31 | return reference; 32 | } 33 | 34 | public static String checkNotBlank(String content, String hint){ 35 | if(StrUtils.isBlank(content)){ 36 | throw new IllegalArgumentException(hint); 37 | } 38 | return content; 39 | } 40 | 41 | public static int checkNotNegative(int number, String hint){ 42 | if(number < 0){ 43 | throw new IllegalArgumentException(hint + ": " + number + " (expected: >= 0)"); 44 | } 45 | return number; 46 | } 47 | 48 | public static int checkPositive(int number, String hint){ 49 | if(number <= 0){ 50 | throw new IllegalArgumentException(hint + ": " + number + " (expected: > 0)"); 51 | } 52 | return number; 53 | } 54 | 55 | public static short checkPositive(short number, String hint){ 56 | if(number <= 0){ 57 | throw new IllegalArgumentException(hint + ": " + number + " (expected: > 0)"); 58 | } 59 | return number; 60 | } 61 | 62 | public static long checkPositive(long number, String hint){ 63 | if(number <= 0){ 64 | throw new IllegalArgumentException(hint + ": " + number + " (expected: > 0)"); 65 | } 66 | return number; 67 | } 68 | 69 | public static int checkBytePositive(int number){ 70 | if(number <= 0 || number > 0x7F){ 71 | throw new IllegalArgumentException("number: " + number + " (expected: 0 < number <= 0x7F)"); 72 | } 73 | return number; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/Address.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.net.SocketAddress; 21 | 22 | import sailfish.remoting.utils.ParameterChecker; 23 | 24 | /** 25 | * remote address or local address 26 | * @author spccold 27 | * @version $Id: RemoteAddress.java, v 0.1 2016年10月26日 下午11:48:58 jileng Exp $ 28 | */ 29 | public class Address extends SocketAddress{ 30 | private static final long serialVersionUID = 1L; 31 | 32 | private String host; 33 | private int port; 34 | 35 | public Address(String host, int port) { 36 | this.host = ParameterChecker.checkNotBlank(host, "host"); 37 | this.port = checkPort(port); 38 | } 39 | 40 | public String host() { 41 | return host; 42 | } 43 | 44 | public int port() { 45 | return port; 46 | } 47 | 48 | private int checkPort(int port) { 49 | if (port < 0 || port > 0xFFFF) 50 | throw new IllegalArgumentException("port out of range:" + port); 51 | return port; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | final int prime = 31; 57 | int result = 1; 58 | result = prime * result + ((host == null) ? 0 : host.hashCode()); 59 | result = prime * result + port; 60 | return result; 61 | } 62 | 63 | @Override 64 | public boolean equals(Object obj) { 65 | if (this == obj) 66 | return true; 67 | if (obj == null) 68 | return false; 69 | if (getClass() != obj.getClass()) 70 | return false; 71 | Address other = (Address) obj; 72 | if (host == null) { 73 | if (other.host != null) 74 | return false; 75 | } else if (!host.equals(other.host)) 76 | return false; 77 | if (port != other.port) 78 | return false; 79 | return true; 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | return host+":"+port; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/DefaultClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import sailfish.remoting.channel.ExchangeChannelGroup; 21 | import sailfish.remoting.configuration.ExchangeClientConfig; 22 | import sailfish.remoting.exceptions.ExceptionCode; 23 | import sailfish.remoting.exceptions.SailfishException; 24 | import sailfish.remoting.future.ResponseFuture; 25 | 26 | /** 27 | * 28 | * @author spccold 29 | * @version $Id: DefaultClient.java, v 0.1 2016年10月31日 上午10:47:17 jileng Exp $ 30 | */ 31 | public class DefaultClient{ 32 | 33 | private ExchangeChannelGroup exchanger; 34 | 35 | public DefaultClient(ExchangeClientConfig config) throws SailfishException{ 36 | this.exchanger = Exchanger.connect(config); 37 | } 38 | 39 | public void oneway(byte[] data, RequestControl requestControl) throws SailfishException{ 40 | checkAvailable(); 41 | exchanger.oneway(data, requestControl); 42 | } 43 | 44 | public ResponseFuture request(byte[] data, RequestControl requestControl) throws SailfishException{ 45 | checkAvailable(); 46 | return exchanger.request(data, requestControl); 47 | } 48 | 49 | public void request(byte[] data, ResponseCallback callback, RequestControl requestControl) throws SailfishException{ 50 | checkAvailable(); 51 | exchanger.request(data, callback, requestControl); 52 | } 53 | 54 | public void close(){ 55 | this.exchanger.close(); 56 | } 57 | 58 | public void close(int timeout){ 59 | this.exchanger.close(timeout); 60 | } 61 | 62 | public boolean isClosed() { 63 | return exchanger.isClosed(); 64 | } 65 | 66 | public boolean isAvailable() { 67 | return exchanger.isAvailable(); 68 | } 69 | 70 | private void checkAvailable() throws SailfishException{ 71 | if(!isAvailable()){ 72 | throw new SailfishException(ExceptionCode.EXCHANGER_NOT_AVAILABLE, "exchange channel not available"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/RequestControl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import sailfish.remoting.utils.ParameterChecker; 21 | 22 | /** 23 | * 24 | * @author spccold 25 | * @version $Id: RequestControl.java, v 0.1 2016年11月1日 下午2:24:47 jileng Exp $ 26 | */ 27 | public class RequestControl { 28 | /** 29 | * response timeout or write timeout if {@code sent} is true 30 | */ 31 | private int timeout = 2000; 32 | private short opcode; 33 | private byte serializeType; 34 | private byte compressType; 35 | //wait write success or not 36 | private boolean sent; 37 | 38 | /** 39 | * {@code sent} will be ignored when {@code preferHighPerformanceWriter} is true 40 | */ 41 | private boolean preferHighPerformanceWriter; 42 | 43 | public RequestControl(){ 44 | this(false); 45 | } 46 | public RequestControl(boolean preferHighPerformanceWriter) { 47 | this.preferHighPerformanceWriter = preferHighPerformanceWriter; 48 | } 49 | 50 | public int timeout() { 51 | return timeout; 52 | } 53 | 54 | public void timeout(int timeout) { 55 | this.timeout = ParameterChecker.checkPositive(timeout, "timeout"); 56 | } 57 | 58 | public short opcode() { 59 | return opcode; 60 | } 61 | 62 | public void opcode(short opcode) { 63 | this.opcode = opcode; 64 | } 65 | 66 | public byte serializeType() { 67 | return serializeType; 68 | } 69 | 70 | public void serializeType(byte serializeType) { 71 | this.serializeType = serializeType; 72 | } 73 | 74 | public byte compressType() { 75 | return compressType; 76 | } 77 | 78 | public void compressType(byte compressType) { 79 | this.compressType = compressType; 80 | } 81 | 82 | public boolean sent() { 83 | return sent; 84 | } 85 | 86 | public void sent(boolean sent) { 87 | this.sent = sent; 88 | } 89 | 90 | public boolean preferHighPerformanceWriter(){ 91 | return preferHighPerformanceWriter; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/exceptions/SailfishException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.exceptions; 19 | 20 | import sailfish.remoting.utils.StrUtils; 21 | 22 | /** 23 | * 24 | * @author spccold 25 | * @version $Id: RemotingException.java, v 0.1 2016年10月27日 下午3:44:04 jileng Exp $ 26 | */ 27 | public class SailfishException extends Exception { 28 | 29 | /** */ 30 | private static final long serialVersionUID = 1L; 31 | private ExceptionCode errorCode; 32 | 33 | public SailfishException(String message) { 34 | super(message); 35 | } 36 | 37 | public SailfishException(Throwable cause){ 38 | super(cause); 39 | } 40 | 41 | public SailfishException(ExceptionCode errorCode, String message) { 42 | super(message(errorCode, message)); 43 | this.errorCode = errorCode; 44 | } 45 | 46 | public SailfishException(String message, Throwable cause) { 47 | super(message, cause); 48 | } 49 | 50 | public SailfishException(ExceptionCode errorCode, String message, Throwable cause) { 51 | super(message(errorCode, message), cause); 52 | this.errorCode = errorCode; 53 | } 54 | 55 | private static String message(ExceptionCode errorCode, String message) { 56 | if(null == errorCode){ 57 | errorCode = ExceptionCode.DEFAULT; 58 | } 59 | String prefix = "[errorCode:" + errorCode.toString() + "]"; 60 | return StrUtils.isBlank(message) ? prefix : prefix + ", "+ message; 61 | } 62 | 63 | public ExceptionCode code() { 64 | return errorCode; 65 | } 66 | 67 | public RemoteSailfishException toRemoteException() { 68 | return new RemoteSailfishException(errorCode, getMessage(), getCause()); 69 | } 70 | 71 | /** 72 | * exception for remote peer 73 | */ 74 | class RemoteSailfishException extends SailfishException{ 75 | private static final long serialVersionUID = 1L; 76 | public RemoteSailfishException(ExceptionCode errorCode, String message, Throwable cause) { 77 | super(errorCode, message, cause); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | sailfish 5 | sailfish 6 | 0.0.1-SNAPSHOT 7 | pom 8 | sailfish 9 | A reliable, scalable, fast remoting module for rapid development of high performance network program 10 | https://github.com/spccold/sailfish 11 | 2016.10.01 10:30:00 12 | 13 | spccold 14 | https://github.com/spccold 15 | 16 | 17 | GitHub Issue Management 18 | https://github.com/spccold/sailfish/issues 19 | 20 | 21 | sailfish-kernel 22 | 23 | 24 | 25 | 4.1.6.Final 26 | 1.7.6 27 | 1.1.2 28 | 4.11 29 | 30 | 31 | 32 | 33 | org.slf4j 34 | slf4j-api 35 | ${slf4j.version} 36 | 37 | 38 | ch.qos.logback 39 | logback-core 40 | ${logback.version} 41 | test 42 | 43 | 44 | ch.qos.logback 45 | logback-classic 46 | ${logback.version} 47 | test 48 | 49 | 50 | junit 51 | junit 52 | ${junit.version} 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | io.netty 61 | netty-all 62 | ${netty.version} 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-compiler-plugin 72 | 3.3 73 | 74 | 1.7 75 | 1.7 76 | 77 | 78 | 79 | 80 | 81 | 82 | https://github.com/spccold/sailfish 83 | scm:git:https://github.com/spccold/sailfish 84 | scm:git:https://github.com/spccold/sailfish 85 | 86 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/Exchanger.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import sailfish.remoting.channel.DefaultExchangeChannelGroup; 21 | import sailfish.remoting.channel.ExchangeChannelGroup; 22 | import sailfish.remoting.channel.ReadWriteExchangeChannelGroup; 23 | import sailfish.remoting.configuration.AbstractExchangeConfig; 24 | import sailfish.remoting.configuration.ExchangeClientConfig; 25 | import sailfish.remoting.configuration.ExchangeServerConfig; 26 | import sailfish.remoting.exceptions.SailfishException; 27 | import sailfish.remoting.handler.DefaultMsgHandler; 28 | import sailfish.remoting.handler.MsgHandler; 29 | import sailfish.remoting.protocol.Protocol; 30 | import sailfish.remoting.utils.ParameterChecker; 31 | 32 | /** 33 | * 34 | * @author spccold 35 | * @version $Id: Exchanger.java, v 0.1 2016年10月26日 下午11:40:38 jileng Exp $ 36 | */ 37 | public class Exchanger { 38 | 39 | public static ExchangeChannelGroup connect(ExchangeClientConfig config) throws SailfishException { 40 | checkConfig(config); 41 | MsgHandler msgHandler = new DefaultMsgHandler(config.getRequestProcessors()); 42 | switch (config.mode()) { 43 | case simple: 44 | case multiconns: 45 | return new DefaultExchangeChannelGroup(msgHandler, config.address(), config.connections(), 46 | config.connectTimeout(), config.reconnectInterval(), config.idleTimeout(), config.idleTimeout(), 47 | config.isLazyConnection(), config.reversed(), config.getEventLoopGroup(), 48 | config.getEventExecutorGroup()); 49 | case readwrite: 50 | return new ReadWriteExchangeChannelGroup(msgHandler, config.address(), config.connectTimeout(), 51 | config.reconnectInterval(), config.idleTimeout(), config.maxIdleTimeout(), 52 | config.isLazyConnection(), config.connections(), config.writeConnections(), config.reversed(), 53 | config.getEventLoopGroup(), config.getEventExecutorGroup()); 54 | default: 55 | throw new IllegalArgumentException("invalid channel mode"); 56 | } 57 | } 58 | 59 | public static DefaultServer bind(ExchangeServerConfig config) throws SailfishException { 60 | checkConfig(config); 61 | return new DefaultServer(config); 62 | } 63 | 64 | private static void checkConfig(AbstractExchangeConfig config) { 65 | ParameterChecker.checkNotNull(config, "exchange config").check(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/DefaultExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import io.netty.bootstrap.Bootstrap; 21 | import io.netty.channel.EventLoopGroup; 22 | import io.netty.util.concurrent.EventExecutorGroup; 23 | import sailfish.remoting.Address; 24 | import sailfish.remoting.Tracer; 25 | import sailfish.remoting.configuration.NegotiateConfig; 26 | import sailfish.remoting.exceptions.SailfishException; 27 | import sailfish.remoting.handler.MsgHandler; 28 | import sailfish.remoting.protocol.Protocol; 29 | 30 | /** 31 | * @author spccold 32 | * @version $Id: DefaultExchangeChannelGroup.java, v 0.1 2016年11月22日 下午4:10:23 spccold Exp $ 33 | */ 34 | public final class DefaultExchangeChannelGroup extends MultiConnectionsExchangeChannelGroup { 35 | 36 | public DefaultExchangeChannelGroup(MsgHandler msgHandler, Address address, short connections, 37 | int connectTimeout, int reconnectInterval, byte idleTimeout, byte maxIdleTimeOut, boolean lazy, 38 | boolean reverseIndex, EventLoopGroup loopGroup, EventExecutorGroup executorGroup) throws SailfishException { 39 | super(new Tracer(), msgHandler, address, connections, connectTimeout, reconnectInterval, idleTimeout, 40 | maxIdleTimeOut, lazy, reverseIndex, null, null, loopGroup, executorGroup); 41 | } 42 | 43 | public DefaultExchangeChannelGroup(Tracer tracer, MsgHandler msgHandler, Address address, 44 | short connections, int connectTimeout, int reconnectInterval, byte idleTimeout, byte maxIdleTimeOut, 45 | boolean lazy, boolean reverseIndex, NegotiateConfig config, ExchangeChannelGroup parentGroup, 46 | EventLoopGroup loopGroup, EventExecutorGroup executorGroup) throws SailfishException { 47 | super(tracer, msgHandler, address, connections, connectTimeout, reconnectInterval, idleTimeout, maxIdleTimeOut, 48 | lazy, reverseIndex, config, parentGroup, loopGroup, executorGroup); 49 | } 50 | 51 | /** 52 | * {@link ReadWriteExchangeChannelGroup}'s read connections must be initialed eagerly whether 53 | * lazy is true or false 54 | */ 55 | @Override 56 | protected ExchangeChannel newChild(ExchangeChannelGroup parent, Bootstrap bootstrap, int reconnectInterval, 57 | boolean lazy, boolean readChannel) throws SailfishException { 58 | if (lazy && (!readChannel)) { 59 | return new LazyExchangeChannel(bootstrap, parent, reconnectInterval); 60 | } 61 | return new EagerExchangeChannel(bootstrap, parent, reconnectInterval); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sailfish 2 | * A reliable, scalable, fast remoting module for rapid development of high performance network program 3 | * with so many advanced features, I guarantee you'll like it! 4 | 5 | ## notice 6 | * 实验室作品,未经过任何严格生产环境的检验 7 | * 仅希望可以给你一些启示和灵感 8 | 9 | ## design concept 10 | * 一个通用的remoting模块应该致力于tcp连接的管理(新建, 关闭, 流控, 复用, 异常恢复等)和简单二进制数据的传输,除此之外不应该再承担任何责任 11 | 12 | ## sailfish's advantage 13 | * 精简的二进制协议 14 | * linux上epoll(`edge-triggered`)的支持 15 | * 支持高性能写, 单连接的写操作越多,收益越明显(自动合并多个写操作, 减少不必要的flush,每次flush都是一次系统调用,具有较大的开销) 16 | * 多种channle模型(多连接时可以设置server端是否反转channle) 17 | * 单连接 18 | * egaer channel, 在初始化客户端的时候就初始化连接 19 | * lazy channel, 在第一次真正发起调用的时候初始化连接 20 | * 多连接 21 | * 所有的连接会以round-robin分给所有的请求 22 | * 多连接,但支持读写分离(一部分连接用于读, 其它连接用于写) 23 | * 对于需要大量重复创建并且生命周期短的对象用netty recycler进行回收利用,减少对象创建的开销和gc的压力 24 | * 序列化,压缩方式无关的设计,因为sailfish只进行二进制的传输,至于你使用何种序列化,压缩方式,是不关心的(`但是会把这些信息传输到对端`),这意味着请求和响应可以采用不同的序列化和压缩方式,并且每一次请求都可以使用不同的序列化和压缩方式(`你可能根据数据量,对象类型采用不同的序列化方式`),只要你把此次请求的序列化,压缩方式通过协议传输给对端就好了 25 | 26 | 27 | ## sailfish channel mode 28 | ![channelmode](https://github.com/spccold/sailfish/blob/master/sailfish-channelmode.png) 29 | 30 | * 多连接 31 | * 3个连接,分别为channel0, channel1, channel2,客户端抽象为`DefaultExchangeChannelGroup`, 服务端抽象为`ServerExchangeChannelGroup` 32 | * 客户端的所有请求和响应调用channel的次序为channel0->channel1->channel2->channel0->......(round-robin) 33 | * 服务单的所有请求和响应调用channel的次序为channel2->channel1->channel0->channel2->......(round-robin) 34 | * 多连接+读写分离(客户端的写连接对应服务端的读连接,客户端的读连接对应服务端的写连接) 35 | * 5个连接,客户端抽象为`ReadWriteExchangeChannelGroup`, 包含2个读连接, 3个写连接, 服务端抽象为`ReadWriteServerExchangeChannelGroup`, 包含2个写连接(`对应客户端的读连接`),3个读连接(`对应客户端的写连接`) 36 | * 客户端的所有请求和响应调用channel的次序为writeChannel0->writeChannel1->writeChannel2->writeChannel0->......(round-robin), 如果所有的写连接不可用则尝试读连接,次序为readChannel0->readChannel1->readChannel0->......(round-robin) 37 | * 服务单的所有请求和响应调用channel的次序为writeChannel1->writeChannel0->writeChannel1......(round-robin), 如果所有的写连接不可用则尝试读连接,次序为readChannel2->readChannel1->readChannel0->readChannel2->......(round-robin) 38 | 39 | 40 | ## todo 41 | * 服务端还没有很好的标识客户端连接,反向调用时没办法很好的指定特定的连接, 现在只能获得所有的客户端连接快照(`通过appName, group或者一些tags区分,这个还有待考虑`) 42 | * 缺乏极端场景下的集成和自动化测试 43 | * 自己笔记本上简单的测试了一下和dubbo的性能对比,相同的连接数,相同的业务处理线程数,相同的业务逻辑,相同的序列化方式,无压缩的场景下,sailfish同步调用的吞吐量是dubbo的1.5倍, 异步调用的吞吐量是dubbo的4倍,当然这个数据不具备很高的可信性,还有待多主机之间真实的测试 44 | 45 | 46 | ## netty's recycler 47 | * 对象的创建与回收 48 | * 创建 49 | * 是否在固定的线程中创建, Recycler依赖ThreadLocal或者FastThreadLocal(in netty), 与之关联这例如Stack,Handle等对象,在集中的线程中使用Recycler可以减少对象的创建 50 | * 线程是否是FastThreadLocalThread,FastThreadLocalThread在ThreadLocal实现上可以获取速度优势,Recycler又是依赖ThreadLocal的,所以尽可能在FastThreadLocalThread中使用Recycler(DefaultThreadFactory产生的线程就是FastThreadLocalThread) 51 | 52 | * 回收 53 | * 是否在创建的线程中回收, 这样在recycle时是最及时的 54 | * 是否在多个异步线程中回收, 异步线程中recycle会产生WeakOrderQueue, 一个线程对应一个WeakOrderQueue(所有Queue形成一个链),所以在固定或者相同的线程中回收既可以提高recycle速度,也可以减少WeakOrderQueue的创建 55 | 56 | ## some articles about netty 57 | * http://netty.io/wiki/using-as-a-generic-library.html 58 | * http://netty.io/wiki/thread-affinity.html 59 | * http://www.antonkharenko.com/2015/08/netty-best-practices-distilled.html 60 | * https://github.com/netty/netty/issues/1759 61 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/ReconnectManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.concurrent.ScheduledThreadPoolExecutor; 21 | import java.util.concurrent.ThreadFactory; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import sailfish.remoting.channel.ExchangeChannel; 28 | 29 | /** 30 | * 31 | * @author spccold 32 | * @version $Id: ReconnectManager.java, v 0.1 2016年11月7日 下午4:26:22 jileng Exp $ 33 | */ 34 | public class ReconnectManager { 35 | private static final Logger logger = LoggerFactory.getLogger(ReconnectManager.class); 36 | public static final ReconnectManager INSTANCE = new ReconnectManager(); 37 | private final ScheduledThreadPoolExecutor reconnectExecutor; 38 | private ReconnectManager(){ 39 | reconnectExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { 40 | @Override 41 | public Thread newThread(Runnable r) { 42 | Thread t = new Thread(); 43 | t.setName("sailfish-ReconnectManager"); 44 | t.setDaemon(true); 45 | return t; 46 | } 47 | }); 48 | reconnectExecutor.setKeepAliveTime(1, TimeUnit.MINUTES); 49 | } 50 | 51 | public void addReconnectTask(ExchangeChannel reconnectedChannel, int reconnectInterval){ 52 | reconnectExecutor.schedule(new ReconnectTask(reconnectedChannel, reconnectInterval), 0, TimeUnit.MILLISECONDS); 53 | } 54 | 55 | private class ReconnectTask implements Runnable{ 56 | private final ExchangeChannel reconnectedChannel; 57 | private final int reconnectInterval; 58 | public ReconnectTask(ExchangeChannel reconnectedChannel, int reconnectInterval) { 59 | this.reconnectedChannel = reconnectedChannel; 60 | this.reconnectInterval = reconnectInterval; 61 | } 62 | 63 | @Override 64 | public void run() { 65 | if(reconnectedChannel.isClosed()){ 66 | return; 67 | } 68 | try{ 69 | reconnectedChannel.update(reconnectedChannel.doConnect()); 70 | }catch(Throwable cause){ 71 | String msg = "reconnect to remoteAddress[%s] fail"; 72 | logger.error(String.format(msg, reconnectedChannel.remoteAdress().toString(), cause)); 73 | //reconnect next time 74 | ReconnectManager.this.reconnectExecutor.schedule(this, reconnectInterval, TimeUnit.MILLISECONDS); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /best-practice-in-netty.md: -------------------------------------------------------------------------------- 1 | # best practice in netty 2 | * writeAndFlush不要一直调用, 是否可以通过调用write,并且在适当的时间flush,因为每次系统flush都是一次系统调用,如果可以的话write的调用次数也应该减少,因为它会经过整个pipeline(https://github.com/netty/netty/issues/1759) 3 | * 如果你不是很关注write的结果,可以使用channel.voidPromise(),可以减少对象的创建 4 | * 一直写对于处理能力较弱的接受者来说,可能会引起OutMemoryError,关注channel.isWritable()和channelhandler中的cahnnelWritabilityChanged()将会很有帮助,channel.bytesBeforeUnwritable和channel.bytesBeforeWritable()同样值得关注 5 | * 关注write_buffer_high_water_mark和write_buffer_low_water_mark的配置, 例如high:32kb(default 64kb), low:8kb(default 32kb) 6 | * 可以通过channelpipeline 触发custome events (pipeline.fireUserEventTriggered(MyCustomEvent)), 可以在DuplexChannelHandler中处理相应的事件 7 | * use pooledBytebuffer as default channel options(since netty 4.1.x) 8 | * only use heap buffres if need to operate on byte[] in channeloutboundhandler! (perfer to use direct buffers always), take this as rule of thumb 9 | * ByteBuf.forEanchByte()(via ByteBufProcessor) fast than normal find via range check, because 10 | * can eliminate range checks 11 | * can be created and shared 12 | * easier to inline by the JIT 13 | * use it whenever you need to find some pattern in a ByteBuf 14 | * other buffer tips 15 | * alloc() over Unpooled 16 | * slice(), duplicate() over copy 17 | * bulk operation over loops 18 | * never block the EventLoop 19 | * Thread.sleep() 20 | * CountDownLatch.await() or any other blocking operation from java.util.concurrent 21 | * long-lived computationally in intensive operations 22 | * Blocking operations that might take a while(e.g. DB query) 23 | * Schedule and execute tasks via EventLoop this reduces the needed Threads and also makes it's Thread-safe(notice: the task should be lightweight) 24 | * Re-use EventLoopGroup if you can 25 | * proxy like application which reduce context-switching to minimum by share the same EventLoop between both channels, like 26 | 27 | public class ProxyHandler extends ChannelInboundHandlerAdapter{ 28 | public void channelActive(ChannelHandlerContext ctx){ 29 | final channel inboundchannel = ctx.channel(); 30 | Bootstrap b = new Bootstrap(); 31 | b.group(inboundchannel.eventLoop()); 32 | ... 33 | ChannelFuture f = b.connect(remoteHost, remotePort); 34 | ... 35 | } 36 | } 37 | * always combine operations if possible when act on channel from outside the eventloop to reduce overhead of wakeups and object creation! 38 | 39 | //Not recommended, will create two Runnable Object 40 | channel.write(msg1); 41 | channel.writeAndFlush(msg3); 42 | //Combine for the WIN! 43 | channel.eventLoop().execute(new Runnable(){ 44 | public void tun(){ 45 | channel.write(msg1); 46 | channel.writeAndFlush(msg3); 47 | }}); 48 | * perfer to use ChannelHandlerContext to writeAndFlush than Channel, because shortest path as possible to get maximal performance 49 | * Share ChannelHandlers if stateless(@ChannelHandler.Shareable) 50 | * Remove ChannelHandler once not needed anymore, this keep the channelPipeline as short as possible and so eliminate overhead of traversing as much as possible(e.g. UniPortHandler) 51 | * channel auto-read option? when did the channelReadComplete occur? i can't understand 52 | * epoll for linux 53 | * Less gc as NIO 54 | * Less synchronization 55 | * Edge-Triggered! 56 | * Supports TCP_CORK (with channel option) -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/EmptyExchangeChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.net.SocketAddress; 21 | import java.util.UUID; 22 | 23 | import io.netty.channel.Channel; 24 | import sailfish.remoting.RequestControl; 25 | import sailfish.remoting.ResponseCallback; 26 | import sailfish.remoting.Tracer; 27 | import sailfish.remoting.exceptions.SailfishException; 28 | import sailfish.remoting.future.ResponseFuture; 29 | import sailfish.remoting.handler.MsgHandler; 30 | import sailfish.remoting.protocol.Protocol; 31 | import sailfish.remoting.protocol.ResponseProtocol; 32 | 33 | /** 34 | * for test or something else 35 | * @author spccold 36 | * @version $Id: EmptyExchangeChannel.java, v 0.1 2016年11月25日 下午8:37:47 spccold Exp $ 37 | */ 38 | public class EmptyExchangeChannel implements ExchangeChannel{ 39 | 40 | @Override 41 | public UUID id() { 42 | return null; 43 | } 44 | 45 | @Override 46 | public boolean isAvailable() { 47 | return false; 48 | } 49 | 50 | @Override 51 | public MsgHandler getMsgHander() { 52 | return null; 53 | } 54 | 55 | @Override 56 | public Tracer getTracer() { 57 | return null; 58 | } 59 | 60 | @Override 61 | public void close() { 62 | } 63 | 64 | @Override 65 | public void close(int timeout) { 66 | } 67 | 68 | @Override 69 | public boolean isClosed() { 70 | return false; 71 | } 72 | 73 | @Override 74 | public void oneway(byte[] data, RequestControl requestControl) throws SailfishException { 75 | } 76 | 77 | @Override 78 | public ResponseFuture request(byte[] data, RequestControl requestControl) throws SailfishException { 79 | return null; 80 | } 81 | 82 | @Override 83 | public void request(byte[] data, ResponseCallback callback, RequestControl requestControl) 84 | throws SailfishException { 85 | } 86 | 87 | @Override 88 | public void response(ResponseProtocol response) throws SailfishException { 89 | } 90 | 91 | @Override 92 | public ExchangeChannel next() { 93 | return this; 94 | } 95 | 96 | @Override 97 | public ExchangeChannelGroup parent() { 98 | return null; 99 | } 100 | 101 | @Override 102 | public Channel update(Channel newChannel) { 103 | return null; 104 | } 105 | 106 | @Override 107 | public Channel doConnect() throws SailfishException { 108 | return null; 109 | } 110 | 111 | @Override 112 | public void recover() { 113 | } 114 | 115 | @Override 116 | public SocketAddress localAddress() { 117 | return null; 118 | } 119 | 120 | @Override 121 | public SocketAddress remoteAdress() { 122 | return null; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ReadWriteServerExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.UUID; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import sailfish.remoting.Tracer; 26 | import sailfish.remoting.configuration.NegotiateConfig; 27 | import sailfish.remoting.exceptions.SailfishException; 28 | import sailfish.remoting.handler.MsgHandler; 29 | import sailfish.remoting.protocol.Protocol; 30 | 31 | /** 32 | * @author spccold 33 | * @version $Id: ReadWriteServerExchangeChannelGroup.java, v 0.1 2016年11月24日 下午6:40:27 spccold Exp $ 34 | */ 35 | public final class ReadWriteServerExchangeChannelGroup extends ReferenceCountedServerExchangeChannelGroup{ 36 | 37 | private static final Logger logger = LoggerFactory.getLogger(ReadWriteServerExchangeChannelGroup.class); 38 | 39 | private final ServerExchangeChannelGroup readGroup; 40 | private final ServerExchangeChannelGroup writeGroup; 41 | 42 | private final MsgHandler msgHandler; 43 | private final Tracer tracer; 44 | 45 | public ReadWriteServerExchangeChannelGroup(MsgHandler msgHandler, UUID id, int connctions, int writeConnections) { 46 | super(id); 47 | 48 | this.msgHandler = msgHandler; 49 | this.tracer = new Tracer(); 50 | //read to write, write to read 51 | this.writeGroup = new ServerExchangeChannelGroup(tracer, msgHandler, id, connctions - writeConnections); 52 | this.readGroup = new ServerExchangeChannelGroup(tracer, msgHandler, id, writeConnections); 53 | } 54 | 55 | @Override 56 | public ExchangeChannel next() throws SailfishException { 57 | try { 58 | return writeGroup.next(); 59 | } catch (SailfishException cause) { 60 | logger.warn("writeGroup not available, try to choose readGroup", cause); 61 | } 62 | //try readGroup if writeGroup no available 63 | return readGroup.next(); 64 | } 65 | 66 | @Override 67 | public boolean isAvailable() { 68 | if (writeGroup.isAvailable()) { 69 | return true; 70 | } 71 | return readGroup.isAvailable(); 72 | } 73 | 74 | @Override 75 | public void close(int timeout) { 76 | throw new UnsupportedOperationException("close with timeout: "+timeout); 77 | } 78 | 79 | @Override 80 | public MsgHandler getMsgHander() { 81 | return msgHandler; 82 | } 83 | 84 | @Override 85 | public Tracer getTracer() { 86 | return tracer; 87 | } 88 | 89 | @Override 90 | public void addChild(ExchangeChannel channel, NegotiateConfig config) { 91 | //read to write, write to read 92 | if(config.isRead()){ 93 | writeGroup.addChild(channel, config); 94 | }else if(config.isWrite()){ 95 | readGroup.addChild(channel, config); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ServerExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.UUID; 21 | 22 | import sailfish.remoting.Tracer; 23 | import sailfish.remoting.configuration.NegotiateConfig; 24 | import sailfish.remoting.exceptions.SailfishException; 25 | import sailfish.remoting.handler.MsgHandler; 26 | import sailfish.remoting.protocol.Protocol; 27 | 28 | /** 29 | * @author spccold 30 | * @version $Id: ServerExchangeChannelGroup.java, v 0.1 2016年11月24日 下午6:22:36 spccold Exp $ 31 | */ 32 | public final class ServerExchangeChannelGroup extends ReferenceCountedServerExchangeChannelGroup{ 33 | 34 | private final ExchangeChannel[] children; 35 | private final ExchangeChannel[] deadChildren; 36 | private final ExchangeChannelChooserFactory.ExchangeChannelChooser chooser; 37 | 38 | private final MsgHandler msgHandler; 39 | private final Tracer tracer; 40 | 41 | public ServerExchangeChannelGroup(MsgHandler msgHandler, UUID id, int connections){ 42 | this(new Tracer(), msgHandler, id, connections); 43 | } 44 | 45 | public ServerExchangeChannelGroup(Tracer tracer, MsgHandler msgHandler, UUID id, int connections) { 46 | super(id); 47 | 48 | this.msgHandler = msgHandler; 49 | this.tracer = tracer; 50 | 51 | children = new ExchangeChannel[connections]; 52 | deadChildren = new ExchangeChannel[connections]; 53 | 54 | chooser = DefaultExchangeChannelChooserFactory.INSTANCE.newChooser(children, deadChildren); 55 | } 56 | 57 | @Override 58 | public ExchangeChannel next() throws SailfishException { 59 | return chooser.next(); 60 | } 61 | 62 | @Override 63 | public boolean isAvailable() { 64 | if(children.length == 1){//one connection check 65 | return (null != children[0] && children[0].isAvailable()); 66 | } 67 | 68 | // can hit most of the time 69 | if (deadChildren[0] == null || deadChildren[0].isAvailable()) { 70 | return true; 71 | } 72 | 73 | for (int i = 1; i < children.length; i++) { 74 | if (deadChildren[i] == null || deadChildren[i].isAvailable()) { 75 | return true; 76 | } 77 | } 78 | return false; 79 | } 80 | 81 | @Override 82 | public void close(int timeout) { 83 | throw new UnsupportedOperationException("close with timeout: "+timeout); 84 | } 85 | 86 | @Override 87 | public MsgHandler getMsgHander() { 88 | return msgHandler; 89 | } 90 | 91 | @Override 92 | public Tracer getTracer() { 93 | return tracer; 94 | } 95 | 96 | @Override 97 | public void addChild(ExchangeChannel channel, NegotiateConfig config) { 98 | children[config.index()] = channel; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/ServerClientTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collection; 22 | import java.util.List; 23 | import java.util.concurrent.locks.LockSupport; 24 | 25 | import org.junit.Assert; 26 | import org.junit.Test; 27 | 28 | import io.netty.util.CharsetUtil; 29 | import sailfish.remoting.channel.ExchangeChannelGroup; 30 | import sailfish.remoting.configuration.ExchangeClientConfig; 31 | import sailfish.remoting.configuration.ExchangeServerConfig; 32 | import sailfish.remoting.future.ResponseFuture; 33 | import sailfish.remoting.processors.ClientRequestProcessor; 34 | import sailfish.remoting.processors.RequestProcessor; 35 | import sailfish.remoting.processors.ServerRequestProcessor; 36 | 37 | /** 38 | * @author spccold 39 | * @version $Id: ServerClientTest.java, v 0.1 2016年11月29日 下午10:09:08 spccold Exp $ 40 | */ 41 | public class ServerClientTest { 42 | 43 | private static final int originPort = 13143; 44 | private static final byte[] clientRequestData = "client request".getBytes(CharsetUtil.UTF_8); 45 | private static final byte[] serverRequestData = "server request".getBytes(CharsetUtil.UTF_8); 46 | 47 | @Test 48 | public void test() throws Exception{ 49 | ExchangeServerConfig serverConfig = new ExchangeServerConfig(); 50 | serverConfig.address(new Address("localhost", originPort)); 51 | List processors = new ArrayList<>(); 52 | processors.add(new ServerRequestProcessor()); 53 | serverConfig.setRequestProcessors(processors); 54 | DefaultServer server = Exchanger.bind(serverConfig); 55 | server.start(); 56 | 57 | ExchangeClientConfig clientConfig = new ExchangeClientConfig(); 58 | processors.clear(); 59 | processors.add(new ClientRequestProcessor()); 60 | clientConfig.setRequestProcessors(processors); 61 | clientConfig.address(new Address("localhost", originPort)); 62 | DefaultClient client = new DefaultClient(clientConfig); 63 | RequestControl control = new RequestControl(); 64 | 65 | //wait server side deal negotiate finished 66 | LockSupport.parkNanos(1000 * 1000 * 100); 67 | //server send request to client 68 | Collection channelGroups = server.listChannelGroups(); 69 | Assert.assertNotNull(channelGroups); 70 | Assert.assertTrue(channelGroups.size() == 1); 71 | ExchangeChannelGroup channelGroup = (ExchangeChannelGroup)channelGroups.toArray()[0]; 72 | ResponseFuture serverFuture = channelGroup.request(serverRequestData, control); 73 | Assert.assertArrayEquals(serverRequestData, serverFuture.get()); 74 | 75 | //client send request to server 76 | ResponseFuture clientFuture = client.request(clientRequestData, control); 77 | Assert.assertArrayEquals(clientRequestData, clientFuture.get()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/handler/HeartbeatChannelHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.handler; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import io.netty.channel.ChannelDuplexHandler; 24 | import io.netty.channel.ChannelHandler; 25 | import io.netty.channel.ChannelHandlerContext; 26 | import io.netty.handler.timeout.IdleStateEvent; 27 | import sailfish.remoting.constants.ChannelAttrKeys; 28 | import sailfish.remoting.protocol.RequestProtocol; 29 | import sailfish.remoting.utils.ChannelUtil; 30 | 31 | /** 32 | * @author spccold 33 | * @version $Id: HeartbeatHandler.java, v 0.1 2016年11月23日 下午9:21:35 spccold Exp $ 34 | */ 35 | @ChannelHandler.Sharable 36 | public class HeartbeatChannelHandler extends ChannelDuplexHandler { 37 | 38 | private static final Logger logger = LoggerFactory.getLogger(HeartbeatChannelHandler.class); 39 | public static final HeartbeatChannelHandler INSTANCE = new HeartbeatChannelHandler(); 40 | 41 | private HeartbeatChannelHandler() {} 42 | 43 | @Override 44 | public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 45 | if (ctx.channel().isActive() && ctx.channel().isRegistered()) { 46 | ctx.channel().attr(ChannelAttrKeys.lastReadTimeMillis).set(System.currentTimeMillis()); 47 | } 48 | } 49 | 50 | @Override 51 | public void channelRegistered(ChannelHandlerContext ctx) throws Exception { 52 | if (ctx.channel().isActive()) { 53 | ctx.channel().attr(ChannelAttrKeys.lastReadTimeMillis).set(System.currentTimeMillis()); 54 | } 55 | ctx.fireChannelRegistered(); 56 | } 57 | 58 | @Override 59 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 60 | ctx.channel().attr(ChannelAttrKeys.lastReadTimeMillis).set(System.currentTimeMillis()); 61 | ctx.fireChannelActive(); 62 | } 63 | 64 | @Override 65 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 66 | ctx.channel().attr(ChannelAttrKeys.lastReadTimeMillis).set(System.currentTimeMillis()); 67 | ctx.fireChannelReadComplete(); 68 | } 69 | 70 | @Override 71 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 72 | if (evt instanceof IdleStateEvent) { 73 | int maxIdleTimeout = ctx.channel().attr(ChannelAttrKeys.maxIdleTimeout).get(); 74 | long expireTime = System.currentTimeMillis() - ctx.channel().attr(ChannelAttrKeys.lastReadTimeMillis).get(); 75 | if (expireTime >= maxIdleTimeout * 1000) { 76 | logger.warn("readIdleTimeout exceed maxIdleTimeout, real timeout {}, this channel[{}] will be closed", 77 | expireTime, ctx.channel().toString()); 78 | ChannelUtil.closeChannel(ctx.channel()); 79 | } else if (ChannelUtil.clientSide(ctx)) { 80 | // send heart beat to remote peer 81 | ctx.writeAndFlush(RequestProtocol.newHeartbeat()); 82 | } 83 | } else { 84 | ctx.fireUserEventTriggered(evt); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ReferenceCountedServerExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import static io.netty.util.internal.ObjectUtil.checkPositive; 21 | 22 | import java.util.UUID; 23 | import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; 24 | 25 | import io.netty.util.IllegalReferenceCountException; 26 | import io.netty.util.internal.PlatformDependent; 27 | import sailfish.remoting.configuration.NegotiateConfig; 28 | import sailfish.remoting.handler.NegotiateChannelHandler; 29 | 30 | /** 31 | * @author spccold 32 | * @version $Id: ReferenceCountedServerExchangeChannelGroup.java, v 0.1 2016年11月26日 下午5:05:43 33 | * spccold Exp $ 34 | */ 35 | public abstract class ReferenceCountedServerExchangeChannelGroup extends AbstractExchangeChannelGroup { 36 | 37 | private static final AtomicIntegerFieldUpdater refCntUpdater; 38 | 39 | static { 40 | AtomicIntegerFieldUpdater updater = PlatformDependent 41 | .newAtomicIntegerFieldUpdater(ReferenceCountedServerExchangeChannelGroup.class, "refCnt"); 42 | if (updater == null) { 43 | updater = AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedServerExchangeChannelGroup.class, "refCnt"); 44 | } 45 | refCntUpdater = updater; 46 | } 47 | 48 | private volatile int refCnt = 0; 49 | 50 | protected ReferenceCountedServerExchangeChannelGroup(UUID id) { 51 | super(id); 52 | } 53 | 54 | public ReferenceCountedServerExchangeChannelGroup retain() { 55 | return retain0(1); 56 | } 57 | 58 | public ReferenceCountedServerExchangeChannelGroup retain(int increment) { 59 | return retain0(checkPositive(increment, "increment")); 60 | } 61 | 62 | private ReferenceCountedServerExchangeChannelGroup retain0(int increment) { 63 | for (;;) { 64 | int refCnt = this.refCnt; 65 | final int nextCnt = refCnt + increment; 66 | if (refCntUpdater.compareAndSet(this, refCnt, nextCnt)) { 67 | break; 68 | } 69 | } 70 | return this; 71 | } 72 | 73 | public boolean release(){ 74 | return release0(1); 75 | } 76 | 77 | public boolean release(int decrement) { 78 | return release0(checkPositive(decrement, "decrement")); 79 | } 80 | 81 | private boolean release0(int decrement) { 82 | for (;;) { 83 | int refCnt = this.refCnt; 84 | if (refCnt < decrement) { 85 | throw new IllegalReferenceCountException(refCnt, -decrement); 86 | } 87 | 88 | if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) { 89 | if (refCnt == decrement) { 90 | deallocate(); 91 | return true; 92 | } 93 | return false; 94 | } 95 | } 96 | } 97 | 98 | private void deallocate(){ 99 | NegotiateChannelHandler.uuid2ChannelGroup.remove(id().toString()); 100 | } 101 | 102 | public abstract void addChild(ExchangeChannel channel, NegotiateConfig config); 103 | } 104 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/executor/SimpleExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.executor; 19 | 20 | import java.util.LinkedList; 21 | import java.util.concurrent.Executor; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import io.netty.util.concurrent.DefaultThreadFactory; 27 | import io.netty.util.concurrent.GlobalEventExecutor; 28 | import io.netty.util.internal.SystemPropertyUtil; 29 | 30 | /** 31 | * similar implementation 32 | * 33 | *
 34 |  * 		OrderedExecutorFactory In Vert.x
 36 |  * 		
 37 |  * 		GlobalEventExecutor In Netty
 39 |  * 
40 | * need long-running? 41 | * 42 | * @author spccold 43 | * @version $Id: SimpleExecutor.java, v 0.1 2016年11月1日 下午3:14:52 jileng Exp $ 44 | */ 45 | public class SimpleExecutor implements Executor, Runnable { 46 | 47 | private static final Logger logger = LoggerFactory.getLogger(SimpleExecutor.class); 48 | 49 | private static final boolean preferGlobalEventExecutor; 50 | static{ 51 | preferGlobalEventExecutor = SystemPropertyUtil.getBoolean("sailfish.simpleExector.preferGlobalEventExecutor", true); 52 | if(logger.isDebugEnabled()){ 53 | logger.debug("-Dsailfish.simpleExector.preferGlobalEventExecutor: {}", preferGlobalEventExecutor); 54 | } 55 | } 56 | 57 | // produce FastThreadLocalThread(can benefit from FastThreadLocal(e.g. Recycler)) 58 | private final DefaultThreadFactory THREADFACTORY = new DefaultThreadFactory("sailfish-simpleexecutor", true); 59 | 60 | public static final SimpleExecutor INSTANCE = new SimpleExecutor(); 61 | private final LinkedList tasks = new LinkedList<>(); 62 | private volatile boolean isRunning = false; 63 | 64 | private SimpleExecutor() { } 65 | 66 | @Override 67 | public void execute(Runnable task) { 68 | if(preferGlobalEventExecutor){ 69 | GlobalEventExecutor.INSTANCE.execute(task); 70 | return; 71 | } 72 | synchronized (tasks) { 73 | tasks.add(task); 74 | if (!this.isRunning) { 75 | this.isRunning = true; 76 | newThread().start(); 77 | } 78 | } 79 | } 80 | 81 | private Thread newThread() { 82 | return THREADFACTORY.newThread(this); 83 | } 84 | 85 | @Override 86 | public void run() { 87 | for (;;) { 88 | Runnable currentTask = null; 89 | synchronized (tasks) { 90 | currentTask = tasks.poll(); 91 | if (null == currentTask) { 92 | this.isRunning = false; 93 | break; 94 | } 95 | } 96 | try { 97 | currentTask.run(); 98 | } catch (Throwable cause) { 99 | logger.error("catch exception by SimpleExecutor", cause); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/configuration/AbstractExchangeConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.configuration; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import io.netty.channel.EventLoopGroup; 24 | import io.netty.util.concurrent.EventExecutorGroup; 25 | import sailfish.remoting.Address; 26 | import sailfish.remoting.NettyPlatformIndependent; 27 | import sailfish.remoting.constants.RemotingConstants; 28 | import sailfish.remoting.processors.RequestProcessor; 29 | import sailfish.remoting.utils.ParameterChecker; 30 | 31 | /** 32 | * 33 | * @author spccold 34 | * @version $Id: AbstractExchangeConfig.java, v 0.1 2016年10月27日 下午5:43:27 jileng Exp $ 35 | */ 36 | public abstract class AbstractExchangeConfig { 37 | /** 38 | * remote address for client, local address for server 39 | */ 40 | protected Address address; 41 | // in seconds 42 | protected byte idleTimeout = RemotingConstants.DEFAULT_IDLE_TIMEOUT; 43 | protected byte maxIdleTimeout = RemotingConstants.DEFAULT_MAX_IDLE_TIMEOUT; 44 | 45 | protected List requestProcessors = new ArrayList<>(0); 46 | 47 | /** 48 | * should use {@link NettyPlatformIndependent#newEventLoopGroup(int, java.util.concurrent.ThreadFactory)} to create {@link EventLoopGroup} 49 | */ 50 | private EventLoopGroup eventLoopGroup; 51 | private EventExecutorGroup eventExecutorGroup; 52 | 53 | //check parameters 54 | public void check(){ 55 | ParameterChecker.checkNotNull(address, "address"); 56 | ParameterChecker.checkBytePositive(idleTimeout); 57 | ParameterChecker.checkBytePositive(maxIdleTimeout); 58 | if(idleTimeout > maxIdleTimeout){ 59 | throw new IllegalArgumentException("maxIdleTimeout must be greater than idleTimeout"); 60 | } 61 | } 62 | 63 | public Address address() { 64 | return address; 65 | } 66 | public void address(Address address) { 67 | this.address = ParameterChecker.checkNotNull(address, "address"); 68 | } 69 | public byte idleTimeout() { 70 | return idleTimeout; 71 | } 72 | 73 | public void idleTimeout(byte idleTimeout) { 74 | this.idleTimeout = idleTimeout; 75 | } 76 | public byte maxIdleTimeout() { 77 | return maxIdleTimeout; 78 | } 79 | public void maxIdleTimeout(byte maxIdleTimeout) { 80 | this.maxIdleTimeout = maxIdleTimeout; 81 | } 82 | 83 | public List getRequestProcessors() { 84 | return requestProcessors; 85 | } 86 | 87 | public void setRequestProcessors(List requestProcessors) { 88 | this.requestProcessors = ParameterChecker.checkNotNull(requestProcessors, "requestProcessors"); 89 | } 90 | 91 | public EventLoopGroup getEventLoopGroup() { 92 | return eventLoopGroup; 93 | } 94 | 95 | public void setEventLoopGroup(EventLoopGroup eventLoopGroup) { 96 | this.eventLoopGroup = ParameterChecker.checkNotNull(eventLoopGroup, "eventLoopGroup"); 97 | } 98 | 99 | public EventExecutorGroup getEventExecutorGroup() { 100 | return eventExecutorGroup; 101 | } 102 | 103 | public void setEventExecutorGroup(EventExecutorGroup eventExecutorGroup) { 104 | this.eventExecutorGroup = ParameterChecker.checkNotNull(eventExecutorGroup, "eventExecutorGroup"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/ReadWriteExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.UUID; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import io.netty.channel.EventLoopGroup; 26 | import io.netty.util.concurrent.EventExecutorGroup; 27 | import sailfish.remoting.Address; 28 | import sailfish.remoting.Tracer; 29 | import sailfish.remoting.configuration.NegotiateConfig; 30 | import sailfish.remoting.exceptions.SailfishException; 31 | import sailfish.remoting.handler.MsgHandler; 32 | import sailfish.remoting.protocol.Protocol; 33 | 34 | /** 35 | * @author spccold 36 | * @version $Id: ReadWriteExchangeChannelGroup.java, v 0.1 2016年11月22日 下午6:22:19 spccold Exp $ 37 | */ 38 | public class ReadWriteExchangeChannelGroup extends AbstractExchangeChannelGroup { 39 | private static final Logger logger = LoggerFactory.getLogger(ReadWriteExchangeChannelGroup.class); 40 | 41 | private final ExchangeChannelGroup readGroup; 42 | private final ExchangeChannelGroup writeGroup; 43 | 44 | private final MsgHandler msgHandler; 45 | private final Tracer tracer; 46 | 47 | public ReadWriteExchangeChannelGroup(MsgHandler msgHandler, Address address, int connectTimeout, 48 | int reconnectInterval, byte idleTimeout, byte maxIdleTimeOut, boolean lazy, short connections, 49 | short writeConnections, boolean reverseIndex, EventLoopGroup loopGroup, EventExecutorGroup executorGroup) 50 | throws SailfishException { 51 | super(UUID.randomUUID()); 52 | this.msgHandler = msgHandler; 53 | this.tracer = new Tracer(); 54 | 55 | NegotiateConfig readConfig = new NegotiateConfig(idleTimeout, maxIdleTimeOut, id(), ChannelType.read.code(), 56 | connections, writeConnections, (short) 0, reverseIndex); 57 | this.readGroup = new DefaultExchangeChannelGroup(tracer, msgHandler, address, 58 | (short) (connections - writeConnections), connectTimeout, reconnectInterval, idleTimeout, 59 | maxIdleTimeOut, lazy, reverseIndex, readConfig, this, loopGroup, executorGroup); 60 | 61 | NegotiateConfig writeConfig = new NegotiateConfig(idleTimeout, maxIdleTimeOut, id(), ChannelType.write.code(), 62 | connections, writeConnections, (short) 0, reverseIndex); 63 | this.writeGroup = new DefaultExchangeChannelGroup(tracer, msgHandler, address, writeConnections, connectTimeout, 64 | reconnectInterval, idleTimeout, maxIdleTimeOut, lazy, reverseIndex, writeConfig, this, loopGroup, 65 | executorGroup); 66 | } 67 | 68 | @Override 69 | public ExchangeChannel next() throws SailfishException { 70 | try { 71 | return writeGroup.next(); 72 | } catch (SailfishException cause) { 73 | logger.warn("writeGroup not available, try to choose readGroup", cause); 74 | } 75 | // try readGroup if writeGroup no available 76 | return readGroup.next(); 77 | } 78 | 79 | @Override 80 | public boolean isAvailable() { 81 | if (writeGroup.isAvailable()) { 82 | return true; 83 | } 84 | return readGroup.isAvailable(); 85 | } 86 | 87 | @Override 88 | public void close(int timeout) { 89 | writeGroup.close(timeout); 90 | readGroup.close(timeout); 91 | } 92 | 93 | @Override 94 | public MsgHandler getMsgHander() { 95 | return msgHandler; 96 | } 97 | 98 | @Override 99 | public Tracer getTracer() { 100 | return tracer; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/Tracer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.Map; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | import java.util.concurrent.ConcurrentMap; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import sailfish.remoting.channel.ExchangeChannel; 28 | import sailfish.remoting.future.ResponseFuture; 29 | import sailfish.remoting.protocol.ResponseProtocol; 30 | import sailfish.remoting.utils.CollectionUtils; 31 | import sailfish.remoting.utils.ParameterChecker; 32 | 33 | /** 34 | * tcp communication tracer 35 | * 36 | * @author spccold 37 | * @version $Id: Tracer.java, v 0.1 2016年10月26日 下午2:46:38 jileng Exp $ 38 | */ 39 | public class Tracer { 40 | 41 | private static final Logger logger = LoggerFactory.getLogger(Tracer.class); 42 | private static final Object EMPTY_VALUE = new Object(); 43 | 44 | private final ConcurrentMap traces = new ConcurrentHashMap<>(); 45 | private final ConcurrentMap> singleChannelTraces = new ConcurrentHashMap<>(); 46 | 47 | public Map popPendingRequests(ExchangeChannel channel) { 48 | return singleChannelTraces.remove(channel); 49 | } 50 | 51 | public Map peekPendingRequests(ExchangeChannel channel) { 52 | return singleChannelTraces.get(channel); 53 | } 54 | 55 | public void trace(ExchangeChannel channel, int packageId, ResponseFuture future) { 56 | traces.putIfAbsent(packageId, new TraceContext(channel, future)); 57 | 58 | ConcurrentMap packetIds = singleChannelTraces.get(channel); 59 | if (null == packetIds) { 60 | ConcurrentMap old = singleChannelTraces.putIfAbsent(channel, 61 | packetIds = new ConcurrentHashMap<>()); 62 | if (null != old) { 63 | packetIds = old; 64 | } 65 | } 66 | packetIds.put(packageId, EMPTY_VALUE); 67 | } 68 | 69 | public void erase(ResponseProtocol protocol) { 70 | if (protocol.heartbeat()) { 71 | protocol.recycle(); 72 | return; 73 | } 74 | TraceContext traceContext = traces.remove(protocol.packetId()); 75 | if (null == traceContext) { 76 | logger.info("trace no exist for packageId[{}]", protocol.packetId()); 77 | protocol.recycle(); 78 | return; 79 | } 80 | traceContext.respFuture.putResponse(protocol.body(), protocol.result(), protocol.cause()); 81 | ConcurrentMap packetIds = singleChannelTraces.get(traceContext.channel); 82 | if (CollectionUtils.isNotEmpty(packetIds)) { 83 | packetIds.remove(protocol.packetId()); 84 | } 85 | protocol.recycle(); 86 | } 87 | 88 | public void remove(int packetId){ 89 | TraceContext traceContext = traces.remove(packetId); 90 | if(null != traceContext){ 91 | ConcurrentMap packetIds = singleChannelTraces.get(traceContext.channel); 92 | if (CollectionUtils.isNotEmpty(packetIds)) { 93 | packetIds.remove(packetId); 94 | } 95 | } 96 | } 97 | 98 | static class TraceContext { 99 | ExchangeChannel channel; 100 | ResponseFuture respFuture; 101 | 102 | public TraceContext(ExchangeChannel channel, ResponseFuture respFuture) { 103 | this.channel = ParameterChecker.checkNotNull(channel, "channel"); 104 | this.respFuture = ParameterChecker.checkNotNull(respFuture, "respFuture"); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/RemoteExceptionTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.concurrent.CountDownLatch; 23 | import java.util.concurrent.Executor; 24 | 25 | import org.junit.Assert; 26 | import org.junit.Test; 27 | 28 | import io.netty.util.CharsetUtil; 29 | import sailfish.remoting.configuration.ExchangeClientConfig; 30 | import sailfish.remoting.configuration.ExchangeServerConfig; 31 | import sailfish.remoting.exceptions.SailfishException; 32 | import sailfish.remoting.future.ResponseFuture; 33 | import sailfish.remoting.processors.RemoteExceptionTestRequestHandler; 34 | import sailfish.remoting.processors.RequestProcessor; 35 | 36 | /** 37 | * @author spccold 38 | * @version $Id: RemoteExceptionTest.java, v 0.1 2016年11月29日 下午8:33:47 spccold Exp $ 39 | */ 40 | public class RemoteExceptionTest { 41 | 42 | private static final int originPort = 13142; 43 | private static final String exceptionMsg = "invalid parameter"; 44 | 45 | @Test 46 | public void test() throws Exception { 47 | ExchangeServerConfig serverConfig = new ExchangeServerConfig(); 48 | serverConfig.address(new Address("localhost", originPort)); 49 | List processors = new ArrayList<>(); 50 | processors.add(new RemoteExceptionTestRequestHandler()); 51 | serverConfig.setRequestProcessors(processors); 52 | DefaultServer server = Exchanger.bind(serverConfig); 53 | server.start(); 54 | 55 | ExchangeClientConfig clientConfig = new ExchangeClientConfig(); 56 | clientConfig.address(new Address("localhost", originPort)); 57 | DefaultClient client = new DefaultClient(clientConfig); 58 | RequestControl control = new RequestControl(); 59 | control.sent(false); 60 | control.opcode((short)(RemoteExceptionTestRequestHandler.OPCODE-1)); 61 | ResponseFuture future = client.request(exceptionMsg.getBytes(CharsetUtil.UTF_8), control); 62 | try{ 63 | future.get(); 64 | }catch(Throwable cause){ 65 | //1. test RequestProcessor not found 66 | Assert.assertTrue(cause instanceof SailfishException); 67 | Assert.assertTrue(cause.getMessage().contains("request processor not found")); 68 | } 69 | 70 | control.opcode(RemoteExceptionTestRequestHandler.OPCODE); 71 | future = client.request(exceptionMsg.getBytes(CharsetUtil.UTF_8), control); 72 | try{ 73 | future.get(); 74 | }catch(Throwable cause){ 75 | //2. test normal request remote exception 76 | Assert.assertTrue(cause instanceof SailfishException); 77 | Assert.assertTrue(cause.getMessage().contains(exceptionMsg)); 78 | } 79 | 80 | control.timeout(3000); 81 | final CountDownLatch latch = new CountDownLatch(1); 82 | client.request(exceptionMsg.getBytes(CharsetUtil.UTF_8), new ResponseCallback() { 83 | @Override 84 | public void handleResponse(byte[] resp) { 85 | Assert.assertFalse(true); 86 | } 87 | 88 | @Override 89 | public void handleException(Exception cause) { 90 | //3. test callback request remote exception 91 | Assert.assertTrue(cause instanceof SailfishException); 92 | Assert.assertTrue(cause.getMessage().contains(exceptionMsg)); 93 | latch.countDown(); 94 | } 95 | 96 | @Override 97 | public Executor getExecutor() { 98 | return null; 99 | } 100 | },control); 101 | 102 | latch.await(); 103 | Assert.assertTrue(latch.getCount() == 0); 104 | 105 | client.close(); 106 | server.close(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/HighPerformanceChannelWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.Queue; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | 23 | import io.netty.channel.Channel; 24 | import io.netty.util.Attribute; 25 | import io.netty.util.internal.PlatformDependent; 26 | import sailfish.remoting.constants.ChannelAttrKeys; 27 | import sailfish.remoting.protocol.Protocol; 28 | 29 | /** 30 | *
 31 |  * 	Optimize writeAndFlush
 32 |  * 	netty-td
 33 |  * 
34 | * 35 | * @author spccold 36 | * @version $Id: HighPerformanceChannelWriter.java, v 0.1 2016年11月30日 下午10:10:40 spccold Exp $ 37 | */ 38 | public class HighPerformanceChannelWriter { 39 | 40 | /** 41 | * task for "merge" all pending flushes 42 | */ 43 | private final WriteAndFlushTask writeAndFlushTask = new WriteAndFlushTask(this); 44 | 45 | private final AtomicInteger state = new AtomicInteger(); 46 | // lock free(See https://github.com/spccold/JCTools) 47 | private final Queue queue = PlatformDependent.newMpscQueue(); 48 | 49 | private final Channel channel; 50 | 51 | public HighPerformanceChannelWriter(Channel channel) { 52 | this.channel = channel; 53 | } 54 | 55 | public static void write(Channel channel, Protocol protocol) { 56 | final HighPerformanceChannelWriter highPerformanceWriter = getWriter(channel); 57 | highPerformanceWriter.queue.add(protocol); 58 | if (highPerformanceWriter.addTask()) { 59 | channel.eventLoop().execute(highPerformanceWriter.writeAndFlushTask); 60 | } 61 | } 62 | 63 | private static HighPerformanceChannelWriter getWriter(Channel channel) { 64 | Attribute attr = channel.attr(ChannelAttrKeys.highPerformanceWriter); 65 | HighPerformanceChannelWriter writer = attr.get(); 66 | if (null == writer) { 67 | HighPerformanceChannelWriter old = attr.setIfAbsent(writer = new HighPerformanceChannelWriter(channel)); 68 | if (null != old) { 69 | writer = old; 70 | } 71 | } 72 | return writer; 73 | } 74 | 75 | private void writeAndFlush() { 76 | while (fetchTask()) { 77 | Protocol protocol = null; 78 | while (null != (protocol = queue.poll())) { 79 | channel.write(protocol, channel.voidPromise()); 80 | } 81 | } 82 | channel.flush(); 83 | } 84 | 85 | /** 86 | * @return true if we have to recheck queues 87 | */ 88 | private boolean fetchTask() { 89 | int old = state.getAndDecrement(); 90 | if (old == State.RUNNING_GOT_TASKS.ordinal()) { 91 | return true; 92 | } else if (old == State.RUNNING_NO_TASKS.ordinal()) { 93 | return false; 94 | } else { 95 | throw new AssertionError(); 96 | } 97 | } 98 | 99 | /** 100 | * @return true if caller has to schedule task execution 101 | */ 102 | private boolean addTask() { 103 | // fast track for high-load applications 104 | // atomic get is cheaper than atomic swap 105 | // for both this thread and fetching thread 106 | if (state.get() == State.RUNNING_GOT_TASKS.ordinal()) 107 | return false; 108 | 109 | int old = state.getAndSet(State.RUNNING_GOT_TASKS.ordinal()); 110 | return old == State.WAITING.ordinal(); 111 | } 112 | 113 | static final class WriteAndFlushTask implements Runnable { 114 | 115 | private final HighPerformanceChannelWriter writer; 116 | 117 | public WriteAndFlushTask(HighPerformanceChannelWriter writer) { 118 | this.writer = writer; 119 | } 120 | 121 | @Override 122 | public void run() { 123 | writer.writeAndFlush(); 124 | } 125 | } 126 | 127 | private enum State { 128 | /** actor is not currently running */ 129 | WAITING, 130 | /** actor is running, and has no more tasks */ 131 | RUNNING_NO_TASKS, 132 | /** actor is running, but some queues probably updated, actor needs to recheck them */ 133 | RUNNING_GOT_TASKS, 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/DefaultExchangeChannelChooserFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | import sailfish.remoting.exceptions.ExceptionCode; 23 | import sailfish.remoting.exceptions.SailfishException; 24 | 25 | /** 26 | * Default implementation which uses simple round-robin to choose next 27 | * {@link ExchangeChannel} until which {@link ExchangeChannel#isAvailable()} 28 | * return true or all children has been chosen. 29 | * 30 | * @author spccold 31 | * @version $Id: DefaultExchangeChannelChooserFactory.java, v 0.1 2016年11月22日 32 | * 下午4:40:01 spccold Exp $ 33 | */ 34 | public class DefaultExchangeChannelChooserFactory implements ExchangeChannelChooserFactory { 35 | 36 | public static final DefaultExchangeChannelChooserFactory INSTANCE = new DefaultExchangeChannelChooserFactory(); 37 | 38 | private DefaultExchangeChannelChooserFactory() { } 39 | 40 | @Override 41 | public ExchangeChannelChooser newChooser(ExchangeChannel[] channels, ExchangeChannel[] deadChannels) { 42 | if (isPowerOfTwo(channels.length)) { 43 | return new PowerOfTowExchangeChannelChooser(channels, deadChannels); 44 | } else { 45 | return new GenericExchangeChannelChooser(channels, deadChannels); 46 | } 47 | } 48 | 49 | private static boolean isPowerOfTwo(int val) { 50 | return (val & -val) == val; 51 | } 52 | 53 | private static final class PowerOfTowExchangeChannelChooser implements ExchangeChannelChooser { 54 | private final AtomicInteger idx = new AtomicInteger(); 55 | private final ExchangeChannel[] channels; 56 | private final ExchangeChannel[] deadChannels; 57 | 58 | PowerOfTowExchangeChannelChooser(ExchangeChannel[] channels, ExchangeChannel[] deadChannels) { 59 | this.channels = channels; 60 | this.deadChannels = deadChannels; 61 | } 62 | 63 | @Override 64 | public ExchangeChannel next() throws SailfishException { 65 | if(channels.length == 1){//one connection check 66 | if(null == channels[0] || !channels[0].isAvailable()){ 67 | throw new SailfishException(ExceptionCode.EXCHANGER_NOT_AVAILABLE, "exchanger is not available!"); 68 | } 69 | return channels[0]; 70 | } 71 | 72 | int arrayIndex = 0; 73 | int currentIndex = idx.getAndIncrement(); 74 | for (int i = 0; i < channels.length; i++) { 75 | ExchangeChannel currentChannel = channels[arrayIndex = ((currentIndex++) & channels.length - 1)]; 76 | if (null != currentChannel && currentChannel.isAvailable()) { 77 | if (null != deadChannels[arrayIndex]) { 78 | deadChannels[arrayIndex] = null; 79 | } 80 | return currentChannel; 81 | } 82 | deadChannels[arrayIndex] = currentChannel; 83 | } 84 | throw new SailfishException(ExceptionCode.EXCHANGER_NOT_AVAILABLE, "exchanger is not available!"); 85 | } 86 | } 87 | 88 | private static final class GenericExchangeChannelChooser implements ExchangeChannelChooser { 89 | private final AtomicInteger idx = new AtomicInteger(); 90 | private final ExchangeChannel[] channels; 91 | private final ExchangeChannel[] deadChannels; 92 | 93 | GenericExchangeChannelChooser(ExchangeChannel[] channels, ExchangeChannel[] deadChannels) { 94 | this.channels = channels; 95 | this.deadChannels = deadChannels; 96 | } 97 | 98 | @Override 99 | public ExchangeChannel next() throws SailfishException { 100 | int arrayIndex = 0; 101 | int currentIndex = idx.getAndIncrement(); 102 | for (int i = 0; i < channels.length; i++) { 103 | ExchangeChannel currentChannel = channels[arrayIndex = Math.abs((currentIndex++) % channels.length)]; 104 | if (null != currentChannel && currentChannel.isAvailable()) { 105 | if (null != deadChannels[arrayIndex]) { 106 | deadChannels[arrayIndex] = null; 107 | } 108 | return currentChannel; 109 | } 110 | deadChannels[arrayIndex] = currentChannel; 111 | } 112 | throw new SailfishException(ExceptionCode.EXCHANGER_NOT_AVAILABLE, "exchanger is not available!"); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/configuration/NegotiateConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.configuration; 19 | 20 | import java.io.ByteArrayInputStream; 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.DataInputStream; 23 | import java.io.DataOutputStream; 24 | import java.io.IOException; 25 | import java.util.UUID; 26 | 27 | import sailfish.remoting.channel.ChannelType; 28 | import sailfish.remoting.constants.Opcode; 29 | import sailfish.remoting.protocol.RequestProtocol; 30 | import sailfish.remoting.utils.ArrayUtils; 31 | 32 | /** 33 | * 34 | * @author spccold 35 | * @version $Id: NegotiateConfig.java, v 0.1 2016年11月22日 下午2:58:33 spccold Exp $ 36 | */ 37 | public class NegotiateConfig { 38 | private byte idleTimeout; 39 | private byte maxIdleTimeout; 40 | 41 | private final UUID uuid; 42 | private final byte type; 43 | private final short connections; 44 | private final short writeConnections; 45 | private final boolean reversed; 46 | 47 | private short index; 48 | 49 | public NegotiateConfig(byte idleTimeout, byte maxIdleTimeout, UUID uuid, byte type, short connections, 50 | short writeConnections, short index, boolean reversed) { 51 | this.idleTimeout = idleTimeout; 52 | this.maxIdleTimeout = maxIdleTimeout; 53 | 54 | this.uuid = uuid; 55 | this.type = type; 56 | this.connections = connections; 57 | this.writeConnections = writeConnections; 58 | this.reversed = reversed; 59 | 60 | this.index = index; 61 | } 62 | 63 | public byte idleTimeout() { 64 | return this.idleTimeout; 65 | } 66 | 67 | public byte maxIdleTimeout() { 68 | return this.maxIdleTimeout; 69 | } 70 | 71 | public UUID uuid() { 72 | return this.uuid; 73 | } 74 | 75 | public byte type() { 76 | return this.type; 77 | } 78 | 79 | public short connections() { 80 | return this.connections; 81 | } 82 | 83 | public short writeConnections() { 84 | return this.writeConnections; 85 | } 86 | 87 | public NegotiateConfig index(short index) { 88 | this.index = index; 89 | return this; 90 | } 91 | 92 | public short index() { 93 | return this.index; 94 | } 95 | 96 | public boolean reversed() { 97 | return this.reversed; 98 | } 99 | 100 | public boolean isRead() { 101 | return ChannelType.read.code() == type; 102 | } 103 | 104 | public boolean isWrite() { 105 | return ChannelType.write.code() == type; 106 | } 107 | 108 | public boolean isReadWrite(){ 109 | return ChannelType.readwrite.code() == type; 110 | } 111 | 112 | public void reverseIndex(){ 113 | if(!this.reversed){ 114 | return; 115 | } 116 | if(isReadWrite()){ 117 | this.index = (short)ArrayUtils.reverseArrayIndex(this.connections, this.index); 118 | }else if(isRead()){ 119 | int readConnections = this.connections - this.writeConnections; 120 | this.index = (short) ArrayUtils.reverseArrayIndex(readConnections, index); 121 | }else if(isWrite()){ 122 | this.index = (short) ArrayUtils.reverseArrayIndex(this.writeConnections, index); 123 | } 124 | } 125 | 126 | public NegotiateConfig deepCopy() { 127 | return new NegotiateConfig(idleTimeout, maxIdleTimeout, uuid, type, connections, 128 | writeConnections, index, reversed); 129 | } 130 | 131 | public RequestProtocol toNegotiateRequest() throws IOException { 132 | int size = 1 + 1 + 16 + 1 + 2 + 2 + 2 + 1; 133 | try (ByteArrayOutputStream baos = new ByteArrayOutputStream(size); 134 | DataOutputStream dos = new DataOutputStream(baos);) { 135 | dos.writeByte(this.idleTimeout); 136 | dos.writeByte(this.maxIdleTimeout); 137 | dos.writeLong(this.uuid.getMostSignificantBits()); 138 | dos.writeLong(this.uuid.getLeastSignificantBits()); 139 | dos.writeByte(this.type); 140 | dos.writeShort(this.connections); 141 | dos.writeShort(this.writeConnections); 142 | dos.writeShort(this.index); 143 | dos.writeBoolean(this.reversed); 144 | return RequestProtocol.newHeartbeat().opcode(Opcode.HEARTBEAT_WITH_NEGOTIATE).body(baos.toByteArray()); 145 | } 146 | } 147 | 148 | public static NegotiateConfig fromNegotiate(byte[] negotiateData) throws IOException { 149 | try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(negotiateData))) { 150 | return new NegotiateConfig(dis.readByte(), dis.readByte(), new UUID(dis.readLong(), dis.readLong()), 151 | dis.readByte(), dis.readShort(), dis.readShort(), dis.readShort(), dis.readBoolean()); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/RecycleTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.concurrent.CountDownLatch; 21 | 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | 25 | import io.netty.util.Recycler; 26 | import io.netty.util.internal.SystemPropertyUtil; 27 | 28 | /** 29 | * @author spccold 30 | * @version $Id: RecycleCheckTest.java, v 0.1 2016年12月1日 下午11:55:23 spccold Exp $ 31 | */ 32 | public class RecycleTest { 33 | 34 | @Test 35 | public void testRecycleInSyncThread() throws Exception{ 36 | //recycle 37 | Resource2 resource1 = Resource2.newInstance(); 38 | resource1.recycle(); 39 | 40 | //don't recycle 41 | resource1 = Resource2.newInstance(); 42 | 43 | Resource2 temp =null; 44 | // By default we allow one push to a Recycler for each 8th try on handles that were never recycled before. 45 | // This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation 46 | // bursts. 47 | int stackDefaultRatioMask = SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8) - 1; 48 | for(int i =0; i< stackDefaultRatioMask; i++){ 49 | temp = Resource2.newInstance(); 50 | temp.recycle(); 51 | Assert.assertTrue(temp != Resource2.newInstance()); 52 | } 53 | 54 | temp = Resource2.newInstance(); 55 | temp.recycle(); 56 | Assert.assertTrue(temp == Resource2.newInstance()); 57 | } 58 | 59 | /** 60 | * 异步线程recycle测试 61 | *
 62 | 	 * 	每个异步recycle的线程都会产生一个WeakOrderQueue, 多个线程产的的WeakOrderQueue会形成链, 如下所示
 63 | 	 * 		Recycler.Stack.head -> WeakOrderQueue@2(thread2产生) -> WeakOrderQueue@1(thread1产生) -> null
 64 | 	 * 		Recycler.Stack.cursor最初指向Recycler.Stack.head(例如最初指向WeakOrderQueue@1), 但是当产生新的WeakOrderQueue时
 65 | 	 * 		(例如WeakOrderQueue@2)时,Recycler.Stack.cursor指针并不会及时调整, 知道此次Recycler.Stack.pop结束,Recycler.Stack.cursor
 66 | 	 * 		才会重新指向Recycler.Stack.head, 即指向WeakOrderQueue链的头
 67 | 	 * 
68 | * @throws Exception 69 | */ 70 | @Test 71 | public void testRecycleInAsyncThread() throws Exception{ 72 | final CountDownLatch latch = new CountDownLatch(1); 73 | final Resource resource1 = Resource.newInstance(); 74 | //recycle in thread1 75 | new Thread(){ 76 | @Override 77 | public void run() { 78 | resource1.recycle(); 79 | latch.countDown(); 80 | } 81 | }.start(); 82 | 83 | latch.await(); 84 | Resource resource2 = Resource.newInstance(); 85 | Assert.assertTrue(resource1 == resource2); 86 | resource2.recycle(); 87 | 88 | final CountDownLatch latch2 = new CountDownLatch(1); 89 | final Resource resource3 = Resource.newInstance(); 90 | //recycle in thread2 91 | new Thread(){ 92 | @Override 93 | public void run() { 94 | resource3.recycle(); 95 | latch2.countDown(); 96 | } 97 | }.start(); 98 | 99 | latch2.await(); 100 | Resource resource4 = Resource.newInstance(); 101 | //thread2中recycle时并没有调整Recycler.Stack.cursor, 此时Recycler.Stack.cursor还指向thread1产生的WeakOrderQueue1 102 | //此时Recycler.Stack.pop只能从WeakOrderQueue1开始遍历,但resource3 recycle在thread2产生的WeakOrderQueue2,所有pop为null,会创建新的对象 103 | Assert.assertTrue(resource4 != resource3); 104 | //上次Resource.newInstance()已经让Recycler.Stack.cursor重新指向WeakOrderQueue的头,所以这次可以获取到thread2中recycle的resource3 105 | Assert.assertTrue(Resource.newInstance() == resource3); 106 | } 107 | 108 | static final class Resource{ 109 | 110 | private static final Recycler RECYCLER = new Recycler(){ 111 | @Override 112 | protected Resource newObject(Recycler.Handle handle) { 113 | return new Resource(handle); 114 | } 115 | }; 116 | 117 | public static Resource newInstance(){ 118 | return RECYCLER.get(); 119 | } 120 | 121 | private final Recycler.Handle handle; 122 | private Resource(Recycler.Handle handle) { 123 | this.handle = handle; 124 | } 125 | 126 | public void recycle() { 127 | handle.recycle(this); 128 | } 129 | } 130 | 131 | static final class Resource2{ 132 | 133 | private static final Recycler RECYCLER = new Recycler(){ 134 | @Override 135 | protected Resource2 newObject(Recycler.Handle handle) { 136 | return new Resource2(handle); 137 | } 138 | }; 139 | 140 | public static Resource2 newInstance(){ 141 | return RECYCLER.get(); 142 | } 143 | 144 | private final Recycler.Handle handle; 145 | private Resource2(Recycler.Handle handle) { 146 | this.handle = handle; 147 | } 148 | 149 | public void recycle() { 150 | handle.recycle(this); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/MultiConnectionsExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import io.netty.bootstrap.Bootstrap; 21 | import io.netty.channel.EventLoopGroup; 22 | import io.netty.util.concurrent.EventExecutorGroup; 23 | import sailfish.remoting.Address; 24 | import sailfish.remoting.Tracer; 25 | import sailfish.remoting.configuration.NegotiateConfig; 26 | import sailfish.remoting.exceptions.SailfishException; 27 | import sailfish.remoting.handler.MsgHandler; 28 | import sailfish.remoting.protocol.Protocol; 29 | 30 | /** 31 | * @author spccold 32 | * @version $Id: MultiConnectionsExchangeChannelGroup.java, v 0.1 2016年11月22日 下午4:01:53 spccold Exp 33 | * $ 34 | */ 35 | public abstract class MultiConnectionsExchangeChannelGroup extends AbstractConfigurableExchangeChannelGroup { 36 | 37 | private final ExchangeChannel[] children; 38 | private final ExchangeChannel[] deadChildren; 39 | private final ExchangeChannelChooserFactory.ExchangeChannelChooser chooser; 40 | private final MsgHandler msgHandler; 41 | private final Tracer tracer; 42 | 43 | protected MultiConnectionsExchangeChannelGroup(Tracer tracer, MsgHandler msgHandler, Address address, 44 | short connections, int connectTimeout, int reconnectInterval, byte idleTimeout, byte maxIdleTimeOut, 45 | boolean lazy, boolean reverseIndex, NegotiateConfig config, ExchangeChannelGroup parentGroup, 46 | EventLoopGroup loopGroup, EventExecutorGroup executorGroup) throws SailfishException { 47 | 48 | this.tracer = tracer; 49 | this.msgHandler = msgHandler; 50 | 51 | children = new ExchangeChannel[connections]; 52 | deadChildren = new ExchangeChannel[connections]; 53 | 54 | if (null == config) { 55 | config = new NegotiateConfig(idleTimeout, maxIdleTimeOut, id(), ChannelType.readwrite.code(), 56 | (short) connections, (short) connections, (short) 0, reverseIndex); 57 | } 58 | 59 | Bootstrap bootstrap = null; 60 | for (short i = 0; i < connections; i++) { 61 | boolean success = false; 62 | final NegotiateConfig deepCopy = config.deepCopy().index(i); 63 | parentGroup = (null == parentGroup ? this : parentGroup); 64 | bootstrap = configureBoostrap(address, connectTimeout, deepCopy, parentGroup, loopGroup, executorGroup); 65 | try { 66 | children[i] = newChild(parentGroup, bootstrap, reconnectInterval, lazy, deepCopy.isRead()); 67 | success = true; 68 | } catch (SailfishException cause) { 69 | throw cause; 70 | } finally { 71 | if (!success) { 72 | close(Integer.MAX_VALUE); 73 | } 74 | } 75 | } 76 | 77 | chooser = DefaultExchangeChannelChooserFactory.INSTANCE.newChooser(children, deadChildren); 78 | } 79 | 80 | @Override 81 | public ExchangeChannel next() throws SailfishException { 82 | return chooser.next(); 83 | } 84 | 85 | /** 86 | * Return the number of {@link ExchangeChannel} this implementation uses. This number is the 87 | * maps 1:1 to the connections it use. 88 | */ 89 | public final int channelOCount() { 90 | return children.length; 91 | } 92 | 93 | public void close(int timeout) { 94 | if (this.isClosed()) { 95 | return; 96 | } 97 | synchronized (this) { 98 | if (this.isClosed()) { 99 | return; 100 | } 101 | this.closed = true; 102 | for (int i = 0; i < children.length; i++) { 103 | deadChildren[i] = null; 104 | if (null != children[i]) { 105 | children[i].close(timeout); 106 | } 107 | } 108 | } 109 | } 110 | 111 | @Override 112 | public boolean isAvailable() { 113 | if (this.isClosed()) { 114 | return false; 115 | } 116 | 117 | if (children.length == 1) {// one connection check 118 | return (null != children[0] && children[0].isAvailable()); 119 | } 120 | 121 | // can hit most of the time 122 | if (deadChildren[0] == null || deadChildren[0].isAvailable()) { 123 | return true; 124 | } 125 | 126 | for (int i = 1; i < children.length; i++) { 127 | if (deadChildren[i] == null || deadChildren[i].isAvailable()) { 128 | return true; 129 | } 130 | } 131 | return false; 132 | } 133 | 134 | @Override 135 | public MsgHandler getMsgHander() { 136 | return msgHandler; 137 | } 138 | 139 | @Override 140 | public Tracer getTracer() { 141 | return tracer; 142 | } 143 | 144 | /** 145 | * Create a new {@link ExchangeChannel} which will later then accessible via the {@link #next()} 146 | * method. 147 | */ 148 | protected abstract ExchangeChannel newChild(ExchangeChannelGroup parent, Bootstrap bootstrap, int reconnectInterval, 149 | boolean lazy, boolean readChannel) throws SailfishException; 150 | } 151 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/AbstractConfigurableExchangeChannelGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.UUID; 21 | import java.util.concurrent.CountDownLatch; 22 | 23 | import static sailfish.remoting.constants.ChannelAttrKeys.OneTime; 24 | 25 | import io.netty.bootstrap.Bootstrap; 26 | import io.netty.buffer.PooledByteBufAllocator; 27 | import io.netty.channel.ChannelInitializer; 28 | import io.netty.channel.ChannelOption; 29 | import io.netty.channel.ChannelPipeline; 30 | import io.netty.channel.EventLoopGroup; 31 | import io.netty.channel.WriteBufferWaterMark; 32 | import io.netty.channel.socket.SocketChannel; 33 | import io.netty.handler.timeout.IdleStateHandler; 34 | import io.netty.util.concurrent.EventExecutorGroup; 35 | import sailfish.remoting.Address; 36 | import sailfish.remoting.NettyPlatformIndependent; 37 | import sailfish.remoting.codec.RemotingDecoder; 38 | import sailfish.remoting.codec.RemotingEncoder; 39 | import sailfish.remoting.configuration.NegotiateConfig; 40 | import sailfish.remoting.constants.ChannelAttrKeys; 41 | import sailfish.remoting.eventgroup.ClientEventGroup; 42 | import sailfish.remoting.handler.HeartbeatChannelHandler; 43 | import sailfish.remoting.handler.NegotiateChannelHandler; 44 | import sailfish.remoting.handler.ConcreteRequestHandler; 45 | 46 | /** 47 | * @author spccold 48 | * @version $Id: AbstractConfigurableExchangeChannelGroup.java, v 0.1 2016年11月23日 下午3:47:32 spccold 49 | * Exp $ 50 | */ 51 | public abstract class AbstractConfigurableExchangeChannelGroup extends AbstractExchangeChannelGroup { 52 | 53 | protected AbstractConfigurableExchangeChannelGroup() { 54 | super(UUID.randomUUID()); 55 | } 56 | 57 | protected Bootstrap configureBoostrap(Address remoteAddress, int connectTimeout, NegotiateConfig config, 58 | ExchangeChannelGroup channelGroup, EventLoopGroup loopGroup, EventExecutorGroup executorGroup) { 59 | Bootstrap boot = newBootstrap(); 60 | if (null == loopGroup) { 61 | loopGroup = ClientEventGroup.INSTANCE.getLoopGroup(); 62 | } 63 | if (null == executorGroup) { 64 | executorGroup = ClientEventGroup.INSTANCE.getExecutorGroup(); 65 | } 66 | boot.group(loopGroup); 67 | boot.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout); 68 | boot.remoteAddress(remoteAddress.host(), remoteAddress.port()); 69 | boot.handler(newChannelInitializer(config, channelGroup, executorGroup)); 70 | return boot; 71 | } 72 | 73 | private Bootstrap newBootstrap() { 74 | Bootstrap boot = new Bootstrap(); 75 | boot.channel(NettyPlatformIndependent.channelClass()); 76 | boot.option(ChannelOption.TCP_NODELAY, true); 77 | // replace by heart beat 78 | boot.option(ChannelOption.SO_KEEPALIVE, false); 79 | // default is pooled direct 80 | // ByteBuf(io.netty.util.internal.PlatformDependent.DIRECT_BUFFER_PREFERRED) 81 | boot.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 82 | // 32kb(for massive long connections, See 83 | // http://www.infoq.com/cn/articles/netty-million-level-push-service-design-points) 84 | // 64kb(RocketMq remoting default value) 85 | boot.option(ChannelOption.SO_SNDBUF, 32 * 1024); 86 | boot.option(ChannelOption.SO_RCVBUF, 32 * 1024); 87 | // temporary settings, need more tests 88 | boot.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(8 * 1024, 32 * 1024)); 89 | //default is true, reduce thread context switching 90 | boot.option(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP, true); 91 | return boot; 92 | } 93 | 94 | private ChannelInitializer newChannelInitializer(final NegotiateConfig config, 95 | final ExchangeChannelGroup channelGroup, final EventExecutorGroup executorGroup) { 96 | return new ChannelInitializer() { 97 | @Override 98 | protected void initChannel(SocketChannel ch) throws Exception { 99 | ChannelPipeline pipeline = ch.pipeline(); 100 | ch.attr(ChannelAttrKeys.maxIdleTimeout).set(config.maxIdleTimeout()); 101 | ch.attr(ChannelAttrKeys.channelGroup).set(channelGroup); 102 | ch.attr(ChannelAttrKeys.clientSide).set(true); 103 | ch.attr(OneTime.awaitNegotiate).set(new CountDownLatch(1)); 104 | ch.attr(OneTime.channelConfig).set(config); 105 | // TODO should increase ioRatio when every ChannelHandler bind to executorGroup? 106 | pipeline.addLast(executorGroup, 107 | RemotingEncoder.INSTANCE, 108 | new RemotingDecoder(), 109 | new IdleStateHandler(config.idleTimeout(), 0, 0), 110 | HeartbeatChannelHandler.INSTANCE, 111 | NegotiateChannelHandler.INSTANCE, 112 | ConcreteRequestHandler.INSTANCE); 113 | } 114 | }; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/SingleConnctionExchangeChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.util.Map; 21 | import java.util.concurrent.locks.LockSupport; 22 | 23 | import io.netty.bootstrap.Bootstrap; 24 | import io.netty.channel.Channel; 25 | import io.netty.handler.timeout.IdleStateHandler; 26 | import sailfish.remoting.ReconnectManager; 27 | import sailfish.remoting.Tracer; 28 | import sailfish.remoting.constants.ChannelAttrKeys.OneTime; 29 | import sailfish.remoting.exceptions.ExceptionCode; 30 | import sailfish.remoting.exceptions.SailfishException; 31 | import sailfish.remoting.handler.MsgHandler; 32 | import sailfish.remoting.protocol.Protocol; 33 | import sailfish.remoting.protocol.ResponseProtocol; 34 | import sailfish.remoting.utils.ChannelUtil; 35 | import sailfish.remoting.utils.CollectionUtils; 36 | import sailfish.remoting.utils.ParameterChecker; 37 | 38 | /** 39 | * @author spccold 40 | * @version $Id: SingleConnctionExchangeChannel.java, v 0.1 2016年11月21日 下午11:05:58 spccold Exp $ 41 | */ 42 | public abstract class SingleConnctionExchangeChannel extends AbstractExchangeChannel { 43 | private final Bootstrap reusedBootstrap; 44 | 45 | private volatile boolean reconnectting = false; 46 | private final int reconnectInterval; 47 | 48 | /** 49 | * Create a new instance 50 | * 51 | * @param parent 52 | * the {@link ExchangeChannelGroup} which is the parent of this instance and belongs 53 | * to it 54 | * @param connectTimeout 55 | * connect timeout in milliseconds 56 | * @param reconnectInterval 57 | * reconnect interval in milliseconds for {@link ReconnectManager} 58 | * @param idleTimeout 59 | * idle timeout in seconds for {@link IdleStateHandler} 60 | * @param maxIdleTimeOut 61 | * max idle timeout in seconds for {@link ChannelEventsHandler} 62 | * @param doConnect 63 | * connect to remote peer or not when initial 64 | */ 65 | protected SingleConnctionExchangeChannel(Bootstrap bootstrap, ExchangeChannelGroup parent, int reconnectInterval, 66 | boolean doConnect) throws SailfishException { 67 | super(parent); 68 | this.reusedBootstrap = bootstrap; 69 | this.reconnectInterval = reconnectInterval; 70 | if (doConnect) { 71 | this.channel = doConnect(); 72 | } 73 | } 74 | 75 | @SuppressWarnings("deprecation") 76 | @Override 77 | public Channel doConnect() throws SailfishException { 78 | try { 79 | Channel channel = reusedBootstrap.connect().syncUninterruptibly().channel(); 80 | channel.attr(OneTime.awaitNegotiate).get().await(); 81 | channel.attr(OneTime.awaitNegotiate).remove(); 82 | return channel; 83 | } catch (Throwable cause) { 84 | throw new SailfishException(cause); 85 | } 86 | } 87 | 88 | @Override 89 | public void recover() { 90 | // add reconnect task 91 | ReconnectManager.INSTANCE.addReconnectTask(this, reconnectInterval); 92 | } 93 | 94 | @Override 95 | public Channel update(Channel newChannel) { 96 | synchronized (this) { 97 | this.reconnectting = false; 98 | if (this.isClosed()) { 99 | ChannelUtil.closeChannel(newChannel); 100 | return channel; 101 | } 102 | Channel old = channel; 103 | this.channel = newChannel; 104 | return old; 105 | } 106 | } 107 | 108 | @Override 109 | public boolean isAvailable() { 110 | boolean isAvailable = false; 111 | if (!reconnectting && !(isAvailable = super.isAvailable())) { 112 | synchronized (this) { 113 | if (!reconnectting && !(isAvailable = super.isAvailable())) { 114 | recover(); 115 | this.reconnectting = true; 116 | } 117 | } 118 | } 119 | return isAvailable && super.isWritable(); 120 | } 121 | 122 | @Override 123 | public void close(int timeout) { 124 | ParameterChecker.checkNotNegative(timeout, "timeout"); 125 | if (this.isClosed()) { 126 | return; 127 | } 128 | synchronized (this) { 129 | if (this.isClosed()) { 130 | return; 131 | } 132 | this.closed = true; 133 | // deal unfinished requests, response with channel closed exception 134 | long start = System.currentTimeMillis(); 135 | while (CollectionUtils.isNotEmpty(getTracer().peekPendingRequests(this)) 136 | && (System.currentTimeMillis() - start < timeout)) { 137 | LockSupport.parkNanos(1000 * 1000 * 10L); 138 | } 139 | Map pendingRequests = getTracer().popPendingRequests(this); 140 | if (CollectionUtils.isNotEmpty(pendingRequests)) { 141 | for (Integer packetId : pendingRequests.keySet()) { 142 | getTracer().erase(ResponseProtocol.newErrorResponse(packetId, 143 | new SailfishException(ExceptionCode.UNFINISHED_REQUEST, 144 | "unfinished request because of channel:" + channel.toString() + " be closed"))); 145 | } 146 | } 147 | ChannelUtil.closeChannel(channel); 148 | } 149 | } 150 | 151 | @Override 152 | public MsgHandler getMsgHander() { 153 | return parent().getMsgHander(); 154 | } 155 | 156 | @Override 157 | public Tracer getTracer() { 158 | return parent().getTracer(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/ProtocolTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | import io.netty.buffer.ByteBuf; 24 | import io.netty.buffer.ByteBufAllocator; 25 | import sailfish.remoting.constants.CompressType; 26 | import sailfish.remoting.constants.LangType; 27 | import sailfish.remoting.constants.RemotingConstants; 28 | import sailfish.remoting.constants.SerializeType; 29 | import sailfish.remoting.exceptions.SailfishException; 30 | import sailfish.remoting.protocol.RequestProtocol; 31 | import sailfish.remoting.protocol.ResponseProtocol; 32 | 33 | /** 34 | * 35 | * @author spccold 36 | * @version $Id: ProtocolTest.java, v 0.1 2016年11月3日 上午11:17:13 jileng Exp $ 37 | */ 38 | public class ProtocolTest { 39 | 40 | @Test 41 | public void testRequestProtocol() throws SailfishException{ 42 | RequestProtocol send = RequestProtocol.newInstance(); 43 | send.body(new byte[]{1,2,3,4}); 44 | send.compressType(CompressType.NON_COMPRESS); 45 | send.heartbeat(false); 46 | send.langType(LangType.JAVA); 47 | send.oneway(false); 48 | send.opcode((short)1); 49 | send.packetId(1); 50 | send.serializeType(SerializeType.NON_SERIALIZE); 51 | 52 | ByteBuf output = ByteBufAllocator.DEFAULT.buffer(128); 53 | send.serialize(output); 54 | 55 | Assert.assertTrue(output.readShort() == RemotingConstants.SAILFISH_MAGIC); 56 | RequestProtocol receive = RequestProtocol.newInstance(); 57 | Assert.assertTrue(send == receive); 58 | 59 | receive.deserialize(output, output.readInt()); 60 | Assert.assertArrayEquals(send.body(), receive.body()); 61 | Assert.assertTrue(receive.compressType() == CompressType.NON_COMPRESS); 62 | Assert.assertFalse(receive.heartbeat()); 63 | Assert.assertTrue(receive.langType() == LangType.JAVA); 64 | Assert.assertFalse(receive.oneway()); 65 | Assert.assertTrue(1 == receive.opcode()); 66 | Assert.assertTrue(1 == receive.packetId()); 67 | Assert.assertTrue(receive.serializeType() == SerializeType.NON_SERIALIZE); 68 | 69 | 70 | output.clear(); 71 | send.body(new byte[]{-1, -1, -1, -1}); 72 | send.heartbeat(true); 73 | send.oneway(true); 74 | send.langType(LangType.CPP); 75 | send.serializeType(SerializeType.PROTOBUF_SERIALIZE); 76 | send.compressType(CompressType.LZ4_COMPRESS); 77 | send.opcode((short)100); 78 | send.packetId(1000); 79 | send.serialize(output); 80 | 81 | Assert.assertTrue(output.readShort() == RemotingConstants.SAILFISH_MAGIC); 82 | receive = RequestProtocol.newInstance(); 83 | Assert.assertTrue(send == receive); 84 | 85 | receive.deserialize(output, output.readInt()); 86 | Assert.assertArrayEquals(send.body(), receive.body()); 87 | Assert.assertTrue(receive.compressType() == CompressType.LZ4_COMPRESS); 88 | Assert.assertTrue(receive.heartbeat()); 89 | Assert.assertTrue(receive.langType() == LangType.CPP); 90 | Assert.assertTrue(receive.oneway()); 91 | Assert.assertTrue(100 == receive.opcode()); 92 | Assert.assertTrue(1000 == receive.packetId()); 93 | Assert.assertTrue(receive.serializeType() == SerializeType.PROTOBUF_SERIALIZE); 94 | } 95 | 96 | @Test 97 | public void testResponseProtocol() throws SailfishException{ 98 | ResponseProtocol send = ResponseProtocol.newInstance(); 99 | send.body(new byte[]{1,2,3,4}); 100 | send.compressType(CompressType.GZIP_COMPRESS); 101 | send.heartbeat(false); 102 | send.packetId(1); 103 | send.result((byte)0); 104 | send.serializeType(SerializeType.JDK_SERIALIZE); 105 | 106 | ByteBuf output = ByteBufAllocator.DEFAULT.buffer(128); 107 | send.serialize(output); 108 | 109 | ResponseProtocol receive = ResponseProtocol.newInstance(); 110 | Assert.assertTrue(send == receive); 111 | Assert.assertTrue(output.readShort() == RemotingConstants.SAILFISH_MAGIC); 112 | receive.deserialize(output, output.readInt()); 113 | Assert.assertArrayEquals(send.body(), receive.body()); 114 | Assert.assertTrue(send.compressType() == CompressType.GZIP_COMPRESS); 115 | Assert.assertTrue(send.serializeType() == SerializeType.JDK_SERIALIZE); 116 | Assert.assertFalse(receive.heartbeat()); 117 | Assert.assertTrue(1 == receive.packetId()); 118 | Assert.assertTrue(0 == receive.result()); 119 | 120 | 121 | output.clear(); 122 | send.heartbeat(true); 123 | send.serialize(output); 124 | 125 | Assert.assertTrue(output.readShort() == RemotingConstants.SAILFISH_MAGIC); 126 | receive = ResponseProtocol.newInstance(); 127 | Assert.assertTrue(send == receive); 128 | receive.deserialize(output, output.readInt()); 129 | Assert.assertTrue(receive.heartbeat()); 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/channel/AbstractExchangeChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting.channel; 19 | 20 | import java.net.SocketAddress; 21 | import java.util.UUID; 22 | 23 | import io.netty.channel.Channel; 24 | import io.netty.channel.ChannelFuture; 25 | import sailfish.remoting.RequestControl; 26 | import sailfish.remoting.ResponseCallback; 27 | import sailfish.remoting.exceptions.ExceptionCode; 28 | import sailfish.remoting.exceptions.SailfishException; 29 | import sailfish.remoting.future.BytesResponseFuture; 30 | import sailfish.remoting.future.ResponseFuture; 31 | import sailfish.remoting.protocol.RequestProtocol; 32 | import sailfish.remoting.protocol.ResponseProtocol; 33 | 34 | /** 35 | * @author spccold 36 | * @version $Id: AbstractExchangeChannel.java, v 0.1 2016年11月21日 下午10:49:12 spccold Exp $ 37 | */ 38 | public abstract class AbstractExchangeChannel implements ExchangeChannel { 39 | /** underlying channel */ 40 | protected volatile Channel channel; 41 | protected volatile boolean closed = false; 42 | 43 | private final ExchangeChannelGroup parent; 44 | 45 | protected AbstractExchangeChannel(ExchangeChannelGroup parent) { 46 | this.parent = parent; 47 | } 48 | 49 | @Override 50 | public ExchangeChannelGroup parent() { 51 | return parent; 52 | } 53 | 54 | @Override 55 | public ExchangeChannel next() { 56 | return this; 57 | } 58 | 59 | @Override 60 | public UUID id() { 61 | return null; 62 | } 63 | 64 | @Override 65 | public SocketAddress localAddress() { 66 | if (null == channel) { 67 | return null; 68 | } 69 | return channel.localAddress(); 70 | } 71 | 72 | @Override 73 | public SocketAddress remoteAdress() { 74 | if (null == channel) { 75 | return null; 76 | } 77 | return channel.remoteAddress(); 78 | } 79 | 80 | @Override 81 | public boolean isAvailable() { 82 | return null != channel && channel.isOpen() && channel.isActive(); 83 | } 84 | 85 | protected boolean isWritable(){ 86 | return (null != channel && channel.isWritable()); 87 | } 88 | 89 | @Override 90 | public void close() { 91 | close(0); 92 | } 93 | 94 | @Override 95 | public boolean isClosed() { 96 | return closed; 97 | } 98 | 99 | @Override 100 | public void oneway(byte[] data, RequestControl requestControl) throws SailfishException { 101 | RequestProtocol protocol = RequestProtocol.newRequest(requestControl); 102 | protocol.oneway(true); 103 | protocol.body(data); 104 | 105 | if(requestControl.preferHighPerformanceWriter()){ 106 | HighPerformanceChannelWriter.write(channel, protocol); 107 | return; 108 | } 109 | 110 | if (requestControl.sent() && requestControl.timeout() > 0) { 111 | ChannelFuture future = channel.writeAndFlush(protocol); 112 | waitWriteDone(future, requestControl.timeout(), protocol, false); 113 | return; 114 | } 115 | // reduce memory consumption 116 | channel.writeAndFlush(protocol, channel.voidPromise()); 117 | } 118 | 119 | @Override 120 | public ResponseFuture request(byte[] data, RequestControl requestControl) throws SailfishException { 121 | return requestWithFuture(data, null, requestControl); 122 | } 123 | 124 | @Override 125 | public void request(byte[] data, ResponseCallback callback, RequestControl requestControl) 126 | throws SailfishException { 127 | requestWithFuture(data, callback, requestControl); 128 | } 129 | 130 | @Override 131 | public void response(ResponseProtocol response) throws SailfishException { 132 | channel.writeAndFlush(response, channel.voidPromise()); 133 | } 134 | 135 | private ResponseFuture requestWithFuture(byte[] data, ResponseCallback callback, 136 | RequestControl requestControl) throws SailfishException { 137 | final RequestProtocol protocol = RequestProtocol.newRequest(requestControl); 138 | protocol.oneway(false); 139 | protocol.body(data); 140 | 141 | ResponseFuture respFuture = new BytesResponseFuture(protocol.packetId(), getTracer()); 142 | respFuture.setCallback(callback, requestControl.timeout()); 143 | // trace before write 144 | getTracer().trace(this, protocol.packetId(), respFuture); 145 | 146 | if(requestControl.preferHighPerformanceWriter()){ 147 | HighPerformanceChannelWriter.write(channel, protocol); 148 | return respFuture; 149 | } 150 | 151 | if (requestControl.sent()) { 152 | ChannelFuture future = channel.writeAndFlush(protocol); 153 | waitWriteDone(future, requestControl.timeout(), protocol, true); 154 | return respFuture; 155 | } 156 | 157 | channel.writeAndFlush(protocol, channel.voidPromise()); 158 | return respFuture; 159 | } 160 | 161 | private void waitWriteDone(ChannelFuture future, int timeout, RequestProtocol request, boolean needRemoveTrace) 162 | throws SailfishException { 163 | boolean done = future.awaitUninterruptibly(timeout); 164 | if (!done) { 165 | // useless at most of time when do writeAndFlush(...) invoke 166 | future.cancel(true); 167 | if (needRemoveTrace) { 168 | getTracer().remove(request.packetId()); 169 | } 170 | throw new SailfishException(ExceptionCode.WRITE_TIMEOUT, 171 | String.format("write to remote[%s] timeout, protocol[%s]", channel.remoteAddress(), request)); 172 | } 173 | if (!future.isSuccess()) { 174 | if (needRemoveTrace) { 175 | getTracer().remove(request.packetId()); 176 | } 177 | throw new SailfishException(ExceptionCode.CHANNEL_WRITE_FAIL, 178 | String.format("write to remote[%s] fail, protocol[%s]", channel.remoteAddress(), request), 179 | future.cause()); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /sailfish-kernel/src/main/java/sailfish/remoting/DefaultServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import java.util.Collection; 21 | 22 | import io.netty.bootstrap.ServerBootstrap; 23 | import io.netty.buffer.PooledByteBufAllocator; 24 | import io.netty.channel.Channel; 25 | import io.netty.channel.ChannelInitializer; 26 | import io.netty.channel.ChannelOption; 27 | import io.netty.channel.ChannelPipeline; 28 | import io.netty.channel.EventLoopGroup; 29 | import io.netty.channel.WriteBufferWaterMark; 30 | import io.netty.channel.socket.SocketChannel; 31 | import io.netty.handler.timeout.IdleStateHandler; 32 | import io.netty.util.concurrent.DefaultThreadFactory; 33 | import io.netty.util.concurrent.EventExecutorGroup; 34 | import sailfish.remoting.channel.ExchangeChannelGroup; 35 | import sailfish.remoting.codec.RemotingDecoder; 36 | import sailfish.remoting.codec.RemotingEncoder; 37 | import sailfish.remoting.configuration.ExchangeServerConfig; 38 | import sailfish.remoting.constants.ChannelAttrKeys; 39 | import sailfish.remoting.constants.RemotingConstants; 40 | import sailfish.remoting.eventgroup.ServerEventGroup; 41 | import sailfish.remoting.exceptions.SailfishException; 42 | import sailfish.remoting.handler.DefaultMsgHandler; 43 | import sailfish.remoting.handler.HeartbeatChannelHandler; 44 | import sailfish.remoting.handler.MsgHandler; 45 | import sailfish.remoting.handler.NegotiateChannelHandler; 46 | import sailfish.remoting.handler.ConcreteRequestHandler; 47 | import sailfish.remoting.protocol.Protocol; 48 | import sailfish.remoting.utils.ChannelUtil; 49 | import sailfish.remoting.utils.ParameterChecker; 50 | 51 | /** 52 | * 53 | * @author spccold 54 | * @version $Id: ExchangeServer.java, v 0.1 2016年10月26日 下午3:52:19 jileng Exp $ 55 | */ 56 | public class DefaultServer implements Server { 57 | 58 | private volatile boolean isClosed = false; 59 | private final ExchangeServerConfig config; 60 | private final MsgHandler msgHandler; 61 | private Channel channel; 62 | 63 | public DefaultServer(ExchangeServerConfig config) { 64 | this.config = ParameterChecker.checkNotNull(config, "ExchangeServerConfig"); 65 | this.msgHandler = new DefaultMsgHandler(config.getRequestProcessors()); 66 | } 67 | 68 | public void start() throws SailfishException { 69 | ServerBootstrap boot = newServerBootstrap(); 70 | EventLoopGroup accept = NettyPlatformIndependent.newEventLoopGroup(1, 71 | new DefaultThreadFactory(RemotingConstants.SERVER_ACCEPT_THREADNAME)); 72 | if (null != config.getEventLoopGroup()) { 73 | boot.group(accept, config.getEventLoopGroup()); 74 | } else { 75 | boot.group(accept, ServerEventGroup.INSTANCE.getLoopGroup()); 76 | } 77 | final EventExecutorGroup executor = (null != config.getEventExecutorGroup() ? config.getEventExecutorGroup() 78 | : ServerEventGroup.INSTANCE.getExecutorGroup()); 79 | boot.localAddress(config.address().host(), config.address().port()); 80 | boot.childHandler(new ChannelInitializer() { 81 | @Override 82 | protected void initChannel(SocketChannel ch) throws Exception { 83 | ChannelPipeline pipeline = ch.pipeline(); 84 | ch.attr(ChannelAttrKeys.OneTime.idleTimeout).set(config.idleTimeout()); 85 | ch.attr(ChannelAttrKeys.maxIdleTimeout).set(config.maxIdleTimeout()); 86 | ch.attr(ChannelAttrKeys.exchangeServer).set(DefaultServer.this); 87 | pipeline.addLast(executor, 88 | RemotingEncoder.INSTANCE, 89 | new RemotingDecoder(), 90 | new IdleStateHandler(config.idleTimeout(), 0, 0), 91 | HeartbeatChannelHandler.INSTANCE, 92 | NegotiateChannelHandler.INSTANCE, 93 | ConcreteRequestHandler.INSTANCE); 94 | } 95 | }); 96 | try { 97 | channel = boot.bind().syncUninterruptibly().channel(); 98 | } catch (Throwable cause) { 99 | throw new SailfishException(cause); 100 | } 101 | } 102 | 103 | private ServerBootstrap newServerBootstrap() { 104 | ServerBootstrap serverBoot = new ServerBootstrap(); 105 | serverBoot.channel(NettyPlatformIndependent.serverChannelClass()); 106 | // connections wait for accept 107 | serverBoot.option(ChannelOption.SO_BACKLOG, 1024); 108 | serverBoot.option(ChannelOption.SO_REUSEADDR, true); 109 | // replace by heart beat 110 | serverBoot.childOption(ChannelOption.SO_KEEPALIVE, false); 111 | serverBoot.childOption(ChannelOption.TCP_NODELAY, true); 112 | serverBoot.childOption(ChannelOption.SO_SNDBUF, 32 * 1024); 113 | serverBoot.childOption(ChannelOption.SO_RCVBUF, 32 * 1024); 114 | // temporary settings, need more tests 115 | serverBoot.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(8 * 1024, 32 * 1024)); 116 | serverBoot.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 117 | //default is true, reduce thread context switching 118 | serverBoot.childOption(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP, true); 119 | return serverBoot; 120 | } 121 | 122 | @Override 123 | public void close() { 124 | close(0); 125 | } 126 | 127 | @Override 128 | public void close(int timeout) { 129 | if(isClosed()){ 130 | return; 131 | } 132 | synchronized (this) { 133 | if (isClosed()) 134 | return; 135 | ChannelUtil.closeChannel(channel); 136 | } 137 | } 138 | 139 | @Override 140 | public boolean isClosed() { 141 | return this.isClosed; 142 | } 143 | 144 | public MsgHandler getMsgHandler() { 145 | return msgHandler; 146 | } 147 | 148 | @Override 149 | public Collection listChannelGroups() { 150 | return NegotiateChannelHandler.uuid2ChannelGroup.values(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /sailfish-kernel/src/test/java/sailfish/remoting/ExchangeChannelChooserTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016-2016 spccold 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package sailfish.remoting; 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | import sailfish.remoting.channel.DefaultExchangeChannelChooserFactory; 24 | import sailfish.remoting.channel.EmptyExchangeChannel; 25 | import sailfish.remoting.channel.ExchangeChannelChooserFactory; 26 | import sailfish.remoting.exceptions.SailfishException; 27 | 28 | /** 29 | * @author spccold 30 | * @version $Id: ExchangeChannelChooserTest.java, v 0.1 2016年11月25日 下午8:35:52 spccold Exp $ 31 | */ 32 | public class ExchangeChannelChooserTest { 33 | 34 | @Test 35 | public void testPowerOfTwo() throws Exception{ 36 | int connections = 1; 37 | 38 | //test one connection 39 | MockExchangeChannel[] channels = new MockExchangeChannel[connections]; 40 | initMockExchangeChannelArray(channels); 41 | MockExchangeChannel[] deadChannels = new MockExchangeChannel[connections]; 42 | ExchangeChannelChooserFactory.ExchangeChannelChooser chooser = DefaultExchangeChannelChooserFactory.INSTANCE.newChooser(channels, deadChannels); 43 | Assert.assertNotNull(chooser.next()); 44 | 45 | channels[0].setAvailable(false); 46 | try{ 47 | chooser.next(); 48 | Assert.assertFalse(true); 49 | }catch(SailfishException cause){ 50 | Assert.assertTrue(true); 51 | } 52 | 53 | //test two connections 54 | connections = 2; 55 | channels = new MockExchangeChannel[connections]; 56 | initMockExchangeChannelArray(channels); 57 | deadChannels = new MockExchangeChannel[connections]; 58 | chooser = DefaultExchangeChannelChooserFactory.INSTANCE.newChooser(channels, deadChannels); 59 | 60 | //try three times(greater than connections) 61 | MockExchangeChannel mock = (MockExchangeChannel)chooser.next(); 62 | Assert.assertNotNull(mock); 63 | Assert.assertTrue(mock.index() == 0); 64 | 65 | mock = (MockExchangeChannel)chooser.next(); 66 | Assert.assertNotNull(mock); 67 | Assert.assertTrue(mock.index() == 1); 68 | 69 | mock = (MockExchangeChannel)chooser.next(); 70 | Assert.assertNotNull(mock); 71 | Assert.assertTrue(mock.index() == 0); 72 | 73 | mock = (MockExchangeChannel)chooser.next(); 74 | Assert.assertNotNull(mock); 75 | Assert.assertTrue(mock.index() == 1); 76 | 77 | //try to let MockExchangeChannel(index:0) unavailable 78 | channels[0].setAvailable(false); 79 | 80 | mock = (MockExchangeChannel)chooser.next(); 81 | Assert.assertNotNull(mock); 82 | Assert.assertTrue(mock.index() == 1); 83 | 84 | //try to let MockExchangeChannel(index:0) available, MockExchangeChannel(index:1) unavailable 85 | channels[0].setAvailable(true); 86 | channels[1].setAvailable(false); 87 | 88 | mock = (MockExchangeChannel)chooser.next(); 89 | Assert.assertNotNull(mock); 90 | Assert.assertTrue(mock.index() == 0); 91 | 92 | //try to let all channels unavailable 93 | channels[0].setAvailable(false); 94 | try{ 95 | chooser.next(); 96 | Assert.assertFalse(true); 97 | }catch(SailfishException cause){ 98 | Assert.assertTrue(true); 99 | } 100 | 101 | try{ 102 | chooser.next(); 103 | Assert.assertFalse(true); 104 | }catch(SailfishException cause){ 105 | Assert.assertTrue(true); 106 | } 107 | } 108 | 109 | @Test 110 | public void testGeneric() throws Exception{ 111 | int connections =3; 112 | //test one connection 113 | MockExchangeChannel[] channels = new MockExchangeChannel[connections]; 114 | initMockExchangeChannelArray(channels); 115 | MockExchangeChannel[] deadChannels = new MockExchangeChannel[connections]; 116 | ExchangeChannelChooserFactory.ExchangeChannelChooser chooser = DefaultExchangeChannelChooserFactory.INSTANCE.newChooser(channels, deadChannels); 117 | 118 | MockExchangeChannel mock = (MockExchangeChannel)chooser.next(); 119 | Assert.assertNotNull(mock); 120 | Assert.assertTrue(mock.index() == 0); 121 | 122 | mock = (MockExchangeChannel)chooser.next(); 123 | Assert.assertNotNull(mock); 124 | Assert.assertTrue(mock.index() == 1); 125 | 126 | mock = (MockExchangeChannel)chooser.next(); 127 | Assert.assertNotNull(mock); 128 | Assert.assertTrue(mock.index() == 2); 129 | 130 | mock = (MockExchangeChannel)chooser.next(); 131 | Assert.assertNotNull(mock); 132 | Assert.assertTrue(mock.index() == 0); 133 | 134 | // let index:1, index:2 unavailable 135 | channels[1].setAvailable(false); 136 | channels[2].setAvailable(false); 137 | 138 | mock = (MockExchangeChannel)chooser.next(); 139 | Assert.assertNotNull(mock); 140 | Assert.assertTrue(mock.index() == 0); 141 | 142 | //let index:0 unavailable 143 | channels[0].setAvailable(false); 144 | try{ 145 | chooser.next(); 146 | Assert.assertFalse(true); 147 | }catch(SailfishException cause){ 148 | Assert.assertTrue(true); 149 | } 150 | 151 | //let index:2 available 152 | channels[2].setAvailable(true); 153 | Assert.assertNotNull(chooser.next()); 154 | } 155 | 156 | private void initMockExchangeChannelArray(MockExchangeChannel[] channels){ 157 | if(null == channels){ 158 | throw new NullPointerException("channels"); 159 | } 160 | for(int i = 0; i< channels.length; i++){ 161 | channels[i] = new MockExchangeChannel(i); 162 | } 163 | } 164 | 165 | private static class MockExchangeChannel extends EmptyExchangeChannel{ 166 | private int index; 167 | private boolean isAvailable = true; 168 | public MockExchangeChannel(int index) { 169 | this.index = index; 170 | } 171 | 172 | public int index(){ 173 | return index; 174 | } 175 | 176 | public void setAvailable(boolean isAvailable){ 177 | this.isAvailable = isAvailable; 178 | } 179 | 180 | @Override 181 | public boolean isAvailable() { 182 | return this.isAvailable; 183 | } 184 | } 185 | } --------------------------------------------------------------------------------