จัดการฟังก์ชัน


คุณทำให้ใช้งานได้ ลบ และแก้ไขฟังก์ชันโดยใช้คำสั่ง Firebase CLI หรือโดยการตั้งค่าตัวเลือกรันไทม์ในซอร์สโค้ดของฟังก์ชัน

ทำให้ฟังก์ชันใช้งานได้

หากต้องการทำให้ฟังก์ชันใช้งานได้ ให้เรียกใช้คำสั่ง Firebase CLI ดังนี้

firebase deploy --only functions

โดยค่าเริ่มต้น Firebase CLI จะทำให้ฟังก์ชันทั้งหมดภายในใช้งานได้ แหล่งที่มาของคุณไปพร้อมๆ กันด้วย หากโปรเจ็กต์มีฟังก์ชันมากกว่า 5 รายการ เราขอแนะนำให้ใช้ Flag --only ที่มีชื่อฟังก์ชันที่เฉพาะเจาะจง เพื่อติดตั้งใช้งานเฉพาะฟังก์ชัน ที่คุณได้แก้ไข การทำให้ฟังก์ชันบางอย่างใช้งานได้ วิธีนี้จะทำให้กระบวนการติดตั้งใช้งานเร็วขึ้น และช่วยให้คุณหลีกเลี่ยง โควต้าการทำให้ใช้งานได้ เช่น

firebase deploy --only functions:addMessage,functions:makeUppercase

เมื่อทำให้ฟังก์ชันใช้งานได้จำนวนมาก ฟังก์ชันของคุณอาจเกินขีดจำกัด มาตรฐานและรับข้อความแสดงข้อผิดพลาด HTTP 429 หรือ 500 วิธีแก้โจทย์ ทำให้ฟังก์ชันใช้งานได้เป็นกลุ่มไม่เกิน 10 รายการ

ดูข้อมูลอ้างอิง Firebase CLI สำหรับรายการที่มีอยู่ทั้งหมด คำสั่ง

โดยค่าเริ่มต้น Firebase CLI จะดูในโฟลเดอร์ functions/ สำหรับพารามิเตอร์ ซอร์สโค้ด คุณสามารถจัดระเบียบฟังก์ชันได้หากต้องการ ในฐานของโค้ดหรือไฟล์หลายๆ ชุด

ลบฟังก์ชัน

คุณลบฟังก์ชันที่ใช้งานก่อนหน้านี้ได้ด้วยวิธีต่อไปนี้

  • อย่างชัดแจ้งใน Firebase CLI ด้วย functions:delete
  • อย่างชัดเจนในคอนโซล Google Cloud
  • โดยปริยาย โดยนำฟังก์ชันออกจากซอร์สก่อนทำให้ใช้งานได้

การดำเนินการลบทั้งหมด ให้คุณยืนยันก่อนที่จะนำฟังก์ชันออกจากเวอร์ชันที่ใช้งานจริง

การลบฟังก์ชันที่ชัดแจ้งใน Firebase CLI รองรับอาร์กิวเมนต์หลายตัว ตลอดจนฟังก์ชัน กลุ่ม และช่วยให้คุณระบุฟังก์ชันที่จะทำงานในภูมิภาคหนึ่งๆ ได้ นอกจากนี้ คุณยังลบล้างข้อความแจ้งให้ยืนยันได้ด้วย

# Delete all functions that match the specified name in all regions.
firebase functions:delete myFunction
# Delete a specified function running in a specific region.
firebase functions:delete myFunction --region us-east-1
# Delete more than one function
firebase functions:delete myFunction myOtherFunction
# Delete a specified functions group.
firebase functions:delete groupA
# Bypass the confirmation prompt.
firebase functions:delete myFunction --force

เมื่อใช้การลบฟังก์ชันโดยนัย firebase deploy จะแยกวิเคราะห์แหล่งที่มาและ จะลบฟังก์ชันใดๆ ที่ถูกนำออกจากไฟล์ในเวอร์ชันที่ใช้งานจริง

แก้ไขชื่อ ภูมิภาค หรือทริกเกอร์ของฟังก์ชัน

