核心知识点
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简化服务消费方对服务提供方远程服务的调用
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实现类
2.在application.yml配置文件中,配置负载均衡策略
#直接在配置文件中的配置,其优势是,可以将配置提取到配置中心 sca-provider: #基于服务名指定负载均衡策略 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
com.netflix.loadbalancer.RandomRule为IRule实现类的绝对路径