聯席作者:吳毅挺 任浩軍 張彬彬 廖夢鴿 張金星 胡振建
鄭重鳴謝:Nacos - 彥林,Spring Cloud Alibab - 小馬哥、落夜,Nacos 社區 - 張龍(pader)、春少(chuntaojun)
前言
在高速發展的時候,公司規模越來越大,老師人數越來越多,這時候公司不能鋪太多人去做運營與服務,必須提高每個人效,這就需要技術驅動。因此掌門教育轉變成一家技術驅動型的公司,如果被迫成為一家靠資金驅動的公司就活不下去了。
-- 張翼(掌門教育創始人兼 CEO)
掌門教育自 2014 年正式轉型在線教育以來,秉承“讓教育共享智能,讓學習高效快樂”的宗旨和願景,經歷雲計算、大數據、人工智能、 AR / VR / MR 以及現今最火的 5G ,一直堅持用科技賦能教育。掌門教育的業務近幾年得到了快速發展,特別是今年的疫情,使在線教育成為了新的風口,也給掌門教育新的機遇。
隨着業務規模進一步擴大,流量進一步暴增,微服務數目進一步增長,使老的微服務體系所采用的注冊中心 Eureka 不堪重負,同時 Spring Cloud 體系已經演進到第二代,第一代的 Eureka 注冊中心已經不大適合現在的業務邏輯和規模,同時它目前被 Spring Cloud 官方置於維護模式,將不再向前發展。如何選擇一個更為優秀和適用的注冊中心,這個課題就擺在了掌門人的面前。經過對 Alibaba Nacos 、HashiCorp Consul 等開源注冊中心做了深入的調研和比較,最終選定 Alibaba Nacos 做微服務體系 Solar 中的新注冊中心。
背景故事
1. 掌門教育微服務面臨的挑戰
1)第一次生產事故
2020 年疫情爆發后的幾個月后,掌門教育的微服務實例數比去年猛增 40% ,基礎架構部樂觀的認為注冊中心 Eureka 服務器可以抗住該數量級的實例數規模, Eureka 服務器在阿里雲 PROD 環境上執行三台 8C16G 普通型機器三角結構型對等部署,運行了好幾年都一直很穩定,但災難還是在2020年3月某天晚上降臨,當天晚上大概 9 點 30 分左右,其中兩台 Eureka 服務器無征兆的 CPU 占用迅速上升到100%,同時大量業務服務掉線,告警系統被觸發,釘釘機器人告警和郵件告警鋪天蓋地而來。基礎架構部和運維部緊急重啟 Eureka 服務器,但沒多久,CPU 依舊沒抗住,而且更加來勢凶猛,打開的文件描述符數瞬間達到 8000+ ,TCP 連接達到 1 萬+ ,業務服務和 Eureka 服務器的通信產生大面積的 TCP CLOSE_WAIT 事件,且伴有大量 Broken pipe 異常。
org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
運維人員嘗試把機器升級成增強型 8C16G ,折騰一番后,於 23:00 左右恢復正常。
2)第二次生產事故
微服務實例數依舊在增長, Eureka 服務器平穩運行了大概半個月后,災難又一次降臨,CPU 再次飆升到100%,過程就不表述了。處理方式,把機器升級成增強型 16C32G,並把 Eureka 服務器的版本升級到 Spring Cloud Hoxton 版,並優化了它的一些配置參數,爾后事件再也沒出現。
2. 掌門教育新微服務演進思考
雖然 Eureka 服務器目前運行平穩,但我們依舊擔心此類事故在未來會再次發生,於是痛定思痛,經過深入的調研和比較一段時間后,通過由基礎架構部牽頭,各大業務線負責人和架構師參與的專項注冊中心架構評審會上,CTO 拍板,做出決議:選擇落地 Alibaba Nacos 作為掌門教育的新注冊中心。
Talk is cheap,show me the solution。基礎架構部說干就干,Nacos 部署到 FAT 環境后,打頭陣的是測試組的同學,對 Nacos 做全方位的功能和性能測試,畢竟 Nacos 是阿里巴巴拳頭開源產品,迭代了2年多,在不少互聯網型和傳統型公司都已經落地,我們選擇了穩定的 1.2.1 版本,得出結論是功能穩定,性能上佳,關於功能和性能方面的相關數據,具體參考:《掌門1對1微服務體系 Solar | 阿里巴巴 Nacos 企業級落地下篇》。
但是,如何遷移 Eureka 上的業務服務到 Nacos 上?業務服務實例數目眾多,遷移工作量巨大,需要全公司業務部門配合,同時 Eureka 對注冊的業務服務名大小寫不敏感,而 Nacos 對注冊的業務服務名大小寫敏感,那么對於業務服務名不規范的業務部門需要改造。而對於基礎架構部來說, Nacos Eureka Sync 方案如同一座大山橫亘在我們面前,是首先需要邁過去的坎,縱觀整個過程,該方案選型還是折騰了一番,具體參考:《掌門1對1微服務體系 Solar | 阿里巴巴 Nacos 企業級落地中篇》。
阿里巴巴 Nacos 企業級落地的優化代碼,在不久的將來會通過開源的方式回饋給業界。
官方介紹
1. Nacos 簡介
阿里巴巴中間件部門開發的新一代集服務注冊發現中心和配置中心為一體的中間件。它是構建以“服務”為中心的現代應用架構 (例如微服務范式、雲原生范式) 的服務基礎設施,支持幾乎所有主流類型的“服務”的發現、配置和管理,更敏捷和容易地構建、交付和管理微服務平台。
- Nacos Landscape
- Nacos Map
摘自官網 What is Nacos:https://nacos.io/en-us/docs/what-is-nacos.html
2. Spring Cloud Alibaba 簡介
阿里巴巴中間件部門開發的 Spring Cloud 增強套件,致力於提供微服務開發的一站式解決方案。此項目包含開發分布式應用微服務的必需組件,方便開發者通過 Spring Cloud 編程模型輕松使用這些組件來開發分布式應用服務。依托 Spring Cloud Alibaba ,您只需要添加一些注解和少量配置,就可以將 Spring Cloud 應用接入阿里微服務解決方案,通過阿里中間件來迅速搭建分布式應用系統。
摘自官網 Spring Cloud Alibaba Introduction:https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/introduction.adoc
關於 Nacos 和 Spring Cloud Alibaba 如何使用,它的技術實現原理怎樣等,官方文檔或者民間博客、公眾號文章等可以提供非常詳盡且有價值的材料,這些不在本文的討論范圍內,就不一一贅述。筆者嘗試結合掌門教育現有的技術棧以及中間件一體化的戰略,並着眼於強大的 Nacos 和 Spring Cloud Alibaba 技術生態圈展開闡釋。
Nacos 開發篇
1. Nacos Server 落地
1)Nacos Server
- Nacos Server 環境和域名
掌門的應用環境分為 4 套,DEV | FAT | UAT | PROD 分別對應開發、測試、准生產環境、生產環境,因此 Nacos Server 也分為 4 套獨立環境。除了 DEV 環境是單機部署外,其他是集群方式部署。對外均以域名方式訪問,包括 SDK 方式連接 Nacos Server 和訪問 Nacos Server Dashboard 控制台頁面。
- Nacos Server 環境隔離和調用隔離
Nacos Server 可以創建不同的命名空間,做到同一個應用環境的基礎上更細粒度的划分,隔離服務注冊和發現。在某些場景下,開發本地有需要連接測試環境的 Nacos Server ,但其他測試服務不能調用到開發本地,這時候可以將 NacosDiscoveryProperties 的 enabled 屬性設置為 false 。
- Nacos Server 集成 Ldap
Nacos Server Dashboard 集成公司的 Ldap 服務,並在用戶首次登錄時記錄用戶信息。
2)Nacos Server 界面
- Nacos 界面權限
Nacos Server Dashboard 用戶首次登陸時,默認分配普通用戶(即非 ROLE_ADMIN )角色,對查詢以外的按鈕均無操作權限,以免出現誤操作導致服務非正常上下線。
- Nacos 界面顯示服務概覽
Nacos Server Dashboard 頁面增加服務總數及實例總數的統計,該信息每 5 秒刷新一次。
3)Nacos 監控
【Nacos Server 監控】
- 標准監控
基於公司現有的 Prometheus 、 Grafana 、 AlertManager 從系統層監控 Nacos。
- 高級監控
根據 Nacos 監控手冊,結合 Prometheus 和 Grafana 監控 Nacos 指標。
【Nacos Eureka Sync Etcd 監控】
從如下界面可以監控到,業務服務列表是否在同步服務的集群上呈現一致性 Hash 均衡分布。
4)Nacos 日志
- 日志合並及 JSON 格式化
將 Nacos 多模塊的日志統一按 info 、 warn、error 級別合並,定義 schema 字段標記不同模塊,按 JSON 格式滾動輸出到文件,供 ELK 采集展示。
5)Nacos 告警
【Nacos Server 告警】
- 業務服務上下線的告警
- Nacos Eureka Sync 告警
- 服務名大寫告警
- 業務服務同步完畢告警
2. Nacos Client 落地
1)Solar Nacos SDK 環境初始化
應用接入 Solar Nacos SDK 在啟動時需要初始化完成 Nacos Server 的連接配置,即 spring.cloud.nacos.discovery.server-addr 參數的賦值。不同環境下連接的 Nacos Server ,因此需要讀取機器所在的 env 環境參數,來選擇相對應的 Nacos Server 地址。
初始化邏輯代碼如下:
public class NacosClientConfigApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
private static final Logger logger = LoggerFactory.getLogger(NacosClientConfigApplicationContextInitializer.class);
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
try {
Properties props = new Properties();
String path = isOSWindows() ? CommonConstant.SERVER_PROPERTIES_WINDOWS : CommonConstant.SERVER_PROPERTIES_LINUX;
File file = new File(path);
if (file.exists() && file.canRead()) {
FileInputStream fis = new FileInputStream(file);
if (fis != null) {
try {
props.load(new InputStreamReader(fis, Charset.defaultCharset()));
} finally {
fis.close();
}
}
}
String env = System.getProperty("env");
if (!isBlank(env)) {
env = env.trim().toLowerCase();
} else {
env = System.getenv("ENV");
if (!isBlank(env)) {
env = env.trim().toLowerCase();
} else {
env = props.getProperty("env");
if (!isBlank(env)) {
env = env.trim();
} else {
env = NacosEnv.DEV.getCode();
}
}
}
String serverAddr = NacosEnv.getValueByCode(env);
Map<String, Object> nacosClientPropertySource = new HashMap<>();
nacosClientPropertySource.put(CommonConstant.NACOS_DISCOVERY_SERVER_ADDR, serverAddr);
applicationContext.getEnvironment().getPropertySources().addLast(new MapPropertySource("solarNacosClientPropertySource", nacosClientPropertySource));
} catch (Exception e) {
logger.error(e.getMessage());
}
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
private boolean isOSWindows() {
String osName = System.getProperty("os.name");
return !isBlank(osName) && osName.startsWith("Windows");
}
private boolean isBlank(String str) {
return Strings.nullToEmpty(str).trim().isEmpty();
}
}
2)Solar Nacos 藍綠灰度發布和子環境隔離
在 Nacos 和 Eureka 雙注冊中心過渡狀態下, Solar SDK 支持跨注冊中心調用的藍綠灰度發布和子環境功能。下面的圖片,只以 Eureka 為例:
我們只需要把 Eureka SDK 換到 Nacos SDK 即可,實現如下功能:
- Solar 藍綠灰度發布
- 版本匹配灰度發布
- 版本權重灰度發布
- Solar 多區域路由
- 區域匹配灰度路由
- 區域權重灰度路由
- Solar 子環境隔離
- 環境隔離
- 環境路由
- Solar 版本號和區域值,子環境號策略
- DEV 環境,Git 插件自動創建灰度版本號
- DevOps 環境設置
Solar 藍綠灰度發布架構圖:
Solar 基於版本維度的藍綠灰度發布架構圖:
Solar 子環境隔離架構圖:
更多功能參考:
掌門 1 對 1 微服務體系 Solar 第 1 彈:全鏈路灰度藍綠發布智能化實踐,掌門教育已經實現通過灰度藍綠發布方式,實現對流量的精確制導和調撥。
Nepxion Discovery 開源社區:https://github.com/Nepxion/Discovery
3)Solar Nacos 集成 Sentinel
4)Solar Nacos 集成灰度藍綠埋點到 Skywalking
5)Solar Nacos 集成 Sentinel 埋點到 Skywalking
- 微服務上的 Sentinel 埋點
- 網關上的 Sentinel 埋點
6)Solar Nacos 集成 DevOps 發布平台
- 集成攜程 VI Cornerstone 實現服務拉入拉出
Solar Nacos SDK 的服務,在應用發布時需要做服務的拉入拉出,目的是為了發布時流量無損。掌門使用 VI Cornerstone 實現拉入拉出功能。具體實現是在初始化 NacosDiscoveryProperties 對象時設置 instance.enabled 屬性值為 false,在服務完全初始化后,通過發布系統調用 Solar Nacos SDK 的 API 接口再修改為 true 來被外部發現並提供服務。
public class NacosApplicationContextInitializer implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
Boolean bootstrapEnabled = configurableEnvironment.getProperty("devops.enabled", Boolean.class, false);
if (bootstrapEnabled) {
Properties properties = new Properties();
properties.put("spring.cloud.nacos.discovery.instanceEnabled", "false");
PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("devopsEnabledNacosDiscoveryProperties", properties);
MutablePropertySources mutablePropertySources = configurableEnvironment.getPropertySources();
mutablePropertySources.addFirst(propertiesPropertySource);
}
}
}
spring.factories 配置文件:
org.springframework.boot.env.EnvironmentPostProcessor=\
com.ctrip.framework.cs.spring.NacosApplicationContextInitializer
7)Solar Nacos SDK 接入
【Solar 版本定義】
- Solar 2.3.x & 1.3.x,基於 Nacos SDK
- Solar 2.2.x & 1.2.x,基於 Eureka SDK
【Solar 版本關系】
- Solar 版本與 Spring Boot 技術棧的關系
- Solar 版本與注冊中心的關系
【Solar SDK 接入】
- 設置 Parent
<parent>
<groupId>com.zhangmen</groupId>
<artifactId>solar-parent</artifactId>
<version>${solar.version}</version>
</parent>
- 添加到 pom.xml
只需引入一個 Jar 包,對接成本極低,只做基本組件封裝,非常輕量級。
微服務:
<dependency>
<groupId>com.zhangmen</groupId>
<artifactId>solar-framework-starter-service</artifactId>
<version>${solar.version}</version>
</dependency>
網關:
<dependency>
<groupId>com.zhangmen</groupId>
<artifactId>solar-framework-starter-zuul</artifactId>
<version>${solar.version}</version>
</dependency>
- 入口類添加注解
@EnableSolarService , @EnableSolarZuul 封裝了標准 Spring Boot / Spring Cloud / Apollo 等大量注解,降低業務的使用成本。
微服務:
@EnableSolarService
public class DemoApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(DemoApplication.class).run(args);
}
}
網關:
@EnableSolarZuul
public class DemoApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(DemoApplication.class).run(args);
}
}
8)Solar Nacos SDK 和 Solar Eureka SDK 升級和回滾
升級和回滾方案非常簡單,此方式同時適用於網關和服務,見下圖:
作者信息
吳毅挺,掌門技術副總裁,負責技術中台和少兒技術團隊。曾就職於百度、eBay 、攜程,曾任攜程高級研發總監,負責從零打造攜程私有雲、容器雲、桌面雲和 PaaS 平台。
任浩軍,掌門基礎架構部負責人。曾就職於平安銀行、萬達、惠普,曾負責平安銀行平台架構部 PaaS 平台 Halo 基礎服務框架研發。10 多年開源經歷,Github ID:@HaojunRen,Nepxion 開源社區創始人,Nacos Group Member,Spring Cloud Alibaba & Nacos & Sentinel & OpenTracing Committer。
參與 Nacos 落地的基礎架構部成員,包括:童子龍,張彬彬,廖夢鴿,張金星,胡振建,謝璐,謝慶芳,伊安娜
“阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公眾號。”