/** * Serializes the request sent to the * {@link org.apache.flink.queryablestate.network.AbstractServerBase}. * * @param alloc The {@link ByteBufAllocator} used to allocate the buffer to serialize the message into. * @param requestId The id of the request to which the message refers to. * @param request The request to be serialized. * @return A {@link ByteBuf} containing the serialized message. */ public static <REQ extends MessageBody> ByteBuf serializeRequest( final ByteBufAllocator alloc, final long requestId, final REQ request) { Preconditions.checkNotNull(request); return writePayload(alloc, requestId, MessageType.REQUEST, request.serialize()); }
/** * De-serializes the response sent to the * {@link org.apache.flink.queryablestate.network.Client}. * <pre> * <b>The buffer is expected to be at the response position.</b> * </pre> * @param buf The {@link ByteBuf} containing the serialized response. * @return The response. */ public RESP deserializeResponse(final ByteBuf buf) { Preconditions.checkNotNull(buf); return responseDeserializer.deserializeMessage(buf); }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { ByteBuf buf = (ByteBuf) msg; MessageType msgType = MessageSerializer.deserializeHeader(buf); if (msgType == MessageType.REQUEST_RESULT) { long requestId = MessageSerializer.getRequestId(buf); RESP result = serializer.deserializeResponse(buf); callback.onRequestResult(requestId, result); } else if (msgType == MessageType.REQUEST_FAILURE) { RequestFailure failure = MessageSerializer.deserializeRequestFailure(buf); callback.onRequestFailure(failure.getRequestId(), failure.getCause()); } else if (msgType == MessageType.SERVER_FAILURE) { throw MessageSerializer.deserializeServerFailure(buf); } else { throw new IllegalStateException("Unexpected response type '" + msgType + "'"); } } catch (Throwable t1) { try { callback.onFailure(t1); } catch (Throwable t2) { LOG.error("Failed to notify callback about failure", t2); } } finally { ReferenceCountUtil.release(msg); } }
/** * Tests request failure serialization. */ @Test public void testKvStateRequestFailureSerialization() throws Exception { long requestId = Integer.MAX_VALUE + 1111222L; IllegalStateException cause = new IllegalStateException("Expected test"); ByteBuf buf = MessageSerializer.serializeRequestFailure(alloc, requestId, cause); int frameLength = buf.readInt(); assertEquals(MessageType.REQUEST_FAILURE, MessageSerializer.deserializeHeader(buf)); RequestFailure requestFailure = MessageSerializer.deserializeRequestFailure(buf); assertEquals(buf.readerIndex(), frameLength + 4); assertEquals(requestId, requestFailure.getRequestId()); assertEquals(cause.getClass(), requestFailure.getCause().getClass()); assertEquals(cause.getMessage(), requestFailure.getCause().getMessage()); }
/** * Tests response serialization with zero-length serialized result. */ @Test public void testResponseSerializationWithZeroLengthSerializedResult() throws Exception { byte[] serializedResult = new byte[0]; final KvStateResponse response = new KvStateResponse(serializedResult); final MessageSerializer<KvStateInternalRequest, KvStateResponse> serializer = new MessageSerializer<>(new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer()); ByteBuf buf = MessageSerializer.serializeResponse(alloc, 72727278L, response); int frameLength = buf.readInt(); assertEquals(MessageType.REQUEST_RESULT, MessageSerializer.deserializeHeader(buf)); assertEquals(72727278L, MessageSerializer.getRequestId(buf)); KvStateResponse responseDeser = serializer.deserializeResponse(buf); assertEquals(buf.readerIndex(), frameLength + 4); assertArrayEquals(serializedResult, responseDeser.getContent()); }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; assertEquals(MessageType.REQUEST, MessageSerializer.deserializeHeader(buf)); long requestId = MessageSerializer.getRequestId(buf); KvStateInternalRequest request = serializer.deserializeRequest(buf); buf.release(); KvStateResponse response = new KvStateResponse(serializedResult); ByteBuf serResponse = MessageSerializer.serializeResponse( ctx.alloc(), requestId, response); ctx.channel().writeAndFlush(serResponse); } });
/** * Tests server failure serialization. */ @Test public void testServerFailureSerialization() throws Exception { IllegalStateException cause = new IllegalStateException("Expected test"); ByteBuf buf = MessageSerializer.serializeServerFailure(alloc, cause); int frameLength = buf.readInt(); assertEquals(MessageType.SERVER_FAILURE, MessageSerializer.deserializeHeader(buf)); Throwable request = MessageSerializer.deserializeServerFailure(buf); assertEquals(buf.readerIndex(), frameLength + 4); assertEquals(cause.getClass(), request.getClass()); assertEquals(cause.getMessage(), request.getMessage()); }
@Override public AbstractServerHandler<KvStateRequest, KvStateResponse> initializeHandler() { MessageSerializer<KvStateRequest, KvStateResponse> serializer = new MessageSerializer<>( new KvStateRequest.KvStateRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer()); return new KvStateClientProxyHandler(this, queryExecutorThreads, serializer, stats); } }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { final String msg = "Exception in server pipeline. Caused by: " + ExceptionUtils.stringifyException(cause); final ByteBuf err = MessageSerializer.serializeServerFailure(ctx.alloc(), new RuntimeException(msg)); LOG.debug(msg); ctx.writeAndFlush(err).addListener(ChannelFutureListener.CLOSE); }
/** * Helper for serializing the messages. * * @param alloc The {@link ByteBufAllocator} used to allocate the buffer to serialize the message into. * @param requestId The id of the request to which the message refers to. * @param messageType The {@link MessageType type of the message}. * @param payload The serialized version of the message. * @return A {@link ByteBuf} containing the serialized message. */ private static ByteBuf writePayload( final ByteBufAllocator alloc, final long requestId, final MessageType messageType, final byte[] payload) { final int frameLength = HEADER_LENGTH + REQUEST_ID_SIZE + payload.length; final ByteBuf buf = alloc.ioBuffer(frameLength + Integer.BYTES); buf.writeInt(frameLength); writeHeader(buf, messageType); buf.writeLong(requestId); buf.writeBytes(payload); return buf; }
/** * Helper for serializing the header. * * @param buf The {@link ByteBuf} to serialize the header into. * @param messageType The {@link MessageType} of the message this header refers to. */ private static void writeHeader(final ByteBuf buf, final MessageType messageType) { buf.writeInt(VERSION); buf.writeInt(messageType.ordinal()); }
/** * De-serializes the {@link RequestFailure} sent to the * {@link org.apache.flink.queryablestate.network.Client} in case of * protocol related errors. * <pre> * <b>The buffer is expected to be at the correct position.</b> * </pre> * @param buf The {@link ByteBuf} containing the serialized failure message. * @return The failure message. */ public static RequestFailure deserializeRequestFailure(final ByteBuf buf) throws IOException, ClassNotFoundException { long requestId = buf.readLong(); Throwable cause; try (ByteBufInputStream bis = new ByteBufInputStream(buf); ObjectInputStream in = new ObjectInputStream(bis)) { cause = (Throwable) in.readObject(); } return new RequestFailure(requestId, cause); }
/** * De-serializes the header and returns the {@link MessageType}. * <pre> * <b>The buffer is expected to be at the header position.</b> * </pre> * @param buf The {@link ByteBuf} containing the serialized header. * @return The message type. * @throws IllegalStateException If unexpected message version or message type. */ public static MessageType deserializeHeader(final ByteBuf buf) { // checking the version int version = buf.readInt(); Preconditions.checkState(version == VERSION, "Version Mismatch: Found " + version + ", Expected: " + VERSION + '.'); // fetching the message type int msgType = buf.readInt(); MessageType[] values = MessageType.values(); Preconditions.checkState(msgType >= 0 && msgType < values.length, "Illegal message type with index " + msgType + '.'); return values[msgType]; }
@Override public AbstractServerHandler<KvStateInternalRequest, KvStateResponse> initializeHandler() { this.serializer = new MessageSerializer<>( new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer()); return new KvStateServerHandler(this, kvStateRegistry, serializer, stats); }
/** * Serializes the response sent to the * {@link org.apache.flink.queryablestate.network.Client}. * * @param alloc The {@link ByteBufAllocator} used to allocate the buffer to serialize the message into. * @param requestId The id of the request to which the message refers to. * @param response The response to be serialized. * @return A {@link ByteBuf} containing the serialized message. */ public static <RESP extends MessageBody> ByteBuf serializeResponse( final ByteBufAllocator alloc, final long requestId, final RESP response) { Preconditions.checkNotNull(response); return writePayload(alloc, requestId, MessageType.REQUEST_RESULT, response.serialize()); }
/** * De-serializes the request sent to the * {@link org.apache.flink.queryablestate.network.AbstractServerBase}. * <pre> * <b>The buffer is expected to be at the request position.</b> * </pre> * @param buf The {@link ByteBuf} containing the serialized request. * @return The request. */ public REQ deserializeRequest(final ByteBuf buf) { Preconditions.checkNotNull(buf); return requestDeserializer.deserializeMessage(buf); }
/** * Serializes the failure message sent to the * {@link org.apache.flink.queryablestate.network.Client} in case of * server related errors. * * @param alloc The {@link ByteBufAllocator} used to allocate the buffer to serialize the message into. * @param cause The exception thrown at the server. * @return The failure message. */ public static ByteBuf serializeServerFailure( final ByteBufAllocator alloc, final Throwable cause) throws IOException { final ByteBuf buf = alloc.ioBuffer(); // Frame length is set at end buf.writeInt(0); writeHeader(buf, MessageType.SERVER_FAILURE); try (ByteBufOutputStream bbos = new ByteBufOutputStream(buf); ObjectOutput out = new ObjectOutputStream(bbos)) { out.writeObject(cause); } // Set frame length int frameLength = buf.readableBytes() - Integer.BYTES; buf.setInt(0, frameLength); return buf; }
@Override public AbstractServerHandler<TestMessage, TestMessage> initializeHandler() { return new AbstractServerHandler<TestMessage, TestMessage>( this, new MessageSerializer<>(new TestMessage.TestMessageDeserializer(), new TestMessage.TestMessageDeserializer()), requestStats) { @Override public CompletableFuture<TestMessage> handleRequest(long requestId, TestMessage request) { TestMessage response = new TestMessage(getServerName() + '-' + request.getMessage()); return CompletableFuture.completedFuture(response); } @Override public CompletableFuture<Void> shutdown() { return CompletableFuture.completedFuture(null); } }; }
private static Client<KvStateInternalRequest, KvStateResponse> createInternalClient(int threads) { final MessageSerializer<KvStateInternalRequest, KvStateResponse> messageSerializer = new MessageSerializer<>( new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer()); return new Client<>( "Queryable State Proxy Client", threads, messageSerializer, new DisabledKvStateRequestStats()); }
/** * Create the Queryable State Client. * @param remoteAddress the {@link InetAddress address} of the {@code Client Proxy} to connect to. * @param remotePort the port of the proxy to connect to. */ public QueryableStateClient(final InetAddress remoteAddress, final int remotePort) { Preconditions.checkArgument(remotePort >= 0 && remotePort <= 65536, "Remote Port " + remotePort + " is out of valid port range (0-65536)."); this.remoteAddress = new InetSocketAddress(remoteAddress, remotePort); final MessageSerializer<KvStateRequest, KvStateResponse> messageSerializer = new MessageSerializer<>( new KvStateRequest.KvStateRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer()); this.client = new Client<>( "Queryable State Client", 1, messageSerializer, new DisabledKvStateRequestStats()); }