開始使用 Java

本教學課程適用於初次在雲端環境中建構應用程式的新手,例如想要學習重要應用程式開發概念,並在開始使用 Cloud de Confiance by S3NS的過程中加以應用的工程師和網頁程式開發人員。

目標

費用

在本文件中,您會使用下列 Cloud de Confiance by S3NS的計費元件:

本文提供的說明可協助您將資源用量控制在 Cloud de Confiance的「一律免費」方案額度內。

完成本文所述工作後,您可以刪除建立的資源,避免繼續計費,詳情請參閱「清除所用資源」。

事前準備

  1. In the Cloud de Confiance console, on the project selector page, select or create a Cloud de Confiance project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  2. Verify that billing is enabled for your Cloud de Confiance project.

  3. 如要以原生模式建立 Firestore 資料庫,請完成下列步驟:
    1. 前往 Cloud de Confiance 控制台的「Firestore create database」(建立 Firestore 資料庫)頁面。

      前往 Firestore 建立資料庫

    2. 按一下「選取 Cloud Firestore 模式」畫面中的 [選取原生模式]
    3. 選取 Firestore 資料庫的位置。您所設定的位置會是 Cloud de Confiance 專案的預設 Cloud de Confiance 資源位置 。這個位置會用於專案中需要位置設定的服務,具體來說,就是預設的 Cloud Storage 值區和 Cloud Run 應用程式。
    4. 按一下 [建立資料庫]
  4. 啟用 Cloud Run Admin、Cloud Storage、Cloud Logging 和 Error Reporting API。

    啟用 API 時所需的角色

    如要啟用 API,您需要服務使用情形管理員 IAM 角色 (roles/serviceusage.serviceUsageAdmin),其中包含 serviceusage.services.enable 權限。瞭解如何授予角色

    啟用 API

  5. 複製範例存放區,並在 Cloud Shell 中開啟範例應用程式:
    前往 Cloud Shell

    Cloud Shell 可讓您直接在瀏覽器中使用指令列工具存取 Cloud de Confiance 資源。

  6. 如要將程式碼範例和變更下載到應用程式目錄中,請按一下 [繼續]
  7. 在 Cloud Shell 中,將 gcloud 工具設定為使用您新建的 Cloud de Confiance 專案:

    # Configure gcloud for your project
    gcloud config set project PROJECT_ID

    PROJECT_ID 替換為您使用 Cloud de Confiance 控制台建立的 Cloud de Confiance 專案 ID。

    Google Cloud CLI 是您透過指令列與 Cloud de Confiance 資源互動的主要方式。在本教學課程中,您將使用 gcloud 工具部署及監控應用程式。

執行應用程式

  1. 如果您已經使用 Cloud Shell 並設為使用 Java 11,請將 Shell 的 Java 替代路徑 JAVA_HOMEPATH 環境變數改為指定 Java 8。
  2. 切換至 bookshelf/1-cloud-run 目錄並執行應用程式:
    GOOGLE_CLOUD_PROJECT=PROJECT_ID mvn -Plocal clean jetty:run-exploded
    
    PROJECT_ID 替換為您建立的專案 ID。 Cloud de Confiance
  3. 在 Cloud Shell 中,按一下 [網頁預覽],然後選取 [透過以下通訊埠預覽:8080]。這會開啟新視窗,顯示執行中的應用程式。

將應用程式部署至 Cloud Run

Cloud de Confiance 提供數個執行程式碼的選項。以這個例子來說,您會利用 Cloud Run,將可擴充的應用程式部署至 Cloud de Confiance。Cloud Run 不需要您管理伺服器,且會自動調度資源以應付突然爆增的流量。

請按照「將應用程式部署至 Cloud Run」一文中的操作說明進行。

部署成功後,會依照下列格式將端點輸出至在 Cloud Run 中執行的應用程式:

https://bookshelf-abcdefghij-uc.a.run.app

