乐闻世界logo
搜索文章和话题

RPC

远程过程调用(Remote Procedure Call,简称 RPC)是一种计算机通信协议,允许一台计算机(客户端)执行另一台计算机(服务器)上的程序或过程,就好像它是本地程序一样。RPC 隐藏了底层的网络通信细节,使得开发分布式应用程序变得更简单。
RPC
查看更多相关内容
如何使用 python 在 grpc 中增加消息大小
在gRPC中,默认的消息大小限制可能不够用于某些应用场景,尤其是当需要传输大量数据时。如果需要在Python中使用gRPC传输更大的消息,可以通过配置`grpc.max_send_message_length`和`grpc.max_receive_message_length`这两个参数来增加消息的最大大小。 这里是一个如何配置这些参数的示例: ### 服务端 在服务端,你可以在创建`grpc.server`时设置这些参数,以允许接收和发送更大的消息。下面是一个简单的例子: ```python import grpc from concurrent import futures import your_service_pb2 import your_service_pb2_grpc class YourServiceServicer(your_service_pb2_grpc.YourServiceServicer): def YourMethod(self, request, context): # 处理请求的代码 return your_service_pb2.YourResponse() def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), options=[ ('grpc.max_send_message_length', 50 * 1024 * 1024), # 50 MB ('grpc.max_receive_message_length', 50 * 1024 * 1024) # 50 MB ]) your_service_pb2_grpc.add_YourServiceServicer_to_server(YourServiceServicer(), server) server.add_insecure_port('[::]:50051') server.start() server.wait_for_termination() if __name__ == '__main__': serve() ``` ### 客户端 在客户端,同样可以在创建`grpc.insecure_channel`时设置这些参数,以允许发送和接收更大的消息。示例如下: ```python import grpc import your_service_pb2 import your_service_pb2_grpc def run(): with grpc.insecure_channel('localhost:50051', options=[ ('grpc.max_send_message_length', 50 * 1024 * 1024), # 50 MB ('grpc.max_receive_message_length', 50 * 1024 * 1024) # 50 MB ]) as channel: stub = your_service_pb2_grpc.YourServiceStub(channel) response = stub.YourMethod(your_service_pb2.YourRequest()) print("Client received: ", response) if __name__ == '__main__': run() ``` ### 注意事项 - 增加消息大小可能会影响网络性能和内存使用。在生产环境中使用前,建议进行充分的性能测试。 - 这种配置应该根据实际需求谨慎设置,过大的值可能会引起不必要的资源消耗或安全问题。 - 请确保客户端和服务端的设置一致,以避免因配置不匹配导致的通信问题。
阅读 8 · 7月24日 01:45
如何将gRPC定义的API带到web浏览器
gRPC默认使用HTTP/2作为传输协议,这对于服务间通信非常高效,但并不是所有浏览器都原生支持gRPC。要在web浏览器中使用gRPC API,我们可以采用以下几种策略: ### 1. 使用gRPC-Web gRPC-Web 是一种使Web应用能够直接与后端gRPC服务通信的技术。它不是gRPC标准的一部分,但它由相同的团队开发,并得到广泛的支持和维护。 **实现步骤:** - **服务端适配:** 在服务端,需要使用gRPC-Web的代理(例如Envoy),这个代理会将浏览器的HTTP/1请求转换为gRPC服务可以理解的HTTP/2格式。 - **客户端实现:** 在客户端,使用gRPC-Web提供的JavaScript客户端库来发起gRPC调用。这个库能够与Envoy代理通信,并处理请求与响应。 **示例:** ```javascript const {GreeterClient} = require('./generated/helloworld_grpc_web_pb'); const {HelloRequest} = require('./generated/helloworld_pb'); const client = new GreeterClient('https://your-envoy-url', null, null); const request = new HelloRequest(); request.setName('World'); client.sayHello(request, {}, (err, response) => { if (err) { console.error(err); return; } console.log(response.getMessage()); }); ``` ### 2. 使用RESTful API作为中介 如果不想直接在浏览器中处理gRPC逻辑,或者你的应用已经有现成的RESTful API架构,可以通过构建一个REST API作为gRPC服务和Web浏览器之间的中介。 **实现步骤:** - **API Gateway/服务:** 开发一个API Gateway或者一个简单的服务,这个服务监听来自浏览器的HTTP/1请求,将这些请求转换为gRPC调用,然后再将响应转换回HTTP格式发送给浏览器。 - **数据转换:** 这种方法需要在服务器端进行数据格式的转换,比如将JSON转换为protobuf。 **示例:** 假设有一个Node.js的Express应用作为中介: ```javascript const express = require('express'); const {GreeterClient} = require('./generated/helloworld_grpc_pb'); const app = express(); const client = new GreeterClient('localhost:50051', grpc.credentials.createInsecure()); app.get('/sayHello/:name', (req, res) => { const request = new HelloRequest(); request.setName(req.params.name); client.sayHello(request, (error, response) => { if (error) { res.status(500).send(error); return; } res.send(response.getMessage()); }); }); app.listen(3000, () => { console.log('Server is running at http://localhost:3000'); }); ``` ### 总结 选择哪种策略取决于你的具体需求和现有的架构。gRPC-Web提供了一种相对直接的方法,可以让浏览器客户端直接与gRPC服务交互,而使用REST API作为中介则可能更适合那些需要维持现有REST架构的场景。
阅读 11 · 7月24日 01:45
GRPC与REST有何不同?
### gRPC 与 REST 的主要区别 1. **通信协议和数据格式**: - **REST**:RESTful Web服务通常使用HTTP/1.1协议,数据格式多样,包括JSON、XML等,更灵活。 - **gRPC**:gRPC默认使用HTTP/2协议,数据格式是基于ProtoBuf(Protocol Buffers),这是一种轻量级的二进制格式,设计用来更快的数据交换。 2. **性能**: - **REST**:由于使用文本格式如JSON,解析速度可能比二进制格式慢,特别是在数据体积较大时。 - **gRPC**:由于使用HTTP/2的多路复用、服务器推送等高效特性,以及ProtoBuf的二进制格式,gRPC在网络通信中的延迟更低,数据传输更加高效。 3. **API设计**: - **REST**:遵循标准的HTTP方法如GET、POST、PUT、DELETE等,易于理解和使用,API呈现资源状态转换的形式。 - **gRPC**:基于强契约,通过定义服务接口和使用ProtoBuf来严格定义消息结构,支持更复杂的交互模式,如流处理。 4. **浏览器支持**: - **REST**:由于基于纯HTTP,所有现代浏览器均支持无需任何额外配置。 - **gRPC**:由于依赖HTTP/2和ProtoBuf,浏览器支持不如REST广泛,通常需要使用特定的库或者代理转换为WebSocket等技术。 5. **用例适用性**: - **REST**:适用于公共API、少量数据或对开发者友好性有较高要求的场景。 - **gRPC**:适合于微服务架构中服务间的高效通信、大数据传输、实时通信等场景。 #### 示例应用场景 例如,在构建一个微服务架构的在线零售系统时,各个微服务之间的通信可以通过gRPC实现,因为它可以提供更低的延迟和更高的数据传输效率。而对于面向消费者的服务,如商品展示页面等,则可以使用REST API,因为它更易于与现有的Web技术集成,且更易于调试和测试。 ### 结论 gRPC和REST各有优势和适用场景,选择哪种技术取决于具体需求,如对性能的需求、开发资源、客户端兼容性等因素。在实际工作中,两者也可以结合使用,发挥各自的优势。
阅读 7 · 7月24日 01:44
如何在 C 中生成. Proto 文件或使用“Code First gRPC”
在C语言中生成 `.proto` 文件或使用 Code First gRPC 的方法相对有限,因为C语言不支持原生的gRPC Code First 开发方式。通常,我们会使用其他支持 Code First 的语言来生成 `.proto` 文件,然后再将这些文件用于C项目中。但是,我可以为你提供一种可能的方法来在C语言项目中使用gRPC,并解释如何生成 `.proto` 文件。 ### 步骤1: 创建.proto文件 首先,你需要创建一个 `.proto` 文件,这个文件定义了你的服务接口和消息格式。这不是特定于任何编程语言的,而是一种跨语言的方式来定义接口。例如: ```proto syntax = "proto3"; package example; // 定义一个服务 service Greeter { // 定义一个RPC方法 rpc SayHello (HelloRequest) returns (HelloReply); } // 定义消息格式 message HelloRequest { string name = 1; } message HelloReply { string message = 1; } ``` ### 步骤2: 使用protoc生成C代码 一旦你有了 `.proto` 文件,你可以使用 `protoc` 编译器来生成C语言的源代码。gRPC支持多种语言,但对C的支持通过一个叫做gRPC C Core的库来实现。你需要安装 `grpc` 和 `grpc-tools` 来生成C语言的gRPC代码。 在命令行中,可以使用以下命令: ```bash protoc -I=. --c_out=. ./example.proto ``` 注意:这里假设不存在直接的 `--c_out` 选项,因为官方的gRPC对C的支持主要是通过C++ API。实际上,你可能需要生成C++代码,然后通过C语言调用C++代码。 ### 步骤3: 在C项目中使用生成的代码 生成的代码通常包括对应的服务接口和请求/响应消息的序列化和反序列化函数。在你的C或C++项目中,你需要将这些生成的文件包含进来,并且编写相应的服务器和客户端代码来实现定义在 `.proto` 文件中的接口。 ### 示例: C++服务器和C客户端 假设你生成了C++的服务代码,你可以写一个C++服务器: ```cpp #include <grpcpp/grpcpp.h> #include "example.grpc.pb.h" class GreeterServiceImpl final : public example::Greeter::Service { grpc::Status SayHello(grpc::ServerContext* context, const example::HelloRequest* request, example::HelloReply* reply) override { std::string prefix("Hello "); reply->set_message(prefix + request->name()); return grpc::Status::OK; } }; void RunServer() { std::string server_address("0.0.0.0:50051"); GreeterServiceImpl service; grpc::ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); std::cout << "Server listening on " << server_address << std::endl; server->Wait(); } int main() { RunServer(); return 0; } ``` 然后,你可以尝试通过C调用这些服务,尽管通常需要C++客户端来与之交互或者使用专用的C库如 `grpc-c`。 ### 总结 在C语言中直接使用 Code First gRPC 是有挑战性的,主要是因为C语言的限制和gRPC官方的支持偏向现代语言。一个可行的路径是使用C++作为中介或查看是否有第三方库提供了这样的支持。尽管这个过程可能涉及到C++,但你仍然可以将核心功能保留在C语言中。
阅读 9 · 7月24日 01:44
如何共享gRPC的Protobuf定义?
在使用gRPC进行微服务开发时,共享Protobuf(Protocol Buffers)定义是一个常见的需求,因为它允许不同的服务之间能够清晰且一致地理解数据结构。以下是几种有效的方法来共享Protobuf定义: ### 1. 使用统一的仓库(Mono Repository) 创建一个单独的仓库来存储所有的Protobuf定义文件。这种方式的好处是中央管理,任何服务都可以从这个仓库拉取最新的定义文件。 **例子:** 假设你有多个微服务,例如用户服务和订单服务,都需要使用用户信息的Protobuf定义。你可以创建一个名为 `protobuf-definitions` 的Git仓库,其中包含所有公共的`.proto`文件。这样,用户服务和订单服务都可以引用这个仓库中的用户信息定义。 ### 2. 使用包管理工具 将Protobuf定义打包成库,并通过包管理工具(如npm, Maven, NuGet等)进行版本控制和分发。这种方法使得版本管理变得简单,依赖关系明确。 **例子:** 比如使用Java开发,你可以将Protobuf定义打包成一个Jar文件,并通过Maven或Gradle进行管理。当有更新时,只需发布新版本的Jar包,服务依赖的地方可以通过更新依赖版本来同步最新的Protobuf定义。 ### 3. 使用API管理服务 利用API管理工具,如Swagger或Apigee,来托管和分发Protobuf定义。这些工具提供了界面友好、易于访问的方式来查看和下载定义文件。 **例子:** 通过Swagger UI,你可以为Protobuf定义创建一个API文档页面。开发人员可以直接从这个界面获取需要的`.proto`文件,并可以看到每个字段的具体说明,这样增加了使用的便利性和准确性。 ### 4. 内部维护API网关 在内部系统中,可以设置一个API网关来统一管理和分发Protobuf定义。网关可以提供实时的定义更新服务,确保所有服务都在使用最新的定义。 **例子:** 假设你的企业内部有一个API网关,所有的服务调用都必须通过这个网关。可以设置网关的一个模块专门用来存储和分发`.proto`文件。服务在启动时,从网关下载最新的Protobuf定义,保证数据结构的一致性。 ### 总结 共享gRPC的Protobuf定义是微服务架构中的一个重要部分,确保了不同服务之间的数据交互的一致性和准确性。通过上述的方法,可以有效地管理和共享Protobuf定义,提高开发效率和系统的稳定性。
阅读 9 · 7月24日 01:43
如何将 Google 原型时间戳转换为Java LocalDate?
在Java中将Google Protobuf的时间戳转换为`LocalDate`对象,可以通过使用`java.time`包中的`Instant`类和`LocalDate`类来实现。以下是具体的步骤和示例: 1. **引入依赖(如果使用Maven)**: 要使用Google Protobuf,需要在项目的pom.xml文件中添加protobuf的依赖。 ```xml <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.12.0</version> </dependency> ``` 2. **获取Protobuf时间戳**: 假设你已经从某个数据源或API接收到了一个Protobuf的`Timestamp`对象。 3. **转换过程**: 首先,将Protobuf的Timestamp转换为Java的`Instant`对象,然后再将`Instant`转换为`LocalDate`。 下面是一个具体的代码示例: ```java import com.google.protobuf.Timestamp; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; public class TimestampToLocalDate { public static LocalDate convert(Timestamp timestamp) { // 将Timestamp转换为Instant Instant instant = Instant.ofEpochSecond( timestamp.getSeconds(), timestamp.getNanos() ); // 将Instant转换为LocalDate // 这里我们使用的是系统默认时区,也可以指定时区,如ZoneId.of("Asia/Shanghai") LocalDate date = instant.atZone(ZoneId.systemDefault()).toLocalDate(); return date; } public static void main(String[] args) { // 创建一个Timestamp实例(假设是当前时间) Timestamp timestamp = Timestamp.newBuilder() .setSeconds(System.currentTimeMillis() / 1000) .setNanos(0) .build(); LocalDate localDate = convert(timestamp); System.out.println("LocalDate: " + localDate); } } ``` 在上述代码中,利用`Instant.ofEpochSecond`方法将`Timestamp`的秒和纳秒转换为`Instant`对象。然后,使用`Instant.atZone`方法将`Instant`转换为`ZonedDateTime`,最后调用`toLocalDate`得到`LocalDate`对象。 这种转换在处理只需要日期不需要时间的情况下非常有用,例如生日、纪念日等。
阅读 7 · 7月24日 01:43
如何在gRPC服务器中添加全局异常拦截器?
在gRPC服务中添加全局异常拦截器是一个很好的做法,因为它可以帮助您集中处理服务中发生的各种异常,从而使异常管理更加规范和清晰。以下是在gRPC服务器中添加全局异常拦截器的步骤和示例: ### 步骤 1: 创建异常拦截器类 首先,您需要创建一个异常拦截器类。这个类需要实现`ServerInterceptor`接口。在这个拦截器类中,您可以捕获和处理所有未被服务方法直接处理的异常。 ```java public class ExceptionInterceptor implements ServerInterceptor { @Override public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { ServerCall.Listener<ReqT> delegate = next.startCall(call, headers); return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(delegate) { @Override public void onHalfClose() { try { super.onHalfClose(); // 正常处理请求 } catch (RuntimeException e) { handleException(call, e); // 处理异常 } } }; } private <ReqT, RespT> void handleException(ServerCall<ReqT, RespT> call, RuntimeException e) { // 对不同的异常进行处理,设置相应的状态码和描述 if (e instanceof IllegalArgumentException) { call.close(Status.INVALID_ARGUMENT.withDescription(e.getMessage()), new Metadata()); } else { call.close(Status.UNKNOWN.withDescription("Unknown error occurred"), new Metadata()); } } } ``` 这个实现捕获了方法执行过程中抛出的异常,并根据异常类型返回合适的错误状态。 ### 步骤 2: 注册拦截器到服务中 创建了拦截器之后,接下来需要在gRPC服务器配置时将其添加到服务器中。 ```java public class GrpcServer { public static void main(String[] args) throws IOException, InterruptedException { Server server = ServerBuilder.forPort(8080) .addService(new YourServiceImpl()) // 添加您的服务实现 .intercept(new ExceptionInterceptor()) // 添加拦截器 .build(); server.start(); server.awaitTermination(); } } ``` 在这个例子中,`ExceptionInterceptor`被添加到了服务器构建过程中,这意味着它将会拦截所有通过该服务器处理的调用。 ### 测试和验证 完成了上述步骤后,您应该通过单元测试或集成测试来验证拦截器的行为。确保在触发了预期的异常时,拦截器能够正确地捕获这些异常并返回适当的错误响应。 通过以上步骤,您可以在gRPC服务器中有效地添加和使用全局异常拦截器,这将帮助您更好地管理和响应服务中的异常情况。
阅读 11 · 7月24日 01:42
如何调试grpc调用?
gRPC是一个高性能、开源和通用的RPC框架,由Google主导开发。它使用HTTP/2作为传输协议,支持多种语言,能够实现跨语言的服务调用。gRPC常用于微服务架构中的服务间调用。 ### 常见问题类型 调试gRPC调用通常涉及以下几种情况: 1. **连接问题**:无法建立连接或连接不稳定。 2. **性能问题**:调用延迟高或吞吐量低。 3. **数据问题**:请求或响应数据不符合预期。 4. **错误处理**:服务端或客户端错误处理不当。 ### 调试步骤与技术 #### 1. 日志记录 开启gRPC和HTTP/2的详细日志是理解问题的第一步。例如,在Java中,可以通过设置系统属性来开启gRPC的日志记录: ```java System.setProperty("io.grpc.ChannelLogger", "FINE"); System.setProperty("java.util.logging.ConsoleHandler.level", "FINEST"); ``` 在Python中可以通过设置环境变量来控制日志级别: ```bash export GRPC_TRACE=all export GRPC_VERBOSITY=DEBUG ``` #### 2. 错误码检查 gRPC定义了一系列的标准错误码,如 `UNAVAILABLE`, `DEADLINE_EXCEEDED`等。检查这些错误码可以快速定位问题类型。在客户端和服务端都应该有错误处理和记录机制。 #### 3. 网络工具使用 使用网络调试工具如Wireshark来观察gRPC的HTTP/2流量。这可以帮助你理解连接问题和性能问题。Wireshark能够展示完整的请求和响应消息,以及它们对应的HTTP/2帧。 #### 4. 服务端监控 在服务端实现监控,记录每个RPC调用的响应时间、请求大小、响应大小等参数。这些数据对于分析性能问题非常有帮助。可以使用Prometheus、Grafana等工具进行监控数据的收集和展示。 #### 5. 调试工具 使用专门的调试工具,如BloomRPC或Postman,这些工具可以模拟客户端请求,帮助你在没有编写客户端代码的情况下测试gRPC服务。 ### 实际案例 我曾经参与过一个项目,其中一个gRPC服务表现出了高延迟和偶发的连接超时。通过启用详细日志,我们发现部分请求因为 `DEADLINE_EXCEEDED`错误失败。进一步分析后发现,服务端处理某些特定请求的时间过长。通过优化那部分处理逻辑,我们成功降低了延迟,并解决了超时问题。 ### 结论 调试gRPC调用可以从多个方面进行,包括日志记录、网络监测、服务监控以及使用调试工具。根据问题的不同类型选择合适的工具和策略是关键。
阅读 8 · 7月24日 01:42
gRPC和CORBA有什么区别?
**gRPC** 和 **CORBA** (Common Object Request Broker Architecture)是两种不同的系统间通信(inter-process communication)技术。它们都允许从一个程序调用在另一个程序中运行的代码,但它们在设计、执行以及使用的时代背景上有所不同。 ### 1. 设计语言和协议 - **gRPC** 是基于HTTP/2协议的,支持多种语言,如Python、Go、Java等。它使用Protobuf(Protocol Buffers)作为其接口定义语言(IDL),这使得定义服务和生成相应的代码变得非常高效和简洁。 - **CORBA** 使用一种叫做IDL(Interface Definition Language)的语言来定义接口,这种语言独立于任何编程语言。CORBA支持多种编程语言,但其自身的IDL和复杂的服务描述可能使得学习和使用门槛较高。 ### 2. 性能和效率 - **gRPC** 利用HTTP/2的特性,如头部压缩、多路复用等,来提高性能和降低延迟。Protobuf也是为了高效率而设计,序列化和反序列化操作非常快。 - **CORBA**,虽然在设计时期也考虑了高效的通信,但由于其使用的技术和协议(如GIOP/IIOP)相对较老,可能不如gRPC高效。 ### 3. 容错性和可伸缩性 - **gRPC** 支持客户端和服务端流式处理,这种方式可以在处理大量数据时保持连接的开放状态,而不是对每个小块数据都建立新的连接。此外,gRPC的使用HTTP/2使其在现代互联网架构下更容易扩展和维护。 - **CORBA** 也支持类似的特性,如对象持久性和服务的位置透明性,但在现代的微服务和容器化部署中,其较为复杂的配置和较老的技术栈可能会增加实现这些特性的难度。 ### 4. 使用场景和时代背景 - **gRPC** 是由Google开发,主要用于支持其内部微服务架构,并在2015年开源。因此,它与现代互联网技术(如微服务、容器和云计算)的结合更为紧密。 - **CORBA** 是在1990年代由OMG(Object Management Group)开发的,主要目标是支持不同系统之间的通信和操作的互操作性。随着技术的发展,CORBA的使用已经大大减少,尤其是在新的项目中。 ### 示例 假设我们有一个需要处理大量实时数据的金融服务。使用**gRPC**,我们可以定义一个服务接口,使用Protobuf来定义数据模型,然后利用gRPC的客户端和服务端流功能来持续接收和发送数据,而这一切都在一个持久的HTTP/2连接中完成。相比之下,如果使用**CORBA**,可能需要更多的配置,并且要确保所有参与的系统都正确实现了CORBA的标准,这在现代多样化的技术栈中可能是一个挑战。 总结来说,虽然gRPC和CORBA都是有效的跨语言通信解决方案,但gRPC更适合现代的应用场景,特别是在需要高效率、高性能的微服务架构中。CORBA虽然在历史上在企业应用中占有一席之地,但在现代的应用中,它的使用正在逐渐被新技术所取代。
阅读 6 · 7月24日 01:42
如何在Protocol Buffers中弃用整个消息?
在Protocol Buffers中,弃用整个消息通常是为了让使用这个消息的开发者知道该消息将在未来的版本中被移除或不再推荐使用。要做到这一点,我们可以通过在消息的定义中添加适当的注释来标记该消息为已弃用。这是一个重要的过程,因为它能够帮助维护API的向后兼容性,同时告知开发者需要逐渐迁移到新的实现或消息格式。 ### 示例 假设我们有一个Protocol Buffers消息定义如下: ```protobuf message Book { string title = 1; string author = 2; } ``` 如果我们需要弃用这个`Book`消息,我们可以在消息定义前添加`deprecated`选项,像这样: ```protobuf // Deprecated: This message will be removed in future versions. message Book { option deprecated = true; string title = 1; string author = 2; } ``` ### 实施步骤 1. **添加注释**: 为消息添加一个注释,说明它被弃用的原因以及推荐的替代方案。 2. **使用`deprecated`选项**: 在消息定义中设置`option deprecated = true`。这将明确地标记消息为已弃用。 3. **文档和通知**: 更新相关文档,并通知使用这个消息的开发者关于弃用决定及其影响。 4. **提供替代方案**: 如果可能,提供一个替代的消息定义或方法,帮助开发者平滑过渡。 ### 注意事项 - **向后兼容性**: 弃用消息时,考虑到向后兼容性是非常重要的。确保在完全移除之前,有足够的时间让开发者迁移到新的消息或方法。 - **版本控制**: 弃用和最终移除消息应该伴随着版本号的变化。通常,弃用发生在主要版本的小更新中,而移除则发生在更大的版本更新中。 - **清晰的交流**: 弃用的决定和计划应该清晰地传达给所有相关的利益相关者,避免混淆和可能的错误。 通过这样的方式,我们不仅能够保持协议的整洁和更新,还可以确保开发者社区能够有序地适应变化,减少因突然变更带来的潜在问题。
阅读 9 · 7月24日 01:42