How to Build Robust, High‑Concurrency APIs: Design, Security, and Scaling Tips
This article outlines practical guidelines for designing and implementing enterprise‑grade APIs, covering request/response definitions, unified error handling, interceptor chains, token‑based authentication, rate limiting, load balancing, clustering, caching, and strategies for achieving high concurrency and high availability.
What Is an API?
An API is a server endpoint that receives client requests with a predefined set of parameters, processes the request, and returns data in a specified format such as JSON or XML.
Key Development Considerations
1. Define Input Parameters
Write a clear API contract that documents each request parameter, its type, whether it is required, and any validation rules.
2. Define Output Structure
Encapsulate responses in a consistent wrapper that includes a result code, message, and payload. Example response models:
package com.caiex.vb.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Result", propOrder = {"resultCode", "resultMsg"})
public class Result implements Serializable {
private static final long serialVersionUID = 10L;
protected int resultCode;
protected String resultMsg;
public int getResultCode() { return this.resultCode; }
public void setResultCode(int value) { this.resultCode = value; }
public String getResultMsg() { return this.resultMsg; }
public void setResultMsg(String value) { this.resultMsg = value; }
} package com.caiex.vb.model;
import java.io.Serializable;
public class Response implements Serializable {
private static final long serialVersionUID = 2360867989280235575L;
private Result result;
private Object data;
public Result getResult() {
if (this.result == null) { this.result = new Result(); }
return result;
}
public void setResult(Result result) { this.result = result; }
public Object getData() { return data; }
public void setData(Object data) { this.data = data; }
}3. Choose HTTP Method
Follow RESTful conventions: GET for reads, POST for creates, PUT/PATCH for updates, DELETE for removals.
4. Unified Return Codes
public static final int NO_AGENT_RATE = 1119; // rate not found
public static final int SCHEME_COMMIT_FAIL = 4000; // scheme submission failed
public static final int SCHEME_CONFIRMATION = 4001; // scheme confirming
public static final int SCHEME_NOT_EXIST = 4002; // scheme does not exist
public static final int SCHEME_CANCEL = 4005; // scheme cancelled
// ... other codes5. Global Exception Handling
package com.caiex.vb.interceptor;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.caiex.vb.model.Response;
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@ExceptionHandler(value = Exception.class)
public Response allExceptionHandler(HttpServletRequest request, Exception exception) {
logger.error("Caught exception:", exception);
Response response = new Response();
response.setData(null);
response.getResult().setResultCode(9999);
response.getResult().setResultMsg("System busy");
return response;
}
}6. Interceptor Chain
Use Spring MVC interceptors to validate signatures, rate‑limit requests, and enforce common parameters.
package com.caiex.vb.interceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson.JSON;
import com.caiex.redis.service.api.RedisApi;
import com.caiex.vb.model.Response;
import com.caiex.vb.utils.CaiexCheckUtils;
@Component
public class SignInterceptor extends BaseValidator implements HandlerInterceptor {
private Logger logger = LogManager.getLogger(this.getClass());
@Resource
private RedisApi redisApi;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (isTestIpAddr(request)) { return true; }
String securityKey = redisApi.hGet("securityKey", request.getParameter("agentid"));
if (StringUtils.isEmpty(securityKey)) {
Response resp = new Response();
resp.setData(null);
resp.getResult().setResultCode(8001);
resp.getResult().setResultMsg("Missing private key, channel: " + request.getParameter("agentid"));
logger.error("Missing private key, channel: " + request.getParameter("agentid"));
InterceptorResp.printJson(response, resp);
return false;
}
String sign = request.getParameter("sign");
if (StringUtils.isEmpty(sign) || !sign.equals(CaiexCheckUtils.getSign(request.getParameterMap(), securityKey))) {
Response resp = new Response();
resp.setData(null);
resp.getResult().setResultCode(3203);
resp.getResult().setResultMsg("Signature verification failed");
logger.error("Signature verification failed: " + JSON.toJSONString(request.getParameterMap()) + " securityKey=" + securityKey);
InterceptorResp.printJson(response, resp);
return false;
}
return true;
}
// afterCompletion and postHandle omitted for brevity
}7. Spring MVC Configuration
package com.caiex.oltp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.caiex.oltp.interceptor.*;
@EnableWebMvc
@Configuration
@ComponentScan
public class WebAppConfigurer extends WebMvcConfigurerAdapter {
@Bean
public CommonValidator commonInterceptor() { return new CommonValidator(); }
@Bean
public DDSAuthValidator ddsAuthInterceptor() { return new DDSAuthValidator(); }
@Bean
public QueryPriceParamsValidator queryPriceParamsInterceptor() { return new QueryPriceParamsValidator(); }
@Bean
public TradeParamsValidator tradeParamsInterceptor() { return new TradeParamsValidator(); }
@Bean
public APILimitRateValidator aPILimitRateInterceptor() { return new APILimitRateValidator(); }
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(aPILimitRateInterceptor()).addPathPatterns("/*/*");
registry.addInterceptor(ddsAuthInterceptor())
.addPathPatterns("/tradeState/*", "/recycle/*", "/matchInfo/*", "/price/tradeTicketParam");
registry.addInterceptor(commonInterceptor())
.addPathPatterns("/price/tradeTicketParam", "/tradeState/*", "/recycle/*");
registry.addInterceptor(queryPriceParamsInterceptor()).addPathPatterns("/price/getPriceParam");
registry.addInterceptor(tradeParamsInterceptor()).addPathPatterns("/price/tradeTicketParam");
super.addInterceptors(registry);
}
}8. Token Generation and Signature
Provide a token‑creation endpoint for third‑party callers. Tokens expire after one day; repeated requests within a minute return the same token to ease consistency.
private String createToken() {
String utk = "Msk!D*" + System.currentTimeMillis() + "UBR&FLP";
logger.info("create token --- " + Md5Util.md5(utk));
return Md5Util.md5(utk);
}Signature algorithm: concatenate parameterName + parameterValue + privateKey for every non‑null parameter, then MD5 the string. Verification repeats the process and compares the result with the supplied sign value.
Improving API Concurrency and Availability
Load Balancing
Deploy Alibaba Cloud SLB in front of a Kubernetes (k8s) cluster. The k8s service exposes internal load‑balancing, and the external SLB distributes traffic across the nodes.
Clustering
When a single service becomes a bottleneck, add more instances. If the bottleneck shifts to message queues, increase consumer instances or adjust broker configurations to balance producer‑consumer rates.
Caching
Cache slow‑to‑fetch data in Redis or an in‑process ConcurrentHashMap with expiration. Use batch operations like mGet to reduce round‑trips.
Cache objects in ConcurrentHashMap with TTL.
Store rarely‑changing keys in Redis and refresh via scheduled tasks.
Batch fetch multiple keys with mGet to improve throughput.
High‑Availability Architecture
Address single‑point failures by employing Redis master‑slave replication, ZooKeeper‑managed Dubbo clusters, and Storm master‑slave setups.
Idempotency and Order Uniqueness
Use Redis SETNX with a short TTL (e.g., 3 seconds) to lock an order ID during processing, preventing duplicate submissions while allowing retries after a failure.
Timeout Handling
If processing exceeds 10 seconds, automatically mark the transaction as failed and return an error response.
Conclusion
Effective API design requires clear contracts, unified response formats, robust exception handling, and a layered interceptor strategy. Scaling for high concurrency involves load balancing, clustering, caching, and rate limiting, while high availability demands redundant services and careful handling of idempotency and timeouts.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
