SpringCloud微服务的调用与远程调用测试示例

Java
328
0
0
2023-04-28
目录
  • 微服务相关简述
  • 微服务架构中的重要角色
  • 微服务架构
  • 分布式远程调用
  • RESTFUL接口
  • 资源
  • 表现层
  • 状态转化
  • RPC
  • CAP原理
  • springCloud简述
  • 服务注册与发现
  • 负载均衡
  • 熔断
  • 链路追踪
  • API网关
  • 测试模拟远程调用
  • 创建父工程remote_call模块
  • 创建子模块server_a
  • 创建子模块server_b

微服务相关简述

微服务架构中的重要角色

  • 服务调用者:可以暂时认为是与用户交互的角色(因为存在微服务之间的调用),可以根据该用户的类型将其赋予不同的服务调用权限,通过一次http请求访问调用对应的微服务获取想要的数据。
  • 微服务:你可以将一个具备对外开放接口的springboot项目作为一个服务,当然这个服务最基本的功能必须要能够具备数据库数据交换与持久的能力。这样的一个springboot项目可以将其称之为一个微服务。通常在一个微服务架构中,微服务A可能会存在多个(为了保证软件系统的可用性)。
  • 服务注册中心:相当于一个微服务架构中的组件通讯录,不仅可以保存注册信息,还可以进行服务的心跳监听(健康检测)。
  • 熔断:当下游微服务业务访问量剧增的时候,为了防止上层微服务收到其影响,采用熔断器对其进行强行熔断。
  • 链路追踪:为了记录复杂的微服务之间调用的过程,采用链路的追踪确保能够清晰的看到微服务之间调用的过程可以很好的排查和治理微服务。
  • API服务网关:统一管理微服务的接口。

微服务架构

优点:通过服务的原子化拆分,以及微服务的独立打包、部署和升级,小团队的交付周期将缩短,运维成本也将大幅度下降,微服务遵循单一原则。微服务之间采用Restful等轻量协议传输。

缺点:微服务过多,服务治理成本高,不利于系统维护。分布式系统开发的技术成本高(容错、分布式事务等)。

分布式远程调用

我们知道,两个springboot项目如果放在同一台主机上运行,那么我们可以通过HttpClient或者Okhttp等等方式去创建一个Http客户端实现由这一个springboot项目去访问另一个springboot项目的过程,那么如果这两个项目不在同一个主机上呢?在局域网中?在公网中?其实大同小异,只要我们能够通过http协议的方式远程访问到对应的API接口就可以实现两个项目之间的调用。这就是一次远程调用,当然这里指的是基于Http的远程调用方式。

在微服务架构中,通常存在多个服务之间的远程调用的需求。远程调用通常包含两个部分:序列化和通信协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等,目前主流的远程调用技术有基于HTTP的RESTFUL接口以及基于TCP的RPC协议。

RESTFUL接口

REST,即Representational State Transfer的缩写,如果一个架构符合REST原则,就称它为RESTful架构。

资源

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资 源"(Resources)的"表现层"。

表现层

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。

状态转化

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。综合上面的解释,我们总结一下什么是RESTful架构:每一个URI代表一种资源;客户端和服务器之间,传递这种资源的某种表现层;客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

RPC

RPC(Remote Procedure Call ) 一种进程间通信方式。允许像调用本地服务一样调用远程服务。RPC框架的主要目标就是让远程服务调用更简单、透明。RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/JSON/二进制)和通信细节。开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节和调用过程。RPC 框架作为架构微服务化的基础组件,它能大大降低架构微服务化的成本,提高调用方与服务提供方的研发效率,屏蔽跨进程调用函数(服务)的各类复杂细节。让调用方感觉就像调用本地函数一样调用远端函数、让服务提供方感觉就像实现一个本地函数一样来实现服务。

CAP原理

  • Consistency(一致性):数据一致更新,所有数据的变化都是同步的
  • Availability(可用性):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求
  • Partition tolerance(分区容忍性):某个节点的故障,并不影响整个系统的运行

任何情况下一个分布式架构系统下CAP三者只能满足其中两个。

springCloud简述

