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

1. ภาพรวม

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

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

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

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

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

  • วิธีแลกเปลี่ยนข้อความระหว่างแอปด้วย 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 และสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้อง สร้างบัญชี

b35bf95b8bf3d5d8.pnga99b7ace416376c4.pngbd84a6d3004737c5.png

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

Google Cloud Shell

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

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

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

55efc1aaa7a4d3ad.png

หากเริ่มต้น Cloud Shell เป็นครั้งแรก คุณจะเห็นหน้าจอระดับกลางที่อธิบายว่า 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

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

เพื่อให้แอปเขียนลงในช่องได้ เราสามารถใช้ Spring Integration Messaging Gateway ได้ ใช้โปรแกรมแก้ไขข้อความจาก 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);
  }
}

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

เราต้องมี Channel Adapter ขาออกเพื่อใช้ข้อความใหม่ในช่องและเผยแพร่ไปยังหัวข้อ 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 ในกรณีนี้ เราจะเรียก Channel Adapter ขาออก PubSubMessageHandler เพื่อเผยแพร่ข้อความไปยังหัวข้อ exampleTopic ของ Google Cloud Pub/Sub

เมื่อมี Channel Adapter แล้ว ตอนนี้เราสามารถเชื่อมโยงออบเจ็กต์ 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 Controller ด้วย

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

ในแอปก่อนหน้านี้ การประกาศ Messaging Gateway ได้สร้าง Channel ขาออกให้เรา เนื่องจากเราไม่ได้ใช้ Messaging Gateway เพื่อรับข้อความ เราจึงต้องประกาศ 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();
  }
}

เราจะต้องมี Channel Adapter ขาเข้าเพื่อรับข้อความจาก 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;
  }
}

Adapter นี้จะผูกตัวเองกับ pubsubInputChannel และรับฟังข้อความใหม่จากการสมัครใช้บริการ exampleSubscription ของ Google Cloud Pub/Sub

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

มาประมวลผลข้อความด้วย @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 แอปที่ใช้ Spring Integration Channel Adapter สำหรับ Google Cloud Pub/Sub แอปเหล่านี้แลกเปลี่ยนข้อความกันเองโดยไม่ต้องโต้ตอบกับ Google Cloud Pub/Sub API เลย

9. ยินดีด้วย

คุณได้เรียนรู้วิธีใช้ Spring Integration Channel Adapter สำหรับ Google Cloud Pub/Sub แล้ว

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

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้ Creative Commons Attribution 2.0 Generic License