In this document, we will create a Spring Boot project and introduce the basic dependencies of SOFABoot as well as its Health Check expansion capability, to demonstrate how to get started quickly with SOFABoot.
Environment Preparation
To use SOFABoot, we need to prepare the basic environment first. SOFABoot depends on the following environment: - JDK7 or JDK8 - Needs to be compiled with Apache Maven 3.2.5 or above
Create Project
SOFABoot is directly built on Spring Boot, so it can be generated by Spring Boot Generators. In this document, we need to add a web dependency for final view of its effect in the browser.
Add SOFABoot dependencies
When creating a Spring Boot project, we need to import SOFABoot dependencies. First, extract the ‘zip’ package of the project generated above and modify the ‘pom.xml’ file, or the maven project configuration file. Replace
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<relativePath/>
</parent>
as:
<parent>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofaboot-dependencies</artifactId>
<version>${sofa.boot.version}</version>
</parent>
Here, ${sofa.boot.version}
denotes the SOFABoot version (please refer to release note). Then, add a SOFABoot dependency of Health Check extension and Spring Boot Web Starter.
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>healthcheck-sofa-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Finally, configure parameters commonly used in the SOFABoot project in the application.properties
file. The spring.application.name
parameter is required to name the current application; the logging path
specifies the output directory for logging information.
# Application Name
spring.application.name=SOFABoot Demo
# logging path
logging.path=./logs
Advice to refer to the SOFABoot Module document before learn this demo.
Run it
We can import the project into IDE and run the ‘main’ method in the generated project (generally in the XXXApplication class) to start the application, or we can execute the mvn spring-boot:run
command under the project’s root directory, which will print the startup logging in the console:
2018-04-05 21:36:26.572 INFO ---- Initializing ProtocolHandler ["http-nio-8080"]
2018-04-05 21:36:26.587 INFO ---- Starting ProtocolHandler [http-nio-8080]
2018-04-05 21:36:26.608 INFO ---- Using a shared selector for servlet write/read
2018-04-05 21:36:26.659 INFO ---- Tomcat started on port(s): 8080 (http)
We can browse http://localhost:8080/sofaboot/versions to view the version summary generated by Maven plugin in SOFABoot. The result is like the following:
[
{
GroupId: "com.alipay.sofa",
Doc-Url: "https://github.com/sofastack/sofa-boot",
ArtifactId: "infra-sofa-boot-starter",
Built-Time: "2018-04-05T20:55:26+0800",
Commit-Time: "2018-04-05T20:54:26+0800",
Commit-Id: "049bf890bb468aafe6a3e07b77df45c831076996",
Version: "2.4.4"
}
]
Note: In SOFABoot 3.x, the endpoint path has been changed from sofaboot/versions to actuator/versions
We can browse http://localhost:8080/health/readiness to check the status of the Readiness Check. The result is like the following:
{
status: "UP",
sofaBootComponentHealthCheckInfo: {
status: "UP"
},
springContextHealthCheckInfo: {
status: "UP"
},
DiskSpace: {
status: "UP",
total: 250140434432,
free: 22845308928,
threshold: 10485760
}
}
Note: In SOFABoot 3.x, the endpoint path has been changed from health/readiness to actuator/readiness.
status: "UP"
indicates that Readiness Check is in good condition. We can browse http://localhost:8080/health
to get the application’s running state (it may change over time).
Viewing logs
In the above application.properties
file, the logging directory we have configured is ./logs
, namely the application root directory (which can be configured according to our needs), where log files may have the following structure:
./logs
├── health-check
│ ├── sofaboot-common-default.log
│ └── sofaboot-common-error.log
├── infra
│ ├── common-default.log
│ └── common-error.log
└── spring.log
If the application fails to start or the Health Check fails, we can find the error cause in the corresponding log file (sometimes we need to pay attention to the common-error.log
file)
Testing
As we know, SpringBoot provides the SpringRunner
integrated with JUnit 4 for developers to write integration test cases. In SOFABoot, we can still use the native SpringRunner
, but we recommend that you use SOFABoot’s SofaBootRunner
and SofaJUnit4Runner
to write integration tests and unit tests; beyond that, the application requires additional import of the following Starter:
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>test-sofa-boot-starter</artifactId>
</dependency>
Note that if you need to use SOFABoot’s class Isolation, import the above dependencies, and run testing with SofaBootRunner
and SofaJUnit4Runner
.
Modular Development
This section will focus on how we can carry out modular development under the SOFABoot environment, you can find this document’s sample code under the project. The project is composed of four modules:
.
│
├── service-facade
│
├── service-provider
│
├── service-consumer
│
└── sofa-boot-run
The functions of each module are:
- service-facade: an API package that tells you how to publish and reference JVM services.
- service-provider: demonstrate how to publish JVM services in XML, annotation, or API mode.
- service-consumer: demonstrate how to reference JVM services in XML, annotation, or API mode.
- sofa-boot-run: start the SOFA Boot application that contains the SOFABoot module.
Defining Service API
The service-facade module contains APIs that demonstrate how to publish and reference JVM services:
public interface SampleJvmService {
String message();
}
Publishing JVM services
Service-provider is a SOFABoot module used to demonstrate how to publish JVM services in XML, annotation, or API mode.
Defining SOFABoot Module
To define a SOFABoot module, add the ‘sofa-module.properties’ file to the service-provider module:
Module-Name=com.alipay.sofa.service-provider
Publishing Services in XML Mode
Implement the SampleJvmService interface:
public class SampleJvmServiceImpl implements SampleJvmService {
private String message;
@Override
public String message() {
System.out.println(message);
return message;
}
// getters and setters
}
Add the META-INF/spring/service-provide.xml file, and publish SampleJvmServiceImpl as a JVM service:
<bean id="sampleJvmService" class="com.alipay.sofa.isle.sample.SampleJvmServiceImpl">
<property name="message" value="Hello, jvm service xml implementation."/>
</bean>
<sofa:service ref="sampleJvmService" interface="com.alipay.sofa.isle.sample.SampleJvmService">
<sofa:binding.jvm/>
</sofa:service>
Publishing Service in Annotation Mode
Implement the SampleJvmService interface and add the @SofaService annotation:
@SofaService(uniqueId = "annotationImpl")
public class SampleJvmServiceAnnotationImpl implements SampleJvmService {
@Override
public String message() {
String message = "Hello, jvm service annotation implementation.";
System.out.println(message);
return message;
}
}
To differentiate the JVM services published in XML mode, we need to add a ‘uniqueId’ attribute to the annotation.
Configure the SampleJvmServiceAnnotationImpl class as a Spring Bean to ensure that the @SofaService annotation takes effect:
<bean id="sampleJvmServiceAnnotation" class="com.alipay.sofa.isle.sample.SampleJvmServiceAnnotationImpl"/>
Publishing Services in API Mode
Create a class named PublishServiceWithClient to demonstrate how to publish a service in API mode:
public class PublishServiceWithClient implements ClientFactoryAware {
private ClientFactory clientFactory;
public void init() {
ServiceClient serviceClient = clientFactory.getClient(ServiceClient.class);
ServiceParam serviceParam = new ServiceParam();
serviceParam.setInstance(new SampleJvmServiceImpl("Hello, jvm service service client implementation."));
serviceParam.setInterfaceType(SampleJvmService.class);
serviceParam.setUniqueId("serviceClientImpl");
serviceClient.service(serviceParam);
}
@Override
public void setClientFactory(ClientFactory clientFactory) {
this.clientFactory = clientFactory;
}
}
Configure the PublishServiceWithClient class as Spring Bean, and set ‘init-method’ in the class so that the service will be published when Spring refreshes it:
<bean id="publishServiceWithClient" class="com.alipay.sofa.isle.sample.PublishServiceWithClient" init-method="init"/>
Referencing JVM Services
Service-consumer is a SOFABoot module used to demonstrate how to reference JVM services in XML, annotation, or API mode.
Defining SOFABoot Module
Add the ‘sofa-module.properties’ file for the service-consumer module, and define it as a SOFABoot module:
Module-Name=com.alipay.sofa.service-consumer
Require-Module=com.alipay.sofa.service-provider
We need to specify the Require-Module in the ‘sofa-module.properties’ file to ensure that the service-provider module is refreshed before the service-consumer module.
Referencing Service in XML Mode
Add the META-INF/spring/service-consumer.xml file to reference the services published by the service-provider module:
<sofa:reference id="sampleJvmService" interface="com.alipay.sofa.isle.sample.SampleJvmService">
<sofa:binding.jvm/>
</sofa:service>
Referencing Service in Annotation Mode
Define the JvmServiceConsumer class, and add the @SofaReference annotation to its sampleJvmServiceAnnotationImpl property:
public class JvmServiceConsumer implements ClientFactoryAware {
@SofaReference(uniqueId = "annotationImpl")
private SampleJvmService sampleJvmServiceAnnotationImpl;
}
Configure the JvmServiceConsumer as a Spring Bean to ensure that the @SofaReference annotation takes effect:
<bean id="consumer" class="com.alipay.sofa.isle.sample.JvmServiceConsumer" init-method="init" />
Referencing Service in API Mode
The JvmServiceConsumer class implements the ClientFactoryAware interface and references the JVM services in its init method:
public class JvmServiceConsumer implements ClientFactoryAware {
private ClientFactory clientFactory;
public void init() {
ReferenceClient referenceClient = clientFactory.getClient(ReferenceClient.class);
ReferenceParam<SampleJvmService> referenceParam = new ReferenceParam<>();
referenceParam.setInterfaceType(SampleJvmService.class);
referenceParam.setUniqueId("serviceClientImpl");
SampleJvmService sampleJvmServiceClientImpl = referenceClient.reference(referenceParam);
sampleJvmServiceClientImpl.message();
}
@Override
public void setClientFactory(ClientFactory clientFactory) {
this.clientFactory = clientFactory;
}
}
Starting SOFABoot Application
Configure the module parent as SOFABoot:
<parent>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofaboot-dependencies</artifactId>
<version>${sofa.boot.version}</version>
</parent>
Add the isle-sofa-boot-starter, service-provider, and service-consumer dependencies to the module:
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>isle-sofa-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>service-provider</artifactId>
</dependency>
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>service-consumer</artifactId>
</dependency>
Run the ApplicationRun class, and the console will display the following:
Hello, jvm service xml implementation.
Hello, jvm service annotation implementation.
Hello, jvm service service client implementation.
Writing Test Cases
SOFABoot’s modular test method is consistent with that of SpringBoot. We only need to add the @SpringBootTest and @RunWith (SpringRunner.class) annotations into the test cases. We can also use the @SofaReference annotation to test the services published by the SOFABoot module:
@SpringBootTest
@RunWith(SpringRunner.class)
public class SofaBootWithModulesTest {
@SofaReference
private SampleJvmService sampleJvmService;
@SofaReference(uniqueId = "annotationImpl")
private SampleJvmService sampleJvmServiceAnnotationImpl;
@SofaReference(uniqueId = "serviceClientImpl")
private SampleJvmService sampleJvmServiceClientImpl;
@Test
public void test() {
Assert.assertEquals("Hello, jvm service xml implementation.", sampleJvmService.message());
Assert.assertEquals("Hello, jvm service annotation implementation.",
sampleJvmServiceAnnotationImpl.message());
Assert.assertEquals("Hello, jvm service service client implementation.",
sampleJvmServiceClientImpl.message());
}
}