3.服务器负载均衡的实现

目录

核心知识点

1.@LoadBalanced

@LoadBalanced使用方式

2.基于Feign进行远程服务调用

出生背景

概念

 Feign应用

Feign配置进阶

3.负载均衡策略配置

常见问题分析

LoadbalnancerClient对象的作用

Ribbon是什么

你是如何理解Feign的

你是如何使用Feign的

Feign的调用原理(底层代理)

服务熔断后你是如何进行记录的


核心知识点

1.@LoadBalanced

当RestTemplante进行远程服务调用时,假如需要负载均衡,我们可以使用@LoadBalanced注解,该注解作用是在RestTemplante对象进行远程服务调用时,底层自动使用拦截器,根据服务名调用服务实例,从一个服务实例中拿到ip和端口,把接收到的url进行重构,拦截器底层用的就是LoadBalancrClient机制,然后根据重构后的url进行服务的调用

注意:此方式虽然代码简单了,因为多了一个拦截器对象,性能会降低一点

@LoadBalanced使用方式

第一步:在主启动类创建具有能被拦截的RestTemplate组件

@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
    return new RestTemplate();
}

第二步:在使用RestTemplate的泪中注入该对象,然后基于RestTemplate进行服务的调用

@Autowired
private RestTemplate loadBalancedRestTemplate;

@GetMapping("/consumer/doRestEcho3")
public String doRestEcho03(){
    String url=String.format("http://%s/provider/echo/%s","sca-provider",appName);
    //向服务提供方发起http请求,获取响应数据
    return loadBalancedRestTemplate.getForObject(
            url,//要请求的服务的地址
            String.class);//String.class为请求服务的响应结果类型
}

第三步:测试

RestTemplate对象进行服务调用时,被LoadBalancerInterceptor拦截,他的作用就是用于RestTemplate的负载均衡, LoadBalancerInterceptor将负载均衡的核心交给LoadBalancer,核心代码如下:

public ClientHttpResponse intercept(final HttpRequest request, 
    final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
    final URI originalUri = request.getURI();
    String serviceName = originalUri.getHost();
    return this.loadBalancer.execute(serviceName, 
    requestFactory.createRequest(request, body, execution));
}

注意:@LoadBalancer注解是属于spring的,而不是Ribbon的,在spring容器启动时,如果检测到Bean组件被@LoadBalanced注解修饰,speing就会为其生成 LoadBalancerInterceptor拦截器

2.基于Feign进行远程服务调用

出生背景

服务消费方基于RestTemplate方式进行服务调用,一种直接的方式就是自己拼接url,拼接参数后进行服务调用,这种方式太过复杂

概念

feign是一种声明式web客户端,底层封装了对RestTemplate技术的应用,通过Feign简化服务消费方对服务提供方远程服务的调用

 Feign应用

第一步:在服务消费方添加Feign依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

第二步:在主启动类添加@EenableFeignClients

/**
 * @EnableFeignClients 用于描述启动类或配置类,
 * 此时项目在启动时,就会启动一个FeignStarter组件,
 * 这个组件会对项目中使用了@FeignClient的类创建代理对象
 * */
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);   
}

}

 第三步:定义http请求API,基于此API借助OpenFeign进行远端服务

package com.jt.consumer.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * @FeignClient 该注解描述的接口为远程服务调用的接口
 * 此接口中我们应该定义这些元素:
 * 1)调用什么服务?(一般用服务名进行标识)
 * 2)访问服务中的什么资源?(数据,资源的标识--url)
 * 3)对服务中的资源进行什么操作?(CRUD)
 * 4)资源访问不到怎么办?()
 * 操作资源过程中出现异常怎么办?(使用fallbackFactory属性,进行容错处理)
 * 5)此对象默认bean名称是什么?(假如没有指定,默认就是name或value的值,
 * 推荐基于contextId手动指定一个bean名字)
 * 6)此接口对象交给spring容器管理,spring创建此接口的代理实现类,
 * 基于代理实现类创建代理对象
 * */