現在,您只要前往這個連結 (以下稱為 YOUR_CLOUD_RUN_URL),就能看到自己的應用程式。請在網路瀏覽器中,輸入這個網址來檢視應用程式。

Bookshelf 應用程式首頁

使用 Firestore 保留資料

您無法將資訊儲存在 Cloud Run 執行個體上,因為這些資訊在執行個體重新啟動後將會遺失,而且也不會存在於新建立的執行個體當中。因此,您要使用一個資料庫,並讓所有執行個體都從該資料庫中讀取資料,並將資料寫入到其中。

Cloud de Confiance 提供多個儲存資料的選項。在此範例中,您會使用 Firestore 儲存每本書的資料。Firestore 是全代管、無伺服器的 NoSQL 文件資料庫,可讓您儲存及查詢資料。Firestore 可根據您應用程式的需求自動調整資源配置,您不使用應用程式時,Firestore 也會將資源調度率降至零。讓我們馬上開始新增您的第一本書。

  1. 透過網路瀏覽器前往 YOUR_CLOUD_RUN_URL
  2. 如要為已部署的應用程式建立書籍,請按一下 [新增書籍]

    將書籍新增至 Bookshelf 應用程式
  3. 在「Title」欄位中,輸入 Moby Dick
  4. 在「Author」欄位中,輸入 Herman Melville
  5. 按一下 [儲存]。現在您已在 Bookshelf 應用程式中加入項目。

    Bookshelf 應用程式項目「Moby Dick」
  6. 在 Cloud de Confiance 控制台中,如要重新整理 Firestore 頁面,請按一下「重新整理」圖示 。資料即會出現在 Firestore 中。Bookshelf 應用程式會將每本書儲存為具有專屬 ID 的 Firestore 文件,並將這些文件全都儲存在 Firestore 集合。基於本教學課程的目標,我們將這個集合稱為「books」。Firestore 文件的範例。

Firestore 會使用 Firestore 用戶端程式庫儲存書籍資料。以下提供擷取 Firestore 文件的範例:

public class FirestoreDao implements BookDao {
  private CollectionReference booksCollection;

  public FirestoreDao() {
    Firestore firestore = FirestoreOptions.getDefaultInstance().getService();
    booksCollection = firestore.collection("books");
  }

  private Book documentToBook(DocumentSnapshot document) {
    Map<String, Object> data = document.getData();
    if (data == null) {
      System.out.println("No data in document " + document.getId());
      return null;
    }

    return new Book.Builder()
        .author((String) data.get(Book.AUTHOR))
        .description((String) data.get(Book.DESCRIPTION))
        .publishedDate((String) data.get(Book.PUBLISHED_DATE))
        .imageUrl((String) data.get(Book.IMAGE_URL))
        .createdBy((String) data.get(Book.CREATED_BY))
        .createdById((String) data.get(Book.CREATED_BY_ID))
        .title((String) data.get(Book.TITLE))
        .id(document.getId())
        .build();
  }

  @Override
  public String createBook(Book book) {
    String id = UUID.randomUUID().toString();
    DocumentReference document = booksCollection.document(id);
    Map<String, Object> data = Maps.newHashMap();

    data.put(Book.AUTHOR, book.getAuthor());
    data.put(Book.DESCRIPTION, book.getDescription());
    data.put(Book.PUBLISHED_DATE, book.getPublishedDate());
    data.put(Book.TITLE, book.getTitle());
    data.put(Book.IMAGE_URL, book.getImageUrl());
    data.put(Book.CREATED_BY, book.getCreatedBy());
    data.put(Book.CREATED_BY_ID, book.getCreatedById());
    try {
      document.set(data).get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }

    return id;
  }

