Ark service communication

Updated at:

To solve the communication problem between Biz, SOFAArk introduces the SofaService/SofaReference programming interface provided by SOFABoot, and this article describes how to use it.

Note

If you want to solve the communication problem between Plugin and Biz, you can release and reference the plug-in service. For more information, see Ark service mechanism.

Introduce dependencies

Introduce runtime-sofa-boot-plugin dependencies:

<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>runtime-sofa-boot-plugin</artifactId>
    <version>${sofa.boot.version}</version>
</dependency>
  • If the application is developed based on Spring Boot 1.x, you recommend use V2.6.1.

  • If the application is developed based on Spring Boot 2.x, you recommend use V3.1.3.

Publish and reference JVM services

SOFAArk introduces the concept of SofaService/SofaReference JVM services provided by SOFABoot. SOFABoot provides the following three methods for developers to publish and reference JVM services:

  • XML mode

  • Annotation mode

  • Programming API mode

Note

For more information, see Publish and reference JVM services.

XML mode

Publish service

First you need to define a bean:

<bean id="sampleService" class="com.alipay.sofa.runtime.test.service.SampleServiceImpl">

Then, use the Spring extension tag provided by SOFA to publish the above bean as a SOFA JVM service.

<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService">
     <sofa:binding.jvm/>
</sofa:service>
  • interface is the interface that needs to be published as a service.

  • ref point to the bean that needs to be published as a JVM service.

Reference service

Use the Spring extension tag provided by SOFA to reference services:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleServiceRef">
    <sofa:binding.jvm/>
</sofa:service>
  • The interface is the interface of the service and must be the same as the interface configured when the service is published.

  • The meaning of the id property is the same as that of Spring BeanId.

The above configuration will generate a Spring bean with id sampleServiceRef. You can inject sampleServiceRef this bean into any place in the Spring context of the current SOFABoot module.

Note

service/reference tags also allow you to publish RPC services. For more information, see Publish SOFARPC services.

Annotation Method

In addition to publishing JVM services and references through XML, SOFABoot also provides annotations to publish and reference JVM services.

Publish service

To publish a JVM service by using annotations, you only need to add a @SofaService annotation to the implementation class. Example:

@SofaService
public class SampleImpl implements SampleInterface{
    public void test(){

    }
}

Precautions

  • The role of the @SofaService is to publish a Bean as a JVM service. Although you do not need to write <sofa:service/\> configuration, you still need to configure the class annotated by the @SofaService as a Spring Bean in advance.

  • If a service has been @SofaService annotated, you can no longer publish the service in XML.

  • When the class being @SofaService annotated has only one interface, the framework will directly use this interface as the interface of the service, and when the class being @SofaService annotated implements multiple interfaces, you can set the interfaceType field of the @SofaService to specify the service interface, for example:

    @SofaService(interfaceType=SampleInterface.class)
    public class SampleImpl implements SampleInterface,Serializable{
         public void test(){
    
         }
    }
  • When using @SofaService annotations to publish services, you need to put @SofaService annotations on the implementation class. When Spring Boot uses the Bean Method to create beans, the @Bean and @SofaService are scattered in two places, and it is impossible to use different uniqueids for the same implementation class. Therefore, since SOFABoot v3.1.0, the support @SofaService works on top of the bean method, for example:

    @Configuration
    public class SampleSofaServiceConfiguration{
       @Bean("sampleSofaService")
       @SofaService(uniqueId ="service1")
        SampleService service(){
           return new SampleServiceImpl("");
        }
    }

Reference service

Corresponding to @SofaService, SOFA provides @SofaReference to reference a JVM service. Suppose we need to use SampleJvmService JVM service in a Spring Bean, just add a @SofaReference annotation to the field:

public class SampleServiceRef{
    @SofaReference
     private SampleService sampleService;
}

Precautions

  • Similar to @SofaService, the service interface is not specified on the @SofaReference because the @SofaReference uses the type of the annotated field as the service interface when the service interface is not specified. You can also specify the interface by setting the interfaceType property of the @SofaReference:

    public class SampleServiceRef{
       @SofaReference(interfaceType=SampleService.class)
       private SampleService sampleService;
    }
  • Since SOFABoot v2.6.0 (open source version) and v3.1.0 (commercial version), @SofaReference annotations can be used to reference JVM services in bean method parameters to facilitate the use of annotations @SofaReference reference services in Spring Boot Bean Method. For example:

    @Configuration
    public class MultiSofaReferenceConfiguration{
         @Bean("sampleReference")
         TestService service(@Value("$spring.application.name")String appName,
         @SofaReference(uniqueId ="service")SampleService service){
                 return new TestService(service);
         }
    }

Programming API

SOFABoot provides a set of programming APIs for publishing and referencing JVM services, making it easy to publish and reference JVM services directly in code. Similar to Spring's ApplicationContextAware, if you use the programming API, you first need to implement the ClientFactoryAware interface to obtain the programming component API:

public class ClientFactoryBean implements ClientFactoryAware{
        private ClientFactory clientFactory;

        @Override
        public void setClientFactory(ClientFactory clientFactory){
            this.clientFactory = clientFactory;
        }
    }

