Статьи

Cajo, самый простой способ реализовать распределенные вычисления на Java

Получено из вводного раздела статьи Джонаса Бонера «Распределенные вычисления — это просто», опубликованной на TheServerSide.com 1 мая 2006 года:

«Распределенные вычисления становятся все более важными в мире разработки корпоративных приложений. Сегодня разработчикам постоянно приходится решать такие вопросы, как: Как повысить масштабируемость, масштабируя приложение за пределы одного узла? Как вы можете гарантировать высокую доступность, устранить отдельные точки отказа и обеспечить соответствие вашим SLA?

Для многих разработчиков наиболее естественным способом решения этой проблемы было бы разделение архитектуры на группы компонентов или служб, которые распределены между различными серверами. Хотя это и неудивительно, учитывая наследие CORBA, EJB, COM и RMI, которое присутствует у большинства разработчиков, если вы решите пойти по этому пути, у вас будет много проблем. В большинстве случаев это не стоит усилий и даст вам больше проблем, чем решит. »

С другой стороны, распределенные вычисления и Java прекрасно сочетаются друг с другом. Будучи первым языком, разработанным снизу вверх с учетом работы с сетями, Java облегчает взаимодействие компьютеров. Даже самый простой апплет, работающий в браузере, является распределенным приложением, если вы об этом думаете. Клиент, на котором запущен браузер, загружает и выполняет код, который доставляется какой-либо другой системой. Но даже этот простой апплет был бы невозможен без гарантий переносимости и безопасности Java: апплет может работать на любой платформе и не может саботировать свой хост.

Проект cajo — это небольшая библиотека, обеспечивающая мощное динамическое взаимодействие с несколькими машинами. Это удивительно простой в использовании, но непревзойденный по производительности. Это уникальная интегрированная среда распределенных вычислений, то есть она не накладывает ни структурных требований на ваши приложения, ни исходных изменений. Это позволяет нескольким удаленным JVM без проблем работать вместе, как один.

Владелец проекта Джон Катерино утверждает: «Король горы! 😉 » и бросает вызов всем, кто хочет доказать, что в Java существует распределенная вычислительная среда, которая одинаково гибкая и такая же быстрая, как cajo .

Честно говоря, лично меня убеждает высказывание Джона; и я твердо верю, что вы также будете, если вы просто позволите мне показать вам пример клиент-сервер. Вы будете удивлены тем, насколько простой и гибкий фреймворк cajo :

Сервер.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import gnu.cajo.Cajo; // The cajo implementation of the Grail
 
public class Server {
 
   public static class Test { // remotely callable classes must be public
      // though not necessarily declared in the same class
      private final String greeting;
      // no silly requirement to have no-arg constructors
      public Test(String greeting) { this.greeting = greeting; }
      // all public methods, instance or static, will be remotely callable
      public String foo(Object bar, int count) {
         System.out.println("foo called w/ " + bar + ' ' + count + " count");
         return greeting;
      }
      public Boolean bar(int count) {
         System.out.println("bar called w/ " + count + " count");
         return Boolean.TRUE;
      }
      public boolean baz() {
         System.out.println("baz called");
         return true;
      }
      public String other() { // functionality not needed by the test client
         return "This is extra stuff";
      }
   } // arguments and return objects can be custom or common to server and client
 
   public static void main(String args[]) throws Exception { // unit test
      Cajo cajo = new Cajo(0);
      System.out.println("Server running");
      cajo.export(new Test("Thanks"));
   }
}

Компилировать через:

1
javac -cp cajo.jar;. Server.java

Выполнить через:

1
java -cp cajo.jar;. Server

Как вы можете видеть только с 2 командами:

1
2
Cajo cajo = new Cajo(0);
cajo.export(new Test("Thanks"));

мы можем выставить любой POJO (простой старый Java-объект) как распределенный сервис!

А теперь Client.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import gnu.cajo.Cajo;
 
import java.rmi.RemoteException; // caused by network related errors
 
interface SuperSet {  // client method sets need not be public
   void baz() throws RemoteException;
} // declaring RemoteException is optional, but a nice reminder
 
