HoshiuZ's blog.
type: channel
link: https://www.hoshiuz.top/
description: Your description here.
items: A测-气压测控仿真系统
items: CF Round1045 Div.2
items: ABC420
items: 自建博客的图床问题-PicGo+github+obsidian
items: CF Round1043 Div.3
items: 生物信息学算法课程实验
items: 分布式计算第四次实验
items: 分布式计算第三次实验
items: 分布式计算第二次实验
items: 分布式计算第一次实验

A测-气压测控仿真系统
type: item
date: 2025-09-20T17:55:00+08:00
description: 测试内容是去年的补测题目。区别在于，去年的上位机软件直接用的是 XCOM，而今年的正测需要自行编写上位机软件。关于软件的安装以及初步的使用，参考了 西电A测：串口通信仿真-CSDN博客 与 2024春季 西电A测-气压测控仿真系统-CSDN博客。过程虽然今年让自己写一个上位机软件，但打算还是先用 XCOM 进行测试，一切正常后打算用 Python Tkinter 来写个 GUI。电路图已经提供了，只需分析电路图然后用 arduino IDE 写代码即可。这是 LCD，发现接了引脚 12,11,5,4,3,2，所以初始化 LCD 时对这几个引脚初始化。这是直流电机驱动电路，发现接了引脚 7，所以控制直流电机的操作可以通过将引脚 7 写高/低电平来实现。Arduino IDE 代码通过上述分析写出如下代码。选用的是串口 COM3.#include <Wire.h> #include <Adafruit_BMP085.h> #include <LiquidCrystal.h> Adafruit_BMP085 bmp; LiquidCrystal lcd(12, 11, 5, 4, 3, 2); void setup() { Serial.begin(9600); lcd.begin(16, 2); pinMode(7, OUTPUT); bmp.begin(); } int setATM = 860; void loop() { int realATM = (bmp.readPressure() + 50) / 100; String str = ""; lcd.print("ID:"); if(Serial.available() > 0) { while(Serial.available() > 0) { str += (char)Serial.read(); delay(10); } str.trim(); lcd.println(str); int len = str.length(); int last = str[len - 1] - '0'; setATM = 860 + last; str = ""; } Serial.print("ATM: "); Serial.print(realATM); Serial.println("hPa"); lcd.setCursor(0, 1); lcd.print("ATM: "); lcd.print(realATM); lcd.print("hPa"); lcd.home(); if(realATM >= setATM) { digitalWrite(7, HIGH); } else { digitalWrite(7, LOW); } delay(1000); }将代码用 arduino IDE 编译后，proteus 选中 arduino UNO 元件，并将 program file 选中生成的 .hex 文件。我所使用的串口是 COM3 和 COM4，用 VSPD 配置一下就行。然后打开 XCOM，设为 COM4，接着用 proteus 开启仿真。如图所示，正常运行。接下来就只剩下 GUI 的编写。GUI编写采用的是 Python 语言的 Tkinter 库，并用 pyserial 来实现串口通信。代码如下：import tkinter as tk from tkinter import ttk, scrolledtext import serial import serial.tools.list_ports from threading import Thread, Event import time class MyCOM: def __init__(self, window): # 初始化主窗口 self.window = window self.window.title("114514 田所浩二 - 气压测控仿真系统") self.window.geometry("800x600") # 串口相关变量 self.serial_port = None self.is_serial_open = False self.receive_thread = None self.stop_event = Event() self.create_widgets() # 创建界面控件 self.update_com_list() # 初始化时更新可用串口列表 def create_widgets(self): """创建并布局所有GUI控件""" # 顶部框架 - 串口控制区域 top_frame = ttk.LabelFrame(self.window, text="串口配置…

