本次和大家分享的是怎么来消费服务,上篇文章讲了使用Feign来消费,本篇来使用rest+ribbon消费服务,并且通过轮询方式来自定义了个简易消费组件,本文分享的宗旨是:自定义消费服务的思路;思路如果有可取之处还请“赞”一下:

  • Rest+Ribbon实现消费服务
  • Rest+轮询自定义简易消费组件
  • 使用Scheduled刷新服务提供者信息

Rest+Ribbon实现消费服务

  做为服务消费方准确的来说进行了两种主流程区分1)获取可以服务2)调用服务,那么又是如何获取服务的并且又是通过什么来调用服务的,下面我们来看一副手工图:

  

  手工图上能够看出消费方先获取了服务方的真实接口地址,然后再通过地址去调用接口;然后对于微服务架构来说获取某一个类ip或端口然后去调用接口肯定是不可取的,因此微服务中产生了一种serviceid的概念;简单流程介绍完了,下面通过实例来分析;首先添加依赖如:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.cloud</groupId>
  7. <artifactId>spring-cloud-starter-eureka</artifactId>
  8. </dependency>

  再来我们通过上篇文章搭建的eureka_server(服务中心),eureka_provider(服务提供者)来做测试用例,这里我重新定义eureka_consumer_ribbon模块做为消费服务;先创建service层类和代码:

  1. @Service
  2. public class UserService implements UserInterface {
  3.  
  4. @Autowired
  5. protected RestTemplate restTemplate;
  6.  
  7. @Override
  8. public MoRp<List<MoUser>> getUsers(MoRq rq) {
  9. return null;
  10. }
  11.  
  12. @Override
  13. public String getMsg() {
  14.  
  15. String str = restTemplate.getForObject("http://EUREKA-PROVIDER/msg", String.class);
  16. return str;
  17. }
  18. }

  主要用到了RestTemplate的restTemplate.getForObject函数,然后需要定义个Controller来吧获取到的数据响应到页面上,为了简单这里仅仅只拿getMsg服务接口测试:

  1. @RestController
  2. public class UserController {
  3.  
  4. @Autowired
  5. private UserService userService;
  6.  
  7. @GetMapping("/msg")
  8. public String getMsg(){
  9.  
  10. return userService.getMsg();
  11. }
  12. }

  最后我们在启动类添加入下代码,注意@LoadBalanced标记必须加,因为咋们引入的eureka依赖里面包含了ribbon(Dalston.RELEASE版本),ribbon封装了负载均衡的算法,如果不加这个注解,那后面rest方法的url就必须是可用的url路径了,当然这里加了注解就可以使用上面说的serviceId:

  1. @SpringBootApplication
  2. @EnableDiscoveryClient //消费客户端
  3. public class EurekaConsumerRibbonApplication {
  4.  
  5. @Bean
  6. @LoadBalanced //负载均衡
  7. RestTemplate restTemplate(){
  8. return new RestTemplate();
  9. }
  10.  
  11. public static void main(String[] args) {
  12. SpringApplication.run(EurekaConsumerRibbonApplication.class, args);
  13. }
  14. }

  下面来消费方显示的效果:

  

