JAVA性能统计项目

article/2025/9/27 4:16:35

一、项目背景:

我们希望设计开发一个小的框架,能够获取接口调用的各种统计信息,比如,响应时间的最大值(max)、最小值(min)、平均值(avg)、百分位值(percentile)、接口调用次数(count)、频率(tps) 等,并且支持将统计结果以各种显示格式(比如:JSON 格式、网页格式、自定义显示格式等)输出到各种终端(Console 命令行、HTTP 网页、Email、日志文件、自定义输出终端等),以方便查看。

二、需求分析:

接口统计信息:包括接口响应时间的统计信息,以及接口调用次数的统计信息等。

统计信息的类型:max、min、avg、percentile、count、tps 等。

统计信息显示格式:Json、Html、自定义显示格式。

统计信息显示终端:Console、Email、HTTP 网页、日志、自定义显示终端。

统计触发方式:包括主动和被动两种。主动表示以一定的频率定时统计数据,并主动推送到显示终端,比如邮件推送。被动表示用户触发统计,比如用户在网页中选择要统计的时间区间,触发统计,并将结果显示给用户。

统计时间区间:框架需要支持自定义统计时间区间,比如统计最近 10 分钟的某接口的 tps、访问次数,或者统计 12 月 11 日 00 点到 12 月 12 日 00 点之间某接口响应时间的最大值、最小值、平均值等。

统计时间间隔:对于主动触发统计,我们还要支持指定统计时间间隔,也就是多久触发一次统计显示。比如,每间隔 10s 统计一次接口信息并显示到命令行中,每间隔 24 小时发送一封统计信息邮件。

三、框架设计:

对于性能计数器这个框架的开发来说,我们可以先聚焦于一个非常具体、简单的应用场景,比如统计用户注册、登录这两个接口的响应时间的最大值和平均值、接口调用次数,并且将统计结果以 JSON 的格式输出到命令行中。现在这个需求简单、具体、明确,设计实现起来难度降低了很多。


