Thư viện quy trình nền PHP

Bài viết này là một phần của loạt bài về xây dựng một ứng dụng mẫu — một blog thư viện nhiều hình ảnh — để đo điểm chuẩn và tối ưu hóa hiệu suất. (Xem repo tại đây. )


Trong một bài viết trước, chúng tôi đã thêm tính năng thay đổi kích thước hình ảnh theo yêu cầu. Hình ảnh được thay đổi kích thước theo yêu cầu đầu tiên và được lưu vào bộ đệm để sử dụng sau. Bằng cách này, chúng tôi đã thêm một số chi phí vào lần tải đầu tiên;

Cách tiếp cận được tối ưu hóa sẽ là hiển thị hình thu nhỏ sau khi tạo thư viện. Bạn có thể đang nghĩ, "Được rồi, nhưng sau đó chúng ta sẽ chặn người dùng đang tạo thư viện?" . Người dùng sẽ bối rối về thời gian tải lâu hoặc thậm chí tệ hơn là gặp phải thời gian chờ và/hoặc lỗi nếu hình ảnh quá nặng để xử lý. Giải pháp tốt nhất là di chuyển các tác vụ nặng nề này vào nền

công việc nền

Công việc nền là cách tốt nhất để thực hiện bất kỳ xử lý nặng nào. Chúng tôi có thể thông báo ngay cho người dùng của mình rằng chúng tôi đã nhận được yêu cầu của họ và lên lịch xử lý yêu cầu đó. Tương tự như cách YouTube thực hiện với các video đã tải lên. chúng không thể truy cập được sau khi tải lên. Người dùng cần đợi cho đến khi video được xử lý hoàn tất để xem trước hoặc chia sẻ nó

Xử lý hoặc tạo tệp, gửi email hoặc bất kỳ tác vụ không quan trọng nào khác phải được thực hiện trong nền

Quá trình xử lý nền hoạt động như thế nào?

Có hai thành phần chính trong phương pháp xử lý nền. hàng đợi công việc và (các) công nhân. Ứng dụng tạo các công việc cần được xử lý trong khi công nhân đang chờ đợi và lấy từ hàng đợi một công việc tại một thời điểm

Thư viện quy trình nền PHP

Bạn có thể tạo nhiều phiên bản worker (quy trình) để tăng tốc độ xử lý, cắt một công việc lớn thành các phần nhỏ hơn và xử lý chúng đồng thời. Tùy thuộc vào cách bạn muốn tổ chức và quản lý xử lý nền, nhưng lưu ý rằng xử lý song song không phải là một nhiệm vụ tầm thường. bạn nên quan tâm đến các điều kiện cuộc đua tiềm ẩn và xử lý các nhiệm vụ thất bại một cách duyên dáng

ngăn xếp công nghệ của chúng tôi

Chúng tôi đang sử dụng hàng đợi công việc Beanstalkd để lưu trữ công việc, thành phần Bảng điều khiển Symfony để triển khai công nhân dưới dạng lệnh điều khiển và Người giám sát để quản lý các quy trình của công nhân

Nếu bạn đang sử dụng Homestead Cải tiến, Beanstalkd và Trình giám sát đã được cài đặt nên bạn có thể bỏ qua phần hướng dẫn cài đặt bên dưới

Cài đặt Beanstalkd

Cây đậu là

hàng đợi công việc nhanh với giao diện chung được thiết kế ban đầu để giảm độ trễ của lượt xem trang trong các ứng dụng web có khối lượng lớn bằng cách chạy các tác vụ tốn thời gian một cách không đồng bộ

Có nhiều thư viện máy khách có sẵn mà bạn có thể sử dụng. Trong dự án của chúng tôi, chúng tôi đang sử dụng Pheanstalk

Để cài đặt Beanstalkd trên máy chủ Ubuntu hoặc Debian của bạn, chỉ cần chạy



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
0. Hãy xem trang tải xuống chính thức để tìm hiểu cách cài đặt Beanstalkd trên các hệ điều hành khác

Sau khi cài đặt, Beanstalkd được khởi động dưới dạng daemon, chờ máy khách kết nối và tạo (hoặc xử lý) công việc

/etc/init.d/beanstalkd
Usage: /etc/init.d/beanstalkd {start|stop|force-stop|restart|force-reload|status}

Cài đặt Pheanstalk dưới dạng phụ thuộc bằng cách chạy



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
1