CF Round1045 Div.2
type: item
date: 2025-08-27T00:31:07+08:00
description: A当 $n, a, b$ 同奇偶，显然成立。由于按顺序的，所以只能由 $b$ 覆盖 $a$，所以当三者奇偶性不相同但是 $n$ 与 $b$ 同奇偶时，也成立。除了这两种情况以外都不成立。#include<bits/stdc++.h> #define ll long long int T; void solve() { int n, a, b; std::cin >> n >> a >> b; if(n % 2 == a % 2 && a % 2 == b % 2) std::cout << "YES\n"; else if(n % 2 == b % 2 && a < b) std::cout << "YES\n"; else std::cout << "NO\n"; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cin>>T; while(T--) { solve(); } return 0; }B当 $k$ 为奇数时，只需把每个数都加成偶数即可。当 $k$ 为偶数时，需要找到一个奇数 $b$ 使得 $b$ 不是 $k$ 的因数，然后将所有的数都加成 $b$ 的倍数，可以证明每个数加的次数一定都小于 $k$ 。$b$ 的求法，可以将 $k$ 一直除以 $2$ 直至为奇数，然后再加上 $2$ ，这样一个值即为一个符合条件的 $b$ ，时间复杂度 $O(\log k)$.知道了 $b$ 后，如何将 $a[i]$ 加成一个 $b$ 的倍数？有 $a[i] + kx = by$ ，所以即为同余方程 $kx + by = a[i]$，由上述 $b$ 的求法，显然 $b$ 与 $k$ 互质，所以这个同余方程一定有解，用扩展欧几里得算法求解即可，得到 $x_0, y_0$.题目中要求加的次数不超过 $k$ ，所以有 $-k \le x \le 0$，而有 $x = x_0 + tb$，代入解得 $\dfrac{-k-x_0}{b} \le t \le \dfrac{-x_0}{b}$，取 $t = \Big\lceil\dfrac{-k-x_0}{b}\Big\rceil$ 再进行调整即可得出 $x$.$a[i]$ 最终的值即为 $a[i] - kx$.#include<bits/stdc++.h> #define ll long long int T; void Exgcd(ll a,ll b, ll& x, ll& y) { if(!b) x = 1, y = 0; else { Exgcd(b, a % b, x, y); int t = x; x = y, y = t - a / b * y; } } void solve() { ll n, k; std::cin >> n >> k; std::vector<ll> a(n + 1); for(int i = 1; i <= n; i++) { std::cin >> a[i]; } if(n == 1) { std::cout << a[1] + k << "\n"; } else { if(k & 1) { for(int i = 1; i <= n; i++) { if(a[i] & 1) a[i] += k; } for(int i = 1; i <= n; i++) { std::cout << a[i] << " "; } std::cout << "\n"; } else { ll b = k; while(b % 2 == 0) b /= 2; b += 2; ll x, y; Exgcd(k, b, x, y); for(int i = 1; i <= n; i++) { ll X, Y; X = x * a[i], Y = y * a[i]; ll t = ceil((double)(-k - X) / b); if(X + b * t < -k) t++; if(X + b * t > 0) t--; X += b * t; a[i] -= X * k; } for(int i = 1; i <= n; i++) { std::cout << a[i] << " "; } std::cout << "\n"; } } } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cin>>T; while(T--) { solve(); } return 0; }C先考虑奇数项，把其变成左右两个偶数项中的较小值。再考虑偶数项，经过了上一步操作，每次一个子区间往后/往前扩展两个位置时，偶…

