当前位置: 首页 > news >正文

SpringCloud 三种服务调用方式,你知道几种?

搭建服务测试环境

测试中,服务发现层采用Netflix的Eureka搭建。

主要步骤如下:

1.引入Eureka所需依赖

<!--eureka服务端-->
&nbsp;&nbsp; <dependency>
&nbsp;&nbsp;&nbsp;&nbsp; <groupId>org.springframework.cloud </groupId>
&nbsp;&nbsp;&nbsp;&nbsp; <artifactId>spring-cloud-starter-eureka-server </artifactId>
&nbsp;&nbsp; </dependency>
<!--客户端-->
<dependency>
&nbsp;&nbsp; <groupId>org.springframework.cloud </groupId>
&nbsp;&nbsp; <artifactId>spring-cloud-starter-eureka </artifactId>
</dependency>

2.修改配置文件

服务端:

eureka:
&nbsp; instance:
&nbsp;&nbsp; hostname:&nbsp; eureka9001.com&nbsp; #eureka服务端的实例名称
&nbsp;&nbsp; instance-id:&nbsp; eureka9001
client:
&nbsp;&nbsp; register-with-eureka:&nbsp; false&nbsp; #false表示不向注册中心注册自己
&nbsp;&nbsp; fetch-registry:&nbsp; false&nbsp; #&nbsp;#false&nbsp;表示自己就是注册中心,职责就是维护服务实例,并不需要去检索服务
&nbsp;&nbsp; service-url:
&nbsp;&nbsp; defaulteZone:&nbsp; http://127.0.0.1:9001

客户端1:

server:
&nbsp;&nbsp; port:&nbsp; 8002
spring:
&nbsp;&nbsp; application:
&nbsp;&nbsp;&nbsp;&nbsp; name:&nbsp; licensingservice
eureka:
&nbsp;&nbsp; instance:
&nbsp;&nbsp;&nbsp;&nbsp; instance-id:&nbsp; licensing-service-8002
&nbsp;&nbsp;&nbsp;&nbsp; prefer-ip-address:&nbsp; true
&nbsp;&nbsp; client:
&nbsp;&nbsp;&nbsp;&nbsp; register-with-eureka:&nbsp; true
&nbsp;&nbsp;&nbsp;&nbsp; fetch-registry:&nbsp; true
&nbsp;&nbsp;&nbsp;&nbsp; service-url:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; defaultZone:&nbsp; http://127.0.0.1:9001/eureka/,

客户端2:

server:
&nbsp; port:&nbsp; 8002
spring:
&nbsp; application:
&nbsp;&nbsp;&nbsp; name:&nbsp; licensingservice
eureka:
&nbsp; instance:
&nbsp;&nbsp;&nbsp; instance-id:&nbsp; licensing-service-8002
&nbsp;&nbsp;&nbsp; prefer-ip-address:&nbsp; true
&nbsp; client:
&nbsp;&nbsp;&nbsp; register-with-eureka:&nbsp; true
&nbsp;&nbsp;&nbsp; fetch-registry:&nbsp; true
&nbsp;&nbsp;&nbsp; service-url:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; defaultZone:&nbsp; http://127.0.0.1:9001/eureka/,&nbsp;&nbsp;&nbsp;&nbsp;

一组微服务的不同实例采服务名相同,不同的实例Id区分,分别对应,spring.application.name 和eureka.instance.instance-id。

3.启动服务

服务端:

@SpringBootApplication
@EnableEurekaServer
public&nbsp; class&nbsp;EurekaServerPort9001_App&nbsp;{
&nbsp;&nbsp; public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;SpringApplication.run(EurekaServerPort9001_App .class,args);
&nbsp;&nbsp;}
}

客户端:

@SpringBootApplication
@RefreshScope
@EnableEurekaClient
public&nbsp; class&nbsp;LicenseApplication_8002&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SpringApplication.run(LicenseApplication_8002 .class,&nbsp;args);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

4.测试

进入到Eureka服务端地址,我这是127.0.0.1:9001,可以查看注册到注册中心的服务。

如图:

e7639d9bafcd88a0a97f262f1a83cabe.jpeg

注意事项:Eureka通过三次心跳检测均通过,服务才会成功注册到注册中心,默认每次间隔10s,及初次启动服务需等待30s才能在Eureka中看到注册服务。

消费者搭建

1.Discover Client方式

微服务中服务既是消费者也可以是调用者,因此消费者配置和上面服务配置大体一致,依赖及配置参考上面服务端搭建方式。启动主类添加EnableEurekaClient注释:

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public&nbsp; class&nbsp;ConsumerApplication_7002&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SpringApplication.run(ConsumerApplication_7002 .class,&nbsp;args);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

核心配置类:

