简单理解
接口跨服务调用 说白了 就是原来只是调用一个接口就能得到资源,现在是调用两个或者更多接口,这些接口来自不同的服务
(从前端的角度来看依然只是调用这个接口,只是这个接口背后又去调用其他的接口了)
没有SpringCloud提供的东西也能做到,例如使用最底层的HttpClient,是Java桌面端 和 安卓的一些通信的解决办法,同样使用于JavaWeb
RestTemplate的使用
1、首先任何组件都需要注册到SpringBoot中
package cn.itcast.order; import cn.itcast.feign.clients.UserClient; import cn.itcast.feign.config.DefaultFeignConfiguration; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @MapperScan("cn.itcast.order.mapper") @SpringBootApplication @EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } /** * 创建RestTemplate并注入Spring容器 */ @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } /* @Bean public IRule randomRule() { return new RandomRule(); }*/ }
通常来说这种注册的实例,都会被集中管理到配置类编写
第二步是调用
package cn.itcast.order.service; import cn.itcast.feign.clients.UserClient; import cn.itcast.feign.pojo.User; import cn.itcast.order.mapper.OrderMapper; import cn.itcast.order.pojo.Order; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private UserClient userClient; @Autowired private RestTemplate restTemplate; // public Order queryOrderById(Long orderId) { // // 1.查询订单 // Order order = orderMapper.findById(orderId); // // 2.用Feign远程调用 // User user = userClient.findById(order.getUserId()); // // 3.封装user到Order // order.setUser(user); // // 4.返回 // return order; // } public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2.利用RestTemplate发起http请求,查询用户 // 2.1.url路径 String url = "http://userservice/user/" + order.getUserId(); // 2.2.发送http请求,实现远程调用 User user = restTemplate.getForObject(url, User.class); // 3.封装user到Order order.setUser(user); // 4.返回 return order; } }
提供者 & 消费者 概念
提供者,这个服务提供给其他服务进行消费(所谓消费就是调用的意思,提供也可以是生产的意思)
消费者,这个服务调用其他服务的接口
对于真实的企业架构而言,会存在一个服务链,例如这样 A -> B -> C -> .... 这样
所有提供者和消费者的概念是相对的,也可以说一个服务既可以是提供者,也可以是一个消费者
Eureka 服务注册中心
发现的问题:
public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2.利用RestTemplate发起http请求,查询用户 // 2.1.url路径 String url = "http://userservice/user/" + order.getUserId(); // 2.2.发送http请求,实现远程调用 User user = restTemplate.getForObject(url, User.class); // 3.封装user到Order order.setUser(user); // 4.返回 return order; }
真实开发环境是有
1测试环境DEV
2生产环境PROD
3预生产环境PREPROD
等等
而且一个服务还有可能架设服务集群,服务01服务02服务03...
所以不能写死一个服务地址,在以前没有Eureka注册中心的时候,解决方案是配置文件 + 注释,要切换环境就找注释的环境换回来
配置文件中记载所有的环境信息,但是这些信息只能让我们知道有哪些服务,并不知道服务的健康状态,
所谓健康状态,就是不知道这个服务是否正常运行中的
概念与作用
Eureka本身也是一个服务,这个服务脱离业务需求功能,专门用于注册服务信息
注册中心收集所有的服务信息,这些服务就可以通过Eureka来获取 业务需要的服务信息
被注册的服务就是客户端,注册中心则是服务端
Eureka同时还要检测服务是否正常,如何检测?就是向服务发送请求,如果服务正常响应 那就是正常的,反之则不正常
代码片段(搭建注册中心):
依赖:
<!--eureka服务端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
执行类配置Eureka注解:
package cn.itcast.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
Eureka服务的application.yml配置信息
eureka自身也需要注册进来,因为Eureka作为服务也可能是一个服务集群的存在
server:
port: 10086 # 服务端口
spring:
application:
name: eurekaserver # eureka的服务名称
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
运行Eureka之后访问地址:
http://localhost:10086/
注册服务:
将需要被注册的服务添加Eureka的注册组件,编写注册信息即可
所有需要注册的服务都添加上Eureka注册组件
<!--eureka客户端依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
在服务的application.yml中配置注册信息:
spring:
application:
name: userservice
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
一个服务启动多个实例的操作:
发现服务
被注册的服务都有服务的名称
spring:
application:
name: userservice
名称代表了 ip地址 + 端口
所以在远程调用的时候就可以直接使用服务名称了
String url = "http://userservice/user/" + order.getUserId();
为了实现负载均衡,(在视频的案例中一个服务可以开启集群,他们的名字相同,但是IP和端口会不同)
要保证请求的调用压力都平均的分摊到集群中去,就要解决这个负载均衡的问题
在RestTemplate上注解@LoadBalanced
package cn.itcast.order; import cn.itcast.feign.clients.UserClient; import cn.itcast.feign.config.DefaultFeignConfiguration; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @MapperScan("cn.itcast.order.mapper") @SpringBootApplication @EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } /** * 创建RestTemplate并注入Spring容器 */ @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } /* @Bean public IRule randomRule() { return new RandomRule(); }*/ }
Ribbon 负载均衡组件
实现流程
均衡规则:
1、轮巡调度 RoundRobin
2、重试? Retry
3、随机访问 RandomRule
ZoneAvoidance规则
https://blog.csdn.net/chengqiuming/article/details/81290016
配置均衡策略:
使用Bean实例配置
要使用什么策略,就返回对应的策略实例
package cn.itcast.order; import cn.itcast.feign.clients.UserClient; import cn.itcast.feign.config.DefaultFeignConfiguration; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @MapperScan("cn.itcast.order.mapper") @SpringBootApplication @EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } /** * 创建RestTemplate并注入Spring容器 */ @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } @Bean public IRule randomRule() { return new RandomRule(); } }
第二种配置方式使用application.yml配置
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
Ribbon饥饿加载配置:
Ribbon默认是进行懒加载的,也就是第一次调用会非常慢,以后调用才会正常速度
在订单服务中指定用户服务饥饿加载
也就是消费者中指定提供者配置
ribbon: eager-load: enabled: true # 开启饥饿加载 clients: # 指定饥饿加载的服务名称 - userservice
所以饥渴加载的意义在于降低首次访问的耗时,在服务启动的时候准备就绪