谈谈怎么做服务隔离

本文探讨了微服务架构中服务隔离的重要性,包括按种类和服务调用方的隔离方法,以及通过线程池和用户分组实现的隔离策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.toutiao.com/a6663999994401391117/?tt_from=mobile_qq&utm_campaign=client_share&timestamp=1551645741&app=news_article&utm_source=mobile_qq&iid=64554186805&utm_medium=toutiao_android&group_id=6663999994401391117

引言

OK,如下图所示

 

谈谈怎么做服务隔离

 

 

谈谈怎么做服务隔离

 

 

 

那显而易见,做服务隔离的目的就是避免服务之间相互影响。毕竟谁也不能说自己的微服务百分百可用,如果不做隔离,一旦一个服务出现了问题,整个系统的稳定性都会受到影响!

因此,做服务隔离是很有必要的。那么怎么隔离呢?

  • 种类隔离
  • 用户隔离

OK,接下来开始细说这两种方式!

正文

种类隔离

其实按照服务种类隔离要从两个纬度来说:即服务提供方服务调用方

假设我们一个系统有三个服务:订单服务,库存服务,支付服务!有如下调用关系:

 

谈谈怎么做服务隔离

 

 

OK,我们先明确一点,上面有几个服务扮演服务提供方的角色?

一共是三个:支付服务(给用户提供服务)、库存服务(给支付服务提供服务)、订单服务(给支付服务提供服务)

有几个服务扮演服务调用方的角色?

一共是一个:支付服务(调用订单服务和库存服务)

针对服务提供方这个角度而言,怎么做隔离呢?

很简单,每一个服务乃至其对应的数据库,给一个服务器部署就行!这样某个服务出现了故障,就不会相互影响,达到一种物理层面上的隔离!

什么,你们公司服务器不够?了解一下《微服务为什么一定要上Docker?

针对服务调用方这个角度而言,怎么做隔离呢?

OK,先明白一点,服务调用方不做隔离会出现什么情况?如图所示

 

谈谈怎么做服务隔离

 

 

一个请求过来,占用支付服务中的Tomcat的一个线程。然后,该线程去顺序调用订单服务和库存服务!那么,一旦库存服务出问题了,这个Tomcat的线程就一直卡在那,无法返回!与此同时,页面上源源不断的有请求过来,会把Tomcat里头的线程池资源全部消耗完毕!对于后面的请求,Tomcat就无法响应!

因此,如果不针对被调服务做服务隔离,一个被调服务出问题,就将导致调用方服务不可用!

那怎么隔离呢?

这里介绍一种线程池隔离方式,给每个微服务都初始化出一个线程池,如下图所示,给订单服务和库存服务都初始化出一个线程池,不使用Tomcat线程池中的线程直接调用,而是用相应线程池中的线程去调用!

 

谈谈怎么做服务隔离

 

 

OK,如果此时库存服务不可用了呢?

库存服务线程池会被迅速塞满,此时后面进来的新请求发现库存服务线程池满啦,于是乎就不去调库存服务,直接返回!如下图所示

 

谈谈怎么做服务隔离

 

 

ps:目前业内有信号量隔离和线程池隔离两种隔离方式,这里举的是线程池隔离!

怎么实现呢?

可以了解一下Hytrix、Sentinel、以及Resilience4j如何和你的项目结合起来使用!Resilience4j只提供信号量隔离!

用户隔离

 

谈谈怎么做服务隔离

 

 

谈谈怎么做服务隔离

 

 

 

OK,我们先明白一点这里的租户和用户不是一个概念!

  • 用户: 一个环境/系统的一个使用者即该环境/系统的一个用户。
  • 租户:用户从某种粒度上被分到若干内,每组成为一个租户。

这里的可以这么理解:用户根据一定的特征去做分组,比如是VIP的一组,不是VIP的一组。又或者北方的用户一组,南方的用户一组。按照自己的业务场景来分组。

那么所谓的用户隔离,就是按照不同的分组形成不同的服务实例。这样某个服务实例挂了,只影响对应分组的用户,而不是全部用户!

有如下三种方式!

  • 方式一:每个租户有独立的服务和独立的数据库
  • 方式二:每个租户有共享的服务和独立的数据库
  • 方式三:每个租户有共享的服务和共享的数据库

下面开始逐个说明

方式一

每个租户有独立的服务和独立的数据库!

这个在生产上一般是这么做,如下所示

 

谈谈怎么做服务隔离

 

 

如图所示,用户在请求的时候会经过网关!网关根据tenant_id识别出对应的服务实例,进行转发。至于用什么当网关,我们用的是Zuul。

方式二

每个租户有共享的服务和独立的数据库!

这个在生产上一般是这么做,如下所示

 

谈谈怎么做服务隔离

 

 

如图所示,用户在请求的时候会经过网关,网关将数据转发给用户服务!用户服务根据tenant_id确定该操作哪一个数据库!

OK,这个时候大家应该有一个疑问,

在项目代码中,怎么确定该操作的数据库?

好,这个就是ORM框架,动态选择数据源的问题!我以国内流行的hibernate和mybatis来进行说明!

(1)hibernate方式

在4.0版本hibenate开始支持多租户架构,即对不同租户使用独立数据库!大家可以搜索一个配置,叫hibernate.multiTenancy。该值有一个value值为

DATABASE:一个租户一个database。 

 

