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

服务端面试题手册

RPC 和 RESTful API 有什么区别?在什么场景下选择 RPC?

RPC 和 RESTful API 是两种常见的分布式系统通信方式,它们各有优缺点:RPC(远程过程调用)特点:优势:高性能:使用二进制序列化(如 Protobuf),传输效率高强类型:通过 IDL(接口定义语言)定义服务契约,编译时检查低延迟:支持 HTTP/2 多路复用,减少连接开销双向流:支持双向流式通信,适合实时场景代码生成:自动生成客户端和服务端代码,减少开发工作量劣势:调试困难:二进制协议不易直接查看和调试学习曲线:需要学习特定的 RPC 框架和协议浏览器兼容性:部分 RPC 协议(如 gRPC)需要额外支持耦合度较高:客户端和服务端需要共享接口定义RESTful API 特点:优势:简单易用:基于 HTTP 协议,使用 JSON/XML 格式通用性强:跨平台、跨语言,浏览器原生支持易于调试:可以使用 curl、Postman 等工具直接测试松耦合:客户端和服务端通过 URL 和 HTTP 方法交互缓存友好:利用 HTTP 缓存机制,提高性能劣势:性能较低:文本格式(JSON)传输效率不如二进制冗余数据:每个请求都包含 HTTP 头,增加传输开销无状态:需要额外的机制维护会话状态实时性差:不适合需要实时双向通信的场景选择建议:内部微服务通信:优先选择 RPC(如 gRPC、Dubbo)对外 API:优先选择 RESTful API实时通信场景:选择支持流的 RPC 框架跨语言团队协作:考虑 RESTful API 的通用性性能敏感场景:选择 RPC 以获得更好的性能
阅读 0·2月22日 14:07

如何选择合适的 RPC 框架?主流 RPC 框架(gRPC、Dubbo、Thrift 等)的对比和选择建议是什么?

RPC 框架的选择需要根据项目需求、技术栈、团队经验等多方面因素综合考虑:主流 RPC 框架对比:1. gRPC特点:Google 开源,基于 HTTP/2 和 Protobuf优势:高性能:HTTP/2 多路复用,Protobuf 高效序列化跨语言:支持 10+ 种语言流式通信:支持单向流和双向流强类型:IDL 定义接口,编译时检查生态完善:拦截器、负载均衡、链路追踪劣势:浏览器支持有限(需要 grpc-web)学习曲线较陡调试相对困难(二进制协议)适用场景:微服务内部通信需要流式通信的场景跨语言服务调用高性能要求的场景技术栈:Go、Java、Python、C++、Node.js 等2. Dubbo特点:阿里巴巴开源,Java 生态优势:易用性:与 Spring 深度集成功能全面:服务治理、负载均衡、容错机制性能优秀:基于 Netty,支持长连接社区活跃:阿里巴巴和社区持续维护文档完善:中文文档丰富劣势:主要面向 Java跨语言支持相对较弱适用场景:Java 微服务架构国内企业项目需要完善服务治理的场景技术栈:Java、Spring Boot、Spring Cloud Alibaba3. Thrift特点:Facebook 开源,支持多种协议和传输方式优势:跨语言:支持多种编程语言灵活性:支持多种序列化格式和传输协议代码生成:强大的代码生成功能性能优秀:二进制序列化效率高劣势:学习曲线较陡文档相对较少社区活跃度不如 gRPC适用场景:跨语言、多协议的复杂场景需要灵活配置的场景技术栈:Java、Python、Go、C++、Node.js 等4. Spring Cloud OpenFeign特点:基于 HTTP 的声明式 RPC优势:简单易用:声明式接口定义Spring 集成:与 Spring Cloud 深度集成通用性强:基于 HTTP,跨平台易于调试:文本协议,易于查看劣势:性能相对较低(基于 HTTP/1.x)不支持流式通信适用场景:Spring Cloud 微服务架构对外 API性能要求不高的场景技术栈:Java、Spring Boot、Spring Cloud5. Motan特点:微博开源,Java RPC 框架优势:简单易用:配置简单性能优秀:基于 Netty支持多种协议:RPC、HTTP服务治理:支持服务注册、发现、负载均衡劣势:社区相对较小主要面向 Java适用场景:Java 微服务架构需要简单易用的 RPC 框架技术栈:Java、Spring Boot6. brpc特点:百度开源,C++ RPC 框架优势:高性能:C++ 实现,性能优秀功能全面:支持多种协议、服务治理跨语言:支持多语言客户端劣势:主要面向 C++学习曲线较陡适用场景:C++ 微服务架构高性能要求的场景技术栈:C++、Java、Python、Go 等选择建议:1. 根据技术栈选择Java 生态:Dubbo、Spring Cloud OpenFeign、MotanGo 生态:gRPC、ThriftPython 生态:gRPC、ThriftC++ 生态:gRPC、brpc、Thrift多语言:gRPC、Thrift2. 根据性能要求选择高性能:gRPC、Dubbo、brpc一般性能:Thrift、Motan低性能要求:Spring Cloud OpenFeign3. 根据功能需求选择需要流式通信:gRPC需要完善服务治理:Dubbo、gRPC需要简单易用:Spring Cloud OpenFeign、Motan需要灵活配置:Thrift4. 根据团队经验选择熟悉 Spring:Dubbo、Spring Cloud OpenFeign熟悉 Google 技术:gRPC熟悉 Facebook 技术:Thrift5. 根据项目场景选择内部微服务:gRPC、Dubbo对外 API:Spring Cloud OpenFeign、RESTful API实时通信:gRPC跨语言:gRPC、Thrift性能对比(大致排序):序列化性能:Protobuf (gRPC) > Hessian (Dubbo) > Thrift > JSON (Feign)传输性能:HTTP/2 (gRPC) > TCP (Dubbo) > HTTP/1.x (Feign)综合性能:gRPC > Dubbo > brpc > Thrift > Motan > Feign代码示例对比:gRPC:service UserService { rpc GetUser (GetUserRequest) returns (GetUserResponse) {}}Dubbo:public interface UserService { User getUser(Long id);}Feign:@FeignClient(name = "user-service")public interface UserService { @GetMapping("/user/{id}") User getUser(@PathVariable("id") Long id);}最佳实践:优先选择社区活跃、文档完善的框架考虑团队技术栈和学习成本评估性能和功能需求考虑未来扩展性进行性能测试验证参考行业最佳实践
阅读 0·2月22日 14:06

