对于各个服务都配置到注册中心,我们需要一个技术使得这些服务能够互相调用使用。在原始服务调用是采用httpclient,之后演变成restTemplate(由spring提供的访问rest服务的模板工具集)。
1.Ribbon:基于Netflix Ribbon实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。
2.Ribbon工作流程:首先选择EurekaServer,优先选择同一个区域内负载较少的server。再在根据用户指定的策略,从server中取到的服务注册列表中选择一个地址。
3.Ribbon是采用LB(Load Balance)负载均衡:将用户的多个请求平均分摊到多个服务上面。和我们所认知的NGINX负载均衡不同,Nginx是服务器的负债均衡,客户端将所有请求交给Nginx,再由Nginx实现转发请求,即负载均衡是由服务器完成。
4.DiscoveryClient:对于注册eureka里面的微服务,可以通过服务发现来获得该服务的信息。需要在主启动类上加@EnableDiscoveryClient
@GetMapping("/payment/discovery")
public Object discovery(){
// 获取注册中心的服务
List<String> services = discoveryClient.getServices();
for(String element: services){
log.info("***element***"+element);
}
// 获取指定服务名的服务信息
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for(ServiceInstance instance:instances){
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+
instance.getUri());
}
return this.discoveryClient;
}
5.RestTemplate有两种调用服务方法:postForObject和postForEntity
//static final String PAYMENT_URL = "http://localhost:8081";
static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
/** postForObject(返回对象为响应体中数据转化为对象:Json)
* 1.url: 请求地址
* 2.responseType: 返回的数据
*/
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPaymentEntity(@PathVariable("id") Long id){
/** getForEntity(返回responseEntity对象含有相应中一些信息响应头、响应状态码) 返回需要加上getBody方法
* 1.url: 请求地址
* 2.responseType: 返回的数据
*/
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
log.info("ResponseEntity{}",entity);
return entity.getBody();
}else{
return new CommonResult<>(400,"操作失败");
}
}
补充:对于Eureka集群 消费端不应该再去写死地址而是写服务名称(注册中心名字;大写),并且加上@LoadBalanced注解(默认是轮询机制)
6.Ribbon自带7种负载规则:
①com.netflix.loadbalancer.RoundRobinRule(轮询)
②com.netflix.loadbalancer.RandomRule(随机)
③com.netflix.loadbalancer.RetryRule(先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务)
④WeightedResponseTimeRule(对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择)
⑤BestAvailableRule(会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务)
⑥AvailabilityFilteringRule(先过滤掉故障实例,再选择并发较小的实例)
⑦ZoneAvoidanceRule(默认规则,复合判断server所在区域的性能和server的可用性选择服务器)
7.Ribbon的替换规则:这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。不能去掉@LoadBalanced
8.负载均衡算法:rest第几次请求书%(取余)服务集群总数 = 实际调用服务器位置下标,每次重启rest从1开始。
注意:手写算法调用需要去掉@LoadBalanced注解
public interface LoadBalancer {
ServiceInstance serviceInstance(List<ServiceInstance> serviceInstances);
}
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
/**
* 获取当前请求次数
* @return
*/
public final int getAndIncrement(){
int current;
int next;
do{
current = this.atomicInteger.get();
next = current >+ Integer.MAX_VALUE ? 0:current + 1;
}while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("*********next访问次数为:"+next);
return next;
}
/**
* 负载均衡算法:
* 第几次请求书%(取余)服务集群总数 = 实际调用服务器位置下标,每次重启rest从1开始。
* @param serviceInstances
* @return
*/
@Override
public ServiceInstance serviceInstance(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
controller层代码:
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if(instances == null || instances.size() <= 0) {
return null;
}
ServiceInstance serviceInstance = loadBalancer.serviceInstance(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb",String.class);
}
注意当这边一个服务挂掉都也能访问该服务但是会报错。
源码:
服务出错和结果:
1.Feign:一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。只需创建一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
2.Ribbon+RestTemplate是模板化调用服务(一个接口被多处调用),而feign只需要定义服务绑定接口以声明式方法。
3.Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需 创建一个接口并在接口上添加注解即可
application.yml
server:
port: 80
eureka:
client:
register-with-eureka: false
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
logging:
level:
# feign日志以什么级别监控哪个接口
com.charon.springcloud.service.PaymentFeignService: debug
主启动类:
@SpringBootApplication
@EnableFeignClients
public class OpenFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OpenFeignMain80.class,args);
}
}
Feign调用(接口)
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();
}
4.openFeign一般默认是1秒钟,得不到就返回报错。所以有时候需要超时控制。
5.OpenFeign提供了日志打印的功能,了解Feign中Http请求的细节。说白了就是对于日志的监控和输出。
NULL:默认,不显示日志。
BASIC:仅记录请求方法,url,响应状态码。
HEADERS:出来了BASIC外还有请求和响应信息。
FULL:除了HEADERS外还有请求和响应的正文元数据。
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}