@Component
public&nbsp; class&nbsp;ConsumerDiscoveryClient&nbsp;{

&nbsp;&nbsp; @Autowired
&nbsp;&nbsp; private&nbsp;DiscoveryClient&nbsp;discoveryClient;

&nbsp;&nbsp; public&nbsp;ServiceInstance&nbsp;getServiceInstance()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;List<ServiceInstance>&nbsp;serviceInstances&nbsp;=&nbsp;discoveryClient.getInstances( "licensingservice");
&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;(serviceInstances.size()&nbsp;==&nbsp; 0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;serviceInstances.get( 0);
&nbsp;&nbsp;}

&nbsp;&nbsp; public&nbsp;String&nbsp;getUrl(String&nbsp;url)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ServiceInstance&nbsp;serviceInstance= this.getServiceInstance();
&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;(serviceInstance== null)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw&nbsp; new&nbsp;RuntimeException( "404&nbsp;,NOT&nbsp;FOUND");
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;urlR=String.format(url,serviceInstance.getUri().toString());
&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;urlR;
&nbsp;&nbsp;}
}

通过DiscoveryClient从Eureka中获取licensingservice服务的实例数组,并返回第一个实例。

测试Controller

@RestController
@RequestMapping( "test")
public&nbsp; class&nbsp;TestController&nbsp;{
&nbsp;&nbsp; //注意必须new,否则会被ribbon拦截器拦截,改变URL行为
&nbsp;&nbsp; private&nbsp;RestTemplate&nbsp;restTemplate= new&nbsp;RestTemplate();
&nbsp;&nbsp; @Autowired
&nbsp;&nbsp; private&nbsp;ConsumerDiscoveryClient&nbsp;consumerDiscoveryClient;
&nbsp;&nbsp; @RequestMapping( "/getAllEmp")
&nbsp;&nbsp; public&nbsp;List<Emp>&nbsp;getAllLicense(){
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;url=consumerDiscoveryClient.getUrl( "%s/test/getAllEmp");
&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;restTemplate.getForObject(url,List .class);
&nbsp;&nbsp;}
}

测试:

  1. 调试信息

354a98b75d0dd6835e344bf18614447d.jpeg从该图可以直观看到licensingservice,拥有两个服务实例,并可以查看实例信息。

  1. 页面返回信息

f0f45b74f399ddb1cf717d688fcc79c2.jpeg成功查询到数据库存储信息。

2.Ribbon功能的Spring RestTemplate方式

依赖同上。

核心配置类:

//指明负载均衡算法
@Bean
public&nbsp;IRule&nbsp;iRule()&nbsp;{
&nbsp;&nbsp; return&nbsp; new&nbsp;RoundRobinRule();
}

@Bean
@LoadBalanced&nbsp; //告诉Spring创建一个支持Ribbon负载均衡的RestTemplate
public&nbsp;RestTemplate&nbsp;restTemplate()&nbsp;{
&nbsp;&nbsp; return&nbsp; new&nbsp;RestTemplate();
}

设置负载均衡方式为轮询方式。

测试类:

@RestController
@RequestMapping( "rest")
public&nbsp; class&nbsp;ConsumerRestController&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp; @Autowired
&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp;RestTemplate&nbsp;restTemplate;
&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp; final&nbsp; static&nbsp;String&nbsp;SERVICE_URL_PREFIX&nbsp;=&nbsp; "http://LICENSINGSERVICE";

&nbsp;&nbsp;&nbsp;&nbsp; @RequestMapping( "/getById")
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;Emp&nbsp;getById(Long&nbsp;id)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MultiValueMap<String,&nbsp;Object>&nbsp;paramMap&nbsp;=&nbsp; new&nbsp;LinkedMultiValueMap<String,&nbsp;Object>();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;paramMap.add( "id",&nbsp;id);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;restTemplate.postForObject(SERVICE_URL_PREFIX&nbsp;+&nbsp; "/test/getById",&nbsp;paramMap,&nbsp;Emp .class);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

测试结果:

  • 第一次:
e5003933a3f25747a54a84a2e3a966bd.jpeg
  • 第二次:
5ddb006fcfb7b4bdc9e8de1ecbefc98f.jpeg
  • 第三次:

c3cf9d1341ac679ef119415a9e1229b6.jpeg因为采用轮询负载均衡方式分别调用不同服务实例,未区别,将name做出了一定更改。

以上两种方式对比,Ribbon方式是对第一种方式的封装且内置不同的负载算法,支持自定义。使用更加简单,但此两次均需编写RestTemplate的请求方法,较为繁琐且容易出错,第三种方式Feign客户端则极大的降低了开发难度和提升速度。

3.feign客户端方式

引入依赖:

<dependency>
&nbsp;&nbsp;&nbsp;&nbsp; <groupId>org.springframework.cloud </groupId>
&nbsp;&nbsp;&nbsp;&nbsp; <artifactId>spring-cloud-starter-feign </artifactId>
</dependency>

