avatar

Cài đặt môi trường, chuẩn bị ứng dụng Springboot

Trong bài đầu tiên, chúng ta cùng nhau cài đặt môi trường phát triển cho ứng dụng Java Springboot

Đăng vào
77 phút

title

Mục lục

1. Giới thiệu chung

Trong chuỗi bài này, chúng ta cùng nhau tìm hiểu cách phát triển và triển khai một ứng dụng quản lý học sinh sử dụng Spring boot trên Kubernetes (k8s). Qua việc triển khai ứng dụng, chúng ta sẽ tìm hiểu những thành phần cơ bản và cách cấu hình ứng dụng với k8s. Bài viết đầu tiên sẽ trình bày về việc chuẩn bị môi trường phát triển, tạo mới project và giới thiệu qua về ứng dụng chúng ta sẽ phát triển/triển khai.

2. Cài đặt môi trường

Trong chuỗi bài này chúng ta sẽ sử dụng MicroK8s để tạo cụm Kubernetes. Đối với MicroK8s khi tương tác với cụm Kubernetes thì sẽ kèm theo chuỗi "microk8s" ở phía trước câu lệnh như: "microk8s kubectl...". Để ngắn gọn hơn thì bạn có thể làm như sau:

cd $HOME
mkdir .kube
cd .kube
microk8s config > config

kubectl config use-context microk8s

3. Giới thiệu về ứng dụng

Hình mô tả services

Ứng dụng đơn quản lý học sinh đơn giản được viết bằng Java gồm hai service "Students-service" và "Courses-service":

  1. Students-service: Quản lý các thông tin về học sinh. Gồm các API như:
# Tạo mới học sinh:
curl --location --request POST 'localhost:8080/api/students' \
--header 'Content-Type: application/json' \
--data-raw '{
    "fullName": "NGUYEN BA THANH",
    "dateOfBirth": "29/04/1998",
    "hometown": "HA DONG, HA NOI",
    "gender": "MALE"
}'

# Danh sách học sinh:
curl --location --request GET 'localhost:8080/api/students'
  1. Courses-service: Quản lý các thông tin khóa học. Gồm các API như:
# Tạo mới khóa học:
curl --location --request POST 'localhost:8081/api/courses/v1/createCourse' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "HOC LAM GIAU",
    "desc": "NEU MUON CO 100 ti THI PHAI THAM GIA NGAY :D",
    "author": "VNTechies"
}'

# Tham gia khóa học:
curl --location --request POST 'localhost:8081/api/courses/v1/joinCourse' \
--header 'Content-Type: application/json' \
--data-raw '{
    "courseId": "1",
    "studentId": "1"
}'

# Lấy thông tin chi tiết khóa học:
curl --location --request GET 'localhost:8081/api/courses/v1/getCourseDetailBy?courseId=1'

Bạn có thể tham khảo source code ở đây.

4. Setup Spring boot application

Truy cập Spring Initializr để tạo mới một Spring Boot project. Điền thêm một số thông tin của project.

4.1 Setup Students service

  1. Tạo mới project Spring boot với Spring Initializr

Tạo mới project Spring boot với Spring Initializr

Thông tin project và các dependencies:

ProjectValue
Tênstudents
Build toolMaven
Spring Boot version2.7.10
Java version11
DependenciesSpring Web
Spring Boot DevTools
Lombok
Spring Configuration Processor
MySQL Driver
Spring Data JPA
  1. Tạo file application.yaml để cấu hình service
src/main/resources/application.yaml
server:
  port: ${SERVER_PORT:8081}

# Cấu hình thông tin database (MySQL) gồm: tên database, username và password của user MySQL.
spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: ${MYSQL_URL:jdbc:mysql://localhost:3306/students}
    username: ${MYSQL_USERNAME:students}
    password: ${MYSQL_PASSWORD:VNTechies2023}
  jpa:
    hibernate.ddl-auto: update
    generate-ddl: true
    show-sql: true
  1. Một số class của Students service

File Student.java dưới đây định nghĩa các trường thông tin về học sinh:

students/src/main/java/com/vntechies/k8sspringbootseries/students/entities/Student.java
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "students")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "fullName")
    private String fullName;

    @Column(name = "dob")
    private String dateOfBirth;

    @Column(name = "hometown")
    private String hometown;

    @Column(name = "gender")
    @Enumerated(EnumType.STRING)
    private Gender gender;

    public static Student createFrom(CreateStudentRequest request) {
        Student student = new Student();
        student.setFullName(request.getFullName());
        student.setDateOfBirth(request.getDateOfBirth());
        student.setHometown(request.getHometown());
        student.setGender(request.getGender());
        return student;
    }
}