interface ClientSet extends SuperSet {
   boolean bar(Integer quantum) throws RemoteException;
   Object foo(String barbaz, int foobar) throws RemoteException;
} // the order of the client method set does not matter
 
public class Client {
   public static void main(String args[]) throws Exception { // unit test
      Cajo cajo = new Cajo(0);
      if (args.length > 0) { // either approach must work...
         int port = args.length > 1 ? Integer.parseInt(args[1]) : 1198;
         cajo.register(args[0], port);
         // find server by registry address & port, or...
      } else Thread.currentThread().sleep(100); // allow some discovery time
 
      Object refs[] = cajo.lookup(ClientSet.class);
      if (refs.length > 0) { // compatible server objects found
         System.out.println("Found " + refs.length);
         ClientSet cs = (ClientSet)cajo.proxy(refs[0], ClientSet.class);
         cs.baz();
         System.out.println(cs.bar(new Integer(77)));
         System.out.println(cs.foo(null, 99));
      } else System.out.println("No server objects found");
      System.exit(0); // nothing else left to do, so we can shut down
   }
}

Компилировать через:

1
javac -cp cajo.jar;. Client.java

Выполнить через:

1
java -cp cajo.jar;. Client

Клиент может находить объекты сервера либо путем предоставления адреса и порта сервера (если доступно), либо с помощью многоадресной рассылки. Для поиска соответствующего объекта сервера используется « Подтип динамического клиента ». Для всех вас, кто не знает, что означает « Динамический подтип клиента », Джон Катерино объясняет в своем блоге :

«Часто сервисные объекты реализуют большой, богатый интерфейс. В других случаях сервисные объекты реализуют несколько интерфейсов, объединяя их функциональность в различные логические задачи. Довольно часто клиенту нужно использовать только небольшую часть интерфейса; или, возможно, некоторые методы из нескольких интерфейсов логической группировки, чтобы удовлетворить свои собственные потребности.

Способность клиента определять свой собственный интерфейс из тех, которые определены сервисным объектом, в Java называется подтипом. (в отличие от подклассов) Однако, в отличие от обычного подтипирования Java; Динамическое подтипирование клиента означает создание совершенно другого интерфейса. Что делает этот подтип динамическим, так это то, что он работает с исходным неизмененным сервисным объектом.

Это может быть очень эффективным методом управления сложностью на стороне клиента ».

Разве это не круто ??? Нам просто нужно определить интерфейс, в котором «нуждается» наш клиент, и найти соответствующий объект сервера, который соответствует спецификации клиента. Следующая команда, полученная из нашего примера, выполняет именно это:

1
Object refs[] = cajo.lookup(ClientSet.class);

И последнее, но не менее важное: мы можем создать «прокси» на стороне клиента для объекта сервера и удаленно вызывать его методы, как обычную ссылку на локальный объект, выполнив следующую команду:

1
ClientSet cs = (ClientSet)cajo.proxy(refs[0], ClientSet.class);

Вот и все. Они обеспечивают полную совместимость между распределенными JVM. Это просто не может быть легче, чем это.

Что касается производительности, я провел несколько предварительных тестов на предоставленном примере и набрал в среднем 12000 TPS в следующей системе:

Sony Vaio со следующими характеристиками:

  • Система: openSUSE 11.1 (x86_64)
  • Процессор (ЦП): Процессор Intel® Core ™ 2 Duo T6670 с частотой 2,20 ГГц
  • Скорость процессора: 1200,00 МГц
  • Общий объем памяти (ОЗУ): 2,8 ГБ
  • Java: OpenJDK 1.6.0_0 64-битная

Для вашего удобства я предоставляю фрагмент кода, который я использовал для проведения стресс-теста:

1
2
3
4
5
int repeats = 1000000;
long start = System.currentTimeMillis();
for(int i = 0; i < repeats;i ++)
  cs.baz();
System.out.println("TPS : " + repeats/((System.currentTimeMillis() - start)/1000d));

Удачного кодирования! и не забудьте поделиться!

Джастин

Статьи по Теме :