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

核心知识点

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拦截器

基于Feign进行远程服务调用

出生背景

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

概念

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

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

 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

负载均衡策略配置

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

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

IRule实现类

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

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

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

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

上一篇:makedown过滤标签解决黑产内容通过<>规避词库检测的问题


下一篇:【XSY3479】子区间(扫描线)