Eureka主要负责微服务框架中的服务治理功能,它分为Eureka服务端(服务注册中心)和Eureka客户端(处理服务的注册与发现)。这可是springcloud最牛逼的小弟,任何小弟需要其它小弟支持什么都需要从这里来拿,同样你有什么绝活都需要到Eureka服务端报道,供其它小弟调用;它的好处是你不需要对接其他小弟,只需要到服务中心来领取,也不需要知道提供支持的小弟在哪里,反正拿来用就行。
负载均衡是对系统高可用、网络压力的缓解和处理能力扩容的重要手段之一。我们常说的负载均衡指的是服务端负载均衡,分为硬件负载均衡(在服务器节点间安装用于负载均衡的设备)和软件负载均衡(在服务器上安装具有负载均衡的软件来完成请求分发工作)。只要是服务端负载均衡都类似于下图的框架,负载均衡设备维护一个下挂可用的服务端清单,通过心跳检测剔除故障服务端节点,当客户端发送请求时,该设备按某种算法(如线性轮询,按权重/流量负载)从清单中取出一个服务端地址,然后进行转发。
Ribbon是一个基于HTTP和TCP的客户端负载均衡工具。与服务端负载均衡不同点在于服务清单所存储的位置,客户端负载均衡中,所有客户节点都维护着自己要访问的服务端清单,而这些清单来自于服务注册中心,如Eureka服务端。
本例子中Spring Boot版本为1.5.13,Spring Cloud版本为Edgware.SR3,两者要根据官方的规定配合使用,不然会报莫名的错误。
(1)服务注册中心
创建一个Spring Boot工程,命名为cloud_eureka_server,并在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">
<modelVersion>4.0.0</modelVersion>
<groupId>iwhale</groupId>
<artifactId>cloud_eureka_server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cloud_eureka_server</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<!--idea自动提示引入依赖,但是没有需要的版本,如spring-cloud-starter-eureka-server。这就是一个典型的版本老旧的问题。
建议大家不易一个一个去定义,直接使用dependencyManagement 自动引入即可-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在应用中添加注解@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class CloudEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaServerApplication.class, args);
}
}
在配置文件application.yml添加一下配置:
server:
port: 8888
eureka:
instance:
hostname: localhost
lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳后,等下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90s。
lease-renewal-interval-in-seconds: 10 #服务续约间隔时间
server:
enable-self-preservation: false #关闭保护机制,以确保注册中心可以将不可用的实例正确剔除
client:
register-with-eureka: false #不注册自己
fetch-registry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
(2)服务提供者
创建一个Spring Boot工程,命名为cloud_service,pom.xml内容与注册中心一样
在应用中添加注解@EnableDiscoveryClient,获得服务发现的能力
@EnableDiscoveryClient
@SpringBootApplication
public class CloudServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceApplication.class, args);
}
}
编写请求处理接口/hello,注入DiscoveryClient对象
@RestController
public class HelloController {
@Autowired
private DiscoveryClient client;
@RequestMapping("hello")String hello(HttpServletRequest request) {
String s=request.getParameter("para");
ServiceInstance instance=client.getLocalServiceInstance();
return "host:"+instance.getHost()+" ServiceId:"+instance.getServiceId()+
" port:"+instance.getPort()+" 参数为:"+s;
}
}
在配置文件application.yml指定服务命名cloudservice,指定服务注册中心地址,添加一下配置:
server:
port: 9881
# context-path: /${spring.application.name}
#restTemplate.getForEntity("http://cloudservice/hello",String.class).getBody(); 这样访问时需注释掉context-path
#############################spring配置#############################
spring:
application:
name: cloudservice
#############################eureka配置#############################
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8888/eureka/
eureka-server-read-timeout-seconds: 180
(3)服务消费者
服务消费者一般是web前端发起请求,创建一个Spring Boot工程,命名为cloud_web,pom.xml中dependencies内容如下,其他内容相同,新增的依赖模块是spring-boot-starter-ribbon
<dependencies>
<!--全栈web开发模块,包含tomcat,SpringMVC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--放在tomcat运行时,排除内置的tomcat-->
<!--<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>-->
</dependency>
<!--通用测试模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--监控管理模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
在应用主类添加注解@EnableDiscoveryClient,同时创建RestTemplate的Spring Bean实例,并通过@LoadBalanced注解开启客户端的负载均衡。
@EnableDiscoveryClient
@SpringBootApplication
public class CloudWebApplication {
/*Spring提供的用于访问Rest服务的客户端*/
@Bean
@LoadBalanced //开启客户端负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(CloudWebApplication.class, args);
}
}
创建CloudWebController类并实现/helloFang接口,在该接口中,通过上面创建的RestTemplate实现对cloudservice服务提供的/hello接口进行调用。可看到访问的地址是服务名cloudservice,而不是一个具体的地址。
@RestController
public class CloudWebController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/helloFang",method = RequestMethod.GET)
public String helloFang(){
ResponseEntity<String> p=restTemplate.getForEntity("http://cloudservice/hello?para={1}",String.class,"hjy");
return p.getBody();
}
}
在配置文件application.yml指定服务命名cloudweb,指定服务注册中心地址,添加一下配置:
server:
port: 9884
context-path: /${spring.application.name}
#############################spring配置#############################
spring:
application:
name: cloudweb
############################################################
# spring-boot-starter-actuator,监控管理,查看端点信息,部分端点的访问需要鉴权,可将安全校验关闭,生产环境下需设为true
management:
security:
enabled: true
context-path: /actuator #为了端点安全,增加前缀
endpoints:
health:
path: /checkHealth #修改/health端点的原始路径
#############################eureka配置#############################
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8888/eureka/
eureka-server-read-timeout-seconds: 180
instance:
health-check-url-path: /${endpoints.health.path} #修改/health端点的原始路径
(4)测试
- 首先在idea中开启注册中心eureka服务
- 将工程cloud_service打成jar包,命名为cloud_service.jar,可在windows下运行两个服务,端口分别为9881、9882
- 在idea中启动cloud_web工程
查看8888端口,可看到有3个服务已注册到服务中心
通过向http://localhost:9884/cloudweb/helloFang发起GET请求,成功返回端口为9881的服务cloudservice的信息,再刷新页面,发现返回端口为9882的服务信息,可以发现以轮询的方式交替访问两个不同的cloudservice服务,可用来判断cloudweb对cloudservice的调用是否负载均衡。
过程中发现存在问题,cloudservice设置了context-path,http://localhost:9884/cloudweb/helloFang就访问不到/hello接口,报以下错误:org.springframework.web.client.HttpClientErrorException: 404 null
解决方法是把context-path注释掉,至于为啥我也不知道。。。。