Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Trong bài viết này, tôi sẽ nói về cách dự án mà tôi đang thực hiện chuyển đổi từ một khối nguyên khối lớn thành một tập hợp các vi dịch vụ.

Dự án đã bắt đầu lịch sử của nó cách đây khá lâu, vào đầu năm 2000. Các phiên bản đầu tiên được viết bằng Visual Basic 6. Theo thời gian, rõ ràng là việc phát triển bằng ngôn ngữ này sẽ khó được hỗ trợ trong tương lai, vì IDE và bản thân ngôn ngữ cũng kém phát triển. Vào cuối những năm 2000, người ta quyết định chuyển sang C# hứa hẹn hơn. Phiên bản mới được viết song song với việc sửa đổi phiên bản cũ, dần dần ngày càng có nhiều mã được viết bằng .NET. Phần cuối trong C# ban đầu tập trung vào kiến ​​trúc dịch vụ, nhưng trong quá trình phát triển, các thư viện chung có logic đã được sử dụng và các dịch vụ được khởi chạy trong một quy trình duy nhất. Kết quả là một ứng dụng mà chúng tôi gọi là “dịch vụ nguyên khối”.

Một trong số ít ưu điểm của sự kết hợp này là khả năng các dịch vụ gọi lẫn nhau thông qua API bên ngoài. Có những điều kiện tiên quyết rõ ràng để chuyển đổi sang một dịch vụ chính xác hơn và kiến ​​trúc vi dịch vụ trong tương lai.

Chúng tôi bắt đầu công việc phân hủy vào khoảng năm 2015. Chúng tôi vẫn chưa đạt đến trạng thái lý tưởng - vẫn còn những phần của một dự án lớn khó có thể gọi là nguyên khối, nhưng chúng trông cũng không giống microservice. Tuy nhiên, sự tiến bộ là đáng kể.
Tôi sẽ nói về nó trong bài viết.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

nội dung

Kiến trúc và các vấn đề của giải pháp hiện có


Ban đầu, kiến ​​trúc trông như thế này: UI là một ứng dụng riêng biệt, phần nguyên khối được viết bằng Visual Basic 6, ứng dụng .NET là một tập hợp các dịch vụ liên quan hoạt động với cơ sở dữ liệu khá lớn.

Nhược điểm của giải pháp trước

Điểm thất bại duy nhất
Chúng tôi có một điểm lỗi duy nhất: ứng dụng .NET chạy trong một quy trình duy nhất. Nếu bất kỳ mô-đun nào bị lỗi, toàn bộ ứng dụng sẽ bị lỗi và phải khởi động lại. Vì chúng tôi tự động hóa một số lượng lớn quy trình cho những người dùng khác nhau nên do một trong số đó bị lỗi nên mọi người đều không thể làm việc trong một thời gian. Và trong trường hợp xảy ra lỗi phần mềm thì ngay cả việc sao lưu cũng không giúp được gì.

Hàng đợi cải tiến
Hạn chế này là khá tổ chức. Ứng dụng của chúng tôi có nhiều khách hàng và tất cả họ đều muốn cải thiện nó càng sớm càng tốt. Trước đây, việc này không thể thực hiện song song và tất cả khách hàng đều phải xếp hàng. Quá trình này mang tính tiêu cực đối với các doanh nghiệp vì họ phải chứng minh rằng nhiệm vụ của họ có giá trị. Và đội ngũ phát triển đã dành thời gian sắp xếp hàng đợi này. Việc này tốn rất nhiều thời gian và công sức, và cuối cùng sản phẩm không thể thay đổi nhanh chóng như họ mong muốn.

Sử dụng tài nguyên dưới mức tối ưu
Khi lưu trữ các dịch vụ trong một quy trình, chúng tôi luôn sao chép hoàn toàn cấu hình từ máy chủ này sang máy chủ khác. Chúng tôi muốn đặt riêng các dịch vụ được tải nặng nhất để không lãng phí tài nguyên và có được quyền kiểm soát linh hoạt hơn đối với kế hoạch triển khai của mình.

Khó triển khai công nghệ hiện đại
Một vấn đề quen thuộc với tất cả các nhà phát triển: mong muốn đưa các công nghệ hiện đại vào dự án nhưng không có cơ hội. Với một giải pháp nguyên khối lớn, bất kỳ bản cập nhật nào của thư viện hiện tại, chưa kể đến việc chuyển sang thư viện mới, đều trở thành một nhiệm vụ khá không hề nhỏ. Phải mất một thời gian dài để chứng minh cho trưởng nhóm thấy rằng điều này sẽ mang lại nhiều tiền thưởng hơn là lãng phí thần kinh.

Khó đưa ra các thay đổi
Đây là vấn đề nghiêm trọng nhất - chúng tôi phát hành bản phát hành hai tháng một lần.
Mỗi bản phát hành đều trở thành một thảm họa thực sự đối với ngân hàng, bất chấp sự thử nghiệm và nỗ lực của các nhà phát triển. Doanh nghiệp hiểu rằng vào đầu tuần, một số chức năng của nó sẽ không hoạt động. Và các nhà phát triển hiểu rằng một tuần đầy sự cố nghiêm trọng đang chờ đợi họ.
Mọi người đều có mong muốn thay đổi tình hình.

