Goal: Fleet SaaS platform
Fleet management similar SaaS platform, from 0 to 1, we continue..
The last article said that objective factors, the choice of database isolation scheme, but as a good program ape, must support a variety of temporary scenarios, can quickly respond to, or will be pulled to heaven…
So now the product in communication, in the design, the excellent program ape also want to be prepared at the same time, otherwise how to keep the evil and mysterious ~~
Well, then, data isolation scheme since first isolated from the business field, so I have to support multiple data sources, the framework of one thousand a client one day he was asked to their own data separate, eh, this possible, such as we push people, want to pull up a big customer, somebody else has asked, that must be satisfied.
(I am Java after midnight, in this share the next experience, those who rely on copy to carry the author, do not copy to other platforms at will, which day free to complain)
Serving: more advice ~~
Database isolation, for back-end program ape, that is multi-data source, multi-data on the Internet can also find some solutions, some practices, even some stepped on the pit, we have collected some, or good code.
Then we (paste a code) also need to have the function support of multiple data sources,
The code:
Start by defining the multi-data source configuration
Dynamic: datasource: slave1: driver-class-name: org.postgresql. driver url: jdbc:postgresql://localhost:3306/yydsCarBase username: root password: 123456 slave2: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:3306/yydsCar1 username: root password: 123456Copy the code
Good code always has a few comments
import java.lang.annotation.*; /** * @target ({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface DataSource { String value() default ""; }Copy the code
Configure multiple data sources
/ * * * Configuration data source * / @ Configuration @ EnableConfigurationProperties (DynamicDataSourceProperties. Class) public class DynamicDataSourceConfig { @Autowired private DynamicDataSourceProperties properties; @Bean @ConfigurationProperties(prefix = "spring.datasource.druid") public DataSourceProperties dataSourceProperties() { return new DataSourceProperties(); } @Bean public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) { DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(getDynamicDataSource()); // Configure the default data source, no matter how many data sources there are, configure a default, Otherwise not run DruidDataSource defaultDataSource = DynamicDataSourceFactory. BuildDruidDataSource (dataSourceProperties); dynamicDataSource.setDefaultTargetDataSource(defaultDataSource); return dynamicDataSource; } /** * private map< Object, * @return */ private map< Object, Object> getDynamicDataSource(){ Map<String, DataSourceProperties> dataSourcePropertiesMap = properties.getDatasource(); Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size()); dataSourcePropertiesMap.forEach((k, v) -> { DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v); targetDataSources.put(k, druidDataSource); }); return targetDataSources; }} / * * * * / public data source class DynamicDataSource extends AbstractRoutingDataSource {@ Override protected Object determineCurrentLookupKey() { return DynamicContextHolder.peek(); }}Copy the code
Database link configuration
Public class DynamicDataSourceFactory {/** * * @param properties * @return */ public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) { DruidDataSource druid = new DruidDataSource(); try { druid.setDriverClassName(properties.getDriverClassName()); druid.setUrl(properties.getUrl()); druid.setUsername(properties.getUsername()); druid.setPassword(properties.getPassword()); druid.setInitialSize(properties.getInitialSize()); druid.setMaxActive(properties.getMaxActive()); druid.setMinIdle(properties.getMinIdle()); druid.setMaxWait(properties.getMaxWait()); druid.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis()); druid.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis()); druid.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis()); druid.setValidationQuery(properties.getValidationQuery()); druid.setValidationQueryTimeout(properties.getValidationQueryTimeout()); druid.setTestOnBorrow(properties.isTestOnBorrow()); druid.setTestOnReturn(properties.isTestOnReturn()); druid.setPoolPreparedStatements(properties.isPoolPreparedStatements()); druid.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements()); druid.setSharePreparedStatements(properties.isSharePreparedStatements()); druidDataSource.setFilters(properties.getFilters()); druidDataSource.init(); } catch (SQLException e) {// Mark e.printStackTrace(); // Mark e.printstackTrace (); } return druidDataSource; }}Copy the code
Support for multiple data sources
public class DynamicContextHolder { private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = new ThreadLocal() { @Override protected Object initialValue() { return new ArrayDeque(); }}; Public static String peek() {return context_holder.get ().peek(); } /** * @param dataSource */ public static void push(String dataSource) { CONTEXT_HOLDER.get().push(dataSource); Public static void poll() {Deque<String> Deque = context_holder.get (); deque.poll(); if (deque.isEmpty()) { CONTEXT_HOLDER.remove(); }}}Copy the code
Finally, AOP is treated uniformly
/** * multiple data sources, */ @aspect @Component @order (Ordered.HIGHEST_PRECEDENCE) public class DataSourceAspect { @Pointcut("@annotation(io.renren.commons.dynamic.datasource.annotation.DataSource) " + "|| @within(io.renren.commons.dynamic.datasource.annotation.DataSource)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Class targetClass = point.getTarget().getClass(); Method method = signature.getMethod(); DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class); DataSource methodDataSource = method.getAnnotation(DataSource.class); if(targetDataSource ! = null || methodDataSource ! = null){ String value; if(methodDataSource ! = null){ value = methodDataSource.value(); }else { value = targetDataSource.value(); } DynamicContextHolder.push(value); } try { return point.proceed(); } finally { DynamicContextHolder.poll(); }}}Copy the code
conclusion
Multi-data source processing, many online methods or open source, can be directly used, just from the concept or thought, need to understand how he is operating. Other actually do not go deep into the need to modify the underlying source code, basic can be used.
The SaaS system is built from zero to one, and the next part continues at….