ABC420
type: item
date: 2025-08-24T22:19:00+08:00
description: 赛时 5/7，中途有点事，G 没打完。A直接将 $x$ 和 $y$ 相加后对 $12$ 取模即可。特判取模后为 $0$ 的情况。#include<bits/stdc++.h> #define ll long long int T; void solve() { int x, y; std::cin >> x >> y; int ans = (x + y) % 12; if(!ans) ans = 12; std::cout << ans << "\n"; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); T = 1; while(T--) { solve(); } return 0; }B模拟题，直接根据题意模拟即可。#include<bits/stdc++.h> #define ll long long int T; void solve() { int n, m; std::cin >> n >> m; std::vector<std::string> s(n + 1); for(int i = 1; i <= n; i++) { std::cin >> s[i]; s[i] = ' ' + s[i]; } std::vector<int> score(n + 1, 0), ans; int maxn = 0; for(int i = 1; i <= m; i++) { int x = 0, y = 0; for(int j = 1; j <= n; j++) { if(s[j][i] == '0') x++; else y++; } if(x < y) { for(int j = 1; j <= n; j++) { if(s[j][i] == '0') score[j]++; maxn = std::max(maxn, score[j]); } } else { for(int j = 1; j <= n; j++) { if(s[j][i] == '1') score[j]++; maxn = std::max(maxn, score[j]); } } } for(int i = 1; i <= n; i++) { if(score[i] == maxn) ans.push_back(i); } for(auto x: ans) { std::cout << x << " "; } std::cout << "\n"; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); T = 1; while(T--) { solve(); } return 0; }C维护答案，修改时先减去，修改后再加上即可。#include<bits/stdc++.h> #define ll long long int T; void solve() { int n, q; std::cin >> n >> q; std::vector<ll> a(n + 1), b(n + 1); for(int i = 1; i <= n; i++) { std::cin >> a[i]; } for(int i = 1; i <= n; i++) { std::cin >> b[i]; } ll ans = 0; for(int i = 1; i <= n; i++) { ans += std::min(a[i], b[i]); } for(int i = 1; i <= q; i++) { char c; ll x, y; std::cin >> c >> x >> y; ans -= std::min(a[x], b[x]); if(c == 'A') { a[x] = y; }else { b[x] = y; } ans += std::min(a[x], b[x]); std::cout << ans << "\n"; } } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); T = 1; while(T--) { solve(); } return 0; }D稍微麻烦一点的走迷宫问题。BFS即可，对于开关操作，用开关反转的两个图，记录开关次数来判断在哪个图上跑。#include<bits/stdc++.h> #define ll long long int T; const int dx[4] = {1, -1, 0, 0}; const int dy[4] = {0, 0, 1, -1}; void solv…

自建博客的图床问题-PicGo+github+obsidian
type: item
date: 2025-08-24T11:25:00+08:00
description: 一直用的是外界的公共图床，担心用久了之后图床突然没了会很麻烦，于是在网上开始搜索有无更好的图片存储方案。根据我自己的使用情况，选择了这种 PicGo + github + obsidian 的方案。首先在 github 上新建一个仓库用于存储图片，需要 Public，不然图片无法访问。接着在 github 上新建一个 token，并且保存下生成的 token。然后下载好 PicGo，根据下面这篇文章设置。Github+PicGo搭建个人免费图床 - misakivv - 博客园设置完成后，上传图片，发现可以正常上传到 github 上。接下来只剩下 Obisidian 的设置。当然，用 Typora 也可以，~~但是我已经转向 Obisidian 了在插件市场下载插件 Image auto upload，并启用，选择上传器为 PigGo 即可。具体的效果就是，用 Obisidian 写文章，粘贴一个图片到文章里时，会自动通过 PicGo 上传到 Github。非常的好用！

CF Round1043 Div.3
type: item
date: 2025-08-22T01:07:00+08:00
description: 赛时 6/8.A根据题意模拟即可。#include<bits/stdc++.h> #define ll long long int T; void solve() { int n, m; std::string a, b, c; std::cin >> n; std::cin >> a; std::cin >> m; std::cin >> b; std::cin >> c; for(int i = 0; i < m; i++) { if(c[i] == 'V') a = b[i] + a; else a = a + b[i]; } std::cout << a << "\n"; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cin>>T; while(T--) { solve(); } return 0; }B$y = 10^kx$ ，所以 $n = (10^k +1)x$，枚举 $k$ 即可求出 $x$ 与 $y$.#include<bits/stdc++.h> #define ll long long int T; void solve() { ll n; std::cin >> n; std::vector<ll> ans; ll x = 10; while(x + 1 <= n) { if(n % (x + 1) == 0) ans.push_back(n / (x + 1)); x *= 10; } sort(ans.begin(), ans.end()); std::cout << ans.size() << "\n"; for(auto x: ans) std::cout << x << " "; if(ans.size()) std::cout << "\n"; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cin>>T; while(T--) { solve(); } return 0; } C1交易数量最少，所以按照三进制表示来交易即可。#include<bits/stdc++.h> #define ll long long int T; ll qpow(ll x, ll y) { if(!y) return 1; ll t = qpow(x, y / 2); if(y & 1) return t * t * x; return t * t; } void solve() { ll n; std::cin >> n; ll x = 1, y = 0; while(x * 3 <= n) { x *= 3; y++; } ll ans = 0; while(n) { while(n >= x) { n -= x; ans += qpow(3, y + 1) + y * qpow(3, y - 1); } x /= 3; y--; } std::cout << ans << "\n"; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cin>>T; while(T--) { solve(); } return 0; } C2一个 $x$ 可以换成 $3$ 个 $x-1$. 要求总个数不超过 $k$.由 $\dfrac{3^{x+1} + x\cdot 3^{x - 1}}{3^x} = 3 +$ $\dfrac{ x }{3}$ ，所以发现 $x$ 越大，西瓜的单价越高。且 $3^{x+1} + x\cdot 3^{x - 1} - 3\times (3^x +(x - 1)\cdot 3^{x - 2}) = 3^{x-1}$，所以 $x$ 越大，换成 $x-1$ 所减少的金币数越多。于是先求出三进制表示，然后从高位来向下换即可。#include<bits/stdc++.h> #define ll long long int T; ll qpow(ll x, ll y) { if(!y) return 1; ll t = qpow(x, y / 2); if(y & 1) return t * t * x; return t * t; } ll f(ll x) { ll res = 0; while(x) { res += x % 3; x /= 3; } return res; } void solve() { ll n, k; std::cin >> n >> k; if(n <= k) { std::cout << n …

