JavaRush /จาวาบล็อก /Random-TH /การเพิ่ม Spring Scheduler - "โครงการ Java จาก A ถึง Z"
Roman Beekeeper
ระดับ

การเพิ่ม Spring Scheduler - "โครงการ Java จาก A ถึง Z"

เผยแพร่ในกลุ่ม
สวัสดีทุกคนเพื่อนรักของฉัน ในบทความก่อนหน้านี้เราได้เตรียมไคลเอนต์สำหรับการทำงานกับ JavaRush API สำหรับบทความ ตอนนี้เราสามารถเขียนตรรกะสำหรับงานของเราได้ ซึ่งจะถูกดำเนินการทุกๆ 15 นาที ตรงตามที่แสดงในแผนภาพนี้: “ โครงการ Java จาก A ถึง Z”: การเพิ่ม Spring Scheduler - 1ทุก ๆ 15 นาทีจะมีการเปิดตัวงาน (ในความเห็นของเรา เป็นเพียงวิธีการในคลาสเฉพาะ) ซึ่งจะดำเนินการในพื้นหลังของแอปพลิเคชันหลักและทำสิ่งต่อไปนี้:
  1. ค้นหาในทุกกลุ่มที่อยู่ในฐานข้อมูลของเราบทความใหม่ที่เผยแพร่หลังจากการดำเนินการครั้งก่อน

    รูปแบบนี้ระบุกลุ่มจำนวนน้อยกว่า - เฉพาะกลุ่มที่มีผู้ใช้งานอยู่เท่านั้น ในเวลานั้นมันดูสมเหตุสมผลสำหรับฉัน แต่ตอนนี้ฉันเข้าใจแล้วว่าไม่ว่าจะมีผู้ใช้งานที่สมัครเป็นสมาชิกกลุ่มใดกลุ่มหนึ่งหรือไม่ คุณยังคงต้องเก็บบทความล่าสุดที่บอทประมวลผลให้ทันสมัยอยู่เสมอ สถานการณ์อาจเกิดขึ้นเมื่อผู้ใช้ใหม่ได้รับบทความที่เผยแพร่ครบจำนวนทันทีนับตั้งแต่การปิดใช้งานกลุ่มนี้ นี่ไม่ใช่พฤติกรรมที่คาดหวัง และเพื่อหลีกเลี่ยงสิ่งนี้ เราจำเป็นต้องเก็บกลุ่มเหล่านั้นจากฐานข้อมูลของเราซึ่งปัจจุบันยังไม่มีผู้ใช้งานอยู่
  2. หากมีบทความใหม่ ให้สร้างข้อความสำหรับผู้ใช้ทุกคนที่สมัครเป็นสมาชิกกลุ่มนี้ หากไม่มีบทความใหม่ เราก็ดำเนินการให้เสร็จสิ้น

อย่างไรก็ตาม ฉันได้บอก ไปแล้ว ในช่อง TG ของฉันว่าบอทกำลังทำงานและส่งบทความใหม่ตามการสมัครสมาชิก มาเริ่มเขียนFindNewArtcileService กันดี กว่า งานค้นหาและส่งข้อความทั้งหมดจะเกิดขึ้นที่นั่นและงานจะเปิดตัววิธีการของบริการนี้เท่านั้น:

ค้นหาบริการบทความใหม่:

package com.github.javarushcommunity.jrtb.service;

/**
* Service for finding new articles.
*/
public interface FindNewArticleService {