主要代码:

@FeignClient(value&nbsp;=&nbsp; "LICENSINGSERVICE",fallbackFactory&nbsp;=&nbsp;ServiceImp .class)
public&nbsp;interface&nbsp;ServiceInterface&nbsp;{

&nbsp;&nbsp; @RequestMapping( "/test/getById")
&nbsp;&nbsp; Emp&nbsp;getById(@RequestParam("id")&nbsp;Long&nbsp;id);

&nbsp;&nbsp; @RequestMapping( "/test/getLicenseById")
&nbsp;&nbsp; License&nbsp;getLicenseById(@RequestParam("id")&nbsp;Long&nbsp;id);

&nbsp;&nbsp; @RequestMapping( "/test/getAllEmp")
&nbsp;&nbsp; List<Emp>&nbsp;getAllLicense();
}
@Component
public&nbsp; class&nbsp;ServiceImp&nbsp;implements&nbsp;FallbackFactory<ServiceInterface>&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;ServiceInterface&nbsp;create(Throwable&nbsp;throwable)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; new&nbsp;ServiceInterface()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;Emp&nbsp;getById(Long&nbsp;id)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Emp&nbsp;emp&nbsp;=&nbsp; new&nbsp;Emp();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emp.setName( "i&nbsp;am&nbsp;feign&nbsp;fallback&nbsp;create");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;emp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;License&nbsp;getLicenseById(Long&nbsp;id)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;List<Emp>&nbsp;getAllLicense()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
&nbsp;&nbsp;&nbsp;&nbsp;}
}

采用接口模式开发,通过注解指明服务名以及后备方法,在服务表现不佳时,方便返回默认的结果,而不是一个不友好的错误。

主启动类:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public&nbsp; class&nbsp;Consumer_feign_Application_7004&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SpringApplication.run(Consumer_feign_Application_7004 .class,&nbsp;args);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

测试类:

@RestController
@RequestMapping( "rest")
public&nbsp; class&nbsp;ConsumerRestController&nbsp;{
&nbsp;&nbsp; @Autowired
&nbsp;&nbsp; private&nbsp;ServiceInterface&nbsp;serviceInterface;

&nbsp;&nbsp; @Autowired
&nbsp;&nbsp; private&nbsp;RestTemplate&nbsp;restTemplate;
&nbsp;&nbsp; @RequestMapping( "/getById")
&nbsp;&nbsp; public&nbsp;Emp&nbsp;getById(Long&nbsp;id)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;serviceInterface.getById(id);
&nbsp;&nbsp;}
}

测试结果:

  • 正常测试:
dad54a257ee8c7c153d9ed5b8e50f3b9.jpeg
  • 关闭两个服务实例,模拟服务实例死亡:

a9fcefba0e32b0afdb066e099fbafaa2.jpegFeign除了能简化服务调用,也可以实现当调用的服务失败时,友好的反馈信息。

此三种调用方式,由低至上,从不同层次实现了SpringCloud中的微服务互相调用。

相关文章:

  • 阿里二面:SpringCloud 有几种服务调用方式?
  • PHP HTTP 函数
  • springboot 整合使用redis发布订阅功能
  • mysql主从复制搭建
  • SpringCloud Alibaba实战——服务治理:实现服务调用的负载均衡
  • Windows如何编辑hosts
  • 背废完虐面试官!字节架构师8年心血终成《图解设计模式》手册
  • docker(5)-数据卷
  • Leetcode 1582. 二进制矩阵中的特殊位置
  • 网络数据采集-免费网络数据采集软件
  • 高等教育心理学:知识的学习
  • Addressing Function Approximation Error in Actor-Critic Methods
  • c语言学习5==TCP和socket
  • 【web-渗透测试方法】(15.5)测试访问控件
  • Linux 基础指令
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • Angular 4.x 动态创建组件
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • CSS盒模型深入
  • download使用浅析
  • ES6系列(二)变量的解构赋值
  • Java 23种设计模式 之单例模式 7种实现方式
  • java概述
  • Xmanager 远程桌面 CentOS 7
  • 阿里云Kubernetes容器服务上体验Knative
  • 编写符合Python风格的对象
  • 从输入URL到页面加载发生了什么
  • 使用 QuickBI 搭建酷炫可视化分析
  • 试着探索高并发下的系统架构面貌
  • 写代码的正确姿势
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • ​卜东波研究员:高观点下的少儿计算思维
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (NSDate) 时间 (time )比较
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (第一天)包装对象、作用域、创建对象
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • .gitignore文件_Git:.gitignore
  • .NET BackgroundWorker
  • .NET 使用配置文件
  • .net(C#)中String.Format如何使用
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法