//应用场景:统计下面两个接口(注册和登录)的响应时间和访问次数
public class UserController {public void register(UserVo user) {//...}public UserVo login(String telephone, String password) {//...}
}

要输出接口的响应时间的最大值、平均值和接口调用次数,我们首先要采集每次接口请求的响应时间,并且存储起来,然后按照某个时间间隔做聚合统计,最后才是将结果输出。在原型系统的代码实现中,我们可以把所有代码都塞到一个类中,暂时不用考虑任何代码质量、线程安全、性能、扩展性等等问题,怎么简单怎么来就行。

最小原型的代码实现如下所示。其中,recordResponseTime() 和 recordTimestamp() 两个函数分别用来记录接口请求的响应时间和访问时间。startRepeatedReport() 函数以指定的频率统计数据并输出结果。


public class Metrics {// Map的key是接口名称,value对应接口请求的响应时间或时间戳;private Map<String, List<Double>> responseTimes = new HashMap<>();private Map<String, List<Double>> timestamps = new HashMap<>();private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();public void recordResponseTime(String apiName, double responseTime) {responseTimes.putIfAbsent(apiName, new ArrayList<>());responseTimes.get(apiName).add(responseTime);}public void recordTimestamp(String apiName, double timestamp) {timestamps.putIfAbsent(apiName, new ArrayList<>());timestamps.get(apiName).add(timestamp);}public void startRepeatedReport(long period, TimeUnit unit){executor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {Gson gson = new Gson();Map<String, Map<String, Double>> stats = new HashMap<>();for (Map.Entry<String, List<Double>> entry : responseTimes.entrySet()) {String apiName = entry.getKey();List<Double> apiRespTimes = entry.getValue();stats.putIfAbsent(apiName, new HashMap<>());stats.get(apiName).put("max", max(apiRespTimes));stats.get(apiName).put("avg", avg(apiRespTimes));}for (Map.Entry<String, List<Double>> entry : timestamps.entrySet()) {String apiName = entry.getKey();List<Double> apiTimestamps = entry.getValue();stats.putIfAbsent(apiName, new HashMap<>());stats.get(apiName).put("count", (double)apiTimestamps.size());}System.out.println(gson.toJson(stats));}}, 0, period, unit);}private double max(List<Double> dataset) {//省略代码实现}private double avg(List<Double> dataset) {//省略代码实现}
}

我们通过不到 50 行代码就实现了最小原型。接下来,我们再来看,如何用它来统计注册、登录接口的响应时间和访问次数。具体的代码如下所示:


//应用场景:统计下面两个接口(注册和登录)的响应时间和访问次数
public class UserController {private Metrics metrics = new Metrics();public UserController() {metrics.startRepeatedReport(60, TimeUnit.SECONDS);}public void register(UserVo user) {long startTimestamp = System.currentTimeMillis();metrics.recordTimestamp("regsiter", startTimestamp);//...long respTime = System.currentTimeMillis() - startTimestamp;metrics.recordResponseTime("register", respTime);}public UserVo login(String telephone, String password) {long startTimestamp = System.currentTimeMillis();metrics.recordTimestamp("login", startTimestamp);//...long respTime = System.currentTimeMillis() - startTimestamp;metrics.recordResponseTime("login", respTime);}
}

最小原型的代码实现虽然简陋,但它却帮我们将思路理顺了很多,我们现在就基于它做最终的框架设计。下面是我针对性能计数器框架画的一个粗略的系统设计图。图可以非常直观地体现设计思想,并且能有效地帮助我们释放更多的脑空间,来思考其他细节问题。

如图所示,我们把整个框架分为四个模块:数据采集、存储、聚合统计、显示。每个模块负责的工作简单罗列如下。

数据采集:负责打点采集原始数据,包括记录每次接口请求的响应时间和请求时间。数据采集过程要高度容错,不能影响到接口本身的可用性。除此之外,因为这部分功能是暴露给框架的使用者的,所以在设计数据采集 API 的时候,我们也要尽量考虑其易用性。

存储:负责将采集的原始数据保存下来,以便后面做聚合统计。数据的存储方式有多种,比如:Redis、MySQL、HBase、日志、文件、内存等。数据存储比较耗时,为了尽量地减少对接口性能(比如响应时间)的影响,采集和存储的过程异步完成。

聚合统计:负责将原始数据聚合为统计数据,比如:max、min、avg、pencentile、count、tps 等。为了支持更多的聚合统计规则,代码希望尽可能灵活、可扩展。

显示:负责将统计数据以某种格式显示到终端,比如:输出到命令行、邮件、网页、自定义显示终端等。

四、面向对象设计与实现

1. 划分职责进而识别出有哪些类

MetricsCollector 类负责提供 API,来采集接口请求的原始数据。我们可以为 MetricsCollector 抽象出一个接口,但这并不是必须的,因为暂时我们只能想到一个 MetricsCollector 的实现方式。

MetricsStorage 接口负责原始数据存储,RedisMetricsStorage 类实现 MetricsStorage 接口。这样做是为了今后灵活地扩展新的存储方法,比如用 HBase 来存储。

Aggregator 类负责根据原始数据计算统计数据。

ConsoleReporter 类、EmailReporter 类分别负责以一定频率统计并发送统计数据到命令行和邮件。至于 ConsoleReporter 和 EmailReporter 是否可以抽象出可复用的抽象类,或者抽象出一个公共的接口,我们暂时还不能确定。

2. 定义类及类与类之间的关系

MetricsCollector 类的定义非常简单,具体代码如下所示。对比上一节课中最小原型的代码,MetricsCollector 通过引入 RequestInfo 类来封装原始数据信息,用一个采集函数代替了之前的两个函数。


public class MetricsCollector {private MetricsStorage metricsStorage;//基于接口而非实现编程//依赖注入public MetricsCollector(MetricsStorage metricsStorage) {this.metricsStorage = metricsStorage;}//用一个函数代替了最小原型中的两个函数public void recordRequest(RequestInfo requestInfo) {if (requestInfo == null || StringUtils.isBlank(requestInfo.getApiName())) {return;}metricsStorage.saveRequestInfo(requestInfo);}
}public class RequestInfo {private String apiName;private double responseTime;private long timestamp;//...省略constructor/getter/setter方法...
}

MetricsStorage 类和 RedisMetricsStorage 类的属性和方法也比较明确


public interface MetricsStorage {void saveRequestInfo(RequestInfo requestInfo);List<RequestInfo> getRequestInfos(String apiName, long startTimeInMillis, long endTimeInMillis);Map<String, List<RequestInfo>> getRequestInfos(long startTimeInMillis, long endTimeInMillis);
}public class RedisMetricsStorage implements MetricsStorage {//...省略属性和构造函数等...@Overridepublic void saveRequestInfo(RequestInfo requestInfo) {//...}@Overridepublic List<RequestInfo> getRequestInfos(String apiName, long startTimestamp, long endTimestamp) {//...}@Overridepublic Map<String, List<RequestInfo>> getRequestInfos(long startTimestamp, long endTimestamp) {//...}
}

MetricsCollector 类和 MetricsStorage 类的设计思路比较简单,不同的人给出的设计结果应该大差不差。但是,统计和显示这两个功能就不一样了,可以有多种设计思路。实际上,如果我们把统计显示所要完成的功能逻辑细分一下的话,主要包含下面 4 点:

1、根据给定的时间区间,从数据库中拉取数据;

2、根据原始数据,计算得到统计数据;

3、将统计数据显示到终端(命令行或邮件);

4、定时触发以上 3 个过程的执行。

我们选择把第 1、3、4 逻辑放到 ConsoleReporter 或 EmailReporter 类中,把第 2 个逻辑放到 Aggregator 类中。其中,Aggregator 类负责的逻辑比较简单,我们把它设计成只包含静态方法的工具类。具体的代码实现如下所示:


public class Aggregator {public static RequestStat aggregate(List<RequestInfo> requestInfos, long durationInMillis) {double maxRespTime = Double.MIN_VALUE;double minRespTime = Double.MAX_VALUE;double avgRespTime = -1;double p999RespTime = -1;double p99RespTime = -1;double sumRespTime = 0;long count = 0;for (RequestInfo requestInfo : requestInfos) {++count;double respTime = requestInfo.getResponseTime();if (maxRespTime < respTime) {maxRespTime = respTime;}if (minRespTime > respTime) {minRespTime = respTime;}sumRespTime += respTime;}if (count != 0) {avgRespTime = sumRespTime / count;}long tps = (long)(count / durationInMillis * 1000);Collections.sort(requestInfos, new Comparator<RequestInfo>() {@Overridepublic int compare(RequestInfo o1, RequestInfo o2) {double diff = o1.getResponseTime() - o2.getResponseTime();if (diff < 0.0) {return -1;} else if (diff > 0.0) {return 1;} else {return 0;}}});int idx999 = (int)(count * 0.999);int idx99 = (int)(count * 0.99);if (count != 0) {p999RespTime = requestInfos.get(idx999).getResponseTime();p99RespTime = requestInfos.get(idx99).getResponseTime();}RequestStat requestStat = new RequestStat();requestStat.setMaxResponseTime(maxRespTime);requestStat.setMinResponseTime(minRespTime);requestStat.setAvgResponseTime(avgRespTime);requestStat.setP999ResponseTime(p999RespTime);requestStat.setP99ResponseTime(p99RespTime);requestStat.setCount(count);requestStat.setTps(tps);return requestStat;}
}public class RequestStat {private double maxResponseTime;private double minResponseTime;private double avgResponseTime;private double p999ResponseTime;private double p99ResponseTime;private long count;private long tps;//...省略getter/setter方法...
}

ConsoleReporter 类相当于一个上帝类,定时根据给定的时间区间,从数据库中取出数据,借助 Aggregator 类完成统计工作,并将统计结果输出到命令行。具体的代码实现如下所示:


public class ConsoleReporter {private MetricsStorage metricsStorage;private ScheduledExecutorService executor;public ConsoleReporter(MetricsStorage metricsStorage) {this.metricsStorage = metricsStorage;this.executor = Executors.newSingleThreadScheduledExecutor();}// 第4个代码逻辑:定时触发第1、2、3代码逻辑的执行;public void startRepeatedReport(long periodInSeconds, long durationInSeconds) {executor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {// 第1个代码逻辑:根据给定的时间区间,从数据库中拉取数据;long durationInMillis = durationInSeconds * 1000;long endTimeInMillis = System.currentTimeMillis();long startTimeInMillis = endTimeInMillis - durationInMillis;Map<String, List<RequestInfo>> requestInfos =metricsStorage.getRequestInfos(startTimeInMillis, endTimeInMillis);Map<String, RequestStat> stats = new HashMap<>();for (Map.Entry<String, List<RequestInfo>> entry : requestInfos.entrySet()) {String apiName = entry.getKey();List<RequestInfo> requestInfosPerApi = entry.getValue();// 第2个代码逻辑:根据原始数据,计算得到统计数据;RequestStat requestStat = Aggregator.aggregate(requestInfosPerApi, durationInMillis);stats.put(apiName, requestStat);}// 第3个代码逻辑:将统计数据显示到终端(命令行或邮件);System.out.println("Time Span: [" + startTimeInMillis + ", " + endTimeInMillis + "]");Gson gson = new Gson();System.out.println(gson.toJson(stats));}}, 0, periodInSeconds, TimeUnit.SECONDS);}
}public class EmailReporter {private static final Long DAY_HOURS_IN_SECONDS = 86400L;private MetricsStorage metricsStorage;private EmailSender emailSender;private List<String> toAddresses = new ArrayList<>();public EmailReporter(MetricsStorage metricsStorage) {this(metricsStorage, new EmailSender(/*省略参数*/));}public EmailReporter(MetricsStorage metricsStorage, EmailSender emailSender) {this.metricsStorage = metricsStorage;this.emailSender = emailSender;}public void addToAddress(String address) {toAddresses.add(address);}public void startDailyReport() {Calendar calendar = Calendar.getInstance();calendar.add(Calendar.DATE, 1);calendar.set(Calendar.HOUR_OF_DAY, 0);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);calendar.set(Calendar.MILLISECOND, 0);Date firstTime = calendar.getTime();Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {long durationInMillis = DAY_HOURS_IN_SECONDS * 1000;long endTimeInMillis = System.currentTimeMillis();long startTimeInMillis = endTimeInMillis - durationInMillis;Map<String, List<RequestInfo>> requestInfos =metricsStorage.getRequestInfos(startTimeInMillis, endTimeInMillis);Map<String, RequestStat> stats = new HashMap<>();for (Map.Entry<String, List<RequestInfo>> entry : requestInfos.entrySet()) {String apiName = entry.getKey();List<RequestInfo> requestInfosPerApi = entry.getValue();RequestStat requestStat = Aggregator.aggregate(requestInfosPerApi, durationInMillis);stats.put(apiName, requestStat);}// TODO: 格式化为html格式,并且发送邮件}}, firstTime, DAY_HOURS_IN_SECONDS * 1000);}
}

3. 将类组装起来并提供执行入口

因为这个框架稍微有些特殊,有两个执行入口:一个是 MetricsCollector 类,提供了一组 API 来采集原始数据;另一个是 ConsoleReporter 类和 EmailReporter 类,用来触发统计显示。框架具体的使用方式如下所示:


public class Demo {public static void main(String[] args) {MetricsStorage storage = new RedisMetricsStorage();ConsoleReporter consoleReporter = new ConsoleReporter(storage);consoleReporter.startRepeatedReport(60, 60);EmailReporter emailReporter = new EmailReporter(storage);emailReporter.addToAddress("wangzheng@xzg.com");emailReporter.startDailyReport();MetricsCollector collector = new MetricsCollector(storage);collector.recordRequest(new RequestInfo("register", 123, 10234));collector.recordRequest(new RequestInfo("register", 223, 11234));collector.recordRequest(new RequestInfo("register", 323, 12334));collector.recordRequest(new RequestInfo("login", 23, 12434));collector.recordRequest(new RequestInfo("login", 1223, 14234));try {Thread.sleep(100000);} catch (InterruptedException e) {e.printStackTrace();}}
}

五、Review 设计与实现

1、MetricsCollector

MetricsCollector 负责采集和存储数据,职责相对来说还算比较单一。它基于接口而非实现编程,通过依赖注入的方式来传递 MetricsStorage 对象,可以在不需要修改代码的情况下,灵活地替换不同的存储方式,满足开闭原则。

2、MetricsStorage、RedisMetricsStorage

MetricsStorage 和 RedisMetricsStorage 的设计比较简单。当我们需要实现新的存储方式的时候,只需要实现 MetricsStorage 接口即可。因为所有用到 MetricsStorage 和 RedisMetricsStorage 的地方,都是基于相同的接口函数来编程的,所以,除了在组装类的地方有所改动(从 RedisMetricsStorage 改为新的存储实现类),其他接口函数调用的地方都不需要改动,满足开闭原则。

3、Aggregator

Aggregator 类是一个工具类,里面只有一个静态函数,有 50 行左右的代码量,负责各种统计数据的计算。

4、ConsoleReporter、EmailReporter


http://chatgpt.dhexx.cn/article/w6AkPSid.shtml

相关文章

java中文章的阅读数统计

java中文章的阅读数统计 1.直接操作数据库进行增加操作存在问题 存在问题:每次请求都要update数据库,如果访问量大了之后,数据库存在操作压力,此时怎么办? 解决方法:1.提高数据库性能(横向/纵向拓展)2.使用缓存方式减轻数据压力 推荐使用缓存方式:优点:操作快捷,可以减轻数据…

Java Math3 Descriptive Statistics (描述性统计分析)

文章目录 1、术语2、UnivariateStatistic&#xff1a; 单变量统计分析3、Frequency distributions&#xff1a;频率分布4、Simple regression &#xff1a; 简单回归5、Multiple linear regression&#xff1a;多元线性回归6、Rank transformations &#xff1a;7、Covariance …

字符个数统计(Java实现)

描述 编写一个函数&#xff0c;计算字符串中含有的不同字符的个数。字符在 ASCII 码范围内( 0~127 &#xff0c;包括 0 和 127 )&#xff0c;换行表示结束符&#xff0c;不算在字符里。不在范围内的不作统计。多个相同的字符只计算一次 例如&#xff0c;对于字符串 abaca 而言…

Java绘制统计图

0. 前言 本文采用第三方库xchart进行绘制&#xff0c;所需要使用的jar包如下&#xff1a; https://download.csdn.net/download/hfy1237/86508408 https://download.csdn.net/download/hfy1237/86508849 一、折线图 1. 方式一&#xff08;快速绘图&#xff09; package pl…

mysql 中文本类型有哪些_mysql数据类型有哪些

mysql数据类型有:1、数值类型;2、日期和时间类型;3、字符串类型是CHAR、VARCHAR、BINARY【二进制数据类型】、BLOB、TEXT【文本类型】、ENUM【枚举类型】和SET【数据集合】。 mysql数据类型有: MySQL支持的数据类型在几类:数值类型,日期和时间类型和字符串(字符)类型。 数…

mysql polygon 类型_MySQL数据类型 - 空间数据类型 (4)

支持的空间数据格式 有两种标准空间数据格式用于表示查询中的几何对象: ●已知文本(WKT)格式 ●已知二进制(WKB)格式 在内部,MySQL用与WKT或WKB格式不同的格式存储几何值。(内部格式类似于WKB,但有一个4个字节表示SRID。) MySQL提供函数可用于在不同的数据格式之间进行转换。…

MySQL数据类型-整数类型

MySQL提供了多种数据类型&#xff0c;包括整数类型、浮点数类型、定点数类型、日期和时间类型、字符串类型、二进制数据类型&#xff1b; 不同的数据类型有各自的的类型&#xff0c;使用范围也各不相同&#xff0c;而且存储方式也不相同 有两种类型的数字&#xff1a;整数和实…

MySQL 数据类型及占用空间

MySQL 数据类型 元数据 VARCHAR类型字符串及DECIMAL的占用空间实际上包含2部分&#xff0c;一是存储数据本身占用的空间&#xff0c;二是描述数据的元数据占用的空间&#xff0c;例如VARCHAR类型会使用1个字节记录存入数据实际的字符数。 最大行大小限制 MySQL表的内部表示…

Mysql数据类型与选择规则(mysql5.7)

概述 基于mysql5.7。 mysql数据类型大致可以分为四类&#xff1a;数值型、字符串型、日期和时间类型、空间类型。常用的如数值型的tinyint、int、bigint,字符串型的char、varchar、text&#xff0c;日期型的date、datetime、timestemp等&#xff0c;详细见下图&#xff1a; …

Mysql数据类型详解

1、整数类型&#xff0c;包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT&#xff0c;分别表示1字节、2字节、3字节、4字节、8字节整数。 任何整数类型都可以加上UNSIGNED属性&#xff0c;表示数据是无符号的&#xff0c;即非负整数。 长度&#xff1a;整数类型可以被指定长度…

mysql成绩是什么数据类型_mysql数据类型

1、整型 取值范围如果加了unsigned&#xff0c;则最大值翻倍&#xff0c;如tinyint unsigned的取值范围为(0~256)。 int(m)里的m是表示SELECT查询结果集中的显示宽度&#xff0c;并不影响实际的取值范围&#xff0c;没有影响到显示的宽度&#xff0c;不知道这个m有什么用。 2、…

最全MySQL数据类型详解

一、概述 1、MySQL中的数据类型 2、常见数据类型的属性 二、各种数据类型精讲 1、整数类型 1.1 整数类型介绍 整数类型一共有 5 种&#xff0c;包括 TINYINT、SMALLINT、MEDIUMINT、INT&#xff08;INTEGER&#xff09;和 BIGINT。它们的区别如下表所示&#xff1a; 1.2…

mysql住址数据类型_MySql数据类型

MySQL中定义数据字段的类型对你数据库的优化是非常重要的。 MySQL支持多种类型&#xff0c;大致可以分为三类&#xff1a;数值、日期/时间和字符串(字符)类型。 数值类型 MySQL支持所有标准SQL数值数据类型。 这些类型包括严格数值数据类型(INTEGER、SMALLINT、DECIMAL和NUMERI…

不全?MySQL数据类型精讲,定点日期枚举文本字符串,json二进制,空间,选择建议,完整详细可收藏

文章目录 1. MySQL中的数据类型2. 整数类型3. 浮点数类型4. 定点数类型5. 位类型&#xff1a;BIT6. 日期与时间类型7. 文本字符串类型8. ENUM类型9. SET类型10. 二进制字符串类型11. JSON 类型12. 空间类型13. 选择建议 1. MySQL中的数据类型 常见数据类型的属性&#xff0c;如…

MySQL的10种常用数据类型

MySQL的数据类型 常用的数据类型有&#xff1a; 整型&#xff08;xxxint&#xff09;位类型(bit)浮点型&#xff08;float和double、real&#xff09;定点数&#xff08;decimal,numeric&#xff09;日期时间类型&#xff08;date,time,datetime,year&#xff09;字符串&…

定点数除法

定点数除法 一&#xff0c;手工除法运算方法二&#xff0c;原码除法运算方法三&#xff0c;原码加/减交替除法运算方法&#xff08;不恢复余数法&#xff09;四&#xff0c;原码加/减交替除法实现逻辑五&#xff0c;阵列除法 一&#xff0c;手工除法运算方法 图 1 图1 图1 二&…

浮点数与定点数理解、定点数转浮点数相互转换

1、浮点数理解 在平常的代码编写中大家经常用到float 32、double 64等&#xff0c;但是否有深入的去了解一下这类数据是怎么表示的呢&#xff1f;今天我们就去学习浮点数的表示方法&#xff0c;以及其优缺点。 首先浮点数为什么叫浮点数呢&#xff1f;因为浮点数的小数点的位…

定点数的几种表示形式

第二章 数制与编码 2.1 进制之间的转换 掌握&#xff1a;二进制、八进制、十六进制(末位加H表示)、十进制及彼此之间的转换&#xff1b; 问&#xff1a;小数怎么转换&#xff1f; 十进制转二进制&#xff1a;乘以权值再累加&#xff0c;如小数点后第一位的权值是2^(-1) 二进…

计算机中定点数表示方法练习

1【单选题】针对8位二进制数,下列说法中正确的是 。&#xff08;5.0分&#xff09; A、-127的补码为10000000 B、-127的反码等于0的移码 C、1的移码等于-127的反码 D、0的补码等于-1的反码 正确答案&#xff1a; B 2【单选题】若某数x的真值为-0.1010,在计算机中该数表示为1.0…

定点数的表示和运算

本文主要介绍以下几方面知识&#xff1a; 定点数的表示&#xff08;无符号数、有符号数&#xff09;移位运算&#xff08;原码、反码、补码&#xff09;加减运算&#xff08;原码加减、补码加减&#xff09;乘法运算&#xff08;原码乘法、补码乘法&#xff09;除法运算&#x…