一、引言
在正式开始学习之前我们需要先掌握两个微服务中的概念,服务提供者(Service Provider)和服务消费者(Service Consumer),我们知道在微服项目中,由于服务的职责单一性,数据的独立性,我们需要其他数据库数据时,需要发送HTTP请求跨服务读取另一数据库的内容。此时被请求的一方即提供者,请求方为消费者。
而面对大量的跨服务请求我们如果手动在各个服务里硬编码HTTP请求路径将非常不利于集群的开发和维护,我们今天将使用Eureka解决这一问题。
二、Eureka简介
Spring Cloud Eureka 是 Netflix 开源的一个服务发现框架,它被集成到了 Spring Cloud 中,提供了服务注册与发现的功能。在微服务架构中,服务实例可能会动态地增加或减少,服务发现机制允许各个服务实例能够互相感知对方的存在。Spring Cloud Eureka 提供了一个简单而强大的服务注册与发现机制,使得在微服务架构中管理和调用服务变得更加容易。
三、入门项目
1.创建Eureka服务端:
-
创建一个Maven工程,用于搭建Eureka服务端。
-
在
pom.xml
文件中导入Eureka Server的依赖。
<dependencies>
<!-- eureka服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
-
在
application.yml
文件中配置Eureka Server的相关信息。
server:
port: 7071
# 注册eureka服务
spring:
application:
name: eurekaserver #eureka的服务器名称
eureka:
client:
service-url: #eureka的地址信息
defaultZone: http://localhost:10086/eureka
register-with-eureka: false # 不向注册中心注册自己
fetch-registry: false # 不从注册中心抓取服务
-
创建主启动类,并在主启动类上添加
@EnableEurekaServer
注解,以启动Eureka Server。@EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
-
启动Eureka注册中心。然后,可以通过浏览器访问
http://localhost:7001/
来查看Eureka注册中心的界面。
2.搭建服务提供者:
-
创建一个新的Maven工程,用于搭建服务提供者。
-
在
pom.xml
文件中,导入Eureka Client的依赖和Spring Boot的启动依赖等。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!-- eureka客户端依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
在
application.yml
文件中,配置服务提供者的相关信息(主要配置为spring.application.name和eureka)。server: port: 8081 spring: datasource: url: username: password: driver-class-name: com.mysql.jdbc.Driver application: name: userservice mybatis: type-aliases-package: cn.cds.user.pojo configuration: map-underscore-to-camel-case: true logging: level: cn.itcast: debug pattern: dateformat: MM-dd HH:mm:ss:SSS eureka: client: service-url: #eureka的地址信息 defaultZone: http://localhost:7071/eureka
-
创建Controller,用于对外提供服务。
import cn.cds.user.pojo.User; import cn.cds.user.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; /** * @param id 用户id * @return 用户 */ @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id) { return userService.queryById(id); } }
-
在主启动类上添加
@EnableEurekaClient
注解,以启用Eureka Client。import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.mybatis.spring.annotation.MapperScan; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @MapperScan("cn.cds.user.mapper") @SpringBootApplication @EnableEurekaClient public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }
3.创建服务消费者:
重复服务提供者搭建的所有操作,做一个用于请求服务提供者的接口,不同的是,为了模拟实际业务场景,我们所配置的数据库和端口皆不同于已创建的其他服务。
import cn.cds.order.pojo.Order;
import cn.cds.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
return orderService.queryOrderById(orderId);
}
}
并且在Service层不再需要硬编码请求地址,改为注册中心中的服务器名即可
import cn.cds.order.mapper.OrderMapper;
import cn.cds.order.pojo.Order;
import cn.cds.order.pojo.User;
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 RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
//2.利用RestTemplate发起http请求,查询用户
//注意此处url
String url="http://userservice/user/"+order.getUserId();
User user = restTemplate.getForObject(url, User.class);
//3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
}
此时我们再通过浏览器访问 http://localhost:7001/
来查看Eureka注册中心的界面,userservice和orderservice均以注册。
4.Ribbon负载均衡测试
另外我们再扩展一小部分,我们知道,在真实的业务场景中一个服务可能对应多个实例或服务器,在微服务架构中,负载均衡通常用于服务网关,以分发进入的API请求到后端的微服务实例。
-
我们再复制一个userservice
-
启动UserApplication2再去浏览器的注册中心查看,此时列表中有两个userservice
-
在服务消费者的配置类中为RestTemplate添加@LoadBalanced注解,使拦截器对其拦截并进行IRule规则的负载均衡
/** * 创建RestTemplate并注入IOC容器,发送http请求 */ @LoadBalanced @Bean public RestTemplate restTemplate(){ return new RestTemplate(); }
-
负载均衡测试
我们两次调用服物消费者所在接口,可以看到两次请求分别根据负载均衡规则请求到了8082和8081端口