Memcached: giới thiệu và cài đặt

Ở bài trước, mình đã giới thiệu về cache với guava. Về cơ bản, guava là local cache, dữ liệu cache sẽ được lưu trữ cùng với bộ nhớ của ứng dụng (JVM), dữ liệu cache sẽ mất khi restart ứng dụng. Khác với guava, memcached là cache server, dữ liệu cache sẽ được lưu trữ ở server, có thể cùng hoặc khác với server deploy ứng dụng. Do đó, dữ liệu cache chỉ mất khi restart memcached hoặc server.

Bài viết hôm nay mình sẽ giới thiệu về memcached, cách cài đặt, một vài thao tác với memcached trên server cho những bạn mới bắt đầu làm quen với memcached. Nào, chúng ta cùng bắt đầu nhé.

Giới thiệu

Memcached là một in-memory cache, lưu trữ dữ liệu dưới dạng key-value. Tất cả các operation sẽ đều có độ phức tạp là O(1).

Thường thì mình sử dụng memcached để cache kết quả các request đọc vào database mà kết quả trả về chậm, hoặc các request với cường độ lớn. Tốc độ API sẽ tăng đáng kể sau khi dùng cache.

Về expired, chúng ta có thể set time expired cho value cache. Tuy nhiên, cơ chế evicted mặc định của  memcached là LRU (Least Recently Used). Lưu ý là do không có cơ chế persistence như Redis, do đó memcached chỉ được sử dụng để cache, không thể sử dụng memcached như là một database.

Các bạn có thể đọc thêm về memcache tại trang chủ. Phần tiếp theo mình sẽ hướng dẫn cài đặt.

Cài đặt

Để cài đặt memcached trên ubuntu, đơn giản nhất, các bạn có thể dùng lệnh:

sudo apt-get install memcached

Cài đặt memcached trên ubuntu

Thông báo memcached đã được cài đặt version mới nhất

Sau khi được cài đặt, server memcached sẽ được start, các bạn có thể kiểm tra thông memcached đang chạy bằng lệnh:

ps -ef | grep mem

Kiểm tra memcached đang chạy

Thông tin server memcached đang chạy

Đến lúc này, chúng ta đã có thể thao tác với memcached. Chúng ta có thể connect đến memcached bằng lệnh telnets:

telnet localhost 11211

Telnet

Telnet để connect với memcached và stats để xem thông tin

Sau khi connect đến memcache, việc đầu tiên mình thương làm là chạy lệnh stats. Lệnh stats khá quan trọng vì sẽ show tất cả thông tin về memcached bao gồm version, total_items, curr_items, get_hits, get_misses … giúp chúng ta kiểm tra tình trạng của memcached.

Vì memcached cung cấp service access thông qua App Engine API, do đó, sau khi connect, chúng ta có thể thực hiện các thao tác như add, set, get, delete… bằng command. Thông thường mình chỉ dùng các command set, get sau khi set up memcached để chắc chắn là memcached hoạt động tốt. Các bạn có thể xem thêm các command của memcached tại đây

Để thao tác với memcached bằng Java, chúng ta cần Java memcached client. Có thể mình sẽ có 1 demo nhỏ về sử dụng memcached bằng Java client ở bài viết sau.

Thông thường, khi server memcached có vấn đề, chúng ta có thể dùng thần chú restart bằng lệnh:

 /etc/init.d/memcached restart

Lệnh restart server memcached khi cần

Kết

Vừa rồi là phần giới thiệu cài đặt và một vài thao tác đơn giản cho các bạn mới làm quen với memcached. Ở bài sau, nếu cần có thể mình sẽ có một demo nhỏ sử dụng memcached với Java client. Mọi ý kiến đóng góp của các bạn vui lòng comment bên dưới, mình sẽ follow.

Cảm ơn và hẹn gặp các bạn ở các bài viết sau.

 

Apache Thrift phần 2: kiểu dữ liệu và ví dụ minh họa