File StudentControllers.java dưới đây định nghĩa một số API như:

  • POST: /api/students: Tạo mới học sinh.
  • GET: /api/students: Lấy danh sách học sinh.
students/src/main/java/com/vntechies/k8sspringbootseries/students/controllers/StudentControllers.java
@RestController
@RequestMapping(value = "/api/students")
public class StudentControllers {

    private final IStudentService iStudentService;

    public StudentControllers(IStudentService iStudentService) {
        this.iStudentService = iStudentService;
    }

    @PostMapping
    public ResponseEntity<StudentDTO> createStudent(@RequestBody CreateStudentRequest request) {
        return new ResponseEntity<>(iStudentService.createStudent(request), HttpStatus.CREATED);
    }

    @GetMapping
    public ResponseEntity<List<StudentDTO>> getAllStudents() {
        return new ResponseEntity<>(iStudentService.getAll(), HttpStatus.OK);
    }
}

File StudentServiceImpl.java dưới đây định nghĩa các logic cho các API phía trên:

students/src/main/java/com/vntechies/k8sspringbootseries/services/StudentServiceImpl.java
@Service
public class StudentServiceImpl implements IStudentService {

    private static final ObjectMapper objectMapper = new ObjectMapper();
    private final IStudentRepository iStudentRepository;

    public StudentServiceImpl(IStudentRepository iStudentRepository) {
        this.iStudentRepository = iStudentRepository;
    }

    @Override
    public StudentDTO createStudent(CreateStudentRequest request) {
        Student student = iStudentRepository.save(Student.createFrom(request));
        return objectMapper.convertValue(student, StudentDTO.class);
    }

    @Override
    public List<StudentDTO> getAll() {
        List<Student> students = iStudentRepository.findAll();
        List<StudentDTO> studentRespList = new ArrayList<>();
        for (Student student : students) {
            StudentDTO studentResp = objectMapper.convertValue(student, StudentDTO.class);
            studentRespList.add(studentResp);
        }
        return studentRespList;
    }
}

4.2 Setup Courses service

  1. Tạo mới project Spring boot với Spring Initializr

Tạo mới project Spring boot với Spring Initializr

Thông tin project và các dependencies:

ProjectValue
Têncourses
Build toolMaven
Spring Boot version2.7.10
Java version11
DependenciesSpring Web
Spring Boot DevTools
Lombok
Spring Configuration Processor
MySQL Driver
Spring Data JPA
  1. Tạo file application.yaml để cấu hình service
courses/src/main/resources/application.yaml
server:
  port : ${SERVER_PORT:8080}

# Cấu hình thông tin database (MySQL) gồm: tên database, username và password của user MySQL.
spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: ${MYSQL_URL:jdbc:mysql://localhost:3306/courses}
    username: ${MYSQL_USERNAME:courses}
    password: ${MYSQL_PASSWORD:VNTechies2023}
  jpa:
    hibernate.ddl-auto: update
    generate-ddl: true
    show-sql: true

# Cấu hình thông tin URI của Students service.
clients:
  services:
    students:
      uri: ${STUDENTS_URI:http://localhost:8081}
  1. Một số class của Courses service

File Courses.java định nghĩa các thông tin khóa học:

courses/src/main/java/com/vntechies/k8sspringbootseries/courses/entities/Courses.java
@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "courses")
public class Courses {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "description")
    private String desc;

    @Column(name = "author")
    private String author;
}

File CoursesMembers.java dùng để lưu thông tin học sinh đã tham gia khóa học:

courses/src/main/java/com/vntechies/k8sspringbootseries/courses/entities/CoursesMembers.java
@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "courses_members")
public class CoursesMembers {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "course_id")
    private Long courseId;

    @Column(name = "member_id")
    private Long memberId;

    public CoursesMembers(Long courseId, Long memberId) {
        this.courseId = courseId;
        this.memberId = memberId;
    }
}

File CoursesControllers.java dưới đây định nghĩa một số API như:

  • POST: /v1/createCourse: Tạo mới khóa học.
  • POST: /v1/joinCourse: Học sinh tham gia khóa học.
  • GET: /v1/getCourseDetailBy: Lấy thông tin chi tiết của khóa học.
