1.服务负载均衡设计及特点
一个服务实例可以处理的请求是有限的,假如服务实例的并发访问比较大,我们会启动多个服务实例,让这些服务实例采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,在nacos中服务的负载均衡(nacos客户端负载均衡)是如何应用的?
1.1 LoadBalanceClient应用
LoadBalanceClient对象可以从nacos中基于服务名获取服务实例,然后在工程中基于特点算法实现负载均衡的调用
1.1.1 启动服务消费方,启动服务提供方,两个端口:8081,8082
对于服务提供方,打开idea服务启动配置,修改并发运行选项,再修改sca-provider的配置文件端口,分别以8081,8082端口的方式启动
1.1.2 修改ConsumerController类,注入LoadBalanceClient对象,并添加doRestEcho2方法,然后进行服务访问
/**
* 负载均衡客户端对象
* 注入LoadBalancerClient对象
* 此对象可以从nacos中基于服务名获取服务实例
* 然后再工程中基于特点算法实现负载均衡方式的调用
* 采用一定的负载均衡算法
*/
@Autowired
private LoadBalancerClient loadBalancerClient;
@Value("${spring.application.name:8090}")
private String appName;
//http://localhost:8090/consumer/doRestEcho2
@GetMapping("/consumer/doRestEcho2")
public String doRestEcho2(){
//基于服务名获取服务实例
ServiceInstance serviceInstance =
loadBalancerClient.choose("sca-provider");//serviceId中的服务名
//构建远端服务的url
//基于服务实例中的ip,port构建服务的url
String url = String.format("http://%s:%s/provider/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName);
System.out.println("request url:"+url);
return restTemplate.getForObject(url, String.class);
}
1.1.3 重新启动sca-consumer项目模块,打开浏览器,输入网址进行重复访问
会发现sca-provider的两个服务都可以处理sca-consumer的请求
这里多个实例并发提供服务的方式为负载均衡,这里的负载均衡实现默认是nacos集成了Ribbon来实现的,Ribbon配合ResrTemplate,可以非常容易的实现服务之间的访问.Ribbon是spring cloud 的核心组件之一,它提供的最重要的功能就是客户端的负载均衡(客户端可以在用一定的算法:比如轮询访问,访问服务端的实例信息),这个功能可以让我们轻松地将面向服务的REST模板请求自动转换为客户端负载均衡的服务调用
1.2 @LoadBalanced 应用
1.2.1 在RestTemplate对象构件时,加@LoadBalanced实现负载均衡
当使用RestTemplate进行远程服务调用时,假如需要负载均衡,可以在RestTemplate对象构建时,使用@LoadBalanced对构建RestTemplate的方法进行修饰,例如在ConsumerApplication中构建名字为loadBalancedRestTemplate的RestTemplate对象
/**
* 当我们使用@LoadBalanced注解描述RestTemplate对象时
* 假如此时再基于RestTemplate对象对远程服务进行访问
* 此请求就会被一个拦截器拦截下来
* 请求被拦截到以后,就会现基于服务名找到服务实例
*/
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){//基于此对象实现远端服务调用
return new RestTemplate();
}
1.2.2 在需要RestTemplate实现负载均衡调用的地方进行依赖注入
在ConsumerController类中添加loadBalancedRestTemplate
@Autowired
//@Qualifier("loadBalancedRestTemplate")
private RestTemplate loadBalancedRestTemplate;
1.2.3 在对应的服务端调用方的方法内,基于RestTemplate借助服务名进行服务调用
@GetMapping("/consumer/doRestEcho3")
public String doRestEcho3(){
String url=String.format("http://%s/provider/echo/%s","sca-provider",appName);
return loadBalancedRestTemplate.getForObject(
url,//要请求的服务的地址
String.class);//String.class为请求服务的响应结果类型
}
RestTemplate在发送请求的时候会被LoadBalancerInterceptor拦截,它的作用就是用于RestTemplate的负载均衡,LoadBalancerInterceptor将负载均衡的核心逻辑交给了loadBalanced
@LoadBalanced注解属于Spring,而不是Ribbon的,Spring在初始化容器的时候,如果检测到Bean被@LoadBalanced注解,Spring会为其设置LoadBalancerInterceptor拦截器
*2.面试题
- @Bean注解的作用?(一般用于配置类的内部,用于告诉Spring此方法的返回值要交给spring管理,bean的名字默认为方法名,假如需要指定名字可以@Bean(“bean的名字”),最多的应用场景是为了整合第三方的资源-对象)
- @Autowired注解的作用?(此注解用于描述属性,构造方法,set方法等等,用于告诉spring框架,按照一定的规则为属性进行DI操作,默认按照属性方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字也不同,直接报错)
- nacos中的负责均衡的底层是如何实现的?(通过Ribbon中定义了一些负载均衡的算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务)
- ribbon是什么(netfix公司提供的负载均衡客户端,一般应用于服务的消费方法)
- ribbon可以解决什么问题?(基于负载均衡策略进行服务的调用,所有的策略都会实现rule端口)
- ribbon内置的负载策略有哪些(8种)
- @LoadBalanced的作用是什么?(描述RestTemplate对象,用于告诉Spring框架,在使用RestTemplate进行服务调用时,这个调用过程会被一个拦截器拦截,然后再拦截器的内部,启动负载均衡策略)
- 我们可以自己定义负载均衡策略码?(可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现)
3.基于Feign的远程服务调用
feign是一种声明式web服务客户端,底层封装了Rest技术的应用,通过feign可以简化服务消费方对远程服务提供方法的调用实现
3.1 在服务的消费方,添加项目依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.2 在启动类上添加@EnableFeignClient注解,代码:
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication{...}
3.3 定义http请求API,基于此API借助OpenFeign访问远端服务
package com.jt.consumer.service;
import com.jt.service.factory.ProviderFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 这里的@FeignClient注解用于描述Feign远程服务调用接口
* 假如在项目配置类或启动类上添加了@EnableFeignClients注解
* 系统会在启动时,扫描@FeignClient
* 注解描述的接口
* 并为接口创建实现类对象(代理对象)
* 此对象内部会封装对远程服务调用的过程
* @FeignClient注解中name属性的值有两个层面的含义:
* 1>远程调用服务名
* 2>RemoteProviderService接口类型对象交给spring管理时,这个对象(bean)的名字*/
/*描述的接口底层会为其创建实现类*/
@FeignClient(name = "sca-provider",contextId="remoteProviderService",
fallbackFactory = ProviderFallbackFactory.class)
public interface RemoteProviderService {
@GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
public String echoMessage(@PathVariable("string") String string);
}
其中,@FeignClient描述的接口底层会为其创建实现类
3.4 添加feign访问
@Autowired
private RemoteProviderService remoteProviderService;
@GetMapping("/echo/{msg}")
public String doRestEcho4(@PathVariable String msg){
return remoteProviderService.echoMessage(msg);
}