Chào mừng các bạn đã quay trở lại với thachleblog. Ở bài trước, mình đã giới thiệu sơ lượt và hướng dẫn cách cài đặt Apache Thrift compiler. Bài viết hôm nay sẽ trình bày về các kiểu dữ liệu của Thrit cộng thêm ví dụ minh họa cách để build một service bằng Thrift. Hy vọng qua ví dụ đơn giản này có thể giúp các bạn chưa có chút idea nào về Thrift có thể hình dung được. Nào, chúng ta cùng bắt đầu nhé.

Ở bài trước, mình đã giới thiệu Thrift cho phép định nghĩa kiểu dữ liệu và service bằng một ngôn ngữ trung lập (single language-neutral), mình sẽ tạm gọi là ngôn ngữ Thrift. Theo mình nghĩ, gọi là ngôn ngữ trung lập bởi vì từ file source Thrift, thông qua compiler, chúng ta có thể compile thành các source file với các ngôn ngữ tương ứng khác nhau, kiểu như là của chung vậy :D.

Kiểu dữ liệu: Thrift hỗ trợ các kiểu dữ liệu

Kiểu dữ liệu cơ bản:

  • bool A boolean value, true or false
  • byte A signed byte
  • i16 A 16-bit signed integer
  • i32 A 32-bit signed integer
  • i64 A 64-bit signed integer
  • double A 64-bit floating point number
  • string An encoding-agnostic text or binary string

Struct: tương tự như struct trong C

Containers:

  • list<type>
  • set<type>
  • map<key,value>

Cách dùng tương tự như các collections interface trong Java.

Exeptions

Services: service trong Thrift được định nghĩa tương tự như interface trong Java. Cái này mình sẽ mô tả trong ví dụ bên dưới.

Ví dụ

student.thrift

namespace java thach.le.student.thrift
 
enum ErrorCode {
	SUCCESS = 0,
	NOT_FOUND = 1,
}
 
struct TStudent {
  1:optional i32 studentId,
  2:optional string name,
  3:optional string className,
  4:optional list<string> subjects
}
 
struct TStudentResult {
  1:required i32 error,
  2:optional TStudent value,
}

Ở trên là file mình định nghĩa các kiểu dữ liệu gồm các struct TStudent, TStudentResult (object trả về cho client) và enum ErrorCode.

Tiếp theo mình sẽ định nghĩa một service có method getStudentById(int studentId), method này sẽ trả về TStudentResult. Vì mình định nghĩa ở 1 file khác nên mình include file “student.thift”.

student_service.thrift

include "student.thrift"
namespace java thach.le.student.service.thrift
 
service StudentService {
	student.TStudentResult getStudentById(1:required i32 studentId);
}

Tới đây, lần lượt sử dụng lệnh để generate ra các file class Java.

thrift –gen java student.thrift
thrift –gen java student_service.thrift

Sau khi chạy lệnh, chúng ta sẽ được các file class trong thư mục gen-code bao gồm: ErrorCode, TStudent, TStudentResult, StudentService. Công việc của chúng ta ở phía server là sẽ implement method getStudentById(int id) của StudentService interface. Code minh họa bên dưới:

StudentServiceHandler

Enter copackage thach.le.student.thrift.handlers;
 
import java.util.ArrayList;
import java.util.List;
import org.apache.thrift.TException;
import thach.le.student.service.thrift.StudentService;
import thach.le.student.thrift.ErrorCode;
import thach.le.student.thrift.TStudent;
import thach.le.student.thrift.TStudentResult;
 
 
/**
 *
 * @author thachlp
 */
public class StudentServiceHandler implements StudentService.Iface {
 
    @Override
    public TStudentResult getStudentById(int studentId) throws TException {
        TStudentResult result = new TStudentResult();
        if(studentId == 10520204){
            TStudent student = new TStudent();
            List<String> subjects = new ArrayList<>();
            subjects.add("Game development");
            subjects.add("Big data");
            student.setStudentId(10520204);
            student.setName("Thach Le");
            student.setClassName("SE05");
            student.setSubjects(subjects);
 
            result.setError(ErrorCode.SUCCESS.getValue());
            result.setValue(student);            
        } else {
            result.setError(ErrorCode.NOT_FOUND.getValue());
        }
        return result;
    }
}de here