RPC 调用中的容错机制有哪些?如何处理网络异常和服务故障?

RPC 调用过程中,网络异常、服务故障等问题不可避免,需要完善的容错机制来保证系统稳定性:1. 超时机制(Timeout)作用:防止客户端无限等待实现:设置合理的超时时间(连接超时、读取超时)策略:根据网络状况和业务需求动态调整示例:Dubbo 的 timeout 配置、gRPC 的 deadline2. 重试机制(Retry)适用场景:网络抖动、临时性故障重试策略:指数退避(Exponential Backoff):每次重试间隔逐渐增加固定间隔:每次重试间隔相同最大重试次数:避免无限重试注意事项:幂等性设计,避免重复执行导致数据不一致3. 熔断机制(Circuit Breaker)原理:当故障率达到阈值时,快速失败,避免雪崩状态:关闭(Closed)、开启(Open)、半开启(Half-Open)实现:Hystrix、Resilience4j、Sentinel参数配置:失败率阈值、超时时间、恢复时间4. 限流机制(Rate Limiting)目的:保护服务不被过载算法:令牌桶(Token Bucket)漏桶(Leaky Bucket)固定窗口(Fixed Window)滑动窗口(Sliding Window)实现:Guava RateLimiter、Redis + Lua5. 服务降级(Fallback)作用:服务不可用时提供备用方案策略:返回默认值返回缓存数据调用备用服务返回友好错误提示6. 负载均衡(Load Balancing)算法:轮询(Round Robin)随机(Random)最少连接(Least Connections)一致性哈希(Consistent Hash)健康检查:定期检测服务实例健康状态7. 服务注册与发现作用:动态管理服务实例实现:Consul、Etcd、Zookeeper、Nacos特性:健康检查、服务剔除、自动注册8. 链路追踪(Distributed Tracing)作用:快速定位问题实现:Zipkin、Jaeger、SkyWalking信息:请求 ID、调用链路、耗时统计最佳实践:组合使用多种容错机制根据业务重要性配置不同的容错策略监控和告警及时发现问题定期演练故障场景
阅读 0·2月22日 14:06