Kỳ vọng từ microservice


Vấn đề về các thành phần khi đã sẵn sàng. Cung cấp các thành phần khi đã sẵn sàng bằng cách phân tách giải pháp và tách các quy trình khác nhau.

Nhóm sản phẩm nhỏ. Điều này rất quan trọng vì rất khó quản lý một nhóm lớn làm việc trên nền tảng nguyên khối cũ. Một nhóm như vậy buộc phải làm việc theo một quy trình nghiêm ngặt, nhưng họ muốn có sự sáng tạo và độc lập hơn. Chỉ những đội nhỏ mới có thể đủ khả năng này.

Cô lập các dịch vụ trong các quy trình riêng biệt. Lý tưởng nhất là tôi muốn cô lập nó trong các vùng chứa, nhưng một số lượng lớn các dịch vụ được viết bằng .NET Framework chỉ chạy trên Windows. Các dịch vụ dựa trên .NET Core hiện đang xuất hiện nhưng vẫn còn rất ít.

Triển khai linh hoạt. Chúng tôi muốn kết hợp các dịch vụ theo cách chúng tôi cần chứ không phải theo cách mã ép buộc.

Sử dụng các công nghệ mới. Đây là điều thú vị đối với bất kỳ lập trình viên nào.

Vấn đề chuyển tiếp


Tất nhiên, nếu việc chia khối nguyên khối thành microservice có thể dễ dàng thì sẽ không cần phải nói về nó tại các hội nghị và viết bài. Có rất nhiều cạm bẫy trong quá trình này; tôi sẽ mô tả những cạm bẫy chính đã cản trở chúng tôi.

Vấn đề đầu tiên điển hình cho hầu hết các khối nguyên khối: tính mạch lạc của logic nghiệp vụ. Khi chúng tôi viết một khối, chúng tôi muốn sử dụng lại các lớp của mình để không viết mã không cần thiết. Và khi chuyển sang microservices, điều này trở thành một vấn đề: tất cả các mã được liên kết khá chặt chẽ và rất khó để tách các dịch vụ ra.

Vào thời điểm bắt đầu hoạt động, kho lưu trữ có hơn 500 dự án và hơn 700 nghìn dòng mã. Đây là một quyết định khá lớn và vấn đề thứ hai. Không thể đơn giản lấy nó và chia thành các microservice.

Vấn đề thứ ba - thiếu cơ sở hạ tầng cần thiết. Trên thực tế, chúng tôi đã sao chép mã nguồn vào máy chủ theo cách thủ công.

Cách chuyển từ nguyên khối sang microservice


Cung cấp vi dịch vụ

Đầu tiên, chúng tôi ngay lập tức xác định rằng việc tách các vi dịch vụ là một quá trình lặp đi lặp lại. Chúng tôi luôn được yêu cầu phát triển song song các vấn đề kinh doanh. Làm thế nào chúng tôi sẽ thực hiện điều này về mặt kỹ thuật đã là vấn đề của chúng tôi. Vì vậy, chúng tôi đã chuẩn bị cho một quá trình lặp đi lặp lại. Nó sẽ không hoạt động theo bất kỳ cách nào khác nếu bạn có một ứng dụng lớn và ban đầu nó chưa sẵn sàng để viết lại.

Chúng tôi sử dụng phương pháp nào để cô lập microservice?

Cách đầu tiên - di chuyển các mô-đun hiện có dưới dạng dịch vụ. Về vấn đề này, chúng tôi thật may mắn: đã có những dịch vụ được đăng ký hoạt động bằng giao thức WCF. Họ được tách thành các hội đồng riêng biệt. Chúng tôi đã chuyển chúng một cách riêng biệt, thêm một trình khởi chạy nhỏ vào mỗi bản dựng. Nó được viết bằng thư viện Topshelf tuyệt vời, cho phép bạn chạy ứng dụng dưới dạng dịch vụ và bảng điều khiển. Điều này thuận tiện cho việc gỡ lỗi vì không cần thêm dự án nào trong giải pháp.

Các dịch vụ được kết nối theo logic nghiệp vụ vì chúng sử dụng các cụm chung và làm việc với cơ sở dữ liệu chung. Chúng khó có thể được gọi là microservice ở dạng thuần túy. Tuy nhiên, chúng tôi có thể cung cấp các dịch vụ này một cách riêng biệt, theo các quy trình khác nhau. Chỉ riêng điều này đã có thể làm giảm ảnh hưởng của họ lên nhau, giảm bớt vấn đề phát triển song song và một điểm thất bại duy nhất.

Hợp ngữ với máy chủ chỉ là một dòng mã trong lớp Chương trình. Chúng tôi giấu công việc với Topshelf trong một lớp phụ trợ.

namespace RBA.Services.Accounts.Host
{
   internal class Program
   {
      private static void Main(string[] args)
      {
        HostRunner<Accounts>.Run("RBA.Services.Accounts.Host");

       }
    }
}