Tiếp theo, khởi tạo Thrift server để “ready” khi client gọi tới. Lưu ý đây chỉ là demo đơn giản nên mình dùng một TSimpleServer, trên thực tế cần dùng các Server có hỗ trợ multi-thread và thread-pool để tăng hiệu năng cho server.

TServer

Enter code herepackage thach.le.student.thrift.servers;
 
import org.apache.thrift.transport.TTransportException;
import org.apache.thrift.server.TServer.Args;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import thach.le.student.service.thrift.StudentService;
import thach.le.student.thrift.handlers.StudentServiceHandler;
 
 
/**
 *
 * @author thachlp
 */
public class TServer {
 
    public static StudentServiceHandler handler;
 
    public static StudentService.Processor processor;
 
    public static void main(String[] args) {
        try {
            handler = new StudentServiceHandler();
            processor = new StudentService.Processor(handler);
 
            Runnable simple = new Runnable() {
                @Override
                public void run() {
                    simple(processor);
                }
            };
 
            new Thread(simple).start();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
 
    public static void simple(StudentService.Processor processor) {
        try {
            TServerTransport serverTransport = new TServerSocket(9090);
            TSimpleServer server = new TSimpleServer(new Args(serverTransport).processor(processor));
 
            System.out.println("Starting the simple server...");
            server.serve();
        } catch (TTransportException e) {
            System.out.println(e.getMessage());
        }
    }
 
}

Tiếp theo, ở phía Client, chúng ta cần tạo một socket connection để connect đến TServer và gọi method getStudentById(int studentId).

StudentClient

Enter code herepackage thach.le.student.thrift.wrapper;
 
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import thach.le.student.service.thrift.StudentService;
import thach.le.student.thrift.TStudentResult;
 
/**
 *
 * @author thachlp
 */
public class StudentClient {
 
    public static void main(String[] args) {
 
        try {
            TTransport transport;
            transport = new TSocket("localhost", 9090);
            transport.open();
 
            TProtocol protocol = new TBinaryProtocol(transport);
            StudentService.Client client = new StudentService.Client(protocol);
 
            perform(client);
 
            transport.close();
        } catch (TException e) {
            System.out.println(e.getMessage());
        }
    }
 
    private static void perform(StudentService.Client client) throws TException {
 
        TStudentResult search = client.getStudentById(10520204);
        System.out.println(search.getError());
        System.out.println(search.getValue());
    }
 
}

DONE! Vậy là chúng ta đã hoàn thành một ví dụ nhỏ về mô hình client – server bằng Thrift. Nếu làm theo mình đến bước này, các bạn đã có thể run file TServer để start server đồng thời run file StudentClient để test. Hãy thử để xem kết quả nhé.

Một lưu ý nữa là đối với client và server, chúng ta có thể sử dụng bằng các ngôn ngữ khác nhau mà Thrift hỗ trợ. Các bạn nếu biết Python hoặc Ruby cũng có thể tạo một StudentService để gọi đến server mình tạo sẵn ở trên.

Vừa rồi là phần trình bày demo của mình về mô hình giao tiếp bằng Thrift. Hiện tại, Thrift được sử dụng rộng rãi trong các hệ thống backend lớn như Facebook, Hadoop, Cassandra…. Hy vọng thông qua ví dụ đơn giản này sẽ giúp các bạn hiểu được cách định nghĩa kiểu dữ liệu và service bằng Thrift, đồng thời có thể build được hệ thống client – server bằng Thrift. Thrift còn cung cấp nhiều tính năng khác, nếu có thời gian các bạn hãy đọc để hiểu thêm nhé (mình cũng chưa đọc hết ^.^).

Mọi thắc mắc và góp ý, vui lòng comment bên dưới, mình sẽ follow. Chào và hẹn gặp lại các bạn ở các bài viết sau nhé.