Hàng đợi sẽ được sử dụng cho cả việc tạo và tìm nạp công việc, vì vậy chúng tôi sẽ tập trung việc tạo hàng đợi vào một dịch vụ của nhà máy



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
2



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}

Giờ đây, chúng tôi có thể đưa dịch vụ của nhà máy vào bất cứ nơi nào chúng tôi cần tương tác với hàng đợi Beanstalkd. Chúng tôi đang xác định tên hàng đợi là một hằng số và tham chiếu đến nó khi đưa công việc vào hàng đợi hoặc xem hàng đợi trong công nhân

Giám sát cài đặt

Theo trang chính thức, Người giám sát là một

hệ thống máy khách/máy chủ cho phép người dùng theo dõi và kiểm soát một số quy trình trên các hệ điều hành giống UNIX

Chúng tôi sẽ sử dụng nó để bắt đầu, khởi động lại, mở rộng quy mô và giám sát các quy trình của nhân viên

Cài đặt Trình giám sát trên máy chủ Ubuntu/Debian của bạn bằng cách chạy



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
3. Sau khi cài đặt, Trình giám sát sẽ chạy ẩn dưới dạng daemon. Sử dụng


namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
4 để kiểm soát các quy trình của người giám sát.

$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version

Để kiểm soát các quy trình với Người giám sát, trước tiên chúng tôi phải viết một tệp cấu hình và mô tả cách chúng tôi muốn các quy trình của mình được kiểm soát. Các cấu hình được lưu trữ trong



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
5. Cấu hình Trình giám sát đơn giản dành cho công nhân thay đổi kích thước sẽ trông như thế này

[program:resize-worker]
process_name=%(program_name)s_%(process_num)02d
command=php PATH-TO-YOUR-APP/bin/console app:resize-image-worker
autostart=true
autorestart=true
numprocs=5
stderr_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stderr.log
stdout_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stdout.log

Chúng tôi đang nói với Người giám sát cách đặt tên cho các quy trình được sinh ra, đường dẫn đến lệnh sẽ được chạy, để tự động bắt đầu và khởi động lại các quy trình, chúng tôi muốn có bao nhiêu quy trình và nơi ghi nhật ký đầu ra. Tìm hiểu thêm về cấu hình Người giám sát tại đây

Thay đổi kích thước hình ảnh trong nền

Khi chúng tôi đã thiết lập cơ sở hạ tầng của mình (tôi. e. , Beanstalkd và Người giám sát đã cài đặt), chúng tôi có thể sửa đổi ứng dụng của mình để thay đổi kích thước hình ảnh trong nền sau khi thư viện được tạo. Để làm được như vậy, chúng ta cần phải

  • cập nhật logic phục vụ hình ảnh trong
    
    
    namespace App\Service;
    
    use Pheanstalk\Pheanstalk;
    
    class JobQueueFactory
    {
        private $host = 'localhost';
        private $port = '11300';
    
        const QUEUE_IMAGE_RESIZE = 'resize';
    
        public function createQueue(): Pheanstalk
        {
            return new Pheanstalk($this->host, $this->port);
        }
    }
    
    6
  • triển khai thay đổi kích thước công nhân dưới dạng lệnh điều khiển
  • tạo cấu hình Người giám sát cho công nhân của chúng tôi
  • cập nhật đồ đạc và thay đổi kích thước hình ảnh trong lớp đồ đạc

Cập nhật logic cung cấp hình ảnh

Cho đến nay, chúng tôi đã thay đổi kích thước hình ảnh theo yêu cầu đầu tiên. nếu tệp hình ảnh cho kích thước được yêu cầu không tồn tại, nó sẽ được tạo nhanh chóng

Bây giờ chúng tôi sẽ sửa đổi để chỉ trả về phản hồi hình ảnh cho kích thước được yêu cầu nếu tệp hình ảnh đã thay đổi kích thước tồn tại (i. e. , chỉ khi hình ảnh đã được thay đổi kích thước)

Nếu không, ứng dụng sẽ trả về phản hồi hình ảnh giữ chỗ chung cho biết rằng hình ảnh đang được thay đổi kích thước vào lúc này. Lưu ý rằng phản hồi hình ảnh trình giữ chỗ có các tiêu đề kiểm soát bộ đệm khác nhau, vì chúng tôi không muốn lưu hình ảnh trình giữ chỗ vào bộ đệm;

