热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

springcloud之自定义简易消费服务组件

这篇文章主要介绍了springcloud之自定义简易消费服务组件,本篇来使用rest+ribbon消费服务,并且通过轮询方式来自定义了个简易消费组件,感兴趣的小伙伴们可以参考一下

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

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

Rest+Ribbon实现消费服务

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

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


  org.springframework.boot
  spring-boot-starter-web


  org.springframework.cloud
  spring-cloud-starter-eureka

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

@Service
public class UserService implements UserInterface {

  @Autowired
  protected RestTemplate restTemplate;

  @Override
  public MoRp> getUsers(MoRq rq) {
    return null;
  }

  @Override
  public String getMsg() {

    String str = restTemplate.getForObject("http://EUREKA-PROVIDER/msg", String.class);
    return str;
  }
}

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

@RestController
public class UserController {

  @Autowired
  private UserService userService;

  @GetMapping("/msg")
  public String getMsg(){

    return userService.getMsg();
  }
}

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

@SpringBootApplication
@EnableDiscoveryClient //消费客户端
public class EurekaConsumerRibbonApplication {

  @Bean 
  @LoadBalanced //负载均衡
  RestTemplate restTemplate(){
    return new RestTemplate();
  }
  
  public static void main(String[] args) {
    SpringApplication.run(EurekaConsumerRibbonApplication.class, args);
  }
}

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

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

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

/**
 * Created by shenniu on 2018/6
 * 

* rest+eureka+自定义client端 */ @Component public class ShenniuBanlance { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; /** * 服务真实地址 ConcurrentHashMap<"服务应用名称", ("真实接口ip", 被访问次数)> */ public static ConcurrentHashMap> sericesMap = new ConcurrentHashMap<>(); /** * 设置服务提供者信息到map */ public void setServicesMap() { //获取所有服务提供者applicationName List appNames = discoveryClient.getServices(); //存储真实地址到map for (String appName : appNames) { //获取某个服务提供者信息 List instanceInfos = discoveryClient.getInstances(appName); if (instanceInfos.isEmpty()) { continue; } List services = new ArrayList<>(); instanceInfos.forEach(b -> { MoService service = new MoService(); //被访问次数 service.setWatch(0L); //真实接口地址 service.setUrl(b.getUri().toString()); services.add(service); }); //如果存在就更新 sericesMap.put(appName.toLowerCase(), services); } } /** * 根据app获取轮询方式选中后的service * * @param appName * @return */ public MoService choiceServiceByAppName(String appName) throws Exception { appName = appName.toLowerCase(); //某种app的服务service集合 List serviceMap = sericesMap.get(appName); if (serviceMap == null) { //初始化所有app服务 setServicesMap(); serviceMap = sericesMap.get(appName); if (serviceMap == null) { throw new Exception("未能找到" + appName + "相关服务"); } } //筛选出被访问量最小的service 轮询的方式 MoService moService = serviceMap.stream().min( Comparator.comparing(MoService::getWatch) ).get(); //负载记录+1 moService.setWatch(moService.getWatch() + 1); return moService; } /** * 自动刷新 服务提供者信息到map */ @Scheduled(fixedDelay = 1000 * 10) public void refreshServicesMap() { setServicesMap(); } /** * get请求服务获取返回数据 * * @param appName 应用名称 ApplicationName * @param serviceName 服务名称 ServiceName * @param map url上请求参数 * @param tClass 返回类型 * @param * @return */ public T getServiceData( String appName, String serviceName, Map map, Class tClass) { T result = null; try { //筛选获取真实Service MoService service = choiceServiceByAppName(appName); //请求该service的url String apiUrl = service.getUrl() + "/" + serviceName; System.out.println(apiUrl); result = map != null &#63; restTemplate.getForObject(apiUrl, tClass, map) : restTemplate.getForObject(apiUrl, tClass); } catch (Exception ex) { ex.printStackTrace(); } return result; } /** * Service信息 */ public class MoService { /** * 负载次数记录数 */ private Long watch; /** * 真实接口地址: http://xxx.com/api/add */ private String url; public Long getWatch() { return watch; } public void setWatch(Long watch) { this.watch = watch; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } } }

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

@Override
  public String getMsg() {

    String str = banlance.getServiceData(
        "EUREKA-PROVIDER", "msg",
        null,
        String.class
    );
    return str;
  }

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

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

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

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

/**
   * 自动刷新 服务提供者信息到map 
   */
  @Scheduled(fixedDelay = 1000 * 10)
  public void refreshServicesMap() {
    setServicesMap();
  }

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

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


推荐阅读
  • HBase运维工具全解析
    本文深入探讨了HBase常用的运维工具,详细介绍了每种工具的功能、使用场景及操作示例。对于HBase的开发人员和运维工程师来说,这些工具是日常管理和故障排查的重要手段。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 深入理解一致性哈希算法及其应用
    本文详细介绍了分布式系统中的一致性哈希算法,探讨其原理、优势及应用场景,帮助读者全面掌握这一关键技术。 ... [详细]
  • 本文介绍了数据库体系的基础知识,涵盖关系型数据库(如MySQL)和非关系型数据库(如MongoDB)的基本操作及高级功能。通过三个阶段的学习路径——基础、优化和部署,帮助读者全面掌握数据库的使用和管理。 ... [详细]
  • 计算机网络复习:第五章 网络层控制平面
    本文探讨了网络层的控制平面,包括转发和路由选择的基本原理。转发在数据平面上实现,通过配置路由器中的转发表完成;而路由选择则在控制平面上进行,涉及路由器中路由表的配置与更新。此外,文章还介绍了ICMP协议、两种控制平面的实现方法、路由选择算法及其分类等内容。 ... [详细]
  • 本文将介绍如何使用 Go 语言编写和运行一个简单的“Hello, World!”程序。内容涵盖开发环境配置、代码结构解析及执行步骤。 ... [详细]
  • 线性Kalman滤波器在多自由度车辆悬架主动控制中的应用研究
    本文探讨了线性Kalman滤波器(LKF)在不同自由度(2、4、7)的车辆悬架系统中进行主动控制的应用。通过详细的仿真分析,展示了LKF在提升悬架性能方面的潜力,并总结了调参过程中的关键要点。 ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • C++实现经典排序算法
    本文详细介绍了七种经典的排序算法及其性能分析。每种算法的平均、最坏和最好情况的时间复杂度、辅助空间需求以及稳定性都被列出,帮助读者全面了解这些排序方法的特点。 ... [详细]
  • 本文介绍如何利用动态规划算法解决经典的0-1背包问题。通过具体实例和代码实现,详细解释了在给定容量的背包中选择若干物品以最大化总价值的过程。 ... [详细]
  • 本文详细探讨了Java中的24种设计模式及其应用,并介绍了七大面向对象设计原则。通过创建型、结构型和行为型模式的分类,帮助开发者更好地理解和应用这些模式,提升代码质量和可维护性。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
author-avatar
哲亚Zoe
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有