การรับส่งข้อความด้วยการผสานรวม Spring และ Google Cloud Pub/Sub

1. ภาพรวม

การผสานรวม Spring มีกลไกการรับส่งข้อความเพื่อแลกเปลี่ยน Messages ผ่าน MessageChannels โดยใช้อะแดปเตอร์ช่องในการสื่อสารกับระบบภายนอก

ในแบบฝึกหัดนี้ เราจะสร้างแอป 2 แอปที่สื่อสารกันโดยใช้อะแดปเตอร์ช่องทางการรวม Spring จาก Spring Cloud GCP อะแดปเตอร์เหล่านี้ทำให้ Spring Integration ใช้ Google Cloud Pub/Sub เป็นแบ็กเอนด์ของการแลกเปลี่ยนข้อความ

คุณจะได้ดูวิธีใช้ Cloud Shell และคำสั่ง gcloud ของ Cloud SDK

บทแนะนำนี้จะใช้โค้ดตัวอย่างจากคู่มือเริ่มต้นใช้งานการเปิดเครื่องช่วงฤดูใบไม้ผลิ

สิ่งที่คุณจะได้เรียนรู้

  • วิธีแลกเปลี่ยนข้อความระหว่างแอปด้วย Google Cloud Pub/Sub โดยใช้ Spring Integration และ Spring Cloud GCP

สิ่งที่คุณต้องมี

  • โปรเจ็กต์ Google Cloud Platform
  • เบราว์เซอร์ เช่น Chrome หรือ Firefox
  • คุ้นเคยกับเครื่องมือแก้ไขข้อความมาตรฐานของ Linux เช่น Vim, EMACs หรือ Nano

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านเท่านั้น อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์ในการสร้างเว็บแอป HTML/CSS อย่างไร

มือใหม่ ระดับกลาง ผู้ชำนาญ

คุณจะให้คะแนนความพึงพอใจสำหรับประสบการณ์การใช้บริการ Google Cloud Platform อย่างไร

มือใหม่ ระดับกลาง ผู้ชำนาญ

2. การตั้งค่าและข้อกำหนด

การตั้งค่าสภาพแวดล้อมตามเวลาที่สะดวก

  1. ลงชื่อเข้าใช้ Google Cloud Console และสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • ชื่อโครงการคือชื่อที่แสดงของผู้เข้าร่วมโปรเจ็กต์นี้ เป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ โดยคุณจะอัปเดตวิธีการชำระเงินได้ทุกเมื่อ
  • รหัสโปรเจ็กต์จะไม่ซ้ำกันในทุกโปรเจ็กต์ของ Google Cloud และจะเปลี่ยนแปลงไม่ได้ (เปลี่ยนแปลงไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ปกติแล้วคุณไม่สนว่าอะไรเป็นอะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยปกติจะระบุเป็น PROJECT_ID) หากคุณไม่ชอบรหัสที่สร้างขึ้น คุณสามารถสร้างรหัสแบบสุ่มอื่นได้ หรือคุณจะลองดำเนินการเองแล้วดูว่าพร้อมให้ใช้งานหรือไม่ คุณจะเปลี่ยนแปลงหลังจากขั้นตอนนี้ไม่ได้และจะยังคงอยู่ตลอดระยะเวลาของโปรเจ็กต์
  • สำหรับข้อมูลของคุณ ค่าที่ 3 คือหมายเลขโปรเจ็กต์ ซึ่ง API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 ค่าเหล่านี้ในเอกสารประกอบ
  1. ถัดไป คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของระบบคลาวด์ การใช้งาน Codelab นี้จะไม่มีค่าใช้จ่ายใดๆ หากมี หากต้องการปิดทรัพยากรเพื่อหลีกเลี่ยงการเรียกเก็บเงินที่นอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่คุณสร้างหรือลบโปรเจ็กต์ได้ ผู้ใช้ Google Cloud ใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรี$300 USD

Google Cloud Shell

แม้ Google Cloud จะทำงานจากระยะไกลได้จากแล็ปท็อป แต่ใน Codelab นี้ เราจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมแบบบรรทัดคำสั่งที่ทำงานในระบบคลาวด์

เปิดใช้งาน Cloud Shell

  1. คลิกเปิดใช้งาน Cloud Shell 853e55310c205094.png จาก Cloud Console

55efc1aaa7a4d3ad.png