RPC 调用中的安全性问题有哪些?如何实现身份认证、数据加密和授权?

RPC 调用涉及网络传输,安全性是必须考虑的重要问题。以下是 RPC 安全性的关键方面和实现方法:1. 身份认证(Authentication)Token 认证客户端在请求中携带 Token服务端验证 Token 有效性Token 可以是 JWT、OAuth2 等实现示例: // gRPC 拦截器实现 Token 认证 public class AuthInterceptor implements ServerInterceptor { @Override public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall( ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { String token = headers.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER)); if (!validateToken(token)) { call.close(Status.UNAUTHENTICATED.withDescription("Invalid token"), headers); return new ServerCall.Listener<ReqT>() {}; } return next.startCall(call, headers); } }API Key 认证为每个客户端分配唯一的 API Key简单但安全性相对较低适合内部服务调用双向 TLS(mTLS)客户端和服务端都验证对方证书提供强身份认证适用于高安全要求的场景2. 数据加密(Encryption)传输层加密TLS/SSL:加密整个通信通道HTTPS:基于 HTTP 的 RPC 使用 HTTPSgRPC over TLS:gRPC 支持 TLS 加密实现示例: // gRPC TLS 配置 NettyChannelBuilder.forAddress(host, port) .sslContext(GrpcSslContexts.forClient() .trustManager(new File("ca.pem")) .build()) .build();应用层加密对敏感数据进行额外加密使用 AES、RSA 等加密算法即使传输层被破解,数据仍然安全3. 授权(Authorization)基于角色的访问控制(RBAC)为用户分配角色角色关联权限检查用户是否有权限调用特定服务基于资源的访问控制细粒度控制对资源的访问可以控制到方法级别权限注解使用注解标记需要权限的方法拦截器统一处理权限检查4. 防重放攻击时间戳验证请求中包含时间戳服务端验证时间戳是否在有效范围内防止旧请求被重放Nonce 机制每次请求使用唯一的随机数服务端记录已使用的 Nonce防止相同请求被重复使用请求签名对请求参数进行签名签名包含时间戳和 Nonce服务端验证签名有效性5. 防止 DDoS 攻击限流限制单个客户端的请求频率使用令牌桶、漏桶等算法实现示例: // Guava RateLimiter RateLimiter rateLimiter = RateLimiter.create(100); // 100 QPS if (rateLimiter.tryAcquire()) { // 处理请求 } else { throw new RateLimitExceededException(); }黑名单/白名单拦截来自黑名单 IP 的请求只允许白名单 IP 访问验证码对可疑请求要求验证码防止自动化攻击6. 数据完整性消息认证码(MAC)使用 HMAC 等算法验证消息完整性防止数据在传输中被篡改数字签名使用私钥签名,公钥验证提供不可抵赖性7. 安全审计日志记录记录所有 RPC 调用包括调用者、时间、参数等便于事后审计和问题排查监控告警监控异常调用模式及时发现安全威胁8. 安全配置最佳实践最小权限原则只授予必要的权限定期审查权限配置定期更新证书及时更新过期的证书使用证书自动管理工具安全配置检查定期进行安全扫描使用安全配置检查工具敏感信息保护不在日志中记录敏感信息使用配置中心管理密钥定期轮换密钥9. 框架特定安全配置gRPC 安全启用 TLS使用拦截器实现认证和授权配置 ALTS(Application Layer Transport Security)Dubbo 安全配置 Token 认证使用 Dubbo Filter 实现安全检查支持自定义序列化协议加密Thrift 安全使用 TSSLTransport实现 TProcessor 拦截器自定义协议层加密
阅读 0·2月22日 14:06

如何实现 RPC 的异步调用?异步调用有哪些模式和优势?

