1.ScheduleMaster Là Gì
ScheduleMaster là hệ thống quản lý công việc phân tán, được viết bởi một nhà phát triển trong nước. Hiểu đơn giản, ScheduleMaster là một khung để quản lý tập trung các công việc từ các hệ thống khác nhau.
Ví dụ, nếu chúng ta có nhiều hệ thống với các tác vụ riêng biệt và mỗi hệ thống phải tự duy trì thì sẽ rất khó khăn khi số lượng tác vụ tăng lên. Điều này làm gia tăng chi phí bảo trì và kỹ thuật. Vì vậy, cần sử dụng một khung hệ thống phân tán để quản lý tập trung.
Chúng ta sẽ học về ScheduleMaster, một trong những khung hệ thống phân tán tốt nhất hiện nay.
2.Sử Dụng ScheduleMaster
1.Tạo vài dịch vụ vi mô bằng ASP.NET Core Web API cho các chức năng như chấm công, tính lương, email, và tin nhắn.
2.Tải xuống mã nguồn mở của ScheduleMaster và dùng nó để điều phối các giao diện API của chúng ta.
- sqlserver: "Persist Security Info = False; User ID =sa; Password =abcdef; Initial Catalog =job_master; Server =."
- postgresql: "Server=localhost;Port=5432;Database=job_master;User Id=admin;Password=abcdef;Pooling=true;MaxPoolSize=20;"
- mysql: "Data Source=localhost;Database=job_master;User ID=root;Password=abcdef;pooling=true;CharSet=utf8mb4;port=3306;sslmode=none;TreatTinyAsBoolean=true"
Chỉnh sửa cấu hình Host và hỗ trợ cơ sở dữ liệu, mặc định khung dùng MySQL.
Chỉnh sửa cấu hình Web và hỗ trợ cơ sở dữ liệu, mặc định khung dùng MySQL.
3.Vào thư mục xuất bản của dự án Hos.ScheduleMaster.Web, chạy lệnh dotnet Hos.ScheduleMaster.Web.dll để khởi động dự án, lúc này cơ sở dữ liệu sẽ được tạo ra.
Tài khoản đăng nhập: admin
Mật khẩu: 654321
4.Vào thư mục xuất bản của dự án Hos.ScheduleMaster.QuartzHost, thực thi lệnh sau để khởi động:
dotnet Hos.ScheduleMaster.QuartzHost.dll --urls http://*:30004
1.Cấu Hình Nhiệm Vụ HTTP
5.Sau khi sẵn sàng, vào phần quản lý nút trên giao diện web, bạn sẽ thấy nút chính 30001 và giao diện nhiệm vụ 30003 đang hoạt động.
6.Sử dụng menu danh sách nhiệm vụ, tạo nhiệm vụ lập lịch, cấu hình thông tin cơ bản và siêu dữ liệu, sau đó lưu lại để bắt đầu thực hiện nhiệm vụ.
2.Cấu Hình Nhiệm Vụ Tập Tin Chương Trình
1.Tạo một thư viện lớp, cài đặt thư viện ScheduleMaster, tạo một lớp kế thừa TaskBase, triển khai phương thức trừu tượng và biên dịch chương trình.
namespace JobServiceLib
{
class ProgramTask : TaskBase
{
public override void Execute(JobContext context)
{
context.WriteLog("Nhiệm vụ tập tin chương trình");
}
}
}
2.Tìm thư mục debug, loại bỏ tập tin Base, sau đó thêm nó vào tệp nén.
3.Trong giao diện web, tìm phần cấu hình nhiệm vụ, chọn tập tin chương trình để cấu hình cơ bản, cấu hình siêu dữ liệu, nhập tên tập tin chương trình, sau đó tải lên và lưu để chạy.
3.Sử Dụng API Để Kết Nối Nhiệm Vụ
Để tích hợp dễ dàng hơn, ScheduleMaster không chỉ cho phép tạo nhiệm vụ qua giao diện mà còn cung cấp WebAPI để kết nối từ hệ thống kinh doanh.
1.Quy Trình Kết Nối API Server
- Tạo tài khoản người dùng chuyên dụng trong giao diện.
- Sử dụng tên người dùng làm giá trị
api_usertrong header HTTP. - Sử dụng khóa bí mật đã băm làm giá trị
api_secrettrong header HTTP, tính toán theo quy tắc: ghép chuỗi{username}{hash(password)}{username}, sau đó băm chuỗi kết quả bằng hàm MD5 32 bit chữ thường. - Thực hiện yêu cầu HTTP dạng form, nếu không hợp lệ sẽ trả về lỗi 401 - Unauthorized.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("api_user", "admin");
client.DefaultRequestHeaders.Add("api_secret", HashHelper.MD5($"admin{HashHelper.MD5("654321")}}admin"));
Các giao diện đều trả về định dạng thống nhất với các trường sau:
| Tên Trường | Kiểu Dữ Liệu | Mô Tả |
|---|---|---|
| Success | bool | Có thành công hay không |
| Status | int | Trạng thái kết quả, 0-Thất bại 1-Thành công 2-Lỗi đăng nhập 3-Lỗi tham số 4-Lỗi dữ liệu |
| Message | string | Thông điệp trả về |
| Data | object | Dữ liệu trả về |
2.Tạo Nhiệm Vụ Tập Tin Chương Trình
Không thể tải tập tin chương trình qua API nên cần đảm bảo tập tin đã được tải lên trước khi khởi động nhiệm vụ.
- Địa chỉ giao diện:
http://yourip:30001/api/job/create - Loại yêu cầu:
POST - Định dạng tham số:
application/x-www-form-urlencoded - Kết quả trả về: ID nhiệm vụ khi tạo thành công
- Danh sách tham số:
| Tên Trường | Kiểu Dữ Liệu | Bắt Buộc | Mô Tả |
|---|---|---|---|
| TypeMeta | int | Có | Loại nhiệm vụ, cố định là 1 |
| Title | string | Có | Tên nhiệm vụ |
| RunLoop | bool | Có | Có chạy tuần hoàn hay không |
| CronExpression | string | Không | Biểu thức cron, nếu RunLoop là true thì bắt buộc |
| AssemblyName | string | Có | Tên tập tin chương trình |
| ClassName | string | Có | Tên lớp thực thi, gồm toàn bộ namespace |
| StartDate | DateTime | Có | Thời gian bắt đầu nhiệm vụ |
| EndDate | DateTime | Không | Thời gian dừng nhiệm vụ, nếu trống thì không giới hạn |
| Description | string | Không | Mô tả nhiệm vụ |
| Keepers | List<int> | Không | ID giám sát viên |
| Nexts | List<guid> | Không | ID nhiệm vụ con |
| Executors | List<string> | Không | Tên nút thực thi |
| RunNow | bool | Không | Có chạy ngay sau khi tạo thành công hay không |
| Params | List<JobParam> | Không | Danh sách tham số tùy chỉnh, cũng có thể truyền chuỗi JSON qua trường CustomParamsJson |
JobParam:
| Tên Trường | Kiểu Dữ Liệu | Bắt Buộc | Mô Tả |
|---|---|---|---|
| ParamKey | string | Có | Tên tham số |
| ParamValue | string | Có | Giá trị tham số |
| ParamDescription | string | Không | Mô tả tham số |
HttpClient client = new HttpClient();
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>();
args.Add(new KeyValuePair<string, string>("TypeMeta", "1"));
args.Add(new KeyValuePair<string, string>("RunLoop", "true"));
args.Add(new KeyValuePair<string, string>("CronExpression", "30 0/7 * * * ?"));
args.Add(new KeyValuePair<string, string>("Description", "Tạo bởi Xunit Tester"));
args.Add(new KeyValuePair<string, string>("StartDate", DateTime.Today.ToString("yyyy-MM-dd HH:mm:ss")));
args.Add(new KeyValuePair<string, string>("Title", "Nhiệm vụ kiểm tra tập tin chương trình"));
args.Add(new KeyValuePair<string, string>("AssemblyName", "Hos.ScheduleMaster.Sample"));
args.Add(new KeyValuePair<string, string>("ClassName", "Hos.ScheduleMaster.Sample.Simple"));
args.Add(new KeyValuePair<string, string>("CustomParamsJson", "[{\"ParamKey\":\"k1\",\"ParamValue\":\"1111\",\"ParamDescription\":\"r1\"},{\"ParamKey\":\"k2\",\"ParamValue\":\"2222\",\"ParamDescription\":\"r2\"}]"));
args.Add(new KeyValuePair<string, string>("Keepers", "1"));
args.Add(new KeyValuePair<string, string>("Keepers", "2"));
//args.Add(new KeyValuePair<string, string>("Nexts", ""));
//args.Add(new KeyValuePair<string, string>("Executors", ""));
HttpContent reqContent = new FormUrlEncodedContent(args);
var response = await client.PostAsync("http://localhost:30001/api/Job/Create", reqContent);
var content = await response.Content.ReadAsStringAsync();
Debug.WriteLine(content);
3.Tạo Nhiệm Vụ HTTP
- Địa chỉ giao diện:
http://yourip:30001/api/job/create - Loại yêu cầu:
POST - Định dạng tham số:
application/x-www-form-urlencoded - Kết quả trả về: ID nhiệm vụ khi tạo thành công
- Danh sách tham số:
| Tên Trường | Kiểu Dữ Liệu | Bắt Buộc | Mô Tả |
|---|---|---|---|
| TypeMeta | int | Có | Loại nhiệm vụ, cố định là 2 |
| Title | string | Có | Tên nhiệm vụ |
| RunLoop | bool | Có | Có chạy tuần hoàn hay không |
| CronExpression | string | Không | Biểu thức cron, nếu RunLoop là true thì bắt buộc |
| StartDate | DateTime | Có | Thời gian bắt đầu nhiệm vụ |
| EndDate | DateTime | Không | Thời gian dừng nhiệm vụ, nếu trống thì không giới hạn |
| Description | string | Không | Mô tả nhiệm vụ |
| HttpRequestUrl | string | Có | Địa chỉ yêu cầu |
| HttpMethod | string | Có | Phương thức yêu cầu, chỉ hỗ trợ GET\\POST\\PUT\\DELETE |
| HttpContentType | string | Có | Định dạng tham số, chỉ hỗ trợ application/json và application/x-www-form-urlencoded |
| HttpHeaders | string | Không | Thiết lập tiêu đề yêu cầu tùy chỉnh, chuỗi JSON của danh sách JobParam |
| HttpBody | string | Có | Nếu là tham số json thì là chuỗi JSON tương ứng; nếu là tham số form thì là chuỗi JSON của danh sách JobParam. |
| Keepers | List<int> | Không | ID giám sát viên |
| Nexts | List<guid> | Không | ID nhiệm vụ con |
| Executors | List<string> | Không | Tên nút thực thi |
| RunNow | bool | Không | Có chạy ngay sau khi tạo thành công hay không |
HttpClient client = new HttpClient();
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>();
args.Add(new KeyValuePair<string, string>("TypeMeta", "2"));
args.Add(new KeyValuePair<string, string>("RunLoop", "true"));
args.Add(new KeyValuePair<string, string>("CronExpression", "20 0/7 * * * ?"));
args.Add(new KeyValuePair<string, string>("Description", "Tạo bởi Xunit Tester"));
args.Add(new KeyValuePair<string, string>("StartDate", DateTime.Today.ToString("yyyy-MM-dd HH:mm:ss")));
args.Add(new KeyValuePair<string, string>("Title", "Nhiệm vụ kiểm tra HTTP"));
args.Add(new KeyValuePair<string, string>("HttpRequestUrl", "http://localhost:56655/api/1.0/value/jsonpost"));
args.Add(new KeyValuePair<string, string>("HttpMethod", "POST"));
args.Add(new KeyValuePair<string, string>("HttpContentType", "application/json"));
args.Add(new KeyValuePair<string, string>("HttpHeaders", "[]"));
args.Add(new KeyValuePair<string, string>("HttpBody", "{ \"Posts\": [{ \"PostId\": 666, \"Title\": \"tester\", \"Content\":\"testtesttest\" }], \"BlogId\": 111, \"Url\":\"qweqrrttryrtyrtrtrt\" }"));
HttpContent reqContent = new FormUrlEncodedContent(args);
var response = await client.PostAsync("http://localhost:30001/api/Job/Create", reqContent);
var content = await response.Content.ReadAsStringAsync();
Debug.WriteLine(content);
4.Tạo Nhiệm Vụ Trễ
- Địa chỉ giao diện:
http://yourip:30001/api/delayjob/create - Loại yêu cầu:
POST - Định dạng tham số:
application/x-www-form-urlencoded - Kết quả trả về: ID nhiệm vụ khi tạo thành công
- Danh sách tham số:
| Tên Trường | Kiểu Dữ Liệu | Bắt Buộc | Mô Tả |
|---|---|---|---|
| SourceApp | string | Có | Nguồn |
| Topic | string | Có | Chủ đề |
| ContentKey | string | Có | Khóa nội dung nghiệp vụ |
| DelayTimeSpan | int | Có | Thời gian trễ tương đối |
| DelayAbsoluteTime | DateTime | Có | Thời gian trễ tuyệt đối |
| NotifyUrl | string | Có | Địa chỉ gọi lại |
| NotifyDataType | string | Có | Định dạng tham số gọi lại, chỉ hỗ trợ application/json và application/x-www-form-urlencoded |
| NotifyBody | string | Có | Tham số gọi lại, chuỗi JSON |
for (int i = 0; i < 5; i++)
{
int rndNum = new Random().Next(20, 500);
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>();
args.Add(new KeyValuePair<string, string>("SourceApp", "TestApp"));
args.Add(new KeyValuePair<string, string>("Topic", "TestApp.Trade.TimeoutCancel"));
args.Add(new KeyValuePair<string, string>("ContentKey", i.ToString()));
args.Add(new KeyValuePair<string, string>("DelayTimeSpan", rndNum.ToString()));
args.Add(new KeyValuePair<string, string>("DelayAbsoluteTime", DateTime.Now.AddSeconds(rndNum).ToString("yyyy-MM-dd HH:mm:ss")));
args.Add(new KeyValuePair<string, string>("NotifyUrl", "http://localhost:56655/api/1.0/value/delaypost"));
args.Add(new KeyValuePair<string, string>("NotifyDataType", "application/json"));
args.Add(new KeyValuePair<string, string>("NotifyBody", "{ \"Posts\": [{ \"PostId\": 666, \"Title\": \"tester\", \"Content\":\"testtesttest\" }], \"BlogId\": 111, \"Url\":\"qweqrrttryrtyrtrtrt\" }"));
HttpContent reqContent = new FormUrlEncodedContent(args);
var response = await client.PostAsync("http://localhost:30001/api/DelayJob/Create", reqContent);
var content = await response.Content.ReadAsStringAsync();
Debug.WriteLine(content);
}
4.Phân Tích Khung Hệ Thống
1.Thiết Kế Toàn Cục
Theo sơ đồ thiết kế trên trang web chính thức và quy trình vận hành, có thể tổng kết quy trình toàn cục của nhiệm vụ như sau: Client ——> Master ——> Work ——> Thực Hiện Nhiệm Vụ. Về mặt kiến trúc, ý tưởng lớn là tách rời nhiệm vụ khỏi dịch vụ đơn lẻ để quản lý và gọi độc lập.
2.Phân Tích Master Và Work
Chúng ta phân tích từ nút chính, nút phụ, và bảng dữ liệu. Quan hệ giữa Master và Work là hỗ trợ lẫn nhau. Master thêm nhiệm vụ vào cơ sở dữ liệu, Work lấy nhiệm vụ từ cơ sở dữ liệu, tạo cấu hình và thực thi bằng Quartz.
Master và Work ngoài việc cung cấp giao diện người dùng và xử lý hậu trường, còn thực hiện các công việc cụ thể như sau:
Master
- 1.Phân bổ nhiệm vụ và chọn nút
- 2.Kiểm tra sức khỏe của nút Work, chuyển đổi nhiệm vụ khi gặp sự cố
Work
- 1.Lấy thông tin cấu hình nhiệm vụ
- 2.Chạy nhiệm vụ bằng Quartz theo cấu hình
- 3.Gọi phản chiếu tập tin chương trình
- 4.Gọi giao diện HTTP bằng HttpClient