Привет всем, я Букай Чен~
РазвитыйSaaS
система Друзья на платформе, должно быть, правымного арендатор
Это понятие не незнакомо,Проще говоря, арендатор – корпоративный заказчик.,многие арендаторы используют одну и ту же SaaSсистему,Когда SaaSсистема недоступна,Тогда все арендаторы недоступны. Вы можете понять, что SaaSсистема – это как здание.,Арендатором является компания, которая арендует офисные помещения в здании.,Обычно каждая компания занимается своим бизнесом.,Не мешайте друг другу,Но однажды лифт в здании сломался,Тогда пострадают все компании.
много арендаторвопрос,Это Архитектурный Метод дизайна представляет собой SaaSсистему, работающую на одном или группе серверов, которая может предоставлять услуги множеству арендаторов (клиентов). Цель состоит в том, чтобы позволить множеству арендаторов использовать один и тот же набор программ в среде Интернета и обеспечить изоляцию данных между арендаторами. . Из этого архитектурного По шаблону дизайна,Это нетрудно увидеть,Основное внимание в многоарендной архитектуре уделяется изоляции данных многоклиентов с помощью одного и того же набора процедур. Так как данные арендатора установлены середина хранилища,Таким образом, для обеспечения безопасности данных,Просто чтобы посмотреть, сможем ли мы изолировать данные арендаторов,Предотвращайте арендаторов от непреднамеренного или злонамеренного получения и вмешательства в жизнь других лиц. Прежде чем говорить о реализации изоляции данных многих арендаторов,Давайте сначала посмотрим, чтоSaaSсистема
。
Платформа SaaS — это платформа для эксплуатации программного обеспечения SaaS. Поставщики SaaS создают всю сетевую инфраструктуру, программное обеспечение и аппаратные платформы, необходимые для информатизации предприятия, и отвечают за ряд услуг, таких как раннее внедрение и последующее обслуживание. Арендаторам (предприятиям) не нужно приобретать программное и аппаратное обеспечение, собирать компьютер. помещений или нанять ИТ-персонал, можно использовать информационные системы через Интернет. SaaS — это модель компоновки программного обеспечения, приложения которой предназначены для доставки по сети, и пользователям легко их размещать, развертывать и получать к ним доступ через Интернет.
Проще говоря, арендаторы могут пользоваться функциональными услугами, предоставляемыми платформой, оплачивая аренду SaaS-платформы. В настоящее время наиболее типичными являются различные облачные платформы и поставщики облачных услуг.
В настоящее время существует три архитектурных проекта изоляции данных в мультитенантной системе SaaS, которые заключаются в том, чтобы предоставить каждому арендатору независимую базу данных, независимое табличное пространство и разделить арендаторов по полям. Каждое решение имеет свои применимые ситуации.
Один арендатор имеет независимую базу данных
Арендатор использует одну базу данных независимо, а это означает, что наша SaaS-система должна подключаться к нескольким базам данных. Это решение реализации фактически такое же, как и архитектура подбазы данных и подтаблицы. Преимуществом является высокий уровень изоляции данных и хорошая безопасность. В конце концов, каждый арендатор использует только одну базу данных, но стоимость физического оборудования и стоимость обслуживания также становятся выше.
независимое табличное пространство
Как реализовать это решение,То есть все арендаторы используют одну и ту же систему библиотеки данных.,Но каждый арендатор в системе библиотеки данныхсередина владеет независимым табличным пространством.
Изолировать арендаторов по полю идентификатора арендатора
Это решение представляет собой простейший метод изоляции данных в многопользовательском решении, то есть добавление поля для различения арендаторов (например, tenant_id или org_id) в каждой таблице, чтобы определить, какому арендатору принадлежит каждый фрагмент данных при запросе каждого оператора. добавьте это поле в качестве условия фильтра. Его особенностью является то, что все данные арендатора хранятся в одной таблице. Изоляция данных полностью различается по полям.
Сравнение трех проектов архитектуры изоляции данных выглядит следующим образом:
План изоляции | расходы | Количество поддерживаемых арендаторов | преимущество | недостаток |
---|---|---|---|---|
независимая система баз данных | высокий | немного | данныеуровень изоляциивысокий,безопасность,Индивидуальные потребности могут быть разработаны для отдельных арендаторов. | независимая установка библиотеки данных,Физические затраты и расходы на техническое обслуживание относительно высоки |
независимое табличное пространство | середина | Более | Обеспечивает определенную степень логической изоляции данных, а одна система баз данных может поддерживать несколько клиентов. | Управление базой данных затруднено, таблиц много, а восстановление данных немного затруднено. |
Различать по полю идентификатора арендатора | Низкий | много | Расходы на обслуживание и приобретениемакс. Низкий, каждая библиотека данных может поддерживать максимальное количество арендаторов. | Уровень изоляции самый низкий.,безопасность Ясай Низкий |
Большинство компаний используют третий тип: Изолировать. арендаторов по полю идентификатора арендатора Архитектурный дизайн реализует изоляцию большого количества данных арендатора. Далее давайте посмотрим, как реализовать изоляцию большого количества данных арендатора на уровне кода. СОЗРЕЗОДЕГО на публичном аккаунте: Колонка технологий Code Обезьяна, ключевое слово ответа: 1111 Получите внутреннее руководство Alibaba по настройке производительности Java!
Выше мы сказали Изолировать арендаторов по полю идентификатора арендатора Этот метод заключается в добавлении идентификатора арендатора в качестве условия фильтра к каждому оператору SQL при получении данных для изоляции данных арендатора. Но это означает, что каждый SQL-запрос должен добавлять условие фильтрации идентификатора клиента.,Если вы опустите добавление, это означает, что будут запрошены данные разных арендаторов.,Это категорически запрещено,В то же время для каждого интерфейса запроса необходимо вручную установить условия фильтрации.,Дублирование работы,Совсем не элегантно. На данный момент мне нужно поговорить о большом расширении арендатора mybatis-plus.,Посмотрите, как элегантно реализована изоляция арендаторов? Прежде чем рассказать больше,Давайте сначала подумаем, как элегантно добиться изоляции данных? Во-первых, мы требуем, чтобы каждый оператор SQL добавлялся с условием фильтрации идентификатора клиента.,Это означает, что нам нужно проанализировать необработанный SQL и добавить фильтр идентификатора арендатора, где это необходимо.,Мы знаем, что точка расширения, предоставляемая mybatis, является перехватчиком.,Может улучшить логику до и после обработки операторов SQL.,Вот что делает нумерация страниц,Поэтому для нас вполне естественно улучшить здесь SQL.,Далее давайте посмотрим, как плагин mybatis-plus multi tenant реализует изоляцию данных большого количества клиентов.,ознакомительный адрес официального сайта плагина: https://www.baomidou.com/pages/aef2f2/#tenantlineinnerinterceptor,Часть исходного кода этого перехватчика выглядит следующим образом:
public class TenantLineInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
// много Процессор арендатора
private TenantLineHandler tenantLineHandler;
// Измените SQL и добавьте условие много идентификатора арендатора.
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if (!InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(this.parserSingle(mpBs.sql(), (Object)null));
}
}
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
return;
}
MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(this.parserMulti(mpBs.sql(), (Object)null));
}
}
// Из-за нехватки места код, опущенный ниже, наследует абстрактный класс JsqlParserSupport для анализа SQL, а затем добавляет условие многозначного идентификатора клиента. Вы можете просмотреть исходный код самостоятельно.
......
}
Далее посмотрим на процессорTenantLineHandler
,Это интерфейс,Нам нужно предоставить индивидуальную реализацию,обозначениемного арендатор Соответствующая конфигурация:
public class TenantDatabaseHandler implements TenantLineHandler {
private final Set<String> ignoreTables = new HashSet<>();
public TenantDatabaseHandler(TenantProperties properties) {
// Синхронизировать регистр игнорируемых имен таблиц, настроенный в файле конфигурации, для адаптации к различным методам записи.
properties.getIgnoreTables().forEach(table -> {
ignoreTables.add(table.toLowerCase());
ignoreTables.add(table.toUpperCase());
});
}
/**
* Получить имя поля клиента
* <p>
* Имя поля по умолчанию: tenant_id, здесь я использую org_id
*
* @return Имя поля арендатора
*/
@Override
public String getTenantIdColumn() {
return "org_id";
}
@Override
public Expression getTenantId() {
// Здесь идентификатор арендатора возвращается перехватчику многоарендатора через контекст информации для входа в систему, чтобы улучшить использование SQL.
return new LongValue(RequestUserHolder.getCurrentUser().getOrgId());
}
@Override
public boolean ignoreTable(String tableName) {
// Игнорировать много столов арендаторов
return CollUtil.contains(ignoreTables, tableName);
}
}
Свойства конфигурации следующие:
@ConfigurationProperties(prefix = "ptc.tenant")
@Data
public class TenantProperties {
/**
* Глобально контролировать, включать ли функцию многоклиента
*/
private Boolean enable = Boolean.TRUE;
/**
* нуждаться Игнорировать много столов арендаторов
*
* То есть во всех таблицах функция многоклиента включена по умолчанию, поэтому не забудьте добавить соответствующую tenant_id Поле
*/
private Set<String> ignoreTables = Collections.emptySet();
}
Затем внедрите плагин-перехватчик:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(TenantProperties properties) {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// Необходимо убедиться, что много плагинов клиента находится перед плагином подкачки, это MyBatis-plus правила
if (properties.getEnable()) {
mybatisPlusInterceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantDatabaseHandler(properties)));
}
// Пагинацияплагин mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
Примеры использования следующие: Вот общий случай: SQL для запроса ассоциации пользователей и ролей: getUserList().
<select id="getUserList" resultType="com.plasticene.textile.entity.User">
select u.* from user u
left join user_role r on u.id = r.user_id
<where>
<if test="query.status != null">
and u.status = #{query.status}
</if>
<if test="query.roleId != null">
and r.role_id = #{query.roleId}
</if>
<if test="query.keyword != null">
and ((u.name like concat('%',#{query.keyword},'%')) or (u.mobile like concat(#{query.keyword},'%')))
</if>
<if test="query.startEntryTime != null">
and u.entry_time >= #{query.startEntryTime}
</if>
<if test="query.endEntryTime != null">
<![CDATA[ and u.entry_time <= #{query.endEntryTime}]]>
</if>
</where>
group by u.id
order by u.id desc
</select>
Запустите проект, сначала войдите в систему, а затем используйте токен, чтобы отключить интерфейс и выполнить следующую логику кода:
public PageResult<UserDTO> getList(UserQuery query) {
Page<UserDTO> page = new Page<>(query.getPageNo(), query.getPageSize());
List<User> userList = userDAO.getUserList(page, query);
List<UserDTO> userDTOS = toUserDTOList(userList);
return new PageResult<>(userDTOS, page.getTotal(), page.getPages());
}
Посмотрев на консоль, мы обнаружили:
[1658720355293990912] [DEBUG] [2023-05-17 14:25:25.504] [http-nio-16688-exec-1@23652] com.plasticene.textile.dao.UserDAO.getUserList debug : ==> Preparing: SELECT u.* FROM user u LEFT JOIN user_role r ON u.id = r.user_id AND r.org_id = 3 WHERE u.org_id = 3 GROUP BY u.id ORDER BY u.id DESC LIMIT ?
[1658720355293990912] [DEBUG] [2023-05-17 14:25:25.505] [http-nio-16688-exec-1@23652] com.plasticene.textile.dao.UserDAO.getUserList debug : ==> Parameters: 20(Long)
user
поверхностьuплюсu.org_id=3
этотмного фильтр арендатора,user_role
Тоже самоеплюс Понятно,Описаниемного арендаторплагин Это сработало。
Конечно, если вы хотите игнорировать этоповерхностьuser
,Нам нужно только настроить файл конфигурации следующим образом:
ptc:
tenant:
ignore-tables: user
такuser
поверхностьuникогда большеплюсu.org_id=3
этотмного фильтр арендатора,Но здесь есть деталь, требующая внимания.,потому чтоuser
существоватьMySQLсерединаэто ключевое слово,Поэтому иногда я пишу SQL для стандартизации.,Будет написано следующее:
select u.* from `user` u
left join user_role r on u.id = r.user_id
В настоящее время вы найдете вышеуказанную конфигурациюизпренебрегатьповерхностьuser
не работает,Все еще будетплюсu.org_id=3
этотмного фильтр арендатора,Изучив исходный код, я обнаружил, что мы настроили его выше.измного Процессор арендатораTenantLineHandler
только правильноповерхность名进行Понятно大小写适配,Однако здесьSQLанализироватьпублично заявитьизповерхностьимя: **user
**,Поэтому, если конфигурация не может быть подобрана, она не будет работать.
Конечно, у нас есть возможностьнуждаться Ориентируясь на одинSQLЗаявление без добавлениямного фильтр арендатора,Можно использовать@InterceptorIgnore
аннотация:
public interface UserDAO extends BaseMapperX<User> {
@InterceptorIgnore(tenantLine = "true")
List<User> getUserList(IPage<UserDTO> userPage, @Param("query") UserQuery query);
}
таквызовgetUserList()
Больше не буду добавлятьмного фильтр арендатора Понятно。
通过上面я们知道Понятноэтотмного арендаторплагин Фактически, именно через анализSQL,Затем соедините множество условий фильтра идентификатора арендатора, чтобы добиться улучшения SQL для обеспечения изоляции данных.,анализироватьSQLиз Рамка называется:JSqlParser
,Официальная документация: https://github.com/JSQLParser/JSqlParser/wiki.,Я уже резюмировал статью оДруид анализирует динамический SQL。DruidХОРОШОанализироватьSQL,Мы все знаем, что операторы SQL генерируют синтаксические деревья.,Неизвестно, являются ли они более сильными или слабыми при разборе SQL (особенно сложного SQL).,Вы можете проверить и сравнить самостоятельно,Я дам тебе один здесьJSqlParser
анализировать Ошибкаиз Состояние,Положите верхизSQLзаявлениеuser_role r
Изменить на user_role ur
select u.* from user u
left join user_role ur on u.id = ur.user_id
Вызовите и выполните, как указано выше.getUserList()
, Будет сообщено об ошибке синтаксического анализа:
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: select u.* from user u
left join user_role ur on u.id = ur.user_id
group by u.id
order by u.id desc
at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:39)
at com.baomidou.mybatisplus.extension.parser.JsqlParserSupport.parserSingle(JsqlParserSupport.java:52)
at com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.beforeQuery(TenantLineInnerInterceptor.java:65)
at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:78)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:62)
at com.sun.proxy.$Proxy178.query(Unknown Source)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
... 101 common frames omitted
Caused by: net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: "ur" <K_ISOLATION>
at line 2, column 29.
ясуществоватьmybatis-plus
из官方提Понятно一个issue:https://github.com/baomidou/mybatis-plus/issues/5086,Также есть официальный сопровождающийиз Быстро ответьте и скажите «да»JSqlParser
анализироватьизвопрос,нетmybatis-plus
извопрос~~~,данныйиз Предлагается использовать псевдонимur
Сменить на другойиз,или обновить доJSqlParser
изпоследняя версия。
До сих пор,Наша реализация многотенантной системаданной изоляции,Архитектурный дизайн,И вот как элегантно реализовать глобальную изоляцию данных операций, все готово.,Тоже верноmybati-plus
измного Краткий анализ принципов реализации и процедур исходного кода арендатораплагин,Он также предоставляет доказательства для случаев практического применения и объясняет соответствующие детали. Конечно, права доступа к данным остаются не только на уровне арендатора (компании).,Разрешения на данные крупных многосистем будут контролироваться в соответствии с ролями в организационной структуре бизнеса.,Процедура разрешения данных такая же, как и оценка разрешений меню на основе роли. Поскольку разрешения на данные обычно связаны с бизнесом компании,Более персонализированный,Организационная структура бизнеса каждой компании различна.,Таким образом, изоляцию разрешений на данные в реальных проектах разработки по-прежнему необходимо модифицировать в соответствии с фактическими потребностями.,Но в целом мы можем имитировать реализацию изоляции множества клиентов.,Например, структура бизнес-организации имеет компанию (org_id).,В компании много отделов (dept_id),В отделе имеется множество групп команд (team_id).,В команде много людей (user_id). Разные персонажи могут видеть только разные данные.,Руководители отделов могут видеть данные только своего отдела.,Лидеры групп могут видеть только членов своей группы.,Эти процедуры логики реализации можно элегантно реализовать, имитируя множество плагинов арендатора.,Это то, что я хочу изучить, когда у меня будет время позже.,В будущем будет еще одно резюме плана реализации разрешений на данные.