异步 RPC 调用是提高系统性能和并发能力的重要技术,允许客户端在等待响应的同时处理其他任务:异步调用模式:1. Future/Promise 模式原理:调用后立即返回 Future 对象,通过 Future 获取结果优点:简单易用,不阻塞调用线程缺点:需要主动获取结果,代码可能不够优雅实现示例: // Dubbo 异步调用 <dubbo:reference interface="com.example.UserService" async="true"/> // 使用 userService.getUser(1L); Future<User> future = RpcContext.getContext().getFuture(); User user = future.get(1000, TimeUnit.MILLISECONDS);2. 回调模式(Callback)原理:调用时传入回调函数,结果返回时执行回调优点:事件驱动,适合异步处理缺点:回调地狱,代码可读性差实现示例: public interface AsyncCallback<T> { void onSuccess(T result); void onFailure(Throwable t); } // 使用 userService.getUserAsync(1L, new AsyncCallback<User>() { @Override public void onSuccess(User user) { // 处理成功结果 } @Override public void onFailure(Throwable t) { // 处理失败 } });3. 响应式编程(Reactive)原理:使用响应式流(Reactive Streams)处理异步数据优点:代码优雅,支持背压,适合流式处理缺点:学习曲线较陡实现示例: // Reactor Mono<User> userMono = userService.getUserReactive(1L); userMono.subscribe( user -> System.out.println(user), error -> System.err.println(error) ); // RxJava Observable<User> userObs = userService.getUserRx(1L); userObs.subscribe( user -> System.out.println(user), error -> System.err.println(error) );4. gRPC 异步调用原理:使用 StreamObserver 处理异步响应优点:支持流式通信,与 gRPC 深度集成实现示例: // 一元异步调用 stub.sayHello(request, new StreamObserver<HelloResponse>() { @Override public void onNext(HelloResponse response) { // 处理响应 } @Override public void onError(Throwable t) { // 处理错误 } @Override public void onCompleted() { // 调用完成 } }); // 双向流 StreamObserver<Request> requestObserver = stub.bidirectionalStream( new StreamObserver<Response>() { @Override public void onNext(Response response) { // 处理响应 } @Override public void onError(Throwable t) { // 处理错误 } @Override public void onCompleted() { // 调用完成 } }); // 发送请求 requestObserver.onNext(request1); requestObserver.onNext(request2); requestObserver.onCompleted();5. CompletableFuture原理:Java 8 引入的异步编程工具优点:功能强大,支持链式调用实现示例: CompletableFuture<User> future = CompletableFuture.supplyAsync( () -> userService.getUser(1L) ); // 链式调用 future.thenAccept(user -> System.out.println(user)) .exceptionally(t -> { System.err.println(t); return null; }); // 组合多个 Future CompletableFuture<User> userFuture = userService.getUserAsync(1L); CompletableFuture<Order> orderFuture = orderService.getOrderAsync(1L); CompletableFuture<Result> resultFuture = userFuture.thenCombineAsync( orderFuture, (user, order) -> new Result(user, order) );异步调用的优势:1. 提高并发能力不阻塞调用线程可以同时处理多个请求充分利用系统资源2. 降低延迟客户端可以并行发起多个调用减少等待时间提高响应速度3. 提高吞吐量单位时间内处理更多请求适合高并发场景4. 更好的用户体验避免界面卡顿实现实时更新异步调用的挑战:1. 代码复杂度异步代码难以理解和调试错误处理复杂需要处理线程安全问题2. 上下文传递异步调用时上下文可能丢失需要显式传递上下文信息解决方案:使用 ThreadLocal、TransmittableThreadLocal3. 超时控制需要合理设置超时时间避免无限等待实现示例: CompletableFuture<User> future = userService.getUserAsync(1L); try { User user = future.get(1000, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { future.cancel(true); }4. 资源管理需要合理管理线程池避免资源耗尽实现示例: ExecutorService executor = Executors.newFixedThreadPool(10); CompletableFuture<User> future = CompletableFuture.supplyAsync( () -> userService.getUser(1L), executor );最佳实践:1. 合理选择异步模式简单场景:Future/Promise事件驱动:回调模式流式处理:响应式编程高性能要求:CompletableFuture2. 完善的错误处理捕获所有异常提供有意义的错误信息实现重试机制3. 超时控制设置合理的超时时间超时后取消请求避免资源泄漏4. 资源管理使用线程池管理线程及时释放资源避免内存泄漏5. 监控和日志记录异步调用日志监控异步调用性能及时发现问题适用场景:高并发场景需要并行调用多个服务流式数据处理实时性要求高的场景长时间运行的任务
阅读 0·2月22日 14:06

