博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
springcloud之自定义简易消费服务组件
阅读量:6180 次
发布时间:2019-06-21

本文共 6843 字,大约阅读时间需要 22 分钟。

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

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

Rest+Ribbon实现消费服务

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

  

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

1 
2
org.springframework.boot
3
spring-boot-starter-web
4
5
6
org.springframework.cloud
7
spring-cloud-starter-eureka
8

  再来我们通过上篇文章搭建的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
> getUsers(MoRq rq) { 9 return null;10 }11 12 @Override13 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  * 

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

> sericesMap = new ConcurrentHashMap<>(); 19 20 /** 21 * 设置服务提供者信息到map 22 */ 23 public void setServicesMap() { 24 //获取所有服务提供者applicationName 25 List
appNames = discoveryClient.getServices(); 26 27 //存储真实地址到map 28 for (String appName : 29 appNames) { 30 //获取某个服务提供者信息 31 List
instanceInfos = discoveryClient.getInstances(appName); 32 if (instanceInfos.isEmpty()) { 33 continue; 34 } 35 36 List
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
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() + 1); 77 return moService; 78 } 79 80 /** 81 * 自动刷新 服务提供者信息到map 82 */ 83 @Scheduled(fixedDelay = 1000 * 10) 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
96 * @return 97 */ 98 public
T getServiceData( 99 String appName, String serviceName,100 Map
map,101 Class
tClass) {102 T result = null;103 try {104 //筛选获取真实Service105 MoService service = choiceServiceByAppName(appName);106 107 //请求该service的url108 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/add129 */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 = 1000 * 10)5     public void refreshServicesMap() {6         setServicesMap();7     }

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

  

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

转载地址:http://exbda.baihongyu.com/

你可能感兴趣的文章
Android基础—四大组件之Activity
查看>>
Nginx 学习笔记
查看>>
你为什么选择程序员这个职业?
查看>>
[译] 用于 iOS 的 ML Kit 教程:识别图像中的文字
查看>>
有关https的SSL加密方式
查看>>
ES6的开发环境搭建
查看>>
iOS JSON、XML解析技巧
查看>>
Android下InputStream发生网络中断时的解决办法的代码
查看>>
8 jQuery学习笔记第八节 Jq的效果之自定义动画
查看>>
8月不支持 64 位,App 将无法上架 Google Play!需要怎么做?
查看>>
Vs - 基于 d3.js 和 vue.js 的数据可视化
查看>>
优雅地使用loading
查看>>
Node8.0 之 Napi 探秘
查看>>
TypeScript入坑
查看>>
(三)spring cloud微服务分布式云架构-服务网关zuul初级篇
查看>>
Spring Cloud--Honghu Cloud分布式微服务云系统—System系统管理
查看>>
Linux服务器配置——SAMBA
查看>>
我的WP7应用
查看>>
js打开连接 _无需整理
查看>>
我的友情链接
查看>>