springcloud是常见的微服务框架之一,作为spring家族的金字塔尖,它并不是一项新的项目,而是集成市面上大部分的分布式微服务工程中优质的项目,具备各种优秀的组件使得在一个微服务工程中程序员可以直接通过springcloud这一个包去调用各种优质的jar包。

服务注册与发现

服务注册:服务实例将自身服务信息注册到注册中心。这部分服务信息包括服务所在主机IP和提供服务的Port,以及暴露服务自身状态以及访问协议等信息。

服务发现:服务实例请求注册中心获取所依赖服务信息。服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务。

负载均衡

负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性

熔断

熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。

链路追踪

随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要对一次请求涉及的多个服务链路进行日志记录,性能监控即链路追踪

API网关

随着微服务的不断增多,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信可能出现:

  • 客户端需要调用不同的url地址,增加难度
  • 在一定的场景下,存在跨域请求的问题
  • 每个微服务都需要进行单独的身份认证

针对这些问题,API网关顺势而生。API网关直面意思是将所有API调用统一接入到API网关层,由网关层统一接入和输出。一个网关的基本功能有:统一接入、安全防护、协议适配、流量管控、长短链接支持、容错能力。有了网关之后,各个API服务提供团队可以专注于自己的的业务逻辑处理,而API网关更专注于安全、流量、路由等问题。

测试模拟远程调用

构想一下思路:我们建立同一个父工程remote_call下的两个子模块server_a与server_b,要将两个子模块跑起来,之后利用server_b模块去调用server_a的一个API接口并获取它的返回信息展示在页面上,server_b 去通信 server_a 的方法不限,我这里采取RestTemplate这个spring为我们封装好的Http客户端工具。

创建父工程remote_call模块

只需要配置它的pom文件即可,这里我的remote_call模块的父工程已经将用到的SpringBoot依赖都导入且统一管理了。

remote_call

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <parent> 
        <artifactId>spring_cloud</artifactId> 
        <groupId>org.example</groupId> 
        <version>1.0-SNAPSHOT</version> 
    </parent> 
    <modelVersion>4.0.0</modelVersion> 
    <artifactId>remote_call</artifactId> 
    <packaging>pom</packaging> 
    <modules> 
        <module>server_a</module> 
    </modules> 
    <properties> 
        <maven.compiler.source>17</maven.compiler.source> 
        <maven.compiler.target>17</maven.compiler.target> 
    </properties> 
</project>

spring_cloud(后面的测试都是要用这个作为父模块)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>org.example</groupId> 
    <artifactId>spring_cloud</artifactId> 
    <packaging>pom</packaging> 
    <version>1.0-SNAPSHOT</version> 
    <modules> 
        <module>product</module> 
        <module>eureka</module> 
        <module>order</module> 
        <module>remote_call</module> 
    </modules> 
    <!--    管理版本统一管理和spring相关的依赖-->
    <parent> 
        <artifactId>spring-boot-starter-parent</artifactId> 
        <groupId>org.springframework.boot</groupId> 
        <version>2.6.7</version> 
    </parent> 
    <properties> 
        <maven.compiler.source>17</maven.compiler.source> 
        <maven.compiler.target>17</maven.compiler.target> 
    </properties> 
    <dependencies> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter-web</artifactId> 
        </dependency> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter-logging</artifactId> 
        </dependency> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter-test</artifactId> 
        </dependency> 
        <dependency> 
            <groupId>org.projectlombok</groupId> 
            <artifactId>lombok</artifactId> 
            <version>1.18.24</version> 
            <scope>provided</scope> 
        </dependency> 
    </dependencies> 
    <dependencyManagement> 
        <dependencies> 
            <dependency> 
                <groupId>org.springframework.cloud</groupId> 
                <artifactId>spring-cloud-dependencies</artifactId> 
                <version>2021.0.3</version> 
                <type>pom</type> 
                <scope>import</scope> 
            </dependency> 
        </dependencies> 
    </dependencyManagement> 
    <repositories> 
        <repository> 
            <id>spring-snapshots</id> 
            <name>Spring Snapshots</name> 
            <url>http://repo.spring.io/libs-snapshot-local</url> 
            <snapshots> 
                <enabled>true</enabled> 
            </snapshots> 
        </repository> 
        <repository> 
            <id>spring-milestones</id> 
            <name>Spring Milestones</name> 
            <url>http://repo.spring.io/libs-milestone-local</url> 
            <snapshots> 
                <enabled>false</enabled> 
            </snapshots> 
        </repository> 
        <repository> 
            <id>spring-releases</id> 
            <name>Spring Releases</name> 
            <url>http://repo.spring.io/libs-release-local</url> 
            <snapshots> 
                <enabled>false</enabled> 
            </snapshots> 
        </repository> 
    </repositories> 
    <pluginRepositories> 
        <pluginRepository> 
            <id>spring-snapshots</id> 
            <name>Spring Snapshots</name> 
            <url>http://repo.spring.io/libs-snapshot-local</url> 
            <snapshots> 
                <enabled>true</enabled> 
            </snapshots> 
        </pluginRepository> 
        <pluginRepository> 
            <id>spring-milestones</id> 
            <name>Spring Milestones</name> 
            <url>http://repo.spring.io/libs-milestone-local</url> 
            <snapshots> 
                <enabled>false</enabled> 
            </snapshots> 
        </pluginRepository> 
    </pluginRepositories> 
    <build> 
        <plugins> 
            <plugin> 
                <groupId>org.springframework.boot</groupId> 
                <artifactId>spring-boot-maven-plugin</artifactId> 
            </plugin> 
        </plugins> 
    </build> 
