The target

  • Integrate using SOFA plug-in
  • Soul gateway complete SOFA call source analysis
  • conclusion

## Gateway integrates SOFA plugin

  • pom
< the dependency > < groupId > com. Alipay. Sofa < / groupId > < artifactId > sofa xml-rpc -all < / artifactId > < version > 5.7.6 < / version > </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-client</artifactId> < version > 4.0.1 < / version > < / dependency > < the dependency > < groupId > org. Apache. The curator < / groupId > < artifactId > curator - framework < / artifactId > < version > 4.0.1 < / version > < / dependency > < the dependency > < the groupId > org. Apache. Curator < / groupId > < artifactId > curator - recipes < / artifactId > < version > 4.0.1 < / version > < / dependency > <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-sofa</artifactId> <version>${soul-version}</version> </dependency>Copy the code

Business services access the Soul gateway

  • pom
	   <dependency>
           <groupId>org.dromara</groupId>
           <artifactId>soul-spring-boot-starter-client-sofa</artifactId>
           <version>${soul-version}</version>
       </dependency>
Copy the code
  • application.yml
soul:
  sofa:
    adminUrl: http://localhost:9095
    contextPath: /sofa
    appName: sofa
Copy the code
  • The interface is registered with the gateway

    • The @soulsofaclient annotation on the method of your SOFA service implementation class indicates that the interface method is registered with the gateway.
    • Start your provider and export logssofa client register successDone, your SOFA interface has been published to the Soul Gateway. If there are still do not understand, you can refer tosoul-test-sofaThe project.
@Service("sofaTestService") public class SofaTestServiceImpl implements DubboTestService { @Override @SoulSofaClient(path = "/findById", desc = "Find by Id") public DubboTest findById(final String id) { DubboTest dubboTest = new DubboTest(); dubboTest.setId(id); dubboTest.setName("hello world Soul Sofa, findById"); return dubboTest; } @Override @SoulSofaClient(path = "/findAll", desc = "Get all data") public DubboTest findAll() { DubboTest dubboTest = new DubboTest(); dubboTest.setName("hello world Soul Sofa , findAll"); dubboTest.setId(String.valueOf(new Random().nextInt())); return dubboTest; } @Override @SoulSofaClient(path = "/insert", desc = "Insert a row of data") public DubboTest insert(final DubboTest dubboTest) { dubboTest.setName("hello world Soul Sofa: " + dubboTest.getName()); return dubboTest; }}Copy the code

  • Soul-admin Interface registration result

Soul-admin supports sofa plug-in

  • First of all insoul-adminPlug-in management, thesofaThe plug-in is enabled.
  • Second insofaConfigure your registry address or the address of another registry in the plugin.

Soul gateway complete SOFA call source analysis

  • SofaPluginConfiguration
