如何在AWS EC2上无需外部调用永久运行Firebase监听Jar文件?
Hey there! Let's tackle your two main questions: optimizing your Firebase listener code, and getting your JAR to run non-stop on AWS EC2. I'll break this down clearly for you as a Java beginner.
Part 1: Optimizing Your Firebase Listener Code
First, let's fix some issues in your current code—right now it has a couple of problems that could cause unexpected behavior or resource waste:
Key Issues in Your Current Code
- Repeated Listener Registration: Your
while(true)loop keeps adding newValueEventListenerinstances every time it runs. This means every data change will trigger all registered listeners (not just one), leading to duplicate database updates and wasted memory. - Useless Loop + Latch Block: The
latch.await()call blocks the main thread forever on the first run, so the loop never executes a second time anyway. You don't need the loop at all because Firebase listeners stay active as long as your app is running.
Optimized Code Example
import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.ValueEventListener; import com.google.firebase.database.DatabaseError; import java.util.concurrent.CountDownLatch; public class FirebaseSyncService { // Mark as volatile to ensure thread-safe access across listener and main thread private static volatile DataSnapshot nextGames; public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(1); // Initialize your Firebase reference once DatabaseReference nextGamesRef = FirebaseDatabase.getInstance().getReference("your-next-games-path"); // Register the listener ONCE—Firebase will keep listening for changes indefinitely nextGamesRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { nextGames = snapshot; // Ensure setNextGame handles database updates safely (use thread-safe logic if needed) setNextGame(nextGames); System.out.println("Successfully synced latest game data"); } @Override public void onCancelled(DatabaseError error) { // Use System.err for error messages to distinguish them from regular logs System.err.println("Firebase listener cancelled with error: " + error.getMessage()); System.err.println("Error details: " + error.getDetails()); // Uncomment below if you want to shut down the service on critical errors // latch.countDown(); } }); try { // Block the main thread to keep the service running latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("Service was interrupted: " + e.getMessage()); } } private static void setNextGame(DataSnapshot nextGames) { // Your database sync logic here // Tip: If writing to a SQL database, use connection pools and ensure thread safety } }
What Changed & Why
- Single Listener Registration: We moved the listener outside the loop so it only registers once. Firebase maintains this listener as long as your app is alive.
- Cleaner Latch Usage: The latch only exists to keep the main thread from exiting (since Java apps shut down when the main thread finishes). We added proper interrupt handling for graceful service stops.
- Better Error Logging: Using
System.errfor errors makes them easier to spot in logs, and we included more details for debugging.
Part 2: Running Your JAR Permanently on AWS EC2
You're right—Tomcat is for web apps that respond to HTTP requests, so it's not a good fit for your background service. Instead, use systemd (a built-in Linux service manager) to make your JAR run automatically on boot, restart if it crashes, and stay running indefinitely.
Step-by-Step Setup
Move Your JAR to a Stable Directory
SSH into your EC2 instance and copy your JAR to a dedicated folder (e.g.,/opt/firebase-sync/):sudo mkdir -p /opt/firebase-sync sudo cp /path/to/your/firebase-sync-service.jar /opt/firebase-sync/ sudo chown ec2-user:ec2-user /opt/firebase-sync/* # Replace ec2-user with your instance's username (e.g., ubuntu for Ubuntu instances)Create a Systemd Service File
Create a file at/etc/systemd/system/firebase-sync.servicewith the following content (usesudo nano /etc/systemd/system/firebase-sync.serviceto edit):[Unit] Description=Firebase to Database Sync Service After=network.target # Wait for network to be ready before starting [Service] User=ec2-user # Match your instance's username WorkingDirectory=/opt/firebase-sync ExecStart=/usr/bin/java -jar firebase-sync-service.jar Restart=always # Automatically restart if the service crashes RestartSec=5 # Wait 5 seconds before restarting StandardOutput=journal+console # Send logs to systemd's journal StandardError=journal+console # Send errors to the same journal [Install] WantedBy=multi-user.target # Start when the system reaches multi-user modeEnable & Start the Service
Run these commands to activate the service:sudo systemctl daemon-reload # Reload systemd to recognize the new service sudo systemctl start firebase-sync # Start the service now sudo systemctl enable firebase-sync # Set it to start on bootCheck Service Status & Logs
- To see if the service is running:
sudo systemctl status firebase-sync - To view real-time logs:
journalctl -u firebase-sync -f(pressCtrl+Cto exit) - To view all logs for the service:
journalctl -u firebase-sync
- To see if the service is running:
Alternative: Temporary Run with nohup
If you need a quick temporary fix (not recommended for production), use nohup to run the JAR in the background even after you close your SSH session:
nohup java -jar firebase-sync-service.jar > service.log 2>&1 &
This will write logs to service.log, but the service won't restart on crash or server reboot—so stick with systemd for production use.
内容的提问来源于stack exchange,提问作者waleed

