Kiến trúc Microservice là gì?
Kiến trúc Microservice là một mẫu thiết kế phần mềm trong đó chúng ta viết các ứng dụng bằng cách kết hợp một số chương trình nhỏ. Các chương trình này, được gọi là microservices, hoạt động cùng nhau vì một mục tiêu chung. Đối với một số nhóm, việc viết một số ứng dụng nhỏ sẽ mất ít thời gian và công sức hơn nhiều so với một ứng dụng lớn.
Một dự án theo định hướng dịch vụ vi mô bao gồm một loạt các ứng dụng nhỏ, mỗi ứng dụng thực hiện một phần của mình, chạy trong một quy trình riêng biệt và giao tiếp với phần còn lại thông qua các giao diện được tiêu chuẩn hóa. Cách tiếp cận này cho phép các nhóm lựa chọn công cụ tốt nhất cho từng vấn đề mà không cần cam kết với một ngôn ngữ hoặc khuôn khổ. Nó cũng cho phép chúng tôi phân chia công việc giữa các nhóm chuyên biệt hơn.
Từ Monolith đến Microservice
Trong phần đầu tiên của loạt bài này, chúng ta đã nói về đá nguyên khối. Dễ dàng bắt đầu bằng đá nguyên khối. Tuy nhiên, tốc độ phát triển có quy mô kém, chủ yếu là do mọi thứ được kết hợp chặt chẽ với nhau. Ngay cả một thay đổi mã nhỏ cũng buộc chúng tôi phải xây dựng lại và kiểm tra toàn bộ dự án, dẫn đến chu kỳ phát hành dài một cách khó chịu.
Làm thế nào để chúng ta đi từ nguyên khối sang microservice? Lấy trường hợp của Amazon. Một thời gian trước, họ bắt đầu như một khối nguyên khối và theo thời gian chuyển sang các dịch vụ siêu nhỏ. Thiết kế ban đầu của họ có thể trông như thế này:
Tất nhiên, tôi đang đơn giản hóa ở đây, nhưng tôi tin rằng tôi đã bao gồm hầu hết những điều cơ bản. Điều gì sẽ xảy ra nếu họ đã chọn theo mô hình microservice ngay từ đầu? Họ sẽ chia ứng dụng theo chức năng, mỗi thành phần tập trung vào một vấn đề.
Họ cũng cần phải xác định các giao diện và giao thức để giao tiếp giữa các dịch vụ, thường là với các cơ chế nhẹ như API RESTful.
Không gian tên là gì
Một thiết kế microservice có một loạt thách thức riêng. Điều chính có lẽ là thiết bị đo đạc và báo cáo lỗi. Hãy nghĩ về nó, chúng tôi đang giám sát hàng chục hoặc hàng trăm thành phần trải rộng trên các nền tảng và ngôn ngữ khác nhau. Và bằng cách nào đó, chúng ta phải để mắt đến tất cả chúng, đồng thời tránh làm mất đi bức tranh toàn cảnh. Không gian tên có thể giúp chúng tôi tập trung một nhóm các dịch vụ nhỏ được ghép nối lỏng lẻo thành một bức tranh thống nhất.
Trong AppSignal, không gian tên là vùng chứa các chỉ số được thu thập. AppSignal sử dụng ba không gian tên theo mặc định (web
, background
và frontend
), nhưng chúng ta có thể tạo của riêng mình bằng cách thêm một vài dòng mã. Chúng ta sẽ xem cách chúng hoạt động tiếp theo.
Một ứng dụng để cai trị tất cả
Khi thiết lập một thành phần microservice, điều đầu tiên cần làm là định cấu hình tên và môi trường ứng dụng chung. Do đó, AppSignal trình bày tất cả các chỉ số và cảnh báo được thu thập trên cùng một trang tổng quan.
Chi tiết cụ thể về cách định cấu hình các giá trị này phụ thuộc vào ngôn ngữ và sự tích hợp. Ví dụ:để cấu hình ứng dụng Ruby on Rails sử dụng tên “Nozama”:
# config/appsignal.yml
production:
active: true
push_api_key: "YOUR APPSIGNAL API KEY"
name: "Nozama"
Điều này rất giống với cách chúng tôi định cấu hình tích hợp Elixir:
# config/config.exs
config :appsignal, :config,
active: true,
name: "Nozama",
push_api_key: "YOUR APPSIGNAL API KEY",
env: "production"
Mặt khác, trong Node.js, chúng tôi sử dụng:
const { Appsignal } = require("@appsignal/nodejs");
const appsignal = new Appsignal({
active: true,
name: "Nozama",
apiKey: "YOUR APPSIGNAL API KEY",
});
Để tích hợp JavaScript trực diện, chúng tôi sử dụng @appsignal/javascript
thay vào đó:
import Appsignal from "@appsignal/javascript";
export default new Appsignal({
name: "Nozama",
key: "YOUR FRONTEND API KEY",
});
Bạn có thể tìm thông tin về cách cài đặt và cấu hình AppSignal tại đây:
- Thêm ứng dụng mới
- Định cấu hình ứng dụng
Sử dụng Không gian tên trong Microservices
Hãy xem chúng ta sẽ tiến hành viết mã từng microservice như thế nào. Hãy bắt đầu với hệ thống thanh toán; chúng tôi sẽ sử dụng Elixir và Phoenix cho phần này.
Khi chúng tôi đã làm theo thiết lập tích hợp Phoenix, chúng tôi có thể bắt đầu làm việc trên các bộ điều khiển. Đoạn mã sau đặt không gian tên thành billing
:
# in a Phoenix controller, we use plug to run the namespace initialization
defmodule BillingPageController.PageController do
use BillingPageController, :controller
plug :set_appsignal_namespace
defp set_appsignal_namespace(conn, _params) do
# Sets all actions in this controller to report in the "billing" namespace
Appsignal.Transaction.set_namespace(:billing)
conn
end
# rest of the controller ...
end
Dữ liệu sẽ bắt đầu xuất hiện trong trang tổng quan sau khi dịch vụ vi mô đang chạy và bộ điều khiển thấy một số hoạt động.
Tất nhiên, việc thanh toán sẽ không giúp chúng ta tiến xa trừ khi mọi người mua thứ gì đó. Đây là một vấn đề mà chúng tôi có thể giải quyết trong một microservice riêng biệt. Theo mô hình tương tự, chúng tôi sẽ viết một ứng dụng Phoenix hoàn toàn mới với PayButtonController
bộ điều khiển bắt đầu như thế này:
defmodule PayButtonController.PageController do
use PayButtonController, :controller
plug :set_appsignal_namespace
defp set_appsignal_namespace(conn, _params) do
Appsignal.Span.set_namespace(Appsignal.Tracer.root_span(), "pay_button")
conn
end
# rest of the controller ...
end
Bây giờ chúng ta có hai không gian tên trong bảng điều khiển. Sử dụng cùng một tên và môi trường đảm bảo rằng dữ liệu từ PayButtonController
được hiển thị cùng với BillingPageController
, ngay cả khi chúng là các ứng dụng riêng biệt chạy trên các máy khác nhau.
Thành phần tiếp theo là công cụ khuyến nghị. Chúng tôi sẽ triển khai điểm cuối API hiển thị các đề xuất sản phẩm với Express. Chúng tôi đặt không gian tên trong Node.js như được hiển thị:
app.get("/", (req, res) => {
const tracer = appsignal.tracer();
tracer.withSpan(
tracer.createSpan({ namespace: "recommendations" }),
(span) => {
// code to measure goes here
span.close();
}
);
});
Chúng tôi hiện có tối đa ba không gian tên.
Nhóm thiết bị di động và giao diện người dùng có thể muốn ghi lại lỗi trong trang tổng quan. Tích hợp JavaScript của AppSignal tự động chỉ định dữ liệu đến với frontend
không gian tên. Nhưng chúng ta có thể thay đổi nó như thế này:
try {
// code that might fail
} catch (error) {
// handle the error
// send error to AppSignal
appsignal.sendError(error, {}, "Mobile");
}
Sau một thời gian, dữ liệu sẽ bắt đầu xuất hiện trong Mobile
không gian tên.
Ví dụ hiển thị JavaScript đơn giản, nhưng có thể có các bước thiết lập bổ sung cần thiết nếu bạn đang sử dụng khung giao diện người dùng như React hoặc Angular.
Đối với trang web, chúng ta hãy thử Ruby on Rails, một khuôn khổ MVC rất nổi tiếng mà AppSignal tích hợp với các ứng dụng mới. Chúng tôi sẽ khởi động bộ điều khiển Rails bằng đoạn mã sau để đặt không gian tên thành homepage
:
# in Rails we use before_action callback to change
# the namespace before the request starts
class HomepageController < ApplicationController
before_action :set_appsignal_namespace
def set_appsignal_namespace
Appsignal.set_namespace("homepage")
end
# controller actions ...
end
Tiếp theo, chúng tôi có thể sử dụng các điểm cuối API để cung cấp dữ liệu cho trang web và các ứng dụng di động. Đối với điều này, chúng tôi có thể sử dụng Grape, một khung API REST nhẹ cho Ruby. Lần này, việc định cấu hình AppSignal mất nhiều công hơn một chút.
Sau khi định cấu hình tích hợp Ruby trong ´config / appsignal.yml´, như chúng tôi đã làm trước đó, bạn có thể bắt đầu ghi nhật ký các sự kiện và số liệu với:
Appsignal.start_logger
Appsignal.start
Sau đó, chèn phần mềm trung gian AppSignal vào chuỗi xử lý lỗi:
require "appsignal"
require "appsignal/integrations/grape"
class API < Grape::API
insert_before Grape::Middleware::Error, Appsignal::Grape::Middleware
resource :search do
desc 'return a product search'
before do
Appsignal.set_namespace("search")
end
get :product do
# product search logic
end
end
end
Để biết thêm ví dụ, hãy xem tài liệu tích hợp Grape.
Để hoàn thành bức tranh, chúng ta sẽ kết thúc với công việc nền Sidekiq. Sidekiq là một bộ xử lý công việc phổ biến cho Ruby và đây là cách chúng ta có thể khởi động nó ở chế độ độc lập sau khi định cấu hình ´config / appsignal.yml´:
# config.ru
require 'appsignal'
Sidekiq.on(:startup) do
Appsignal.start
end
Sidekiq.on(:shutdown) do
Appsignal.stop('Sidekiq shutdown')
end
AppSignal tự động gán dữ liệu từ các công việc cho background
không gian tên. Chúng tôi có thể muốn thay đổi nó thành một không gian tên cụ thể hơn.
require 'sidekiq'
require 'appsignal'
class PlainOldRuby
include Sidekiq::Worker
def perform()
Appsignal.set_namespace("urgent_background")
# job logic
end
end
Thu thập số liệu với tác nhân độc lập
Tác nhân độc lập thu thập số liệu sử dụng tài nguyên từ bất kỳ máy Ubuntu, RedHat hoặc CentOS nào. Chúng tôi có thể sử dụng tác nhân để giám sát các máy chủ vệ tinh cung cấp các cơ sở như cơ sở dữ liệu, cổng hoặc môi giới tin nhắn cho các ứng dụng microservice.
Trong AppSignal, chúng tôi sử dụng tác nhân để giám sát các máy chủ Kafka của riêng mình. Tác nhân cũng rất hữu ích để tạo công cụ tùy chỉnh trên các ngôn ngữ và khuôn khổ không được hỗ trợ trực tiếp.
Để bắt đầu với tác nhân, hãy tải xuống và cài đặt nó theo hướng dẫn cài đặt.
Khi nó đang chạy, chúng tôi sẽ cần chỉnh sửa tệp cấu hình để đặt khóa API, tên ứng dụng và môi trường. Sử dụng các giá trị tương tự được sử dụng trong phần còn lại của microservice để đưa mọi thứ vào một trang tổng quan.
# /etc/appsignal-agent.conf
push_api_key = "YOUR APPSIGNAL API KEY"
app_name = "Nozama"
environment = "production"
Kết luận
Không gian tên là một cách tuyệt vời để tổ chức dữ liệu được phân phối giữa nhiều hệ thống. Chúng cũng cho phép chúng tôi định cấu hình thông báo và tinh chỉnh việc xử lý cảnh báo. Để xem cách hoạt động, hãy xem phần đầu tiên của loạt bài này.
Các bài đọc bổ sung:
- Bạn có thể làm gì với không gian tên?
- Giám sát bất kỳ hệ thống nào bằng StatsD và AppSignal's Standalone Agent
- Các chỉ số và cảnh báo trên máy chủ được cải thiện