我正在尝试使用Firebase实时数据库为分布式系统实施pub / sub。我想要像Amazon SQS这样的东西,但我不知道我怎么能保证只有一个用户会处理这个消息。
交易会完成这项工作吗?有没有办法锁定一行或类似的东西?
更新
以下是我目前的订户代码,这样安全吗?
public class TaskToRun {
private String id;
private String data;
private boolean running;
//getters and setters
}
public class TaskSubscriber implements ChildEventListener{
private Query reference;
private LinkedList<TaskToRun> queue = new LinkedList<>();
private Object lockQueue = new Object();
private final Semaphore available = new Semaphore(1, true);
private boolean running;
public TaskSubscriber(){
reference = FirebaseDatabase.getInstance().getReference().child("tasks").orderByChild("running").equalTo(false);
}
public void start(){
running = true;
queue.clear();
reference.addChildEventListener(this);
AsyncTask t = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
while (running){
try {
available.acquire();
if(!running)
break;
TaskToRun task = null;
synchronized(lockQueue){
if(queue.size() > 0){
task = queue.pop();
}
}
if(task != null){
//Try to update the object to running
Transaction.Handler handler = new Transaction.Handler() {
private boolean canRun = false;
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
canRun = false;
TaskToRun t = mutableData.getValue(TaskToRun.class);
if(t == null)
return Transaction.success(mutableData);
if(t.isRunning())
return Transaction.success(mutableData);
t.setRunning(true);
mutableData.setValue(t);
canRun = true;
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
if(canRun && databaseError == null) {
runTask(dataSnapshot.getValue(TaskToRun.class));
}
}
};
FirebaseDatabase.getInstance().getReference().child("tasks").child(task.getId()).runTransaction(handler);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
};
t.execute();
}
private void runTask(TaskToRun task) {
Log.e("TASKS","Running task "+task.getId());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void stop(){
running = false;
reference.removeEventListener(this);
synchronized (lockQueue) {
queue.clear();
}
//Release all threads
int usedPermits = 1 - available.availablePermits();
available.release(usedPermits);
}
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
synchronized (lockQueue){
TaskToRun task = dataSnapshot.getValue(TaskToRun.class);
task.setId(dataSnapshot.getKey());
if(!task.isRunning()){
queue.addLast(task);
available.release();
}
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
synchronized (lockQueue){
TaskToRun task = dataSnapshot.getValue(TaskToRun.class);
task.setId(dataSnapshot.getKey());
if(task.isRunning()){
for(int i = 0; i < queue.size();i++){
TaskToRun t = queue.get(i);
if(t.getId().equals(task.getId())){
queue.remove(i);
break;
}
}
}
}
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
synchronized (lockQueue){
TaskToRun task = dataSnapshot.getValue(TaskToRun.class);
task.setId(dataSnapshot.getKey());
if(task.isRunning()){
for(int i = 0; i < queue.size();i++){
TaskToRun t = queue.get(i);
if(t.getId().equals(task.getId())){
queue.remove(i);
break;
}
}
}
}
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
}
主要代码
new TaskSubscriber().start();
要发布的代码
TaskToRun task = new TaskToRun();
task.setData("send_sms");
FirebaseDatabase.getInstance().getReference().child("tasks").push().setValue(task);