研究完了服务注册中心,然后就要看看注册在服务中心上的各个服务相互间是怎么进行通信的,在springcloud中,服务间调用方式主要是使用 http restful方式进行服务间调用,而且是基于RestTemplate的服务调用。
关于RestTemplate:
spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
接下来写一个订单服务和用户服务,注册到服务中心后,使用RestTemplate进行通信:
注:我这里的UserController和OrderController也是把后面使用ribbon相关代码都写好了。
package com.jin.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author jinyunlong
* @date 2021/7/8 9:49
* @profession ICBC锅炉房保安
*/
@RestController
public class UserController {
@Autowired //服务注册与发现客户端对象,因为依赖里直接有耐非ribbon这个组件,所以直接去工厂取来注入
private DiscoveryClient discoveryClient; //负载均衡客户端低昂
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("user") //对应下头的RestTemplate()方法调用选择
public String invokeDemo(){
System.out.println("user demo...");
//1.调用订单服务 服务地址url:http://localhost:9999/order 必须GET方式 接收返回值String类型
// RestTemplate restTemplate = new RestTemplate(); //方法遵循restful风格 ,比如getForObject(url,responsetype)
// String orderResult = restTemplate.getForObject("http://localhost:9999/order",String.class);
//2.使用ribbon组件+RestTemplate实现负载均衡调用 1.DiscoveryClient 2.LoadBalanceClient @LoadBalance
// List<ServiceInstance> serviceInstances = discoveryClient.getInstances("ORDERS");
// serviceInstances.forEach(serviceInstance -> {
// System.out.println("服务主机:"+serviceInstance.getHost()+"服务端口:"+serviceInstance.getPort()+"服务地址:"
// +serviceInstance.getUri());
// });
//
// String result = new RestTemplate().getForObject(serviceInstances.get(0).getUri() + "/order", String.class);
//3.使用loadbalanceClient 进行服务调用
// ServiceInstance serviceInstance = loadBalancerClient.choose("ORDERS"); //默认轮询
// System.out.println("服务主机:"+serviceInstance.getHost()+"服务端口:"+serviceInstance.getPort()+"服务地址:"
// +serviceInstance.getUri());
// String result = restTemplate.getForObject(serviceInstance.getUri() + "/order", String.class);
//4.使用@LoadBalanced 作用:可以让对象具有ribbon负载均衡特性
String result = restTemplate.getForObject("http://ORDERS/order", String.class);
// System.out.println("调用订单服务成功:"+orderResult);
// return "调用订单服务成功"+orderResult;
return "ok"+result;
}
}
package com.jin.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author jinyunlong
* @date 2021/7/8 9:46
* @profession ICBC锅炉房保安
*/
@RestController
public class OrderController {
@Value("${server.port}")
private int port;
@GetMapping("order")
public String demo(){
System.out.println("order demo...");
return "order demo ok,当前提供的端口为:"+port;
}
}
主要看UserController 注释1的部分
//1.调用订单服务 服务地址url:http://localhost:9999/order 必须GET方式 接收返回值String类型
// RestTemplate restTemplate = new RestTemplate(); //方法遵循restful风格 ,比如getForObject(url,responsetype)
// String orderResult = restTemplate.getForObject("http://localhost:9999/order",String.class);
因为我使用的是@GetMapping 所以restTemplate遵循restful开发规范的话要调用getForObject()方法,写死order服务接口地址即可完成调用。
但是问题也很明显了:
1、这种调用方式好像都没和注册中心挂钩。2、地址值写死,order要是改了端口或者接口咋办3、负载均衡咋做。
所以,为了解决部分问题,耐非公司推出了ribbon组件。
ribbon的概念:Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。简言之,做负载均衡的。
然后因为我们已经使用了eureka client或者consul client ,所以就不用自己引入ribbon的依赖了,因为在eureka,consul中默认集成了ribbon组件,例如可以在consul依赖里找到
然后ribbon作为负载均衡,还是要搭配resttemplate才能完成服务间的调用,ribbon负载均衡的实现方式有三种:1、使用discovery client 2、使用loadBalanceClient 3、使用@loadBalanced
使用discovery Client形式调用和使用loadBalance Client形式调用都需要用代码来调用,调用方法不同,但是使用loadBalance Client形式调用会实现默认轮询机制,所以比前者好一些,但是两者都是通过方法去找注册中心服务端的serverid。
代码不贴了,注定这两种都不行。看看@LoadBalanced注解:
简化代码,在配置类中把RestTemplate注入到工厂中,同时加入@LoadBalanced即可在业务层进行调用
package com.jin.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author jinyunlong
* @date 2021/7/9 9:12
* @profession ICBC锅炉房保安
*/
@Configuration //代表这是一个springboot 配置类 spring.xml 工厂 创建对象 bean id class=""
public class BeanConfig {
//工厂创建restTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
关键就在这一行调用,记得@Autowired private RestTemplate restTemplate;别忘了写。
//4.使用@LoadBalanced 作用:可以让对象具有ribbon负载均衡特性
String result = restTemplate.getForObject("http://ORDERS/order", String.class);
我们也可以根据ribbon负责均衡的规则去指定我们到底是要轮询、随机或者是加权等,从choose()方法层层跟进源码最后发现底层接口是IRULE接口
我们只需要在配置文件里修改默认策略就行,我是改成随机了
# 1.修改服务默认随机策略
- 服务id.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
`下面的products为服务的唯一标识
#修改用户服务调用订单服务默认负载均衡策略不再使用轮询 使用随机策略
ORDERS.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
用ribbon负载均衡也能做了,但是服务要是改了接口地址,他还是不好办,而且还得写代码才能实现ribbon,所以接下来就要用OpenFeign了
总结一下: 只用restTemplate,没负载均衡且不灵活 ;使用ribbon+restTemplate ,甭管ribbon用哪个方法去实现,都能实现负载均衡,但还是不灵活,所以下一节就要看OpenFeign了。
全程没贴页面截图,就是服务轮询和随机,图片太多太麻烦了。两个订单服务没单起新项目,用jvm参数指定不同端口启动的。