生物信息学算法课程实验
type: item
date: 2025-06-19T15:19:00+08:00
description: 实验内容实验环境语言：PythonIDE：Pycharm实验思路步骤一这一步其实就是筛选节点，得到图的点集，并把每个点都连边形成一个无向完全图。 每组数据行为基因，列为细胞。每个基因的表达方差其实就是这个基因的这一行的表达值的方差，然后从所有基因中筛选出方差最大的前 1000 个来作为图的节点集合，节点标识符定义为 ”基因名/CancerCell“ 或 ”基因名/TCell“。每种癌症有两组数据，所以每种癌症会有两个无向完全图。步骤二这一步其实就是把上一步得到的六个无向完全图之间连边。根据提供的 LigandReceptor_Human.txt 提供的受体配体来连边。最终每种癌症的两个无向完全图会连在一起，形成一个新的无向完全图。步骤三这一步是为步骤一连的边加上边权。根据要求计算边权即可。spearman 相关系数可以直接用 pandas 库里的方法，效率要比 spicy 库的 spearmanr 函数快很多。步骤四这一步是为步骤二连的边加上边权。根据要求计算边权即可。步骤五把每种癌症得到的无向图求最小生成树即可。由于是稠密图，所以考虑使用 Prim 算法。直接用库里的 get_mininum_spanning_tree 函数即可，效率要比自己手写的 MST 要高。步骤六把步骤五中得到的最小生成树中的 Intercellular Edges 提取出来，并把其两端延伸的五阶邻居也提取出来。可使用库里的 single_source_shortest_path 函数来处理，比自己手写的 BFS 效率要高。步骤七对于每个最小生成树，求出其所有节点的度中心性，筛选出前 50 名。每个最小生成树的前 50 名比较异同，用韦恩图展示结果。步骤八对于每个最小生成树，求出其所有节点的介数中心性，筛选出前 50 名。每个最小生成树的前 50 名比较异同，用韦恩图展示结果。代码根据上述思路可得以下代码。import os import networkx as nx import pandas as pd import numpy as np import matplotlib.pyplot as plt from matplotlib_venn import venn3 from sklearn.preprocessing import minmax_scale os.makedirs("Results", exist_ok=True) def get_data(cancer, cell, base_path="InputData"): file_path = os.path.join(base_path, f"{cancer}_{cell}_GeneExpression.csv") df = pd.read_csv(file_path, index_col=0) return df def select_data(df): var = df.var(axis=1) selected = var.sort_values(ascending=False).head(1000).index return df.loc[selected] def build_intracellular_graph(df, cell): graph = nx.Graph() genes = df.index.tolist() for gene in genes: graph.add_node(f"{gene}/{cell}") corr_matrix = df.T.corr(method='spearman').abs() corr_matrix = corr_matrix.fillna(0) upper_triangle = np.triu_indices_from(corr_matrix, k=1) edges = [ (f"{genes[i]}/{cell}", f"{genes[j]}/{cell}") for i, j in zip(upper_triangle[0], upper_triangle[1]) ] weights = 1 - minmax_scale(corr_matrix.values[upper_triangle]) for (u, v), w in zip(edges, weights): graph.add_edge(u, v, weight=w) return graph def add_intercellular_edges(graph, cancer_df, tcell_df, ligand_receptor_file="LigandReceptor_Human.txt"): ligand_receptor_df = pd.read_…

