1.什么是eureka?
Eureka 由 Netflix 开发,是一种基于REST(Representational State Transfer)的服务,用于定位服务(服务注册与发现),以实现中间层服务的负载均衡和故障转移,此服务被称为 Eureka Server。同时,它还附带了基于 Java 的客户端组件:Eureka Client,它使得客户端与 Eureka Server 的交互变得更加的容易。
- Register(服务注册):把自己的IP和端口注册给Eureka。
- Renew(服务续约):发送心跳包,每30秒发送一次。告诉Eureka自己还活着。
- Cancel(服务下线):当provider关闭时会向Eureka发送消息,把自己从服务列表中删除。防止consumer调用到不存在的服务。
- Get Registry(获取服务注册列表):获取其他服务列表。
- Replicate(集群中数据同步):eureka集群中的数据复制与同步。
- Make Remote Call(远程调用):完成服务的远程调用。
Eureka Server
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
2.环境搭建
eureka主要作为注册中心使用,搭建2节点集群
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>eureka</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<dependencies>
<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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
noode1
server:
port: 8761
eureka:
instance:
hostname: node1 # eureka name
prefer-ip-address: false
client:
fetch-registry: false
register-with-eureka: true
# eureka url
service-url:
defaultZone: http://node2:8762/eureka
spring:
application:
name: "eureka-server-ha"
management:
endpoints:
web:
exposure:
include: "*"
node2
server:
port: 8762
eureka:
instance:
hostname: node2 # eureka name
prefer-ip-address: false
client:
fetch-registry: false
register-with-eureka: true
# eureka url
service-url:
defaultZone: http://node1:8761/eureka
spring:
application:
name: "eureka-server-ha"
management:
endpoints:
web:
exposure:
include: "*"
启动类
package com.et.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
启动node1和node2,访问http://node2:8762/
3.代码工程
实验目标
实现消费者调用生产者服务
生产者
提供服务接口 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>eureka</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-provider</artifactId>
<dependencies>
<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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
controller
package com.er.eureka.provider.controller;
import com.er.eureka.provider.model.Student;
import com.er.eureka.provider.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
@RestController
@RequestMapping("/student")
public class StudentHandler {
@Autowired
private StudentService studentService;
@Value("${server.port}")
private String port;
@GetMapping("/findAll")
public Collection findAll(){
return studentService.findAll();
}
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id){
return studentService.findById(id);
}
@PostMapping("/save")
public void save(@RequestBody Student student){
studentService.saveOrUpdate(student);
}
@PutMapping("/update")
public void update(@RequestBody Student student){
studentService.saveOrUpdate(student);
}
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id){
studentService.deleteById(id);
}
@GetMapping("/index")
public String index(){
return "port:"+this.port;
}
}
service
package com.er.eureka.provider.service;
import com.er.eureka.provider.model.Student;
import java.util.Collection;
public interface StudentService {
public Collection findAll();
public Student findById(long id);
public void saveOrUpdate(Student student);
public void deleteById(long id);
}
package com.er.eureka.provider.service;
import com.er.eureka.provider.model.Student;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@Repository
public class StudentServiceImpl implements StudentService {
private static Map studentMap;
static {
studentMap = new HashMap<>();
studentMap.put(1L,new Student(1L,"zhangsan",22));
studentMap.put(2L,new Student(2L,"lisi",23));
studentMap.put(3L,new Student(3L,"wangwu",24));
}
@Override
public Collection findAll() {
return studentMap.values();
}
@Override
public Student findById(long id) {
return (Student) studentMap.get(id);
}
@Override
public void saveOrUpdate(Student student) {
studentMap.put(student.getId(),student);
}
@Override
public void deleteById(long id) {
studentMap.remove(id);
}
}
application.yaml
server:
port: 8011
spring:
application:
name: provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
management:
endpoints:
web:
exposure:
include: "*"
启动类
package com.er.eureka.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
消费者
调用服务者提供的接口 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>eureka</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-consumer</artifactId>
<dependencies>
<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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
controller
package com.et.eureka.consumer.controller;
import com.et.eureka.consumer.model.Student;
import com.et.eureka.consumer.service.MyFeignClient;
import com.netflix.appinfo.InstanceInfo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
import java.util.List;
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerHandler {
@Autowired
RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@Autowired
private MyFeignClient myFeignClient;
@GetMapping("/findAll")
public String findAll(){
return myFeignClient.findAll();
}
@GetMapping("/findAll2")
public Collection findAll2(){
return restTemplate.getForObject("http://PROVIDER/student/findAll",Collection.class);
}
@GetMapping("/findAll3")
public String findAll3(){
RestTemplate restTemplate = new RestTemplate();
List<ServiceInstance> instances = discoveryClient.getInstances("PROVIDER");
if (instances == null || instances.size() == 0) {
log.info("instances is empty..");
return "";
}
String uri = instances.get(0).getUri().toString();
String serviceUrl = String.format("%s/student/findAll", uri);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity(headers);
log.info("uri is :{},formatted url is:{}", uri, serviceUrl);
ResponseEntity<String> resultExchange = restTemplate.exchange(serviceUrl, HttpMethod.GET, entity, String.class, "");
if (resultExchange == null) {
log.info("invoke result is null");
return "";
}
String body = resultExchange.getBody();
log.info("body is :{}", body);
return body;
}
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id){
return restTemplate.getForEntity("http://PROVIDER/student/findById/{id}",Student.class,id).getBody();
}
@GetMapping("/findById2/{id}")
public Student findById2(@PathVariable("id") long id){
return restTemplate.getForObject("http://PROVIDER/student/findById/{id}",Student.class,id);
}
@PostMapping("/save")
public void save(@RequestBody Student student){
restTemplate.postForEntity("http://PROVIDER/student/save",student,null).getBody();
}
@PostMapping("/save2")
public void save2(@RequestBody Student student){
restTemplate.postForObject("http://PROVIDER/student/save",student,null);
}
@PutMapping("/update")
public void update(@RequestBody Student student){
restTemplate.put("http://PROVIDER/student/update",student);
}
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id){
restTemplate.delete("http://PROVIDER/student/deleteById/{id}",id);
}
}
service
package com.et.eureka.consumer.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "provider")
public interface MyFeignClient {
@GetMapping("/student/findAll")
String findAll();
}
启动类
package com.et.eureka.consumer;
import com.netflix.discovery.DiscoveryClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
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;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
application.yaml
server:
port: 8020
spring:
application:
name: consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
management:
endpoints:
web:
exposure:
include: "*"
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
- https://github.com/Harries/springcloud-demo(eureka)
4.测试
- 启动node1
- 启动node2
- 启动服务端
- 启动消费端
可以看到服务者和消费者都注册到eureka注册中心上
访问http://127.0.0.1:8020/consumer/findAll,会调用服务服务端接口并返回数据。
5.引用
- Spring Cloud Netflix