Spring Cloud Eureka
搭建服务注册中心
创建eureka-center,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>org.mythsky</groupId> <artifactId>eureka-center</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-center</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.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> <spring-cloud.version>Edgware.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!--<dependency>--> <!--<groupId>org.mybatis.spring.boot</groupId>--> <!--<artifactId>mybatis-spring-boot-starter</artifactId>--> <!--<version>1.3.1</version>--> <!--</dependency>--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</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>
在Spring Boot启动类上添加@EnableEurekaServer
@EnableEurekaServer @SpringBootApplication public class EurekaCenterApplication { public static void main(String[] args) { SpringApplication.run(EurekaCenterApplication.class, args); } }
编辑application.properties
server.port=1111 eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ eureka.server.enable-self-preservation=false
eureka.client.register-with-eureka=false
默认设置下,服务中心会将自己作为客户端来注册自己,所以这里配置为false
eureka.client.fetch-registry=false
禁用检索服务
eureka.server.enable-self-preservation=false
禁用Eureka Server的自我保护机制,Eureka Server在运行期间会统计心跳失败的比例在15分钟内是否低于85%,在单机调试时很容易满足,从而不能正常调用服务
这里还需要注意一个设置,这个设置也是在自我保护机制关闭的情况下生效
eureka.server.eviction-interval-timer-in-ms=5000
这个设置是eureka server 每5秒钟剔除失效的服务,这个配合服务提供端的设置生效
比如服务提供端有如下配置
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.instance.lease-expiration-duration-in-seconds=15
意思是服务提供端每10秒发送一次服务续约,服务的失效时间为15秒,即如果服务端下线,15秒后eureka server将其服务剔除,如果上面的主动剔除时间设为30秒,那么这里会在30秒后剔除服务
还有一个服务消费端获取服务的设置
eureka.client.registry-fetch-interval-seconds=10
表示每10s获取一次服务列表
如果在10s内服务提供端下线并重新上线,服务消费端还没有拉取最新服务地址,会出现连接超时报错
继续将服务提供端下线,10s后消费端重新获取服务地址,此时再调用服务,会提示没有可用服务
运行后访问:http://localhost:1111/,即可看到如下界面:
这里的红色提示是因为我们上面的配置关闭了自我保护。
高可用注册中心
创建application-peer1.properties
spring.application.name=eureka-server server.port=1111 eureka.instance.hostname=peer1 eureka.client.service-url.defaultZone=http://peer2:1112/eureka/
创建application-peer2.properties
spring.application.name=eureka-server server.port=1112 eureka.instance.hostname=peer2 eureka.client.service-url.defaultZone=http://peer1:1111/eureka/
在host中添加转换
127.0.0.1 peer1 127.0.0.1 peer2
通过spring.profiles.active 分别启动peer1 和peer2
java -jar eureka-center-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 java -jar eureka-center-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2
这里要注意,在application-peer2.properties中没配置但在application.properties中有配置的话依旧会读取application.properties中的内容
还需要注意的是eureka.instance.prefer-ip-address=true这个配置,在这个配置下eureka.client.service-url.defaultZone=http://${spring.security.user.name}:${spring.security.user.password}@172.26.10.110:8090/eureka/
这里可以用IP+port,此时可以不设置eureka.instance.hostname
如果eureka.instance.prefer-ip-address=false,eureka.client.service-url.defaultZone里面可以用域名yourdomain:8090/eureka,此时必须设置eureka.instance.hostname=yourdomain
从下面的界面即可看到
打开http://localhost:1111/
打开http://localhost:1112/
这里可以看到replicas都是不可用的
修复上面的问题,正确结果应该如下
这里还有两个重要属性
eureka.instance.lease-renewal-interval-in-seconds=30 eureka.instance.lease-expiration-duration-in-seconds=90
第一个用于定义服务续约任务的调用时间,默认30秒
第二个用于定义服务失效的时间,默认90秒
服务提供者
新建一个Spring Boot项目,添加pom依赖
<?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>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.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> <spring-cloud.version>Edgware.RELEASE</spring-cloud.version> </properties> <dependencies> <!--<dependency>--> <!--<groupId>org.mybatis.spring.boot</groupId>--> <!--<artifactId>mybatis-spring-boot-starter</artifactId>--> <!--<version>1.3.1</version>--> <!--</dependency>--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</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>
在入口添加@EnableDiscoveryClient
@EnableDiscoveryClient @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
添加一个控制器
@RestController public class HelloController { private final Logger logger=Logger.getLogger(String.valueOf(getClass())); @Autowired private DiscoveryClient client; @RequestMapping(value = "/hello",method = RequestMethod.GET) public String index(){ ServiceInstance instance=client.getLocalServiceInstance(); logger.info("/hello,host:"+instance.getHost()+", service_id:"+instance.getServiceId()); return "hello world"; } }
新版接口中无法使用client.getLocalServiceInstance()
更改如下
String serviceId=client.getServices().get(0);
ServiceInstance instance=client.getInstances(serviceId).get(0);
在配置中应用服务名称及服务中心地址
server.port=8080 management.security.enabled=false spring.application.name=hello-service eureka.client.service-url.defaultZone=http://localhost:1111/eureka/,http://localhost:1112/eureka/
启动服务,在Eureka界面即可看到注册的服务
同时在控制台中可以看到日志
在Idea中点击Exit
可以看到服务立刻下线
如果是点击上面的Stop,并不能立刻看到服务下线,而是等到上面的轮询时间才能看到。
服务发现和消费
新建Spring Boot项目,取名为ribbon-consumer,pom如下
<?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>org.mythsky</groupId> <artifactId>ribbon-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>ribbon-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.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> <spring-cloud.version>Edgware.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</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>
应用入口处添加@EnableDiscoveryClient和@LoadBalanced开启客户端负载均衡
@EnableDiscoveryClient @SpringBootApplication public class RibbonConsumerApplication { @Bean @LoadBalanced RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RibbonConsumerApplication.class, args); } }
添加控制器
@RestController public class ConsumerController { @Autowired RestTemplate restTemplate; @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET) public String helloConsumer(){ return restTemplate.getForEntity("http://hello-service/hello",String.class).getBody(); } }
修改配置
spring.application.name=ribbon-consumer server.port=9000 eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
启动应用,访问http://localhost:9000/ribbon-consumer,会看到返回了服务提供的信息
此时在Eureka界面可以看到
多次刷新消费页面,观察两个注册中心的日志,可以看到两个中心轮流提供服务
启用安全认证
在注册中心启用security
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
配置
spring.security.user.name=user spring.security.user.password=pwd123 eureka.client.service-url.defaultZone=http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
由于spring默认开启csrf,所以做如下修改
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); super.configure(http); } }
服务提供者配置
service.username=user service.password=pwd123 eureka.client.service-url.defaultZone=http://${service.username}:${service.password}@localhost:1111/eureka/
这样就能实现安全认证了。