Rest+轮询自定义简易消费组件

  自定义消费组件原来和面手工图差不多,就是先想法获取服务提供端真实的接口地址,然后通过rest去调用这个url,得到相应的结果输出;这里自定义了一个ShenniuBanlance的组件类:

  1. /**
  2. * Created by shenniu on 2018/6
  3. * <p>
  4. * rest+eureka+自定义client端
  5. */
  6. @Component
  7. public class ShenniuBanlance {
  8.  
  9. @Autowired
  10. private RestTemplate restTemplate;
  11.  
  12. @Autowired
  13. private DiscoveryClient discoveryClient;
  14.  
  15. /**
  16. * 服务真实地址 ConcurrentHashMap<"服务应用名称", ("真实接口ip", 被访问次数)>
  17. */
  18. public static ConcurrentHashMap<String, List<MoService>> sericesMap = new ConcurrentHashMap<>();
  19.  
  20. /**
  21. * 设置服务提供者信息到map
  22. */
  23. public void setServicesMap() {
  24. //获取所有服务提供者applicationName
  25. List<String> appNames = discoveryClient.getServices();
  26.  
  27. //存储真实地址到map
  28. for (String appName :
  29. appNames) {
  30. //获取某个服务提供者信息
  31. List<ServiceInstance> instanceInfos = discoveryClient.getInstances(appName);
  32. if (instanceInfos.isEmpty()) {
  33. continue;
  34. }
  35.  
  36. List<MoService> services = new ArrayList<>();
  37. instanceInfos.forEach(b -> {
  38. MoService service = new MoService();
  39. //被访问次数
  40. service.setWatch(0L);
  41. //真实接口地址
  42. service.setUrl(b.getUri().toString());
  43. services.add(service);
  44. });
  45.  
  46. //如果存在就更新
  47. sericesMap.put(appName.toLowerCase(), services);
  48. }
  49. }
  50.  
  51. /**
  52. * 根据app获取轮询方式选中后的service
  53. *
  54. * @param appName
  55. * @return
  56. */
  57. public MoService choiceServiceByAppName(String appName) throws Exception {
  58. appName = appName.toLowerCase();
  59. //某种app的服务service集合
  60. List<MoService> serviceMap = sericesMap.get(appName);
  61. if (serviceMap == null) {
  62. //初始化所有app服务
  63. setServicesMap();
  64. serviceMap = sericesMap.get(appName);
  65. if (serviceMap == null) {
  66. throw new Exception("未能找到" + appName + "相关服务");
  67. }
  68. }
  69.  
  70. //筛选出被访问量最小的service 轮询的方式
  71. MoService moService = serviceMap.stream().min(
  72. Comparator.comparing(MoService::getWatch)
  73. ).get();
  74.  
  75. //负载记录+1
  76. moService.setWatch(moService.getWatch() + );
  77. return moService;
  78. }
  79.  
  80. /**
  81. * 自动刷新 服务提供者信息到map
  82. */
  83. @Scheduled(fixedDelay = * )
  84. public void refreshServicesMap() {
  85. setServicesMap();
  86. }
  87.  
  88. /**
  89. * get请求服务获取返回数据
  90. *
  91. * @param appName 应用名称 ApplicationName
  92. * @param serviceName 服务名称 ServiceName
  93. * @param map url上请求参数
  94. * @param tClass 返回类型
  95. * @param <T>
  96. * @return
  97. */
  98. public <T> T getServiceData(
  99. String appName, String serviceName,
  100. Map<String, ?> map,
  101. Class<T> tClass) {
  102. T result = null;
  103. try {
  104. //筛选获取真实Service
  105. MoService service = choiceServiceByAppName(appName);
  106.  
  107. //请求该service的url
  108. String apiUrl = service.getUrl() + "/" + serviceName;
  109. System.out.println(apiUrl);
  110. result = map != null ?
  111. restTemplate.getForObject(apiUrl, tClass, map) :
  112. restTemplate.getForObject(apiUrl, tClass);
  113. } catch (Exception ex) {
  114. ex.printStackTrace();
  115. }
  116. return result;
  117. }
  118.  
  119. /**
  120. * Service信息
  121. */
  122. public class MoService {
  123. /**
  124. * 负载次数记录数
  125. */
  126. private Long watch;
  127. /**
  128. * 真实接口地址: http://xxx.com/api/add
  129. */
  130. private String url;
  131.  
  132. public Long getWatch() {
  133. return watch;
  134. }
  135.  
  136. public void setWatch(Long watch) {
  137. this.watch = watch;
  138. }
  139.  
  140. public String getUrl() {
  141. return url;
  142. }
  143.  
  144. public void setUrl(String url) {
  145. this.url = url;
  146. }
  147. }
  148. }

  以上就是主要的实现代码,代码逻辑:设置服务提供者信息到map-》根据app获取轮询方式选中后的service-》请求服务获取返回数据;轮询实现的原理是使用了一个负载记录数,每次被请求后自动+1,当要获取某个服务提供者时,通过记录数筛选出最小值的一个实例,里面存储有真实接口地址url;调用只需要这样(当然可以弄成注解来调用):

  1. @Override
  2. public String getMsg() {
  3.  
  4. String str = banlance.getServiceData(
  5. "EUREKA-PROVIDER", "msg",
  6. null,
  7. String.class
  8. );
  9. return str;
  10. }

  这里需要注意由于我们在前面RestTemplate使用加入了注解@LoadBalanced,这样使得rest请求时必须用非ip的访问方式(也就是必须serviceid)才能正常响应,不然会提示错误如:

  

  简单来说就是不用再使用ip了,因为有负载均衡机制;当我们去掉这个注解后,我们自定义的组件就能运行成功,效果图和实例1一样就不贴图了;

使用Scheduled刷新服务提供者信息

  在微服务架构中,如果某台服务挂了之后,必须要及时更新client端的服务缓存信息,不然就可能请求到down的url去,基于这种考虑我这里采用了EnableSched标记来做定时刷新;首先在启动类增加 @EnableScheduling ,然后定义一个刷行服务信息的服务如:

  1. /**
  2. * 自动刷新 服务提供者信息到map
  3. */
  4. @Scheduled(fixedDelay = * )
  5. public void refreshServicesMap() {
  6. setServicesMap();
  7. }

  为了方便看测试效果,我们在server,provider(2个),consumer已经启动的情况下,再启动一个端口为2005的provider服务;然后刷新consumer接口看下效果:

  

  这个时候能够看到调用2005端口的接口成功了,通过@Scheduled定时服务吧最新或者失效的服务加入|移除掉,就达到了咋们的需求了;如果你觉得该篇内容对你有帮助,不防赞一下,谢谢。