  @Override
  public Book readBook(String bookId) {
    try {
      DocumentSnapshot document = booksCollection.document(bookId).get().get();

      return documentToBook(document);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return null;
  }

  @Override
  public void updateBook(Book book) {
    DocumentReference document = booksCollection.document(book.getId());
    Map<String, Object> data = Maps.newHashMap();

    data.put(Book.AUTHOR, book.getAuthor());
    data.put(Book.DESCRIPTION, book.getDescription());
    data.put(Book.PUBLISHED_DATE, book.getPublishedDate());
    data.put(Book.TITLE, book.getTitle());
    data.put(Book.IMAGE_URL, book.getImageUrl());
    data.put(Book.CREATED_BY, book.getCreatedBy());
    data.put(Book.CREATED_BY_ID, book.getCreatedById());
    try {
      document.set(data).get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void deleteBook(String bookId) {
    try {
      booksCollection.document(bookId).delete().get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }

  private List<Book> documentsToBooks(List<QueryDocumentSnapshot> documents) {
    List<Book> resultBooks = new ArrayList<>();
    for (QueryDocumentSnapshot snapshot : documents) {
      resultBooks.add(documentToBook(snapshot));
    }
    return resultBooks;
  }

  @Override
  public Result<Book> listBooks(String startTitle) {
    Query booksQuery = booksCollection.orderBy("title").limit(10);
    if (startTitle != null) {
      booksQuery = booksQuery.startAfter(startTitle);
    }
    try {
      QuerySnapshot snapshot = booksQuery.get().get();
      List<Book> results = documentsToBooks(snapshot.getDocuments());
      String newCursor = null;
      if (results.size() > 0) {
        newCursor = results.get(results.size() - 1).getTitle();
      }
      return new Result<>(results, newCursor);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return new Result<>(Lists.newArrayList(), null);
  }

  @Override
  public Result<Book> listBooksByUser(String userId, String startTitle) {
    Query booksQuery =
        booksCollection.orderBy("title").whereEqualTo(Book.CREATED_BY_ID, userId).limit(10);
    if (startTitle != null) {
      booksQuery = booksQuery.startAfter(startTitle);
    }
    try {
      QuerySnapshot snapshot = booksQuery.get().get();
      List<Book> results = documentsToBooks(snapshot.getDocuments());
      String newCursor = null;
      if (results.size() > 0) {
        newCursor = results.get(results.size() - 1).getTitle();
      }
      return new Result<>(results, newCursor);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return new Result<>(Lists.newArrayList(), null);
  }
}

如要進一步瞭解如何使用 Firestore,請參閱將資料新增至 Firestore 的說明。

將上傳的檔案儲存在 Cloud Storage 中

現在您已新增了一本書,接下來請新增書籍的封面圖片。您無法將檔案儲存在執行個體中,資料庫也不適合用來儲存圖片檔。建議您可以使用 Cloud Storage。

Cloud Storage 是 Cloud de Confiance主要的 blob 儲存庫。您可以使用 Cloud Storage 託管要在 Cloud de Confiance中共用的應用程式資產。如要使用 Cloud Storage,您必須建立一個 Cloud Storage bucket,這個基本容器可用來保存資料。

  1. 前往 Cloud de Confiance 控制台的「Cloud Storage 瀏覽器」頁面。

    前往「Cloud Storage 瀏覽器」頁面

  2. 按一下 [建立值區]
  3. 在「建立值區」對話方塊中,將您的 Cloud de Confiance 專案 ID 加到 _bucket 字串的後面來輸入值區的名稱,讓名稱看起來像 YOUR_PROJECT_ID_bucket 這樣。這個名稱必須遵守值區命名規定,所有其他欄位則可以保留其預設值。
  4. 按一下 [建立]。
  5. 建立值區之後,您必須開放物件的公開存取權,以便使用者查看。如需開放物件公開存取權的相關操作說明,請參閱將資料設為公開
  6. 請按一下 [編輯書籍],然後選取上傳後要當做書籍封面的圖片。舉例來說,您可以使用這個公有領域的圖片:
    Moby Dick 書籍封面
  7. 按一下 [Save]。系統會將您重新導向至首頁,當中會顯示 Bookshelf 應用程式項目。
    Bookshelf 應用程式項目 Moby Dick

Bookshelf 應用程式會使用 Cloud Storage 用戶端程式庫將上傳的檔案傳送至 Cloud Storage。

public class CloudStorageHelper {

  private final Logger logger = Logger.getLogger(CloudStorageHelper.class.getName());
  private static Storage storage = null;

  static {
    storage = StorageOptions.getDefaultInstance().getService();
  }


  /**
   * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME environment
   * variable, appending a timestamp to end of the uploaded filename.
   */
  public String uploadFile(FileItemStream fileStream, final String bucketName)
      throws IOException, ServletException {
    checkFileExtension(fileStream.getName());

    System.out.println("FileStream name: " + fileStream.getName() + "\nBucket name: " + bucketName);

    DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS");
    DateTime dt = DateTime.now(DateTimeZone.UTC);
    String dtString = dt.toString(dtf);
    final String fileName = fileStream.getName() + dtString;

    // the inputstream is closed by default, so we don't need to close it here
    @SuppressWarnings("deprecation")
    BlobInfo blobInfo =
        storage.create(
            BlobInfo.newBuilder(bucketName, fileName)
                // Modify access list to allow all users with link to read file
                .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER))))
                .build(),
            fileStream.openStream());
    logger.log(
        Level.INFO, "Uploaded file {0} as {1}", new Object[] {fileStream.getName(), fileName});
    // return the public download link
    return blobInfo.getMediaLink();
  }


  /** Checks that the file extension is supported. */
  private void checkFileExtension(String fileName) throws ServletException {
    if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) {
      String[] allowedExt = {".jpg", ".jpeg", ".png", ".gif"};
      for (String ext : allowedExt) {
        if (fileName.endsWith(ext)) {
          return;
        }
      }
      throw new ServletException("file must be an image");
    }
  }
}

如要進一步瞭解如何使用 Cloud Storage,請參閱 Cloud Storage 簡介

使用 Google Cloud Observability 監控應用程式

您已部署應用程式,並建立及修改了書籍資訊。如要為您的使用者監控這些事件,請使用應用程式效能管理。

使用 Cloud Logging 監控記錄檔