   /**
    * Find new articles and notify subscribers about it.
    */
   void findNewArticles();
}
ง่ายมากใช่มั้ย? นี่คือสาระสำคัญและความยากลำบากทั้งหมดจะอยู่ที่การดำเนินการ:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.JavaRushPostClient;
import com.github.javarushcommunity.jrtb.javarushclient.dto.PostInfo;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class FindNewArticleServiceImpl implements FindNewArticleService {

   public static final String JAVARUSH_WEB_POST_FORMAT = "https://javarush.com/groups/posts/%s";

   private final GroupSubService groupSubService;
   private final JavaRushPostClient javaRushPostClient;
   private final SendBotMessageService sendMessageService;

   @Autowired
   public FindNewArticleServiceImpl(GroupSubService groupSubService,
                                    JavaRushPostClient javaRushPostClient,
                                    SendBotMessageService sendMessageService) {
       this.groupSubService = groupSubService;
       this.javaRushPostClient = javaRushPostClient;
       this.sendMessageService = sendMessageService;
   }


   @Override
   public void findNewArticles() {
       groupSubService.findAll().forEach(gSub -> {
           List<PostInfo> newPosts = javaRushPostClient.findNewPosts(gSub.getId(), gSub.getLastArticleId());

           setNewLastArticleId(gSub, newPosts);

           notifySubscribersAboutNewArticles(gSub, newPosts);
       });
   }

   private void notifySubscribersAboutNewArticles(GroupSub gSub, List<PostInfo> newPosts) {
       Collections.reverse(newPosts);
       List<String> messagesWithNewArticles = newPosts.stream()
               .map(post -> String.format("✨Вышла новая статья <b>%s</b> в группе <b>%s</b>.✨\n\n" +
                               "<b>Описание:</b> %s\n\n" +
                               "<b>Ссылка:</b> %s\n",
                       post.getTitle(), gSub.getTitle(), post.getDescription(), getPostUrl(post.getKey())))
               .collect(Collectors.toList());

       gSub.getUsers().stream()
               .filter(TelegramUser::isActive)
               .forEach(it -> sendMessageService.sendMessage(it.getChatId(), messagesWithNewArticles));
   }

   private void setNewLastArticleId(GroupSub gSub, List<PostInfo> newPosts) {
       newPosts.stream().mapToInt(PostInfo::getId).max()
               .ifPresent(id -> {
                   gSub.setLastArticleId(id);
                   groupSubService.save(gSub);
               });
   }

   private String getPostUrl(String key) {
       return String.format(JAVARUSH_WEB_POST_FORMAT, key);
   }
}
ที่นี่เราจะจัดการกับทุกสิ่งตามลำดับ:
  1. การใช้groupServiceเราจะค้นหากลุ่มทั้งหมดที่อยู่ในฐานข้อมูล

  2. จากนั้นเราก็แยกย้ายกันไปทุกกลุ่มและสำหรับแต่ละกลุ่มเราเรียกไคลเอนต์ที่สร้างขึ้นในบทความที่แล้ว- javaRushPostClient.findNewPosts

  3. ต่อไป โดยใช้ เมธอด setNewArticleIdเราจะอัปเดต ID บทความของบทความใหม่ล่าสุดของเรา เพื่อให้ฐานข้อมูลของเราทราบว่าเราได้ประมวลผลบทความใหม่แล้ว

  4. และด้วยความจริงที่ว่า GroupSub มีกลุ่มผู้ใช้ เราจะดำเนินการผ่านกลุ่มที่ใช้งานอยู่และส่งการแจ้งเตือนเกี่ยวกับบทความใหม่

เราจะไม่พูดคุยถึงสิ่งที่เป็นข้อความในตอนนี้ มันไม่สำคัญสำหรับเรามากนัก สิ่งสำคัญคือวิธีการนี้ใช้ได้ผล ตรรกะในการค้นหาบทความใหม่และส่งการแจ้งเตือนพร้อมแล้ว ดังนั้นคุณจึงสามารถก้าวไปสู่การสร้างงานได้

สร้าง FindNewArticleJob

เราได้พูดคุยไปแล้วว่า SpringScheduler คืออะไร แต่มาทำซ้ำกันอย่างรวดเร็ว: มันเป็นกลไกใน Spring Framework สำหรับการสร้างกระบวนการพื้นหลังที่จะทำงานในเวลาที่เรากำหนดไว้ คุณต้องการอะไรสำหรับสิ่งนี้? ขั้นตอนแรกคือการเพิ่ม คำอธิบายประกอบ @EnableSchedulingให้กับคลาสอินพุตสปริงของเรา:
package com.github.javarushcommunity.jrtb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class JavarushTelegramBotApplication {

   public static void main(String[] args) {
       SpringApplication.run(JavarushTelegramBotApplication.class, args);
   }

}
ขั้นตอนที่สองคือการสร้างคลาส เพิ่มคลาสลงในApplicationContextและสร้างเมธอดในคลาสที่จะเรียกใช้เป็นระยะ เราสร้างแพ็กเกจงานที่ระดับเดียวกับพื้นที่เก็บข้อมูล การบริการ และอื่นๆ และที่นั่นเราสร้าง คลาส FindNewArticleJob :
package com.github.javarushcommunity.jrtb.job;

