文章目录
- 环境
- spring cloud 介绍
- spring cloud 技术组成
- Spring Cloud 对比 Dubbo
- 一、service - 服务
- 二、commons 通用项目
-
- 新建 maven 项目
- pom.xml
- java 源文件
-
- pojo
-
- Item
- User
- Order
- service
-
- ItemService
- UserService
- OrderService
- util
-
- CookieUtil
- JsonUtil
- JsonResult
- 三、item service 商品服务
-
- 新建 spring boot 起步项目
- 选择依赖项
- pom.xml
- application.yml
- 主程序
- java 源文件
-
- ItemServiceImpl
- ItemController
- Spring MVC 几个接收参数的注释
- 访问测试
- 四、user service 用户服务
-
- 新建 spring boot 起步项目
- 选择依赖项
- pom.xml
- application.yml
- 主程序
- java源文件
-
- UserServiceImpl
- UserController
- 访问测试
- 五、order service 订单服务
-
- 新建 spring boot 起步项目
- 选择依赖项
- pom.xml
- application.yml
- 主程序
- java 源文件
-
- OrderServiceImpl
- OrderController
- 访问测试
- 六、service 访问测试总结
- 七、eureka 注册与发现
-
- 创建 eureka server 项目:sp05-eureka
- pom.xml
- application.yml
- 主程序
- 修改 hosts 文件,添加 eureka 域名映射
- 启动并访问测试
- 八、service provider 服务提供者
-
- pom.xml 添加 eureka 客户端依赖
- application.yml 添加 eureka注册配置
- 主程序启用服务注册发现客户端
- 启动并访问 eureka 查看注册信息
- 九、eureka 和 服务提供者的高可用性
-
- item-service 高可用
-
- 配置启动参数
- 启动测试
- eureka 高可用
-
- application-eureka1.yml
- application-eureka2.yml
- 配置启动参数 `--spring.profiles.active` 和 `--server.port`
- 访问 eureka 查看服务器的注册信息
- eureka客户注册时,向两个服务器注册
- 十、ribbon 服务消费者
-
- RestTemplate
- 新建 sp06-ribbon 项目
- pom.xml
- application.yml
- 主程序
- RibbonController
- 启动服务,访问测试
- 十一、ribbon 负载平衡和重试
-
- Ribbon 负载均衡
-
- 添加 ribbon 起步依赖(可选)
- RestTemplate 设置 `@LoadBalanced`
- 服务设置了访问路径id
- 访问测试
- ribbon 重试
-
- pom.xml 添加 spring-retry 依赖
- application.yml 配置 ribbon 重试
- 主程序设置 RestTemplate 请求工厂的超时属性
- item-service 的 ItemController 添加延迟代码进行测试 ribbon 的重试机制
- 访问,测试 ribbon 重试机制
- 十二、Hystrix 断路器
-
- 微服务停机时,ribbon 请求无法转发
- 复制 sp06-ribbon 项目,命名为sp07-hystrix
- 修改 pom.xml
- 添加 hystrix 起步依赖
- 修改 application.yml
- 主程序添加 `@EnableCircuitBreaker` 启用 hystrix 断路器
- RibbonController 添加降级方法
- hystrix 超时设置
- 测试启动项目
- 十三、hystrix dashboard 断路器仪表盘
-
- sp07-hystrix 项目添加 actuator,并暴露 hystrix 监控端点
-
- pom.xml 添加 actuator 依赖
- 调整 application.yml 并暴露配置 `hystrix.stream` 监控端点
- 访问 actuator 检查监控端点
- Hystrix dashboard 仪表盘
-
- pom.xml
- application.yml
- 主程序添加 `@EnableHystrixDashboard` 和 `@EnableDiscoveryClient`
- 启动,并访问测试
-
- 访问 hystrix dashboard
- 填入 hystrix 的监控端点,开启监控
- hystrix 熔断
-
- 使用 apache 的并发访问测试工具 ab
- hystrix 配置
- 十四、feign 声明式客户端接口
-
- 新建 sp09-feign 项目
- pom.xml
- application.yml
- 主程序添加 `@EnableDiscoveryClient` 和 `@EnableFeignClients`
- java 源文件
-
- feign 声明式客户端
- ItemFeignService
- UserFeignService
- OrderFeignService
- FeignController
- 调用流程
- 启动服务,并访问测试
- 十五、feign + ribbon 负载均衡和重试
-
- application.yml 配置 ribbon 超时和重试
- 启动服务,访问测试
- 十六、feign + hystrix 降级
-
- feign 启用 hystrix
-
- application.yml 添加配置
- 可以添加配置,暂时减小降级超时时间,以便后续对降级进行测试
- feign + hystrix 降级
-
- feign 远程接口中指定降级类
-
- ItemFeignService
- UserFeignService
- OrderFeignService
- 降级类
-
- ItemFeignServiceFB
- UserFeignServiceFB
- OrderFeignServiceFB
- 启动服务,访问测试
- 十七、feign + hystrix 监控和熔断测试
-
- 修改sp09-feign项目pom.xml 添加 hystrix 起步依赖
- 主程序添加 `@EnableCircuitBreaker`
- sp09-feign 配置 actuator,暴露 `hystrix.stream` 监控端点
-
- actuator 依赖
- application.yml 暴露 `hystrix.stream` 端点
- 启动服务,查看监控端点
- hystrix dashboard
- 熔断测试
- 十八、order service 调用商品库存服务和用户服务
-
- pom.xml
- application.yml
- 主程序
- ItemFeignService
- UserFeignService
- ItemFeignServiceFB
- UserFeignServiceFB
- OrderServiceImpl
- order-service 配置启动参数,启动两台服务器
- 启动服务,访问测试
- hystrix dashboard 监控 order service 断路器
- 十九、hystrix + turbine 集群聚合监控
-
- 新建 sp10-turbine 项目
- pom.xml
- application.yml
- 主程序
- 访问测试
- 二十、zuul API网关
-
- 新建 sp11-zuul 项目
- pom.xml
- application.yml
- 主程序
- 启动服务,访问测试
- zuul + ribbon 负载均衡
- zuul + ribbon 重试
-
- pom.xml 添加 spring-retry 依赖
- 配置 zuul 开启重试,并配置 ribbon 重试参数
- zuul + hystrix 降级
-
- 创建降级类
- ItemServiceFallback
- OrderServiceFallback
- 降低 hystrix 超时时间,以便测试降级
- 启动服务,测试降级
- zuul + hystrix 数据监控
-
- 暴露 hystrix.stream 监控端点
- 开启监控
- zuul + turbine 聚合监控
- 熔断测试
- 二十一、zuul 请求过滤
-
- 定义过滤器,继承 ZuulFilter
- 访问测试
- 二十二、zuul Cookie过滤
- 二十三、config 配置中心
-
- github 上存放配置文件
-
- 新建 "Project",命名为 config
- 将sp02,sp03,sp04,sp11四个项目的yml配置文件,复制到config项目,并改名
- 禁止配置中心的配置信息覆盖客户端配置
- 将 config 项目上传到 github
- config 服务器
-
- 新建 sp12-config 项目
- pom.xml
- application.yml
- 主程序添加 `@EnableConfigServer` 和 `@EnableDiscoveryClient`
- 启动,访问测试
- config 客户端
-
- pom.xml 添加 config 客户端依赖
- 在四个项目中添加 bootstrap.yml
- 启动服务,观察从配置中心获取配置信息的日志
- 配置刷新
-
- pom.xml
- yml 配置文件中暴露 refresh 端点
- UserServiceImpl 添加 `@RefreshScope` 注解
- 重启配置中心, 再重启sp03, 查看暴露的刷新端点
- 修改config项目的user-service-dev.yml文件并提交
- 用postman访问刷新端点, 刷新配置
- 访问 user-service,查看动态更新的新用户数据
- 二十四、config bus + rabbitmq 消息总线配置刷新
-
- rabbitmq 安装笔记
- 需要动态更新配置的微服务,添加 spring cloud bus 依赖,并添加 rabbitmq 连接信息
-
- pom.xml 添加 spring cloud bus 依赖
- 配置文件中添加 rabbitmq 连接信息
- config-server 暴露 bus-refresh 刷新端点
- 启动服务,请求刷新端点发布刷新消息
- config 本地文系统
-
- 把配置文件保存到 sp12-config 项目的 resources/config 目录下
- 修改 application.yml 激活 native profile,并指定配置文件目录
- 二十五、sleuth 链路跟踪
-
- 微服务中添加 spring cloud sleuth 依赖
- 在控制台查看链路跟踪日志
- 二十六、sleuth + zipkin 链路分析
-
- 链路数据抽样比例
- zipkin 服务
-
- 下载 zipkin 服务器
- 启动 zipkin 时,连接到 rabbitmq
- 微服务添加 zipkin 起步依赖
- 启动并访问服务,访问 zipkin 查看链路分析
- 二十七、向eureka注册正确的ip地址
-
- 选择正确网卡
- 注册ip地址,而不是主机名
环境
- STS
- Maven
- Lombok
spring cloud 介绍
spring cloud 是一系列框架的集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 spring boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
spring cloud 对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用 spring cloud 一站式解决方案能在从容应对业务发展的同时大大减少开发成本。同时,随着近几年微服务架构和 docker 容器概念的火爆,也会让 spring cloud 在未来越来越“云”化的软件开发风格中立有一席之地,尤其是在目前五花八门的分布式解决方案中提供了标准化的、一站式的技术方案,意义可能会堪比当年 servlet 规范的诞生,有效推进服务端软件系统技术水平的进步。
spring cloud 技术组成
-
微服务治理,服务注册和发现
-
负载均衡、请求重试
-
断路器,服务降级、熔断
-
ribbon + hystrix 集成,并提供声明式客户端
-
hystrix 数据监控
-
API 网关,提供微服务的统一入口,并提供统一的权限验证
-
配置中心
-
消息总线, 配置刷新
-
链路跟踪
Spring Cloud 对比 Dubbo
-
- Dubbo只是一个远程调用(RPC)框架
- 默认基于长连接,支持多种序列化格式
-
- 框架集
- 提供了一整套微服务解决方案(全家桶)
- 基于http调用, Rest API
一、service - 服务
- 商品服务 item service,端口 8001
- 用户服务 user service,端口 8101
- 订单服务 order service,端口 8201
二、commons 通用项目
新建 maven 项目
pom.xml
<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>cn.tedu</groupId>
<artifactId>sp01-commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sp01-commons</name>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
java 源文件
pojo
Item
package cn.tedu.sp01.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Item {
private Integer id;
private String name;
private Integer number;
}
User
package cn.tedu.sp01.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}
Order
package cn.tedu.sp01.pojo;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private String id;
private User user;
private List<Item> items;
}
service
ItemService
package cn.tedu.sp01.service;
import java.util.List;
import cn.tedu.sp01.pojo.Item;
public interface ItemService {
List<Item> getItems(String orderId);
void decreaseNumbers(List<Item> list);
}
UserService
package cn.tedu.sp01.service;
import cn.tedu.sp01.pojo.User;
public interface UserService {
User getUser(Integer id);
void addScore(Integer id, Integer score);
}
OrderService
package cn.tedu.sp01.service;
import cn.tedu.sp01.pojo.Order;
public interface OrderService {
Order getOrder(String orderId);
void addOrder(Order order);
}
util
CookieUtil
package cn.tedu.web.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CookieUtil {
/** * @param response * @param name * @param value * @param maxAge */
public static void setCookie(HttpServletResponse response,
String name, String value, String domain, String path, int maxAge) {
Cookie cookie = new Cookie(name, value);
if(domain != null) {
cookie.setDomain(domain);
}
cookie.setPath(path);
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
public static void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
setCookie(response, name, value, null, "/", maxAge);
}
public static void setCookie(HttpServletResponse response, String name, String value) {
setCookie(response, name, value, null, "/", 3600);
}
public static void setCookie(HttpServletResponse response, String name) {
setCookie(response, name, "", null, "/", 3600);
}
/** * @param request * @param name * @return */
public static String getCookie(HttpServletRequest request, String name) {
String value = null;
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
value = cookie.getValue();
}
}
}
return value;
}
/** * @param response * @param name * @return */
public static void removeCookie(HttpServletResponse response, String name, String domain, String path) {
setCookie(response, name, "", domain, path, 0);
}
}
JsonUtil
package cn.tedu.web.util;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JsonUtil {
private static ObjectMapper mapper;
private static JsonInclude.Include DEFAULT_PROPERTY_INCLUSION = JsonInclude.Include.NON_DEFAULT;
private static boolean IS_ENABLE_INDENT_OUTPUT = false;
private static String CSV_DEFAULT_COLUMN_SEPARATOR = ",";
static {
try {
initMapper();
configPropertyInclusion();
configIndentOutput();
configCommon();
} catch (Exception e) {
log.er