深入探讨微服务架构中的同步通讯机制

笑看天下无敌手  金牌会员 | 2024-8-9 09:09:36 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 974|帖子 974|积分 2922

微服务架构是一种设计方法,将应用步伐分别为一组小型服务,每个服务在独立的历程中运行,通常根据业务能力举行组织。这些服务通过多种通讯方式交互,以实现整个应用的功能。今天我们着重介绍同步通讯,关于异步通讯和消息队列(MQ)等内容将在后续解说。
这里所指的通讯,是指我们在客户端内部举行的服务间通讯,而非通过调用外部的Web服务举行访问。好的,让我们开始。
负载均衡

服务端负载均衡

在深入探讨客户端负载均衡之前,我们首先应该对服务端负载均衡有一个更加深入的了解,例如nginx组件。如今让我们来仔细看一下:

nginx通常用来对我们服务器内部举行负载转发请求,而客户端则是在各个服务内部举行负载分担。
客户端负载均衡

在Spring Cloud中,例如使用Ribbon时,客户端会维护一个服务器所在列表,在发送请求之前通过负载均衡算法选择一个服务器举行访问。这种方式被称为客户端负载均衡,由于它在客户端内部就完成了负载均衡算法的分配工作。

同步通讯

一般环境下,我们在举行HTTP请求时常会借助各种工具类。我信赖大家之前大概经常自行封装httputils等类,或者使用其他官方提供的工具。比如,今天我们来解说一下RestTemplate工具类。它实在就是一个用来发送HTTP请求的工具,只不过它在此基础上多做了一些额外的工作。接下来我们先看一下它的基本用法。
RestTemplate

RestTemplate 是由 Spring 框架提供的一个功能强盛的类,专门用于举行同步客户端的 HTTP 访问。它的设计旨在简化使用 HTTP 客户端举行 REST 调用的复杂流程,并提供了丰富的方法和功能来处理各种 HTTP 请求和相应。
创建 RestTemplate 实例

首先,你需要创建一个 RestTemplate 的实例。这可以通过直接实例化或使用Spring的主动装配来完成。
  1. import org.springframework.web.client.RestTemplate;
  2. RestTemplate restTemplate = new RestTemplate();
复制代码
发送请求

使用 RestTemplate 发送一个GET请求并获取相应体。
  1. String url = "http://example.com/api/resource";
  2. String result = restTemplate.getForObject(url, String.class);
  3. System.out.println(result);
复制代码
发送一个POST请求,通常包含请求体。
  1. String url = "http://example.com/api/resource";
  2. Map<String, Object> requestMap = new HashMap<>();
  3. requestMap.put("key1", "value1");
  4. requestMap.put("key2", "value2");
  5. HttpHeaders headers = new HttpHeaders();
  6. headers.setContentType(MediaType.APPLICATION_JSON);
  7. HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestMap, headers);
  8. String response = restTemplate.postForObject(url, entity, String.class);
  9. System.out.println(response);
复制代码
看到这里,你大概会想到,这与微服务间的通讯有什么关系呢?难道不只是简单的 HTTP 调用吗?让我们继承深入探讨。
LoadBalanced注解

微服务架构中通常会涉及到注册中心。今天我们专注讨论微服务间的通讯,而不深入解说注册中心,例如 Nacos 是如何管理所有微服务的注册以及如何使一个服务节点发现其他服务节点的。假设我们已经获得了其他服务节点的 IP 所在,你大概会想直接将上述示例中的域名替换为 IP 所在,但是在面对保证高可用的多节点微服务时,直接在代码中写死 IP 所在将会带来灾难性的后果。所有的 IP 所在应当由一个统一组件举行管理和选择。
因此,Ribbon应运而生。一般环境下,如果在项目的pom文件中集成了Nacos依靠,通常会默认包含Ribbon组件,因此不需要单独配置pom文件来引入Ribbon依靠。
基本用法

如前所述,一种方法是直接实例化对象,另一种则是通过Spring容器举行管理和注入。
  1. @Configuration
  2. public class RestConfig {
  3.     @Bean
  4.     @LoadBalanced
  5.     public RestTemplate restTemplate() {
  6.         return new RestTemplate();
  7.     }
  8. }