将这项的value值设为DATABASE后,还需要给hibernate.tenant_identifier_resolver配置项赋值,即告诉hibernate,如何解析出tenant_id。以及给hibernate.multi_tenant_connection_provider配置项赋值,即告诉hibernate如何以租户特有的方式获取数据连接!

ps:看不懂的童鞋略过,懂hibernate的童鞋自然懂这个配置!

(2)mybatis方式

mybatis没提供这种多租户架构的支持!我们必须要扩展AbstractRoutingDataSource抽象类,实现多数据源切换!

嫌麻烦?

OK,介绍你一个插件叫mybatis plus可以实现这种动态数据源切换!

API地址都给你贴出来了:

https://mp.baomidou.com/guide/dynamic-datasource.html

ps:我只能给你点一下思路,自己去查。因为具体如何配置,都可以写一篇文章!我很不爱写这种贴配置的文章,觉得含金量不高,所以大家根据我的思路去实现即可!

方式三

每个租户有共享的服务和共享的数据库!

这个在生产上一般是这么做,如下所示

 

谈谈怎么做服务隔离

 

 

如图所示,用户在请求的时候会经过网关,网关将数据转发给用户服务!用户服务根据tenant_id确定操作数据库中的哪一行记录!

老规矩,和你们说一下在ORM中难点在哪!以mybatis为例,所有的sql上都要加一句

AND t.tenant_id = ?

 

是不是觉得很麻烦?怎么解决呢?

(1)hibernate方式

利用hibernate filter配置, OR-Mapping配置文件使用Filter,可以在进行数据查询时自动过滤数据!

如下所示

<class name="User" table="user_tb">
 //省略
 <filter name="tenantFilter" condition="tenant_id = :tenantFilterParam" />
</class>

ps:看不懂的童鞋略过,懂hibernate的童鞋自然懂这个配置!

(2)mybatis方式

mybatis中有一个东西叫做自定义Interceptor,可以拦截出你要执行的sql,然后动态拼上你的租户条件即可!

嫌麻烦?

OK,介绍你一个插件叫mybatis plus可以实现这种多租户的更改,可以动态的解析出sql,增加上条件!

API地址都给你贴出来了:

https://mp.baomidou.com/guide/tenant.html

总结

本文介绍了服务隔离的分类,以及在生产上具体是怎么做的,希望大家有所收获!

 

### 深拷贝与浅拷贝的概念 在 Java 中,深拷贝和浅拷贝是用来描述对象复制行为的术语。浅拷贝是指创建一个新的对象,但是这个新对象中的引用类型字段仍然指向原始对象中的相同数据[^1]。这意味着如果修改了这些引用所指向的数据,则两个对象都会受到影响。 相比之下,深拷贝不仅会创建新的对象实例,还会递归地复制该对象内部所有的引用类型成员变量及其依赖的对象树结构,从而确保源对象与其副本之间没有任何关联[^2]。这样做的好处在于更改其中一个对象的内容不会影响到另一个对象的状态。 --- ### 浅拷贝的特点及实现方式 #### 特点 当执行浅拷贝操作时,基础数据类型会被直接赋值给目标对象;而对于那些属于引用类型的属性来说,则仅仅只是把原来的地址传递过去而已——也就是说两者依旧共同拥有同一个实际存在的实体部分[^3]。 #### 实现方法之一:`Object.clone()` 方法 要完成这种形式下的克隆动作可以通过继承自 `java.lang.Object` 类里的默认无参构造器或者覆盖其定义好的 protected 函数 clone 来达成目的。需要注意的是类必须实现 Cloneable 接口以避免抛出异常,并且父级类别也需要支持同样的功能以便能够正常运作[^1]。 ```java public class ShallowCopyExample implements Cloneable { private int value; private String reference; public Object clone() throws CloneNotSupportedException { return super.clone(); // 使用默认的浅拷贝机制 } } ``` --- ### 深拷贝的特点及实现方式 #### 特点 正如前面提到过的那样,在做深层次上的备份过程中除了简单数值之外还需要考虑如何处理复杂嵌套关系以及动态分配出来的额外空间等问题。最终目的是让每一个组成部分都成为独一无二的存在而不与其他任何地方发生联系[^2]。 #### 实现方法之一:利用序列化(Serializable) 一种常见的做法就是借助于 Java 提供的标准库特性 Serializable 将整个体系转化为字节数组后再反向解析回来形成全新的个体。这种方法虽然效率较低但对于大多数情况而言已经足够用了[^3]。 ```java import java.io.*; public class DeepCopyExample implements Serializable, Cloneable { private static final long serialVersionUID = 1L; private transient int[] array; // 需要注意标记为transient的字段不会被自动序列化 @Override public Object clone() throws IOException, ClassNotFoundException{ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (DeepCopyExample)ois.readObject(); } } ``` 另一种可能的选择是手动编写相应的辅助函数逐一访问各个子节点逐层构建起完整的映射图谱直至达到最底层为止然后再按照相反顺序依次组装回去得到期望的结果集。不过这种方式通常较为繁琐而且容易出错所以除非特殊需求否则一般不推荐采用此策略[^1]。 --- ### 总结对比表 | **比较维度** | **浅拷贝** | **深拷贝** | |--------------------|-----------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | **性能开销** | 较低 | 更高,尤其是面对大型或深层嵌套的对象 | | **安全性/独立性** | 新旧两份资料间存在潜在耦合风险 | 完全隔离,互不影响 | | **适用范围** | 当不需要担心后续变更传播至其他位置时可选用 | 如果希望获得绝对意义上的全新版本则应优先考虑 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值