@FeignClient(name = "sca-provider",contextId = "remoteProviderService",
        fallbackFactory = ProviderFallbackFactory.class)
public interface RemoteProviderService {
    //Rest风格:通过URI定义资源,通过请求动作操作资源
    @GetMapping("/provider/echo/{msg}")
    String echoMessage(@PathVariable("msg") String msg);
}

其中,@FeignClient描述的接口,底层会自动为其创建实现类

第四步:创建FeignConsumerController并添加Feign功能访问

package com.jt.consumer.controller;
@RestController
@RequestMapping("/consumer/ ")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;
    /**基于feign方式的服务调用*/
    @GetMapping("/echo/{msg}")
    public String doFeignEcho(@PathVariable  String msg){
        //基于feign方式进行远端服务调用(前提是服务必须存在)
        return remoteProviderService.echoMessage(msg);
    }
}

 第五步:测试

注意:使用Feign进行远端服务调用,底层会自动基于Robbin实现负载均衡

Feign配置进阶

通常一个服务提供方会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用的接口,如果我们没有在接口中指定contexId,服务就会启动失败,例如,再添加一个接口

 @FeignClient(name="sca-provider")
 public interface RemoteOtherService {
     @GetMapping("/doSomeThing")
     public String doSomeThing();
}

出现以下错误

The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled. 

 @FeignClient注解修饰的接口默认生成对象的名字为该注解value或name属性的值,我们需要在远程服务接口中指定该注解的名字,作为远程服务调用的唯一标识符,使用contexId属性

@FeignClient(name="sca-provider",contextId="remoteProviderService")//sca-provider为服务提供者名称
interface RemoteProviderService{
    @GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
    public String echoMessage(@PathVariable("string") String string);
}

当我们进行远程服务调用时,连接超时或者服务不可用时,一般需要在服务消费端给出具体的容错方案,例如: 在Feign应用中,通过实现FallbackFactory接口的实现类进行默认的相关处理

第一步:创建FallbackFactory接口的实现类,进行默认处理

package com.jt.consumer.service;

import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


/**
 * feign回调接口工厂对象,基于此对象创建feign接口实现类对象
 * 这个实现类对象,用于做容错处理(有备无患,这里做熔断处理)
 * */

@Slf4j //创建Logger实现类对象,对象名字就是log
@Component //将该类的对象交给spring容器管理
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
    //创建logger日志对象
//    private static final Logger log =
//            LoggerFactory.getLogger(ProviderFallbackFactory.class);

    @Override
    public RemoteProviderService create(Throwable throwable) {
//        return new RemoteProviderService() {
//            @Override
//            public String echoMessage(String msg) {
//                return "服务暂时不可用,请稍后再试!!";
//            }
//        };

        //lambda表达式
        return msg -> {
            //使用logger日志对象
            log.error("error: {}","sca-provider服务调用失败");
            return "服务暂时不可用,稍等片刻再访问!";
        };
    }
}

 第二步:在Feign访问的接口中,使用fallbackFactroy属性,应用FallbackFactroy对象

@FeignClient(name = "sca-provider",contextId = "remoteProviderService",
        fallbackFactory = ProviderFallbackFactory.class)
public interface RemoteProviderService {
    //Rest风格:通过URI定义资源,通过请求动作操作资源
    @GetMapping("/provider/echo/{msg}")
    String echoMessage(@PathVariable("msg") String msg);
}

 第三步:在application.yml配置文件中,启动熔断机制,当访问出错时,就停止访问

feign:
  hystrix:
    enabled: true #启动熔断处理,默认为false

3.负载均衡策略配置

1.在主启动类加载IRulebean组件,返回指定的负载均衡策略类对象

  /**
     * 指定负载均衡策略
     * 具体方案:
     * 1.构建IRule接口实现类对象
     * 2.将对象交给spring容器管理
     * 3.返回指定的策略实现类
     * */
