目录
2.1.2 MyBatis-Plus实现多租户架构的核心原理
2.2.2 Spring Cloud Alibaba服务订阅负载均衡的核心原理
2.3.2 基于Spring Cloud Alibaba的配置信息动态变更的核心原理
2.4.2 基于Spring Cloud ALibaba,动态加载和持久化高可用流量防护规则的原理
2.6.2 Spring Cloud Alibaba是如何封装RocketMQ的?
2.7.2 Spring Cloud Gateway如何整合Redis,做网关限流
笔者的畅销书 Spring Cloud Alibaba微服务架构实战派(上下册)出版了,京东、当当和天猫已经开始预售。
1 写书缘由
笔者也是机缘巧合,才会开启自己的写书之路。
在写这本书之前,我先后在两家杭州的“独角兽”公司担任技术负责人,并推进公司核心业务的“中台化”改造。在落地业务中台和技术中台的过程中,督促并指导开发人员统一使用Spring Cloud Alibaba作为中台服务最底层的基础框架。为了快速推进业务服务Spring Cloud Alibaba化的进度,我冲在业务的第一线,收集和整理开发人员在使用Spring Cloud Alibaba过程中反馈的技术问题,并提供有效的技术解决方案,直至项目落地。
我每周都会做技术复盘,通过分析大量的问题总结出一个结论:开发人员反馈的问题大部分都是由于Spring Cloud Alibaba使用不合理所造成的。也就是说,很多开发人员并不了解Spring Cloud Alibaba的原理及如何落地实践。于是,我就产生了把我这几年落地Spring Cloud Alibaba的经验通过图书的方式输出的想法。
2 本书上册核心内容
2.1 Spring Cloud Alibaba基础实战
2.1.1 主要内容
Spring Cloud Alibaba“牛刀小试”,包括:使用Spring Cloud Alibaba作为基础框架实现乐观锁、实现多数据源和实现SQL语句中表名的动态替换。
【实例】用Maven和Spring Cloud Alibaba实现多环境部署,学习完本章内容,读者可以快速的使用配套源码,搭建可扩展的多环境运维部署环境。
【实例】用“MyBatis-Plus + Spring Cloud Alibaba”实现多租户架构,学习完本章内容,读者可以快速的使用配套源码,实现微服务架构中的多租户架构。
2.1.2 MyBatis-Plus实现多租户架构的核心原理
熟悉Mybatis原理的开发应该都知道它的拦截器机制,Mybatis会使用注解@Intercepts去标注一个拦截器,并在Mybatis框架启动的过程中,扫描当前Spring IOC容器中被注解@Intercepts标记的拦截器。
第一步:MyBatis-Plus定义一个全局拦截器MybatisPlusInterceptor类,如下所示。
//通过注解@Intercepts,将MyBatis-Plus和Mybatis绑定在一起
@Intercepts(
{
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
@Signature(type = StatementHandler.class, method = "getBoundSql", args = {}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class MybatisPlusInterceptor implements Interceptor {
@Setter
private List<InnerInterceptor> interceptors = new ArrayList<>();
@Override
public Object intercept(Invocation invocation) throws Throwable {
//遍历内部拦截器列表,并执行InnerInterceptor.beforeUpdate()
}
}
第二步:MyBatis-Plus定义一个内部多租户拦截器TenantLineInnerInterceptor类,如下所示。
public class TenantLineInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
private TenantLineHandler tenantLineHandler;
//使用代理和反射,生成一个租户处理器TenantLineHandler
@Override
public void setProperties(Properties properties) {
PropertyMapper.newInstance(properties)
.whenNotBlack("tenantLineHandler", ClassUtils::newInstance, this::setTenantLineHandler);
}
...
}
2.2 分布式服务治理——基于Nacos
2.2.1 主要内容
认识分布式服务治理
了解主流的注册中心
将应用接入Nacos 注册中心
用“NacosNamingService类 + @EnableDiscoveryClient”实现服务的注册/订阅
用“Ribbon + Nacos Client”实现服务发现的负载均衡
用CP模式和AP模式来保持注册中心的数据一致性
用缓存和文件来存储Nacos的元数据
用Nacos Sync来实现应用服务的数据迁移
2.2.2 Spring Cloud Alibaba服务订阅负载均衡的核心原理
Spring Cloud Alibaba定义了一个加载负载均衡规则的类NacosRule,它继承了ribbon-loadbalancer项目中的AbstractLoadBalancerRule类,具体如下所示:
public class NacosRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Override
public Server choose(Object key) {
try {
//获取Nacos的集群名称
String clusterName = this.nacosDiscoveryProperties.getClusterName();
//获取Group的名称
String group = this.nacosDiscoveryProperties.getGroup();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();
//实例化一个Nacos Client的服务注册中心的名称服务对象NamingService
NamingService namingService = nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties());
//获取指定服务名称的所有健康的服务实例信息
List<Instance> instances = namingService.selectInstances(name, group, true);
if (CollectionUtils.isEmpty(instances)) {
LOGGER.warn("no instance in service {}", name);
return null;
}
...
//使用负载均衡算法,均衡的选举一个服务实例,并返回一个NacosServer对象,完成负载均衡
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
return new NacosServer(instance);
}
catch (Exception e) {
LOGGER.warn("NacosRule error", e);
return null;
}
}
}
Spring Cloud Alibaba复用了Nacos提供的服务负载均衡算法,当然开发人员可以自己实现一个负载均衡算法。Nacos的服务负载均衡算法如下所示。
public class Balancer {
//按照随机权重,进行服务的负载均衡
protected static Instance getHostByRandomWeight(List<Instance> hosts) {
NAMING_LOGGER.debug("entry randomWithWeight");
if (hosts == null || hosts.size() == 0) {
NAMING_LOGGER.debug("hosts == null || hosts.size() == 0");
return null;
}
NAMING_LOGGER.debug("new Chooser");
List<Pair<Instance>> hostsWithWeight = new ArrayList<Pair<Instance>>();
//过滤掉不健康的服务实例
for (Instance host : hosts) {
if (host.isHealthy()) {
hostsWithWeight.add(new Pair<Instance>(host, host.getWeight()));
}
}
NAMING_LOGGER.debug("for (Host host : hosts)");
Chooser<String, Instance> vipChooser = new Chooser<String, Instance>("www.taobao.com");
//刷新服务实例的权重信息,这些权重信息可以通过Nacos的UI控制台,或者Open API动态的修改,并实时的生效
vipChooser.refresh(hostsWithWeight);
NAMING_LOGGER.debug("vipChooser.refresh");
//执行负载均衡算法
return vipChooser.randomWithWeight();
}
...
}
//负载均衡算法选择器
public class Chooser<K, T> {
public T randomWithWeight() {
Ref<T> ref = this.ref;
//产生随机种子
double random = ThreadLocalRandom.current().nextDouble(0, 1);
//采用二分查找,获取下标编号
int index = Arrays.binarySearch(ref.weights, random);
if (index < 0) {
index = -index - 1;
} else {
return ref.items.get(index);
}
if (index >= 0 && index < ref.weights.length) {
if (random < ref.weights[index]) {
return ref.items.get(index);
}