Ark service communication
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.
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
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>interfaceis the interface that needs to be published as a service.refpoint 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
interfaceis the interface of the service and must be the same as theinterfaceconfigured when the service is published.The meaning of the
idproperty 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.
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
@SofaServiceis 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@SofaServiceas a Spring Bean in advance.If a service has been
@SofaServiceannotated, you can no longer publish the service in XML.When the class being
@SofaServiceannotated has only one interface, the framework will directly use this interface as the interface of the service, and when the class being@SofaServiceannotated implements multiple interfaces, you can set theinterfaceTypefield of the@SofaServiceto specify the service interface, for example:@SofaService(interfaceType=SampleInterface.class) public class SampleImpl implements SampleInterface,Serializable{ public void test(){ } }When using
@SofaServiceannotations to publish services, you need to put@SofaServiceannotations on the implementation class. When Spring Boot uses the Bean Method to create beans, the@Beanand@SofaServiceare 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@SofaServiceworks 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@SofaReferencebecause the@SofaReferenceuses 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 theinterfaceTypeproperty 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),
@SofaReferenceannotations can be used to reference JVM services in bean method parameters to facilitate the use of annotations@SofaReferencereference 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:
The
ServiceClientobject is obtained byclientFactory.Construct a
ServiceParamobject.The
ServiceParamobject contains the parameters required to publish the service, the object to be published as a JVM service is set by thesetInstancemethod, and the interface of the service is set by thesetInterfaceType.Call the
ServiceClientservicemethod 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:
Gets a
ReferenceClientfrom theClientFactory.Similar to publishing a service, construct a
ReferenceParamand then set up the interface of the service.Call the
ReferenceClientreferencemethod.
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>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();
}Package the plug-in.
Because direct calls are used, interface class
SampleServiceand 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)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 = falsewhen you publish the service.Annotations specify
@SofaService(bindings ={@SofaServiceBinding(serialize =false)})XML to specify
<sofa:binding.jvmserialize="true"/>