启动类代码:
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
service层代码:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate resTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
//order 可能为空需要判断
if(order == null){
return null;
}
//获取userId
Long userId = order.getUserId();
//远程调用userService提供的接口获取User数据
String url ="http://localhost:8081/user/"+ userId;
User user = resTemplate.getForObject(url, User.class);
//拼装user数据
order.setUser(user);
// 4.返回
return order;
}
}
以上代码通过RestTemplate实现服务远程调用,这种方式的url(ip+端口)即使部署多台服务,也无法切法,没有起到负载均衡的作用,始终是单实例部署如图:
主要问题:
1、服务调用者不知道服务提供者的地址;
2、如果知道服务提供者的地址,如上图有三个服务 该如何选择?
3、选择一个服务后,如果选中的的服务挂掉,或者有新的服务开启后,无法感知服务状态的变更
注册中心的原理:
- 所有的微服务在启动的时候就去注册中心注册服务名、服务地址 (服务信息包括:ip多少、端口多少、服务名字是什么、可以提供什么方法)这个叫服务注册。
- 注册中心会记录服务信息(注册中心保存服务名称到服务实例地址列表的映射关系)。
- 消费者在调用服务的时候,首选根据服务名去注册中心查询服务的地址信息,然后注册中心向调用者返回服务地址的集合(根据服务名称,拉去实例地址列表) 这个叫服务发现或服务拉取。
- 通过负载均衡策略挑选一个服务进行调用
- 服务提供者还要定期向注册中心发送请求,报告自己状态,称为心跳,当超过一定时间没有发送心跳,注册中心会认为微服务实例故障,然后将实例从服务列表中剔除,从而实现将故障实例排除
注意:一个微服务,既可以是服务提供者,又可以是服务消费者,因此eureka将服务注册、服务发现等功能统一封装到了eureka-client端
如何实现Eureka并调用远程服务?
项目结构如下:
1、搭建EurekaServer
在cloud-demo父工程下,创建一个子模块eureka-server:
在eureka-server模块中的pom文件中导入eureka的starter依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
再编写eureka-server模块的启动类
@SpringBootApplication
@EnableEurekaServer //开启eureka的注册中心功能
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
编写配置文件application.yml
server:
#注册中心端口号
port: 10086
spring:
application:
# 服务名: 需要注册到注册中心
name: eureka-server
eureka:
client:
service-url:
# 注册中心地址
defaultZone: http://127.0.0.1:10086/eureka
eureka-server模块的结构如下:
启动eureka-server服务后,用浏览器访问http://127.0.0.1:10086
2、服务注册
在user-service的pom文件中,引入eureka-client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在user-service中,修改application.yml文件,添加服务名称、eureka地址:
spring:
#注册名称
application:
name: user-service
eureka:
client:
service-url:
# 注册中心地址
defaultZone: http://127.0.0.1:10086/eureka
启用服务 然后查看eureka-server管理页面:
3、服务发现
在order-service的pom文件中,引入下面的eureka-client依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在order-service中,修改application.yml文件,添加服务名称、eureka地址:
spring:
#注册名称
application:
name: order-service
eureka:
client:
service-url:
# 注册中心地址
defaultZone: http://127.0.0.1:10086/eureka
服务拉取和负载均衡(在order-service的OrderApplication中,给RestTemplate这个Bean添加一个@LoadBalanced注解)
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
@LoadBalanced // 启用负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public IRule randomRule(){
return new RandomRule();
}
}
修改order-service服务中的cn.itcast.order.service包下的OrderService类中的queryOrderById方法。修改访问的url路径,用服务名代替ip、端口
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate resTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
//order 可能为空需要判断
if (order == null) {
return null;
}
//获取userId
Long userId = order.getUserId();
//远程调用userService提供的接口获取User数据
//String url ="http://localhost:8081/user/"+ userId; 将url写死的情况
//替换
String url = "http://user-service/user/" + userId;
User user = resTemplate.getForObject(url, User.class);
//拼装user数据
order.setUser(user);
// 4.返回
return order;
}
}
spring会自动帮助我们从eureka-server端,根据userservice这个服务名称,获取实例列表,而后完成负载均衡。