springcloud之自定义简易消费服务组件的更多相关文章

  1. SpringCloud学习系列之二 ----- 服务消费者(Feign)和负载均衡(Ribbon)使用详解

    前言 本篇主要介绍的是SpringCloud中的服务消费者(Feign)和负载均衡(Ribbon)功能的实现以及使用Feign结合Ribbon实现负载均衡. SpringCloud Feign Fei ...

  2. SpringCloud系列四:Eureka 服务发现框架(定义 Eureka 服务端、Eureka 服务信息、Eureka 发现管理、Eureka 安全配置、Eureka-HA(高可用) 机制、Eureka 服务打包部署)

    1.概念:Eureka 服务发现框架 2.具体内容 对于服务发现框架可以简单的理解为服务的注册以及使用操作步骤,例如:在 ZooKeeper 组件,这个组件里面已经明确的描述了一个服务的注册以及发现操 ...

  3. 白话SpringCloud | 第三章:服务注册与发现(Eureka)-下

    前言 上一章节,讲解了在单机模式下的服务注册与发现的相关知识点及简单示例.而在实际生产或者在这种微服务架构的分布式环境中,需要考虑发生故障时,各组件的高可用.而其实高可用,我的简单粗俗理解就是,通过系 ...

  4. django框架简介及自定义简易版框架

    web应用与web框架本质 概念 什么是web应用程序呢? Web应用程序就一种可以通过互联网来访问资源的应用程序, 用户可以只需要用一个浏览器而不需要安装其他程序就可以访问自己需要的资源. 应用软件 ...

  5. SpringCloud全家桶学习之服务注册与发现及Eureka高可用集群搭建(二)

    一.Eureka服务注册与发现 (1)Eureka是什么? Eureka是NetFlix的一个子模块,也是核心模块之一.Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故 ...

  6. SpringCloudAlibaba 微服务组件 Nacos 之配置中心源码深度解析

    大家好,这篇文章跟大家聊下 SpringCloudAlibaba 中的微服务组件 Nacos.Nacos 既能做注册中心,又能做配置中心,这篇文章主要来聊下做配置中心时 client 端的一些设计,主 ...

  7. SpringCloud教程 | 第三篇: 服务消费者(Feign)

    上一篇文章,讲述了如何通过RestTemplate+Ribbon去消费服务,这篇文章主要讲述如何通过Feign去消费服务.一.Feign简介 Feign是一个声明式的伪Http客户端,它使得写Http ...

  8. SpringCloud教程 | 第三篇: 服务消费者(Feign)(Finchley版本)

    上一篇文章,讲述了如何通过RestTemplate+Ribbon去消费服务,这篇文章主要讲述如何通过Feign去消费服务. 一.Feign简介 Feign是一个声明式的伪Http客户端,它使得写Htt ...

  9. 史上最简单的SpringCloud教程 | 第三篇: 服务消费者(Feign)(Finchley版本)

    转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f3-feign/ 本文出自方志朋的博客 上一篇文章,讲述了如 ...

随机推荐

  1. jQuery学习之旅 Item6 好用的each()

    1.javascript 函数的调用方式 首先来研究一下jquery的each()方法的源码,在这之前,先要回顾一下javascript函数具体调用样式: 普通函数调用 setName(); 可以作为 ...

  2. python爬取所有微信好友的信息

    ''' 爬取所有T信好友的信息 ''' import itchat from pandas import DataFrame itchat.login() friends=itchat.get_fri ...

  3. 【转】一则使用WinDbg工具调试iis进程调查内存占用过高的案例

    最近遇到一个奇葩内存问题,跟了三四天,把Windbg玩熟了,所以打算分享下. 症状简介 我们团队的DEV开发环境只有一台4核16G的win2012r2.这台服务器上装了SqlServer.TFS(项目 ...

  4. 【线段树】Bzoj1798 [AHOI2009] 维护序列

    Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2 ...

  5. Django 基础二(View和urls)

    上一篇博文已经成功安装了python环境和Django,并且新建了一个空的项目.接下来就可以正式开始进行Django下 的Web开发了.首先进入项目的主目录: cd ./DjangoLearn/hol ...

  6. vue+axios访问本地json数据踩坑点

    当我们想在vue项目中模拟后台接口访问json数据时,我们发现无论如何也访问不到本地的json数据. 注意:1.在vue-cli项目中,我们静态资源只能放在static文件夹中,axios使用get请 ...

  7. 基于SpringBoot从零构建博客网站 - 确定需求和表结构

    要确定一个系统的需求,首先需要明确该系统的用户有哪些,然后针对每一类用户,确定其需求.对于博客网站来说,用户有3大类,分别是: 作者,也即是注册用户 游客,也即非注册用户 管理员,网站维护人员 那么从 ...

  8. ASP.NET MVC如何做一个简单的非法登录拦截

    摘要:做网站的时候,经常碰到这种问题,一个没登录的用户,却可以通过localhost:23244/Main/Index的方式进入到网站的内部,查看网站的信息.我们知道,这是极不安全的,那么如何对这样的 ...

  9. 简述Java中的final关键字

    final关键字可用于修饰类.方法和变量,final修饰的类不能被继承:final修饰的方法不可被重写:final修饰的变量不可被改变. 1. final类 final修饰的类不能被继承意思是fina ...

  10. css实现发光文字,以及一点点js特效

    效果图: 代码如下: </head> <style> body{ background-color:#000; } .textArea{ font-size:100px; co ...