Перейти к основному содержанию

Memcached-like приложение

В этом руководстве мы создадим клиент-серверное приложение типа memcached, основанное на протоколе обмена данными RPC и технологии ActiveJ.

Полные пример вы можете найти на GitHub.

Модули клиента и сервера Memcached#

Прежде всего, рассмотрим начальную реализацию ActiveJ RPC этих модулей, поскольку наше приложение будет построено их помощью:

note

Данная реализация охватывает только базовое использование. Вы можете добавлять дополнительные функции по мере необходимости.

Создание клиента и сервера#

Давайте напишем свой собственный сервер MemcacheLikeServer . Мы также будем использовать ActiveJ Launcher для управления жизненным циклом приложения и молниеносную библиотеку Dependency Injection ActiveJ Inject:

public class MemcacheLikeServer extends Launcher {  @Inject  WorkerPool.Instances<RpcServer> instances;
  @Provides  WorkerPool workerPool(WorkerPools workerPools) {    return workerPools.createPool(3);  }
  @Provides  Config config() {    return Config.create()        .with("memcache.buffers", "4")        .with("memcache.bufferCapacity", "64mb");  }
  @Override  protected Module getModule() {    return ModuleBuilder.create()        .install(ServiceGraphModule.create())        .install(MemcacheMultiServerModule.create())        .install(WorkerPoolModule.create())        .build();  }
  @Override  protected void run() throws Exception {    awaitShutdown();  }
  public static void main(String[] args) throws Exception {    MemcacheLikeServer server = new MemcacheLikeServer();    server.launch(args);  }}

Поскольку мы расширяем Launcher, нам необходимо переопределить 2 метода: getModule для предоставления ServiceGraphModule и run для описания основной логики примера.

  • Количество шардов равно количествуworkerPools
  • Что касается функциональности "memcached" - мы указываем количество буферов и их емкость в файле Config
  • Config используется для настройки всего, что необходимо MemcacheMultiServerModule для обработки предстоящих запросов. Наш MemcacheLikeClient будет создавать put и get запросы:
public class MemcacheLikeClient extends Launcher {  @Provides  Eventloop eventloop() {    return Eventloop.create();  }
  @Provides  RawMemcacheClientAdapter rawMemcacheClientAdapter(RawMemcacheClient client) {    return new RawMemcacheClientAdapter(client);  }
  @Provides  Config config() {    return Config.create()        .with("protocol.compression", "false")        .with("client.addresses", "localhost:9000, localhost:9001, localhost:9002");  }
  @Inject  RawMemcacheClientAdapter client;
  @Inject  Eventloop eventloop;
  @Override  protected Module getModule() {    return ModuleBuilder.create()        .install(ServiceGraphModule.create())        .install(MemcacheClientModule.create())        .install(ConfigModule.create()            .withEffectiveConfigLogger())        .build();  }
  @Override  protected void run() throws ExecutionException, InterruptedException {    String message = "Hello, Memcached Server";
    System.out.println();    CompletableFuture<Void> future = eventloop.submit(() ->        sequence(            () -> Promises.all(range(0, 25).mapToObj(i ->                client.put(i, message))),            () -> Promises.all(range(0, 25).mapToObj(i ->                client.get(i).whenResult(res -> System.out.println(i + " : " + res))))));    future.get();    System.out.println();  }
  public static void main(String[] args) throws Exception {    Launcher client = new MemcacheLikeClient();    client.launch(args);  }}
  • Поскольку MemcacheClientModule использует Rendezvous Hashing Strategy для достижения согласия для запросов между шардами серверов, в клиенте нам необходимо предоставить адреса этих шардов - 9001, 9002, и 9003 порты.
  • В Eventloop мы просим поместить сообщение в текущий i массив байтов[i] , и получить его обратно из соответствующей ячейки.
  • Таким образом, клиент будет выполнять эти операции асинхронно для трех шардов, поэтому в результате мы получим неупорядоченный выходной блок.
  • RawMemcacheClientAdapter это класс, созданный вручную. Он использует RawMemcacheClient (реализацию MemcacheClient ) и определяет логику методов put и get для нашего MemcacheLikeClient.

Запуск приложения#

  • Сначала откройте и запустите MemcacheLikeServer , чтобы запустить сервер;
  • Для запуска клиента запустите main() метод MemcacheLikeClient; Вы увидите примерно такой же вывод в консоли сервера (... опускает подобные сообщения):
Server on port #9000 accepted message!Server on port #9000 accepted message!...Server on port #9000 accepted message!Server on port #9002 accepted message!Server on port #9002 accepted message!...Server on port #9002 accepted message!Server on port #9001 accepted message!...Server on port #9001 accepted message!

В консоли клиента вы увидите аналогичный вывод, подтверждающий, что клиент получил асинхронные ответы от сервера:

0 : Hello, Memcached Server3 : Hello, Memcached Server4 : Hello, Memcached Server...11 : Hello, Memcached Server13 : Hello, Memcached Server...20 : Hello, Memcached Server21 : Hello, Memcached Server24 : Hello, Memcached Server...17 : Hello, Memcached Server19 : Hello, Memcached Server