Publish service

The following code shows how to use a SampleService to publish a JVM service through a programming API by using the clientFactory:

ServiceClient serviceClient = clientFactory.getClient(ServiceClient.class);

ServiceParam serviceParam =newServiceParam();
serviceParam.setInstance(newSampleServiceImpl());
serviceParam.setInterfaceType(SampleService.class);
serviceClient.service(serviceParam);

The process is as follows:

  1. The ServiceClient object is obtained by clientFactory.

  2. Construct a ServiceParam object.

    The ServiceParam object contains the parameters required to publish the service, the object to be published as a JVM service is set by the setInstance method, and the interface of the service is set by the setInterfaceType.

  3. Call the ServiceClient service method to publish a JVM service.

Reference service

The following code provides an example on how to use a programming API to reference a JVM service:

ReferenceClient referenceClient = clientFactory.getClient(ReferenceClient.class);

ReferenceParam<SampleService> referenceParam =new ReferenceParam<SampleService>();
referenceParam.setInterfaceType(SampleService.class);
SampleService proxy = referenceClient.reference(referenceParam);

The process is as follows:

  1. Gets a ReferenceClient from the ClientFactory.

  2. Similar to publishing a service, construct a ReferenceParam and then set up the interface of the service.

  3. Call the ReferenceClient reference method.

Important

The reference object created by using the dynamic client is a very important object. We recommend that you do not frequently create the reference object and cache the reference object. Otherwise, the memory may overflow.

In addition to implementing ClientFactoryAware interfaces for getting ServiceClient and ReferenceClient objects, you can also use simple annotation @SofaClientFactory to get programming APIs, such as:

public class ClientBean{
   @SofaClientFactory
   private ReferenceClient referenceClient;

   @SofaClientFactory
   private ServiceClient serviceClient;
}

Unique ID

Sometimes, for an interface, you may need to publish two services that correspond to different implementations. Taking the previous SampleService as an example, there may be two SampleService implementations, both of which need to be published as a JVM Service for SOFA. According to the above tutorial using XML, it is possible to configure it in the following way:

<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService1">
</sofa:service>
<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService "ref="sampleService2">
</sofa:service>

After the configuration is complete, there is no problem with service publishing, but when the service needs to be referenced, problems will occur. For example, in the following configuration, we cannot know which JVM service is referenced by this JVM reference:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleService">
</sofa:reference>

To solve the preceding problems, SOFABoot introduces the concept of Unique ID. You can use Unique ID to distinguish JVM services that have the same service interface. For example, in the code published by the service above, add the Unique ID:

<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService1" unique-id="ss1">
</sofa:service>
<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService2" unique-id="ss2">
</sofa:service>

Then, specify the Unique ID when referencing the service. For example, if you want to use the service of sampleService1, you can specify the unique-id as ss1:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleService" unique-id="ss1">
</sofa:reference>

To use the service of sampleService2, you can specify the unique-id as ss2:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleService" unique-id="ss2">
</sofa:reference>
Note

The above example shows the use of Unique ID in XML. When you publish JVM services and references in Annotation, you can set Unique ID by setting the Unique ID attribute of @SofaService and SofaReference. When you publish or reference JVM services by programming API, you can set Unique ID by ServiceParam and ReferenceParam setUniqueId methods.

Skip serialization

When using JVM service calls between Biz, because each Biz has a separate class loader, each JVM call will follow the Hessian serialization protocol. In some cases, to improve performance, you may not want to use serialization, but instead use direct calls. In this case, you need to do two additional steps. Take the following interface service as an example:

public interface SampleService{
     Result service();
}
  1. Package the plug-in.

    Because direct calls are used, interface class SampleService and their dependencies (such as parameters and return values) need to be sunk into Ark Plugins and exported in the plug-in configuration. The purpose of this is that when the interface is used in multiple Biz, the interface class is uniformly delegated to Plugin for loading. Otherwise, the following error is reported:

    java.lang.IllegalArgumentException:objectisnot an instance of declaring class
      at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:498)
      at com.alipay.sofa.runtime.integration.invoke.DynamicJvmServiceProxyFinder$DynamicJvmServiceInvoker.doInvoke(DynamicJvmServiceProxyFinder.java:164)
      at com.alipay.sofa.runtime.spi.service.ServiceProxy.invoke(ServiceProxy.java:39)
      at com.alipay.sofa.runtime.service.binding.JvmBindingAdapter$JvmServiceInvoker.doInvoke(JvmBindingAdapter.java:171)
      at com.alipay.sofa.runtime.spi.service.ServiceProxy.invoke(ServiceProxy.java:39)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
      at com.sun.proxy.$Proxy73.service(UnknownSource)
      at com.alipay.sofa.demo.service.SampleRestService.sampleService(SampleRestService.java:87)
  2. Publish the service.

    JVM services are published through annotations or XML, and are serialized when cross-Biz calls are made. If you want to skip this step, you must specify a serialize = false when you publish the service.

    • Annotations specify

      @SofaService(bindings ={@SofaServiceBinding(serialize =false)})
    • XML to specify

      <sofa:binding.jvmserialize="true"/>