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

SpringCloud 三种服务调用方式,你学会了吗?

本文主要介绍SpringCloud中三种服务调用方式:

  • Spring DiscoveryClient

  • 支持Ribbon的RestTemplate

  • Feign客户端

搭建服务测试环境

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

主要步骤如下:

1.引入Eureka所需依赖

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

2.修改配置文件

服务端:

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

客户端1:

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

客户端2:

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

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

3.启动服务

服务端:

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

客户端:

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

4.测试

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

如图:

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

消费者搭建

1.Discover Client方式

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

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

核心配置类:

@Component
public class ConsumerDiscoveryClient {

  @Autowired
  private DiscoveryClient discoveryClient;

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

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

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

测试Controller

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

测试:

  1. 调试信息

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

  1. 页面返回信息

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

2.Ribbon功能的Spring RestTemplate方式

依赖同上。

核心配置类:

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

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

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

测试类:

@RestController
@RequestMapping("rest")
public class ConsumerRestController {

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

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

测试结果:

  • 第一次:

  • 第二次:

  • 第三次:

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

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

3.feign客户端方式

引入依赖:

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

主要代码:

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

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

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

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

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

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

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

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

主启动类:

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

测试类:

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

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

测试结果:

  • 正常测试:

  • 关闭两个服务实例,模拟服务实例死亡:

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

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

相关文章:

  • 路由-hash和history模式的区别
  • 剑指:二叉树有关题目
  • 四天拿捏SpringSecurity?这份阿里架构师编写的理论与实战结合的学习笔记到底有多强
  • Hive3.1.2的HQL执行过程
  • 浅谈前端微服务背景及Micro-app的使用
  • 【Notepad】Notepad++ 安装XML/Json插件,格式化xml/json文件
  • 3分钟学会批量混合查询多家快递的物流信息
  • ElasticSearch Query DSL(二)
  • 【重识云原生】第六章容器6.1.7.1节——Docker核心技术cgroups综述
  • 关于C# HttpClient 的用法及相关问题的解决方法
  • illustrator插件-什么是脚本-如何使用-什么是动作-AI插件
  • UVA 10405【LCS】【背包】
  • Git学习总结
  • Java项目:SSM医药信息管理系统
  • python——装饰器深入研究(一)
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Javascript基础之Array数组API
  • JavaWeb(学习笔记二)
  • Js基础知识(四) - js运行原理与机制
  • learning koa2.x
  • Linux快速复制或删除大量小文件
  • PHP的Ev教程三(Periodic watcher)
  • React Native移动开发实战-3-实现页面间的数据传递
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • tensorflow学习笔记3——MNIST应用篇
  • unity如何实现一个固定宽度的orthagraphic相机
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 两列自适应布局方案整理
  • 如何解决微信端直接跳WAP端
  • 试着探索高并发下的系统架构面貌
  • 数据可视化之 Sankey 桑基图的实现
  • 通过npm或yarn自动生成vue组件
  • 学习Vue.js的五个小例子
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 一文看透浏览器架构
  • 用 Swift 编写面向协议的视图
  • Hibernate主键生成策略及选择
  • ​Spring Boot 分片上传文件
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​你们这样子,耽误我的工作进度怎么办?
  • #{}和${}的区别?
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • $().each和$.each的区别
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (分享)自己整理的一些简单awk实用语句
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转)负载均衡,回话保持,cookie
  • (转)树状数组
  • (转)四层和七层负载均衡的区别