หากเริ่มต้นใช้งาน Cloud Shell เป็นครั้งแรก คุณจะเห็นหน้าจอตรงกลางที่อธิบายว่านี่คืออะไร หากระบบแสดงหน้าจอตรงกลาง ให้คลิกต่อไป

9c92662c6a846a5c.png

การจัดสรรและเชื่อมต่อกับ Cloud Shell ใช้เวลาเพียงไม่กี่นาที

9f0e51b578fecce5.png

เครื่องเสมือนนี้โหลดด้วยเครื่องมือการพัฒนาทั้งหมดที่จำเป็น โดยมีไดเรกทอรีหลักขนาด 5 GB ถาวรและทำงานใน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพของเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก หรืองานส่วนใหญ่ใน Codelab นี้ก็สามารถทำได้ผ่านเบราว์เซอร์

เมื่อเชื่อมต่อกับ Cloud Shell แล้ว คุณควรเห็นข้อความตรวจสอบสิทธิ์และโปรเจ็กต์ได้รับการตั้งค่าเป็นรหัสโปรเจ็กต์แล้ว

  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคุณได้รับการตรวจสอบสิทธิ์แล้ว
gcloud auth list

เอาต์พุตจากคำสั่ง

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคำสั่ง gcloud รู้เกี่ยวกับโปรเจ็กต์ของคุณ
gcloud config list project

เอาต์พุตจากคำสั่ง

[core]
project = <PROJECT_ID>

หากไม่ใช่ ให้ตั้งคำสั่งด้วยคำสั่งนี้

gcloud config set project <PROJECT_ID>

เอาต์พุตจากคำสั่ง

Updated property [core/project].

3. จัดสรรทรัพยากร Pub/Sub

ไปที่หน้าหัวข้อ Google Cloud Pub/Sub

คลิกสร้างหัวข้อ

4c938409dc7169a6.png

พิมพ์ exampleTopic เป็นชื่อของหัวข้อแล้วคลิกสร้าง..

e2daeec91537f672.png

หลังจากสร้างหัวข้อแล้ว ให้อยู่ในหน้าหัวข้อ มองหาหัวข้อที่คุณเพิ่งสร้างขึ้น กดจุดแนวตั้ง 3 จุดที่ท้ายบรรทัด แล้วคลิกการสมัครใช้บริการใหม่

975efa26e5054936.png

พิมพ์ exampleSubscription ในช่องข้อความชื่อการสมัครใช้บริการ แล้วคลิกสร้าง

f7a91d9e1cb48009.png

4. เริ่มต้นแอปพลิเคชัน Spring Boot

หลังจากเปิดตัว Cloud Shell แล้ว คุณจะใช้บรรทัดคำสั่งเพื่อสร้างแอปพลิเคชัน Spring Boot ใหม่ 2 รายการได้ด้วย Spring Initializr ดังนี้

$ curl https://start.spring.io/starter.tgz \
  -d bootVersion=3.0.5 \
  -d dependencies=web,integration,cloud-gcp-pubsub \
  -d type=maven-project \
  -d baseDir=spring-integration-sender | tar -xzvf -

$ curl https://start.spring.io/starter.tgz \
  -d bootVersion=3.0.5 \
  -d dependencies=web,integration,cloud-gcp-pubsub \
  -d type=maven-project \
  -d baseDir=spring-integration-receiver | tar -xzvf -

5. สร้างแอปพลิเคชันเพื่อส่งข้อความ

คราวนี้เราจะมาสร้างแอปรับส่งข้อความ เปลี่ยนเป็นไดเรกทอรีของแอปที่ส่ง

$ cd spring-integration-sender

เราต้องการให้แอปเขียนข้อความถึงช่อง หลังจากที่ข้อความอยู่ในแชแนลแล้ว ข้อความจะได้รับการเลือกโดยอะแดปเตอร์ช่องทางขาออก ซึ่งจะแปลงข้อความนั้นจากข้อความ Spring ทั่วไปเป็นข้อความ Google Cloud Pub/Sub และเผยแพร่ไปยังหัวข้อ Google Cloud Pub/Sub

เพื่อให้แอปของเราเขียนไปยังแชแนลได้ เราจะใช้เกตเวย์การรับส่งข้อความ Spring Integration ใช้เครื่องมือแก้ไขข้อความจาก vim, emacs หรือ nano เพื่อประกาศอินเทอร์เฟซ PubsubOutboundGateway ในคลาส DemoApplication

