Maximum execution time of 60 seconds exceeded
Lỗi này anh em hay nói vui với nhau là "code thối", dẫn tới hiệu năng kém và chạy quá thời gian cho phép của server. Để làm nó chạy lại được thì rất dễ - đó là config PHP bằng mấy tham số như dưới đây:
- max_execution_time
- memory_limit
Việc sửa đổi các cấu hình thì sẽ gây ra các vấn đề kéo theo như một request được server xử lý quá lâu sẽ dẫn tới chiếm hết các tài nguyên server - nó sẽ thành điểm tuyệt vời để hacker nhắm vào giúp DOS server của bạn dễ hơn. Nếu config một con số chưa đủ cao (theo thời gian số lượng dữ liệu tăng lên) thì lỗi sẽ lại tái diễn.
Các trên không giải quyết triệt để vấn đề. Vấn đề cốt lõi ở đây là chất lượng code. Bạn nên refactor lại code, áp dụng thêm các giải pháp khác nhau như: Paginate, cache, lazy-loading... để code xử lý trả ra kết quả thật nhanh. Từ đó dù dữ liệu nhiều hay ít thì server vẫn hoạt động ổn định.
Giúp em về Cron job Task Scheduler trong laravel với các anh :(((
Bạn sử dụng Task Scheduler của Laravel là đúng rồi nhé. Kết hợp dùng Task Scheduler + Artisan Command sẽ là sự lựa chọn tuyệt vời. Cách thức thực hiện thì sẽ làm như sau:
- Tạo một Artisan Command theo hướng dẫn: https://laravel.com/docs/9.x/artisan VD: SendDailyReport
- Viết code logic của bạn vào trong method
handle
trong command - Cấu hình Task Scheduler để Laravel tự động chạy command trên theo hướng dẫn: https://laravel.com/docs/9.x/scheduling#scheduling-artisan-commands
- Cấu hình Crontab trên server để server tự động chạy Task Scheduler của Laravel
Bạn làm theo các hướng dẫn của mình bên trên là được.
Làm thế nào biết record được tạo ra khi nào?
Nếu bạn không có cột date nào để tự lưu thì không có cách nào để biết record đó tạo khi nào đâu nhé. Giờ chỉ có nước là tìm qua các bảng liên quan xem có chỗ nào lưu date mà phù hợp để lấy ra dùng thay thế thôi.
Kiểu như bảng user không có cột created_at (thời gian tạo) nhưng lại có một bảng activation_email_logs có cột sent_at (thời gian mail xác thực tạo tài khoản được gửi) thì có thể bê cái sent_at về làm cho created_at để chống cháy.
Hỏi về xây dựng website
Dùng luôn wordpress nha bạn.
Filter theo Quý( 3 tháng bằng 1 quý) trong react js
- Về giao diện thì em dùng mấy thư viện datepicker để build phần giao diện chọn ngày, thường thì trong các UI Library như Ant design, material ui... đều có sẵn.
- Hoặc nếu chỉ muốn xem theo quý thì em có thể để UI dạng Select > Option, một mục cho chọn năm và một mục cho chọn quý. VD: year=2022 ; quarter=Q2
- Sau đó thì gửi kèm những tham số này theo API thôi.
Về phần backend:
- Nếu gửi payload là quarter=Q2 -> quý 2 thì thêm bước chuyển đổi giá trị Q2 thành thời gian bắt đầu và thời gian kết thúc quý. Q2 thì lúc nào cũng cố định là tháng 4, 5, 6.
- Sử dụng các hàm
startOfQuarter
vàendOfQuarter
trong các thư viện xử lý ngày tháng cho tiện - Nếu muốn lấy ra tổng số KH và tổng số giao dịch thì hãy viết một cái API mới. Dùng hàm
COUNT
trong sql để đếm và dùng where để lọc đúng quý.
Hỏi về hàm dispatch Job trong laravel
Bạn thử đặt lại tên Job xem nhé:
use App\Jobs\CreateSqlJob;
dispatch(new CreateSqlJob($databaseName, $data['company_name']));
[Rails] Tạo và quản lý jobs với dynamic thời gian chạy job và Timezone
Mình thấy cách này cũng ổn mà.
- Tạo một cái cronjob để cứ 1p / lần sẽ chạy cái một cái command của app.
- Cái command này của app sẽ check xem schedule nào thoả mãn để xử lý.
- Chỗ này bạn nên có cột
status
cho bảngschedule
và tạo index phù hợp để tối ưu cho truy vấn sql - Trong cái command, phần logic chạy cho mỗi chiến dịch quảng cáo phù hợp bạn đưa vào hệ thống queue-job. Mình sẽ scale bằng cách có nhiều queue và nhiều queue-workers.
- Nếu chưa đáp ứng được mà muốn scale hơn nữa thì bạn có thể chuyển hẳn phần chạy chíến dịch quảng cáo thành service riêng, viết bằng go hoặc rust chẳng hạn.
Bạn có thể chia sẻ thêm về vấn đề performance mà bạn đang gặp phải được không?
😭😭😭😭MỌI NGƯỜI ƠI CỨU EM, em lỡ tay bấm nhầm Undo changes:((
-
Nếu bạn chưa tạo commit cho các file đó thì không có cách nào khác để lấy lại các file đã xóa bằng git đâu. TH này chắc chỉ có nước dùng các phần mềm khôi phục file giống như trong bài này: https://www.thegioididong.com/hoi-dap/top-20-phan-mem-khoi-phuc-du-lieu-da-xoa-tren-o-cung-usb-1301314.
-
Nếu bạn đã từng tạo commit cho các file đó thì có thể khôi phục lại được về các commit đó. Bạn dùng lệnh:
git reflog -20
Nó sẽ hiện lịch sử 20 bước các sửa đổi của source code trong git. (Bỏ cái -20 cũng được). Sau đó dùng lệnh checkout
để quay về phiên bản mong muốn.
Mình ví dụ như này:
> git reflog
d2a2f9a (HEAD -> fix, master) HEAD@{0}: checkout: moving from master to fix
d2a2f9a (HEAD -> fix, master) HEAD@{1}: rebase -i (finish): returning to refs/heads/master
d2a2f9a (HEAD -> fix, master) HEAD@{2}: rebase -i (squash): refactor!: remove port binding to localhost for database services
a5968d7 HEAD@{3}: rebase -i (start): checkout HEAD~2
19aa40a (origin/master, origin/HEAD) HEAD@{4}: commit (amend): feat!: traefik has been compatible with multi-projects
d90af3a HEAD@{5}: commit: feat: traefik has been compatible with multi-projects
514f3d4 (upstream/master) HEAD@{6}: clone: from git@github.com:kimyvgy-forks/docker-php-development.git
> git checkout HEAD@{5}
Làm sao để tảo một editor cho markdown giống viblo bằng nextjs?
Viblo thì sử dụng thư viện Simple MDE, ngày trước mình cũng từng trả lời một bạn câu hỏi này trên Viblo. Bạn có thể đọc lại câu trả lời của mình tại: https://viblo.asia/a/J3ZgM9xL5mB
Rất lâu về trước cũng có một bác viết bài: Làm trình soạn thảo giống Viblo. Bạn thử đọc nhé.
Ngoài ra, có nhiều editor khác cũng dùng markdown như Editor.js, Marktext. Mình khá là thích editor của Marktext, khi dùng nó rất tiện và trực quan hơi Editor hiện tại của Viblo. Đối với Marktext thì họ public editor đó tại đây: https://github.com/marktext/muya
hỏi sữa lỗi php trên byethost
Cái này là code của bạn bị lỗi. Bạn gửi code của bạn lên đây mọi người còn có thể hỗ trợ được chứ đăng cái ảnh như này thì chẳng ai có thời gian đoán mò để giúp bạn đâu.
Check điều kiện bằng route params trong vue 3?
Cái điều kiện hiển thị Navigation dựa theo path của route thì bạn dùng computed
sẽ là hợp lý. Tham khảo code:
<script setup lang="ts">
import { ref, computed} from "vue";
import { useRoute } from 'vue-router';
// ...
const route = useRoute();
const checkLogin = computed(() => route.name === 'login');
</script>
Auth request với request body trong nginx
Không thêm được vào request body đâu bạn ạ vì nó chỉ dùng GET như bạn nói thôi. Thêm vào cái module đó nó cũng xóa bỏ.
Nhưng mà vẫn có thể truyền dữ liệu bằng cách chèn dữ liệu vào header. Bạn thử dùng header xem.
Cách tự động backup db sang một server khác
Cách 1
Một cách khá đơn giản đó là public cái port 3306 + whitelist IP cho con server chạy backup để có truy cập vào SQL server của môi trường test. Trong cái script backup thì sau khi backup thành công, bạn chạy lệnh CLI import file vào thẳng con SQL server test luôn.
Cách 2
Bạn có thể setup NFS - Network File System, giúp có thể chia sẻ file giữa các server với nhau. Khi đó thì ở server test bạn sẽ đọc được file backup.sql ở server web.
Lúc này thì bạn đặt crontab để tự động import file SQL cho môi trường test theo lịch. VD: Back up DB lúc 1h, import database vào môi trường test sẽ là 2h. Bạn áng chừng thời gian rồi để cách nhau ra như vậy là OK. Tên file SQL bạn để theo ngày tháng để khi import còn check được là có tồn tại file của ngày đó hay không nha.
hỏi về Dockerfile, docker-compose.yml
Lệnh COPY như bạn hiểu là đúng rồi nha bạn. Nó lỗi là do bạn cấu hình context là thư mục ./docker/nodejs
. Docker nó sẽ chỉ tìm được các file trong thư mục này. App của bạn chứa source code ở bên ngoài nên bạn để context là thư mục gốc. Chỉ cần chỉ ra path tới file Dockerfile thôi.
Bạn sửa lại thành như này và chuyển file docker-compose.yml ra ngoài thư mục gốc nhé:
version: '3.7'
services:
app:
build:
dockerfile: ./docker/nodejs/Dockerfile
ports:
- 3000:3000
Sửa lệnh COPY:
COPY . .
hỏi frehost
Không có cái nào free đâu, host mấy web static thì may ra còn có mấy cái như dùng Github Pages, Cloudflare, Netlify... )
hỏi mysql workbeck
Bạn dùng Xampp thì bật PHPMyadmin lên cho nhanh. Nhìn chung hai cái cũng chỉ là để access vào database mà thôi. PHPMyadmin còn dễ dùng và có UI trực quan hơn.
Còn không thì bạn tham khảo hướng dẫn này để tạo connection tới MySQL của Xampp. https://stackoverflow.com/a/59836749
Cách viblo mã hóa title
Đối với phần mã ngẫu nhiên ở phía cuối URL nó là một dạng unique ID được sinh tự động - thường gọi là hashid.
Bạn có thể dùng https://hashids.org, nó có nhiều phiên bản cho các ngôn ngữ lập trình khác nhau: C, C++, PHP, .NET, Objective-C... Bạn có thể dùng lib này để tạo mã hashid trong code. Một cách khác, nó có một bản extension để cài thêm cho PostgreSQL - cách này thì bạn không phải sửa code mà phía PostgreSQL sẽ tự động generate mã hashid cho record mới trong database.
Ngoài ra, một số package sử dụng các thuật toán mới để tạo unique ID với tốc độ cao như: nanoid, cuid. Bạn có thể đọc thêm.
Trong các sản phẩm của Viblo, Viblo sử dụng micro service nên có nhiều hình thức để tạo hashid ở từng service, ví dụ như:
- https://hashids.org
- https://github.com/vinkla/hashids
- https://github.com/paralleldrive/cuid
- https://www.npmjs.com/package/nanoid
Bạn có thể tham khảo tất cả mấy cái đó nhé.
Thắc mắc về hover trong css
Hiệu ứng này cũng khá dễ implement. Bạn sử dụng position: relative / absolute
là được.
<div class="card-image-root">
<img
class="card-image"
src="https://images.unsplash.com/photo-1527004013197-933c4bb611b3?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=720&q=80"
>
<div class="card-image-overlay">
<a class="card-button" href="javascript:alert(1);">
Click me
</a>
</div>
</div>
.card-image-root {
max-width: 300px;
height: auto;
position: relative;
}
.card-image-overlay {
display: none;
border-radius: 20px;
}
.card-image {
width: 100%;
border-radius: 20px;
}
.card-image-root:hover .card-image-overlay {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: rgba(0,0,0,.8);
position: absolute;
top: 0;
left: 0;
}
.card-button {
padding: 0.5rem 1.5rem;
border-radius: 10px;
background: white;
border: none;
cursor: pointer;
color: red;
text-decoration: none;
}
Upload video max size
Cái status code của response là 0 tức là request đấy chưa gửi lên server, có nghĩa là vấn đề có thể nằm ở phía máy client. Điều này theo suy đoán của mình có thể kể đến một số khả năng:
- Kết nối internet của client không ổn định
- Vấn đề DNS không chính xác dưới máy local
- Request bị block bởi trình duyệt: do browser hoặc browser extension chẳng hạn?
- Có service worker nào đó đang xử lý việc upload gặp lỗi và cancel request hoặc đại loại thế.
Bạn thử debug theo một số hướng trên xem. Tốt nhất bạn nên cài đặt thêm công ty thu thập log như Sentry để tracing request và nắm bắt được vấn đề xem chỉ máy bạn bị hay có bao nhiêu user cũng bị giống bạn. Chứ nhiều khi bạn chủ quan test trên thiết bị cá nhân nhưng bản thân thiết bị của bạn bị lỗi chứ không phải do hệ thống.
làm sao reload api lỗi 401
Mình không dùng Angular nên cũng không rành lắm cách triển khai trong code. Bạn thử làm theo hướng dẫn sử dụng Angular Http Interceptor trong bài viết này xem nhé: https://www.bezkoder.com/angular-12-refresh-token/
import { HTTP_INTERCEPTORS, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { TokenStorageService } from '../_services/token-storage.service';
import { AuthService } from '../_services/auth.service';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, switchMap, take } from 'rxjs/operators';
// const TOKEN_HEADER_KEY = 'Authorization'; // for Spring Boot back-end
const TOKEN_HEADER_KEY = 'x-access-token'; // for Node.js Express back-end
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private tokenService: TokenStorageService, private authService: AuthService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<Object>> {
let authReq = req;
const token = this.tokenService.getToken();
if (token != null) {
authReq = this.addTokenHeader(req, token);
}
return next.handle(authReq).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && !authReq.url.includes('auth/signin') && error.status === 401) {
return this.handle401Error(authReq, next);
}
return throwError(error);
}));
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
const token = this.tokenService.getRefreshToken();
if (token)
return this.authService.refreshToken(token).pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.tokenService.saveToken(token.accessToken);
this.refreshTokenSubject.next(token.accessToken);
return next.handle(this.addTokenHeader(request, token.accessToken));
}),
catchError((err) => {
this.isRefreshing = false;
this.tokenService.signOut();
return throwError(err);
})
);
}
return this.refreshTokenSubject.pipe(
filter(token => token !== null),
take(1),
switchMap((token) => next.handle(this.addTokenHeader(request, token)))
);
}
private addTokenHeader(request: HttpRequest<any>, token: string) {
/* for Spring Boot back-end */
// return request.clone({ headers: request.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
/* for Node.js Express back-end */
return request.clone({ headers: request.headers.set(TOKEN_HEADER_KEY, token) });
}
}
export const authInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];