将传统的单块式架构按照定义好的边界切割成独立的组件。Spring cloud为分布式的微服务架构提供了解决方案。
Spring Cloud提供了Config Server。可以在git或者文件系统中集中的放置配置文件。
Spring Cloud提供了注解@EnableConfigServer来启用配置服务。
Spring Cloud使用Eureka作为服务的注册中心。
使用注解的方式提供了Eureka服务端(@EnableEurekaServer)和客户端(@EnableEurekaClient)
使用网关地址将请求代理到不同的服务当中。
Spring Cloud使用Zuul来实现路由网关,支持自动路由映射到在Eureka Server上注册的服务,Spring Cloud提供了注解@EnableZuulProxy来启用路由代理。
Spring CLoud提供了Ribbon和Feign作为客户端的负载均衡。
断路器主要是为了解决当某个方法调用失败的时候,调用后备方法来替代失败的方法,以达到容错,阻止级联错误等功能。
Spring Cloud使用@EnableCircuitBreaker来启用断路器支持,使用@HystrixCommand的fallbackMethod来指定后备方法。
示例由6个微服务组成。
先建一个父模块,把一些公用的依赖引入父模块
<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.0modelVersion>
<groupId>com.xhfgroupId>
<artifactId>SpringCloudSampleartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<name>SpringCloudSample-parentname>
<parent>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-parentartifactId>
<version>Angel.SR3version>
<relativePath />
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
dependencies>
<modules>
<module>configmodule>
<module>discoverymodule>
<module>personmodule>
<module>somemodule>
<module>uimodule>
<module>monitormodule>
modules>
project>
spring-cloud-starter-parent 是 spring cloud 基础的 jar 包依赖,必须要有的。
添加依赖,spring-cloud-starter-eureka-server 作为 eureka 作为服务注册和发现的服务端的依赖添加。
<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.0modelVersion>
<parent>
<groupId>com.xhfgroupId>
<artifactId>SpringCloudSampleartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>discoveryartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eureka-serverartifactId>
dependency>
dependencies>
project>
启动类,使用@EnableEurekaServer开启 eurekaServer 的支持。
package com.xhf.discovery;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryApplication.class, args);
}
}
配置文件 application.yml,详情见注解
server:
port: 8761 #服务端口
eureka:
instance:
hostname: localhost #eureka实例的hostname
client:
register-with-eureka: false #这个服务就不用注册到eureka上了,本身就是注册中心,
fetch-registry: false #不从注册中心去拉取注册的服务,不需要使用别的服务了
依赖引入, spring-cloud-config-server 作为配置服务的依赖,spring-cloud-starter-eureka 作为使用 eureka 服务注册中心的依赖
<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.0modelVersion>
<parent>
<groupId>com.xhfgroupId>
<artifactId>SpringCloudSampleartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>configartifactId>
<name>configname>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
dependencies>
project>
启动类,使用 @EnableConfigServer 开启配置服务器的支持, @EnableEurekaClient 开启作为 eureka server 客户端的支持,这个服务会注册到服务注册中心
package com.xhf.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
配置文件有两个,bootstrap.yml 会先于 application.yml加载。通常由于application.yml可能用到配置中心的一些配置,所以会把配置中心的一些配置放到bootstrap.yml当中。方便这些信息先行加载。
application.yml
spring:
cloud:
config:
server:
native:
search-locations: classpath:/config #表示配置中心在本地的config目录
server:
port: 8888 #服务的端口,掉用这个服务提供的rest服务时要用到
bootstrap.yml
spring:
application:
name: config #在eureka中注册的服务名config
profiles:
active: native #配置服务器使用本地存储
eureka:
instance:
non-secure-port: ${server.port:8888} #注册到eureka的端口,非ssl端口,默认为8888
metadata-map:
instanceId: ${spring.application.name}:${random.value} #注册到eureka中的实例ID
client:
service-url:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ # eureka server的地址,找到这个服务,把config服务注册上去
依赖引入,spring-cloud-config-client 是作为使用配置服务中心的客户端所需依赖。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.hsqldbgroupId>
<artifactId>hsqldbartifactId>
dependency>
<dependency>
<groupId>postgresqlgroupId>
<artifactId>postgresqlartifactId>
<version>9.1-901-1.jdbc4version>
dependency>
dependencies>
启动类,就是开启了eureka client的支持,将服务注册到服务注册中心去。
package com.xhf.person;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class PersonApplication {
public static void main(String[] args) {
SpringApplication.run(PersonApplication.class, args);
}
}
Rest服务, PersonRepository 是作为数据操作的接口,和spring cloud其实没有太大关系,不详解。
添加一个 /save 的 rest 服务。
package com.xhf.person.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.xhf.person.dao.PersonRepository;
import com.xhf.person.domain.Person;
@RestController
public class PersonController {
@Autowired
PersonRepository personRepository;
@RequestMapping(value="/save", method=RequestMethod.POST)
public List<Person> savePerson(@RequestBody String personName) {
Person p = new Person(personName);
personRepository.save(p);
List<Person> people = personRepository.findAll(new PageRequest(0,10)).getContent();
return people;
}
}
配置文件有两个
application.yml
spring:
jpa:
hibernate:
ddl-auto: update
server:
port: 8082 #服务端口8082
bootstrap.yml
spring:
application:
name: person #在eureka中注册的服务名person
cloud:
config:
enabled: true #开启使用配置服务中心
discovery:
enabled: true
service-id: CONFIG #配置服务中心的id,即config注册到eureka上的服务名称
eureka:
instance:
non-secure-port: ${server.port:8082}
client:
service-url:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ #
<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.0modelVersion>
<parent>
<groupId>com.xhfgroupId>
<artifactId>SpringCloudSampleartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>someartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
dependencies>
project>
启动文件,从配置服务中心获取了一个配置值。然后提供了一个rest接口,或者这个值。
package com.xhf.some;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class SomeApplication {
@Value("${my.message}") //从配置服务中心获取的值
private String message;
@RequestMapping(value="/getsome")
public String getSome() {
return message;
}
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
}
配置文件:
application.yml
server:
port: 8083
bootstrap.yml,和前文的配置基本一直,不再赘述
spring:
application:
name: some #在eureka中注册的服务名person
cloud:
config:
enabled: true
discovery:
enabled: true
service-id: CONFIG
eureka:
instance:
non-secure-port: ${server.port:8083}
client:
service-url:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/
UI服务就是提供给用户使用的接口,包括UI交互界面和数据访问的接口。
依赖引入,这里加了一些angular的依赖,不详解,只是为了提供前端界面而已。
<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.0modelVersion>
<parent>
<groupId>com.xhfgroupId>
<artifactId>SpringCloudSampleartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>uiartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>bootstrapartifactId>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>angularjsartifactId>
<version>1.3.15version>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>angular-ui-routerartifactId>
<version>0.2.13version>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
dependency>
dependencies>
project>
启动类,
@EnableFeignClients开启Feign的支持,这个是用来做rest服务调用的负载均衡的。
@EnableCircuitBreaker开启断路器的支持,当远程调用失败的时候可以调用断路器指定的方法。
@EnableZuulProxy开启网关代理的支持,作为web服务和客户端之间的一个路由的角色存在。
package com.xhf.ui;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
@EnableZuulProxy
public class UiApplication {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}
接下来是对Person服务和Some服务的调用实现。
使用Feign调用服务接口,需要先定一个接口,使用@FeignClient(“person”)进行注解,能够将这个接口映射到eureka中对应的服务。
package com.xhf.ui.service;
import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xhf.ui.domain.Person;
@FeignClient("person")
public interface PersonService {
@RequestMapping(
method=RequestMethod.POST,
value="/save",
produces=MediaType.APPLICATION_JSON_VALUE,
consumes=MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
List<Person> save(@RequestBody String name);
}
然后就是服务的调用了, 这里通过@HystrixCommand加入了熔断机制,即服务调用失败后调用指定的处理方法
package com.xhf.ui.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.xhf.ui.domain.Person;
@Service
public class PersonHystrixService {
@Autowired
PersonService personService;
@HystrixCommand(fallbackMethod="fallbackSave")
public List<Person> save(String name) {
return personService.save(name);
}
public List<Person> fallbackSave(String name) {
List<Person> list = new ArrayList<Person>();
Person p = new Person("Person service 故障");
list.add(p);
return list;
}
}
使用ribbon进行服务的调用,只要注入RestTemplate即可,其余配置spring boot已经做好了。
package com.xhf.ui.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Service
public class SomeHystrixService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod="fallbackSome")
public String getSome() {
return restTemplate.getForObject("http://some/getsome", String.class);
}
public String fallbackSome() {
return "some service 模块故障";
}
}
UI服务提供给客户端访问的Rest请求如下,就是一个正常的Rest请求。
package com.xhf.ui.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xhf.ui.domain.Person;
import com.xhf.ui.service.PersonHystrixService;
import com.xhf.ui.service.SomeHystrixService;
@RestController
public class UiController {
@Autowired
private SomeHystrixService someHystrixService;
@Autowired
private PersonHystrixService personHystrixService;
@RequestMapping("/dispatch")
public List<Person> sendMessage(@RequestBody String personName) {
return personHystrixService.save(personName);
}
@RequestMapping(value = "/getsome",produces={MediaType.TEXT_PLAIN_VALUE})
public String getSome(){
return someHystrixService.getSome();
}
}
配置文件如下,配置项前文都解释过,不再赘述。
这里有一个提示,原书中的示例用的 80 端口。在linux环境下,如果使用1024以下的端口则需要root权限,因为我当前使用的不是root权限,所以权限不足而无法使用80端口,会报权限不够异常。这个问题作为记录备忘。
application.yml
server:
port: 8090
bootstrap.yml
spring:
application:
name: ui
eureka:
instance:
non-secure-port: ${server.port:8090}
client:
service-url:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/
依赖如下
<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.0modelVersion>
<parent>
<groupId>com.xhfgroupId>
<artifactId>SpringCloudSampleartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>monitorartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrix-dashboardartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-turbineartifactId>
dependency>
dependencies>
project>
启动类,开启监控支持
package com.xhf.monitor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@SpringBootApplication
@EnableEurekaClient
@EnableHystrixDashboard
@EnableTurbine
public class MonitorApplication {
public static void main(String[] args) {
SpringApplication.run(MonitorApplication.class, args);
}
}
配置文件
application.yml
server:
port: 8989
bootstrap.yml
spring:
application:
name: monitor #在eureka中注册的服务名person
eureka:
instance:
non-secure-port: ${server.port:8989} #非ssl端口,默认为8888
client:
service-url:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ # eureka客户端设置的eureka server的地址