分布式计算第四次实验
type: item
date: 2025-06-12T01:51:00+08:00
description: 实验目的学习基于MPI分布式并行计算程序设计方法。学习基于MapReduce框架的分布式并行计算程序设计方法。学习基于Spark框架的分布式并行计算程序设计方法。实验题目题目 1编写基于 MPI 的并行计算程序，实现对连续函数 $f(x)=\sin(x^2+𝑥)(x\in R)$ 在区 间 $[a, b]$ 上的定积分求解。要求使用“梯形法（Trapezoidal Rule）”进行近似计算。题目 2输入文件为学生成绩信息，包含了必修课与选修课成绩，格式如下： 班级1, 姓名1, 科目1, 必修, 成绩1 （注： 为换行符） 班级2, 姓名2, 科目1, 必修, 成绩2 班级1, 姓名1, 科目2, 选修, 成绩3 ………., ………, ………, ……… 编写 Hadoop 平台上的 MapReduce 程序，分别实现如下功能：计算每个学生必修课的平均成绩。统计每个班级中所有课程（必修+选修）平均成绩排名前五的学生姓名和成绩题目 3输入文件的每一行为具有父子/父女/母子/母女/关系的一对人名，例如： Tim, Andy Harry, Alice Mark, Louis Andy, Joseph ……….., ………… 假定不会出现重名现象。 编写 Hadoop 平台上的 MapReduce 程序，找出所有具有 grandchild-grandparent 关系的人名对。题目 4输入文件为学生成绩信息，包含了必修课与选修课成绩，格式如下： 班级1, 姓名1, 科目1, 必修, 成绩1 （注： 为换行符） 班级2, 姓名2, 科目1, 必修, 成绩2 班级1, 姓名1, 科目2, 选修, 成绩3 ………., ………, ………, ……… 编写Spark程序，实现如下功能： 按班级统计必修课平均成绩在：90~100、80~89、70~79、60~69 和 60 分以下这 5 个分数段的学生人数。准备工作安装 Microsoft MPI首先需要安装 Microsoft MPI。参考了 windows安装MPI及python安装mpi4py_下载mpi4py代码在vmware-CSDN博客。直接照着下载安装配置环境变量即可。安装 Docker安装 WSL 2Docker Desktop for Windows 需要 WSL 2 支持，所以需要先安装 WSL 2。参考了 # Windows10安装WSL的Ubuntu20.04系统及踩坑记录安装 Docker参考了 在国内 Windows 平台上安装 Docker 的详细教程_docker windows intel-CSDN博客部署 Hadoop 和 Spark 实验环境git clone 一下 hadoop-sandbox/hadoop-sandbox: A fully-functional Hadoop Yarn cluster as docker-compose deployment.然后切换到解压后的目录下，使用 powershell 执行 docker-compose up -d，会根据该目录下 .yml 文件来下载镜像并启动容器。接着执行 docker-compose down 来关闭启动的容器。下载老师提供的 spark-install.zip 压缩包，并解压。然后用 powershell 切换到解压后的目录，并执行 docker build -t packet23/hadoop-client:latest . ，此命令将根据 Dockerfile 的指示在名为 hadoop-client 的 Docker 镜像中下载并安装 Python3.8，Spark-3.2.1 和 apache-maven-3.8.5。上述操作结束后就成功生成了实验所需的全部 Docker 镜像。为 clientnode 安装 mrjob 库由于我使用的语言为 Python，所以需要为容器安装 mrjob 库。由于容器内没有 pip，所以先要对其安装 pip。先 docker ps 查看 clientnode 容器 ID，然后 docker exec -it --user root clientnode bash （其中 clientnode 换成容器 ID），进入后再执行apt update apt install -y python3-pip pip3 install mrjob如此，便安装好了 mrjob 库。为 nodemanager 安装 python3再测试老师给的代码时，发现 nodemanager 没有安装 python3 ，无法运行。所以需要对其安装 python3。先 docker ps 查看 nodemaanager 容器 ID，然后 docker exec -it --user …