หากคุณเปลี่ยนชื่อหรือเปลี่ยนภูมิภาคหรือทริกเกอร์สำหรับฟังก์ชันที่ จัดการการรับส่งข้อมูลเวอร์ชันที่ใช้งานจริง โปรดทำตามขั้นตอนในส่วนนี้เพื่อไม่ให้สูญเสียข้อมูล เหตุการณ์ระหว่างการแก้ไข ก่อนที่จะทำตามขั้นตอนเหล่านี้ โปรดตรวจสอบให้แน่ใจว่า คือ idempotent เนื่องจาก ทั้งเวอร์ชันใหม่และเวอร์ชันเก่าของฟังก์ชันจะทำงานที่ ในช่วงเวลาที่เปลี่ยน

เปลี่ยนชื่อฟังก์ชัน

หากต้องการเปลี่ยนชื่อฟังก์ชัน ให้สร้างฟังก์ชันที่มีการเปลี่ยนชื่อใหม่ในแหล่งที่มา จากนั้นเรียกใช้คำสั่งการติดตั้งใช้งาน 2 อย่างแยกกัน คำสั่งแรกจะทำให้ฟังก์ชัน และคำสั่งที่ 2 จะนำฟังก์ชันที่ทำให้ใช้งานได้ก่อนหน้านี้ออก เวอร์ชัน ตัวอย่างเช่น หากคุณมีฟังก์ชัน Node.js โทรหา webhook ที่คุณต้องการ เปลี่ยนเป็น webhookNew แก้ไขโค้ดดังนี้

// before
const functions = require('firebase-functions');

exports.webhook = functions.https.onRequest((req, res) => {
    res.send("Hello");
});

// after
const functions = require('firebase-functions');

exports.webhookNew = functions.https.onRequest((req, res) => {
    res.send("Hello");
});

จากนั้นเรียกใช้คำสั่งต่อไปนี้เพื่อทำให้ฟังก์ชันใหม่ใช้งานได้:

# Deploy new function called webhookNew
firebase deploy --only functions:webhookNew

# Wait until deployment is done; now both webhookNew and webhook are running

# Delete webhook
firebase functions:delete webhook

เปลี่ยนภูมิภาคหรือภูมิภาคของฟังก์ชัน

หากคุณเปลี่ยนภูมิภาคที่ระบุสำหรับ ที่จัดการกับการรับส่งข้อมูลที่ใช้งานจริง คุณจะป้องกันการสูญเสียเหตุการณ์ได้โดย โปรดปฏิบัติตามขั้นตอนเหล่านี้ตามลำดับ

  1. เปลี่ยนชื่อฟังก์ชัน แล้วเปลี่ยนภูมิภาคหรือภูมิภาคตามต้องการ
  2. ทำให้ฟังก์ชันที่เปลี่ยนชื่อใช้งานได้ ซึ่งจะส่งผลให้การเรียกใช้โค้ดเดียวกันชั่วคราวในภูมิภาคทั้ง 2 ชุด
  3. ลบฟังก์ชันก่อนหน้า

เช่น หากคุณมีฟังก์ชัน ที่ชื่อ webhook ซึ่งขณะนี้อยู่ใน ภูมิภาคของฟังก์ชันเริ่มต้นเป็น us-central1 และต้องการย้ายข้อมูลไปยัง asia-northeast1 คุณต้องแก้ไขซอร์สโค้ดก่อนเพื่อเปลี่ยนชื่อ อีกครั้ง และแก้ไขภูมิภาค

// before
const functions = require('firebase-functions');

exports.webhook = functions
    .https.onRequest((req, res) => {
            res.send("Hello");
    });

// after
const functions = require('firebase-functions');

exports.webhookAsia = functions
    .region('asia-northeast1')
    .https.onRequest((req, res) => {
            res.send("Hello");
    });

จากนั้นทำให้ใช้งานได้โดยการเรียกใช้:

firebase deploy --only functions:webhookAsia

ตอนนี้มีฟังก์ชันที่เหมือนกันทุกประการซึ่งทำงานอยู่ 2 รายการ นั่นคือ webhook กำลังทำงานใน us-central1 และ webhookAsia กำลังทำงานใน asia-northeast1

จากนั้นลบ webhook

firebase functions:delete webhook

ตอนนี้มีเพียงฟังก์ชันเดียว คือ webhookAsia ซึ่งกำลังทำงานใน asia-northeast1

เปลี่ยนประเภททริกเกอร์ของฟังก์ชัน

