12.3实战
实战部分主要由6个微服务组成:
config:配置服务器,本例为person-service和some-service提供外部配置。
discovery:Eureka Server为微服务提供注册
person:为UI模块提供保存person的REST服务
some:为UI模块返回一段字符串
UI:作为应用网关,提供外部访问的唯一入口。使用Feign消费person服务、Ribbon消费some服务,且都提供断路器功能
monitor:监控UI模块中的断路器。
12.3.1 项目构建
新建模块化的maven本项目ch12,基pom.xml文件的主要部分如下。
(1)使用标签来实现模块化:
<modules>
<module>config</module>
<module>discovery</module>
<module>ui</module>
<module>person</module>
<module>some</module>
<module>monitor</module>
</modules>
(2)使用spring-cloud-starter-parent替代 spring-boot-starter-parent,其具备spring-boot-starter-parent的同样功能并附加了Spring Cloud的依赖,此处使用Edgware.RELEASE:
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Edgware.RELEASE</version>
<relativePath/>
</parent>
(3)在些pom.xml文件里添加的dependency对所有的子模块都是有效的,即在子模块不用再额外添加这些依赖:
<dependencies>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
12.3.2 服务发现–Discovery(Eureka Server)
1.依赖
服务发现依赖于Eureka Server,所以本模块加上如下依赖即可:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
2.关键代码
package com.wisely.discovery;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //一个常规的Spring Boot项目,我们只需要使用@EnableEurekaServer注解开启对EurakaServer的支持即可。
public class DiscoveryApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryApplication.class, args);
}
}
3.配置
在云计算环境下,习惯使用YAML配置,此处我们也受用YAML配置。
application.yml
server:
port: 8761 #当前Eureka Server服务的端口号为8761
eureka:
instance:
hostname: localhost #当前EurekaServer的hostname为localhost
client:
register-with-eureka: false #当前服务不需要到Eureka Server上注册。
fetch-registry: false
12.3.3 配置–Config(Config Server)
1.依赖
Spring Cloud为我们提供了作为配置服务的依赖spring-cloud-config-server,以及作为eureka客户端的依赖spring-cloud-starter-eureka:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
2.关键代码
package com.wisely.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 //使用@EnableConfigServer开启配置服务器的支持
@EnableEurekaClient //使用@EnableEurekaClient开启作为Eureka Server的客户端支持。
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
3.配置
bootstrap.yml
spring:
application:
name: config #在Erueka Server注册的服务名为config
profiles:
active: native #配置服务器使用本地配置(默认为git配置)
eureka:
instance:
non-secure-port: ${server.port:8888} #非SSL端口,若环境变量中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的地址
代码解释:
这里对bootstrap.yml做一下解释,Spring Cloud应用提供使用bootstrap.yml负责从外部资源加载配置属性。
application.yml
spring:
cloud:
config:
server:
native:
search-locations: classpath:/config
server:
port: 8888
配置期货应用所需的配置文件的位置位于类路径下的config目录下,如图
配置文件的规则为:应用名+profile.yml。
12.3.4 服务模块——Person服务
1.依赖
本模块需要做数据库操作,故添加spring-boot-starter-data-jpa依赖(在开发环境下使用hsqldb,在Dockers生产环境下使用PostgreSQL);本模块还需要使用Config Server的配置,故添加spring-cloud-config-client依赖。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901-1.jdbc4</version>
</dependency>
</dependencies>
2.关键代码
本模块没有特别值得关注的代码,主要是实现数据库的一个保存操作,并将保存操作暴露给UI模块调用。
package com.wisely.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.wisely.person.dao.PersonRepository;
import com.wisely.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;
}
}
3.配置
bootstrap.yml:
spring:
application:
name: person
cloud:
config:
enabled: true
discovery:
enabled: true
service-id: CONFIG #指定ConfigServer的服务名,将会通过Eureka Server发现Config Server
eureka:
instance:
non-secure-port: ${server.port:8082}
client:
service-url:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/
在开发环境下使用hsqldb:(Config Server下的person.yml):
spring:
jpa:
database: HSQL
在Docker生产环境下使用PostgreSQL(Config Server下的person-docker.yml):
spring:
jpa:
database: POSTGRESQL
datasource:
platform: postgres
url: jdbc:postgresql://postgres:5432/postgres
username: postgres
password: postgres
driver-class-name: org.postgresql.Driver
application.yml
server:
port: 8082
spring:
jpa:
hibernate:
ddl-auto: update
12.3.5 服务模块——Some服务
1.依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
2.关键代码
package com.wisely.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
@EnableDiscoveryClient
@RestController
public class SomeApplication {
@Value("${my.message}") //此处通过@value注入的值来处于Config Server
private String message;
@RequestMapping(value = "/getsome")
public String getsome(){
return message;
}
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
}
在开发环境下(Config Server下的some.yml)。
my:
message: Message from Development
在Docker生产环境下(Config Server下的some-docker.yml):
my:
message: Message from Production
3.配置
bootstrap.yml:
spring:
application:
name: some
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/
application.yml:
server:
port: 8083
12.3.6 界面模块——UI(Ribbon,Feign)
1.依赖
本模块会使用ribbon、feign、zuul以及CircuitBreaker,所以需添加相关依赖。本模块是一个具有界面的模块,所以通过webjar加载了一些常用的脚本框架:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angularjs</artifactId>
<version>1.3.15</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angular-ui-router</artifactId>
<version>0.2.13</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
</dependency>
</dependencies>
2.关键代码
(1)入口:
package com.wisely.ui;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //开启feign客户端支持
@EnableCircuitBreaker //开启CircuitBreaker的支持
@EnableZuulProxy //开启网关代理的支持
public class UiApplication {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
@Bean
@LoadBalanced //注册一个restTemplate,并开启负载均衡以支持Robbin
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
(2)使用feign调用Person Service:
package com.wisely.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.wisely.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);
}
代码解释,我们只需通过简单的在接口中声明方法即可调用Person服务的REST服务。
(3)调用Person Service的断路器:
package com.wisely.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.wisely.ui.domain.Person;
@Service
public class PersonHystrixService {
@Autowired
PersonService personService;
@HystrixCommand(fallbackMethod = "fallbackSave") //当本方法调用失败时,调用后备方法fallbackSave。
public List<Person> save(String name) {
return personService.save(name);
}
public List<Person> fallbackSave(String name){
List<Person> list = new ArrayList<>();
Person p = new Person(name+"没有保存成功,Person Service 故障");
list.add(p);
return list;
}
}
(4)使用ribbon调用Some Service,并使用断路器:
package com.wisely.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; //注入即可直接使用Ribbon,Spring Boot为我们自动做的配置
@HystrixCommand(fallbackMethod = "fallbackSome") //当本方法调用失败时调用后备方法
public String getSome() {
return restTemplate.getForObject("http://some/getsome", String.class);
}
public String fallbackSome(){
return "some service模块故障";
}
}
3.配置
bootstrap.yml:
spring:
application:
name: ui
eureka:
instance:
non-secure-port: ${server.port:80}
client:
service-url:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/
application.yml
server:
port: 80
12.3.7 断路器监控——Monitor(DashBoard)
1.依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
</dependencies>
2.主要代码
package com.wisely.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);
}
}
3.配置
bootstrap.yml
spring:
application:
name: monitor
eureka:
instance:
nonSecurePort: ${server.port:8989}
client:
serviceUrl:
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/
application.yml
server:
port: 8989
12.3.8 运行
我们依次启动DiscoveryApplication、ConfigApplication,后面所有的微服务启动不分顺序,最后启动MonitorApplication。此时访问http://localhost:8761,查看Eureka Server,如图
1.访问UI服务
UI服务既是我们的页面,也是我们的网关代理。在实际生产环境中,服务器防火墙只需将此端口暴露给外网即可,访问http://localhost,如图
(1)调用Person Service,如图
(2)调用Some service,如图
3.断路器监控
访问 http://localhost:8989/hystrix.stream,如图
输入http://localhost/hystrix.stream,如图
监控界面如图