smartbuf-springcloud
is a spring-cloud
serialization plugin based on smartbuf
.
SmartBuf Introduce
smartbuf
is a novel, efficient, intelligent and easy-to-use cross-language serialization framework.
It has the same high performance as protobuf
, and has the same versatility, scalability and flexibility as json
.
In Java
ecosystem, it supports multiple RPC
through the following plugins:
smartbuf-dubbo
: Provides a serialization plugin instream
mode fordubbo
.smartbuf-springcloud
: Provides a serialization plugin inpacket
mode forspring-cloud
.
The following is a detailed introduction of the smartbuf-springcloud
.
This plugin wraps packet
mode of smartbuf
,
and exposes an HTTP
message converter named application/x-smartbuf
to spring
via the custom SmartbufMessageConverter
.
This new application/x-smartbuf
could provide better performance during data trasfer of complex object.
This plugin declares an Auto-Configuration
named SmartbufAutoConfiguration
in META-INFO/spring.factories
.
spring-boot
will scan and register it during initializing, it's fully automatic, no additional configuration is required.
For more infomation, please refer to SpringBoot Doc。
SmartbufAutoConfiguration
will add a new SmartbufMessageConverter
instance into spring
container,
it's a custom HttpMessageConverter
, and its MediaType
is application/x-smartbuf
.
spring-mvc
will include this SmartbufMessageConverter
into messageConverters
, and uses it globally.
If the headers of the subsequent http
requests include accept: application/x-smartbuf
or content-type: application/x-smartbuf
,
spring-mvc
will use SmartbufMessageConverter
to decode its input, and encode its output.
The whole process is similar to the implementation of application/json
, expect that the application/x-smartbuf
is
based on another serialization: SmartBuf
SUMMARY: No additional configuration is required, this plugin will register application/x-smartbuf
into spring-mvc
automatically, it has no effect on normal http
request,
and will be activated only if accept
or context-type
in the headers matche application/x-smartbuf
.
This chapter introduces how to add and use smartbuf-springcloud
into your project with a simple example.
You could add smartbuf-springcloud
dependency by the following maven
configuration:
<dependency>
<groupId>com.github.smartbuf</groupId>
<artifactId>smartbuf-springcloud</artifactId>
<version>1.0.0</version>
</dependency>
The spring-cloud
layer uses http
to communicate with the server's spring-mvc
, the controller
might like this:
@RestController
public class DemoController {
@PostMapping("/hello")
public String sayHello(String name) { return "hello " + name; }
}
The client could use this FeignClient
to invoke server:
@FeignClient(name = "demo")
public interface DemoClient {
@PostMapping(value = "/hello", consumes = "application/json", produces = "application/json")
String hello(@RequestParam("name") String name);
}
When client uses DemoClient
to invoke server's DemoController
,
feign
will send a request to server based on consumes
and produces
, the headers will like this:
=== MimeHeaders ===
accept = application/json
content-type = application/json
user-agent = Java/1.8.0_191
connection = keep-alive
The spring-mvc
of server will determine to use application/json
to perform input
decoding and output
decoding
based on accept
and content-type
in the headers of request.
As mentioned earily, smartbuf-springcloud
will automatically register a codec named application/x-smartbuf
into spring-mvc
,
so after add the maven
dependency, no additional configuration is required.
To use application/x-smartbuf
, you just need to change DemoClient
like this:
@FeignClient(name = "demo")
public interface DemoClient {
@PostMapping(value = "/hello", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
String hello(@RequestParam("name") String name);
}
Please pay attention to the changes in consumes
and produces
.
After that, feign
will use application/x-smartbuf
to communicate with the server's spring-mvc
automatically,
the headers will like this:
=== MimeHeaders ===
accept = application/x-smartbuf
content-type = application/x-smartbuf
user-agent = Java/1.8.0_191
connection = keep-alive
Even better, you can use application/json
and application/x-smartbuf
at the same time, like this:
@FeignClient(name = "demo")
public interface DemoClient {
@PostMapping(value = "/hello", consumes = "application/json", produces = "application/json")
String helloJSON(@RequestParam("name") String name);
@PostMapping(value = "/hello", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
String helloSmartbuf(@RequestParam("name") String name);
}
The client could use application/json
to communicate with the server by invoke helloJSON
,
and use application/x-smartbuf
by invoke helloSmartbuf
.
The server's spring-mvc
will switch to correct converter automatically based on the specified accept
and content-type
.
You can checkout
this project and find the whole example in demo
submodule.
The advantage of smartbuf
is the high compression ratio brought by its partition serialization,
especially when it comes to complex objects and arrays, its space utilization is far superior to other serialization schemes.
For RPC, the serialization time is often nanoseconds, while the logical processing and network transmission are often in the order of milliseconds. Therefore, the following test will use a single thread to performing test.
Below we test the difference between json
and smartbuf
through three different types of api
.
hello
is the helloJson
and helloSmartbuf
mentioned above.
The input and output parameters are String
, its internal logic is very simple, 400,000
times requests will cost:
JSON
: 169sSmartBuf
: 170s
Network input(bytes_input
) and output(bytes_output
) are like this:
Since smartbuf
encoding requires an few extra bytes to describe the complete data information,
its space utilization is not as good as json
when dealing with simple data like String
.
getUser
's implementation is like this:
@RestController
public class DemoController {
private UserModel user = xxx; // initialized at somewhere else
@PostMapping("/getUser")
public UserModel getUser(Integer userId) { return user; }
}
The user
is a pre-allocated object for testing,
its model can be found in the UserModel
class in the demo
source.
The client's FeignClient
is defined as following:
@FeignClient(name = "demo")
public interface DemoClient {
@PostMapping(value = "/getUser", consumes = "application/json", produces = "application/json")
UserModel getUserJSON(@RequestParam("userId") Integer userId);
@PostMapping(value = "/getUser", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
UserModel getUserSmartbuf(@RequestParam("userId") Integer userId);
}
300,000
times invocation will cost:
JSON
: 162sSmartBuf
: 149s
Network input(bytes_input
) and output(bytes_output
) are like this:
We can see that the parameter userId
is a single data type,
so the network traffic used by json
and smartbuf
is almost the same.
The return result UserModel
is a more complex object,
and the json
network resource consumption is nearly 3 times of smartbuf
.
Because the test environment is localhost
,
network transmission time does not have much impact on the total time consumption of the interface.
queryPost
's implementation is like this:
@RestController
public class DemoController {
private List<PostModel> posts = xxx; // initialized at somewhere else
@PostMapping("/queryPost")
public List<PostModel> queryPost(String keyword) { return posts; }
}
The return value posts
is a pre-allocated PostModel
array for testing.
Its length is fixed 100
. its model and initialization can be found in the demo
source.
The client's FeignClient
is defined as follows:
@FeignClient(name = "demo")
public interface DemoClient {
@PostMapping(value = "/queryPost", consumes = "application/json", produces = "application/json")
List<PostModel> queryPostJSON(@RequestParam("keyword") String keyword);
@PostMapping(value = "/queryPost", consumes = "application/x-smartbuf", produces = "application/x-smartbuf")
List<PostModel> queryPostSmartbuf(@RequestParam("keyword") String keyword);
}
100,000
times invocation will cost:
JSON
: 195sSmartBuf
: 155s
Network input(bytes_input
) and output(bytes_output
) are like this:
We can see that the parameter keyword
is a single String
,
so the network traffic used by json
and smartbuf
is almost the same.
The return result List<PostModel>
is a large array of complex objects,
and the json
network resource consumption is nearly 10 times of smartbuf
.
Because the test environment is localhost
,
network transmission time does not have much impact on the total time consumption of the interface.
When the RPC
's input and output are very simple, application/x-smartbuf
does not have any performance advantages.
When the RPC
's input and output are large data such as complex objects, arrays, collections.
Using application/x-smartbuf
could greatly reduce the bytes size of the network transmission.
For example, in the queryPost
above, application/x-smartbuf
's network transmission is only 1/10 of application/json
.
What is commendable is that the application/x-smartbuf
and application/json
can coexist and seamleassly switch.
For example, you can use application/json
directly for some simple api
,
and use the efficient application/x-smartbuf
for complex api
with large object to achieve better performance.
For example, you can use application/json
during development and test,
and switch to application/x-smartbuf
when going online.
MIT