  1. 前往 Cloud de Confiance的「Logs Explorer」(記錄檔探索工具)

    前往「Logs Explorer」頁面

    現在,您可以即時監控自己的應用程式了。如果應用程式發生任何問題,這就是您要優先查看的地方之一。

    Stackdriver 記錄檢視器
  2. 在「資源」下拉式清單中,選取 [Cloud Run 修訂版本, bookshelf]

透過 Error Reporting 監控錯誤

  1. 前往 Cloud de Confiance 控制台的「Error Reporting」頁面。
    前往「Error Reporting」頁面
    Error Reporting 會醒目顯示應用程式中的錯誤和例外狀況,並讓您設定與其相關的快訊。
  2. 透過瀏覽器前往應用程式的 /errors 網址。
    YOUR_CLOUD_RUN_URL/errors

    這項操作會產生一個新的測試例外狀況,並傳送給 Google Cloud Observability。

  3. 返回 Cloud de Confiance 控制台的「Error Reporting」頁面,稍後就會顯示新的錯誤。按一下「自動重新載入」,這樣您就不需要手動重新整理頁面。

    Error Reporting 的錯誤訊息

清除所用資源

為避免因為本教學課程所用資源,導致系統向 Google Cloud 收取費用,請刪除含有相關資源的專案,或者保留專案但刪除個別資源。

刪除專案

  1. 前往 Cloud de Confiance 控制台的「Manage resources」(管理資源) 頁面。

    前往「Manage resources」(管理資源)

  2. 在專案清單中選取要刪除的專案,然後點選「Delete」(刪除)
  3. 在對話方塊中輸入專案 ID,然後按一下 [Shut down] (關閉) 以刪除專案。

後續步驟