Gama de utilidades para outros módulos. Inclui LambdaReflection
que permite definir e obter valores de campo de uma instância ou construtor de acesso por meio de interfaces funcionais criadas por LambdaMetafactory
.
Biblioteca de serialização abstrata que suporta qualquer implementação de buffer. (Des)serializa automaticamente classes marcadas com @AutoSerializable
consistindo em campos de tipos padrão, implementações Proto4jSerializable
ou outros membros @AutoSerializable
.
Também suporta herança: a classe serializable pode estender outra @AutoSerializable
, caso em que todos os campos pai também serão serializados transitivamente.
Os campos que devem ser ignorados durante a serialização devem ser anotados com @Transient
.
Biblioteca de rede com implementação personalizada baseada em UDP com confiabilidade configurável.
Proto4jServer
e Proto4jClient
permitem transferir dados usando datagramas entre soquetes.
Por padrão, qualquer dado transmitido é ordenado, confiável, pode ser dividido em vários pacotes UDP e combinado de volta no lado do receptor e tem garantia de entrega.
Todos os pacotes UDP que estão sendo enviados têm a seguinte estrutura:
É sua escolha selecionar como transmitir os dados. Ele pode ser configurado especificando sinalizadores para métodos de envio. Eles estão todos localizados em Proto4jPacket
. Flag
.
Os seguintes sinalizadores estão disponíveis:
Nome | Valor | Significado |
---|---|---|
CONFIRMATION | 0x01 | Marca que este pacote é um indicador de que outros pacotes foram recebidos com sucesso. Necessário para confiabilidade de transmissão. Em geral, apenas para uso interno. |
PARTIAL | 0x02 | Marca que este pacote UDP exato faz parte de um pacote maior. Quando usado junto com o sinalizador CONFIRMATION , indica que alguma parte de um pacote maior foi entregue. |
UNORDERED | 0x04 | Marca que este pacote pode ser tratado fora de ordem. |
UNSIGNED_BODY | 0x08 | Por padrão, todos os pacotes enviados são assinados usando CRC32 , mas para pacotes com esse sinalizador especificado, apenas o cabeçalho do pacote será assinado. Isso significa que os pacotes podem conter bytes inválidos (embora nenhuma perda de dados seja garantida). |
UNRELIABLE | 0x10 | Marca este pacote como não exigindo confirmação. Caso o receptor não receba este pacote, o remetente não fará nada a respeito. |
INDIVISIBLE | 0x20 | Os pacotes UDP têm comprimento limitado, então o Proto4J divide dados enormes em vários pacotes menores. Este sinalizador indica que caso o pacote exceda o limite de tamanho do pacote único, uma exceção será lançada em vez de realizar a divisão. |
Nenhum handshake ou ping é suportado neste nível, mas você pode configurar seus próprios manipuladores de pacotes usando o método Proto4jSocket
.setInitialPacketHandler(BiConsumer<C, Proto4jPacket>)
. Os pacotes que chegam a este ponto nunca são marcados com sinalizadores CONFIRMATION
ou PARTIAL
, portanto, todas as instâncias Proto4jPacket
tratadas lá contêm dados exatos enviados pelo remetente (até o sinalizador UNSIGNED_BODY
).
Além disso, quando você inicia o soquete, um CompletionStage<Void>
será retornado, o que pode ajudá-lo a iniciar a lógica de comunicação entre os soquetes.
Quando você está prestes a instanciar qualquer soquete no Proto4J, você deve passar a quantidade de threads de trabalho e de manipulador para o construtor do soquete.
Os trabalhadores são usados apenas para ler dados do soquete.
Os manipuladores são usados para lidar com a lógica quando um novo pacote aparece.
Esta é uma interface de nível superior em relação ao nível anterior. Para começar a trabalhar com ele, dê uma olhada em Proto4jHighServer
e Proto4jHighClient
ou em suas implementações básicas: BaseProto4jHighServer
e BaseProto4jHighClient
.
Quando o cliente interage inicialmente com o servidor, ele inicia o handshaking . Após a conclusão, o servidor e o cliente farão ping entre si para garantir que a conexão não seja perdida.
Em contraste com o nível baixo , você pode enviar pacotes de alto nível pela rede não apenas manipulando bytes brutos, mas também usando entidades complexas. Para fazer isso, crie sua própria classe estendendo EnumeratedProto4jPacket
ou CallbackProto4jPacket
. Tudo o que você precisa fazer para que funcione é implementar os métodos write(Buffer)
e read(Buffer)
e registrar seu pacote no PacketManager
em ambos os lados.
Além disso, existe uma classe alternativa PacketHandler
que funciona com esses pacotes em vez de Proto4jPacket
s.
É um cenário comum aguardar que algum pacote responda aos enviados. Estas funcionalidades já estão implementadas neste nível. Você pode especificar o tempo máximo de espera e lidar com a resposta da maneira que desejar. Isso pode ser feito enviando o pacote inicial usando HighChannel
. método sendWithCallback(CallbackProto4jPacket)
.
A seguir está uma lista de propriedades do sistema que podem ser usadas para afetar a maneira como os módulos se comportam internamente. Todos os valores de tempo são especificados em milissegundos.
Nome | Valor padrão | Descrição |
---|---|---|
proto4j.maxDatagramSize | 508 | Tamanho máximo permitido do datagrama. Esteja ciente de que ele conta todo o tamanho do pacote UDP. |
proto4j.maxSequenceNumber | 2_000_000_000 | Número de sequência máximo do pacote. Quando o contador interno atingir este valor, ele será zerado. |
proto4j.reliabilityThreshold | 20 | Atraso de pacotes não confirmados (e não marcados com sinalizador UNRELIABLE ). |
proto4j.callbacksRegistryDelay | 100 | Taxa na qual as verificações de registro dos retornos de chamada recuperam seus retornos de chamada expirados. |
proto4j.callbacksInitialDelay | 500 | É o tempo padrão usado sempre que um pacote é enviado e aguardado sempre que o tempo de espera não for especificado explicitamente. |
proto4j.highTimeout | 10_000 | Se o servidor não receber nenhum pacote do cliente por tanto tempo, ele o desconectará. |
proto4j.highPingDelay | 1_000 | Se o servidor indicar que não houve recepções ou envios para o cliente durante tanto tempo, ele enviará a resposta a este e aguardará um pacote de ping. |
Esta é uma API de nível superior em vez de uma de alto nível . Em vez de implementar manualmente os pacotes e manipulá-los, você trabalha por meio de serviços.
Para começar a trabalhar com ele use RpcServer
e RpcClient
.
O servidor neste nível é usado apenas para fins de roteamento, mas os clientes atuam tanto como usuários quanto como implementadores do serviço.
O serviço consiste em partes de interface e implementação. Como usuário de serviço, você pode obter a instância da interface de serviço por meio de RpcClient
.getServiceManager().getService(Class<S>)
. Todos os seus métodos serão proxy para implementações registradas e serão executados remotamente.
Para criar seu próprio serviço, comece com uma interface e anote-a com @Proto4jService.
A interface de serviço pode ter métodos padrão e estáticos, mas seu tipo de retorno deve ser void
, serializável ou um CompletionStage
dos tipos anteriores. Além disso, todos os argumentos devem ser serializáveis.
Os tipos serializáveis são os seguintes:
String
e UUID
@AutoSerializable
BufferSerializable
List
, Set
e Map
de tipos serializáveis Se um método deve ser executado em todas as implementações de serviço registradas, ele deve ser anotado com @Broadcast
no entanto, tais métodos só podem retornar void
ou CompletionStage<Void>
.
Por padrão, quando você invoca o método, ele será executado em uma implementação aleatória. Se você deseja controlar a distribuição da execução, marque alguns dos argumentos do método com @Index
: sempre que o método for invocado, a implementação será selecionada com base no código hash dos argumentos marcados.
Sempre que o serviço é registrado, todos os métodos são convertidos em identificador inteiro. Não pode haver dois métodos com o mesmo identificador, mas tal situação pode ocorrer. Para lidar com isso, anote o método com @MethodIdentifier
com identificador estático especificado explicitamente.
Quando você já criou uma interface de serviço, agora crie sua implementação e registre-a usando RpcClient
.getServiceManager().registerService(Class<S>, I)
.
O cenário comum é ter interface de serviço em dois conjuntos de clientes e ainda ter a implementação em apenas um deles.
Esta é uma camada de nível superior sobre o RPC básico.
Ao criar um back-end distribuído (ou seja, microsserviços), é uma boa prática minimizar o número de pontos de falha. Há apenas um ponto de falha no esquema descrito na seção anterior: a instância de servidor único. Conclave é um conjunto de servidores que funcionam simultaneamente.
Todos os servidores do Conclave estão conectados entre si, mas cada cliente está conectado apenas a um único servidor. As consultas RPC estão sendo distribuídas e roteadas normalmente por toda a rede para que você não precise se preocupar com isso.
Para começar a trabalhar com Conclave , dê uma olhada em RpcConclaveServer
e RpcConclaveClient
. Para instanciar qualquer um deles, você terá que passar um List<InetSocketAddress>
- lista de todos os pontos de destino dos servidores.
Quanto ao módulo Transporte , existe um conjunto de propriedades do sistema que estão sendo buscadas no módulo RPC .
Nome | Valor padrão | Descrição |
---|---|---|
proto4j.conclaveWorkers | 2 | Número de threads de trabalho usados por cada um dos clientes internos do servidor (que estão sendo usados para acessar outros servidores). |
proto4j.conclaveHandlers | 2 | Número de threads manipuladores usados por cada cliente interno do servidor (que estão sendo usados para acessar outros servidores). |
proto4j.conclaveTimeout | 1_000 | Tempo máximo que o servidor aguardará até que o handshake com outro servidor seja feito. Caso contrário, considerará este último como um não executado, encerrando as próprias tentativas de conexão, caso em que a conexão só será reiniciada em caso de solicitação de outro em sua inicialização. |