SpringCloud学习项目 —— 天气预报系统
一、项目实例
1.1 项目列表
- weather-eureka-server:Eureka 服务
- weather-city-server-eureka:获取城市列表微服务
- weather-data-server-eureka:获取某一城市天气数据微服务
- weather-collection-server-eureka:定时任务获取天气信息微服务
- weather-report-server-eureka:完整天气报告展示微服务
- weather-eureka-zuul:API网关
- weather-config-server:SpringCloudConfig 服务
1.2 依赖关系
weather-city-server-eureka 与 weather-data-server-eureka为基础数据服务,他们是数据的来源。
(在本案例中 weather-data-server-eureka 依赖于 weather-config-server 的配置,所以要先启动 weather-config-server,在启动 weather-data-server-eureka )
weather-collection-server-eureka 依赖于 weather-city-server-eureka,因为它需要通过城市列表,来定时爬去某一城市的天气数据。
weather-report-server-eureka 依赖于 weather-city-server-eureka 和 weather-data-server-eureka 和 weather-eureka-zuul,它需要 weather-city-server-eureka 获取城市列表,然后拿某一城市的 id,通过 weather-data-server-eureka 来获取天气数据。当然,首先通过 weather-eureka-zuul 分别路由到 weather-city-server-eureka 和 weather-data-server-eureka。
部署顺序
- weather-eureka-server:Eureka 是基础
- weather-config-server:配置文件是最基础的发布的被依赖方
- weather-city-server-eureka:城市信息微服务,获取城市列表
- weather-collection-server-eureka:天气信息采集微服务(此模块可以不用部署,因为内容仅仅是http请求天气数据,并且保存到redis中。当前案例并没有实际使用 redis,所以可以忽略)
- weather-data-server-eureka:指定城市天气信息微服务
- weather-eureka-zuul:API 网关微服务
- weather-report-server-eureka:天气报告微服务
二、微服务相关介绍
2.1 一个项目的核心部分
image.png- 表示层:连接客户端
- 业务层:复杂的业务逻辑
- 数据访问层:存放业务数据的持久性
2.2 单一项目的缺点
- 升级风险高:每次更新,不得不对整个系统做升级
- 维护成本增加:
- 可伸缩性变差:项目中有的服务压力大,有的服务压力小。压力大的需要的硬件支持就大,压力小的需要硬件支持就小。如果做机器的扩展,只能全部部署整个项目
- 监控困难:
2.3 SOA架构介绍
面向服务架构
2.4 微服务拆分原则
思路:
一个大的问题慢慢拆分成小的问题,逐个解决
2.4.1 拆分足够小
2.4.2 轻量级的通信
2.4.3 领域驱动
粗浅的理解就是单一的业务模型。订单业务是一个独立的领域,资金业务是一个独立的领域,票台业务是一个独立的领域,活动业务是一个独立的领域。
每个服务都专注于某个领域,每个领域之间有各自的边界,不会混杂在一起。
按照领域驱动来进行架构设计,可以保证系统的有逻辑边界
2.4.4 单一职责
2.4.5 DevOps?
2.5 如何设计微服务系统
- 服务拆分:确认好业务边界后的模块拆分
- 服务注册:多个服务的注册表
- 服务发现:从服务注册表中获取服务的信息
- 服务消费:调用其他服务的过程
- 统一入口:
- 配置管理:所有服务共用一套配置信息
- 熔断机制:请求处理不过来,如果所有请求全部处理,可能会造成系统崩溃。此时需要熔断机制
分析数据流向
image.png数据是驱动架构发展的核心
系统的通信设计
- http
- dubbo
- 等
系统存储设计
- Redis
- Mysql
三、SpringCloud实践
3.1 服务注册和发现 —— Eureka 注册中心
3.1.1 配置Eureka-server
使用@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
编写配置文件
server.port=8761
eureka.instance.hostname=localhost
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
spring.freemarker.prefer-file-system-access=false
eureka.server.enable-self-preservation=false
注意:
在application.yml中添加spring.freemarker.prefer-file-system-access: false
spring.freemarker.prefer-file-system-access意为是否优先从文件系统加载template
3.1.2 配置 Eureka-client
使用@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
编写配置文件
spring.application.name=weather-report-eureka
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
feign.client.config.feignName.connect-timeout=5000
feign.client.config.feignName.read-timeout=5000
server.port=8021
3.2 服务消费 —— Feign
- 添加依赖
- application.properties 中进行配置超时时间
- 使用@EnableFeignClients
- 创建客户端接口(专门访问某一服务的接口)
- 业务代码中注入客户端接口
添加依赖
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
在启动类上添加注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
创建客户端接口
创建服务类:
@FeignClient("weather-data-eureka")
public interface WeatherDataClient {
@RequestMapping("/weather/cityId/{cityId}")
WeatherResponse getDataByCityId(@PathVariable("cityId") String cityId);
}
- @FeignClient 中的值是 eureka 注册中心中服务的名称(需要调用的服务的application.name)
- @RequestMapping 中填写需要调用服务的目标 http 接口的地址
配置过期时间
feign.client.config.feignName.connect-timeout=5000
feign.client.config.feignName.read-timeout=5000
3.3 API网关
3.3.1 API 网关的意义
image.png可以通过不同的路径,路由到不同的微服务中去
3.3.2 配置API网关 —— Zuul
配置API网关以后的架构
image.png
添加依赖
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
添加注解
添加@EnableDiscoveryClient注解,Zuul 所在的模块同时也是一个 Eureka-Client,然后再添加@EnableZuulProxy注解。
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
配置文件
spring.application.name=weather-eureka-zuul
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
zuul.routes.city.path=/city/**
zuul.routes.city.service-id=weather-city-server-eureka
zuul.routes.data.path=/data/**
zuul.routes.data.service-id=weather-data-server-eureka
server.port=8011
3.4 集中化配置 —— Spring Cloud Config
文件名配置规则
image.png
- 问题1:配置文件的文件名称必须与 config-client 的 application.name 相同吗?
- 问题2:一个config-client 想读取多个配置文件该咋办?
- 问题3:config-server 启动后,访问 config-server 的 url 只能跟一个key 有关系吗?如果读取到所有的config内容?
- 问题4:url格式就只能 http://#{config-server host name}:#{port}/#{key}/#{profile}
http://localhost:8888/name/dev
3.5 熔断机制
3.5.1 概念相关
目的:
- 停止接受新的业务请求,转而直接返回结果。慢慢消化正在执行的任务,慢慢释放资源
- 保护系统,防止造成雪崩
意义:
- 保持系统稳定
- 减少性能损耗:断路器直接返回简单响应结果,不占用额外资源
- 及时响应
熔断和降级的区别:
相似性:
- 目的一致,通过技术手段保护系统
- 表现形式,都是使得客户端暂时不可用,都是默认的响应内容
- 粒度一致,以服务的级别来设置的
区别: - 触发条件不同
服务熔断:一般是某个服务的故障引起的,一般是指下游服务。下游服务调用请求压力过大
服务降级:一般是从整体的负荷来考虑的。上游服务调用过来的请求减少,当前服务无需提供这么多的实例,需要减少
3.5.2 集成 Hystrix
断路器设置在 weather-report-server-eureka 中
1. 添加依赖
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
2. 添加注解
在启动类上添加注解,相当于打开了 hystrix 的开关
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 设置fallback接口
定义fallback类,并在 Feign 的接口中进行设置
3.1 定义fallback类
@Service
public class DataClientFallback implements DataClient {
@Override
public List<City> cityList() {
City city = new City();
city.setCityId("101280601");
city.setCityName("深圳");
return Lists.newArrayList(city);
}
@Override
public WeatherResponse getDataByCityId(String cityId) {
return null;
}
}
3.2 在 Feign 的接口中进行设置
@FeignClient(name = "weather-eureka-zuul", fallback = DataClientFallback.class)
public interface DataClient {
@RequestMapping("/city/cities")
List<City> cityList();
@RequestMapping("/data/weather/cityId/{cityId}")
WeatherResponse getDataByCityId(@PathVariable("cityId") String cityId);
}
3.3 设置 application.properties 文件
由于词用法是在 Feign 上添加的熔断器处理类,所以配置文件应该开启 Feign 对 Hystrix 的可用开关。
feign.hystrix.enabled=true