> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# LaserStream WebSocket: Truyền dữ liệu Solana theo thời gian thực

> Truyền dữ liệu Solana theo thời gian thực qua LaserStream WebSocket — hỗ trợ các đăng ký Solana tiêu chuẩn cùng những tiện ích mở rộng của Helius như transactionSubscribe, nhanh hơn tới 200 ms.

## LaserStream WebSocket là gì?

LaserStream WebSocket là biến thể sử dụng giao thức WebSocket của [LaserStream](/docs/vi/laserstream). Dịch vụ này cung cấp các phương thức đăng ký Solana JSON-RPC tiêu chuẩn (`accountSubscribe`, `programSubscribe`, `logsSubscribe`, `signatureSubscribe`, `slotSubscribe`, …) cùng các tiện ích mở rộng dành riêng cho Helius như [`transactionSubscribe`](/docs/vi/rpc/websocket/transaction-subscribe) để lọc nâng cao — tất cả đều trên cùng một điểm cuối `wss://mainnet.helius-rpc.com` hợp nhất và cùng phần phụ trợ LaserStream với [sản phẩm gRPC](/docs/vi/laserstream).

WebSocket cung cấp kết nối liên tục theo thời gian thực: thay vì liên tục thăm dò "có gì thay đổi không?", blockchain sẽ thông báo cho bạn ngay khi có sự kiện xảy ra.

<CardGroup cols={2}>
  <Card title="Real-Time Updates" icon="bolt">
    Nhận thông báo ngay lập tức khi tài khoản Solana thay đổi, giao dịch diễn ra hoặc khối mới được tạo
  </Card>

  <Card title="Efficient Resource Usage" icon="leaf">
    Sử dụng một kết nối liên tục thay vì thăm dò bằng hàng trăm yêu cầu HTTP
  </Card>

  <Card title="Low Latency" icon="clock">
    Nhanh hơn WebSocket tiêu chuẩn dựa trên Agave RPC tới 200 ms, do LaserStream cung cấp
  </Card>

  <Card title="Helius Extensions" icon="filter">
    `transactionSubscribe` và phiên bản `accountSubscribe` nâng cao để lọc chuyên sâu trên các phương thức Solana tiêu chuẩn
  </Card>
</CardGroup>

## Điểm cuối

WebSocket — bao gồm cả các phương thức Solana tiêu chuẩn và tiện ích mở rộng dành riêng cho Helius — được cung cấp qua một điểm cuối hợp nhất duy nhất cho mỗi mạng:

**Mainnet:** `wss://mainnet.helius-rpc.com/?api-key=<API_KEY>`
**Devnet:** `wss://devnet.helius-rpc.com/?api-key=<API_KEY>`

### Gatekeeper (Beta)

Để thử nghiệm dịch vụ WebSocket nhanh nhất của chúng tôi, hãy dùng điểm cuối Gatekeeper (Beta) mới:

`wss://beta.helius-rpc.com/?api-key=<API_KEY>`

## Định giá và đo lường mức sử dụng

Mức sử dụng WebSocket được tính là **2 tín dụng cho mỗi 0,1 MB** dữ liệu truyền chưa nén. Mở một kết nối tốn **1 tín dụng**.

