Eureka
在传统的 RPC 远程调用框架中,管理每个服务之间的依赖关系比较复杂,管理也就比较复杂,所以需要使用服务治理,管理服务之间的依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前服务器的信息,比如服务地址和通讯地址以别名方式注册到注册中心上。消费者以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地 RPC 调用。
Eureka 采用了 CS 的设计架构,Eureka Sever 作为服务注册功能的服务器,它是服务注册中心。而系统中的其它微服务,使用 Eureka Client 连接到 Eureka Server 并维持心跳连接。这样就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。
搭建 Eureka Server
准备工作
创建一个 Module,一个空的 Maven 项目。并导入下面的依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>ml.guest997</groupId> <artifactId>Commons</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> </dependency> </dependencies>
|
创建主启动类。
1 2 3 4 5 6 7 8 9 10 11 12 13
| package ml.guest997;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication @EnableEurekaServer public class EurekaServer { public static void main(String[] args) { SpringApplication.run(EurekaServer.class, args); } }
|
编写配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server: port: 7001
eureka: instance: hostname: localhost client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
|
测试
运行模块之后,浏览器访问 127.0.0.1:7001,出现如下页面就算成功。
![]()
注册服务
导入依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
|
编写配置文件
1 2 3 4 5 6 7 8 9 10 11
| spring: application: name: order
eureka: client: register-with-eureka: true fetchRegistry: true service-url: defaultZone: http://localhost:7001/eureka
|
启动 Client
1 2 3 4 5 6 7 8 9 10 11 12 13
| package ml.guest997;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication @EnableEurekaClient public class Order { public static void main(String[] args) { SpringApplication.run(Order.class, args); } }
|
测试
在将之前的两个模块都按照上面的步骤进行服务注册之后,启动所有的模块,浏览器访问 127.0.0.1:7001,就会发现之前的两个模块都注册进去了。
![]()
搭建 Eureka 集群(相互注册)
模拟多个 Eureka Server 地址
修改 hosts 文件。
1 2
| 127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com
|
搭建另一个 Eureka Server
跟上面的步骤是一样的,就不再赘述了。
更改配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server: port: 7001
eureka: instance: hostname: eureka7001.com client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://eureka7002.com:7002/eureka/
|
1 2 3 4 5 6 7 8 9 10 11 12
| server: port: 7002
eureka: instance: hostname: eureka7002.com client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://eureka7001.com:7001/eureka/
|
测试
浏览器分别访问:eureka7001.com:7001 和 eureka7002.com:7002 会发现,在 DS Replicas 项下各自都多了一个相互的域名地址。
注册服务2
要想将之前的模块注册进 Eureka 集群中,只需要改下配置文件,将 defaultZone 设置为多个即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server: port: 80
spring: application: name: order
eureka: client: register-with-eureka: true fetchRegistry: true service-url: defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
|
支付模块集群配置
需要新建一个支付模块,也是更之前一样的步骤,就不再赘述了。注意:记得修改新模块的端口。
修改订单模块的 Config
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package ml.guest997.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate;
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
|
修改订单模块的 Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package ml.guest997.controller;
import ml.guest997.pojo.CommonResult; import ml.guest997.pojo.Payment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController public class OrderController { public static final String PAYMENT_URL = "http://PAYMENT";
@Resource private RestTemplate restTemplate;
@GetMapping("/consumer/payment/add") public CommonResult add(Payment payment) { return restTemplate.postForObject(PAYMENT_URL + "/payment/add", payment, CommonResult.class); }
@GetMapping("/consumer/payment/get/{id}") public CommonResult<Payment> getPayment(@PathVariable("id") Long id) { return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class); } }
|
修改支付模块的 Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package ml.guest997.controller;
import lombok.extern.slf4j.Slf4j; import ml.guest997.pojo.CommonResult; import ml.guest997.pojo.Payment; import ml.guest997.service.PaymentService; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController @Slf4j public class PaymentController { @Resource private PaymentService paymentService; @Value("${server.port}") private String serverPort;
@PostMapping("/payment/add") public CommonResult add(@RequestBody Payment payment) { int res = paymentService.add(payment); log.info("插入结果:" + res); if (res > 0) { return new CommonResult(200, "插入数据库成功,支付服务端口为:" + serverPort, res); } else { return new CommonResult(400, "插入数据库失败"); } }
@GetMapping(value = "/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) { Payment payment = paymentService.getPaymentById(id); if (payment != null) { return new CommonResult(200, "查询成功", payment); } else { return new CommonResult(404, "没有对应记录,查询 id 为:" + id); } } }
|
测试
启动所有的模块,浏览器访问 127.0.0.1/consumer/payment/add?serial=Guest004,会发现页面显示的端口在来回切换,就说明负载均衡功能生效了。