Cách thứ hai để phân bổ microservices là: tạo ra chúng để giải quyết những vấn đề mới. Nếu đồng thời khối đá nguyên khối không phát triển thì điều này đã rất xuất sắc, có nghĩa là chúng ta đang đi đúng hướng. Để giải quyết những vấn đề mới, chúng tôi đã cố gắng tạo ra các dịch vụ riêng biệt. Nếu có cơ hội như vậy, thì chúng tôi đã tạo ra nhiều dịch vụ “chuẩn” hơn để quản lý hoàn toàn mô hình dữ liệu của riêng họ, một cơ sở dữ liệu riêng biệt.

Chúng tôi cũng như nhiều người khác, bắt đầu với các dịch vụ xác thực và ủy quyền. Họ là hoàn hảo cho việc này. Chúng độc lập và theo quy luật, chúng có một mô hình dữ liệu riêng biệt. Bản thân họ không tương tác với khối đá nguyên khối mà chỉ nhờ họ giải quyết một số vấn đề. Sử dụng các dịch vụ này, bạn có thể bắt đầu chuyển đổi sang kiến ​​trúc mới, gỡ lỗi cơ sở hạ tầng trên chúng, thử một số phương pháp liên quan đến thư viện mạng, v.v. Chúng tôi không có nhóm nào trong tổ chức của mình không thể tạo dịch vụ xác thực.

Cách thứ ba để phân bổ microserviceCái chúng tôi sử dụng hơi cụ thể đối với chúng tôi. Đây là việc loại bỏ logic nghiệp vụ khỏi lớp giao diện người dùng. Ứng dụng giao diện người dùng chính của chúng tôi là máy tính để bàn; nó, giống như phần phụ trợ, được viết bằng C#. Các nhà phát triển định kỳ mắc lỗi và chuyển các phần logic sang giao diện người dùng mà lẽ ra phải tồn tại trong phần phụ trợ và được sử dụng lại.

Nếu nhìn vào một ví dụ thực tế từ mã của phần giao diện người dùng, bạn có thể thấy rằng hầu hết giải pháp này chứa logic nghiệp vụ thực sự hữu ích trong các quy trình khác, không chỉ để xây dựng biểu mẫu giao diện người dùng.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Logic giao diện người dùng thực sự chỉ có ở vài dòng cuối cùng. Chúng tôi đã chuyển nó đến máy chủ để có thể tái sử dụng, từ đó giảm giao diện người dùng và đạt được kiến ​​trúc chính xác.

Cách thứ tư và quan trọng nhất để tách biệt microservices, giúp có thể giảm bớt tính nguyên khối, là loại bỏ các dịch vụ hiện có bằng quá trình xử lý. Khi chúng tôi loại bỏ các mô-đun hiện có, kết quả không phải lúc nào cũng theo ý muốn của nhà phát triển và quy trình kinh doanh có thể trở nên lỗi thời kể từ khi chức năng được tạo. Với việc tái cấu trúc, chúng tôi có thể hỗ trợ một quy trình kinh doanh mới vì các yêu cầu kinh doanh liên tục thay đổi. Chúng tôi có thể cải thiện mã nguồn, loại bỏ các lỗi đã biết và tạo mô hình dữ liệu tốt hơn. Có rất nhiều lợi ích tích lũy.

Việc tách các dịch vụ khỏi quá trình xử lý có mối liên hệ chặt chẽ với khái niệm bối cảnh bị ràng buộc. Đây là một khái niệm từ Thiết kế hướng tên miền. Nó có nghĩa là một phần của mô hình miền trong đó tất cả các thuật ngữ của một ngôn ngữ được xác định duy nhất. Hãy xem bối cảnh của bảo hiểm và hóa đơn làm ví dụ. Chúng tôi có một ứng dụng nguyên khối và chúng tôi cần làm việc với tài khoản trong bảo hiểm. Chúng tôi hy vọng nhà phát triển sẽ tìm thấy lớp Tài khoản hiện có trong một tập hợp khác, tham chiếu nó từ lớp Bảo hiểm và chúng tôi sẽ có mã hoạt động. Nguyên tắc DRY sẽ được tôn trọng, tác vụ sẽ được thực hiện nhanh hơn bằng cách sử dụng mã hiện có.

Kết quả là, bối cảnh của tài khoản và bảo hiểm được kết nối với nhau. Khi các yêu cầu mới xuất hiện, sự kết hợp này sẽ cản trở sự phát triển, làm tăng độ phức tạp của logic kinh doanh vốn đã phức tạp. Để giải quyết vấn đề này, bạn cần tìm ra ranh giới giữa các ngữ cảnh trong mã và loại bỏ các vi phạm của chúng. Ví dụ: trong bối cảnh bảo hiểm, rất có thể số tài khoản Ngân hàng Trung ương gồm 20 chữ số và ngày mở tài khoản là đủ.

Để tách các bối cảnh bị giới hạn này với nhau và bắt đầu quá trình tách các dịch vụ vi mô khỏi giải pháp nguyên khối, chúng tôi đã sử dụng một phương pháp như tạo các API bên ngoài trong ứng dụng. Nếu chúng tôi biết rằng một số mô-đun sẽ trở thành một vi dịch vụ, được sửa đổi bằng cách nào đó trong quy trình, thì chúng tôi ngay lập tức thực hiện các lệnh gọi đến logic thuộc một bối cảnh giới hạn khác thông qua các lệnh gọi bên ngoài. Ví dụ: thông qua REST hoặc WCF.

