本篇随笔为学习Spring Cloud的第一篇,因此我从搭建微服务最基本的服务治理开始入手,之后每一篇都以此篇为基础拓展微服务其他内容。首先看下这篇随笔要完成的任务是什么?请看下图。这是一个最基础的微服务模型,我对图中的组件解析下。
1. 采用了Eureka做注册中心,为了防止单点故障问题,我会启动2个Eureka并且互为向对方注册,这样即可搭建起高可用的一个注册中心。
2. ServiceA为一个微服务应用并向注册中心注册自己,这里同样做了高可用。
3. WebA为一个Web服务同样向注册中心注册自己,该服务是对外暴露的,WebA接受到了用户的http请求后,会向注册中心获取到ServiceA的地址,结合Spring Cloud Ribbon通过负载均衡的方式访问ServiceA。
简单来说就是使用Eureka+Ribbon来实现服务治理。
一、搭建高可用注册中心
因为Eureka也是用java实现的,因此我们搭建起来很容易,这里要说明下我配置文件结构。从图中可以看出我要搭建2个Eureka Server,所以共性的配置需要放在application.properties文件,而application-peer1.properties和application-peer2.properties分别用来起Eureka Server1和Eureka Server2的,Eureka Server1和EurekaServer2分别占用5001和5002端口,并且需要互相向对方注册!
application.properties配置如下:
spring.application.name=eureka-server server.port=5000 #调试环境需要关闭Eureka的自我保护 eureka.server.enable-self-preservation=false #搭建高可用注册中心需要向别的Eureka服务注册自己 eureka.client.register-with-eureka=true #搭建高可用注册中心需要服务发现别的注册中心 eureka.client.fetch-registry=true eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
application-peer1.properties配置如下:
server.port=5001 #向Eureka Server2注册自己 eureka.client.serviceUrl.defaultZone=http://localhost:5002/eureka/
application-peer2.properties配置如下:
server.port=5002 #向Eureka Server1注册自己 eureka.client.serviceUrl.defaultZone=http://localhost:5001/eureka
二、搭建ServiceA
ServiceA这个微服务对内提供服务,主要提供服务写在UserController类里面,为了简单起见,我这里只提供了一个接口。同样的ServerA也是做成高可用的,分别用applicaion-peer1.properties和application-peer2.properties配置2个同的端口作为演示6001端口和6002端口。
UserController类
package com.pumpkin.controller; import com.pumpkin.bean.User; import com.pumpkin.service.UserService; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { private final Logger LOGGER = Logger.getLogger(getClass()); @Autowired private UserService userService; @RequestMapping(value = "/{id}", method = RequestMethod.GET) public User get(@PathVariable String id) { LOGGER.info("request id=" +id); return userService.get(Integer.valueOf(id)); } }
三、WebA
WebA是对外提供服务,一般就是我们一些网站后台之类的,用于响应用户的http请求。这里我们要整合上Ribbon使其可以使用多种策略去访问其他微服务,默认是使用轮询的测试。要使用Ribbon首先要用于Spring Cloud Ribbon的依赖,并且使用Spring依赖注入RestTemplate对象,该对象会使用Ribbon的自动化配置,通过配置@LoadBlanced就可以开始客户端的负载均衡了。
依赖引入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>com.pumpkin</groupId> <artifactId>pumpkin-web-a</artifactId> <version>1.0-SNAPSHOT</version> <name>WebA</name> <description>Spring Cloud Study</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.7.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <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.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</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>
依赖注入RestTemplate对象
package com.pumpkin; 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.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication public class Application { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args){ SpringApplication.run(Application.class, args); } }
webAction
该类主要提供对外服务。restTemplate.getForObject("http://SERVICE-A/user/1", String.class);这里直接使用ServerA的应用名去访问ServerA提供的服务(注意要大写)
package com.pumpkin.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class webAction { private final Logger LOGGER = Logger.getLogger(getClass()); @Autowired RestTemplate restTemplate; @RequestMapping(value = "/hello", method = RequestMethod.GET) public String hello() { LOGGER.info("hello....."); String response = restTemplate.getForObject("http://SERVICE-A/user/1", String.class); return response; } }
application.properties
默认情况下Eureka会通过心跳机制去检查注册上来的服务健康程度,是否可服务。如果希望Eureka通过访问/healthy端点去检查服务的健康情况,可以配置eureka.client.healthcheck.enabled=true,同时如果我们为应用设置了context-path要正确设置eureka.instance.statusPageUrlPath和eureka.instance.healthCheckUrlPath路径,不然Eureka就无法正确获取到我们应用的信息了。
spring.application.name=web-a server.port=8080 #设置web访问前缀 server.context-path=/web #ribbon.eureka.enabled=false #SERVICE-A.ribbon.listOfServers=http://localhost:19210,http://localhost:11862 eureka.client.serviceUrl.defaultZone=http://localhost:5001/eureka/,http://localhost:5002/eureka/ eureka.client.healthcheck.enabled=true management.context-path=/manager eureka.instance.statusPageUrlPath=${server.context-path}/${management.context-path}/info eureka.instance.healthCheckUrlPath=${server.context-path}/${management.context-path}/health
四、测试
最后我们启动一下写好的服务,Eureka和ServiceA可以通过java -jar xxx.jar --spirng.profiles.active=xxx的方式指定配置文件启动,比如ServiceA,可以用命令
java -jar pumpkin-service-a-1.0-SNAPSHOT.jar --spring.profiles.active=peer1和java -jar pumpkin-service-a-1.0-SNAPSHOT.jar --spring.profiles.active=peer2启动。
访问http://127.0.0.1:5001或http://127.0.0.1:5002查看当前注册上了的服务,可以看到WebA和ServiceA都正确注册上来了
访问:http://127.0.0.1:8080/web/hello,并且多次访问,可以看到后台2个ServiceA应用的日志是轮流着打印的。
五、总结
虽然到此Spring Cloud的服务治理就搭建完成,但其实里面还有很多细节可以深究的,比如Eureka如果判断服务是否可用,心跳机制的策略又是如何?Ribbon是如何工作的等等,但是一口气吃不成胖子,一篇随笔也不可能写得了方方面面,今天就从最简单的搭建工作做起,之后再慢慢完善吧。
六、参考资料
Spring Cloud微服务实战-翟永超。本系列的学习都是参考该书籍学习的,同时源码使用的Spring Boot和Spring Cloud的版本也与该书保持一致。
七、源码
待整理,上传码云