什么是服务治理?RPC 框架中的服务治理功能有哪些?如何实现?

服务治理是微服务架构中的核心功能,确保服务的稳定运行和高效管理:核心服务治理功能:1. 服务注册与发现功能:服务实例自动注册和发现实现:Zookeeper、Nacos、Consul、Eureka关键点:健康检查:定期检测服务实例健康状态服务剔除:自动移除不健康的实例动态更新:服务列表实时更新配置示例: // Dubbo 服务注册 <dubbo:registry address="zookeeper://127.0.0.1:2181"/> // Spring Cloud 服务发现 @EnableDiscoveryClient2. 负载均衡功能:将请求分发到多个服务实例算法:随机(Random)轮询(Round Robin)最少连接(Least Connections)一致性哈希(Consistent Hash)配置示例: // Dubbo 负载均衡 <dubbo:reference loadbalance="random"/> // Spring Cloud 负载均衡 @LoadBalanced RestTemplate restTemplate;3. 服务容错功能:处理服务调用失败的情况策略:Failover:失败自动切换,重试其他实例Failfast:快速失败,只发起一次调用Failsafe:失败安全,出现异常时忽略Failback:失败自动恢复,后台记录失败请求Forking:并行调用,只要一个成功即返回Broadcast:广播调用,所有调用都成功才算成功配置示例: // Dubbo 容错策略 <dubbo:reference cluster="failover" retries="2"/> // Hystrix 熔断 @HystrixCommand(fallbackMethod = "fallback") public User getUser(Long id) { return userService.getUser(id); }4. 服务降级功能:服务不可用时提供备用方案策略:返回默认值返回缓存数据调用备用服务返回友好错误提示实现示例: @HystrixCommand(fallbackMethod = "getUserFallback") public User getUser(Long id) { return userService.getUser(id); } public User getUserFallback(Long id) { return new User(id, "默认用户"); }5. 服务限流功能:保护服务不被过载算法:令牌桶(Token Bucket)漏桶(Leaky Bucket)固定窗口(Fixed Window)滑动窗口(Sliding Window)实现示例: // Sentinel 限流 @SentinelResource(value = "getUser", blockHandler = "handleBlock") public User getUser(Long id) { return userService.getUser(id); } public User handleBlock(Long id, BlockException ex) { return new User(id, "限流"); } // Guava RateLimiter RateLimiter rateLimiter = RateLimiter.create(100); if (rateLimiter.tryAcquire()) { // 处理请求 }6. 服务熔断功能:当故障率达到阈值时,快速失败,避免雪崩状态:关闭(Closed):正常状态开启(Open):熔断状态,快速失败半开启(Half-Open):尝试恢复状态实现示例: // Hystrix 熔断配置 @HystrixCommand( commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000") } ) public User getUser(Long id) { return userService.getUser(id); }7. 服务路由功能:根据规则将请求路由到特定服务实例策略:条件路由:根据参数条件路由标签路由:根据服务标签路由脚本路由:使用脚本定义路由规则配置示例: // Dubbo 条件路由 <dubbo:router> <dubbo:condition-router rule="host = 192.168.1.1 => provider = 1.0.0"/> </dubbo:router> // Spring Cloud 路由 @RequestMapping("/api/user/**") public String userService() { return "forward:/user-service/api/user/**"; }8. 服务监控功能:监控服务运行状态和性能指标指标:QPS(每秒查询数)TPS(每秒事务数)响应时间(RT)成功率错误率工具:Prometheus + GrafanaSkyWalkingZipkinELK Stack实现示例: // Micrometer 指标收集 @Autowired private MeterRegistry meterRegistry; public User getUser(Long id) { Timer.Sample sample = Timer.start(meterRegistry); try { User user = userService.getUser(id); sample.stop(meterRegistry.timer("user.get", "status", "success")); return user; } catch (Exception e) { sample.stop(meterRegistry.timer("user.get", "status", "error")); throw e; } }9. 服务配置管理功能:集中管理服务配置特性:动态配置更新配置版本管理配置推送配置回滚工具:Nacos ConfigSpring Cloud ConfigApollo配置示例: // Nacos 配置 @Value("${user.service.timeout}") private int timeout; @NacosValue(value = "${user.service.timeout}", autoRefreshed = true) private int dynamicTimeout;10. 服务灰度发布功能:逐步发布新版本服务策略:按比例流量分配按用户标签路由按地域路由实现示例: // 灰度发布配置 @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } // 使用标签路由 @FeignClient(name = "user-service", qualifiers = "v2") public interface UserServiceV2 { // ... }服务治理最佳实践:1. 分层治理基础层:服务注册、发现、负载均衡控制层:限流、熔断、降级监控层:监控、告警、日志配置层:配置管理、灰度发布2. 渐进式实施先实现基础功能逐步添加高级功能持续优化和调整3. 监控和告警完善的监控指标及时的告警机制定期的性能分析4. 容灾演练定期进行故障演练验证容错机制优化应急响应流程
阅读 0·2月22日 14:06