Chúng tôi đã quyết định chắc chắn rằng chúng tôi sẽ không tránh việc mã yêu cầu các giao dịch phân tán. Trong trường hợp của chúng tôi, việc tuân theo quy tắc này hóa ra khá dễ dàng. Chúng tôi chưa gặp phải tình huống thực sự cần đến các giao dịch phân tán nghiêm ngặt - tính nhất quán cuối cùng giữa các mô-đun là khá đủ.

Hãy xem xét một ví dụ cụ thể. Chúng tôi có khái niệm về bộ điều phối - một đường dẫn xử lý thực thể của “ứng dụng”. Anh ta lần lượt tạo một khách hàng, một tài khoản và một thẻ ngân hàng. Nếu khách hàng và tài khoản được tạo thành công nhưng việc tạo thẻ không thành công, ứng dụng sẽ không chuyển sang trạng thái “thành công” và vẫn ở trạng thái “chưa tạo thẻ”. Trong tương lai, hoạt động nền sẽ nhặt nó lên và hoàn thành nó. Hệ thống đã ở trạng thái không nhất quán một thời gian, nhưng nhìn chung chúng tôi hài lòng với điều này.

Nếu một tình huống phát sinh khi cần phải lưu một phần dữ liệu một cách nhất quán, rất có thể chúng tôi sẽ hợp nhất dịch vụ để xử lý dữ liệu đó trong một quy trình.

Hãy xem một ví dụ về việc phân bổ một microservice. Làm thế nào bạn có thể đưa nó vào sản xuất một cách tương đối an toàn? Trong ví dụ này, chúng tôi có một phần riêng biệt của hệ thống - mô-đun dịch vụ trả lương, một trong những phần mã mà chúng tôi muốn tạo microservice.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Trước hết, chúng ta tạo một microservice bằng cách viết lại mã. Chúng tôi đang cải thiện một số khía cạnh mà chúng tôi không hài lòng. Chúng tôi thực hiện các yêu cầu kinh doanh mới từ khách hàng. Chúng tôi thêm Cổng API vào kết nối giữa giao diện người dùng và chương trình phụ trợ, cổng này sẽ cung cấp tính năng chuyển tiếp cuộc gọi.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Tiếp theo, chúng tôi đưa cấu hình này vào hoạt động nhưng ở trạng thái thử nghiệm. Hầu hết người dùng của chúng tôi vẫn làm việc với các quy trình kinh doanh cũ. Đối với người dùng mới, chúng tôi đang phát triển một phiên bản mới của ứng dụng nguyên khối không còn chứa quy trình này nữa. Về cơ bản, chúng tôi có sự kết hợp giữa nguyên khối và vi dịch vụ hoạt động như một thử nghiệm.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Với một cuộc thử nghiệm thành công, chúng tôi hiểu rằng cấu hình mới thực sự có thể thực hiện được, chúng tôi có thể loại bỏ khối cũ khỏi phương trình và để lại cấu hình mới thay cho giải pháp cũ.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Tổng cộng, chúng tôi sử dụng hầu hết tất cả các phương pháp hiện có để phân tách mã nguồn của một khối nguyên khối. Tất cả chúng đều cho phép chúng tôi giảm kích thước các phần của ứng dụng và dịch chúng sang các thư viện mới, tạo ra mã nguồn tốt hơn.

Làm việc với cơ sở dữ liệu


Cơ sở dữ liệu có thể được phân chia kém hơn mã nguồn, vì nó không chỉ chứa lược đồ hiện tại mà còn chứa dữ liệu lịch sử tích lũy.

Cơ sở dữ liệu của chúng tôi, giống như nhiều cơ sở dữ liệu khác, có một nhược điểm quan trọng khác - kích thước khổng lồ của nó. Cơ sở dữ liệu này được thiết kế theo logic nghiệp vụ phức tạp của một khối nguyên khối và các mối quan hệ được tích lũy giữa các bảng của các bối cảnh giới hạn khác nhau.

Trong trường hợp của chúng tôi, ngoài tất cả những rắc rối (cơ sở dữ liệu lớn, nhiều kết nối, đôi khi ranh giới không rõ ràng giữa các bảng), một vấn đề nảy sinh xảy ra trong nhiều dự án lớn: việc sử dụng mẫu cơ sở dữ liệu dùng chung. Dữ liệu được lấy từ các bảng thông qua chế độ xem, thông qua sao chép và chuyển đến các hệ thống khác nơi cần sao chép này. Kết quả là chúng tôi không thể di chuyển các bảng sang một lược đồ riêng vì chúng được sử dụng tích cực.

Việc phân chia tương tự thành các ngữ cảnh giới hạn trong mã giúp chúng ta phân tách. Nó thường cho chúng ta một ý tưởng khá hay về cách chúng ta chia nhỏ dữ liệu ở cấp cơ sở dữ liệu. Chúng tôi hiểu bảng nào thuộc về một bối cảnh bị chặn và bảng nào thuộc về bối cảnh khác.

