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

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

Rest+Ribbon实现消费服务

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

  

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

 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

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

 @Service
public class UserService implements UserInterface { @Autowired
protected RestTemplate restTemplate; @Override
public MoRp<List<MoUser>> 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
* <p>
* rest+eureka+自定义client端
*/
@Component
public class ShenniuBanlance { @Autowired
private RestTemplate restTemplate; @Autowired
private DiscoveryClient discoveryClient; /**
* 服务真实地址 ConcurrentHashMap<"服务应用名称", ("真实接口ip", 被访问次数)>
*/
public static ConcurrentHashMap<String, List<MoService>> sericesMap = new ConcurrentHashMap<>(); /**
* 设置服务提供者信息到map
*/
public void setServicesMap() {
//获取所有服务提供者applicationName
List<String> appNames = discoveryClient.getServices(); //存储真实地址到map
for (String appName :
appNames) {
//获取某个服务提供者信息
List<ServiceInstance> instanceInfos = discoveryClient.getInstances(appName);
if (instanceInfos.isEmpty()) {
continue;
} List<MoService> 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<MoService> 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() + );
return moService;
} /**
* 自动刷新 服务提供者信息到map
*/
@Scheduled(fixedDelay = * )
public void refreshServicesMap() {
setServicesMap();
} /**
* get请求服务获取返回数据
*
* @param appName 应用名称 ApplicationName
* @param serviceName 服务名称 ServiceName
* @param map url上请求参数
* @param tClass 返回类型
* @param <T>
* @return
*/
public <T> T getServiceData(
String appName, String serviceName,
Map<String, ?> map,
Class<T> tClass) {
T result = null;
try {
//筛选获取真实Service
MoService service = choiceServiceByAppName(appName); //请求该service的url
String apiUrl = service.getUrl() + "/" + serviceName;
System.out.println(apiUrl);
result = map != null ?
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 = * )
public void refreshServicesMap() {
setServicesMap();
}

  为了方便看测试效果,我们在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. javascript知识详解之8张思维导图

    学习的道路就是要不断的总结归纳,好记性不如烂笔头,so,下面将po出8张javascript相关的思维导图. 思维导图小tips:思维导图又叫心智图,是表达发射性思维的有效的图形思维工具 ,它简单却又 ...

  2. python assert的作用

    使用assert断言是学习python一个非常好的习惯,python assert 断言句语格式及用法很简单.在没完善一个程序之前,我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件 ...

  3. message.go

    +MsgIDLength:]     return &msg, nil } func writeMessageToBackend(buf *bytes.Buffer, msg *Message ...

  4. 图解java中的bytebuffer

    因何而写 网上关于bytebuffer的文章真的很多,为何在此还要写一篇呢?主要是基于以下几点考虑 很多人在使用t-io时,还不会bytebuffer,只会照着t-io提供的例子照猫画虎,不利于灵活运 ...

  5. UOJ#37. 【清华集训2014】主旋律

    题目大意: 传送门 题解: 神题……Orz. 首先正难则反. 设$f_S$表示选取点集状态为s时,这部分图可以构成非强联通图的方案数. 设$p_{S,i}$表示点集s缩点后有i个入度为0点的方案数,保 ...

  6. 【BZOJ 3534】: [Sdoi2014]重建

    题目大意:(略) 题解: 相对误差……我好方. 考虑答案应该为所有合法答案概率之和.对于一个合法的生成树,其出现概率应为所有选取边的概率出现的积 乘以 所有未选取边不出现概率的积. 即: $\;\pr ...

  7. bzoj 1098 poi2007 办公楼 bfs+链表

    题意很好理解,求给出图反图的联通块个数. 考虑这样一个事情:一个联通块里的点,最多只会被遍历一次,再遍历时没有任何意义 所以用链表来存,每遍历到一个点就将该点删掉 #include<cstdio ...

  8. mysql中利用group by过滤删除重复行

    DELETEFROM peopleWHERE peopleId IN (SELECT peopleId FROM people GROUP BY peopleId HAVING count(peopl ...

  9. 开发教程(四) MIP组件平台使用说明

    组件审核平台用于上传 MIP 组件.经过自动校验之后,提交审核,通过审核的组件会定时推送到线上,供网站使用. 平台地址:https://www.mipengine.org/platform/ 1. 使 ...

  10. Python练习:九九乘法表

    打印 n * n 的乘法表 #打印 9*9 乘法表 def Multiplication(n): # n - 定义打印的行数 max_len = len(str((n)**2)) #计算最大值的占位( ...