@Bean
public IRule rule(){
    return new RandomRule();
}

IRule实现类

2.在application.yml配置文件中,配置负载均衡策略

#直接在配置文件中的配置,其优势是,可以将配置提取到配置中心
sca-provider: #基于服务名指定负载均衡策略
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

 com.netflix.loadbalancer.RandomRule为IRule实现类的绝对路径

常见问题分析

LoadbalnancerClient对象的作用

从nacos获取服务实例,然后在本地负载均衡,LoadBalancerClient的具体是实现是RibbonLoadBanancerClient,调用choose方法根据服务名获取服务实例

public class RibbonLoadBalancerClient implements LoadBalancerClient {
@Override
public ServiceInstance choose(String serviceId) {
   return choose(serviceId, null);
}
}

Ribbon是什么

一个负载均衡组件,其中定义了一些负载均衡算法,nacos的负载均衡都是通过Ribbon实现的

Ribbon详解 - 简书

你是如何理解Feign的

远程服务调用对象,基于声明式服务调用的定义即可

你是如何使用Feign的

1.依赖导入

2.启动类使用@EnableFeignClients注解

3.定义接口,使用@FeignClient(name = "sca-provider",contextId = "remoteProviderService", fallbackFactory = ProviderFallbackFactory.class)注解

4.使用该接口定义的具体方法来调用服务

5.熔断处理,在配置文件开启熔断机制,定义FallbackFactory的实现类,重置返回的错误内容,将这个类的对象交给声明服务的接口

Feign的调用原理(底层代理)

1.通过@EnableFeignClient注解告诉spring cloud,启动Feign start 组件

2.Feign start会在启动时注册全局配置,扫描包下所有由@FeignClient标识的接口,底层会为其创建对应的实现类(jdk代理),并创建出来类的对象,交给spring容器管理(注册IOC容器)

3.Feign接口被调用时,底层代理会将接口中的请求信息通过编码器创建区Reuest对象,基于此对象进行远程服务调用

4.Feign客户端请求对象会经过Ribbon进行负载均衡,挑选出一个健康的服务实例

5.Feign客户端会携带Request进行远端服务调用,并返回一个响应

6.Feign客户端会对响应进行一个解析并返还给客户端

服务熔断后你是如何进行记录的

使用Slf4j-日志门面

第一种方式:创建Logger对象,使用Logger对象

//创建logger日志对象
private static final Logger log =
        LoggerFactory.getLogger(ProviderFallbackFactory.class);  

 @Override
    public RemoteProviderService create(Throwable throwable) {
//        return new RemoteProviderService() {
//            @Override
//            public String echoMessage(String msg) {
//                return "服务暂时不可用,请稍后再试!!";
//            }
//        };

        //lambda表达式
        return msg -> {
            //使用logger日志对象
            log.error("error: {}","sca-provider服务调用失败");
            return "服务暂时不可用,稍等片刻再访问!";
        };
    }

第二种方式:使用@Slfj注解

/**
 * feign回调接口工厂对象,基于此对象创建feign接口实现类对象
 * 这个实现类对象,用于做容错处理(有备无患,这里做熔断处理)
 * */

@Slf4j //创建Logger实现类对象,对象名字就是log
@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
    //创建logger日志对象
//    private static final Logger log =
//            LoggerFactory.getLogger(ProviderFallbackFactory.class);

    @Override
    public RemoteProviderService create(Throwable throwable) {
//        return new RemoteProviderService() {
//            @Override
//            public String echoMessage(String msg) {
//                return "服务暂时不可用,请稍后再试!!";
//            }
//        };

        //lambda表达式
        return msg -> {
            //使用logger日志对象
            log.error("error: {}","sca-provider服务调用失败");
            return "服务暂时不可用,稍等片刻再访问!";
        };
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值