Chúng tôi đã sử dụng hai phương pháp phân vùng cơ sở dữ liệu toàn cầu: phân vùng các bảng hiện có và phân vùng có xử lý.

Tách các bảng hiện có là một phương pháp tốt để sử dụng nếu cấu trúc dữ liệu tốt, đáp ứng yêu cầu kinh doanh và mọi người đều hài lòng với nó. Trong trường hợp này, chúng ta có thể tách các bảng hiện có thành một lược đồ riêng.

Cần có một bộ phận xử lý khi mô hình kinh doanh đã thay đổi rất nhiều và các bảng không còn làm chúng tôi hài lòng nữa.

Tách các bảng hiện có. Chúng ta cần phải xác định những gì chúng ta sẽ tách ra. Nếu không có kiến ​​​​thức này, sẽ không có gì hoạt động và ở đây việc tách các bối cảnh bị giới hạn trong mã sẽ giúp ích cho chúng ta. Theo quy định, nếu bạn có thể hiểu được ranh giới của các ngữ cảnh trong mã nguồn, thì bạn sẽ thấy rõ những bảng nào sẽ được đưa vào danh sách dành cho bộ phận.

Hãy tưởng tượng rằng chúng ta có một giải pháp trong đó hai mô-đun nguyên khối tương tác với một cơ sở dữ liệu. Chúng tôi cần đảm bảo rằng chỉ một mô-đun tương tác với phần của các bảng riêng biệt và mô-đun còn lại bắt đầu tương tác với nó thông qua API. Để bắt đầu, chỉ cần thực hiện ghi âm thông qua API là đủ. Đây là điều kiện cần để chúng ta nói về tính độc lập của microservices. Kết nối đọc có thể duy trì miễn là không có vấn đề gì lớn.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Bước tiếp theo là chúng ta có thể tách phần mã hoạt động với các bảng riêng biệt, có hoặc không có quá trình xử lý, thành một vi dịch vụ riêng biệt và chạy nó trong một quy trình riêng, một vùng chứa. Đây sẽ là một dịch vụ riêng biệt có kết nối với cơ sở dữ liệu nguyên khối và các bảng không liên quan trực tiếp đến nó. Khối nguyên khối vẫn tương tác để đọc với phần có thể tháo rời.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Sau này chúng ta sẽ loại bỏ kết nối này, tức là việc đọc dữ liệu từ một ứng dụng nguyên khối từ các bảng riêng biệt cũng sẽ được chuyển sang API.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Tiếp theo, chúng ta sẽ chọn từ cơ sở dữ liệu chung các bảng mà chỉ microservice mới mới hoạt động. Chúng ta có thể di chuyển các bảng sang một lược đồ riêng biệt hoặc thậm chí sang một cơ sở dữ liệu vật lý riêng biệt. Vẫn có kết nối đọc giữa microservice và cơ sở dữ liệu nguyên khối, nhưng không có gì phải lo lắng, ở cấu hình này nó có thể tồn tại khá lâu.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Bước cuối cùng là loại bỏ hoàn toàn tất cả các kết nối. Trong trường hợp này, chúng tôi có thể cần di chuyển dữ liệu từ cơ sở dữ liệu chính. Đôi khi chúng tôi muốn sử dụng lại một số dữ liệu hoặc thư mục được sao chép từ hệ thống bên ngoài trong một số cơ sở dữ liệu. Điều này xảy ra với chúng tôi theo định kỳ.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Bộ phận xử lý. Phương pháp này rất giống với phương pháp đầu tiên, chỉ theo thứ tự ngược lại. Chúng tôi ngay lập tức phân bổ một cơ sở dữ liệu mới và một vi dịch vụ mới tương tác với khối nguyên khối thông qua API. Nhưng đồng thời, vẫn còn một tập hợp các bảng cơ sở dữ liệu mà chúng tôi muốn xóa trong tương lai. Chúng tôi không cần nó nữa; chúng tôi đã thay thế nó bằng mô hình mới.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Để kế hoạch này có hiệu quả, chúng ta có thể sẽ cần một giai đoạn chuyển tiếp.

Sau đó có hai cách tiếp cận có thể.

Đầu tiên: chúng tôi sao chép tất cả dữ liệu trong cơ sở dữ liệu mới và cũ. Trong trường hợp này, chúng tôi có thể phát sinh vấn đề về dự phòng dữ liệu và đồng bộ hóa. Nhưng chúng ta có thể nhận hai khách hàng khác nhau. Một cái sẽ hoạt động với phiên bản mới, cái còn lại với phiên bản cũ.

Thứ hai: chúng tôi phân chia dữ liệu theo một số tiêu chí kinh doanh. Ví dụ: chúng tôi có 5 sản phẩm trong hệ thống được lưu trữ trong cơ sở dữ liệu cũ. Chúng tôi đặt nhiệm vụ thứ sáu trong nhiệm vụ kinh doanh mới trong cơ sở dữ liệu mới. Nhưng chúng tôi sẽ cần một Cổng API sẽ đồng bộ hóa dữ liệu này và hiển thị cho khách hàng vị trí và nội dung cần lấy.

