Оригинальный адрес:блог haifeiwu и его друзей
адрес блога:www.hchstudio.cn
Добро пожаловать в перепечатку, пожалуйста, указывайте автора и источник, спасибо!
Разработка сервера будет включать в себя использование RPC более или менее.Конечно, если вы остановитесь на его использовании, это будет очень неблагоприятно для вашего собственного роста, поэтому арендодатель будет обсуждать RPC в духе того, что знает это и знает, почему.
дочерняя модель RPC
child-rpc использует метод прямого подключения к сокету для реализации удаленного вызова службы, а затем использует метод динамического прокси jdk, чтобы вызывающая сторона не знала об удаленном вызове.
дочерний 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());
}
}
}
бегать
вывод на стороне сервера
вывод на стороне клиента
конкретная реализация 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.