ขณะที่คุณพัฒนา Cloud Functions สำหรับการทำให้ Firebase ใช้งานได้เมื่อเวลาผ่านไป คุณอาจ จำเป็นต้องเปลี่ยนประเภททริกเกอร์ของฟังก์ชันด้วยเหตุผลหลายประการ ตัวอย่างเช่น คุณอาจต้องการเปลี่ยนจากฐานข้อมูลเรียลไทม์ของ Firebase ประเภทใดประเภทหนึ่ง หรือ เหตุการณ์ Cloud Firestore เป็นประเภทอื่น

คุณไม่สามารถเปลี่ยนประเภทเหตุการณ์ของฟังก์ชันโดยการเปลี่ยนแปลงฟังก์ชัน ซอร์สโค้ดและการเรียกใช้ firebase deploy เพื่อหลีกเลี่ยงข้อผิดพลาด เปลี่ยนประเภททริกเกอร์ของฟังก์ชันตามขั้นตอนนี้

  1. แก้ไขซอร์สโค้ดให้รวมฟังก์ชันใหม่ที่มีประเภททริกเกอร์ที่ต้องการ
  2. ทำให้ฟังก์ชันใช้งานได้ ซึ่งจะส่งผลให้มีการเรียกใช้ทั้งฟังก์ชันเก่าและใหม่ชั่วคราว
  3. ลบฟังก์ชันเก่าออกจากเวอร์ชันที่ใช้งานจริงอย่างชัดเจนโดยใช้ Firebase CLI

ตัวอย่างเช่น หากคุณมีฟังก์ชัน Node.js ที่ชื่อ objectChanged ซึ่งมีฟังก์ชันเดิม onChangeประเภทเหตุการณ์ และคุณต้องการเปลี่ยนเป็น onFinalize ให้เปลี่ยนชื่อก่อน ฟังก์ชันแล้วแก้ไขให้มีประเภทเหตุการณ์ onFinalize

// before
const functions = require('firebase-functions');

exports.objectChanged = functions.storage.object().onChange((object) => {
    return console.log('File name is: ', object.name);
});

// after
const functions = require('firebase-functions');

exports.objectFinalized = functions.storage.object().onFinalize((object) => {
    return console.log('File name is: ', object.name);
});

จากนั้นเรียกใช้คำสั่งต่อไปนี้เพื่อสร้างฟังก์ชันใหม่ก่อนที่จะลบฟังก์ชันเก่า

# Create new function objectFinalized
firebase deploy --only functions:objectFinalized

# Wait until deployment is done; now both objectChanged and objectFinalized are running

# Delete objectChanged
firebase functions:delete objectChanged

ตั้งค่าตัวเลือกรันไทม์

ฟังก์ชันระบบคลาวด์สำหรับ Firebase ช่วยให้คุณเลือกตัวเลือกรันไทม์ได้ เช่น Node.js เวอร์ชันรันไทม์และระยะหมดเวลาต่อฟังก์ชัน การจัดสรรหน่วยความจำ และค่าต่ำสุด/สูงสุด อินสแตนซ์ของฟังก์ชัน

แนวทางปฏิบัติแนะนำคือให้ตั้งค่าตัวเลือกเหล่านี้ (ยกเว้นเวอร์ชัน Node.js) ออบเจ็กต์การกำหนดค่าภายในโค้ดฟังก์ชัน ช่วงเวลานี้ RuntimeOptions เป็นแหล่งข้อมูลที่ถูกต้องสำหรับตัวเลือกรันไทม์ของฟังก์ชัน และจะ ลบล้างตัวเลือกที่ตั้งค่าไว้ด้วยวิธีอื่นๆ (เช่น ผ่าน Google Cloud Console) หรือ gcloud CLI)