@configuration @conditionalonclass (sofaplugin.class) public class SofaPluginConfiguration {/** * sofa plugin initialization */ @bean public SoulPlugin sofaPlugin(final ObjectProvider<SofaParamResolveService> sofaParamResolveService) { // Initialization SofaProxyService sofa service broker return new SofaPlugin (new SofaProxyService (sofaParamResolveService. GetIfAvailable ())); } @bean public SoulPlugin bodyParamPlugin() {return new bodyParamPlugin(); } @bean public SoulPlugin sofaResponsePlugin() {return new sofaResponsePlugin(); } /** * Sofa data processor plug-in initialization, */ @bean public PluginDataHandler() {return new sofaPluginDataHandler(); } /** * sofa metadata change subscription initialization */ @bean public MetaDataSubscriber sofaMetaDataSubscriber() {return new SofaMetaDataSubscriber(); }}Copy the code
  • SofaPlugin

The following is the core code for SOFA request processing

@Override protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, Final RuleData rule) {// Retrieve data from request parameters String body = exchange.getAttribute(Constants.SOFA_PARAMS); // Get soul gateway CONTEXT SoulContext SoulContext = exchange.getAttribute(Constants.CONTEXT); assert soulContext ! = null; MetaData = exchange.getAttribute(Constants.META_DATA); If (! checkMetaData(metaData)) { assert metaData ! = null; log.error(" path is :{}, meta data have error.... {}", soulContext.getPath(), metaData.toString()); exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); Object error = SoulResultWrap.error(SoulResultEnum.META_DATA_ERROR.getCode(), SoulResultEnum.META_DATA_ERROR.getMsg(), null); return WebFluxResultUtils.result(exchange, error); } if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(body)) { exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); Object error = SoulResultWrap.error(SoulResultEnum.SOFA_HAVE_BODY_PARAM.getCode(), SoulResultEnum.SOFA_HAVE_BODY_PARAM.getMsg(), null); return WebFluxResultUtils.result(exchange, error); } / / by proxy call sofa interface final Mono < Object > result = sofaProxyService. GenericInvoker (body, metaData exchange); return result.then(chain.execute(exchange)); }Copy the code
  • SofaProxyService

The following is the core processing of sofa remote call. It can be seen that similar to Dubbo, it is also a generalization call

public Mono<Object> genericInvoker(final String body, final MetaData metaData, Final ServerWebExchange Exchange) throws SoulException {// Obtain the generalization proxy reference ConsumerConfig<GenericService> reference = ApplicationConfigCache.getInstance().get(metaData.getPath()); if (Objects.isNull(reference) || StringUtils.isEmpty(reference.getInterfaceId())) { ApplicationConfigCache.getInstance().invalidate(metaData.getServiceName()); reference = ApplicationConfigCache.getInstance().initRef(metaData); } GenericService genericService = reference.refer(); Pair<String[], Object[]> pair; if (null == body || "".equals(body) || "{}".equals(body) || "null".equals(body)) { pair = new ImmutablePair<>(new String[]{}, new Object[]{}); } else { pair = sofaParamResolveService.buildParameter(body, metaData.getParameterTypes()); } CompletableFuture<Object> future = new CompletableFuture<>(); / / response callback RpcInvokeContext getContext () setResponseCallback (new SofaResponseCallback < Object > () {@ Override public void onAppResponse(final Object o, final String s, final RequestBase requestBase) { future.complete(o); } @Override public void onAppException(final Throwable throwable, final String s, final RequestBase requestBase) { future.completeExceptionally(throwable); } @Override public void onSofaException(final SofaRpcException e, final String s, final RequestBase requestBase) { future.completeExceptionally(e); }}); // GenericService. $invoke(metadata.getMethodName (), psion.getLeft (), psion.getright ())); // According to the callback, Return mono.fromFuture (future.thenapply (ret -> {if (objects.isnull (ret)) {ret = Constants.SOFA_RPC_RESULT_EMPTY; } exchange.getAttributes().put(Constants.SOFA_RPC_RESULT, ret); exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName()); return ret; })).onErrorMap(SoulException::new); }Copy the code
  • SofaResponsePlugin

The following is the core code for handling sofa call response data

@Override public Mono<Void> execute(final ServerWebExchange exchange, Final SoulPluginChain chain) {return chain.execute(exchange).then(mono.defer (() -> {final Object result = exchange.getAttribute(Constants.SOFA_RPC_RESULT); if (Objects.isNull(result)) { Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null); return WebFluxResultUtils.result(exchange, error); } Object success = SoulResultWrap.success(SoulResultEnum.SUCCESS.getCode(), SoulResultEnum.SUCCESS.getMsg(), JsonUtils.removeClass(result)); / / handle return data return WebFluxResultUtils. Result (exchange, success); })); }Copy the code
  • review

    At this point, the soul gateway support SOFA call source code processing process is probably completed analysis, specific processing details need to be further improved

conclusion

The soul-admin sofa plugin should be enabled and the soul-admin SOFA plugin should be configured with a registry. The soul-admin sofa plugin should be enabled and the soul-admin SOFA plugin should be configured. In the stage of source code analysis, it can be seen that the sofa processing process follows the responsibility chain mode to deal with the plug-in chain, which is similar to Dubbo in RPC call.