分布式计算第三次实验
type: item
date: 2025-05-10T01:43:00+08:00
description: 实验内容准备工作环境配置根据老师发的教程安装 activemq 即可。实验环境系统：Windows 11语言：JavaIDE：Intellij IDEA使用 maven 管理项目。pom.xml 文件如下：<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>HoshiuZ</groupId> <artifactId>Log</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>19</maven.compiler.source> <maven.compiler.target>19</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>6.1.6</version> </dependency> <dependency> <groupId>jakarta.jms</groupId> <artifactId>jakarta.jms-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.17.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.17.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version> </dependency> <dependency> <groupId>org.jfree</groupId> <artifactId>jfreechart</artifactId> <version>1.5.3</version> </dependency> </dependencies> </project>jackson 用于 JSON 与对象的转化，jfreechart 用于画折线图，activemq 为使用的消息队列。思路分析先对实验题目进行分析。对于第一个日志采集节点，显然其是一个生产者；第二个异常检测与统计分析微服务，其显然是一个消费者，但是对于其功能二三，其也要生成消息发送到消息队列，所以其既是一个消费者也是一个生产者；第三个实时日志可视化监控微服务，其显然是一个消费者。对于发送 json 数据，可以考虑使用 java 中的 Jackson 库，可以比较方便的将数据打包成 json 发送到消息队列。对于日志采集节点的“每个节点用唯一 ID 标识”这一要求，可以考虑对该程序每次传一个参数进去来作为 ID 标识。需要三个消息队列，一个日志队列 logs，一个分析结果队列 analysisResults ，一个严重告警队列 criticalAlerts 。多个任务采集节点只需一个 producer 。每隔 100 毫秒向日志队列 logs 发送一个日志，其 log_level 可以随机生成。异常检测与统计分析微服务需要一个 consumer 和两个 producer 。一直从日志队列 logs 接收日志，对每个设备维护最…

分布式计算第二次实验
type: item
date: 2025-05-07T01:39:00+08:00
description: 实验内容准备工作我选择的语言是 Python，选用的中间件是 gRPC。环境的配置参考了 快速入门 | Python | gRPC 框架写 .proto文件.proto 文件是 gRPC 的核心部分，它定义了通信使用的消息结构与RPC的接口方法。根据实验内容，可以写出以下 .proto 文件。我新加了一个叫做 getStorageSize 的接口，因为我认为在实际应用中，预约ID应当是服务端生成给客户端的，而最简单的ID生成方法就是从 1 开始挨个排，所以可以在服务端中定义一个全局的字典用来存储预约信息，每次给的 ID 就是字典内元素数量加 1。所以我们需要实现一个给客户端的接口用来获取这个服务端的全局的字典内的元素个数。syntax = 'proto3'; message Appointment{ int32 appointmentId = 1; string patientName = 2; string doctorName = 3; string department = 4; string date = 5; string timeSlot = 6; } message BoolResult{ bool res = 1; } message AppointmentId{ int32 appointmentId = 1; } message PatientName{ string patientName = 1; } message AppointmentList{ repeated Appointment appointments = 1; } message Empty{} message StorageSize{ int32 size = 1; } service AppointmentService{ rpc bookAppointment(Appointment) returns (BoolResult); rpc queryById(AppointmentId) returns (Appointment); rpc queryByPatient(PatientName) returns (AppointmentList); rpc cancelAppointment(AppointmentId) returns (BoolResult); rpc getStorageSize(Empty) returns (StorageSize); }使用 protoc 生成 Python 代码使用 cmd，切换到对应目录下，由于我的文件名为 HospitalAppointment.proto，所以在 cmd 中输入以下命令来生成 Python 代码。python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. HospitalAppointment.proto这会生成两个文件：HospitalAppointment_pb2.py：包含消息类定义HospitalAppointment_pb2_grpc.py：包含服务的接口定义编写服务端代码创建 Python 文件 server.py，实现 .proto 文件中定义的接口，并启用 gRPC 服务监听端口。这几个接口其实并不难写，每个接口都有 request, context 两个参数，而这个 request 就相当于客户端向服务端请求的数据，对于每个方法，这个数据的内容就是 .proto 文件里所定义的对应的 message 。可以写出以下的 Server.py。from concurrent import futures import grpc import HospitalAppointment_pb2 import HospitalAppointment_pb2_grpc appointment_storage = {} class AppointmentServiceServicer(HospitalAppointment_pb2_grpc.AppointmentServiceServicer): # 预约挂号 def bookAppointment(self, request, context): print("预约挂号的信息为：") print(request) print("------------------------------") BoolResult = getattr(HospitalAppointment_pb2, 'BoolResult') appointment_storage[request.appointmentId] = request return BoolRe…

