服务端⾼并发分布式结构演进之路-CSDN博客文章浏览阅读976次,点赞11次,收藏9次。在进行技术学习过程中,由于大部分读者没有经历过一些中大型系统的实际经验,导致无法从服务端⾼并发分布式结构演进之路-----在进行技术学习过程中,由于大部分读者没有经历过一些中大型系统的实际经验,导致无法从全局理解一些概念,所以本文以一个"电子商务"应用为例,介绍从一百个到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,方便大家对后续知识做深入学习时有一定的整体视野。https://blog.csdn.net/qq_45875349/article/details/139639760?spm=1001.2014.3001.5501对于负载均衡的概念还不了解的可以我的上面这篇博客的 2.3 小节去了解一下。
负载均衡(Load Balancing)是一种将网络流量或计算任务分配到多个服务器或资源上的技术,目的是优化资源使用、最大化吞吐量、减少响应时间、提高系统的可靠性和可用性。通过平衡工作负载,负载均衡可以避免单个服务器或资源过载,从而提高整个系统的性能和稳定性。
1问题描述
我们的订单服务每次被调用的时候,都会通过Eureka的服务发现去获取到商品服务的实例列表。如果仅仅单纯获取某一个商品服务,那么流量就一直压在一台服务器之上。如果⼀个服务对应多个实例呢? 流量是否可以合理的分配到多个实例呢?
2场景复现
为了更好的演示效果,我们可以多启动几个product-service的实例。【不修改代码,修改修改配置,调整端口号并启动服务】 项目名名称和端口号可以自己设定,两者最好有对应关系,方便观察。
apply之后,现在IDEA的Service窗⼝就会多出来⼀个启动配置, 右键启动服务就可以,同样的操作, 再启动1个实例, 共启动3个服务。启动后观察Eureka,可以看到product-service下有三个实例:
现在我们来访问订单服务,然后订单服务远程调用商品服务。
结果发现,都是请求多次访问, 都是同⼀台机器.
3 解决方案
先来看看现在的带代码,为什么会出现这样的现象,也就是我们再去获取服务的时候,使用的方式是不对的,这样每次获取到的服务大概率是相同的。
好的,那么我就来来写代码。修改远程调用的逻辑
package com.guan.order.service;
import com.guan.order.mapper.OrderMapper;
import com.guan.order.model.OrderInfo;
import com.guan.product.model.ProductInfo;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Resource
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
private static AtomicInteger atomicInteger = new AtomicInteger(1);
private static List<ServiceInstance> instances;
@PostConstruct
public void init() {
//根据应⽤名称获取服务列表
instances = discoveryClient.getInstances("product-service");
}
public OrderInfo selectOrderByID(Integer orderID) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderID);
// 从Eureka获取服务列表
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
//有多个服务,根据轮询获取
int index = atomicInteger.getAndIncrement() % instances.size();
String uri = instances.get(index).getUri().toString();
String url = uri + "/product/" + orderInfo.getProductId();
log.info("远程调用url:{}", url);
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
现在在来观察服务。
4 什么是负载均衡
5 负载均衡的一些实现
上面的例子中,我们只是简单的对实例进行了轮询,但真实的业务场景会更加复杂,比如根据机器的配置进行负载分配,配置高的分配的流量高,配置低的分配流量低等.也就是“能者多劳”。
服务多机部署时,开发人员都需要考虑负载均衡的实现,所以也出现了一些负载均衡器,来帮助我们实现负载均衡.
负载均衡分为服务端负载均衡和客户端负载均衡
服务端负载均衡
在服务端进行负载均衡的算法分配.
比较有名的服务端负载均衡器是Nginx.请求先到达Nginx负载均衡器,然后通过负载均衡算法,在多个服务器之间选择一个进行访问.
客户端负载均衡
在客户端进行负载均衡的算法分配.
把负载均衡的功能以库的方式集成到客户端,而不再是由一台指定的负载均衡设备集中提供,比如Spring Cloud的Ribbon,请求发送到客户端,客户端从注册中心(比如Eureka)获取服务列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问.Ribbon是Spring Cloud早期的默认实现,由于不维护了,所以最新版本的Spring Cloud负载均衡集成的是Spring Cloud LoadBalancer(Spring Cloud官方维护)