src/main/java/com/example/demo/DemoApplication.java

...
import org.springframework.integration.annotation.MessagingGateway;

@SpringBootApplication
public class DemoApplication {

  ...

  @MessagingGateway(defaultRequestChannel = "pubsubOutputChannel")
  public interface PubsubOutboundGateway {
    void sendToPubsub(String text);
  }
}

ตอนนี้เรามีกลไกในการส่งข้อความไปยังช่องแล้ว แต่ข้อความเหล่านั้นจะย้ายไปที่ไหนหลังจากอยู่ในช่องแล้ว

เราต้องการอะแดปเตอร์ช่องทางขาออกเพื่อใช้ข้อความใหม่ในช่องทาง และเผยแพร่ข้อความไปยังหัวข้อ Google Cloud Pub/Sub

src/main/java/com/example/demo/DemoApplication.java

...
import com.google.cloud.spring.pubsub.core.PubSubTemplate;
import com.google.cloud.spring.pubsub.integration.outbound.PubSubMessageHandler;

import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.MessageHandler;

@SpringBootApplication
public class DemoApplication {

  ...

  @Bean
  @ServiceActivator(inputChannel = "pubsubOutputChannel")
  public MessageHandler messageSender(PubSubTemplate pubsubTemplate) {
    return new PubSubMessageHandler(pubsubTemplate, "exampleTopic");
  }
}

คำอธิบายประกอบ @ServiceActivator จะทำให้ระบบใช้ MessageHandler นี้กับข้อความใหม่ใน inputChannel ในกรณีนี้ เราจะโทรหาอะแดปเตอร์ช่องทางขาออก PubSubMessageHandler เพื่อเผยแพร่ข้อความไปยังหัวข้อ exampleTopic ของ Google Cloud Pub/Sub

เมื่อใช้อะแดปเตอร์ช่องแล้ว เราสามารถต่อสายออบเจ็กต์ PubsubOutboundGateway โดยอัตโนมัติและใช้เพื่อเขียนข้อความไปยังช่องได้

src/main/java/com/example/demo/DemoApplication.java

...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.view.RedirectView;

@SpringBootApplication
public class DemoApplication {

  ...

  @Autowired
  private PubsubOutboundGateway messagingGateway;

  @PostMapping("/postMessage")
  public RedirectView postMessage(@RequestParam("message") String message) {
    this.messagingGateway.sendToPubsub(message);
    return new RedirectView("/");
  }
}

เนื่องจากคำอธิบายประกอบ @PostMapping ตอนนี้เรามีปลายทางที่ฟังคำขอ HTTP POST แต่ไม่ได้เพิ่มคำอธิบายประกอบ @RestController ลงในคลาส DemoApplication เพื่อทำเครื่องหมายเป็นตัวควบคุม REST

src/main/java/com/example/demo/DemoApplication.java

import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {
  ...
}

ตรวจสอบว่าตั้งค่า JAVA_HOME เป็นเวอร์ชันที่ถูกต้อง

export JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64

เรียกใช้แอปผู้ส่ง

# Set the Project ID in environmental variable
$ export GOOGLE_CLOUD_PROJECT=`gcloud config list --format 'value(core.project)'`

$ ./mvnw spring-boot:run

แอปกำลังฟังคำขอ POST ที่มีข้อความบนพอร์ต 8080 และปลายทาง /postMessage แต่เราจะพูดถึงเรื่องนี้ในภายหลัง

6. สร้างแอปพลิเคชันเพื่อรับข้อความ

เราเพิ่งสร้างแอปที่ส่งข้อความผ่าน Google Cloud Pub/Sub ต่อไปเราจะสร้างอีกแอปหนึ่งที่จะรับข้อความเหล่านั้นและประมวลผลข้อความเหล่านั้น

คลิก + เพื่อเปิดเซสชัน Cloud Shell ใหม่

9799bee5fea95aa6.png

จากนั้นในเซสชัน Cloud Shell ใหม่ ให้เปลี่ยนไดเรกทอรีเป็นไดเรกทอรีของแอปตัวรับดังนี้

$ cd spring-integration-receiver