Chúng tôi sẽ tạo một sự kiện đơn giản có tên là GalleryCreatedEvent với ID thư viện dưới dạng tải trọng. Sự kiện này sẽ được gửi trong vòng



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
7 sau khi Thư viện được tạo thành công

/etc/init.d/beanstalkd
Usage: /etc/init.d/beanstalkd {start|stop|force-stop|restart|force-reload|status}
2

Ngoài ra, chúng tôi sẽ cập nhật thông báo flash với “Hình ảnh hiện đang được xử lý. ” để người dùng biết rằng chúng tôi vẫn còn một số việc phải làm với hình ảnh của họ trước khi chúng sẵn sàng

Chúng tôi sẽ tạo người đăng ký sự kiện GalleryEventSubscriber sẽ phản ứng với



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
8 và yêu cầu công việc thay đổi kích thước cho mọi Hình ảnh trong Thư viện mới được tạo

/etc/init.d/beanstalkd
Usage: /etc/init.d/beanstalkd {start|stop|force-stop|restart|force-reload|status}
4

Giờ đây, khi người dùng tạo thành công thư viện, ứng dụng sẽ hiển thị trang thư viện, nhưng một số hình ảnh sẽ không được hiển thị dưới dạng hình thu nhỏ của chúng khi vẫn chưa sẵn sàng

Thư viện quy trình nền PHP

Sau khi công nhân hoàn tất việc thay đổi kích thước, lần làm mới tiếp theo sẽ hiển thị toàn bộ trang Thư viện

Triển khai công nhân thay đổi kích thước dưới dạng lệnh điều khiển

Công nhân là một quy trình đơn giản thực hiện cùng một công việc cho mọi công việc anh ta nhận được từ hàng đợi. Việc thực thi worker bị chặn tại cuộc gọi



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
9 cho đến khi một trong hai công việc được dành riêng cho worker đó hoặc thời gian chờ xảy ra

Chỉ một công nhân có thể nhận và xử lý một Công việc. Công việc thường chứa trọng tải — e. g. , chuỗi hoặc mảng/đối tượng được tuần tự hóa. Trong trường hợp của chúng tôi, đó sẽ là UUID của Thư viện được tạo

Một công nhân đơn giản trông như thế này

/etc/init.d/beanstalkd
Usage: /etc/init.d/beanstalkd {start|stop|force-stop|restart|force-reload|status}
6

Bạn có thể nhận thấy rằng worker sẽ thoát sau một thời gian chờ xác định hoặc khi một công việc được xử lý. Chúng ta có thể gói worker logic trong một vòng lặp vô hạn và để nó lặp lại công việc của nó vô thời hạn, nhưng điều đó có thể gây ra một số vấn đề như hết thời gian chờ kết nối cơ sở dữ liệu sau một thời gian dài không hoạt động và khiến việc triển khai trở nên khó khăn hơn. Để ngăn chặn điều đó, vòng đời công nhân của chúng tôi sẽ kết thúc sau khi nó hoàn thành một tác vụ. Người giám sát sau đó sẽ khởi động lại một công nhân như một quy trình mới

Hãy xem ResizeImageWorkerCommand để có một bức tranh rõ ràng về cách cấu trúc lệnh Worker. Công nhân được triển khai theo cách này cũng có thể được bắt đầu thủ công dưới dạng lệnh bảng điều khiển Symfony.

$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
0

Tạo cấu hình Người giám sát

Chúng tôi muốn công nhân của mình bắt đầu tự động, vì vậy chúng tôi sẽ đặt lệnh

$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
1 trong cấu hình.
Vì worker phải được khởi động lại sau khi hết thời gian chờ hoặc một tác vụ xử lý thành công, nên chúng tôi cũng sẽ đặt lệnh
$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
2.

Phần tốt nhất về xử lý nền là dễ dàng xử lý song song. Chúng tôi có thể đặt một chỉ thị

$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
3 và Người giám sát sẽ tạo ra năm phiên bản công nhân của chúng tôi. Họ sẽ đợi các công việc và xử lý chúng một cách độc lập, cho phép chúng tôi mở rộng quy mô hệ thống của mình một cách dễ dàng. Khi hệ thống của bạn phát triển, có thể bạn sẽ cần tăng số lượng quy trình. Vì chúng tôi sẽ có nhiều quy trình đang chạy, nên chúng tôi cần xác định cấu trúc của tên quy trình, vì vậy chúng tôi đang thiết lập một chỉ thị
$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
4

