RestTemplate对象:
spring web模块中提供了一个RestTemplate对象,基于此对象可以完成远程(Remote)服务的
调用
@Bean
public RestTemplate restTemplate(){//基于此对象实现远端服务调用
return new RestTemplate();
}
1.从spring容器中获取RestTemplate对象的方法中实现对远程服务
2.定义远程服务的url
3.基于RestTemplate的对象中得方法进行服务调用
package com.jt.consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 定义服务消费端Controller,在这个Controller对象
* 的方法中实现对远端服务sca-provider的调用
*/
@RestController
public class ConsumerController {
/**
* 从spring容器获取一个RestTemplate对象,
* 基于此对象实现远端服务调用
*/
@Autowired
private RestTemplate restTemplate;
/**
* 在此方法中通过一个RestTemplate对象调用远端sca-provider中的服务
* @return
* 访问此方法的url: http://localhost:8090/consumer/doRestEcho1
*/
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho01(){
//1.定义要调用的远端服务的url
String url="http://localhost:8081/provider/echo/8090";
//2.基于restTemplate对象中的相关方法进行服务调用
return restTemplate.getForObject(url, String.class);
}
}
远程服务调用成功:
报错分析:500 404
![](https://i-blog.csdnimg.cn/blog_migrate/8ca221a1fee90a4d5c2176cf29244974.png)
服务调用时底层404问题,例如:
服务调用时,连接异常,例如:
LoadBalancerClient应用
服务负载均衡设计及实现(重点)
loadBalancaerClient对象可以基于服务名从nacos中获取多个服务实例、并且基于一定负载均衡算法进行远程服务调用。
1.基于服务名获取服务实例
2.基于服务实例构建要访问的url
服务实例.getHost() 获取ip地址 服务实例.getPort() 获取端口号
@Autowired
private LoadBalancerClient loadBalancerClient;
@Value("${spring.application.name:8090}")
private String appName;
@GetMapping("/consumer/doRestEcho02")
public String doRestEcho02(){
//基于服务名获取服务实例
ServiceInstance serviceInstance = loadBalancerClient.choose("sca-provider");
//基于服务实例构建要访问的url
//serviceInstance.getHost() 获取ip地址
//serviceInstance.getPort() 获取端口号
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);
}
}
流程:
@LoadBalanced
当我们使用@loadBalanced注解描述RestTemplate对象时,假如此时再基于RestTemplate对象对远程服务进行远程服务进行访问时 ,就会先被一个拦截器拦截下来,请求被拦截到以后就会先基于服务名找到服务实例(负载均衡)
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
return new RestTemplate();
}
如何定义算法: 当默然的负载均衡算法不满足我的时候我们可以自定义算法:
基于ribbon中的IRwle接口,可以自定义算法。
基于Feign的远程服务调用(重点)
feign 是一种声明式web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现。
添加feign的添加依赖:
feign 依赖(此依赖添加以后,会为我们的项目注入一个feign starter组件)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第二步:在启动类上添加@EnableFeignClients注解
@EnableFeignClients作用启动一个feignStarter组件,组件的作用是扫描@FeignClient下的接口, 为相关的接口构建实现类,在实现类的内部对远端服务的调用。
package com.jt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){//基于此对象实现远端服务调用
return new RestTemplate();
}
定义Http请求API,基于此API借助OpenFeign访问远端服务
这里的@feignClient注解用于描述feign远成服务调用接口,假如在项目配置类或者启动类上添加了
@EnableFeignClients注解,系统会在启动时,扫描@feignClient注解描述的接口,并为接口创建实现类对象(这个实现类我们称为代理对象),此对象内部会封装对远程服务调用的过程。
@feignClient注解中name属性的值有两个层面:
1)RemoteProviderService接口类型对象交给spring管理时,这个对象(bea)的名字
2)远程调用服务名
package com.jt.consumer.controller.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
//name是远程服务的名字
@FeignClient(name = "sca-provider3")
public interface RemoteProviderService {
@GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
public String echoMessage(@PathVariable("string") String string);
}
创建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客户端进行访问,如图所示(反复刷新检测其响应结果):
feign错误演示:
404:
Feign配置进阶实践:
一个服务提供方通常会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务
启动就会失败,例如,假如在服务消费方再添加一个如下接口,消费方启动时就会启动失败,例如:
@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.
此时我们需要为远程调用服务接口指定一个contextId,作为远程调用服务的唯一标识(这个标识是Bean对象的名字)即可,例如:
@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.cy.service.factory;
/**
* 基于此对象处理RemoteProviderService接口调用时出现的服务中断,超时等问题
*/
@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
/**
* 此方法会在RemoteProviderService接口服务调用时,出现了异常后执行.
* @param throwable 用于接收异常
*/
@Override
public RemoteProviderService create(Throwable throwable) {
return (msg)->{
return "服务维护中,稍等片刻再访问";
};
}
}
:在Feign访问接口中应用FallbackFactory对象:
@FeignClient(name = "sca-provider", contextId = "remoteProviderService",
fallbackFactory = ProviderFallbackFactory.class)//sca-provider为nacos中的服务名
public interface RemoteProviderService {
@GetMapping("/provider/echo/{msg}")
public String echoMsg(@PathVariable String msg);
}
:在配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制.
feign:
hystrix:
enabled: true #默认值为false