Въведение в пролетта с Akka

1. Въведение

В тази статия ще се съсредоточим върху интегрирането на Akka с Spring Framework - за да позволи инжектиране на базирани на Spring услуги в актьори Akka.

Преди да прочетете тази статия, се препоръчва предварително познаване на основите на Akka.

2. Инжектиране на зависимост в Akka

Akka е мощна рамка за приложения, базирана на модела за съвпадение на Actor. Рамката е написана в Scala, което, разбира се, я прави напълно използваема и в приложения, базирани на Java. И така , много често ще искаме да интегрираме Akka със съществуващо Spring-базирано приложение или просто да използваме Spring за свързване на боб в актьори.

Проблемът с интеграцията Spring / Akka се крие в разликата между управлението на зърната през пролетта и управлението на актьори в Akka: актьорите имат специфичен жизнен цикъл, който се различава от типичния жизнен цикъл на пролетния боб .

Освен това, актьорите са разделени на самия актьор (който е вътрешен детайл за изпълнение и не може да се управлява от Spring) и референция към актьор, която е достъпна от клиентски код, както и сериализуема и преносима между различни изпълнения на Akka.

За щастие, Akka предоставя механизъм, а именно разширения на Akka, който прави използването на рамки за инжектиране на външни зависимости доста лесна задача.

3. Зависимости на Maven

За да демонстрираме използването на Akka в нашия проект Spring, ще ни е необходим минимален минимум на Spring зависимостта - библиотеката spring-context , както и библиотеката akka-akter . Версиите на библиотеката могат да бъдат извлечени враздел на пом :

 4.3.1.RELEASE 2.4.8    org.springframework spring-context ${spring.version}   com.typesafe.akka akka-actor_2.11 ${akka.version}  

Не забравяйте да проверите Maven Central за най-новите версии на зависимостите spring-context и akka-akter .

И забележете как, че зависимостта на akka-актьор има в името си постфикс _2.11 , което означава, че тази версия на Akka framework е построена срещу Scala версия 2.11. Съответната версия на библиотеката Scala ще бъде транзитивно включена във вашата компилация.

4. Инжектиране на пролетен боб в Akka Actors

Нека създадем просто приложение Spring / Akka, състоящо се от един актьор, който може да отговори на името на човек, като отправя поздрав към този човек. Логиката на приветствието ще бъде извлечена в отделна услуга. Ще искаме да свържем автоматично тази услуга към екземпляр на актьор. Пролетната интеграция ще ни помогне в тази задача.

4.1. Определяне на актьор и услуга

За да демонстрираме инжектиране на услуга в актьор, ще създадем прост клас GreetingActor, определен като нетипизиран актьор (разширяващ базовия клас на Akka UntypedActor ). Основният метод на всеки актьор на Akka е методът onReceive , който получава съобщение и го обработва според определена логика.

В нашия случай изпълнението на GreetingActor проверява дали съобщението е от предварително дефиниран тип Greet , след това взема името на човека от инстанцията Greet , след това използва GreetingService, за да получи поздрав за това лице и отговаря на подателя с получения поздравителен низ. Ако съобщението е от друг неизвестен тип, то се предава на предварително дефинирания необработен метод на актьора .

Нека погледнем:

