Como o balanceamento de carga é alcançado nos MMOs?

26

Acredito que é um requisito comum dos MMOs que o processamento de um único fragmento ou região possa ser feito em vários servidores para facilitar a carga. Estou curioso para saber como isso pode ser feito, mantendo um mundo unificado e consistente, onde todos os jogadores e todos os NPCs podem interagir.

Minha pergunta é como o balanceamento de carga é alcançado nos MMOs?

Todos os links, livros ou informações gerais sobre como melhorar meu conhecimento sobre esse assunto também são apreciados.

CiscoIPPhone
fonte

Respostas:

30

Tente manter isso o mais simples possível e as interfaces bem definidas e documentadas. Manter e depurar um sistema complexo na produção se transforma facilmente em um inferno. Portanto, se houver uma abordagem simples e complexa, pense duas vezes antes de prosseguir com a complexa.

Definindo Serviços

Acho que o primeiro passo é identificar os serviços e suas dependências : Conteúdo estático, autenticação, bate-papo local, canais globais de bate-papo, canais regionais de bate-papo, lista de amigos, guildas, bolsa / inventário, casa de leilões, mapa global, mundo, ...

Em seguida, para cada um desses serviços, decidiu se o cliente pode falar com eles diretamente. Por exemplo, é muito fácil deixar o cliente falar diretamente com os servidores responsáveis ​​pelos Canais de bate-papo globais. Os servidores mundiais não precisam se envolver em mensagens de bate-papo. O bate-papo regional pode ser implementado da mesma maneira, mas os servidores mundiais precisam informar aos servidores de bate-papo quando os jogadores mudam de região. Novamente, eles não precisam se preocupar com as mensagens.

O terceiro passo é pensar no balanceamento de carga dentro de um serviço . Por exemplo, canais de bate-papo globais e regionais podem ser divididos em vários servidores com base em seus nomes. Provavelmente, é uma boa idéia não codificar essa divisão no cliente, mas fornecer um serviço de pesquisa.

Servidores mundiais

A parte mais difícil são geralmente os servidores mundiais , então estou começando com uma abordagem simples. Provavelmente, é uma boa idéia deixar o cliente falar diretamente com o servidor responsável pela região em que ele está. Portanto, ao fazer o login ou a região que cruza, o cliente deve ser informado a qual servidor se conectar.

A abordagem simples é dividir o mundo em regiões independentes . Com regiões independentes, quero dizer que um jogador não pode olhar de uma parte para outra e monstros não podem cruzar partes. Essas regiões são diferentes das regiões que o jogador vê com base na paisagem e na história do mundo exterior. Geralmente, a maioria dos monstros está nas masmorras e os jogadores tendem a aceitar que precisam atravessar um portal para entrar em uma masmorra. Especialmente se essas masmorras forem instanciadas por grupo de jogadores. Outros exemplos no mundo exterior são diferentes continentes e vales cercados por altas montanhas.

Uma abordagem contínua do mundo se torna complexa muito rapidamente, por isso faz sentido planejá-la bem: de quais informações o cliente precisa? Quais informações os servidores precisam compartilhar? O jogador irá interagir principalmente apenas com os objetos (incluindo monstros e NPCs) na mesma região. Você pode trapacear colocando objetos fora do intervalo de cliques da borda da zona. Isso significa que o cliente está interessado principalmente em informações somente leitura para zonas vizinhas. Nesses casos, os servidores da zona não precisam coordenar nada, exceto pela permissão de verificar se o player está perto o suficiente para conectar-se a uma zona vizinha.

Isso deixa apenas um número muito pequeno de casos difíceis nos quais objetos ou ações precisam atravessar uma borda do servidor. O que é bom, porque esses casos, como flechas e feitiços, são críticos para o desempenho. Pode ser uma boa idéia dividir o combate em ataque e defesa. Portanto, o servidor de um lançador de feitiços definirá os parâmetros de ataque, incluindo a posição do lançador. O servidor do defensor receberá a mensagem sobre o ataque e calculará o impacto. O servidor do invasor não precisa saber sobre o impacto; o cliente aprenderá sobre isso usando sua conexão somente leitura.

Dependendo da complexidade do modelo do seu player, pode levar alguns segundos para transferi-lo para outro servidor (o Second Life tem um grande problema com isso). O problema pode ser atenuado, preparando a transferência com antecedência quando o jogador se aproximar de uma borda virtual. Para que a maioria dos dados do player já esteja em cache no servidor de destino quando a transferência real acontecer.

Sumário

