Java Akka-Actor消息中的封闭逻辑
我最近开始学习AKKA,我创建了一个路由器演员,有一群数据库工作者演员坐在路由器后面。我附带了这个实现,每个工作者参与者将有一个数据库连接,我通过实现接口SQLExecutable将数据库逻辑封装到每条消息中,参与者将执行每条消息。如果连接断开,异常将由路由器的监控策略处理 我的问题是,我知道AKKA中的每一条消息都应该是不可变的,在这里我将逻辑封装在消息中,我不确定这是否会导致任何意外行为。我的目的只是想将业务逻辑和数据库连接解耦Java Akka-Actor消息中的封闭逻辑,java,mysql,akka,actor,slick,Java,Mysql,Akka,Actor,Slick,我最近开始学习AKKA,我创建了一个路由器演员,有一群数据库工作者演员坐在路由器后面。我附带了这个实现,每个工作者参与者将有一个数据库连接,我通过实现接口SQLExecutable将数据库逻辑封装到每条消息中,参与者将执行每条消息。如果连接断开,异常将由路由器的监控策略处理 我的问题是,我知道AKKA中的每一条消息都应该是不可变的,在这里我将逻辑封装在消息中,我不确定这是否会导致任何意外行为。我的目的只是想将业务逻辑和数据库连接解耦 public class TokenRepository {
public class TokenRepository {
private static final ActorSelection DB_ROUTER_SELECTION;
private static final String DB_ROUTER_SELECTION_PATH = "/user/JDBCRouter";
private static final int EXECUTE_TIME_OUT = 1000;
private static final String SAVE_TOKEN_STR = "INSERT INTO Tokens " +
"(uuid, creation, email, expiration, isSignUp) " +
"VALUES (?,?,?,?,?)";
private static final String FIND_TOKEN_STR = "SELECT * FROM Tokens " +
"WHERE uuid = ?";
private static final String DELETE_TOKEN_STR = "DELETE FROM Tokens" +
"WHERE uuid = ?";
static {
DB_ROUTER_SELECTION = AkkaActorSystem.getInstance().actorSelection(DB_ROUTER_SELECTION_PATH);
}
private static class saveTokenExecutor implements SQLExecutable {
private final Token token;
saveTokenExecutor(Token token) {
this.token = token;
}
@Override
public void execute(Connection connection, UntypedActorContext untypedActorContext) throws SQLException {
connection.setAutoCommit(false);
java.sql.PreparedStatement preparedStatement = connection.prepareStatement(SAVE_TOKEN_STR);
preparedStatement.setString(1, token.getUuid());
preparedStatement.setTimestamp(2, new java.sql.Timestamp(token.getCreationTime().getMillis()));
preparedStatement.setString(3, token.getEmail());
preparedStatement.setTimestamp(4, new java.sql.Timestamp(token.getCreationTime().plusMinutes(5).getMillis()));
preparedStatement.setBoolean(5, token.getIsSignUp());
preparedStatement.execute();
connection.commit();
untypedActorContext.sender().tell(token, ActorRef.noSender());
}
}
private static class findTokenExecutor implements SQLExecutable {
private final String uuid;
public findTokenExecutor(String uuid) {
this.uuid = uuid;
}
@Override
public void execute(Connection connection, UntypedActorContext untypedActorContext) throws SQLException {
connection.setAutoCommit(false);
java.sql.PreparedStatement preparedStatement = connection.prepareStatement(FIND_TOKEN_STR);
preparedStatement.setString(1, uuid);
preparedStatement.execute();
ResultSet resultSet = preparedStatement.getResultSet();
resultSet.next();
Token token = new Token();
token.setEmail(resultSet.getString("email"));
token.setIsSignUp(resultSet.getBoolean("isSignUp"));
token.setCreationTime(new DateTime(resultSet.getTimestamp("creation")));
token.setExpirationTime(new DateTime(resultSet.getTimestamp("expiration")));
token.setUuid(resultSet.getString("uuid"));
connection.commit();
untypedActorContext.sender().tell(token, ActorRef.noSender());
}
}
private static class deleteTokenExecutor implements SQLExecutable {
private final String uuid;
public deleteTokenExecutor(String uuid) {
this.uuid = uuid;
}
@Override
public void execute(Connection connection, UntypedActorContext untypedActorContext) throws SQLException {
java.sql.PreparedStatement preparedStatement = connection.prepareStatement(DELETE_TOKEN_STR);
preparedStatement.setString(1, uuid);
}
}
public static Future<Token> saveToken(Token token) {
Future<Object> tokenFuture = ask(DB_ROUTER_SELECTION, new saveTokenExecutor(token), 1000);
return tokenFuture.flatMap(new Mapper<Object, Future<Token>>() {
@Override
public Future<Token> apply(Object parameter) {
return Futures.successful((Token) parameter);
}
}, AkkaActorSystem.getInstance().dispatcher());
}
public static Future<Token> findToken(String uuid) {
Future<Object> tokenFuture = ask(DB_ROUTER_SELECTION, new findTokenExecutor(uuid), EXECUTE_TIME_OUT);
return tokenFuture.flatMap(new Mapper<Object, Future<Token>>() {
@Override
public Future<Token> apply(Object parameter) {
return Futures.successful((Token) parameter);
}
}, AkkaActorSystem.getInstance().dispatcher());
}
/**
* delete token
*
* @param uuid
* @return future<int>
*/
public static Future<Object> deleteToken(String uuid) {
return ask(DB_ROUTER_SELECTION, new deleteTokenExecutor(uuid), EXECUTE_TIME_OUT);
}
}
}这只是我的观点,但如果我是你,我会避免这样做。只是闻起来很怪。如果您确实需要这种基于命令模式的行为,那么您可以在请求消息和命令类之间创建一种关系,以便在收到这些请求时执行。这种解耦感觉更干净,但这完全是我的观点。我对如何在akka中处理数据库阻塞操作感到困惑,并且我没有找到很多关于这方面的好实践。所以我收集了一些想法并实施了一个解决方案@cmbaxter
public class DBWorker extends UntypedActor {
private final String host, user, password, db;
private final int port ;
private java.sql.Connection conn;
private static class DBWorkerCreator implements Creator<DBWorker> {
private final String host, user, password, db;
private final int port;
public DBWorkerCreator(String host, int port, String user, String password, String db){
this.host = host;
this.user = user;
this.port = port;
this.password = password;
this.db = db;
}
@Override
public DBWorker create() throws Exception {
return new DBWorker(host, port, user, password, db);
}
}
public static Props getProps(String host, int port, String user, String password,String db){
return Props.create(new DBWorkerCreator(host, port, user, password, db));
}
public DBWorker(String host, int port, String user, String password, String db){
this.host = host;
this.port = port;
this.user = user;
this.password = password;
this.db = db;
}
/**
* initialization: database connection
*/
@Override
public void preStart() throws Exception {
try {
String url = "jdbc:mysql://"+ host + ":" + port + "/" + db;
System.out.println(url);
conn = DriverManager.getConnection(url,user,password );
} catch (SQLException e) {
//something happens when connecting to the database, throw exception to DB Router
e.printStackTrace();
throw e;
}
}
//cancel post start
@Override
public void postRestart(Throwable reason){
}
@Override
public void onReceive(Object message) throws Exception {
try {
if(message instanceof SQLExecutable){
((SQLExecutable) message).execute(conn,getContext());
} else if (message instanceof String){
conn.createStatement().execute((String)message);
}
else {
// unknown message, do nothing here
}
} catch (SQLException e) {
e.printStackTrace();
try {
//if SQLException is throwing up, rollback
conn.rollback();
} catch (SQLException e1) {
// something serious happens, throw the exception to DB router
e1.printStackTrace();
throw e1;
}
} finally {
}
}