Đối với khối lượng công việc lớn trên các gói Professional, [**Tiện ích bổ sung dữ liệu**](/docs/vi/billing/plans#tiện-ích-bổ-sung-dữ-liệu) cung cấp hạn mức dữ liệu hằng tháng (5TB–100TB) với chi phí có thể dự đoán. Có thể áp dụng tiện ích bổ sung dữ liệu cho LaserStream và WebSocket.

<Note>
  Các phương thức dành riêng cho Helius `transactionSubscribe` và `accountSubscribe` (với bộ lọc Helius) yêu cầu [gói](/docs/vi/billing/plans) **Developer**, **Business** hoặc **Professional**. Các phương thức Solana tiêu chuẩn như `programSubscribe`, `logsSubscribe` và `signatureSubscribe` có sẵn trên mọi gói.
</Note>

## Khái niệm cốt lõi

### Các loại đăng ký

<AccordionGroup>
  <Accordion title="Account Subscriptions" icon="user">
    Theo dõi thay đổi đối với các tài khoản cụ thể như số dư ví, tài khoản token hoặc dữ liệu chương trình.

    **Trường hợp sử dụng:**

    * Theo dõi số dư ví
    * Giám sát tài khoản token
    * Theo dõi thay đổi trạng thái hợp đồng thông minh
    * Cập nhật quyền sở hữu NFT
  </Accordion>

  <Accordion title="Program Subscriptions" icon="code">
    Theo dõi mọi tài khoản thuộc sở hữu của một chương trình cụ thể để phát hiện mọi thay đổi.

    **Trường hợp sử dụng:**

    * Giám sát giao dịch DEX
    * Theo dõi giao thức DeFi
    * Theo dõi hoạt động trên thị trường NFT
    * Theo dõi thay đổi tài sản trong trò chơi
  </Accordion>

  <Accordion title="Transaction Subscriptions" icon="receipt">
    Nhận thông báo khi các giao dịch cụ thể được xác nhận hoặc khi giao dịch đề cập đến một số tài khoản nhất định.

    **Trường hợp sử dụng:**

    * Xác nhận thanh toán
    * Theo dõi trạng thái giao dịch
    * Phê duyệt đa chữ ký
    * Giám sát việc thực thi hợp đồng
  </Accordion>

  <Accordion title="Slot Subscriptions" icon="clock">
    Theo dõi tiến trình và tính hoàn tất của blockchain ở cấp độ slot.

    **Trường hợp sử dụng:**

    * Ứng dụng trình khám phá khối
    * Giám sát tình trạng mạng
    * Theo dõi cơ chế đồng thuận
    * Phân tích hiệu năng
  </Accordion>
</AccordionGroup>

### Các mức cam kết

Hiểu rõ các mức cam kết là yếu tố thiết yếu để xây dựng ứng dụng đáng tin cậy:

<Tabs>
  <Tab title="Processed">
    **Nhanh nhất** - Giao dịch đã được trình xác thực xử lý nhưng chưa được xác nhận

    * Độ trễ: \~400ms
    * Rủi ro: Có thể bị loại bỏ hoặc sắp xếp lại
    * Dùng cho: Cập nhật giao diện người dùng theo thời gian thực (cần thận trọng)
  </Tab>

  <Tab title="Confirmed">
    **Cân bằng** - Đa số cụm đã bỏ phiếu cho slot

    * Độ trễ: \~2-3 giây
    * Rủi ro: Khả năng bị loại bỏ thấp
    * Dùng cho: Hầu hết ứng dụng
  </Tab>

  <Tab title="Finalized">
    **An toàn nhất** - Đại đa số cụm đã bỏ phiếu cho slot

    * Độ trễ: \~15-30 giây
    * Rủi ro: Gần như không thể bị loại bỏ
    * Dùng cho: Ứng dụng tài chính, giao dịch giá trị cao
  </Tab>
</Tabs>

<Info>
  **Bạn muốn tìm hiểu thêm về các mức cam kết?** Hãy đọc bài viết toàn diện trên blog của chúng tôi: [Tìm hiểu các mức cam kết của Solana](https://www.helius.dev/blog/solana-commitment-levels)
</Info>

## Các phương thức WebSocket hiện có

Helius hỗ trợ đầy đủ các phương thức đăng ký Solana WebSocket cùng các tiện ích mở rộng dành riêng cho Helius. Tất cả phương thức dùng chung điểm cuối `wss://mainnet.helius-rpc.com`.

### Tiện ích mở rộng của Helius

Hai phương thức cung cấp khả năng lọc phong phú hơn tiêu chuẩn Solana:

* [`transactionSubscribe`](/docs/vi/rpc/websocket/transaction-subscribe) — đăng ký các giao dịch khớp với danh sách tài khoản cần bao gồm/loại trừ/bắt buộc, cờ biểu quyết/thất bại và một chữ ký cụ thể
* [`accountSubscribe`](/docs/vi/rpc/websocket/account-subscribe) — Helius bổ sung các bộ lọc nâng cao trên cấu trúc `accountSubscribe` tiêu chuẩn

### Các phương thức Solana tiêu chuẩn

`accountSubscribe`, `blockSubscribe`, `logsSubscribe`, `programSubscribe`, `rootSubscribe`, `signatureSubscribe`, `slotSubscribe`, `slotsUpdatesSubscribe`, `voteSubscribe` và các cặp `*Unsubscribe` tương ứng.

<Card title="Complete API Reference" icon="book" href="/docs/vi/api-reference/rpc/websocket-methods">
  **Khám phá toàn bộ hơn 18 phương thức WebSocket**. Mỗi phương thức đều có tham số chi tiết, định dạng phản hồi và ví dụ.
</Card>

## Cách triển khai logic kết nối lại

Kết nối WebSocket có thể bị ngắt vì nhiều lý do — sự cố mạng, bảo trì máy chủ hoặc gián đoạn tạm thời. Ứng dụng trong môi trường production **phải** triển khai logic kết nối lại để bảo đảm độ tin cậy.

### Lý do kết nối bị ngắt

<AccordionGroup>
  <Accordion title="Network Issues">
    * Sự cố kết nối Internet
    * Chuyển đổi WiFi trên thiết bị di động
    * Tường lửa doanh nghiệp hết thời gian chờ
    * Thay đổi định tuyến của ISP
  </Accordion>

  <Accordion title="Server-Side Events">
    * Khoảng thời gian bảo trì theo kế hoạch
    * Khởi động lại bộ cân bằng tải
    * Cập nhật nút RPC
    * Cơ chế bảo vệ tạm thời khi quá tải
  </Accordion>

  <Accordion title="Client-Side Issues">
    * Tab trình duyệt chuyển sang chạy nền
    * Ứng dụng di động bị tạm dừng
    * Máy tính chuyển sang chế độ ngủ/ngủ đông
    * Tiến trình gặp sự cố hoặc khởi động lại
  </Accordion>
</AccordionGroup>

### Phát hiện kết nối bị ngắt

<Tabs>
  <Tab title="JavaScript/Browser">
    ```javascript [expandable] theme={"system"}
    class ConnectionMonitor {
      constructor(ws) {
        this.ws = ws;
        this.pingInterval = null;
        this.lastPong = Date.now();
        this.isConnected = false;
        
        this.setupEventListeners();
        this.startPingMonitoring();
      }
      
      setupEventListeners() {
        this.ws.onopen = () => {
          console.log('Connected');
          this.isConnected = true;
          this.lastPong = Date.now();
        };
        
        this.ws.onclose = (event) => {
          console.log('Disconnected:', event.code, event.reason);
          this.isConnected = false;
          this.stopPingMonitoring();
        };
        
        this.ws.onerror = (error) => {
          console.error('WebSocket error:', error);
          this.isConnected = false;
        };
        
        // Listen for pong responses (server acknowledgment)
        this.ws.onmessage = (event) => {
          const data = JSON.parse(event.data);
          if (data.method === 'pong') {
            this.lastPong = Date.now();
          }
          // Handle other messages...
        };
      }
      
      startPingMonitoring() {
        this.pingInterval = setInterval(() => {
          if (this.isConnected) {
            // Send ping to check connection health
            this.ws.send(JSON.stringify({
              jsonrpc: '2.0',
              method: 'ping',
              id: Date.now()
            }));
            
            // Check if we received a pong recently
            const timeSinceLastPong = Date.now() - this.lastPong;
            if (timeSinceLastPong > 30000) { // 30 seconds timeout
              console.warn('No pong received, connection may be stale');
              this.ws.close();
            }
          }
        }, 10000); // Ping every 10 seconds
      }
      
      stopPingMonitoring() {
        if (this.pingInterval) {
          clearInterval(this.pingInterval);
          this.pingInterval = null;
        }
      }
    }
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript [expandable] theme={"system"}
    const WebSocket = require('ws');

    class NodeConnectionMonitor {
      constructor(url) {
        this.url = url;
        this.ws = null;
        this.pingInterval = null;
        this.isAlive = false;
      }
      
      connect() {
        this.ws = new WebSocket(this.url);
        
        this.ws.on('open', () => {
          console.log('Connected');
          this.isAlive = true;
          this.startHeartbeat();
        });
        
        this.ws.on('close', () => {
          console.log('Disconnected');
          this.isAlive = false;
          this.stopHeartbeat();
        });
        
        this.ws.on('error', (error) => {
          console.error('WebSocket error:', error);
          this.isAlive = false;
        });
        
        // Built-in ping/pong handling
        this.ws.on('pong', () => {
          this.isAlive = true;
        });
      }
      
      startHeartbeat() {
        this.pingInterval = setInterval(() => {
          if (!this.isAlive) {
            console.log('Connection lost, terminating');
            return this.ws.terminate();
          }
          
          this.isAlive = false;
          this.ws.ping();
        }, 30000); // Ping every 30 seconds
      }
      
      stopHeartbeat() {
        if (this.pingInterval) {
          clearInterval(this.pingInterval);
          this.pingInterval = null;
        }
      }
    }
    ```
  </Tab>
</Tabs>

### Chiến lược kết nối lại

<Tabs>
  <Tab title="Exponential Backoff">
    ```javascript [expandable] theme={"system"}
    class ExponentialBackoffReconnector {
      constructor(url, maxRetries = 10) {
        this.url = url;
        this.maxRetries = maxRetries;
        this.retryCount = 0;
        this.baseDelay = 1000; // Start with 1 second
        this.maxDelay = 30000; // Cap at 30 seconds
        this.ws = null;
        this.subscriptions = new Map();
        this.isReconnecting = false;
      }
      
      connect() {
        if (this.isReconnecting) return;
        
        try {
          this.ws = new WebSocket(this.url);
          this.setupEventHandlers();
        } catch (error) {
          console.error('Failed to create WebSocket:', error);
          this.scheduleReconnect();
        }
      }
      
      setupEventHandlers() {
        this.ws.onopen = () => {
          console.log('Connected successfully');
          this.retryCount = 0; // Reset retry count on successful connection
          this.isReconnecting = false;
          this.resubscribeAll(); // Restore subscriptions
        };
        
        this.ws.onclose = (event) => {
          console.log('Connection closed:', event.code);
          if (!this.isReconnecting) {
            this.scheduleReconnect();
          }
        };
        
        this.ws.onerror = (error) => {
          console.error('WebSocket error:', error);
        };
      }
      
      scheduleReconnect() {
        if (this.retryCount >= this.maxRetries) {
          console.error('Max retry attempts reached. Giving up.');
          return;
        }
        
        this.isReconnecting = true;
        this.retryCount++;
        
        // Calculate delay with exponential backoff + jitter
        const delay = Math.min(
          this.baseDelay * Math.pow(2, this.retryCount - 1),
          this.maxDelay
        );
        
        // Add jitter to prevent thundering herd
        const jitteredDelay = delay + (Math.random() * 1000);
        
        console.log(`Reconnecting in ${jitteredDelay}ms (attempt ${this.retryCount}/${this.maxRetries})`);
        
        setTimeout(() => {
          this.connect();
        }, jitteredDelay);
      }
      
      subscribe(method, params, callback) {
        const id = this.generateId();
        this.subscriptions.set(id, { method, params, callback });
        
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
          this.sendSubscription(id, method, params);
        }
        
        return id;
      }
      
      resubscribeAll() {
        console.log(`Restoring ${this.subscriptions.size} subscriptions`);
        for (const [id, sub] of this.subscriptions) {
          this.sendSubscription(id, sub.method, sub.params);
        }
      }
      
      sendSubscription(id, method, params) {
        this.ws.send(JSON.stringify({
          jsonrpc: '2.0',
          id: id,
          method: method,
          params: params
        }));
      }
      
      generateId() {
        return Date.now() + Math.random();
      }
    }
    ```
  </Tab>

  <Tab title="Circuit Breaker Pattern">
    ```javascript [expandable] theme={"system"}
    class CircuitBreakerWebSocket {
      constructor(url, options = {}) {
        this.url = url;
        this.failureThreshold = options.failureThreshold || 5;
        this.recoveryTimeout = options.recoveryTimeout || 60000;
        this.checkInterval = options.checkInterval || 10000;
        
        this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
        this.failureCount = 0;
        this.lastFailureTime = null;
        this.ws = null;
        this.subscriptions = new Map();
      }
      
      connect() {
        if (this.state === 'OPEN') {
          if (Date.now() - this.lastFailureTime > this.recoveryTimeout) {
            console.log('Circuit breaker moving to HALF_OPEN state');
            this.state = 'HALF_OPEN';
          } else {
            console.log('Circuit breaker is OPEN, connection blocked');
            return false;
          }
        }
        
        try {
          this.ws = new WebSocket(this.url);
          this.setupEventHandlers();
          return true;
        } catch (error) {
          this.recordFailure();
          return false;
        }
      }
      
      setupEventHandlers() {
        this.ws.onopen = () => {
          console.log('Connected - Circuit breaker CLOSED');
          this.state = 'CLOSED';
          this.failureCount = 0;
          this.resubscribeAll();
        };
        
        this.ws.onclose = () => {
          this.recordFailure();
        };
        
        this.ws.onerror = (error) => {
          console.error('WebSocket error:', error);
          this.recordFailure();
        };
      }
      
      recordFailure() {
        this.failureCount++;
        this.lastFailureTime = Date.now();
        
        console.log(`Failure recorded: ${this.failureCount}/${this.failureThreshold}`);
        
        if (this.failureCount >= this.failureThreshold) {
          console.log('Circuit breaker opened due to repeated failures');
          this.state = 'OPEN';
        }
      }
      
      startHealthCheck() {
        setInterval(() => {
          if (this.state === 'OPEN' || 
              (this.ws && this.ws.readyState !== WebSocket.OPEN)) {
            this.connect();
          }
        }, this.checkInterval);
      }
    }
    ```
  </Tab>
</Tabs>

### Kiểm thử logic kết nối lại

<Tabs>
  <Tab title="Network Simulation">
    ```javascript [expandable] theme={"system"}
    // Test disconnection scenarios
    class NetworkSimulator {
      constructor(wsManager) {
        this.wsManager = wsManager;
      }
      
      // Simulate network outage
      simulateNetworkOutage(duration = 5000) {
        console.log('Simulating network outage...');
        
        // Force close the connection
        if (this.wsManager.ws) {
          this.wsManager.ws.close(1006, 'Network outage simulation');
        }
        
        // Block reconnection temporarily
        const originalConnect = this.wsManager.connect.bind(this.wsManager);
        this.wsManager.connect = () => {
          console.log('Connection blocked during outage simulation');
        };
        
        // Restore connection after duration
        setTimeout(() => {
          console.log('Network restored');
          this.wsManager.connect = originalConnect;
          this.wsManager.connect();
        }, duration);
      }
      
      // Simulate intermittent connectivity
      simulateIntermittentConnectivity() {
        setInterval(() => {
          if (Math.random() < 0.1) { // 10% chance every 10 seconds
            console.log('Simulating connection drop...');
            this.wsManager.ws?.close(1006, 'Intermittent connectivity');
          }
        }, 10000);
      }
    }

    // Usage
    const simulator = new NetworkSimulator(wsManager);
    simulator.simulateNetworkOutage(10000); // 10 second outage
    ```
  </Tab>

  <Tab title="Automated Testing">
    ```javascript [expandable] theme={"system"}
    // Jest test example
    describe('WebSocket Reconnection', () => {
      let wsManager;
      
      beforeEach(() => {
        wsManager = new ProductionWebSocketManager({
          endpoint: 'ws://localhost:8080',
          apiKey: 'test-key'
        });
      });
      
      test('should reconnect after connection loss', async () => {
        const reconnectPromise = new Promise((resolve) => {
          wsManager.on('connected', resolve);
        });
        
        await wsManager.connect();
        
        // Simulate connection loss
        wsManager.ws.close(1006, 'Test disconnection');
        
        // Wait for reconnection
        await reconnectPromise;
        
        expect(wsManager.connectionState).toBe('CONNECTED');
        expect(wsManager.metrics.reconnections).toBeGreaterThan(0);
      });
      
      test('should restore subscriptions after reconnection', async () => {
        await wsManager.connect();
        
        const messages = [];
        const subscriptionId = wsManager.subscribe(
          'accountSubscribe',
          ['test-account', {}],
          (data) => messages.push(data)
        );
        
        // Verify subscription exists
        expect(wsManager.subscriptions.has(subscriptionId)).toBe(true);
        
        // Simulate disconnection and reconnection
        wsManager.ws.close(1006, 'Test disconnection');
        
        await new Promise(resolve => wsManager.on('connected', resolve));
        
        // Verify subscription was restored
        const subscription = wsManager.subscriptions.get(subscriptionId);
        expect(subscription.pending).toBe(true); // Should be re-subscribing
      });
    });
    ```
  </Tab>
</Tabs>

<Warning>
  **Yếu tố thiết yếu cho môi trường production**: Việc triển khai logic kết nối lại phù hợp là bắt buộc đối với các ứng dụng trong môi trường production. Kết nối WebSocket sẽ bị ngắt — hãy lập kế hoạch, kiểm thử và giám sát tình trạng này trong môi trường production.
</Warning>

## Khắc phục sự cố

<AccordionGroup>
  <Accordion title="Connection Failed">
    **Triệu chứng**: WebSocket không thể kết nối ngay từ đầu

    **Giải pháp**:

    * Xác minh khóa API của bạn chính xác và có đủ tín dụng
    * Kiểm tra định dạng URL của điểm cuối: `wss://mainnet.helius-rpc.com?api-key=YOUR_KEY`
    * Đảm bảo tường lửa cho phép kết nối WebSocket trên cổng 443
    * Trước tiên, hãy thử bằng một lệnh ping đơn giản để xác minh khả năng kết nối cơ bản
  </Accordion>

  <Accordion title="Frequent Disconnections">
    **Triệu chứng**: Kết nối bị ngắt sau mỗi vài phút

    **Giải pháp**:

    * WebSocket có **bộ hẹn giờ không hoạt động 10 phút**. Gửi ping ít nhất một lần mỗi phút để duy trì kết nối (xem các ví dụ kết nối lại ở trên và [Câu hỏi thường gặp về WebSocket](/docs/vi/faqs/websockets#mất-kết-nối-và-thử-lại))
    * Triển khai nhịp ping/pong phù hợp (được minh họa trong các ví dụ kết nối lại ở trên)
    * Kiểm tra độ ổn định của mạng và cài đặt tường lửa doanh nghiệp
    * Theo dõi số lượng đăng ký — quá nhiều đăng ký có thể gây mất ổn định
    * Xác minh rằng bạn đang xử lý đúng vòng đời kết nối
  </Accordion>

  <Accordion title="Missing Messages">
    **Triệu chứng**: Không nhận được các bản cập nhật đăng ký dự kiến

    **Giải pháp**:

    * Xác minh đăng ký đã được xác nhận (kiểm tra phản hồi)
    * Đảm bảo tài khoản/chương trình bạn đang theo dõi thực sự có hoạt động
    * Theo dõi trạng thái kết nối — bỏ lỡ thông báo thường là dấu hiệu kết nối bị ngắt
  </Accordion>

  <Accordion title="High Latency">
    **Triệu chứng**: Truyền thông báo chậm, cập nhật bị trễ

    **Giải pháp**:

    * Dùng mức cam kết "confirmed" thay cho "finalized"
    * Giảm số lượng đăng ký đang hoạt động
    * Tối ưu hóa logic xử lý thông báo
    * Cân nhắc sử dụng nhiều kết nối để phân phối tải
    * Kiểm tra chất lượng kết nối mạng
  </Accordion>

  <Accordion title="Memory Leaks">
    **Triệu chứng**: Mức sử dụng bộ nhớ của ứng dụng tăng dần theo thời gian

    **Giải pháp**:

    * Triển khai cơ chế dọn dẹp đăng ký phù hợp
    * Xóa trình lắng nghe sự kiện khi các thành phần bị ngắt kết nối
    * Xóa nhật ký thông báo định kỳ
    * Theo dõi số lượng đăng ký và áp dụng giới hạn
    * Sử dụng tham chiếu yếu cho các hàm callback khi có thể
  </Accordion>
</AccordionGroup>

## Di chuyển từ RPC tiêu chuẩn

Nếu hiện đang sử dụng cơ chế thăm dò HTTP, bạn có thể di chuyển sang WebSocket như sau:

```javascript [expandable] theme={"system"}
// Old approach - HTTP polling
class HTTPAccountMonitor {
  constructor(connection, accountAddress) {
    this.connection = connection;
    this.accountAddress = accountAddress;
    this.interval = null;
    this.lastKnownBalance = null;
  }

  start() {
    this.interval = setInterval(async () => {
      try {
        const accountInfo = await this.connection.getAccountInfo(
          new PublicKey(this.accountAddress)
        );
        
        const currentBalance = accountInfo?.lamports || 0;
        
        if (this.lastKnownBalance !== currentBalance) {
          console.log(`Balance changed: ${currentBalance}`);
          this.lastKnownBalance = currentBalance;
        }
      } catch (error) {
        console.error('Failed to fetch account info:', error);
      }
    }, 2000); // Poll every 2 seconds
  }

  stop() {
    if (this.interval) {
      clearInterval(this.interval);
      this.interval = null;
    }
  }
}

// New approach - WebSocket subscription
class WebSocketAccountMonitor {
  constructor(wsManager, accountAddress) {
    this.wsManager = wsManager;
    this.accountAddress = accountAddress;
    this.subscriptionId = null;
  }

  start() {
    this.subscriptionId = this.wsManager.subscribe(
      'accountSubscribe',
      [
        this.accountAddress,
        { encoding: 'jsonParsed', commitment: 'confirmed' }
      ],
      (data) => {
        const currentBalance = data.value.lamports;
        console.log(`Balance changed: ${currentBalance}`);
        
        // Handle the change immediately - no polling delay!
      }
    );
  }

  stop() {
    if (this.subscriptionId) {
      this.wsManager.unsubscribe(this.subscriptionId);
      this.subscriptionId = null;
    }
  }
}
```

## Khả năng nâng cao

Đối với ứng dụng cần các tính năng nâng cao hơn nữa, hãy cân nhắc sử dụng [LaserStream gRPC](/docs/vi/laserstream):

<CardGroup cols={2}>
  <Card title="24-Hour Historical Replay" icon="clock-rotate-left">
    Truy xuất dữ liệu bị bỏ lỡ khi ứng dụng không hoạt động
  </Card>

  <Card title="Multi-Node Aggregation" icon="network-wired">
    Nâng cao độ tin cậy nhờ dữ liệu từ nhiều trình xác thực
  </Card>

  <Card title="Higher Throughput" icon="gauge-high">
    Xử lý nhiều đăng ký hơn và tốc độ thông báo cao hơn
  </Card>

  <Card title="Enterprise Features" icon="building">
    Khả năng giám sát, phân tích và quy trình dữ liệu nâng cao
  </Card>
</CardGroup>

## Bắt đầu

Bạn đã sẵn sàng bắt đầu chưa?

Xem [Hướng dẫn bắt đầu nhanh với WebSocket](/docs/vi/rpc/websocket/quickstart) để tham khảo các ví dụ thực tế và quy trình triển khai từng bước.

## Hướng dẫn

Các bài hướng dẫn từng bước kèm mã có thể chạy nằm trên trang [Hướng dẫn LaserStream WebSocket](/docs/vi/rpc/websocket/guides).
