微服务关键点

简介: 本文主要介绍微服务设计以及开发中需要考虑的关键点

今天分享的是微服务的高并发情形,在前面讲过了微服务的理念以及模块的角度划分等。

在微服务高并发下有几点需要考虑:微服务的划分、高并发、数据DB、中间件或缓存问题、IO性能瓶颈问题、监控问题、自动化部署问题等。

一、微服务的划分

微服务的划分:前面说过了,服务的划分,可以从水平的功能划分,也可从垂直的业务划分,粒度的大小,可以根据当前的产品需求来定位,最关键的是要做到:高内聚、低耦合。

听到高内聚、低耦合这六个字,面试官也许会觉得这小伙子不错,有一定的技术设计的基础。那么接下来,问题来了。什么是高内聚,什么是低耦合呢?所谓高内聚:就是说每个服务处于同一个网络或网域下,而且相对于外部,整个的是一个封闭的、安全的盒子,宛如一朵玫瑰花。

盒子对外的接口是不变的,盒子内部各模块之间的接口也是不变的,但是各模块内部的内容可以更改。模块只对外暴露最小限度的接口,避免强依赖关系。增删一个模块,应该只会影响有依赖关系的相关模块,无关的不应该受影响。

所谓低耦合:从小的角度来看,就是要每个Java类之间的耦合性降低,多用接口,利用Java面向对象编程思想的封装、继承、多态,隐藏实现细节。从模块之间来讲,就是要每个模块之间的关系降低,减少冗余、重复、交叉的复杂度,模块功能划分尽可能单一。

二、高并发

一个公司一旦做大了,就需要考虑兼容、扩展、压力等问题。高并发是一个常见的词语。然后如何才能保证高并发,这是一个问题。

高并发从以下几个方面来讲:

  1. 幂等性
  2. 接口代码的规范性
  3. 操作 DB 的性能
  4. 读写分离操作
  5. 服务的横向扩展
  6. 服务的健壮性(缓存、限流、熔灾)

幂等性:所谓幂等性,就是说一次和多次请求某一个资源时对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意次执行所产生的效果和返回的结果都是一样的。这种场景是一个很有效的实现高并发的情景,设想,用户充值某个会员,在并发情况下,用户由于误操作,或者由于网络、时间等问题导致重试机制的发生时,可能会触发触发多次交易的扣费,这样给用户一个很不好的体验。此时,就需要接口幂等性来解决这类问题。

幂等性解决方案有以下几种:

(1) token机制

(2) 接口逻辑实现幂等性

(3) 数据库层处理实现幂等性

token机制:数据提交时携带token,token放到redis,token有效时间,提交后台后校验token,同时删除token,生成新的token并返回。

接口的幂等性:常见的接口幂等性,是定义接口时,加上参数序列号、来源等,序列号与请求来源联合唯一索引,这样可以有效判断本次请求方与请求的序列号,防止重复的请求。

数据库处理:DB层处理有多种方式,1. 悲观锁,2. 乐观锁,3. 唯一索引、组合唯一索引,4. 分布式锁

悲观锁:所谓悲观锁,是指存在危机意识,事先(查询时)加锁处理,防止事情发生。如:
select * from xxx where id= 1 for update

乐观锁:是指存在乐观心理,只在更新时加锁,乐观锁通常用 version 版本号来控制如:update xxx set name=#name#,version=version+1 where version=#version#

也可以通过条件限制,这里就使用了组合唯一索引来处理,如:update xxx set name=#name#,version=version+1 where id=#id# and version=#version#

分布式锁:通过 redis、zookeeper 来设置分布式锁,当插入或更新数据时,获取分布式锁,然后做操作,之后释放锁。

接口的规范性:接口的性能如何,最终还是跟接口的实现逻辑有关,比如代码规范,逻辑实现等,尤其是业务逻辑复杂的情况下,这点需要注意的。

操作DB:对于业务的持久层,用的比较多的就是mybatis、hibernate,还有可能是JPA,无论是哪个,最终都是通过工厂类注入 bean,最后执行 SQL 来操作 DB。所以这里尤为重要的是 SQL 的写法,SQL的优化决定着操作DB的时间以及效果,如果写得不好的话,则会导致死循环,或死锁,或内存溢出。另外测试时,使用真实、规范的数据进行测试,并在测试时不要局限于相同的数据,最后就是并发压测了。

读写分离:当服务足够多,数据足够多时,有可能读与写的占比为:10:1,此时读写应该分离,这样可以有效减少因为读的频繁操作导致的写的性能下降。常见的读写分离的方法有:采用mycat中间件方式、amoeba直接实现读写分离、手动修改mysql操作类直接实现读写分离和随机实现的负载均衡,权限独立分配、mysql-proxy(还是测试版本,时间消耗有点高)。

服务的横向扩展:对于服务的请求越来越多时,此时需要对服务进行多节点部署,这样减少单机带来的服务负载压力。

