Spring Boot - 多租户 - 优化 API 的响应时间
Posted
技术标签:
【中文标题】Spring Boot - 多租户 - 优化 API 的响应时间【英文标题】:Spring Boot - Multitenancy - Optimise response time for API 【发布时间】:2021-03-18 16:41:11 【问题描述】:我有一个 Spring Boot 应用程序,它实现了多模式多租户。在没有多租户的情况下,相同的 API 响应时间为 300-400 毫秒。但在实施多租户后,响应时间增加到 6-7 秒(在同一服务器和同一架构上)。
我知道读取header,根据header切换数据库等需要额外的处理。但我觉得应该不是6-7秒。有人可以建议我如何减少此响应时间。以下是为多租户添加的类
public class TenantAwareRoutingSource extends AbstractRoutingDataSource
@Override
protected Object determineCurrentLookupKey()
return ThreadLocalStorage.getTenantName();
public class TenantNameInterceptor extends HandlerInterceptorAdapter
@Value("$schemas.list")
private String schemasList;
private Gson gson = new Gson();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
String tenantName = request.getHeader("tenant-id");
if(StringUtils.isBlank(schemasList))
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(gson.toJson(new Error("Tenants not initalized...")));
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return false;
if(!schemasList.contains(tenantName))
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(gson.toJson(new Error("User not allowed to access data")));
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return false;
ThreadLocalStorage.setTenantName(tenantName);
return true;
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
ThreadLocalStorage.setTenantName(null);
@Setter
@Getter
@AllArgsConstructor
public static class Error
private String message;
public class ThreadLocalStorage
private static ThreadLocal<String> tenant = new ThreadLocal<>();
public static void setTenantName(String tenantName)
tenant.set(tenantName);
public static String getTenantName()
return tenant.get();
@Configuration
public class AutoDDLConfig
@Value("$spring.datasource.username")
private String username;
@Value("$spring.datasource.password")
private String password;
@Value("$schemas.list")
private String schemasList;
@Value("$db.host")
private String dbHost;
@Bean
public DataSource dataSource()
AbstractRoutingDataSource multiDataSource = new TenantAwareRoutingSource();
if (StringUtils.isBlank(schemasList))
return multiDataSource;
String[] tenants = schemasList.split(",");
Map<Object, Object> targetDataSources = new HashMap<>();
for (String tenant : tenants)
System.out.println("####" + tenant);
tenant = tenant.trim();
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver"); // Change here to MySql Driver
dataSource.setSchema(tenant);
dataSource.setUrl("jdbc:mysql://" + dbHost + "/" + tenant
+ "?autoReconnect=true&characterEncoding=utf8&useSSL=false&useTimezone=true&serverTimezone=Asia/Kolkata&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true");
dataSource.setUsername(username);
dataSource.setPassword(password);
targetDataSources.put(tenant, dataSource);
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(dataSource);
emfBean.setPackagesToScan("com"); // Here mention JPA entity path / u can leave it scans all packages
emfBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
emfBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.default_schema", tenant);
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
emfBean.setJpaPropertyMap(properties);
emfBean.setPersistenceUnitName(dataSource.toString());
emfBean.afterPropertiesSet();
multiDataSource.setTargetDataSources(targetDataSources);
multiDataSource.afterPropertiesSet();
return multiDataSource;
来自 application.properties 的片段
spring.datasource.username=<<username>>
spring.datasource.password=<<pssword>>
schemas.list=suncitynx,kalpavrish,riddhisiddhi,smartcity,businesspark
db.host=localhost
########## JPA Config ###############
spring.jpa.open-in-view=false
spring.jpa.show-sql=false
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.database=mysql
spring.datasource.initialize=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.jdbc.time_zone = Asia/Kolkata
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
##############Debug Logging#########################
#logging.level.org.springframework=DEBUG
#logging.level.org.hibernate.SQL=DEBUG
#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
######### HIkari Pool ##############
spring.datasource.hikari.maximum-pool-size=20
######### Jackson ############
spring.jackson.serialization.WRITE_ENUMS_USING_TO_STRING=true
spring.jackson.deserialization.READ_ENUMS_USING_TO_STRING=true
spring.jackson.time-zone: Asia/Kolkata
#common request logger
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
#Multi part file size
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size = 15MB
【问题讨论】:
【参考方案1】:您确定维护每个租户的连接池吗?
【讨论】:
我没有在任何地方明确编码连接池。 Hibernate 可能会采用默认值。也由 application.properties 提供以上是关于Spring Boot - 多租户 - 优化 API 的响应时间的主要内容,如果未能解决你的问题,请参考以下文章