@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class GreetingActor extends UntypedActor { private GreetingService greetingService; // constructor @Override public void onReceive(Object message) throws Throwable { if (message instanceof Greet) { String name = ((Greet) message).getName(); getSender().tell(greetingService.greet(name), getSelf()); } else { unhandled(message); } } public static class Greet { private String name; // standard constructors/getters } }

Забележете, че типът съобщение Greet е дефиниран като статичен вътрешен клас в този актьор, което се счита за добра практика. Приетите типове съобщения трябва да бъдат дефинирани възможно най-близо до даден актьор, за да се избегне объркване кои типове съобщения може да обработва този актьор.

Също така забележете пролетните анотации @Component и @Scope - те определят класа като управляван от Spring променлив с обхвата на прототипа .

Обхватът е много важен, тъй като всяка заявка за извличане на боб трябва да доведе до новосъздаден екземпляр, тъй като това поведение съответства на жизнения цикъл на актьора на Akka. Ако внедрите този компонент с някакъв друг обхват, типичният случай на рестартиране на актьори в Akka най-вероятно ще функционира неправилно.

И накрая, предупреждение, че ние не трябва да е изрично @Autowire на GreetingService инстанция - това е възможно благодарение на новата функция на пролетта 4.3 нарича неявно Конструктор инжектиране .

Изпълнението на GreeterService е доста ясен, предупреждение, че можем да го определя като Spring управлявана боб чрез добавяне на @Component анотацията към нея (с по подразбиране Сингълтън обхват):

@Component public class GreetingService { public String greet(String name) { return "Hello, " + name; } }

4.2. Добавяне на пролетна поддръжка чрез разширение Akka

Най-лесният начин да интегрирате Spring с Akka е чрез разширение Akka.

Разширението е единичен екземпляр, създаден за система на актьор. Състои се от самия клас на разширение, който реализира маркера Interface Extension и клас на разширение id, който обикновено наследява AbstractExtensionId .

Тъй като тези два класа са тясно свързани, има смисъл да се внедри класът Extension, вложен в класа ExtensionId :

public class SpringExtension extends AbstractExtensionId { public static final SpringExtension SPRING_EXTENSION_PROVIDER = new SpringExtension(); @Override public SpringExt createExtension(ExtendedActorSystem system) { return new SpringExt(); } public static class SpringExt implements Extension { private volatile ApplicationContext applicationContext; public void initialize(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public Props props(String actorBeanName) { return Props.create( SpringActorProducer.class, applicationContext, actorBeanName); } } }

Първо - SpringExtension реализира един метод createExtension от класа AbstractExtensionId - който отчита създаването на екземпляр на разширение, обекта SpringExt .

Класът SpringExtension също има статично поле SPRING_EXTENSION_PROVIDER, което съдържа препратка към единствения си екземпляр. Често има смисъл да се добави частен конструктор, който изрично да заяви, че SpringExtention се предполага, че е единичен клас, но ще го пропуснем за по-голяма яснота.

На второ място , статичният вътрешен клас SpringExt е самото разширение. Тъй като Extension е просто маркер интерфейс, ние можем да дефинираме съдържанието на този клас, както сметнем за добре.

В нашия случай, ние ще се нуждаем от инициализиране метод за водене на пролетния ApplicationContext например - този метод ще се нарича само веднъж на инициализация на разширението.

Also we’ll require the props method for creating a Props object. Props instance is a blueprint for an actor, and in our case the Props.create method receives a SpringActorProducer class and constructor arguments for this class. These are the arguments that this class’ constructor will be called with.

The props method will be executed every time we’ll need a Spring-managed actor reference.

The third and last piece of the puzzle is the SpringActorProducer class. It implements Akka’s IndirectActorProducer interface which allows overriding the instantiation process for an actor by implementing the produce and actorClass methods.

As you probably already have guessed, instead of direct instantiation, it will always retrieve an actor instance from the Spring’s ApplicationContext. As we’ve made the actor a prototype-scoped bean, every call to the produce method will return a new instance of the actor:

public class SpringActorProducer implements IndirectActorProducer { private ApplicationContext applicationContext; private String beanActorName; public SpringActorProducer(ApplicationContext applicationContext, String beanActorName) { this.applicationContext = applicationContext; this.beanActorName = beanActorName; } @Override public Actor produce() { return (Actor) applicationContext.getBean(beanActorName); } @Override public Class actorClass() { return (Class) applicationContext .getType(beanActorName); } }

4.3. Putting It All Together

The only thing that’s left to do is to create a Spring configuration class (marked with @Configuration annotation) which will tell Spring to scan the current package together with all nested packages (this is ensured by the @ComponentScan annotation) and create a Spring container.

We only need to add a single additional bean — the ActorSystem instance — and initialize the Spring extension on this ActorSystem:

@Configuration @ComponentScan public class AppConfiguration { @Autowired private ApplicationContext applicationContext; @Bean public ActorSystem actorSystem() { ActorSystem system = ActorSystem.create("akka-spring-demo"); SPRING_EXTENSION_PROVIDER.get(system) .initialize(applicationContext); return system; } }

4.4. Retrieving Spring-Wired Actors

To test that everything works correctly, we may inject the ActorSystem instance into our code (either some Spring-managed application code, or a Spring-based test), create a Props object for an actor using our extension, retrieve a reference to an actor via Props object and try to greet somebody:

ActorRef greeter = system.actorOf(SPRING_EXTENSION_PROVIDER.get(system) .props("greetingActor"), "greeter"); FiniteDuration duration = FiniteDuration.create(1, TimeUnit.SECONDS); Timeout timeout = Timeout.durationToTimeout(duration); Future result = ask(greeter, new Greet("John"), timeout); Assert.assertEquals("Hello, John", Await.result(result, duration));

Here we use the typical akka.pattern.Patterns.ask pattern that returns a Scala's Future instance. Once the computation is completed, the Future is resolved with a value that we returned in our GreetingActor.onMessasge method.

Може или да изчакаме резултата, като приложим метода на Scala Await.result към бъдещето , или, по-за предпочитане, да изградим цялото приложение с асинхронни модели.

5. Заключение

В тази статия показахме как да интегрираме Spring Framework с Akka и autowire зърна в актьори.

Изходният код на статията е достъпен на GitHub.