import com.github.javarushcommunity.jrtb.service.FindNewArticleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
* Job for finding new articles.
*/
@Slf4j
@Component
public class FindNewArticlesJob {

   private final FindNewArticleService findNewArticleService;

   @Autowired
   public FindNewArticlesJob(FindNewArticleService findNewArticleService) {
       this.findNewArticleService = findNewArticleService;
   }

   @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}")
   public void findNewArticles() {
       LocalDateTime start = LocalDateTime.now();

       log.info("Find new article job started.");

       findNewArticleService.findNewArticles();

       LocalDateTime end = LocalDateTime.now();

       log.info("Find new articles job finished. Took seconds: {}",
               end.toEpochSecond(ZoneOffset.UTC) - start.toEpochSecond(ZoneOffset.UTC));
   }
}
หากต้องการเพิ่มคลาสนี้ในบริบทแอปพลิเคชันฉันใช้ คำอธิบาย ประกอบ @Component และเพื่อให้เมธอดภายในคลาสรู้ว่าจำเป็นต้องรันเป็นระยะ ฉันจึงเพิ่มคำอธิบายประกอบให้กับเมธอด: @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}" ) แต่มันจะเป็นค่าอะไร - เราตั้งค่าไว้แล้วในไฟล์ application.properties:
bot.recountNewArticleFixedRate = 900000
โดยค่านี้จะอยู่ในหน่วยมิลลิวินาที จะเป็นเวลา 15 นาที ในวิธีนี้ ทุกอย่างง่ายดาย: ฉันได้เพิ่มหน่วยเมตริกที่เรียบง่ายสุด ๆ สำหรับตัวเองลงในบันทึกเพื่อคำนวณการค้นหาบทความใหม่ เพื่อที่จะได้เข้าใจคร่าวๆ ว่ามันทำงานเร็วแค่ไหน

การทดสอบฟังก์ชันการทำงานใหม่

ตอนนี้เราจะทดสอบกับบอททดสอบของเรา แต่อย่างไร? ฉันจะไม่ลบบทความทุกครั้งเพื่อแสดงว่ามีการแจ้งเตือนเข้ามา? ไม่แน่นอน เราจะแก้ไขข้อมูลในฐานข้อมูลและเปิดแอปพลิเคชัน ฉันจะทดสอบบนเซิร์ฟเวอร์ทดสอบของฉัน หากต้องการทำสิ่งนี้ ให้สมัครเป็นสมาชิกกลุ่มบางกลุ่ม เมื่อการสมัครสมาชิกเสร็จสมบูรณ์ กลุ่มจะได้รับ ID ปัจจุบันของบทความล่าสุด ไปที่ฐานข้อมูลและเปลี่ยนค่าสองบทความกลับ ด้วยเหตุนี้ เราจึงคาดหวังว่าจะมีบทความมากเท่ากับที่เราตั้งค่าLastArticleId เป็นก่อน หน้า "โครงการ Java จาก A ถึง Z": การเพิ่ม Spring Scheduler - 2ต่อไปเราไปที่ไซต์จัดเรียงบทความในกลุ่มโครงการ Java - บทความใหม่ก่อน - และไปที่บทความที่สามจากรายการ: "โครงการ Java จาก A ถึง Z": การเพิ่ม Spring Scheduler - 3ไปที่บทความด้านล่างและจากแถบที่อยู่เราจะได้รหัสบทความ - 3313: "โครงการ Java จาก A ถึง Z": การเพิ่ม Spring Scheduler - 4ถัดไป ไปที่ MySQL Workbench และเปลี่ยน ค่า LastArticleIdเป็น 3313 เรามาดูกันว่ากลุ่มดังกล่าวอยู่ในฐานข้อมูล: "โครงการ Java จาก A ถึง Z": การเพิ่ม Spring Scheduler - 5และสำหรับมันเราจะดำเนินการคำสั่ง: "โครงการ Java จาก A ถึง Z": การเพิ่ม Spring Scheduler - 6เท่านี้ก็เรียบร้อยตอนนี้คุณต้องรอจนกว่าจะมีการเปิดตัวงานครั้งถัดไป ค้นหาบทความใหม่ เราคาดว่าจะได้รับข้อความสองข้อความเกี่ยวกับบทความใหม่จากกลุ่มโครงการ Java อย่างที่พวกเขาพูดกันว่าผลลัพธ์จะเกิดขึ้นไม่นาน: "โครงการ Java จาก A ถึง Z": การเพิ่ม Spring Scheduler - 7ปรากฎว่าบอททำงานตามที่เราคาดหวัง

