Spring Boot and RESTful API(14)Spring Boot with AKKA

Spring Boot and RESTful API(14)Spring Boot with AKKA

Logging Performance
https://www.slf4j.org/faq.html#logging_performance
logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);

Add AKKA to my Spring Boot Project
pom.xml

<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.12</artifactId>
<version>2.4.19</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-slf4j_2.12</artifactId>
<version>2.4.19</version>
</dependency>

In the main class, system will initiate the AKKA system.
package com.sillycat.jobsmonitorapi;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import com.sillycat.jobsmonitorapi.akka.base.SpringExtension;
import com.sillycat.jobsmonitorapi.service.JobDynamicCronService;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;

@SpringBootApplication
@EnableScheduling
public class JobsMonitorAPIApplication extends SpringBootServletInitializer {

private static final Logger logger = LoggerFactory.getLogger(JobsMonitorAPIApplication.class);

public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(JobsMonitorAPIApplication.class);

logger.info("Start to init the AKKA system...");
SpringExtension ext = context.getBean(SpringExtension.class);
ActorSystem system = context.getBean(ActorSystem.class);
ActorRef supervisor = system.actorOf(ext.props("outOfBudgetSupervisor").withMailbox("akka.priority-mailbox"),
"outOfBudgetSupervisor");
logger.info("supervisor init with path {}", supervisor.path());
logger.info("AKKA system success inited...");
logger.info("Start the cron tasks to monitor ");
context.getBean(JobDynamicCronService.class).startCron();
logger.info("Cron tasks started! ");
}

}

In the configuration Directory, system will auto set up the ENV
package com.sillycat.jobsmonitorapi.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import com.sillycat.jobsmonitorapi.akka.base.SpringExtension;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

import akka.actor.ActorSystem;

@Configuration
@Lazy
@EnableAutoConfiguration
@ComponentScan(basePackages = { "com.sillycat.jobsmonitorapi.akka" })
public class ApplicationConfiguration {

@Autowired
private ApplicationContext applicationContext;

@Autowired
private SpringExtension springExtension;

@Bean
public ActorSystem actorSystem() {
ActorSystem system = ActorSystem.create("JobsMonitorAPIAKKA", akkaConfiguration());
springExtension.initialize(applicationContext);
return system;
}

@Bean
public Config akkaConfiguration() {
return ConfigFactory.load();
}

}

My system will be named as JobsMonitorAPIAKKA, some base class will help Actor use Spring and Spring Bean find Actor
package com.sillycat.jobsmonitorapi.akka.base;

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.sillycat.jobsmonitorapi.akka.base.SpringActorProducer;
import akka.actor.Extension;
import akka.actor.Props;

@Component
public class SpringExtension implements Extension {

private ApplicationContext applicationContext;

public void initialize(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

public Props props(String actorBeanName) {
return Props.create(SpringActorProducer.class, applicationContext, actorBeanName);
}

}


package com.sillycat.jobsmonitorapi.akka.base;

import org.springframework.context.ApplicationContext;
import akka.actor.Actor;
import akka.actor.IndirectActorProducer;

public class SpringActorProducer implements IndirectActorProducer {

private final ApplicationContext applicationContext;
private final String actorBeanName;

public SpringActorProducer(ApplicationContext applicationContext, String actorBeanName) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
}

@Override
public Actor produce() {
return (Actor) applicationContext.getBean(actorBeanName);
}

@SuppressWarnings("unchecked")
@Override
public Class<? extends Actor> actorClass() {
return (Class<? extends Actor>) applicationContext.getType(actorBeanName);
}

}

Customized PriorityMaiBox Policy help system to priority the messages
package com.sillycat.jobsmonitorapi.akka.base;

import com.sillycat.jobsmonitorapi.akka.msg.OutOfBudgetJobMsg;
import com.typesafe.config.Config;

import akka.actor.ActorSystem;
import akka.dispatch.PriorityGenerator;
import akka.dispatch.UnboundedPriorityMailbox;

public class PriorityMailbox extends UnboundedPriorityMailbox {

public PriorityMailbox(ActorSystem.Settings settings, Config config) {

// Create a new PriorityGenerator, lower priority means more important
super(new PriorityGenerator() {

@Override
public int gen(Object message) {
if (message instanceof OutOfBudgetJobMsg) {
return ((OutOfBudgetJobMsg) message).getPriority();
} else {
// default
return 100;
}
}
});

}
}

Message POJO will be as normal, just add one priority column
package com.j2c.jobsmonitorapi.akka.msg;