服务的健壮性:服务的健壮性包括缓存、限流、熔灾。

对于缓存,大家都知道,有常见的许多中间件如:redis、kafka、RabbitMq、zookeeper。对于一些session等常用redis来缓存、共享。对于一些大一点的数据如果嫌弃加载慢,也可以采用缓存机制来解决。

什么叫限流呢?很好理解,就是限制节点的流量,限制服务的请求数。那么如何做到限流呢?常用的限流算法比如有计数器算法、令牌桶算法、漏桶算法。有几种方式:利用 springcloud 组件 zuul 来对请求进行限流,主要是通过谷歌提供的 RateLimiter 结合一些限流算法来限流比较常用。利用 redis 同样可以做限流算法的,甚至可以利用 nginx 直接作计数限流,可以对请求速率进行限制、对每个 ip 连接数量进行限制、对每个服务的连接数量进行限制。如:

#对请求速率进行限制
limit_req_zone $binary_remote_addr zone=req_one:20m rate=12r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
#对每个ip连接数量进行限制
limit_conn_zone $server_name zone=perserver:20m;
#对每个服务的连接数量进行限制
server{
  listen 80;
  location / {
    proxy_pass http://ip:port;
    limit_req zone=req_one burst= 80 nodelay;
    limit_conn addr 20;
  }
}

其实在Springboot2.x中,推出自己的Spring-Cloud-Gateway来作网关,同时Spring-Cloud-Gateway中提供了基于Redis的实现来达到限流的目的。

对于熔灾,或者说熔断,这个在实际的业务当中是很有必要的。比如:用户在某一商城秒杀某一件物品,或在某米商城上抢购某一部手机,在准点抢购时,发现人很多,请求很多,这时,主要是需要有限流机制,同时也需要有熔灾(熔断),给用户留下一个很好的体验的感觉。当用户在点击抢购按钮后,如果当前的请求数很多,需要用户等待,这是需要给一个友好的界面让用户去等待,而不是直接给用户提示请求失败,或者报异常,这样的红色抛出是一个非常不好的事情,用户可能会骂街的,下次也不会逛了。

Spring-Cloud-Gateway作网关时,过滤器时使用 HystrixGatewayFilterFactory 来创建一个 Filter 实现基于 Route 级别的熔断功能。

三、中间件或缓存问题

随着用户的越来愈多,所有的服务压力也会指数型递增,这时候缓存是一个很好的减轻服务压力的方式。这样可以有效缓冲请求对服务的负载压力。常见的缓存可能是Redis、MQ(RabbitMQ、RocketMQ)、Kafka、ZooKeeper等。

Redis 一般主要做 session 或用户信息的缓存,实现多机中 session 的共享。也会用来作分布式锁,在分布式高并发下实现锁的功能,例如实现秒杀、抢单等功能。还会被用作一些订单信息的缓存,防止大量的订单信息被积压而导致服务器的负载很高。总之,Redis 常被用来作为一种缓冲剂使用。

ZooKeeper 也是经常会存储海量数据,例如 Hadoop 中,在使用 YARN 作资源调度时,采用 ZooKeeper 来存储海量的状态机状态以及任务的信息(包括历史信息)。

四、IO性能瓶颈问题

每个行业的业务也许不同,但是大部分行业是存在存储的,说到最直接的数据库,其他的包括电商、物流、AI算法等。电商的存储在于页面的数据与后端存储的交互;物流的存储在于物流信息、物品信息的存储;AI算法在于数据集、模型、镜像文件、训练代码等的存储。整的来说,不管是什么存储,只要跟磁盘、硬盘有关系,就会涉及到IO的问题。

对于IO,在阻塞模式下,经常有线程不够用,就算使用线程池复用线程也无济于事;阻塞I/O模式下,会有大量的线程被阻塞,一直在等待数据,这个时候的线程被挂起,只能干等,CPU利用率很低,即导致系统的吞吐量差,内存占用很高,甚至导致内存溢出。如果网络I/O堵塞或者有网络抖动或者网络故障等,线程的阻塞时间可能很长。整个系统也变的不可靠。

什么是NIO?java.nio 是指JDK 1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。NIO核心API:Channel、Buffer、Selector。

Channel:NIO的通道类似于流,但有些区别:1. 通道可以同时进行读写,而流只能读或者只能写,2. 通道可以实现异步读写数据,3. 通道可以从缓冲读数据,也可以写数据到缓冲。

缓存Buffer:缓冲区本质上是一个可以写入数据的内存块,然后可以再次读取,该对象提供了一组方法,可以更轻松地使用内存块,使用缓冲区读取和写入数据通常有这四个步骤:

  1. 写数据到缓冲区;
  2. 调用buffer.flip()方法;
  3. 从缓冲区中读取数据;
  4. 调用buffer.clear()或buffer.compat()方法

