# open-remote **Repository Path**: zhanglongn/open-remote ## Basic Information - **Project Name**: open-remote - **Description**: saas平台代理适配接口,基于私有化代理请求 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-07-11 - **Last Updated**: 2022-07-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #**Remote Client使用手册** ##**设计背景** 由来税务云申报等其他模块相关变更导致开放能力接口升级的影响不可控制,导致接口升级兼容性复杂度较高,带来升级的风险较高,升级难度大。 实践验证,网络接口调用细节实现隐藏带来的好处远远大于相关维护带来的复杂度,由此经过如上大佬的交流、指导、调研,生成了Remote Client 的基本模型实现的第一阶段,基于刚刚开始,可能产生的问题还会有很多,包括优化调整的因素还不完善,希望大家参与指正 ##**设计目标** 1. 简化程序函数调用(实现能力接口的抽象,隐藏实现细节) 2. 开放接口标准化(设计思想与openfeign相同) 3. 基础支撑能力组件化、高扩展、可插拔、简易性 4. 多环境测试复杂度可控、测试范围可知、测试质量提高 ##**阅读人员** 技术相关 ##**应用环境** SpringBoot项目 ##**应用环境** 应用实践中需要抽象封装,隐藏细节实现,包括不限于仅仅用于能力开放接口网络调用的方案都是可以应用于此,进行设计整合。 ##**时序图** ![时序图](https://gitee.com/zhanglongn/open-remote/blob/master/doc/img/shixutu.png "在这里输入图片标题") ##**接入说明** 1. **定义remote client接口** ``` /** * @author ZonLen since on 2021/7/24 上午12:39 */ @RemoteClient(address = "${tax.cloud.declare.address}", serverContextPath = "${tax.cloud.declare.context-path}") public interface TaxDeclareRemoteClient extends RemoteApi { @PostMapping(value = "/api/declare/tax-declare") ICommonResponse invokeTaxDeclareApply(@RequestBody DeclareApplyOrderReq req); ......接口定义 } ``` 注释:该接口必须实现 RemoteApi接口(这是标准规范) 2. **实现定义接口** ``` @Slf4j @RestController public class DeclareApiProvider implements TaxDeclareRemoteClient { @Autowired private IYunzfService yunzfService; @Autowired private TaxDeclareService taxDeclareService; @Autowired private DeclareApplyFactorySelector declareApplyFactorySelector; @Override public ICommonResponse invokeTaxDeclareApply(@RequestBody DeclareApplyOrderReq req) { log.info("远程调用申报接口入参:{}", JSON.toJSONString(req)); final EReportChannelEnums channelType = EReportChannelEnums .getChannelTypeByVal(req.getChannel()); final ReportTypeEnum sbType = ReportTypeEnum.getReportTypeByDeclareCode(req.getSbType()); declareApplyFactorySelector.selectTaxDeclareApplyStrategy(channelType, sbType) .doRemoteInvoke(Mapper.map(req, DeclareReportDataMapping.class)); return CommonResponseEnum.SUCCESS; } ......接口实现 } ``` 注释:使用方式参照@FeginClient注解(要注意如下事项) 1. **自定义代理条件** ``` /** * @author ZonLen since on 2021/7/24 上午12:39 */ @Component public class TaxDeclareCondition implements ProxyCondition { private final EnvironmentProperties environmentProperties; public TaxDeclareCondition( EnvironmentProperties environmentProperties) { this.environmentProperties = environmentProperties; } @Override public boolean matches() { return environmentProperties.isPrivateDeploy(); } } ``` 注释:这事一个spring的bean,实现mathes方法,来决定是否进行动态代理还是静态代理(默认是开启动态代理)策略是优先获取注解配置的代理条件-->其次全局的代理条件-->默认的 4. **配置** remote.client.enable = true #开启remote client组件 remote.client.scanner.packages= com.xxx #配置remote client包扫描(默认启动类路径下,不配置的话单元测试启动类路径要与应用启动类路径一致) @RemoteClient注解中相关的配置(可以直接写入不用配置)如下是配置的方式 ``` @RemoteClient(address = "${tax.cloud.declare.address}", serverContextPath = "${tax.cloud.declare.context-path}") public interface TaxDeclareRemoteClient extends RemoteApi { ...... } ``` 5. **应用** ``` public abstract class AbstractTaxDeclareStrategy implements TaxDeclareStrategy { @Autowired private TaxDeclareRemoteClient taxDeclareRemoteClient; @Override public void doDeclareApply( DeclareReportDataMapping dataMapping) { //1.初始化申报申请调用对象 final List taxDeclareApplyProcessors = taxDeclareApplyProcessors(); //2.处理器设置优先级进行排序,处理器前置处理 taxDeclareApplyProcessors.sort(Comparator.comparing(TaxDeclareApplyProcessor::getOrder)); for (TaxDeclareApplyProcessor taxDeclareApplyProcessor : taxDeclareApplyProcessors) { if (taxDeclareApplyProcessor.isMatch(dataMapping)) { //处理器前置处理 taxDeclareApplyProcessor.beforePostProcessor(dataMapping); } } //3.申报调用 final ICommonResponse response = taxDeclareRemoteClient .invokeTaxDeclareApply(Mapper.map(dataMapping, DeclareApplyOrderReq.class)); if (response.isFailed()){ throw new BusinessRuntimeException(response.getCode(), response.getMsg()); } ``` 注释:直观代码,流程逻辑跟面向接口静态代理保持一致;如上直接注入TaxDeclareRemoteClient代理在第三步进行申报调用逻辑 ## **注意项** 1. 如果需要单元测试需要配置扫描包路径(默认扫描启动类包路径)remote.client.scanner.packages=com.xxx 多个用逗号分隔 2. 需要实现一个ProxyCondition 用作代理开启条件,如果返回ture则使用动态代理,如果返回false使用静态代理 3. 添加需要实现一个RemoteApi接口并添加注解@RemoteClient配置address地址和context-path 4. 必须使用@RemoteClient注解的接口在其他业务块进行依赖注入(因为就是代理的这个接口) 5. 使用标准的restful请求,不支持参数别名方式 6. 不支持自定义类加载器进行类加载相关代理类,统一spring的类加载器 7. @RemoteClient和@FeginClient两个注解不兼容,如果既有rpc的需求又有开放接口的需求,向上抽取公共接口,向下实现两个接口进行职责分离 ##**可动态配置项** 1. 配置性地址 2. 配置性代理方式 默认动态代理 3. 自定义resttemplate拦截器 4. 自定义请求签名处理器 默认openqpi加签方式 ##**优缺点** **缺点:**需要单独维护相关接口请求参数类(做抉择:模块依赖关系作用域的原因导致,除非对业务不做充血模型《前提业务特别简单》,但是为保证职责单一强烈推荐前者) **优点:**基于接口适配,相当于代理了中间复杂逻辑并隐藏了实现细节(开发过程无需要关注) 开发调试层面:面向接口平滑逻辑调试,隐藏动态代理细节相关复杂性(类似于openfigen隐藏了注册中心注册列表相关接口信息获取调用的过程) 开放接口管理:接口定义标准化,接口版本升级兼容性可控成本降低