原创作者:田超凡(程序员田宝宝)
版权所有,引用请注明原作者,严禁复制转载
Part 1 理论部分
1 什么是客户端负载均衡?
在SpringCloud中,使用Ribbon作为负载均衡客户端实现客户端负载均衡。
Ribbon会从Eureka注册中心服务器端获取服务注册信息列表,然后会缓存到本地,在本地通过轮询、取模、权重、一致性Hash、随机权重、随机等策略实现客户端负载均衡。
2 Ribbon和Nginx的区别?
1 使用环境的区别
Ribbon实现的是客户端负载均衡:
Ribbon是从Eureka注册中心服务器端中获取服务注册信息列表,然后缓存到本地,在本地通过轮询等策略实现负载均衡。
所以Ribbon属于客户端负载均衡,即在客户端本地实现的负载均衡。
Nginx实现的是服务器端负载均衡:
Nginx运行在服务器端,客户端会将所有发送到服务器的请求统一交给Nginx,由Nginx在服务器端实现负载均衡请求转发。
所以Nginx属于服务器端负载均衡,即请求到达服务器之后,由Nginx在服务器上进行转发。
2 应用场景的区别
Ribbon适用于微服务客户端,通过RPC远程服务调用对需要请求的缓存在本地的服务地址列表实现负载均衡的场景,比如在Dubbo、SpringCloud中,服务调用都是采用的客户端负载均衡。
Nginx适用于在服务器端实现负载均衡请求转发的场景,比如Tomcat。
3 什么是Feign?什么是OpenFeign?
Feign客户端是一个Web声明式的、基于HTTP协议的RPC远程服务调用工具,提供了接口和注解等方式方便灵活进行RPC远程服务调用,Feign默认已经整合了Ribbon负载均衡客户端。
OpenFeign是在Feign基础上重新封装的一个支持SpringMVC注解的(比如
@RequestMapping等)RPC远程服务调用工具。
Part 2 实践部分
使用discoveryClient负载本地负载均衡
@Autowired private MemberApifeign memberApifeign; @RequestMapping("/feignMember") public String feignMember() { return memberApifeign.getMember(); } private int requestCount = 1; @RequestMapping("/discoveryClient") public String discoveryClient() { String serviceUrl = getServiceUrl() + "/getMember"; if (StringUtils.isEmpty(serviceUrl)) { return "请求地址为null"; } // 请求地址 System.out.println("serviceUrl:" + serviceUrl); String result = restTemplate.getForObject(serviceUrl, String.class); return result; } @RequestMapping("/getServiceUrl") private String getServiceUrl() { List<ServiceInstance> instances = discoveryClient.getInstances("app-ittcf-member"); if (instances == null || instances.size() == 0) { return null; } int size = instances.size(); int index = requestCount % size; requestCount++; return instances.get(index).getUri().toString(); } |
RestTemplate
请求类型
GET请求
getForEntity
getForEntity方法的返回值是一个ResponseEntity<T>,ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等
@RequestMapping("/getForEntity") public ResponseEntity<String> getForEntity() { ResponseEntity<String> response = restTemplate.getForEntity("http://app-ittcf-member/getMember", String.class); System.out.println("statusCode:" + response.getStatusCode()); System.out.println("Body:" + response.getBody()); return response; } |
getForEntity的第一个参数为我要调用的服务的地址,这里我调用了服务提供者提供的/hello接口,注意这里是通过服务名调用而不是服务地址,如果写成服务地址就没法实现客户端负载均衡了。
getForEntity第二个参数String.class表示我希望返回的body类型是String
拿到返回结果之后,将返回结果遍历打印出来
Get请求传递参数
@RequestMapping("/getForOrderEntity2") public ResponseEntity<String> getForEntity2(String name) { Map<String, String> map = new HashMap<String, String>(); map.put("name", name); ResponseEntity<String> response = restTemplate.getForEntity("http://app-ittcf-member/getUser?name={name}", String.class, map); System.out.println("statusCode:" + response.getStatusCode()); System.out.println("Body:" + response.getBody()); return response; } |
可以用一个数字做占位符,最后是一个可变长度的参数,来一一替换前面的占位符
也可以前面使用name={name}这种形式,最后一个参数是一个map,map的key即为前边占位符的名字,map的value为参数值
getForObject
getForObject函数实际上是对getForEntity函数的进一步封装,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject,举一个简单的例子,如下:
@RequestMapping("/getOrder") public String getOrder() { // 有两种实现方式,一种是采用服务别名方式调用,另一种是直接调用使用别名去注册中心上获取对应的服务调用地址 String url = "http://app-ittcf-member/getMember"; String result = restTemplate.getForObject(url, String.class); System.out.println("订单服务调用用户服务:" + result); return result; } |
POST请求
PostForEntity
postForObject
如果你只关注,返回的消息体,可以直接使用postForObject。用法和getForObject一致
PUT请求
DELETE请求
声明式服务调用SpringCloud Feign
feign介绍
Feign客户端是一个web声明式http远程调用工具,提供了接口和注解方式进行调用。
环境搭建
Maven依赖信息
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> |
feign客户端接口
@Autowired private MemberApifeign memberApifeign; @RequestMapping("/feignMember") public String feignMember() { return memberApifeign.getMember(); } |
feign客户端接口
// name 指定服务名称 @FeignClient(name = "app-ittcf-member") public interface MemberApifeign { @RequestMapping("/getMember") public String getMember(); } |
项目启动加上@EnableFeignClients
@SpringBootApplication @EnableEurekaClient @EnableFeignClients public class AppOrder { public static void main(String[] args) { SpringApplication.run(AppOrder.class, args); // 如果使用rest方式以别名方式进行调用依赖ribbon负载均衡器 @LoadBalanced // @LoadBalanced就能让这个RestTemplate在请求时拥有客户端负载均衡的能力 } // 解决RestTemplate 找不到原因 应该把restTemplate注册SpringBoot容器中 @bean @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } } |
feign继承特性
在使用声明式feign客户端工具的时候,因为书写的方式代码可能会产生重复,可以使用feign客户端集成方式减少代码。
创建springcloud-2.0-parent
Maven依赖信息
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- SpringBoot整合fegnin客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加, 否者各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> |
创建springcloud-2.0-api
创建springcloud-2.0-member-api
创建springcloud-2.0-order-api
创建springcloud-2.0-member
application.yml
###服务启动端口号 server: port: 8000 ###服务名称(服务注册到eureka名称) spring: application: name: app-ittcf-member ###服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka
###因为该应用为注册中心,不会注册自己 register-with-eureka: true ###是否需要从eureka上获取注册信息 fetch-registry: true |
创建springcloud-2.0-order
application.yml
###服务启动端口号 server: port: 8001 ###服务名称(服务注册到eureka名称) spring: application: name: app-ittcf-order ###服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka
###因为该应用为注册中心,不会注册自己 register-with-eureka: true ###是否需要从eureka上获取注册信息 fetch-registry: true |
Ribbon配置
SpringCloud Feign客户端Http调用工具,默认已经整合了Ribbon负载均衡客户端。
配置Feign客户端超时时间
###设置feign客户端超时时间 ribbon: ###指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。 ReadTimeout: 5000 ###指的是建立连接后从服务器读取到可用资源所用的时间。 ConnectTimeout: 5000 |
本文部分素材转载自蚂蚁课堂