Cả hai cách tiếp cận đều có tác dụng, hãy chọn tùy theo tình huống.

Sau khi chúng tôi chắc chắn rằng mọi thứ đều hoạt động, phần nguyên khối hoạt động với cấu trúc cơ sở dữ liệu cũ có thể bị vô hiệu hóa.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Bước cuối cùng là loại bỏ cấu trúc dữ liệu cũ.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Tóm lại, chúng ta có thể nói rằng chúng ta có vấn đề với cơ sở dữ liệu: làm việc với nó khó so với mã nguồn, khó chia sẻ hơn, nhưng có thể và nên làm. Chúng tôi đã tìm ra một số cách cho phép chúng tôi thực hiện việc này khá an toàn, nhưng vẫn dễ mắc lỗi với dữ liệu hơn là với mã nguồn.

Làm việc với mã nguồn


Đây là sơ đồ mã nguồn khi chúng tôi bắt đầu phân tích dự án nguyên khối.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Nó có thể được chia thành ba lớp. Đây là một lớp gồm các mô-đun, plugin, dịch vụ và hoạt động riêng lẻ được khởi chạy. Trên thực tế, đây là những điểm đầu vào trong một giải pháp nguyên khối. Tất cả đều được niêm phong chặt chẽ bằng một lớp Common. Nó có logic kinh doanh mà các dịch vụ được chia sẻ và rất nhiều kết nối. Mỗi dịch vụ và plugin sử dụng tối đa 10 cụm phổ biến trở lên, tùy thuộc vào quy mô của chúng và lương tâm của các nhà phát triển.

Chúng tôi thật may mắn khi có các thư viện cơ sở hạ tầng có thể được sử dụng riêng.

Đôi khi xảy ra tình huống khi một số đối tượng chung không thực sự thuộc về lớp này mà là các thư viện cơ sở hạ tầng. Điều này đã được giải quyết bằng cách đổi tên.

Mối quan tâm lớn nhất là bối cảnh bị giới hạn. Đã xảy ra trường hợp 3-4 bối cảnh được trộn lẫn trong một Hội đồng chung và sử dụng lẫn nhau trong cùng các chức năng kinh doanh. Cần phải hiểu điều này có thể được phân chia ở đâu và dọc theo ranh giới nào cũng như phải làm gì tiếp theo khi ánh xạ sự phân chia này thành các tập hợp mã nguồn.

Chúng tôi đã xây dựng một số quy tắc cho quá trình tách mã.

Việc đầu tiên: Chúng tôi không còn muốn chia sẻ logic kinh doanh giữa các dịch vụ, hoạt động và plugin nữa. Chúng tôi muốn làm cho logic kinh doanh trở nên độc lập trong microservice. Mặt khác, microservice được coi là dịch vụ tồn tại hoàn toàn độc lập. Tôi tin rằng cách tiếp cận này hơi lãng phí và khó đạt được, vì chẳng hạn, các dịch vụ trong C# trong mọi trường hợp sẽ được kết nối bằng một thư viện chuẩn. Hệ thống của chúng tôi được viết bằng C#; chúng tôi chưa sử dụng các công nghệ khác. Vì vậy, chúng tôi quyết định rằng chúng tôi có đủ khả năng để sử dụng các cụm kỹ thuật thông thường. Điều chính là chúng không chứa bất kỳ đoạn logic nghiệp vụ nào. Nếu bạn có một trình bao bọc tiện lợi trên ORM mà bạn đang sử dụng thì việc sao chép nó từ dịch vụ này sang dịch vụ khác sẽ rất tốn kém.

Nhóm của chúng tôi là người yêu thích thiết kế theo hướng miền, vì vậy kiến ​​trúc củ hành rất phù hợp với chúng tôi. Cơ sở dịch vụ của chúng tôi không phải là lớp truy cập dữ liệu mà là một tập hợp với logic miền, chỉ chứa logic nghiệp vụ và không có kết nối với cơ sở hạ tầng. Đồng thời, chúng ta có thể sửa đổi tập hợp miền một cách độc lập để giải quyết các vấn đề liên quan đến khung.

Ở giai đoạn này, chúng tôi gặp phải vấn đề nghiêm trọng đầu tiên. Dịch vụ này phải tham chiếu đến một tập hợp miền, chúng tôi muốn làm cho logic trở nên độc lập và nguyên tắc DRY đã cản trở chúng tôi rất nhiều ở đây. Các nhà phát triển muốn sử dụng lại các lớp từ các tập hợp lân cận để tránh trùng lặp và kết quả là các miền bắt đầu được liên kết lại với nhau. Chúng tôi đã phân tích kết quả và quyết định rằng có lẽ vấn đề còn nằm ở khu vực của thiết bị lưu trữ mã nguồn. Chúng tôi có một kho lưu trữ lớn chứa tất cả mã nguồn. Giải pháp cho toàn bộ dự án là rất khó lắp ráp trên máy cục bộ. Do đó, các giải pháp nhỏ riêng biệt đã được tạo ra cho các phần của dự án và không ai cấm thêm một số cụm chung hoặc miền vào chúng và tái sử dụng chúng. Công cụ duy nhất không cho phép chúng tôi làm điều này là xem xét mã. Nhưng đôi khi nó cũng thất bại.