</project>

创建子模块server_a

1、pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <parent> 
        <artifactId>remote_call</artifactId> 
        <groupId>org.example</groupId> 
        <version>1.0-SNAPSHOT</version> 
    </parent> 
    <modelVersion>4.0.0</modelVersion> 
    <artifactId>server_a</artifactId> 
    <properties> 
        <maven.compiler.source>17</maven.compiler.source> 
        <maven.compiler.target>17</maven.compiler.target> 
    </properties> 
</project>

2、application.yml

server:
 port: 9001
spring:
 application:
  name: server_a

3、启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class server_a_ApplicationStarter {
    public static void main(String[] args) {
        SpringApplication.run(server_a_ApplicationStarter.class,args);
    }
}

4、controller

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("server_a")
public class controller {
    @RequestMapping(value = "sendMessageToServerB",method = RequestMethod.GET)
    @ResponseBody
    public String sendMessage(){
        return "hello,server_b! i am server_a";
    }
}

创建子模块server_b

1、pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <parent> 
        <artifactId>remote_call</artifactId> 
        <groupId>org.example</groupId> 
        <version>1.0-SNAPSHOT</version> 
    </parent> 
    <modelVersion>4.0.0</modelVersion> 
    <artifactId>server_b</artifactId> 
    <properties> 
        <maven.compiler.source>17</maven.compiler.source> 
        <maven.compiler.target>17</maven.compiler.target> 
    </properties> 
</project>

2、application.yml

server:
 port: 9002
spring:
 application:
  name: server_b

3、启动类

@SpringBootApplication
public class server_b_ApplicationStarter {
    @Bean
    public RestTemplate RestTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(server_b_ApplicationStarter.class,args);
    }
}

4、controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("server_b")
public class controller {
    @Autowired
    private RestTemplate restTemplate;
    @RequestMapping(value = "remote_server_a",method = RequestMethod.GET)
    public String remoteCallServerA(){
        return restTemplate.getForObject("http://127.0.0.1:9001/server_a/sendMessageToServerB",String.class);
    }
}

4、测试结果:

访问http://localhost:9002/server_b/remote_server_a得到结果如下:

5、总结:

成功实现两个模块之间的远程调用,那么这种方式我是通过这样一行代码实现的:

restTemplate.getForObject("http://127.0.0.1:9001/server_a/sendMessageToServerB",String.class);

我们不难发现其中的地址信息是硬编码,后续如果server_a这个模块挂掉了,就算它有别的结点服务,我也访问不到了,能不能有一个通讯录记录这个微服务的电话呢?因为我们知道分布式架构的多结点特性是它高可用性的体现依赖,那么一个微服务可能出现在多个主机结点中运行,挂了一个我们还能够使用别的结点中的这一块微服务。但是如果没有一个中间的通讯录,采取硬编码的方式,显然无法实现这样一个系统的需求。于是,优质的程序员们设计出了服务注册中心这一个概念。