หากเวิร์กโฟลว์การพัฒนามีการตั้งค่าตัวเลือกรันไทม์ด้วยตนเองผ่าน คอนโซล Google Cloud หรือ gcloud CLI และคุณไม่ต้องการให้ค่าเหล่านี้ ในการทำให้ใช้งานได้แต่ละครั้ง ให้ตั้งค่าตัวเลือก preserveExternalChanges เป็น true เมื่อใช้ตัวเลือกนี้เป็น true แล้ว Firebase จะรวมตัวเลือกรันไทม์ที่ตั้งค่าไว้ใน ที่มีการตั้งค่าฟังก์ชันเวอร์ชันที่ใช้งานได้ในปัจจุบันด้วย ลำดับความสำคัญต่อไปนี้

  1. มีการตั้งค่าตัวเลือกในโค้ดฟังก์ชัน: ลบล้างการเปลี่ยนแปลงภายนอก
  2. ตัวเลือกได้รับการตั้งค่าเป็น RESET_VALUE ในโค้ดฟังก์ชัน: ลบล้างการเปลี่ยนแปลงภายนอกด้วยค่าเริ่มต้น
  3. ไม่ได้ตั้งค่าตัวเลือกในโค้ดฟังก์ชัน แต่มีการตั้งค่าไว้ในฟังก์ชันที่ทำให้ใช้งานได้ในปัจจุบัน: ใช้ตัวเลือกที่ระบุไว้ในฟังก์ชันที่ทำให้ใช้งานได้

ไม่แนะนำให้ใช้ตัวเลือก preserveExternalChanges: true สำหรับสถานการณ์ส่วนใหญ่เนื่องจาก จะไม่เป็นแหล่งข้อมูลที่ถูกต้องสมบูรณ์สำหรับตัวเลือกรันไทม์สำหรับ หากคุณใช้ ให้ตรวจสอบคอนโซล Google Cloud หรือใช้ gcloud CLI เพื่อดูการกำหนดค่าแบบเต็มของฟังก์ชัน

ตั้งค่าเวอร์ชัน Node.js

Firebase SDK สำหรับ Cloud Functions อนุญาตให้เลือกรันไทม์ของ Node.js ได้ คุณเลือกเรียกใช้ฟังก์ชันทั้งหมดในโปรเจ็กต์บนรันไทม์เท่านั้นได้ ที่สอดคล้องกับหนึ่งในเวอร์ชัน Node.js ที่สนับสนุนเหล่านี้:

  • Node.js 20 (ตัวอย่าง)
  • Node.js 18
  • Node.js 16
  • Node.js 14

วิธีตั้งค่าเวอร์ชัน Node.js

คุณตั้งค่าเวอร์ชันได้ในช่อง engines ใน package.json ที่สร้างขึ้นในไดเรกทอรี functions/ ระหว่างการเริ่มต้น เช่น หากต้องการใช้เพียง เวอร์ชัน 18 ให้แก้ไขบรรทัดนี้ใน package.json:

  "engines": {"node": "18"}

หากคุณใช้ตัวจัดการแพ็กเกจ Yarn หรือมีข้อกำหนดเฉพาะอื่นๆ สำหรับ ในช่อง engines คุณสามารถตั้งค่ารันไทม์ของ Firebase SDK สำหรับ Cloud Functions ใน firebase.json แทน:

  {
    "functions": {
      "runtime": "nodejs18" // or nodejs14, nodejs16 or nodejs20
    }
  }

CLI ใช้ค่าที่ตั้งไว้ใน firebase.json เพื่อตั้งเป็นค่าใดก็ได้ หรือ ช่วงที่คุณตั้งค่าแยกกันใน package.json

อัปเกรดรันไทม์ของ Node.js

วิธีอัปเกรดรันไทม์ของ Node.js

  1. ตรวจสอบว่าโปรเจ็กต์อยู่ใน แพ็กเกจราคา Blaze
  2. ตรวจสอบว่าคุณใช้ Firebase CLI เวอร์ชัน 11.18.0 ขึ้นไป
  3. เปลี่ยนค่า engines ในไฟล์ package.json ที่สร้างใน ไดเรกทอรี functions/ ของคุณระหว่างการเริ่มต้น ตัวอย่างเช่น ถ้าคุณอัปเกรดจากเวอร์ชัน 16 เป็นเวอร์ชัน 18 รายการ ควรมีลักษณะเช่นนี้: "engines": {"node": "18"}
  4. (ไม่บังคับ) ทดสอบการเปลี่ยนแปลงโดยใช้ ชุดโปรแกรมจำลองภายในของ Firebase
  5. ทำให้ฟังก์ชันทั้งหมดใช้งานได้อีกครั้ง

ควบคุมลักษณะการทำงานของการปรับขนาด