分布式计算第一次实验
type: item
date: 2025-03-25T01:32:00+08:00
description: 准备工作安装 jdk参考了 最详细jdk安装以及配置环境（保姆级教程）_jdk环境配置-CSDN博客安装 maven 并为 maven 设置阿里云镜像仓库参考了 全站最全Maven下载安装配置教学（2024更新...全版本）建议收藏...赠送IDEA配置Maven教程-CSDN博客用 maven 编译 Helloworld 版本的 java 程序用 cmd 在 DClab 目录下键入mvn archetype:generate -DgroupId=hoshiuz -DartifactId=helloworld -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false即可在 DClab 下创建 helloworld 项目。接着用 cmd 进入项目目录，使用 mvn compile 来编译项目。编译完成后，在 cmd 中键入 java -cp target/classes com.example.App来运行程序，可以在 cmd 中看到结果 Hello World!。题目基于 Socket 编写支持多客户端并发访问的 Key-Value 存储服务器，客户端可以存储（PUT key value）和检索（GET key）数据。数据存储在内存中（或使用简单的文件持久化）。服务器支持的指令格式：PUT key value ——存储键值对GET key——获取 key 对应的 valueDELETE key——删除 key 及其对应的 valueEXIT——关闭客户端连接用 JMeter 测试你的服务器程序的并发性能（平均响应时间和 TPS 指标）。JMeter 下载安装参考了 【超详细】Jmeter安装配置详细教程_jmeter安装教程以及jdk环境配置-CSDN博客设计思路可以考虑直接在服务端开一个静态的 HashMap，由于要实现多客户端并发，而 HashMap 是线程不安全的，所以应当使用 ConcurrentHashMap。然后剩余操作就和老师讲的示例差不多，服务端两个 socket，一个用来监听，另一个用来通信；客户端只需要用 socket 传指令即可。考虑服务端线程的编写，对于从客户端收到的一行字符串，如何将其识别为指令呢？由于给出的指令类别很少，只有四种，所以可以考虑将字符串 split(" ") 后，根据拆分出的第一个单词来分类讨论处理。但是这样处理会有一个问题：如果指令和操作数之间隔了很多空格的话，识别出的键与值可能会变成空格，所以处理方法为：把 split(" ") 改成 split("\\s+")，用正则表达式来根据连续的空格拆分这样剩下的就是一些简单的分类讨论和用 socket 通信了。代码KeyValueServer 类：// KeyValueServer 类 package lab1; import java.io.*; import java.net.*; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @SuppressWarnings("InfiniteLoopStatement") public class KeyValueServer { static Map<String, String> map = new ConcurrentHashMap<>(); public static void main(String[] args) throws Exception { System.out.println("在端口1234监听。"); try (ServerSocket listenSocket = new ServerSocket(1234)) { while (true) { try { Socket socket = listenSocket.accept(); ServerThread serverThread = new ServerThread(socket); serverThread.start(); } catch (IOException e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } } }ServerThread 类// ServerThread 类 package lab1; import java.io.*; import java.net.Socket; public class ServerThread extends Thread{ Socket socket = null; publi…