什么是服务注册与发现?主流的注册中心有哪些?它们各有什么特点?

服务注册与发现是微服务架构中的核心组件,解决了服务实例动态管理的问题:核心概念:1. 服务注册(Service Registration)服务启动时向注册中心注册自己的信息注册信息包括:服务名、IP、端口、元数据等定期发送心跳保持注册状态2. 服务发现(Service Discovery)客户端从注册中心获取服务实例列表支持动态更新服务列表实现负载均衡和故障转移3. 健康检查(Health Check)定期检测服务实例健康状态自动剔除不健康的实例支持主动和被动检查主流注册中心对比:1. Zookeeper特点:基于 ZAB 协议的分布式协调服务优势:成熟稳定,社区活跃强一致性(CP)支持临时节点和持久节点劣势:性能相对较低运维复杂不支持 HTTP 接口适用场景:对一致性要求高的场景2. Eureka特点:Netflix 开发的服务发现组件优势:简单易用,与 Spring Cloud 集成好支持自我保护机制高可用性(AP)劣势:已停止维护(2.x 版本)一致性较弱不支持跨数据中心适用场景:Spring Cloud 微服务架构3. Consul特点:HashiCorp 开发的服务网格工具优势:功能全面(服务发现、健康检查、KV 存储)支持 HTTP 和 DNS 接口支持多数据中心支持服务网格劣势:学习曲线较陡资源占用相对较高适用场景:需要多功能集成的场景4. Nacos特点:阿里巴巴开源的服务发现和配置管理平台优势:功能全面(服务发现、配置管理、DNS)支持 AP 和 CP 模式切换与 Spring Cloud、Dubbo 集成好支持动态配置推送中文文档完善劣势:相对较新,生态不如 Consul 成熟适用场景:国内微服务架构,特别是使用 Spring Cloud Alibaba5. Etcd特点:CoreOS 开发的分布式键值存储优势:基于 Raft 协议,强一致性性能优秀支持 gRPC 接口Kubernetes 的核心组件劣势:功能相对单一运维复杂度较高适用场景:Kubernetes 环境,对一致性要求高的场景服务发现模式:1. 客户端发现模式客户端从注册中心获取服务列表客户端自行选择服务实例优点:减少注册中心压力,响应快缺点:客户端逻辑复杂代表:Eureka、Consul2. 服务端发现模式客户端通过负载均衡器调用服务负载均衡器从注册中心获取服务列表优点:客户端逻辑简单缺点:增加一层代理,可能影响性能代表:Kubernetes Service、Nginx实现示例(Nacos):服务提供者:@SpringBootApplication@EnableDiscoveryClientpublic class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); }}// 配置spring: application: name: user-service cloud: nacos: discovery: server-addr: localhost:8848服务消费者:@RestControllerpublic class ConsumerController { @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/call") public String callService() { ServiceInstance instance = loadBalancerClient.choose("user-service"); String url = String.format("http://%s:%s/api/user", instance.getHost(), instance.getPort()); return restTemplate.getForObject(url, String.class); }}选择建议:Spring Cloud 生态:优先选择 Nacos 或 EurekaKubernetes 环境:使用 Etcd 或 CoreDNS需要多功能集成:选择 Consul对一致性要求高:选择 Zookeeper 或 Etcd国内项目:优先选择 Nacos
阅读 0·2月22日 14:06

如何在 React 中使用 MobX?

在 React 中使用 MobX 需要将 MobX 的响应式状态与 React 的渲染机制连接起来。MobX 提供了多种方式来实现这种集成。安装依赖npm install mobx mobx-react-lite# 或者npm install mobx mobx-react使用方式1. 使用 observer 高阶组件(mobx-react)import React from 'react';import { observer } from 'mobx-react';import { makeAutoObservable } from 'mobx';class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; }}const todoStore = new TodoStore();// 使用 observer 包装组件const TodoList = observer(() => { return ( <div> <h1>Todo List</h1> <ul> {todoStore.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => todoStore.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => todoStore.addTodo('New Todo')}> Add Todo </button> </div> );});export default TodoList;2. 使用 useObserver Hook(mobx-react-lite)import React from 'react';import { useObserver } from 'mobx-react-lite';import { makeAutoObservable } from 'mobx';class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; }}const todoStore = new TodoStore();function TodoList() { return useObserver(() => ( <div> <h1>Todo List</h1> <ul> {todoStore.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => todoStore.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => todoStore.addTodo('New Todo')}> Add Todo </button> </div> ));}export default TodoList;3. 使用 useLocalObservable Hook(mobx-react-lite)import React from 'react';import { observer, useLocalObservable } from 'mobx-react-lite';function TodoList() { const store = useLocalObservable(() => ({ todos: [], addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); }, toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } })); return ( <div> <h1>Todo List</h1> <ul> {store.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => store.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => store.addTodo('New Todo')}> Add Todo </button> </div> );}export default observer(TodoList);4. 使用 React Context 共享 Storeimport React, { createContext, useContext } from 'react';import { observer } from 'mobx-react';import { makeAutoObservable } from 'mobx';class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; }}const TodoContext = createContext(null);function TodoProvider({ children }) { const store = new TodoStore(); return ( <TodoContext.Provider value={store}> {children} </TodoContext.Provider> );}function useTodoStore() { const store = useContext(TodoContext); if (!store) { throw new Error('useTodoStore must be used within TodoProvider'); } return store;}const TodoList = observer(() => { const store = useTodoStore(); return ( <div> <h1>Todo List</h1> <ul> {store.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => store.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => store.addTodo('New Todo')}> Add Todo </button> </div> );});function App() { return ( <TodoProvider> <TodoList /> </TodoProvider> );}export default App;5. 使用 Provider 和 inject(mobx-react 旧版)import React from 'react';import { Provider, observer, inject } from 'mobx-react';import { makeAutoObservable } from 'mobx';class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; }}const todoStore = new TodoStore();@inject('todoStore')@observerclass TodoList extends React.Component { render() { const { todoStore } = this.props; return ( <div> <h1>Todo List</h1> <ul> {todoStore.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => todoStore.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => todoStore.addTodo('New Todo')}> Add Todo </button> </div> ); }}function App() { return ( <Provider todoStore={todoStore}> <TodoList /> </Provider> );}export default App;最佳实践1. 使用 observer 包裹需要响应式更新的组件// ✅ 正确:只包裹需要响应式更新的组件const TodoItem = observer(({ todo }) => ( <li> <input type="checkbox" checked={todo.completed} onChange={() => todo.toggle()} /> <span>{todo.text}</span> </li>));const TodoList = ({ store }) => ( <ul> {store.todos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))} </ul>);// ❌ 错误:包裹整个应用,可能导致不必要的渲染const App = observer(() => ( <div> <Header /> <TodoList /> <Footer /> </div>));2. 合理拆分 Store// ✅ 正确:按功能拆分 Storeclass TodoStore { @observable todos = []; @observable filter = 'all'; @action addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); }}class UserStore { @observable user = null; @action setUser(user) { this.user = user; }}class AppStore { todoStore = new TodoStore(); userStore = new UserStore();}// 使用 Context 共享const StoreContext = createContext(new AppStore());3. 使用 computed 优化性能class TodoStore { @observable todos = []; @observable filter = 'all'; @computed get filteredTodos() { switch (this.filter) { case 'completed': return this.todos.filter(todo => todo.completed); case 'active': return this.todos.filter(todo => !todo.completed); default: return this.todos; } }}const TodoList = observer(({ store }) => ( <ul> {store.filteredTodos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))} </ul>));4. 使用 React.memo 优化子组件const TodoItem = observer(React.memo(({ todo }) => ( <li> <input type="checkbox" checked={todo.completed} onChange={() => todo.toggle()} /> <span>{todo.text}</span> </li>)));5. 使用 useEffect 清理副作用import { useEffect } from 'react';import { reaction } from 'mobx';const TodoList = observer(({ store }) => { useEffect(() => { const disposer = reaction( () => store.todos.length, (length) => { console.log('Todo count changed:', length); } ); return () => disposer(); }, [store]); return ( <ul> {store.todos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))} </ul> );});常见问题1. 组件不更新// ❌ 错误:忘记使用 observerfunction TodoList({ store }) { return ( <ul> {store.todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> );}// ✅ 正确:使用 observerconst TodoList = observer(({ store }) => ( <ul> {store.todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul>));2. 在 observer 组件外修改状态// ❌ 错误:在 observer 组件外直接修改状态function App() { const store = useStore(); useEffect(() => { store.todos.push({ id: 1, text: 'Todo' }); // 不在 action 中 }, []); return <TodoList store={store} />;}// ✅ 正确:在 action 中修改状态function App() { const store = useStore(); useEffect(() => { store.addTodo('Todo'); // 在 action 中 }, []); return <TodoList store={store} />;}3. 过度使用 observer// ❌ 错误:过度使用 observerconst Header = observer(() => <header>Header</header>);const Footer = observer(() => <footer>Footer</footer>);const Main = observer(() => <main>Main</main>);// ✅ 正确:只在需要响应式更新的组件使用 observerconst Header = () => <header>Header</header>;const Footer = () => <footer>Footer</footer>;const Main = observer(() => <main>Main</main>);总结使用 observer 或 useObserver 将组件连接到 MobX使用 React Context 共享 Store合理拆分 Store,按功能模块组织使用 computed 优化性能使用 React.memo 优化子组件记得在 useEffect 中清理 reaction避免过度使用 observer始终在 action 中修改状态
阅读 0·2月22日 14:05

