Создайте колесо для реализации собственной инфраструктуры RPC на основе Netty.

задняя часть сервер GitHub Netty

Оригинальный адрес:блог haifeiwu и его друзей
адрес блога:www.hchstudio.cn
Добро пожаловать в перепечатку, пожалуйста, указывайте автора и источник, спасибо!

Разработка сервера будет включать в себя использование RPC более или менее.Конечно, если вы остановитесь на его использовании, это будет очень неблагоприятно для вашего собственного роста, поэтому арендодатель будет обсуждать RPC в духе того, что знает это и знает, почему.

дочерняя модель RPC

child-rpc использует метод прямого подключения к сокету для реализации удаленного вызова службы, а затем использует метод динамического прокси jdk, чтобы вызывающая сторона не знала об удаленном вызове.

child-rpc模型

дочерний RPC из коробки

служба публикации

Класс службы RPC должен отслеживать указанный IP-порт, устанавливать реализацию публикуемой службы и ссылку на ее интерфейс, а также указывать метод сериализации.В настоящее время child-rpc поддерживает методы сериализации Hessian и JACKSON.

/**
 * @author wuhf
 * @Date 2018/9/1 18:30
 **/
public class ServerTest {

    public static void main(String[] args) {
        ServerConfig serverConfig = new ServerConfig();
        serverConfig.setSerializer(Serializer.SerializeEnum.HESSIAN.serializer)
                .setPort(5201)
                .setInterfaceId(HelloService.class.getName())
                .setRef(HelloServiceImpl.class.getName());
        ServerProxy serverProxy = new ServerProxy(new NettyServer(),serverConfig);
        try {
            serverProxy.export();
            while (true){

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Служба цитирования

Клиенту RPC необходимо подключиться к удаленному IP-порту, зарегистрировать службу, на которую будет ссылаться, а затем вызвать метод sayHi для вывода результата.

/**
 * @author wuhf
 * @Date 2018/9/1 18:31
 **/
public class ClientTest {

    public static void main(String[] args) {
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.setHost("127.0.0.1")
                .setPort(5201)
                .setTimeoutMillis(100000)
                .setSerializer(Serializer.SerializeEnum.HESSIAN.serializer);
        ClientProxy clientProxy = new ClientProxy(clientConfig,new NettyClient(),HelloService.class);
        for (int i = 0; i < 10; i++) {
            HelloService helloService = (HelloService) clientProxy.refer();
            System.out.println(helloService.sayHi());
        }
    }
}

бегать

вывод на стороне сервера

rpc-srever

вывод на стороне клиента

rpc-client

конкретная реализация child-rpc

Запрос RPC, определение объекта ответного сообщения

Определите формат ответа запроса сообщений, тип сообщения, уникальный идентификатор сообщения и сообщение JSON SEMIALIZEN CONTERN. Уникальный идентификатор используется для совпадения, если клиентские запросы и ответы сервера аутентификации клиента совпадают.

// rpc 请求
public class RpcRequest implements Serializable {
    private static final long serialVersionUID = -4364536436151723421L;

    private String requestId;
    private long createMillisTime;
    private String className;
    private String methodName;
    private Class<?>[] parameterTypes;
    private Object[] parameters;

    // set get 方法省略掉
}
// rpc 响应
public class RpcResponse implements Serializable {
    private static final long serialVersionUID = 7329530374415722876L;

    private String requestId;
    private Throwable error;
    private Object result;
    // set get 方法省略掉
}

Кодирование и декодирование во время передачи по сети

Для кодирования и декодирования сообщения используется пользовательский кодек. В соответствии с сериализатором, используемым при инициализации службы, данные сериализуются в поток байтов. Стратегия распаковки заключается в установке пакета данных указанной длины, прикреплении к сокету и распаковке. пакет Заинтересованные друзья, пожалуйста, переместитеАнализ проблемы липких пакетов в Socket и ее решение

Вот реализация кода декодера:

public class NettyDecoder extends ByteToMessageDecoder {

    private Class<?> genericClass;
    private Serializer serializer;

    public NettyDecoder(Class<?> genericClass, Serializer serializer) {
        this.genericClass = genericClass;
        this.serializer = serializer;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        if (byteBuf.readableBytes() < 4) {
            return;
        }

        byteBuf.markReaderIndex();
        // 读取消息长度
        int dataLength = byteBuf.readInt();
        
        if (dataLength < 0) {
            channelHandlerContext.close();
        }

        if (byteBuf.readableBytes() < dataLength) {
            byteBuf.resetReaderIndex();
            return;
        }

        try {
            byte[] data = new byte[dataLength];
            byteBuf.readBytes(data);
            Object object = serializer.deserialize(data,genericClass);
            list.add(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Вот реализация кодировщика:

public class NettyEncoder extends MessageToByteEncoder<Object> {

    private Class<?> genericClass;
    private Serializer serializer;

    public NettyEncoder(Class<?> genericClass,Serializer serializer) {
        this.serializer = serializer;
        this.genericClass = genericClass;
    }

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object object, ByteBuf byteBuf) throws Exception {
        if (genericClass.isInstance(object)) {
            byte[] data = serializer.serialize(object);
            byteBuf.writeInt(data.length);
            byteBuf.writeBytes(data);
        }
    }
}

Обработчик бизнес-логики RPC

Реализация обработчика обработки бизнес-процессов на стороне сервера. Основная бизнес-логика заключается в реализации вызова метода посредством отражения Java.

public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcRequest rpcRequest) throws Exception {
        // invoke 通过调用反射方法获取 rpcResponse
        RpcResponse response = RpcInvokerHandler.invokeService(rpcRequest);
        channelHandlerContext.writeAndFlush(response);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error(">>>>>>>>>>> child-rpc provider netty server caught exception", cause);
        ctx.close();
    }
}

public class RpcInvokerHandler {
    public static Map<String, Object> serviceMap = new HashMap<String, Object>();
    public static RpcResponse invokeService(RpcRequest request) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Object serviceBean = serviceMap.get(request.getClassName());

        RpcResponse response = new RpcResponse();
        response.setRequestId(request.getRequestId());
        try {
            Class<?> serviceClass = serviceBean.getClass();
            String methodName = request.getMethodName();
            Class<?>[] parameterTypes = request.getParameterTypes();
            Object[] parameters = request.getParameters();

            Method method = serviceClass.getMethod(methodName, parameterTypes);
            method.setAccessible(true);
            Object result = method.invoke(serviceBean, parameters);

            response.setResult(result);
        } catch (Throwable t) {
            t.printStackTrace();
            response.setError(t);
        }
        return response;
    }
}

Основной бизнес-реализацией на стороне клиента является ожидание возврата ответа сервера. Код относительно прост и не будет опубликован.Подробности см. по ссылке на github, приведенной ниже.

Запуск RPC-сервера и клиента

Поскольку запуски сервера и клиента являются кодами шаблонов Netty, они не будут опубликованы из-за недостатка места Заинтересованные партнеры, пожалуйста, переместитеСоздание колес --- RPC руки для достижения.

резюме

Поскольку это просто для понимания сути RPC, в деталях реализации все еще есть много мест, которые не были тщательно вырезаны. Однако цель RPC состоит в том, чтобы позволить удаленным службам вызываться как локальные службы, прозрачно для вызывающей стороны, поэтому мы используем динамические прокси. И используйте обработчик Netty для отправки данных и данных ответа.В общем, фреймворк реализует простые вызовы RPC. Код относительно прост, в основном это идея и понимание лежащей в основе реализации RPC.

Справочная статья

关注我们
Подписывайтесь на нас