โดยค่าเริ่มต้น Cloud Functions for Firebase จะปรับขนาดจำนวนอินสแตนซ์ที่ทำงานอยู่ ขึ้นอยู่กับจำนวนคำขอที่เข้ามา ซึ่งอาจลดขนาดลงเป็น 0 ในกรณีที่ปริมาณการเข้าชมลดลง อย่างไรก็ตาม หากแอปต้องมีการลดราคา เวลาในการตอบสนองและคุณต้องการจำกัดจำนวน Cold Start คุณสามารถเปลี่ยนได้ ตามค่าเริ่มต้น โดยระบุจำนวนอินสแตนซ์คอนเทนเนอร์ขั้นต่ำ ทำให้อบอุ่นและพร้อมที่จะทำตามคำขอ

ในทำนองเดียวกัน คุณสามารถตั้งค่าจำนวนสูงสุดเพื่อจำกัดการปรับขนาดของอินสแตนซ์ใน ตอบสนองต่อคำขอที่เข้ามาใหม่ ใช้การตั้งค่านี้เพื่อควบคุมค่าใช้จ่าย หรือเพื่อจำกัดจำนวนการเชื่อมต่อกับบริการสนับสนุน เช่น ฐานข้อมูล

ลดจำนวน Cold Start

หากต้องการตั้งค่าจำนวนอินสแตนซ์ขั้นต่ำสำหรับฟังก์ชันในซอร์สโค้ด ให้ใช้เมธอด runWith เมธอดนี้ยอมรับออบเจ็กต์ JSON ที่สอดคล้องกับ RuntimeOptions ซึ่งกำหนดค่าสำหรับ minInstances ตัวอย่างเช่น ฟังก์ชันนี้จะกำหนดอินสแตนซ์อย่างน้อย 5 รายการเพื่อทำให้อุณหภูมิไม่คงที่:

exports.getAutocompleteResponse = functions
    .runWith({
      // Keep 5 instances warm for this latency-critical function
      minInstances: 5,
    })
    .https.onCall((data, context) => {
      // Autocomplete a user's search term
    });

ต่อไปนี้เป็นสิ่งที่ควรพิจารณาเมื่อตั้งค่าสำหรับ minInstances

  • หาก Cloud Functions for Firebase ปรับขนาดแอปเหนือการตั้งค่า minInstances คุณจะพบ Cold Start สำหรับแต่ละอินสแตนซ์ที่สูงกว่าเกณฑ์ดังกล่าว
  • Cold Start จะส่งผลกระทบรุนแรงที่สุดกับแอปที่มีการรับส่งข้อมูลในปริมาณสูง หาก แอปมีการเข้าชมสูง และคุณตั้งค่า minInstances สูงพอที่ Cold Start จะลดลงเมื่อการเข้าชมแต่ละครั้งเพิ่มขึ้น คุณจะเห็น เวลาในการตอบสนองที่ลดลง สำหรับแอปที่มีการเข้าชมคงที่ Cold Start จะไม่ อาจส่งผลต่อประสิทธิภาพอย่างมาก
  • การตั้งค่าอินสแตนซ์ขั้นต่ำอาจเหมาะสมสำหรับสภาพแวดล้อมการใช้งานจริง แต่ ที่ควรหลีกเลี่ยงในสภาพแวดล้อมการทดสอบ หากต้องการปรับสัดส่วนเป็น 0 ใน แต่ยังคงลด Cold Start ในโปรเจ็กต์ที่ใช้งานจริง ตั้งค่า minInstances ได้ตามตัวแปรสภาพแวดล้อม FIREBASE_CONFIG ดังนี้

    // Get Firebase project id from `FIREBASE_CONFIG` environment variable
    const envProjectId = JSON.parse(process.env.FIREBASE_CONFIG).projectId;
    
    exports.renderProfilePage = functions
        .runWith({
          // Keep 5 instances warm for this latency-critical function
          // in production only. Default to 0 for test projects.
          minInstances: envProjectId === "my-production-project" ? 5 : 0,
        })
        .https.onRequest((req, res) => {
          // render some html
        });
    

จำกัดจำนวนอินสแตนซ์สูงสุดสำหรับฟังก์ชัน