当向Buffer写入数据时,Buffer会记录下写了多少数据,一旦要读取数据,需要通过flip()方法将Buffer从 write 模式切到 read 模式,在 read 模式下可以读取之前写入到Buffer的所有数据,一旦读完了所有的数据,就需要清空缓冲区,让它可再被写入。

Selector:一个组件,可以检测多个NIO channel,看看读或者写事件是否就绪。多个Channel以事件的方式可以注册到同一个Selector,从而可以用一个线程处理多个请求。

当你调用Selector的select()或者 selectNow() 方法时它只会返回有数据读取的SelectableChannel的实例。

另外,就是利用NIO实现大文件的分片处理。

五、监控问题

随着业务规模的不断扩大,面临着服务数量不断增加、线上环境日益复杂、服务依赖错综复杂等运维痛点,服务依赖自动梳理、调用实时追踪、异常明细分析、调用链路追踪、实时容量规划、问题根因分析等基本的运维诉求及解决方案就尤其重要。

监控的目的主要包括性能监控、服务的健壮性、运维管理、问题自动分析、动态扩容等,监控的方式也有很多,比如Springcloud自己提供的Dashboard,实现服务的链路跟踪。

K8S的Dashboard,可以追踪看到每个服务pod的状态以及各项服务的系统指标。除了k8s的基本监控外(pod运行状况、占用内存、cpu)。为了对微服务项目中的各种参数线程池、TPS、QPS、RT、系统负载、thread、mem、class、tomcat、gc、等jvm指标进行监控。可以采用基于 K8S 的 promethus job 对业务的metrics指标采取收集。同时由于 promethus 支持 grafana前端UI界面。

六、自动化部署问题

随着服务的越来越多,服务的运维管理也是一个麻烦事,如果有一套自动化部署的机制,则可以一键触发自动部署所有微服务,那绝壁是很好的一件事。这块可以参考文章:微服务自动化部署CI/CD 一文,很详细的介绍了自动化部署的实战。

相关文章
BeanUtils的忽略字段工具类
BeanUtils的忽略字段工具类
738 0
|
SQL 存储 NoSQL
MongoDB:21-MongoDB-自增Id
MongoDB:21-MongoDB-自增Id
MongoDB:21-MongoDB-自增Id
|
监控 前端开发 JavaScript
不了解 QPS、TPS、RT、并发数、吞吐量,劝你简历别写熟悉高并发
分布式、微服务、Service Mesh目前都是大家耳熟能详的词语了,现在随便一个互联网公司说出来大家都是在搞微服务。 但我们搞来搞去,怎么样来衡量一个应用当前的状态到底是怎么样的?到底需不需要扩容?是需要横向扩容还是进行项目重构?
11321 2
|
9月前
|
JavaScript Unix Linux
nvm与node.js的安装指南
通过以上步骤,你可以在各种操作系统上成功安装NVM和Node.js,从而在不同的项目中灵活切换Node.js版本。这种灵活性对于管理不同项目的环境依赖而言是非常重要的。
2786 11
|
10月前
|
供应链 监控 数据挖掘
电商API接口:开启供应链管理高效协同与价值提升新通道
电商API接口作为连接电商平台与供应链各环节的关键桥梁,通过实时库存管理、订单自动化、物流追踪、供应商协同等应用,显著提升供应链效率与协同能力,助力企业实现数字化转型与高效运营。
|
12月前
|
人工智能 运维 安全
热门 MCP Server一键部署
本文探讨了MCP(Model Context Protocol)的发展及其云上托管的趋势。尽管MCP协议在2024年发布时未引起广泛关注,但随着Cursor和Manus等平台的集成,以及OpenAI对其Agent SDK的支持,MCP逐渐成为行业标准。然而,本地部署的MCP Server存在效率低、扩展复杂等问题,难以满足企业级需求。函数计算(FC)作为Serverless算力的代表,提供一键托管开源MCP Server的能力,具备成本效益、弹性扩展、简化运维等优势,解决了传统托管的核心痛点。文章还提供了多个开源MCP Server的一键部署链接,助力开发者快速上手。
热门 MCP Server一键部署
seata是怎么进行分布式事务控制的
seata是怎么进行分布式事务控制的
|
开发工具
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
阿里云点播服务web播放器sdk,短剧视频类App实现参考。仿抖音 仿陌陌 短视频 无限滑动播放 视频流。无uniapp video 原生组件的层级、遮挡、覆盖问题,适合与不同功能视图组合使用,实现丰富的应用功能。
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
|
消息中间件 Dubbo Java
微服务
【10月更文挑战第1天】微服务是一种将大型应用分解为小型、独立服务的设计理念,每个服务负责单一业务功能,独立部署、运行,通过轻量级通信机制(如HTTP API或RPC)互联。相比单体应用,微服务提高了部署效率、团队协作效能和系统可用性,但也增加了系统复杂性、通信开销和数据一致性管理的难度。实现微服务架构涉及服务拆分、服务发现、配置管理、服务治理、数据一致性、安全性、监控与日志、持续集成与部署等多个方面。
509 4