Sau đó, chúng tôi bắt đầu chuyển sang mô hình có kho lưu trữ riêng biệt. Logic nghiệp vụ không còn chuyển từ dịch vụ này sang dịch vụ khác nữa, các miền đã thực sự trở nên độc lập. Bối cảnh giới hạn được hỗ trợ rõ ràng hơn. Làm cách nào để chúng tôi sử dụng lại các thư viện cơ sở hạ tầng? Chúng tôi tách chúng thành một kho lưu trữ riêng, sau đó đặt chúng vào các gói Nuget mà chúng tôi đưa vào Artifactory. Với bất kỳ thay đổi nào, quá trình lắp ráp và xuất bản diễn ra tự động.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Các dịch vụ của chúng tôi bắt đầu tham chiếu các gói cơ sở hạ tầng nội bộ giống như các gói bên ngoài. Chúng tôi tải xuống các thư viện bên ngoài từ Nuget. Để làm việc với Artifactory, nơi chúng tôi đặt các gói này, chúng tôi đã sử dụng hai trình quản lý gói. Trong các kho lưu trữ nhỏ, chúng tôi cũng sử dụng Nuget. Trong các kho lưu trữ có nhiều dịch vụ, chúng tôi đã sử dụng Paket, cung cấp tính nhất quán về phiên bản hơn giữa các mô-đun.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Do đó, bằng cách xử lý mã nguồn, thay đổi một chút kiến ​​trúc và tách các kho lưu trữ, chúng tôi làm cho các dịch vụ của mình trở nên độc lập hơn.

Vấn đề cơ sở hạ tầng


Hầu hết nhược điểm của việc chuyển sang microservice là liên quan đến cơ sở hạ tầng. Bạn sẽ cần triển khai tự động, bạn sẽ cần các thư viện mới để chạy cơ sở hạ tầng.

Cài đặt thủ công trong môi trường

Ban đầu, chúng tôi cài đặt giải pháp cho môi trường theo cách thủ công. Để tự động hóa quy trình này, chúng tôi đã tạo một quy trình CI/CD. Chúng tôi chọn quy trình phân phối liên tục vì việc triển khai liên tục vẫn chưa được chúng tôi chấp nhận từ quan điểm của quy trình kinh doanh. Do đó, việc gửi để vận hành được thực hiện bằng một nút và để kiểm tra - tự động.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Chúng tôi sử dụng Atlassian, Bitbucket để lưu trữ mã nguồn và Bamboo để xây dựng. Chúng tôi thích viết tập lệnh xây dựng trong Cake vì nó giống với C#. Các gói làm sẵn sẽ đến Artifactory và Ansible sẽ tự động truy cập vào các máy chủ thử nghiệm, sau đó chúng có thể được kiểm tra ngay lập tức.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Ghi nhật ký riêng


Có một thời, một trong những ý tưởng của khối nguyên khối là cung cấp tính năng ghi nhật ký chung. Chúng tôi cũng cần hiểu phải làm gì với từng nhật ký riêng lẻ trên đĩa. Nhật ký của chúng tôi được ghi vào tập tin văn bản. Chúng tôi quyết định sử dụng ngăn xếp ELK tiêu chuẩn. Chúng tôi không viết thư trực tiếp cho ELK thông qua các nhà cung cấp nhưng đã quyết định rằng chúng tôi sẽ sửa đổi nhật ký văn bản và viết ID theo dõi trong đó làm mã nhận dạng, thêm tên dịch vụ để những nhật ký này có thể được phân tích cú pháp sau này.

Quá trình chuyển đổi từ nguyên khối sang microservice: lịch sử và thực tiễn

Khi sử dụng Filebeat, chúng tôi có cơ hội thu thập nhật ký của mình từ máy chủ, sau đó chuyển đổi chúng, sử dụng Kibana để tạo truy vấn trong giao diện người dùng và xem cuộc gọi diễn ra như thế nào giữa các dịch vụ. ID dấu vết giúp ích rất nhiều cho việc này.

Kiểm tra và gỡ lỗi các dịch vụ liên quan


Ban đầu, chúng tôi không hiểu đầy đủ về cách gỡ lỗi các dịch vụ đang được phát triển. Mọi thứ đều đơn giản với khối nguyên khối; chúng tôi chạy nó trên máy cục bộ. Lúc đầu, họ cố gắng làm điều tương tự với microservice, nhưng đôi khi để khởi chạy hoàn toàn một microservice, bạn cần khởi chạy một số microservice khác và điều này thật bất tiện. Chúng tôi nhận ra rằng chúng tôi cần chuyển sang một mô hình mà chúng tôi chỉ để lại trên máy cục bộ dịch vụ hoặc các dịch vụ mà chúng tôi muốn gỡ lỗi. Các dịch vụ còn lại được sử dụng từ các máy chủ có cấu hình phù hợp với prod. Sau khi gỡ lỗi, trong quá trình thử nghiệm, đối với mỗi tác vụ, chỉ những dịch vụ đã thay đổi mới được cấp cho máy chủ thử nghiệm. Do đó, giải pháp được thử nghiệm ở dạng mà nó sẽ xuất hiện trong sản xuất trong tương lai.

