【架构】单体架构 vs 微服务架构:如何选择最适合你的技术方案?
文章目录
- ⭐前言
- ⭐一、架构设计的本质差异
- 🌟1、代码与数据结构的对比
- 🌟2、技术栈的灵活性
- ⭐二、开发与维护的成本博弈
- 🌟1、开发效率的阶段性差异
- 🌟2、维护成本的隐形陷阱
- ⭐三、部署与扩展的实战策略
- 🌟1、部署模式的本质差异
- 🌟2、扩展性的核心策略
- ⭐四、适用场景与真实案例
- 🌟1、选择单体的典型场景
- 🌟2、微服务的优势战场
- ⭐五、关键决策框架
- 🌟1、4步决策法
- 🌟2、决策树示例
- ⭐六、折中方案:模块化单体
- 🌟1、核心设计原则
- 🌟2、实践案例
- ⭐七、总结与建议
- 🌟1、3条黄金法则
- 🌟2、致开发者的忠告
- ⭐总结
标题 | 详情 |
---|---|
作者 | JosieBook |
头衔 | CSDN博客专家资格、阿里云社区专家博主、软件设计工程师 |
博客内容 | 开源、框架、软件工程、全栈(,NET/Java/Python/C++)、数据库、操作系统、大数据、人工智能、工控、网络、程序人生 |
口号 | 成为你自己,做你想做的 |
欢迎三连 | 👍点赞、✍评论、⭐收藏 |
⭐前言
在软件开发中,架构设计是决定系统可维护性、扩展性和长期生命力的核心因素。单体架构(Monolithic)和微服务架构(Microservices)是两种主流的架构模式,但它们的设计理念和适用场景截然不同。本文将通过技术对比、真实案例和决策框架,帮助你在实际项目中做出明智选择。
⭐一、架构设计的本质差异
🌟1、代码与数据结构的对比
-
单体架构
像一个“大教堂”——所有功能模块(用户管理、订单处理、支付等)集中在单一代码库中,共享同一个数据库。-
优势:代码调用直接(本地方法调用),事务管理简单(ACID保证)。
-
劣势:模块耦合度高,修改一个功能可能引发连锁问题。
-
-
微服务架构
更像“市集”——每个服务独立运行,例如:-
用户服务(Go + MySQL)
-
订单服务(Java + Redis)
-
支付服务(Python + PostgreSQL)
-
通信方式:通过API(REST/gRPC)或消息队列(Kafka)交互。
-
数据自治:每个服务拥有自己的数据库,避免直接共享数据表。
-
🌟2、技术栈的灵活性
单体架构通常强制统一技术(如全栈Spring),而微服务允许按需选择最适合的技术。例如:
-
高性能计算模块用Rust
-
实时通信用Node.js
-
数据分析用Python
⭐二、开发与维护的成本博弈
🌟1、开发效率的阶段性差异
-
单体初期优势
小团队可以快速开发,无需考虑服务拆分和分布式协调。例如,一个3人团队在1个月内完成一个电商MVP(最小可行产品)。 -
微服务的长期收益
随着业务复杂化,微服务的独立部署和按需扩展优势显现。例如:
美团外卖的订单服务每天独立部署10次,而用户服务每周仅需1次更新。
🌟2、维护成本的隐形陷阱
-
单体的“代码沼泽”风险
当代码量超过10万行时,新增功能可能引发不可预见的副作用。典型案例:某传统银行核心系统修改一个字段需测试3个月。 -
微服务的运维复杂度
需要引入以下工具链:-
服务网格(Istio):管理服务间通信和流量
-
分布式追踪(Jaeger):定位跨服务故障
-
日志聚合(ELK Stack):分析全局日志
-
⭐三、部署与扩展的实战策略
🌟1、部署模式的本质差异
-
单体架构
-
全量部署:每次更新需重新打包整个应用(如Java的WAR包)。
-
工具链:Docker容器化部署(单镜像),Jenkins简单流水线。
-
案例:某教育平台用单体架构实现每日1次全量部署,耗时30分钟。
-
-
微服务架构
-
独立部署:仅更新变更的服务(如订单服务独立发版)。
-
工具链:Kubernetes滚动更新 + ArgoCD GitOps自动化。
-
案例:抖音电商通过K8s实现每秒10个服务实例的弹性部署。
-
🌟2、扩展性的核心策略
-
典型场景:
-
秒杀活动:微服务可单独扩展库存服务至100节点,而单体需全系统扩容。
-
突发流量:Netflix利用AWS Auto Scaling在1分钟内扩容千个播放服务实例。
-
⭐四、适用场景与真实案例
🌟1、选择单体的典型场景
-
初创企业快速验证
- 案例:拼多多早期用PHP单体架构,3个月上线核心交易功能。
- 优势:避免分布式系统复杂性,专注业务验证。
-
高实时性要求系统
- 案例:某量化交易系统坚持C++单体,延迟控制在微秒级。
- 原因:微服务网络通信引入的毫秒级延迟不可接受。
-
传统行业遗留系统
- 案例:某银行核心系统仍为COBOL单体,因重构风险过高。
🌟2、微服务的优势战场
-
互联网高并发场景
- 案例:美团外卖通过200+微服务支撑日均5000万订单,各服务独立扩缩容。
-
多团队协同开发
- 案例:字节跳动TikTok使用微服务,让中美团队各自维护推荐算法和内容审核服务。
-
混合技术栈需求
- 案例:特斯拉车载系统:C++实时控制服务 + Python AI推理服务。
⭐五、关键决策框架
🌟1、4步决策法
-
评估业务规模
用户量是否超百万?
功能模块是否超过20个? -
分析团队能力
是否有K8s运维专家?
能否接受每日多次部署? -
技术债务容忍度
能否接受初期更高的开发成本?
是否有3年以上技术演进规划? -
性能与弹性需求
是否需要99.99%可用性?
流量波动是否超过10倍?
🌟2、决策树示例
用户量 < 10万 → 选单体
用户量 > 100万且团队有DevOps经验 → 选微服务
高频交易系统 → 单体优先
多国团队协作 → 必选微服务
⭐六、折中方案:模块化单体
🌟1、核心设计原则
-
模块化分层
按领域划分模块(用户/订单/支付),定义清晰的接口边界。
技术实现:Spring Modulith或Java 9+模块化系统。 -
数据隔离设计
每个模块使用独立数据库Schema,为未来拆分预留可能。 -
渐进式拆分
初期单体开发,当订单模块变更频率超过2次/周时,优先拆分为微服务。
🌟2、实践案例
-
案例1:GitLab坚持模块化Ruby单体,通过严格接口规范管理500万行代码。
-
案例2:某SaaS平台用DDD划分限界上下文,3年后平滑过渡到微服务。
⭐七、总结与建议
🌟1、3条黄金法则
-
规模决定架构
用户量<10万:单体优先
用户量>100万:微服务必选 -
技术为业务服务
金融系统:宁可忍受单体臃肿也要保证事务一致性
社交平台:为弹性扩展必须接受微服务复杂度 -
持续演进思维
单体设计时预留模块边界(如使用领域事件解耦)
微服务实施前先建立监控/日志/CI/CD基础能力
🌟2、致开发者的忠告
-
不要过度设计:Airbnb直到日活百万才开始拆分微服务
-
避免架构虚荣:WhatsApp用Erlang单体支撑20亿用户
-
拥抱变化:架构应像乐高积木,随时可重组
⭐总结
架构选择没有标准答案,只有对业务痛点的精准回应。 无论是单体还是微服务,最终目标都是:用合适的技术,在正确的时间,解决真实的问题。
标题 | 详情 |
---|---|
作者 | JosieBook |
头衔 | CSDN博客专家资格、阿里云社区专家博主、软件设计工程师 |
博客内容 | 开源、框架、软件工程、全栈(,NET/Java/Python/C++)、数据库、操作系统、大数据、人工智能、工控、网络、程序人生 |
口号 | 成为你自己,做你想做的 |
欢迎三连 | 👍点赞、✍评论、⭐收藏 |
相关文章:
【架构】单体架构 vs 微服务架构:如何选择最适合你的技术方案?
文章目录 ⭐前言⭐一、架构设计的本质差异🌟1、代码与数据结构的对比🌟2、技术栈的灵活性 ⭐二、开发与维护的成本博弈🌟1、开发效率的阶段性差异🌟2、维护成本的隐形陷阱 ⭐三、部署与扩展的实战策略🌟1、部署模式的本…...
面试redis常被问到的面试题含答案
什么是Redis?它的特点是什么? Redis是一个开源的内存数据库,用于存储数据并支持多种数据结构(如字符串、哈希、列表、集合、有序集合等)。其特点包括高性能、支持持久化、数据结构丰富、原子性操作、支持事务等。 Red…...
Asp.net Core API 本地化
本文是一个demo,演示了如何根据用户接口查询字段(正常放header中),设置当前culture,并获取当前culture的key value给用户提示 创建Resources文件夹,添加以下三个文件 其中ExceptionUnuse 是一个空的类,供IStringLocalizer使用&a…...
使用Java实现Oracle表结构转换为PostgreSQL的示例方案(AI)
核心代码 import java.sql.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;public class OracleToPGConverter {// 类型映射表private static final Map<String, String> TYPE_MAPPING new HashMap<>()…...
win32汇编环境,网络编程入门之八
;在上一教程里,我们学习了简单的处理服务器返回的数据 ;在这一教程里,我们了解一下,当连接上网站后,应该发送什么数据过去的问题 ;这里有个简单的方式学习,以下是一个示例 ;我们上网的时候可以用谷歌浏览器,…...
Java EE 进阶:MyBatis
MyBatis是一个优秀的持久化框架,用于简化JDBC的开发。 持久层就是持久化访问的层,就是数据访问层(Dao),用于访问数据库的。 MyBatis使用的准备工作 创建项目,导入mybatis的启动依赖,mysql的驱…...
Linux驱动开发基础(can)
目录 1.can的介绍 2.can的硬件连接 2.1 CPU自带can控制器 2.2 CPU没有can控制器 3.电气属性 4.can的特点 5.can协议 5.1 can的种类 5.2 数据帧 5.2.1 标准数据帧格式 5.3.1 扩展数据帧格式 5.3 遥控帧 5.4 错误帧 5.5 过载帧 5.6 帧间隔 5.7 位填充 5.8 位时…...
Linux 命令行整理(完善中)
文件类 查看文件类 cat 用于连接文件并打印到标准输出设备上,可用于查看文件内容.(短文件) use:cat example.txtmore 分页的形式显示文件内容,适合查看较长的文件(长) use: more example.txtless 也是分页查看文件内容ÿ…...
回调方法传值汇总
<template v-slot"scope"><el-switch v-model"scope.row.open" change"(p1) > changeOpen(p1, scope.row)"></el-switch></template>公域流量 多选 selection-change“val > multipleSelection val”...
分享一个精灵图生成和拆分的实现
概述 精灵图(Sprite)是一种将多个小图像合并到单个图像文件中的技术,广泛应用于网页开发、游戏开发和UI设计中。在MapboxGL中,跟之配套的还有一个json文件用来记录图标的大小和位置。本文分享基于Node和sharp库实现精灵图的合并与…...
python中的min函数的key的用法 - abs绝对值
前言 继续上一章节提及的 Python 中 min() 函数的用法,包括其基本语法、处理列表、接收多个参数 这个章节将补充一些新的知识点例如: min函数中key的另一种用法abs绝对值 min(iterable, *iterables, keyNone, defaultNone) 知识点 key 参数 key 是一个可选参数…...
我开发的PDF转WORD免费工具
ZhouShengHuan 欢迎小伙伴使用~...
kubernetes高级实战
一、模拟企业环境进行一个实战部署 [rootmaster node]# kubectl apply -f pod-tomcat.yaml pod/tomcat-test created [rootmaster node]# kubectl get pods NAME READY STATUS RESTARTS AGE tomcat-test 2/2 Running 0 2s [rootmaster node]…...
IntelliJ 配置文件plugin.xml
在 IntelliJ IDEA 插件开发中,plugin.xml 是插件的配置文件,它包含了关于插件的所有基本信息、扩展点、依赖关系等。该文件使用 XML 格式进行定义。以下是 plugin.xml 中常见的元素及其用途: <idea-plugin><!-- 插件的基本信息 --&…...
《心理学与生活》2025最新网课答案
《心理学与生活》2025最新网课答案 文章目录 《心理学与生活》2025最新网课答案发展与教育单元测试情绪与情感单元测验人格与动机单元测试感知与记忆单元测试文化与社会单元测试 发展与教育单元测试 题数 20 棉花糖实验中哪些小孩长大后的表现更好()。 …...
11 python 数据容器-字符串
一、什么是数据容器 举个例子,一个办公室里有一群牛马,他们都有自己的名字,如果没有容器的概念,那么我们用变量来存放他们的名字,比如: name1 "翠花" name2 "玛丽" name3 "二…...
2025.3.20总结
阅读:《时间贫穷》第二章,里面讲到,运动,多行善事,体验自然,都会增强自我效能感,是对抗时间焦虑的强有力的方式。 花时间运动是值得的,公司每周三都是运动周,把运动视作…...
鸿蒙NEXT开发问题大全(不断更新中.....)
目录 问题1:鸿蒙NEXT获取华为手机的udid 问题2:[Fail]ExecuteCommand need connect-key? 问题3:测试时如何安装app包 问题1:鸿蒙NEXT开发获取华为手机的udid hdc -t "设备的序列号" shell bm get --udid 问题2&…...
【北京迅为】iTOP-RK3568开发板OpenHarmony系统南向驱动开发UART接口运作机制
瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…...
大模型在冠心病风险预测及临床方案制定中的应用研究
目录 一、引言 1.1 研究背景与目的 1.2 国内外研究现状 1.3 研究方法与创新点 二、大模型预测冠心病风险原理与方法 2.1 数据收集与预处理 2.1.1 数据来源 2.1.2 数据清洗与整理 2.2 特征工程 2.2.1 特征提取 2.2.2 特征选择与优化 2.3 模型选择与训练 2.3.1 常用…...
【从零开始:如何用Vue3打造响应式个人博客网站】
前言 在前端开发领域,Vue.js 是一个非常流行且强大的框架。本文将详细介绍如何使用 Vue3 构建一个完整的响应式个人博客网站。无论你是初学者还是有一定经验的开发者,本文都将为你提供详细的步骤和代码示例。 1. 环境搭建 首先,确保你已经安…...
【vulhub/wordpress靶场】------获取webshell
1.进入靶场环境: 输入:cd / vulhub / wordpress / pwnscriptum 修改版本号: vim docker-compose.yml version: 3 保存退出 开启靶场环境: docker - compose up - d 开启成功,docker ps查看端口 靶场环境80…...
ngx_http_conf_ctx_t
定义在 src/http/ngx_http_config.h typedef struct {void **main_conf;void **srv_conf;void **loc_conf; } ngx_http_conf_ctx_t; ngx_http_conf_ctx_t 是 Nginx 中用于管理 HTTP 配置上下文的核心结构体,其设计体现了 Nginx 多级配置&…...
大模型+知识图谱:赋能知识智能新升级
在大模型(Large Language Model, LLM)飞速发展的今天,如何把传统行业中沉淀多年的大量结构化与非结构化数据真正“用起来”,正成为推动智能化转型的关键一步。 找得到,看得懂,为何很难? 以制造…...
python学智能算法(八)|决策树
【1】引言 前序学习进程中,已经对KNN邻近算法有了探索,相关文章链接为: python学智能算法(七)|KNN邻近算法-CSDN博客 但KNN邻近算法有一个特点是:它在分类的时候,不能知晓每个类别内事物的具…...
压力测试实战指南:JMeter 5.x深度解析与QPS/TPS性能优化
一、压力测试基础概念 1.1 什么是压力测试? 定义:模拟极端负载场景验证系统性能极限 目的:发现性能瓶颈、评估系统可靠性、验证容错能力 常见类型:负载测试、压力测试、稳定性测试、峰值测试 1.2 核心性能指标解析 1.2.1 QP…...
鸿蒙NEXT项目实战-百得知识库04
代码仓地址,大家记得点个star IbestKnowTeach: 百得知识库基于鸿蒙NEXT稳定版实现的一款企业级开发项目案例。 本案例涉及到多个鸿蒙相关技术知识点: 1、布局 2、配置文件 3、组件的封装和使用 4、路由的使用 5、请求响应拦截器的封装 6、位置服务 7、三…...
Spring Boot Actuator 自定义健康检查(附Demo)
目录 前言1. Demo2. 拓展 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF Spring Boot 的 actuator 提供了应用监控的功能,其中健康检查(Health Check)是一个重要的部分&…...
Flutter小白零基础入门到高级项目实战全集
Flutter零基础入门到高级项目实战全集内容如下: Dart入门基础教程16讲、Null safety 、late 关键字、空类型声明符?、非空断言!、required 、Flutter入门基础、Flutter瀑布流布局、Flutter动画、Flutter异步流、GlobalKey 、Flutter国际化、…...
TCP 协议
文章目录 TCP 协议简介数据包格式TCP的特性连接机制确认与重传缓冲机制全双工通信流量控制差错控制拥塞控制 端口号三次握手数据传输四次挥手抓包参考 本文为笔者学习以太网对网上资料归纳整理所做的笔记,文末均附有参考链接,如侵权,请联系删…...
NO.51十六届蓝桥杯备战|堆算法题|第k小|除2|最小函数值|序列合并|舞蹈课(C++)
P3378 【模板】堆 - 洛谷 #include <bits/stdc.h> using namespace std;const int N 1e6 10; int n; int heap[N];void up(int child) {int parent child / 2;while (parent > 1 && heap[child] < heap[parent]){swap(heap[child], heap[parent]);chil…...
【QA】观察者模式在QT有哪些应用?
1. 信号与槽机制 Qt的**信号与槽(Signals & Slots)**是观察者模式的典型实现,通过元对象系统(Meta-Object System)实现松耦合通信。 核心特点: 类型安全:编译时检查参数匹配跨线程支持&…...
coze ai assistant Task5
没想到coze的组队学习这么快就过去了,我也从一个不懂coze的小白变成了一个能简单尝试小程序的懵懂小白。虽然几次学习并不能掌握很多的技能,但也让我知道coze的无限可能,组队结束后我会继续努力学习,做更多使自己偷懒的小工具~ 需…...
MATLAB神经网络优化1000个案例算法汇总
【2025最新版】MATLAB神经网络优化1000个案例算法汇总(长期更新版) 本文聚焦神经网络、优化算法,神经网络改进,优化算法改进,优化算法优化神经网络权重、超参数等,现在只需订阅即可拥有,简直是人工智能初学者的天堂。…...
Android Coil3 Fetcher preload批量Bitmap拼接扁平宽图,Kotlin
Android Coil3 Fetcher preload批量Bitmap拼接扁平宽图,Kotlin 在这一篇文章基础上改进: Android Coil3阶梯preload批量Bitmap拼接扁平宽图,Kotlin-CSDN博客文章浏览阅读854次,点赞18次,收藏5次。遗留问题,…...
Ubuntu 24 常用命令方法
文章目录 环境说明1、账号管理1.1、启用 root 2、包管理工具 apt & dpkg2.1、apt 简介 & 阿里源配置2.2、dpkg 简介2.3、apt 和 dpkg 两者之间的关系2.4、常用命令 3、启用 ssh 服务4、防火墙5、开启远程登录6、关闭交换分区7、build-essential(编译和开发软…...
uniapp自身bug | uniapp+vue3打包后 index.html无法直接运行
前提: 已经修改了基础路径 打开打包文件,双击运行index.html报错,无法访问页面 uniappvue2项目是可以正常运行的 vue3修改publicPath: ./后,也是可以正常访问打包文件中的index.html 点进控制台提供的链接:https:/…...
go~协程阻塞分析
错误示例 type chanData struct {result stringerror error }func Biz1() {t := time.NewTimer(time.Second * 10)ctx := context.Background()ch := make(chan chanData)go doChan(ctx, ch)fmt.Println("Biz1 begin")for {select {case <-t.C:fmt.Println(&quo…...
【机器学习】什么是逻辑回归
什么是逻辑回归 一、摘要二、逻辑回归算法简介三、sigmoid函数实现四、思考题 一、摘要 本文主要讲述了逻辑回归算法的基本原理和应用。首先介绍了逻辑回归在机器学习领域的重要地位,然后解释了其名称的由来和如何利用样本特征和概率之间的关系进行分类。通过与线性…...
postman小白教程(从入门到实战,详细教学)
目录 1. postman介绍 2. 下载地址 3. 安装流程 4. 注册postman账号 ① 打开postman,点击【创建账号】或【登录】,会跳转到浏览器 ② 若已有账号可以直接登录;若无账号,则创建新账号 ③ 若登录成功会弹出提示框,…...
4.1-4 SadTalker数字人 语音和嘴唇对应的方案
前言: SadTalker是一个强大的数字人相关的RA/SD插件。它本身是一个非常独立的产品。你只需要提供一段视频,一段文字,简单的配置,在RA/SD中简单的生成即可。 视频中人物的嘴唇很好的应对了你要发声的文字内容。效果很赞。仔细学习…...
Linux CentOS7 安装 ffmpeg教程
官网:FFmpeg 操作 先用uname -a 查看内核版本,如果是 3.2.0或者以上就可以按照此办法来安装 cd /tmp wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz# 2. 解压 tar xvf ffmpeg-release-amd64-static.tar.xz# 3. 将…...
docker desktop 集成WSL Ubuntu22.04
Windows docker desktop 设置WSL ubuntu 22.04启用与其他发行版的集成 Windows docker desktop 安装参考 wsl ubuntu 22.04 查看我宿主机的docker desktop 容器全部的信息 wsl -d Ubuntu-22.04 -u root...
【AI】AI编程助手:Cursor、Codeium、GitHub Copilot、Roo Cline、Tabnine
文章目录 一、基本特性对比二、收费标准三、私有部署能力1、Tabnine2、Roo Code 三、代码补全与自然语言生成代码四、安装独立的IDE安装插件安装 五、基本使用(一)Cursor(二)GitHub Copilot1、获取代码建议2.聊天1)上下…...
Android audio(8)-native音频服务的启动与协作(audiopolicyservice和audioflinger)
音频策略的构建 1、概述 2、AudiopolicyService 2.1 任务 2.2 启动流程 2.2.1 加载audio_policy.conf(xml)配置文件 2.2.2 初始化各种音频流对应的音量调节点 2.2.3 加载audio policy硬件抽象库 2.2.4设置输出设备 ps:audiopatch流程简介 2.2.5打开输出设…...
光纤通道 VS iSCSI:存储架构选型的关键抉择
光纤通道 VS iSCSI:存储架构选型的关键抉择 在企业运维中,存储网络的选择可以说是至关重要的一环。尤其是光纤通道(Fibre Channel,简称FC)和iSCSI存储,这两种主流解决方案各有千秋,常常让运维工程师在选型时感到纠结。为了帮大家理清头绪,我们今天就从架构、性能、成本…...
HarmonyOS Next中的弹出框使用
HarmonyOS Next弹出框概述及分类 弹出框是一种模态窗口,通常用于在保持当前上下文环境的同时,临时展示用户需关注的信息或待处理的操作。用户需在模态弹出框内完成相关交互任务之后,才能退出模态模式。弹出框可以不与任何组件绑定࿰…...
Binder机制源码分析
Binder机制源码分析 一、前言 Binder是Android系统中最重要的进程间通信机制,它不仅是应用程序和系统服务通信的基础,也是Android系统安全机制的重要组成部分。本文将深入分析Binder机制的实现原理,帮助读者理解Android系统的核心通信机制。…...
第5课 树莓派的Python IDE—Thonny
1. Thonny的特点 Thonny是一款面向初学者的Python IDE。它由爱沙尼亚的 Tartu 大学开发,其调试器是专为学习和教学编程而设计的。Thonny具有如下特点 易于上手。Thonny 内置了 Python 3.7,因此只需要一个简单的安装程序,你就可以开始学习编程了(如有必要,您还可以使用单独…...
位运算题目:或运算的最小翻转次数
文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题:或运算的最小翻转次数 出处:1318. 或运算的最小翻转次数 难度 4 级 题目描述 要求 给定三个正整数 a \texttt{a} a、 b \texttt{b} b…...