提供者和消费者
在学习Eureka前,我们先来了解下提供者和消费者的概念。
- 服务提供者:暴露接口给其它微服务调用
- 服务消费者:调用其它微服务提供的接口
- 提供者和消费者角色是相对的,一个服务既可以是提供者也可以是消费者
服务调用问题
- 服务消费者该如何获取服务提供者的地址信息?
- 如果有多个服务提供者,消费者该如何选择?
- 消费者如何得知提供者的健康状态呢?
Eureka作用
Eureka可以分为eureka-server服务端和eureka-client客户端。服务端主要提供服务注册与发现的功能,客户端则包括服务提供者和服务消费者。服务提供者会向注册中心注册服务信息,服务消费者拉取服务信息。
以当前项目为例,假设我们又加了两个user-service服务模块,端口分别为8082和8083。
- user-service的3个服务会注册服务信息到eureka-server注册中心
- 而上面我们也说过,一个服务既可能是提供者也可能是消费者,所以order-service也会注册信息到eureka-server。
- 因为业务需求,order-service服务需要调用user-service的信息,这时order-service会向注册中心询问是否有注册的user-service服务,有的话,拉取user-service服务信息。
- order-service拉取完信息后,发现有3个user-service,那么它怎么去判断调用哪个服务地址呢。这时候需要用到负载均衡的算法,从服务列表中选择一个,比如这里选择了localhost:8081。
- 远程调用发送请求到localhost:8081,获取用户信息。
那么怎么保证注册中心的服务是健康的呢?服务提供者每隔30s会向注册中心发送一次心跳,告知Eureka自己是正常状态。如果Eureka在心跳周期内未收到某个服务的心跳,Eureka就会更新服务列表,将该服务从注册中心踢出。这样,服务消费者拉取最新服务列表,就不会获取到挂掉的服务了。
搭建Eureka
Eureka本身就是一个微服务,所以首先创建一个eureka-server服务模块,添加eureka依赖,这里报错的话可以刷新下maven重启idea试试
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud-demo</artifactId>
<groupId>cn.lsy.demo</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
开启注解@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
配置文件
server:
port: 10086 # 服务端口
spring:
application:
name: eurekaserver # eureka的服务名称
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
端口和名称我们好理解,这个地址信息是干啥的,多此一举?并不是,Eureka会把自己的地址信息也注册进去。听起来是不是很奇怪,我注册我自己?如果升级到Eureka集群就好理解了,我们会配置多个Eureka地址信息。
我们来看一下eureka-server服务的包结构
怎么样,是不是很简单?接下来我们启动起来看一下
可以看到,启动成功。这样我们就完成了Eureka的初步搭建。
服务注册
搭建完Eureka服务后,我们就可以将我们的服务注册到Eureka里面去了。
主要分为两步:
以user-service为例,在<dependencies>中引入依赖
<!--eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置文件中添加应用名称和eureka的地址信息
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: 123123
driver-class-name: com.mysql.jdbc.Driver
application:
name: userservice
mybatis:
type-aliases-package: cn.lsy.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.lsy: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
启动服务
打开eureka服务地址,可以看到,user-service已经注册进来了
同理,我们把order-service也注册进来,这里代码就不截图了,就是服务名称不一样,命名为orderservice
为了模拟多个服务负载均衡的情况,我们这里再开启一个user-service。
怎么快速复制服务呢,idea为我们提供了复制配置的功能。我们点击UserApplication,点击复制配置
弹出如下框
我们点击修改选项,添加VM选项
将端口改为8082,不然会端口冲突,并且名称重命名为UserApplication2,点击应用,确定
-Dserver.port=8082
可以看到,左下角多了一个未启动的UserApplication2
我们启动UserApplication2,启动成功
我们来刷新浏览器看下Eureka的注册地址列表
ok,很完美~注册成功!
服务发现
那么现在通过eureka,我们的调用方式有什么不同呢?
- 将原先的请求地址从ip端口的形式改成Eureka中服务名形式。
- 使用@LoadBalanced注解赋予restTemplate解析服务地址和负载均衡的能力。(不加会报错噢,当不使用@LoadBalanced注解的时候,SpringCloud框架不会解析服务名来获取IP地址,会报一个找不到userservice的错误)
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
User user = restTemplate.getForObject("http://userservice/user/"+order.getUserId(), User.class);
order.setUser(user);
// 4.返回
return order;
}
}
@MapperScan("cn.lsy.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();
}
}
重启orderservice服务,我们试着请求几次接口试试
请求成功,我们再看下日志输出
可以看到,我们请求了4次,均匀分发到了两个userservice服务地址上,因为@LoadBalanced默认采用的是轮询策略。这样就简单实现了服务的负载均衡请求啦~