courses/src/main/java/com/vntechies/k8sspringbootseries/courses/controllers/CoursesControllers.java
@RestController
@RequestMapping(value = "/api/courses")
public class CoursesControllers {

    private final ICoursesService iCoursesService;

    public CoursesControllers(ICoursesService iCoursesService) {
        this.iCoursesService = iCoursesService;
    }

    @PostMapping(value = "/v1/createCourse")
    public ResponseEntity<BaseResponse<CourseDto>> createCourse(@RequestBody CreateCourseReq req) {
        return new ResponseEntity<>(BaseResponse.buildSuccessResp(iCoursesService.createCourse(req)), HttpStatus.OK);
    }

    @PostMapping(value = "/v1/joinCourse")
    public ResponseEntity<BaseResponse<CourseDto>> joinCourse(@RequestBody JoinCourseReq req) {
        return new ResponseEntity<>(BaseResponse.buildSuccessResp(iCoursesService.joinCourse(req)), HttpStatus.OK);
    }

    @GetMapping(value = "/v1/getCourseDetailBy")
    public ResponseEntity<BaseResponse<CourseDetail>> getCourseDetailBy(@RequestParam(name = "courseId") Long courseId) {
        return new ResponseEntity<>(BaseResponse.buildSuccessResp(iCoursesService.getCourseDetailBy(courseId)), HttpStatus.OK);
    }
}

File CoursesServiceImpl.java dưới đây định nghĩa các logic cho các API phía trên:

courses/src/main/java/com/vntechies/k8sspringbootseries/courses/services/CoursesServiceImpl.java
@Service
@AllArgsConstructor
public class CoursesServiceImpl implements ICoursesService {

    private final ObjectMapper objectMapper;
    private final IStudentClient iStudentClient;
    private final ICoursesRepository iCoursesRepository;
    private final ICoursesMembersRepository iCoursesMembersRepository;

    @Override
    public CourseDto createCourse(CreateCourseReq req) {
        Courses courses = new Courses();
        courses.setName(req.getName());
        courses.setAuthor(req.getAuthor());
        courses.setDesc(req.getDesc());
        Courses coursesDB = iCoursesRepository.save(courses);
        return objectMapper.convertValue(coursesDB, CourseDto.class);
    }

    @Override
    public CourseDetail getCourseDetailBy(Long courseId) {
        CourseDetail courseDetail = new CourseDetail();
        Courses courses = iCoursesRepository.findByID(courseId);
        if (Objects.isNull(courses)) {
            throw new CustomException("Courses not found!");
        }
        List<Long> membersId = new ArrayList<>();
        List<CoursesMembers> membersOfCourse = iCoursesMembersRepository.getAllById(courseId);
        for (CoursesMembers member : membersOfCourse) {
            membersId.add(member.getMemberId());
        }
        if (!membersId.isEmpty()) {
            GetStudentsByIdsResp studentsResp = iStudentClient.getStudentsByIds(new GetStudentsByIdsReq(membersId));
            List<MemberInfo> membersInfo = studentsResp.getStudents().stream()
                    .map(studentDTO -> objectMapper.convertValue(studentDTO, MemberInfo.class))
                    .collect(Collectors.toList());

            courseDetail.setMembers(membersInfo);
        }
        courseDetail.setCourse(objectMapper.convertValue(courses, CourseDto.class));
        return courseDetail;
    }

    @Override
    public CourseDto joinCourse(JoinCourseReq req) {
        Courses courses = iCoursesRepository.findByID(req.getCourseId());
        if (Objects.isNull(courses)) {
            throw new CustomException("Courses not found!");
        }
        StudentDTO studentDetail = iStudentClient.getStudentById(req.getStudentId());
        if (Objects.isNull(studentDetail)) {
            throw new CustomException("Student not found!");
        }
        CoursesMembers coursesMember = new CoursesMembers(courses.getId(), studentDetail.getId());
        iCoursesMembersRepository.save(coursesMember);
        return objectMapper.convertValue(courses, CourseDto.class);
    }
}

4. Tổng kết

Trong bài mở đầu series về triển khai ứng dụng spring boot với kubernetes này đã giới thiệu về mục tiêu của series, các bước chuẩn bị về môi trường cũng như giới thiệu qua về spring boot application mà sẽ sử dụng. Ở bài tiếp theo thì chúng ta sẽ đi tìm hiểu về cách đóng gói spring boot container với Pod trong Kubernetes.