Cuối cùng nhưng không kém phần quan trọng, chúng tôi muốn lưu trữ kết quả đầu ra của nhân viên để chúng tôi có thể phân tích và gỡ lỗi nếu có sự cố xảy ra. Chúng tôi sẽ xác định các đường dẫn

$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
5 và
$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
6

Cấu hình Người giám sát hoàn chỉnh cho công nhân thay đổi kích thước của chúng tôi trông như thế này

[program:resize-worker]
process_name=%(program_name)s_%(process_num)02d
command=php PATH-TO-YOUR-APP/bin/console app:resize-image-worker
autostart=true
autorestart=true
numprocs=5
stderr_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stderr.log
stdout_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stdout.log

Sau khi tạo (hoặc cập nhật) tệp cấu hình nằm trong thư mục



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
5, bạn phải yêu cầu Người giám sát đọc lại và cập nhật cấu hình của nó bằng cách thực hiện các lệnh sau



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
6

Nếu bạn đang sử dụng Homestead Cải thiện (và bạn nên. ), bạn có thể sử dụng tập lệnh/trình giám sát thiết lập. sh để tạo cấu hình Người giám sát cho dự án này.

$ sudo supervisorctl help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail
avail  fg        pid   remove  shutdown  status  update
clear  maintail  quit  reread  signal    stop    version
8

Cập nhật lịch thi đấu

Hình thu nhỏ của hình ảnh sẽ không được hiển thị trong yêu cầu đầu tiên nữa, vì vậy chúng tôi cần yêu cầu hiển thị cho mọi Hình ảnh một cách rõ ràng khi chúng tôi đang tải đồ đạc của mình trong lớp đồ đạc LoadGallerieData



namespace App\Service;

use Pheanstalk\Pheanstalk;

class JobQueueFactory
{
    private $host = 'localhost';
    private $port = '11300';

    const QUEUE_IMAGE_RESIZE = 'resize';

    public function createQueue(): Pheanstalk
    {
        return new Pheanstalk($this->host, $this->port);
    }
}
8

Bây giờ, bạn sẽ cảm thấy việc tải đồ đạc bị chậm lại như thế nào và đó là lý do tại sao chúng tôi đã chuyển nó vào nền thay vì buộc người dùng của chúng tôi phải đợi cho đến khi quá trình này hoàn tất

Các mẹo và thủ thuật

Công nhân đang chạy ở chế độ nền để ngay cả sau khi bạn triển khai phiên bản mới của ứng dụng, bạn sẽ có các công nhân lỗi thời chạy cho đến khi họ không khởi động lại lần đầu tiên

Trong trường hợp của chúng tôi, chúng tôi phải đợi tất cả nhân viên của mình hoàn thành nhiệm vụ hoặc hết thời gian chờ (5 phút) cho đến khi chúng tôi chắc chắn rằng tất cả nhân viên của mình đều được cập nhật. Hãy nhận biết điều này khi tạo thủ tục triển khai

Chia sẻ bài viết này

Zoran Antolovic

Zoran Antolović là kỹ sư và người giải quyết vấn đề đến từ Croatia. CTO, Chủ sở hữu khởi nghiệp, nhà văn, diễn giả truyền động lực và thường là người tốt. Anh ấy yêu thích năng suất, ghét sự phân tâm và đôi khi làm quá mọi thứ

    công việc nềnxử lý nềncây đậudhomesteadđược cải thiệnhiệu suấthiệu suất-trung tâmhiệu suất-hướng dẫnNgười giám sát

    Làm cách nào để chạy quy trình nền trong PHP?

    Vì vậy, để chạy bất kỳ quy trình nền nào từ PHP, chúng ta chỉ cần sử dụng hàm exec hoặc shell_exec để thực thi bất kỳ lệnh đầu cuối nào và trong lệnh đó, chúng ta chỉ cần thêm & vào . .

    Làm thế nào một quá trình được thực hiện như quá trình nền?

    Các quy trình chạy độc lập với người dùng được gọi là quy trình nền. Các chương trình và lệnh chạy như các quy trình tiền cảnh theo mặc định. Để chạy một quy trình trong nền, hãy đặt dấu và (&) ở cuối tên lệnh mà bạn sử dụng để bắt đầu quy trình .