Có những máy chủ chỉ chạy phiên bản sản xuất của dịch vụ. Những máy chủ này cần thiết trong trường hợp có sự cố, để kiểm tra việc phân phối trước khi triển khai và đào tạo nội bộ.

Chúng tôi đã thêm quy trình kiểm tra tự động bằng thư viện Specflow phổ biến. Các thử nghiệm sẽ tự động chạy bằng NUnit ngay sau khi triển khai từ Ansible. Nếu phạm vi nhiệm vụ hoàn toàn tự động thì không cần phải kiểm tra thủ công. Mặc dù đôi khi vẫn cần phải kiểm tra thủ công bổ sung. Chúng tôi sử dụng thẻ trong Jira để xác định nên chạy thử nghiệm nào cho một vấn đề cụ thể.

Ngoài ra, nhu cầu kiểm tra tải đã tăng lên; trước đây nó chỉ được thực hiện trong một số trường hợp hiếm hoi. Chúng tôi sử dụng JMeter để chạy thử nghiệm, InfluxDB để lưu trữ chúng và Grafana để xây dựng biểu đồ quy trình.

Chúng ta đã đạt được những gì?


Đầu tiên, chúng tôi loại bỏ khái niệm “phát hành”. Đã qua rồi những bản phát hành khủng khiếp kéo dài hai tháng khi bức tượng khổng lồ này được triển khai trong môi trường sản xuất, tạm thời làm gián đoạn các quy trình kinh doanh. Hiện tại, chúng tôi triển khai các dịch vụ trung bình 1,5 ngày một lần, nhóm chúng lại vì chúng sẽ đi vào hoạt động sau khi được phê duyệt.

Không có lỗi nghiêm trọng trong hệ thống của chúng tôi. Nếu chúng tôi phát hành một microservice có lỗi thì chức năng liên quan đến nó sẽ bị hỏng và tất cả các chức năng khác sẽ không bị ảnh hưởng. Điều này cải thiện đáng kể trải nghiệm người dùng.

Chúng tôi có thể kiểm soát mô hình triển khai. Bạn có thể chọn các nhóm dịch vụ riêng biệt với phần còn lại của giải pháp, nếu cần.

Ngoài ra, chúng tôi đã giảm thiểu đáng kể vấn đề với hàng loạt cải tiến. Hiện tại, chúng tôi có các nhóm sản phẩm riêng biệt hoạt động độc lập với một số dịch vụ. Quy trình Scrum đã rất phù hợp ở đây. Một nhóm cụ thể có thể có một Chủ sở hữu sản phẩm riêng biệt, người sẽ giao nhiệm vụ cho nhóm đó.

Tóm tắt thông tin

  • Microservice rất phù hợp để phân tách các hệ thống phức tạp. Trong quá trình này, chúng tôi bắt đầu hiểu hệ thống của mình có những gì, có những bối cảnh hạn chế nào, ranh giới của chúng nằm ở đâu. Điều này cho phép bạn phân phối chính xác các cải tiến giữa các mô-đun và ngăn ngừa nhầm lẫn mã.
  • Microservice mang lại lợi ích cho tổ chức. Chúng thường chỉ được nhắc đến dưới dạng kiến ​​trúc, nhưng bất kỳ kiến ​​trúc nào cũng cần thiết để giải quyết các nhu cầu kinh doanh chứ không phải riêng lẻ. Do đó, chúng ta có thể nói rằng microservice rất phù hợp để giải quyết các vấn đề trong các nhóm nhỏ, vì Scrum hiện rất phổ biến.
  • Sự tách biệt là một quá trình lặp đi lặp lại. Bạn không thể lấy một ứng dụng và chỉ chia nó thành các vi dịch vụ. Sản phẩm tạo ra khó có thể hoạt động được. Khi cung cấp microservice, việc viết lại di sản hiện có sẽ có lợi, tức là biến nó thành mã mà chúng ta thích và đáp ứng tốt hơn nhu cầu kinh doanh về chức năng và tốc độ.

    Một cảnh báo nhỏ: Chi phí chuyển sang microservice là khá đáng kể. Phải mất một thời gian dài mới giải quyết được vấn đề cơ sở hạ tầng. Vì vậy, nếu bạn có một ứng dụng nhỏ không yêu cầu mở rộng quy mô cụ thể, trừ khi bạn có một số lượng lớn khách hàng đang cạnh tranh để giành được sự chú ý và thời gian của nhóm, thì vi dịch vụ có thể không phải là thứ bạn cần ngày nay. Nó khá đắt. Nếu bạn bắt đầu quá trình với microservices, thì chi phí ban đầu sẽ cao hơn so với khi bạn bắt đầu cùng một dự án với việc phát triển nguyên khối.

    Tái bút Một câu chuyện cảm động hơn (và như thể dành cho cá nhân bạn) - theo liên kết.
    Đây là phiên bản đầy đủ của báo cáo.

Nguồn: www.habr.com

Thêm một lời nhận xét