Divida o problema definindo serviços diferentes que podem ser divididos entre servidores com poucas dependências. Como próximo passo, veja como fazer o equilíbrio de carga nos serviços críticos. Delegue o trabalho de balanceamento ao cliente, instruindo-o a conectar-se diretamente aos servidores relevantes (obviamente, os servidores precisam verificar as permissões). Mantenha o mais simples possível, documente bem as responsabilidades dos vários serviços e servidores, forneça a opção para ativar a saída de depuração.

PS: Algumas dessas técnicas podem ser usadas para melhorar a confiabilidade. E lembre-se disso, porque o uso de muitos servidores implica um risco muito maior de que as coisas quebrem; não apenas no software, mas também no nível do hardware.

Hendrik Brummermann
fonte
Mas, realmente, como você faz essa comunicação entre processos? que tipo de IPC deve ser usado?
majidarif
10

Geralmente, o mundo está dividido em várias regiões menores. Cada uma dessas regiões geralmente é um processo de servidor independente (servidores mundiais do WoW ou nós do Eve's Sol) e pode ser executado em várias máquinas. Em alguns jogos, há portas explícitas entre os mapas (Eve, STO, Guild Wars), enquanto outros tentam mascarar isso mais (WAR, Free Realms). Aqueles que optam pela abordagem mais transparente geralmente detectam quando você está perto da fronteira entre dois servidores e os dois processos negociam uma transferência. O melhor lugar para provavelmente procurar uma descrição disso é como as torres de celular fazem transferências de aparelhos móveis. Se a carga de um único mapa (Jita, Ironforge, Earth Space Dock) ficar realmente grande, às vezes você poderá descarregar funções individuais para outros servidores (AI, certas partes do gerenciamento de jogadores), mas isso deve ser incorporado desde o início ou exigiria uma adaptação séria. É quase sempre mais econômico comprar apenas um hardware melhor para dedicar a esses poucos mapas.

coderanger
fonte
9

Acredito que é um requisito comum dos MMOs que o processamento de um único fragmento ou região possa ser feito em vários servidores para facilitar a carga. Estou curioso para saber como isso pode ser feito, mantendo um mundo unificado e consistente, onde todos os jogadores e todos os NPCs podem interagir.

Provavelmente não é tão comum quanto você pensa; pelo menos, não se você estiver pensando que um mundo contínuo é gerenciado por vários servidores simultaneamente.

Sem contar os fragmentos totalmente separados, existem duas direções nas quais você pode dividir um jogo online, que pode ser considerado "horizontal" e "vertical":

  • Divida o jogo em várias áreas geográficas separadas. Toda a funcionalidade de qualquer área geográfica é tratada por um servidor e não há interação real entre eles. (Observe que não há necessariamente apenas uma zona por servidor - um servidor pode lidar com várias zonas simultaneamente e talvez seja possível transferir zonas entre servidores para lidar com a mudança de carga.)
  • Divida o jogo em vários tipos de serviço - por exemplo. login / autorização, regras de jogo e física, chat + leilões, persistência, etc. Cada um desses serviços pode ser tratado por um servidor diferente. A resposta da nhnb enumerou outros serviços em potencial nos quais um desenvolvedor pode particionar seu jogo.

Obviamente, essas abordagens são ortogonais e você pode combinar as duas. Na verdade, é quase obrigatório ter um servidor de banco de dados separado, muito comum para transferir o login / autenticação para uma máquina separada da jogabilidade, e cada vez mais comum também cultivar bate-papo e outras comunicações não críticas, independentemente do mundo do jogo está dividido.

Mas, no geral, quando há particionamento geográfico, a maioria dos jogos evita que você interaja além desses limites, porque é difícil fazer o bem. Em vez disso, eles recorrem a outras maneiras de fazer parecer que você ainda está no mesmo fragmento e no mesmo servidor, quando na verdade não está. por exemplo. - telas de carregamento ou outras animações que encobrem uma alteração de servidor ao fazer a transição entre zonas ou de um continente para outro. - instâncias separadas de masmorra ou invasão isoladas de todos os outros. Eles são como um shard dentro de um shard e podem ser executados facilmente em um servidor separado, ajudando no balanceamento de carga.

Não posso falar com autoridade no WoW, mas acho que eles estão fazendo quase tudo o que precede: instanciar áreas geográficas separadas que não podem interagir unidas por portais de algum tipo, servidores de back-end e autenticação separados. Ouvi dizer que os reinos do WoW têm algo entre 1000 e 10000 jogadores on-line em um determinado reino de uma só vez, o que é facilmente gerenciável com os esquemas acima.

Mas, vamos supor que você tenha um mundo único e massivo e que você precisa permitir que jogadores um servidor interajam com jogadores em um servidor adjacente. Isso é fácil de fazer em teoria - primeiro, os servidores devem cooperar para compartilhar detalhes dos objetos ao longo das bordas (para que um objeto em um servidor possa ter uma representação proxy em outro) e, em seguida, basta alterar toda a sua lógica para passagem de mensagens, com mensagens sendo roteadas de um proxy de volta para a fonte autorizada, quando necessário. As mensagens podem ser passadas entre servidores ou dentro de um servidor de forma bastante transparente, de modo que uma abordagem se adapte a todos os sistemas.

O problema aqui é que a lógica anteriormente simples pode se tornar muito complexa quando traduzida para mensagens - por exemplo. uma troca de dois jogadores que pode acontecer de forma segura e atômica quando os dois jogadores estão em um servidor se torna um processo mais longo, quando as mensagens precisam ser enviadas para lá e para cá, reverificadas a cada envio e salvaguardas para garantir que um jogador não possa explorar o outro, alterando o comércio enquanto uma mensagem está sendo transmitida. Você não pode nem assumir que o outro jogador ainda existirá no momento em que a mensagem chegar (como eles podem morrer, sair, etc), portanto o código se torna muito complexo. E isso se aplicará a praticamente qualquer sistema em que duas ou mais entidades possam interagir ou cooperar - comércio, combate, agrupamento, leilões, compartilhamento de pilhagem, treinamento etc.

Esses problemas não são intransponíveis, mas para a maioria dos jogos, vale a pena tentar demais quando você pode compartilhar a carga por outros meios e manter toda a lógica do jogo em um servidor. Portanto, quase todos os jogos atuais seguem esse caminho.

Kylotan
fonte
3

Existem muitos métodos de balanceamento de carga em um servidor MMO, pois há uma ampla variedade de dados a serem processados. Eu prefiro o método de árvore de bin de processo.

Um servidor global passa as conexões do usuário para uma lixeira do processo que pode lidar com vários usuários ao mesmo tempo. os compartimentos do processo realizam todo o processamento complexo e respondem apenas ao servidor global com dados globalmente relevantes, como bate-papo e posicionamento globais. Esse método se equilibra muito melhor que os servidores da região, pois as regiões podem variar bastante em população, enquanto o processamento geral do usuário é variado o suficiente para que ele se equilibre naturalmente na maior parte do tempo.

Basta fazer um balanceamento de carga básico por meio do servidor global para que, quando uma bandeja do processo atinja um determinado uso de memória / CPU, você inicie um novo servidor da bandeja do processo.

Stephen Belanger
fonte
Como você compartilha dados compartilhados entre posições no processo, por exemplo, uma luta entre dois usuários em posições diferentes no processo. Como você garante a ordem dos eventos? Para que um jogador morto não possa mais atacar, mesmo que o caixote que o mata seja mais lento que o caixão que está atacando. Existe o risco de a sobrecarga de envio ficar alta no servidor global? O modelo de proxy para as conexões do usuário pode entrar nos limites do sistema operacional na pilha de rede.
Hendrik Brummermann
Esse modelo funciona muito bem em sistemas de informação onde a maioria das transações é isolada. Quero dizer, eles geralmente não funcionam com os mesmos dados e, nos raros casos, o bloqueio ou reversão é usado. Mas em jogos em que as lutas incluem vários jogadores e / ou criaturas, e o impacto dos ataques é influenciado pelos atributos do atacante e do defensor, essa abordagem pode ser difícil.
Hendrik Brummermann
Você pode seguir o caminho mais fácil e considerar as interações com vários usuários relevantes globalmente, ou pode criar um método simples para que os compartimentos do processo estejam cientes um do outro e se comuniquem. Cada lixeira do processo deve ser capaz de lidar com cerca de dez mil usuários de uma só vez; portanto, a comunicação de estado entre os usuários não deve ser um problema demais. O método da região é um pouco mais fácil, mas não tão equilibrado, e pode ser facilmente travado se muitos usuários entrarem na mesma região. Em um MMORPG com uma grande base de usuários equilibrando a carga uniformemente, é muito importante.
precisa
Eu adoraria conhecer "um método simples" para 2 caixas de processo para poder negociar um sistema como o combate. O problema não são as comunicações de baixo nível, mas os algoritmos típicos de jogabilidade ficam muito complicados com os participantes distribuídos.
Kylotan
Na minha implementação, meu servidor global mantém uma lista de todos os clientes conectados e monitora em qual posição do processo eles estão conectados. Se um compartimento de processo precisar acessar outro usuário, ele primeiro verifica sua própria lista de usuários. Se isso falhar, ele verifica a lista global e identifica em qual lixeira do processo o outro usuário está conectado. Os compartimentos do processo se conectam diretamente para compartilhar estados do usuário enquanto realiza o processamento unificado.
Stephen Belanger