ในแอปก่อนหน้านี้ การประกาศเกตเวย์การรับส่งข้อความได้สร้างช่องทางขาออกให้เรา เนื่องจากเราไม่ได้ใช้เกตเวย์การรับส่งข้อความในการรับข้อความ เราจึงต้องประกาศ MessageChannel ของเราเองว่าจะได้รับข้อความขาเข้าที่ไหน

src/main/java/com/example/demo/DemoApplication.java

...
import org.springframework.context.annotation.Bean;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.MessageChannel;

@SpringBootApplication
public class DemoApplication {

  ...

  @Bean
  public MessageChannel pubsubInputChannel() {
    return new DirectChannel();
  }
}

เราต้องการอะแดปเตอร์ช่องทางขาเข้าเพื่อรับข้อความจาก Google Cloud Pub/Sub และส่งต่อไปยัง pubsubInputChannel

src/main/java/com/example/demo/DemoApplication.java

...
import com.google.cloud.spring.pubsub.core.PubSubTemplate;
import com.google.cloud.spring.pubsub.integration.inbound.PubSubInboundChannelAdapter;

import org.springframework.beans.factory.annotation.Qualifier;

@SpringBootApplication
public class DemoApplication {

  ...

  @Bean
  public PubSubInboundChannelAdapter messageChannelAdapter(
      @Qualifier("pubsubInputChannel") MessageChannel inputChannel,
      PubSubTemplate pubSubTemplate) {
    PubSubInboundChannelAdapter adapter =
        new PubSubInboundChannelAdapter(pubSubTemplate, "exampleSubscription");
    adapter.setOutputChannel(inputChannel);

    return adapter;
  }
}

อะแดปเตอร์นี้จะเชื่อมโยงตัวเองกับ pubsubInputChannel และฟังข้อความใหม่จากการสมัครใช้บริการ Google Cloud Pub/Sub exampleSubscription

เรามีช่องสำหรับโพสต์ข้อความเข้ามา แต่จะทำอย่างไรกับข้อความดังกล่าว

มาลองประมวลผลด้วย @ServiceActivator ซึ่งจะทริกเกอร์เมื่อมีข้อความใหม่มาถึง pubsubInputChannel ในกรณีนี้ เราจะบันทึกเพย์โหลดของข้อความ

src/main/java/com/example/demo/DemoApplication.java

...
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.annotation.ServiceActivator;

@SpringBootApplication
public class DemoApplication {

  ...

  private static final Log LOGGER = LogFactory.getLog(DemoApplication.class);

  @ServiceActivator(inputChannel = "pubsubInputChannel")
  public void messageReceiver(String payload) {
    LOGGER.info("Message arrived! Payload: " + payload);
  }
}

ตรวจสอบว่าตั้งค่า JAVA_HOME เป็นเวอร์ชันที่ถูกต้อง

export JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64

เรียกใช้แอปตัวรับสัญญาณ

$ ./mvnw spring-boot:run -Dspring-boot.run.jvmArguments="-Dserver.port=8081"

ตอนนี้ข้อความที่คุณส่งไปยังแอปผู้ส่งจะได้รับการบันทึกในแอปตัวรับ หากต้องการทดสอบ ให้เปิดเซสชัน Cloud Shell ใหม่และส่งคำขอ HTTP POST ไปยังแอปผู้ส่ง

$ curl --data "message=Hello world!" localhost:8080/postMessage

จากนั้นตรวจสอบว่าแอปผู้รับบันทึกข้อความที่คุณส่งแล้ว

INFO: Message arrived! Payload: Hello world!

7. ล้างข้อมูล

ลบการสมัครรับข้อมูลและหัวข้อที่สร้างขึ้นในแบบฝึกหัดนี้

$ gcloud pubsub subscriptions delete exampleSubscription
$ gcloud pubsub topics delete exampleTopic

8. สรุป

คุณได้ตั้งค่าแอปการเปิดเครื่อง Spring Boot 2 แอปที่ใช้อะแดปเตอร์แชนเนลการรวมฤดูใบไม้ผลิสำหรับ Google Cloud Pub/Sub และแลกเปลี่ยนข้อความกันเองโดยไม่โต้ตอบกับ Google Cloud Pub/Sub API

9. ยินดีด้วย

คุณรู้วิธีใช้ Spring Integration Channel Adapters สำหรับ Google Cloud Pub/Sub แล้ว

ดูข้อมูลเพิ่มเติม

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้ใบอนุญาตทั่วไปครีเอทีฟคอมมอนส์แบบระบุแหล่งที่มา 2.0