หากต้องการตั้งค่าจำนวนอินสแตนซ์สูงสุดในซอร์สโค้ดของฟังก์ชัน ให้ใช้เมธอด runWith เมธอดนี้ยอมรับออบเจ็กต์ JSON ที่สอดคล้องกับ RuntimeOptions ซึ่งกำหนดว่า สำหรับ maxInstances เช่น ฟังก์ชันนี้ตั้งค่าขีดจำกัดไว้ที่ 100 เพื่อไม่ให้เกิดฐานข้อมูลเดิมสมมติมากเกินไป

exports.mirrorOrdersToLegacyDatabase = functions
    .runWith({
      // Legacy database only supports 100 simultaneous connections
      maxInstances: 100,
    })
    .firestore.document("orders/{orderId}")
    .onWrite((change, context) => {
      // Connect to legacy database
    });

หากฟังก์ชัน HTTP ปรับขนาดเป็นขีดจำกัด maxInstances คำขอใหม่จะ รอ 30 วินาที และถูกปฏิเสธโดยมีโค้ดตอบกลับเป็น 429 Too Many Requests หากไม่มีอินสแตนซ์พร้อมใช้งานภายในเวลาดังกล่าว

ดูข้อมูลเพิ่มเติมเกี่ยวกับแนวทางปฏิบัติแนะนำสำหรับการใช้การตั้งค่าอินสแตนซ์สูงสุดได้ที่ ออก แนวทางปฏิบัติแนะนำสำหรับการใช้ maxInstances

ตั้งค่าการหมดเวลาและการจัดสรรหน่วยความจำ

ในบางกรณี ฟังก์ชันอาจมีข้อกำหนดพิเศษเป็นระยะเวลานาน หรือการจัดสรรหน่วยความจำในปริมาณมาก คุณสามารถตั้งค่าเหล่านี้ได้ใน คอนโซล Google Cloud หรือในซอร์สโค้ดของฟังก์ชัน (Firebase เท่านั้น)

หากต้องการตั้งค่าการจัดสรรหน่วยความจำและระยะหมดเวลาในซอร์สโค้ดของฟังก์ชัน ให้ใช้ runWith เปิดตัวใน Firebase SDK สำหรับ Cloud Functions 2.0.0 ตัวเลือกรันไทม์นี้ยอมรับ ออบเจ็กต์ JSON ที่สอดคล้องกับ RuntimeOptions ซึ่งกำหนดค่าสำหรับ timeoutSeconds และ memory ตัวอย่างเช่น ฟังก์ชันพื้นที่เก็บข้อมูลนี้ใช้หน่วยความจำ 1 GB และหมดเวลาหลังจาก 300 วินาที:

exports.convertLargeFile = functions
    .runWith({
      // Ensure the function has enough memory and time
      // to process large files
      timeoutSeconds: 300,
      memory: "1GB",
    })
    .storage.object()
    .onFinalize((object) => {
      // Do some complicated things that take a lot of memory and time
    });

ค่าสูงสุดสำหรับ timeoutSeconds คือ 540 หรือ 9 นาที จำนวนหน่วยความจำที่ให้กับฟังก์ชันจะสอดคล้องกับ CPU ที่จัดสรร สำหรับฟังก์ชัน โปรดดูรายละเอียดในรายการค่าที่ถูกต้องสำหรับ memory ดังนี้

  • 128MB — 200MHz
  • 256MB — 400MHz
  • 512MB — 800MHz
  • 1GB — 1.4 GHz
  • 2GB — 2.4 GHz
  • 4GB — 4.8 GHz
  • 8GB — 4.8 GHz

วิธีตั้งค่าการจัดสรรหน่วยความจำและระยะหมดเวลาในคอนโซล Google Cloud

  1. ในคอนโซล Google Google Cloud ให้เลือกฟังก์ชันระบบคลาวด์จาก เมนูด้านซ้าย
  2. เลือกฟังก์ชันโดยคลิกชื่อฟังก์ชันในรายการฟังก์ชัน
  3. คลิกไอคอนแก้ไขในเมนูด้านบน
  4. เลือกการจัดสรรหน่วยความจำจากเมนูแบบเลื่อนลงที่มีป้ายกำกับจัดสรรหน่วยความจำแล้ว
  5. คลิกเพิ่มเติมเพื่อแสดงตัวเลือกขั้นสูงและป้อนจำนวนวินาทีในช่องข้อความระยะหมดเวลา
  6. คลิกบันทึกเพื่ออัปเดตฟังก์ชัน