Microservices Engine (MSE) cloud-native gateway supports built-in and custom plug-ins. The following benchmarks compare request latency across plug-in types and languages to help you evaluate performance when choosing a plug-in strategy.
Built-in plug-in latency
Built-in plug-ins operate at the microsecond level.
| Plug-in | Latency per request |
|---|---|
| key-auth | ~20 microseconds |
| basic-auth | ~30 microseconds |
Custom plug-in latency by language
MSE supports custom plug-in development in C++, Golang, Rust, and AssemblyScript.
Benchmark setup
All four language implementations run identical processing logic on each incoming request:
Add 20 request headers.
Read those 20 headers.
Remove all 20 headers.
This workload exercises the core header-manipulation APIs, providing a consistent basis for cross-language comparison.
Results
| Language | Request latency |
|---|---|
| C++ | 0.19 ms |
| Golang | 0.20 ms |
| Rust | 0.21 ms |
| AssemblyScript | 0.21 ms |
All four languages deliver similar per-request latency, within 0.02 ms of each other. Language choice has minimal impact on plug-in performance for typical header-processing workloads. Choose based on team expertise and ecosystem preferences rather than performance alone.
Benchmark code
Each sample below implements the same logic: add 20 request headers, read them, then remove them.
C++
FilterHeadersStatus PluginContext::onRequestHeaders(uint32_t, bool) {
std::string fake_header_key_prefix = "fake_key_";
std::string fake_header_value_prefix = "fake_value_";
// Add 20 headers
for (size_t i = 0; i < 20; i++) {
addRequestHeader(fake_header_key_prefix + std::to_string(i),
fake_header_value_prefix + std::to_string(i));
}
// Read 20 headers
for (size_t i = 0; i < 20; i++) {
getRequestHeader(fake_header_key_prefix + std::to_string(i));
}
// Remove 20 headers
for (size_t i = 0; i < 20; i++) {
removeRequestHeader(fake_header_key_prefix + std::to_string(i));
}
return FilterHeadersStatus::Continue;
}Golang
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
fake_header_key_prefix := "fake_key_"
fake_header_value_prefix := "fake_value_"
for i := 0; i < 20; i++ {
proxywasm.AddHttpRequestHeader(fake_header_key_prefix+strconv.Itoa(i),
fake_header_value_prefix+strconv.Itoa(i))
}
for i := 0; i < 20; i++ {
proxywasm.GetHttpRequestHeader(fake_header_key_prefix + strconv.Itoa(i))
}
for i := 0; i < 20; i++ {
proxywasm.RemoveHttpRequestHeader(fake_header_key_prefix + strconv.Itoa(i))
}
return types.ActionContinue
}Rust
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
for i in 0..20 {
let key = "fake_key_".to_string() + &i.to_string();
let value = "fake_value_".to_string() + &i.to_string();
self.set_http_request_header(&key, Some(&value));
}
for i in 0..20 {
let key = "fake_key_".to_string() + &i.to_string();
self.get_http_request_header(&key);
}
for i in 0..20 {
let key = "fake_key_".to_string() + &i.to_string();
self.set_http_request_header(&key, None);
}
Action::Continue
}AssemblyScript
onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues {
let fake_header_key_prefix: string = "fake_key_";
let fake_header_value_prefix: string = "fake_value_";
for (let i = 0; i < 20; i++) {
stream_context.headers.request.add(fake_header_key_prefix + i.toString(), fake_header_value_prefix + i.toString())
}
for (let i = 0; i < 20; i++) {
stream_context.headers.request.get(fake_header_key_prefix + i.toString())
}
for (let i = 0; i < 20; i++) {
stream_context.headers.request.remove(fake_header_key_prefix + i.toString())
}
return FilterHeadersStatusValues.Continue;
}