ตอนจบ

เช่นเคย เราอัปเดตเวอร์ชันใน pom.xml และเพิ่มรายการใน RELEASE_NOTES เพื่อให้บันทึกประวัติการทำงาน และคุณสามารถย้อนกลับไปและทำความเข้าใจสิ่งที่เปลี่ยนแปลงได้ตลอดเวลา ดังนั้นเราจึงเพิ่มเวอร์ชันทีละหนึ่งหน่วย:
<version>0.7.0-SNAPSHOT</version>
และอัปเดต RELEASE_NOTES:
## 0.7.0-SNAPSHOT * JRTB-4: เพิ่มความสามารถในการส่งการแจ้งเตือนเกี่ยวกับบทความใหม่ * JRTB-8: เพิ่มความสามารถในการตั้งค่าผู้ใช้โทรเลขที่ไม่ใช้งาน * JRTB-9: เพิ่มความสามารถในการตั้งค่าผู้ใช้ที่ใช้งานอยู่และ/หรือเริ่มใช้งาน
ตอนนี้คุณสามารถสร้างคำขอดึงและอัปโหลดการเปลี่ยนแปลงใหม่ได้ นี่คือคำขอดึงที่มีการเปลี่ยนแปลงทั้งหมดในสองส่วน: STEP_8 อะไรต่อไป? ดูเหมือนทุกอย่างจะพร้อมแล้วอย่างที่เราบอกไปว่าสามารถเข้าสู่การผลิตได้ แต่ก็ยังมีบางอย่างที่อยากทำ ตัวอย่างเช่น กำหนดค่าการทำงานของผู้ดูแลระบบสำหรับบอท เพิ่มและเพิ่มความสามารถในการตั้งค่า เป็นความคิดที่ดีที่จะอ่านโค้ดก่อนที่จะเสร็จสิ้นและดูว่ามีสิ่งใดบ้างที่สามารถปรับโครงสร้างใหม่ได้ ฉันเห็นการไม่ซิงโครไนซ์ในการตั้งชื่อบทความ/โพสต์แล้ว ในตอนท้ายเราจะทำการย้อนหลังสิ่งที่เราวางแผนไว้และสิ่งที่เราได้รับ และคุณอยากจะทำอะไรในอนาคต? ตอนนี้ ฉันจะแบ่งปันแนวคิดที่ค่อนข้างหยาบคายกับคุณซึ่งสามารถและจะเห็นแสงสว่างของวัน: เพื่อสร้างตัวเริ่มต้น springboot ที่จะมีฟังก์ชันการทำงานทั้งหมดสำหรับการทำงานกับบอตโทรเลขและค้นหาบทความ สิ่งนี้จะทำให้สามารถรวมแนวทางและใช้กับบอทโทรเลขตัวอื่นได้ สิ่งนี้จะทำให้โครงการนี้เข้าถึงผู้อื่นได้มากขึ้นและเป็นประโยชน์ต่อผู้คนมากขึ้น นี่คือหนึ่งในความคิด อีกแนวคิดหนึ่งคือการเจาะลึกการพัฒนาการแจ้งเตือน แต่เราจะพูดถึงเรื่องนี้อีกสักหน่อย ขอขอบคุณทุกท่านที่ให้ความสนใจเช่นเคย: กดไลค์ - ติดตาม - กระดิ่งติดดาวสำหรับโครงการของเราแสดงความคิดเห็นและให้คะแนนบทความ! ขอบคุณทุกคนที่อ่าน

รายการเนื้อหาทั้งหมดในซีรีส์นี้อยู่ที่ตอนต้นของบทความนี้

ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION