当前位置: 首页 > wzjs >正文

兰州网站seo按天计费重庆公司招聘

兰州网站seo按天计费,重庆公司招聘,兰州医院网站建设,南昌做网站哪个好一、为何需要统一封装? 在讨论统一封装之前,我们先看看 REST 和 RPC 各自的适用场景。 REST API 基于 HTTP 协议,采用 JSON 作为数据交换格式,可读性好且跨语言,非常适合对外提供服务。 RPC(如 Dubbo、gRPC…

一、为何需要统一封装?

在讨论统一封装之前,我们先看看 REST 和 RPC 各自的适用场景。

REST API 基于 HTTP 协议,采用 JSON 作为数据交换格式,可读性好且跨语言,非常适合对外提供服务。

RPC(如 Dubbo、gRPC)采用二进制协议(如 Protobuf),序列化效率高、网络开销小,适合内部微服务间的高频调用。

实际项目中,不同服务可能提供了不同的通信协议,带来服务间调用方式的不一致,带来编码及后续维护的复杂度。

二、设计思路:基于外观模式的统一调用层

解决这个问题的关键是引入 外观模式(Facade Pattern) ,通过一个统一的外观类封装所有调用细节。

同时结合适配器模式和策略模式,实现不同协议的无缝切换。

2.1 核心设计

整个设计分为三层:

统一接口层:定义通用调用接口,屏蔽底层差异
协议适配层:实现 REST 和 RPC 的具体调用逻辑
业务逻辑层:业务服务实现,完全不用关心调用方式

2.2 关键设计模式

外观模式:提供统一入口 UnifiedServiceClient,封装所有调用细节
适配器模式:将 RestTemplateDubboReference 适配为统一接口
策略模式:根据配置动态选择调用方式(REST 或 RPC)

三、实现步骤:从统一响应到协议适配

3.1 统一响应体设计

首先要解决的是返回格式不一致问题。我们定义了统一的响应体 ApiResponse

@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiResponse<T> implements Serializable {private String code;       // 状态码private String message;    // 消息提示private T data;            // 业务数据private long timestamp;    // 时间戳// 成功响应public static <T> ApiResponse<T> success(T data) {return ApiResponse.<T>builder().code("200").message("success").data(data).timestamp(System.currentTimeMillis()).build();}// 失败响应public static <T> ApiResponse<T> fail(String code, String message) {return ApiResponse.<T>builder().code(code).message(message).timestamp(System.currentTimeMillis()).build();}
}

对于 REST 接口,通过 @RestControllerAdvice 实现自动封装

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return true; // 对所有响应生效}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof ApiResponse) {return body; // 已封装的直接返回}return ApiResponse.success(body); // 未封装的自动包装}
}

3.2 统一异常处理

异常处理同样需要统一。对于 REST 接口,使用 @ControllerAdvice

@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public ApiResponse<Void> handleBusinessException(BusinessException e) {return ApiResponse.fail(e.getCode(), e.getMessage());}@ExceptionHandler(Exception.class)public ApiResponse<Void> handleException(Exception e) {log.error("系统异常", e);return ApiResponse.fail("500", "系统内部错误");}
}

对于 Dubbo RPC,通过自定义过滤器实现异常转换:

package com.example.unified;import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.example.unified.exception.BusinessException;
import org.apache.dubbo.rpc.*;import java.util.function.BiConsumer;@Activate(group = Constants.PROVIDER)
public class DubboExceptionFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {try {AsyncRpcResult result = (AsyncRpcResult )invoker.invoke(invocation);if (result.hasException()) {Throwable exception = result.getException();if (exception instanceof BusinessException) {BusinessException e = (BusinessException) exception;return new AppResponse (ApiResponse.fail(e.getCode(), e.getMessage()));}}return result.whenCompleteWithContext(new BiConsumer<Result, Throwable>() {@Overridepublic void accept(Result result, Throwable throwable) {result.setValue(ApiResponse.success(result.getValue()));}});} catch (Exception e) {return new AppResponse (ApiResponse.fail("500", "RPC调用异常"));}}
}

3.3 协议适配层实现

定义统一调用接口 ServiceInvoker

package com.example.unified.invoker;import cn.hutool.core.lang.TypeReference;
import com.example.unified.ApiResponse;public interface ServiceInvoker {<T> ApiResponse<T> invoke(String serviceName, String method, Object param, TypeReference<ApiResponse<T>> resultType);
}

然后分别实现 REST 和 RPC 适配器:

REST 适配器

package com.example.unified.invoker;import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import com.example.unified.ApiResponse;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;@Component
public class RestServiceInvoker implements ServiceInvoker {private final RestTemplate restTemplate;private Environment environment;public RestServiceInvoker(RestTemplate restTemplate,Environment environment) {this.restTemplate = restTemplate;this.environment = environment;}@Overridepublic <T> ApiResponse<T> invoke(String serviceName, String method, Object param, TypeReference<ApiResponse<T>> resultType) {String serviceUrl = environment.getProperty("service.direct-url." + serviceName);String url = serviceUrl + "/" + method;HttpEntity request = new HttpEntity<>(param);String result = restTemplate.postForObject(url, request, String.class);return JSONUtil.toBean(result, resultType, true);}
}

Dubbo 适配器

package com.example.unified.invoker;import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.unified.ApiResponse;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.utils.SimpleReferenceCache;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;import java.util.Arrays;@Component
public class DubboServiceInvoker implements ServiceInvoker {private final SimpleReferenceCache referenceCache;private final Environment environment;public DubboServiceInvoker(SimpleReferenceCache referenceCache, Environment environment) {this.referenceCache = referenceCache;this.environment = environment;}@Overridepublic <T> ApiResponse<T> invoke(String serviceName, String method, Object param,TypeReference<ApiResponse<T>> resultType) {ReferenceConfig<GenericService> reference = new ReferenceConfig<>();String interfaceName = environment.getProperty("dubbo.reference." + serviceName + ".interfaceName");reference.setInterface(interfaceName);reference.setGeneric("true");reference.setRegistry(new RegistryConfig("N/A"));reference.setVersion("1.0.0");// 从配置文件读取直连地址(优先级:代码 > 配置文件)String directUrl = environment.getProperty("dubbo.reference." + serviceName + ".url");if (StrUtil.isNotEmpty(directUrl)) {reference.setUrl(directUrl);  // 设置直连地址,覆盖注册中心发现}GenericService service = referenceCache.get(reference);Object[] params = {param};Object result = service.$invoke(method, getParamTypes(params), params);JSONObject jsonObject = new JSONObject(result);ApiResponse<T> response = JSONUtil.toBean(jsonObject, resultType,true);return response;}private String[] getParamTypes(Object[] params) {return Arrays.stream(params).map(p -> p.getClass().getName()).toArray(String[]::new);}
}

3.4 外观类与策略选择

最后实现外观类 UnifiedServiceClient

package com.example.unified;import cn.hutool.core.lang.TypeReference;
import com.example.unified.config.ServiceConfig;
import com.example.unified.invoker.ServiceInvoker;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;@Component
public class UnifiedServiceClient {private final Map<String, ServiceInvoker> invokerMap;private final ServiceConfig serviceConfig;public UnifiedServiceClient(List<ServiceInvoker> invokers, ServiceConfig serviceConfig) {this.invokerMap = invokers.stream().collect(Collectors.toMap(invoker -> invoker.getClass().getSimpleName(), Function.identity()));this.serviceConfig = serviceConfig;}public <T> ApiResponse<T> call(String serviceName, String method, Object param, TypeReference<ApiResponse<T>> resultType) {// 根据配置选择调用方式String protocol = serviceConfig.getProtocol(serviceName);ServiceInvoker invoker = protocol.equals("rpc") ? invokerMap.get("DubboServiceInvoker") : invokerMap.get("RestServiceInvoker");return invoker.invoke(serviceName, method, param,resultType);}
}

服务调用方式通过配置文件指定:

service:direct-url: # 直连地址配置user-service: http://localhost:8080/user  # 订单服务REST地址config:user-service: rest    # 用户服务用rest调用order-service: rpc  # 订单服务用RPC调用# Dubbo 配置(若使用 Dubbo RPC)
dubbo:application:name: unified-client-demo  # 当前应用名
#    serialize-check-status: DISABLEqos-enable: falseregistry:address: N/Areference:# 为指定服务配置直连地址(无需注册中心)order-service:interfaceName: com.example.unified.service.OrderService  # 服务接口名称url: dubbo://192.168.17.1:20880  # 格式:dubbo://IP:端口protocol:name: dubbo    # RPC 协议名称port: 20880    # 端口

四、使用案例

package com.example.unified.controller;import cn.hutool.core.lang.TypeReference;
import com.example.unified.ApiResponse;
import com.example.unified.UnifiedServiceClient;
import com.example.unified.dto.OrderDTO;
import com.example.unified.dto.UserDTO;
import com.example.unified.service.OrderService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api")
public class DemoController {@Autowiredprivate UnifiedServiceClient serviceClient;@RequestMapping("/user")public ApiResponse<UserDTO> getUser(@RequestBody UserDTO qryUserDTO) {ApiResponse<UserDTO> response = serviceClient.call("user-service", "getUser", qryUserDTO, new TypeReference<ApiResponse<UserDTO>>() {});return response;}@RequestMapping("/order")public ApiResponse<OrderDTO> getOrder(@RequestBody OrderDTO qryOrderDTO) {ApiResponse<OrderDTO> response = serviceClient.call("order-service", "getOrder", qryOrderDTO, new TypeReference<ApiResponse<OrderDTO>>() {});String status = response.getData().getStatus();System.err.println("status:" + status);return response;}
}

六、总结

通过外观模式 + 适配器模式 + 策略模式的组合,实现了 REST API 与 RPC 调用的统一封装。

http://www.dtcms.com/wzjs/825271.html

相关文章:

  • 资源网站怎样做长春建筑工程网
  • 项目网站开发js放的位置网站建设龙采
  • 网站制作学校北京建设质量协会网站
  • dedecms 倒计时 天数 网站首页企业网站开发开题报告
  • 阿里云 网站深圳制作网站培训学校
  • 静态网站上下篇代码什么优化
  • 企业网站后台模板网站的充值是怎么做的
  • 北京网站制作公司兴田德润可以不南京网站开发培训
  • 筹划电子商务网站建设美工素材网站有哪些
  • wordprees可以做棋类网站吗网站如何防止被攻击
  • 门户型网站模板中卫网架配件哪家好
  • 邢台做网站哪家公司好网站宣传的重要性
  • 免费ftp网站寄生虫网站怎么做
  • 产品推广网站排名市场营销活动策划方案
  • 个人网站asp源码河北省建设工程
  • 百度网盘可以做网站吗?企业网站定制
  • 商丘网站建设想象力网络哈尔滨手机网站建设报价
  • 站群网站和做seo那个号合购吧登录WordPress
  • 怎么制作网站开发设计不知怎么入门
  • 尊园地产做的网站西乡塘区网站建设
  • 网站的功能包括哪些内容wordpress pjax
  • 网站 多语言处理建设一个网站的一般过程
  • 网页设计与网站建设从入门到精通五金网站制作
  • 贵州建筑网站谷歌广告联盟怎么做
  • 微信公众号网站开发注意搜索引擎优化专员
  • 深圳网站备全国网站建设大赛
  • 哪个网站用户体验较好上海建网站服务器
  • 自己做网站需要什么软件做水果代理去哪个网站
  • 浙江省职业能力建设处网站电影下载网站模板
  • 网站建设待遇怎样现在确诊阳性什么症状