public class OutOfBudgetJobMsg {

private String id;

private Integer sourceID;

private Integer campaignID;

private String jobReference;

private Boolean paused;

private Boolean dailyCapped;

private Boolean deduped;

private Boolean expired;

private Integer priority;

public OutOfBudgetJobMsg(String id) {
this.id = id;
this.priority = 10;
}
public OutOfBudgetJobMsg(String id, Integer sourceID, Integer campaignID, String jobReference, Boolean paused,
Boolean dailyCapped, Boolean deduped, Boolean expired) {
this.id = id;
this.sourceID = sourceID;
this.campaignID = campaignID;
this.jobReference = jobReference;
this.paused = paused;
this.dailyCapped = dailyCapped;
this.deduped = deduped;
this.expired = expired;
this.priority = 10;
}
…snip...

}

Here is my Actor
package com.sillycat.jobsmonitorapi.akka.actor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.sillycat.jobsmonitorapi.akka.msg.OutOfBudgetJobMsg;
import com.sillycat.jobsmonitorapi.domain.JobCloud;
import com.sillycat.jobsmonitorapi.repository.JobCloudPartialRepositorySolr;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;

@Component
@Scope("prototype")
public class OutOfBudgetActor extends UntypedActor {

private final LoggingAdapter logger = Logging.getLogger(getContext().system(), "TaskProcessor");

@Autowired
JobCloudPartialRepositorySolr jobCloudPartialRepositorySolr;

@Override
public void onReceive(Object message) throws Exception {
if (message instanceof OutOfBudgetJobMsg) {
logger.debug("Message we receive is {}", message);
OutOfBudgetJobMsg msg = (OutOfBudgetJobMsg) message;
jobCloudPartialRepositorySolr.update(new JobCloud(msg.getId(), msg.getSourceID(), msg.getCampaignID(),
msg.getJobReference(), msg.getPaused(), msg.getDailyCapped(), msg.getDeduped(), msg.getExpired()),
false);
// String id, Integer sourceID, Integer campaignID, String
// jobReference, Boolean paused,
// Boolean dailyCapped, Boolean deduped, Boolean expired
logger.debug("Update the status back to SOLR ");
} else {
logger.error("outOfBudgetActor receive msg = {} msg type = {}", message, message.getClass());
unhandled(message);
}

}

}

Here is my SupervisorActor
package com.sillycat.jobsmonitorapi.akka.actor;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.sillycat.jobsmonitorapi.akka.base.SpringExtension;
import com.sillycat.jobsmonitorapi.akka.msg.OutOfBudgetJobMsg;

import akka.actor.ActorRef;
import akka.actor.Terminated;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.routing.ActorRefRoutee;
import akka.routing.Routee;
import akka.routing.Router;
import akka.routing.SmallestMailboxRoutingLogic;

@Component
@Scope("prototype")
public class OutOfBudgetSupervisor extends UntypedActor {

private final LoggingAdapter logger = Logging.getLogger(getContext().system(), "OutOfBudgetSupervisor");

@Autowired
private SpringExtension springExtension;

private Router router;

@Value("${outofBudgetActorCount}")
private Integer outofBudgetActorCount;

public void preStart() throws Exception {
logger.info("Init the operation on OutOfBudget...");
List<Routee> routees = new ArrayList<Routee>();
for (int i = 0; i < this.outofBudgetActorCount; i++) {
ActorRef actor = getContext().actorOf(springExtension.props("outOfBudgetActor"));
getContext().watch(actor);
routees.add(new ActorRefRoutee(actor));
}
router = new Router(new SmallestMailboxRoutingLogic(), routees);
super.preStart();
logger.info("Init the operation on OutOfBudget success!");
}

public void onReceive(Object msg) throws Exception {
if (msg instanceof OutOfBudgetJobMsg) {
router.route(msg, getSender());
} else if (msg instanceof Terminated) {
router = router.removeRoutee(((Terminated) msg).actor());
ActorRef actor = getContext().actorOf(springExtension.props("outOfBudgetActor"));
getContext().watch(actor);
router = router.addRoutee(new ActorRefRoutee(actor));
} else {
logger.error("Unable to handle msg {}", msg);
}
}

public void postStop() throws Exception {
logger.info("Shutting down OutOfBudgetSupervisor");
super.postStop();
}
}

The configuration file application.conf will be as follow:
akka {

loggers = ["akka.event.slf4j.Slf4jLogger"]

# Log level used by the configured loggers (see "loggers") as soon
# as they have been started; before that, see "stdout-loglevel"
# Options: OFF, ERROR, WARNING, INFO, DEBUG
loglevel = "DEBUG"

# Log level for the very basic logger activated during ActorSystem startup.
# This logger prints the log messages to stdout (System.out).
# Options: OFF, ERROR, WARNING, INFO, DEBUG
stdout-loglevel = "DEBUG"

priority-mailbox {
mailbox-type = "com.sillycat.jobsmonitorapi.akka.base.PriorityMailbox"
}

}

References:
http://kimrudolph.de/blog/spring-boot-meets-akka
https://github.com/krudolph/akkaflow
https://www.slf4j.org/faq.html#logging_performance
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值