为什么要用springcloud呢?
springcloud中这么多个微服务,每个微服务都是用的http协议去互相调用的,里都有很多的接口,需要其他的微服务硬编码到代码中,这样不是每次修改都要改其他调用这个接口的其他微服务吗?
我觉得
- 首先,spring的生态的强大是我们用springcloud的一点原因。
- 然后我们每个微服务就算用不同语言也可以互相调用,因为用的是http协议。
- 最后就是灵活,怎么说呢?微服务直接用的http协议进行互相调用,http又是无状态的,我们经常对于同一个微服务进行集群扩展(提供同样的服务),那我们调用者服务对这个微服务集群进行调用,这个集群中的服务可以灵活的上下线也不会影响到调用方。
所以接下来总结一下springcloud微服务直接怎么调用
RestTemplate
在上一篇文章中eureka的文章中,我们注入了RestTemplate对象,传入url和responseType,就可以调用相关服务的接口,并且返回信息后自动帮我们转换成我们需要的responseType的类型。
我们的RestTemplate对象是单例的,所以我们可以写一个RestTemplateConfiguration的类,然后把RestTemplate对象注册到springioc容器中。@LoadBalanced可以实现负载均衡
@Configuration
public class RestTemplateConfiguration {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
RestTemplate还有一个拦截器,可以拦截请求发出和请求返回,我们先新建一个类HttpRequestInterceptor 并且实现ClientHttpRequestInterceptr接口 ,如图
public class HttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
System.out.println("拦截到啦");
System.out.println(httpRequest.getURI());
ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest,bytes);
System.out.println(response.getHeaders());
return response;
}
}
然后再我们的RestTemplate配置类里加上我们的拦截器对象
@Configuration
public class RestTemplateConfiguration {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new HttpRequestInterceptor());
return restTemplate;
}
}
当我们用RestTemplate调用服务时,就会到我们的拦截其中,被拦截到。
Feign
Feign本身不支持springMVC的注解
OpenFeign是springcloud在Feign的基础上支持springMVC的注解
OpenFeign的@FeignClient可以解析springMVC的@RequestMapping注解下的接口,通过动态代理产生实现类,在实现类中做负载均衡并调用其他服务。
首先我们建一个微服务,user-provider,引入spring-web和eruekaClient依赖
先配置好user-provider的properties
server.port=82
spring.application.name=user-provider
#这里的地址是provider去寻找服务的地址和端口
eureka.client.service-url.defaultZone=http://eureka1.com:7001/eureka/
在user-provider中写一个controller服务
@RestController
public class UserProviderController {
@GetMapping("/alive")
public String alive(){
return "ok";
}
}
当然还有建一个微服务,user-consumer,引入spring-web和eruekaClient还有openfeign的依赖
先配置好user-consumer的properties
server.port=83
spring.application.name=user-consumer
#这里的地址是provider去寻找服务的地址和端口
eureka.client.service-url.defaultZone=http://eureka1.com:7001/eureka/
在user-consumer服务的项目入口,加上注解
@SpringBootApplication
@EnableFeignClients
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class, args);
}
}
然后我们可以在user-consumer中新建一个接口,FeignClient可以吧url和GetMapping进行拼接
//这里name是eureka里的服务名
@FeignClient(name = "user-provider")
public interface UserAPI {
@GetMapping("/alive")
public String alive();
}
然后我们写一个user-consumer的controller服务
@RestController
public class UserConsumerController {
@Autowired
UserAPI userAPI;
@GetMapping("/alive")
public String alive(){
return userAPI.alive();
}
}
这样也可以调用成功!!!
会发现以上这种方式和restTemplate一样,需要provider写一个接口文档,如果只是java与java,可以用一下方法。
首先可以先起个接口项目,引入spring-web引用
写一个Api接口
@RequestMapping("/User")
public interface Api {
@GetMapping("/alive")
public String alive();
}
然后用maven打包
然后user-consumer引入打包好的maven项目
<!--引入自定义的api-->
<dependency>
<groupId>com.example</groupId>
<artifactId>user-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
然后user-consumer就只需要继承这个引入的api
//这里name是eureka里的服务名
@FeignClient(name = "user-provider")
public interface UserAPI extends ProviderApi {
}
然后controller就可以直接用啦。然后user-provider的controller也可以同样的引入这个jar包,然后实现这个接口,就可以重写这个接口啦,这样做就可以只修改这个独立的接口项目,对于服务提供者和服务调用者都可以去根据这个接口实现业务代码,一个接口就可以规范两边的服务去实现或者调用,就很棒 !!!!!!!!!!!!!!!!!!!!!!!!
提醒:因为Feign底层是根据注解去获取服务名,还有拼接url,注意还有参数,所以我们的api,里面的参数必须加@RequestParam("xx"),不然的话会无法调通接口,中间的api里的所有注解都是给Feign提供的,Resolved[org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
@RequestMapping("/user")
public interface ProviderApi {
@GetMapping("/alive")
public String alive();
@GetMapping("/getMapById")
public HashMap getMapById(@RequestParam("id") Integer id);
}