为了解决单块式架构可用性低,可伸缩性差,集中发布的生命周期以及违反单一功能原则,微服务(Microservice)应运而生了,将功能按照边界拆分为单个服务,但是每个服务之间的通讯如何解决?Spring Cloud 的出现为我们解决分布式开发中常用的问题给出了完整的方案。Spring Cloud 基于Spring Boot ,为我们提供了配置管理,服务发现,断路器,代理服务等。基于Spring Cloud的开发特别适合在Docker或者其他专业的PaaS部署,所以又称作原生云应用。
spring cloud中文文档:https://www.springcloud.cc/spring-cloud-dalston.html
Spring Cloud 提供了 Config Server,可以集中存储所有应用的配置文件,支持在文件系统或者git中放置的配置文件,用注解@EnableConfigServer启用配置服务。
Spring Cloud 通过 Netflix OSS 的 Eureka 来实现服务发现,服务发现的目的是为了让每个服务之间可以互相通信。Eureka Server为微服务注册中心,Spring Cloud
使用注解的方式提供了Eureka服务端(@EnableEurekaServer)和客户端(@EnableEurekaClient)。
路由网关的作用是让所有的微服务对外只有一个接口,我们只需要访问一个网关地址,可以由网管将我们的请求代理到不同的服务中。Spring Cloud 通过 Zuul来实现的,
支持自动路由映射 Eureka Server 上注册的服务。Spring Cloud 提供了注解@EnableZuulProxy启用路由代理。
Spring Cloud 提供了Ribbon和Feign来实现负载均衡。在Spring Cloud 下,使用Ribbo直接注入一个RestTemplate对象即可,RestTemplate已经做好了负载均衡的配置;
使用Feign需要定义一个注解,有@FeignClient注解的接口,然后使用@RequestMapping注解到方法上映射远程的REST服务,此方法也是做好负责均衡配置的。
断路器(Circuit Breaker)主要是为了解决当某个方法调用失败时,调用后备方法来达到容错,阻止级联错误等功能。Spring Cloud 使用注解 @EnableCircuitBreaker开启断
路器支持,使用 @HystrixCommand注解的fallbackMethod 来指定后备方法。还给我们提供了一个控制台来监控断路器的运行情况,通过注解@EnableHystrixDashboard来开启。
新建模块化的gradle项目cloud-main-manage,项目结构及其每个微服务功能说明如下:
springBootVersion = '1.5.4.RELEASE'
springCloudVersion = 'Dalston.SR1'
根目录配置文件说明
settings.gradle 内容
rootProject.name = 'cloud-main-manager' include 'discovery' include 'config' include 'person' include 'ui' include 'monitor'build.gradle里添加的dependency是对所有子模块都有效的,在子模块不需要额外添加这些依赖:
dependencies { compile('org.springframework.boot:spring-boot-starter-web') compile('org.springframework.boot:spring-boot-starter-actuator') compile('org.springframework.cloud:spring-cloud-starter') testCompile('org.springframework.boot:spring-boot-starter-test') }以下粘贴每个微服务的关键代码,代码说明都在对应的注释里
dependencies { //服务发现依赖于Eureka Server compile('org.springframework.cloud:spring-cloud-starter-eureka-server') }
@SpringBootApplication @EnableEurekaServer //开启对Eureka Server的支持 public class DiscoveryApplication { public static void main(String[] args) { SpringApplication.run(DiscoveryApplication.class, args); } }
server: port: 8761 #当前Eureka Server的服务器端口号 eureka: instance: hostname: localhost #当前Eureka Server的hostname client: register-with-eureka: false #当前服务不需要注册到Eureka Server fetch-registry: false
dependencies { //配置服务依赖 compile('org.springframework.cloud:spring-cloud-config-server') //Eureka客户端依赖 compile('org.springframework.cloud:spring-cloud-starter-eureka') }
@SpringBootApplication @EnableConfigServer//开启配置服务器支持 @EnableEurekaClient//开启Eureka Server的客户端支持 public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
#Spring Cloud 应用提供使用bootstrap.yml 负责从外部资源加载配置熟悉 spring: application: name: config #在Eureka Server 注册的服务名为config profiles: active: native #配置服务器使用本地配置(默认为git) eureka: instance: non-secure-port: ${server.port:8888} #若环境变量中的server.port有值则用,没有用8080 metadata-map: instanceId: ${spring.application.name}:${random.value} #配置在Eureka Server中的实例ID client: service-url: defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ #Eureka客户端设置Eureka Server的地址
#application.yml 配置其他应用所需的配置文件位于类路径下的config目录 #配置文件命名规则:应用名+profile.yml spring: cloud: config: server: native: search-locations: classpath:/config server: port: 8888
为person服务提供配置文件
dependencies { // config server 配置依赖 compile('org.springframework.cloud:spring-cloud-config-client') // Eureka 客户端依赖 compile('org.springframework.cloud:spring-cloud-starter-eureka') }
@SpringBootApplication @EnableDiscoveryClient //在这里与@EnableEurekaClient的功能一样 public class PersonApplication { public static void main(String[] args) { SpringApplication.run(PersonApplication.class, args); } }
@RestController public class PersonController { @Value("${my.message}") private String message; @RequestMapping(value = "/save", method = RequestMethod.POST) public Person savePerson(@RequestBody String name){ Person person = new Person(name); System.out.println(message); return person; } }
spring: application: name: person cloud: config: enabled: true discovery: enabled: true service-id: CONFIG #指定Config Server的服务名,通过Eureka Server 发现Config Server。获取person.yml eureka: instance: non-secure-port: ${server.port:8082} client: service-url: defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/
dependencies { // config Server 配置依赖 compile('org.springframework.cloud:spring-cloud-config-client') // Eureka 客户端依赖 compile('org.springframework.cloud:spring-cloud-starter-eureka') // 断路器依赖 compile('org.springframework.cloud:spring-cloud-starter-hystrix') // 支持路由网关依赖 compile('org.springframework.cloud:spring-cloud-starter-zuul') // feign负责均衡依赖 compile('org.springframework.cloud:spring-cloud-starter-feign') // ribbon负责均衡依赖 compile('org.springframework.cloud:spring-cloud-starter-ribbon') }
@SpringBootApplication @EnableEurekaClient @EnableFeignClients //开启feign客户端支持 @EnableCircuitBreaker // 开启断路器的支持 @EnableZuulProxy // 开启网关代理支持 public class UiApplication { public static void main(String[] args) { SpringApplication.run(UiApplication.class, args); } }
@FeignClient("person") //使用person调用person server,person为服务名 public interface PersonService { @RequestMapping(method = RequestMethod.POST, value = "/save", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody Person save(@RequestBody String name); }
@Service public class PersonHystrixService { @Autowired PersonService personService; //使用HystrixCommand的fallbackMethod参数指定,当本方法调用失败时调用后路方法fallbackSave @HystrixCommand(fallbackMethod = "fallbackSave") public Person save(String name){ return personService.save(name); } public Person fallbackSave(String name){ Person person = new Person(name+"person service 异常"); return person; } }注意:本方法与后来方法的参数要一致,否则程序异常:@HystrixCommand fallback method wasn't found
@RestController public class PersonController { @Autowired PersonHystrixService personHystrixService; @RequestMapping(value = "/save",method = RequestMethod.GET) public Person save(){ return personHystrixService.save("xianjj"); } }
配置省略
dependencies { //断路器依赖 compile('org.springframework.cloud:spring-cloud-starter-hystrix-dashboard') compile('org.springframework.cloud:spring-cloud-starter-turbine') }
@SpringBootApplication @EnableEurekaClient @EnableHystrixDashboard @EnableTurbine public class MonitorApplication { public static void main(String[] args) { SpringApplication.run(MonitorApplication.class, args); } }
配置省略
依次启动discovery,config服务,其余微服务部分顺序启动,最后启动monitor。
1.访问http://127.0.0.1:8761/,查看发现服务 Eureka Server
2.访问UI服务的controller接口http://127.0.0.1/save
成功时返回:
关闭person服务,调用接口异常(feign找不到rest服务),执行后备方法
3.断路器监控
访问:http://127.0.0.1:8989/hystrix.stream
输入:http://127.0.0.1/hystrix.stream
github源码下载地址:https://github.com/jeofey/cloud-main-manage
该项目示例源码下载:http://download.csdn.net/detail/jeofey/9881810
Spring Cloud中文官方网站:https://www.springcloud.cc/
注意事项:
为方便阅读,在配置文件中添加了相应的注释,若项目启动时报异常:Caused by: org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1
可以将application.yml文件中的注释内容删掉,再启动;由于YMAL的语法校验比较严格。