RPC 框架中常见的序列化协议有哪些?它们各有什么优缺点?

序列化是 RPC 框架中的核心组件,直接影响性能和效率。常见的序列化协议各有特点:1. Protobuf(Protocol Buffers)特点:Google 开发,二进制格式,高效紧凑优势:序列化/反序列化速度快数据体积小,传输效率高支持多语言(Java、Python、Go、C++等)向后兼容性好定义清晰的数据结构(.proto 文件)劣势:可读性差,需要 .proto 文件不支持动态类型适用场景:高性能要求的微服务通信2. Thrift特点:Facebook 开发,支持多种协议和传输方式优势:支持多种序列化格式(Binary、JSON、Compact)支持多种传输协议(TCP、HTTP、Memory)代码生成功能强大支持异步和同步调用劣势:学习曲线较陡文档相对较少适用场景:跨语言、多协议的复杂场景3. JSON特点:文本格式,易读易写优势:人类可读,调试方便通用性强,所有语言都支持灵活,支持动态类型浏览器原生支持劣势:数据体积大,传输效率低序列化/反序列化速度慢类型安全性差适用场景:对外 API、Web 应用4. Avro特点:Apache 项目,支持模式演化优势:支持动态模式,无需代码生成模式演化能力强压缩率高适合大数据场景劣势:学习成本较高相对小众适用场景:大数据处理、日志收集5. MessagePack特点:二进制 JSON,高效紧凑优势:比 JSON 更小更快保持 JSON 的数据类型支持多种语言劣势:可读性不如 JSON生态系统相对较小适用场景:需要 JSON 兼容性但要求更高性能的场景6. Hessian特点:二进制序列化,动态类型优势:序列化速度快支持动态类型跨语言支持劣势:数据体积相对较大社区活跃度不高适用场景:Java 生态的 RPC 调用性能对比(大致排序):序列化速度:Protobuf > Hessian > Thrift > MessagePack > Avro > JSON数据体积:Protobuf > MessagePack > Thrift > Hessian > Avro > JSON可读性:JSON > Avro > MessagePack > Thrift > Protobuf > Hessian选择建议:高性能内部服务:Protobuf、Thrift对外 API:JSON大数据场景:Avro需要 JSON 兼容性:MessagePackJava 生态:Hessian
阅读 0·2月22日 14:05