复制代码
这样一来,当需要使用时,就可以轻松地举行服务调用操作。
  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4.     @Autowired
  5.     private RestTemplate restTemplate;
  6.     @RequestMapping(value = "/findOrderByUserId/{id}")
  7.     public R findOrderByUserId(@PathVariable("id") Integer id) {
  8.         String url = "http://mall-order/order/findOrderByUserId/"+id;
  9.         R result = restTemplate.getForObject(url,R.class);
  10.         return result;
  11.     }
  12. }
复制代码
注意,mall-order可不是我们说的网站域名,而是我们配置在nacos等注册中心的服务名。
添加了LoadBalanced注解后,RestTemplate会主动注入所需的依靠项,具体的实现可以通过检察源代码来了解。
源码分析

关于Spring的主动配置,我之前在解说Spring时提到过很多次,这里就不再具体睁开了。我们可以看到它实现了SmartInitializingSingleton接口,因此,想要使用负载均衡功能,必须等到所有的bean都加载完毕才气举行。

好的,我们可以看到,在这里他向RestTemplate类添加了一个拦截器。接下来,我们可以探究一下这个拦截器具体做了什么。我选择不逐步展示整个过程是由于这并不须要。首先,这样做记不住,其次,就像处理业务一样,我们只关注最终走到了哪一个数据表。我们只需记着他一定会颠末的谁人十字路口即可,这样可以减轻大脑的负担。
  1.     public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
  2.             final ClientHttpRequestExecution execution) throws IOException {
  3.         final URI originalUri = request.getURI();
  4.         String serviceName = originalUri.getHost();
  5.         Assert.state(serviceName != null,
  6.                 "Request URI does not contain a valid hostname: " + originalUri);
  7.         return this.loadBalancer.execute(serviceName,
  8.                 this.requestFactory.createRequest(request, body, execution));
  9.     }
复制代码
实在,看到这一点,不用猜也能明白。一旦获取了serviceName,也就是我们之前定义的微服务名,它会在execute方法中被替换为真正的IP所在,然后最终调用HTTP请求完成整个过程。让我们来仔细看一下源代码吧。
  1.     public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
  2.             throws IOException {
  3.         ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
  4.         Server server = getServer(loadBalancer, hint);
  5.         if (server == null) {
  6.             throw new IllegalStateException("No instances available for " + serviceId);
  7.         }
  8.         RibbonServer ribbonServer = new RibbonServer(serviceId, server,
  9.                 isSecure(server, serviceId),
  10.                 serverIntrospector(serviceId).getMetadata(server));
  11.         return execute(serviceId, ribbonServer, request);
  12.     }
复制代码
所有的负载均衡规则都实如今getServer方法中,我们不再深入追踪了。在这一步,我们已经找到了要调用的微服务。如果你还有疑问,我们可以看一下Server类的具体实现,就能明白了。

最终就交给HTTP调用即可。然而,仅仅这样还不够。难道你会愿意在每个服务中编写这种无关紧要的服务调用代码吗?这会非常繁琐,不光增长了业务逻辑的复杂性,还让人感到不便。幸运的是,有了Spring Cloud OpenFeign,这一切变得轻松很多。OpenFeign的出现正是为了办理这种服务间通讯的问题,它将这些繁琐的细节封装起来。
然而,要注意,这种封装并不影响通讯的实质。下次我们将具体讨论Spring Cloud OpenFeign与Dubbo调用组件的区别与使用方法。
总结

今天我们专注于微服务之间的网络通讯。可以清楚地看到,框架的最终目的是使步伐员能够更专注于业务逻辑,而不是被迫写各种无关紧要的代码。总结一下,只管我们使用了框架和各种抽象,但最终仍旧是通过HTTP来举行调用。不同的是,在实际调用之前,我们引入了一个拦截器来实现微服务的负载均衡。这个拦截器中实现了各种均衡算法,最终确定真实的IP所在和端口,以便举行访问并获取所需的数据。
我是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交换与分享,对开源社区布满热情。同时也是一位掘金优秀作者、腾讯云内容共创官、阿里云专家博主、华为云云享专家。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表