dtmrs_server/grpc/
client.rs1use dtmrs_core::BranchResult;
33use prost::bytes::{Buf, BufMut};
34use std::collections::HashMap;
35use std::sync::{Arc, Mutex};
36use std::time::Duration;
37use tonic::codec::{Codec, DecodeBuf, Decoder, EncodeBuf, Encoder};
38use tonic::codegen::http::uri::PathAndQuery;
39use tonic::transport::{Channel, Endpoint};
40use tonic::Status;
41use tracing::{info, warn};
42
43use super::{MD_BRANCH_ID, MD_GID, MD_OP, MD_TRANS_TYPE};
44use crate::registry::GrpcTarget;
45
46#[derive(Debug, Default, Clone, Copy)]
48pub struct BytesCodec;
49
50impl Codec for BytesCodec {
51 type Encode = Vec<u8>;
52 type Decode = Vec<u8>;
53 type Encoder = BytesCodec;
54 type Decoder = BytesCodec;
55
56 fn encoder(&mut self) -> Self::Encoder {
57 *self
58 }
59 fn decoder(&mut self) -> Self::Decoder {
60 *self
61 }
62}
63
64impl Encoder for BytesCodec {
65 type Item = Vec<u8>;
66 type Error = Status;
67
68 fn encode(&mut self, item: Self::Item, dst: &mut EncodeBuf<'_>) -> Result<(), Self::Error> {
69 dst.put_slice(&item);
70 Ok(())
71 }
72}
73
74impl Decoder for BytesCodec {
75 type Item = Vec<u8>;
76 type Error = Status;
77
78 fn decode(&mut self, src: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> {
80 let mut out = vec![0u8; src.remaining()];
81 src.copy_to_slice(&mut out);
82 Ok(Some(out))
83 }
84}
85
86#[derive(Clone)]
92pub struct GrpcCaller {
93 channels: Arc<Mutex<HashMap<String, Channel>>>,
94 timeout: Duration,
95}
96
97impl GrpcCaller {
98 pub fn new(timeout: Duration) -> Self {
99 Self {
100 channels: Arc::new(Mutex::new(HashMap::new())),
101 timeout,
102 }
103 }
104
105 fn channel(&self, endpoint: &str) -> Result<Channel, String> {
106 if let Some(c) = self.channels.lock().unwrap().get(endpoint) {
108 return Ok(c.clone());
109 }
110 let ch = Endpoint::from_shared(endpoint.to_string())
111 .map_err(|e| format!("gRPC 地址不合法: {e}"))?
112 .timeout(self.timeout)
113 .connect_timeout(self.timeout)
114 .connect_lazy();
115 self.channels
116 .lock()
117 .unwrap()
118 .insert(endpoint.to_string(), ch.clone());
119 Ok(ch)
120 }
121
122 pub async fn call(
125 &self,
126 target: &GrpcTarget,
127 gid: &str,
128 trans_type: &str,
129 branch_id: &str,
130 op: &str,
131 ) -> BranchResult {
132 let channel = match self.channel(&target.endpoint) {
133 Ok(c) => c,
134 Err(e) => {
135 warn!(gid, branch = branch_id, endpoint = %target.endpoint, error = %e,
138 "gRPC 分支地址不合法,按结果未知处理(会重试,不回滚)");
139 return BranchResult::Unknown;
140 }
141 };
142
143 let path = match PathAndQuery::try_from(target.path.clone()) {
144 Ok(p) => p,
145 Err(e) => {
146 warn!(gid, branch = branch_id, path = %target.path, error = %e,
147 "gRPC 方法路径不合法,按结果未知处理");
148 return BranchResult::Unknown;
149 }
150 };
151
152 let mut grpc = tonic::client::Grpc::new(channel);
153 if let Err(e) = grpc.ready().await {
154 warn!(gid, branch = branch_id, error = %e, "gRPC 分支不可达,结果未知");
155 return BranchResult::Unknown;
156 }
157
158 let mut req = tonic::Request::new(Vec::<u8>::new());
160 for (k, v) in [
161 (MD_GID, gid),
162 (MD_TRANS_TYPE, trans_type),
163 (MD_BRANCH_ID, branch_id),
164 (MD_OP, op),
165 ] {
166 match v.parse() {
167 Ok(val) => {
168 req.metadata_mut().insert(k, val);
169 }
170 Err(_) => {
171 warn!(
174 gid,
175 branch = branch_id,
176 key = k,
177 "metadata 值不合法(非 ASCII?),无法调用 gRPC 分支"
178 );
179 return BranchResult::Unknown;
180 }
181 }
182 }
183
184 match grpc
185 .unary::<Vec<u8>, Vec<u8>, BytesCodec>(req, path, BytesCodec)
186 .await
187 {
188 Ok(_) => {
189 info!(gid, branch = branch_id, op, "gRPC 分支返回 OK");
190 BranchResult::Success
191 }
192 Err(status) => {
193 let r = BranchResult::from_grpc(status.code() as i32);
194 info!(gid, branch = branch_id, op, code = ?status.code(), result = ?r,
195 "gRPC 分支返回");
196 r
197 }
198 }
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn 地址不合法时是未知而不是失败() {
208 let c = GrpcCaller::new(Duration::from_secs(1));
210 assert!(c.channel("这不是个地址").is_err());
211 }
212
213 #[tokio::test]
214 async fn 连不上的分支不能触发回滚() {
215 let c = GrpcCaller::new(Duration::from_millis(300));
217 let t = GrpcTarget {
218 endpoint: "http://127.0.0.1:1".into(),
219 path: "/a.B/C".into(),
220 };
221 let r = c.call(&t, "g1", "saga", "01", "action").await;
222 assert_eq!(r, BranchResult::Unknown, "连不上必须是未知,不能是失败");
223 }
224
225 #[tokio::test]
229 async fn channel会被缓存复用() {
230 let c = GrpcCaller::new(Duration::from_secs(1));
231 let a = c.channel("http://127.0.0.1:9").unwrap();
232 let b = c.channel("http://127.0.0.1:9").unwrap();
233 assert_eq!(c.channels.lock().unwrap().len(), 1);
235 drop((a, b));
236 }
237}