1 springcloud简介
1.1 简介
spring cloud 是一系列框架的集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 spring boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
spring cloud 对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用 spring cloud 一站式解决方案能在从容应对业务发展的同时大大减少开发成本。同时,随着近几年微服务架构和 docker 容器概念的火爆,也会让 spring cloud 在未来越来越“云”化的软件开发风格中立有一席之地,尤其是在目前五花八门的分布式解决方案中提供了标准化的、一站式的技术方案,意义可能会堪比当年 servlet 规范的诞生,有效推进服务端软件系统技术水平的进步。
1.2 技术组成
在这里插入图片描述
说明:
eureka 微服务治理,服务注册和发现
ribbon 负载均衡、请求重试
hystrix 断路器,服务降级、熔断
feign ribbon + hystrix 集成,并提供声明式客户端
hystrix dashboard 和 turbine hystrix 数据监控
zuul API 网关,提供微服务的统一入口,并提供统一的权限验证
config 配置中心
bus 消息总线, 配置刷新
sleuth+zipkin 链路跟踪
1.3 Spring Cloud 与Dubbo区别
Spring cloud是一个远程调用的的组件集合,而Dubbo则更多是用于远程覅用的问题,多用于自研的一些框架
2.微服务架构
2.1 基础服务设置
我们通过用户积分增加的案例来学习springcloud的各部分组件。
基础的服务分为商品服务,用户服务以及订单服务,架构图如下:

服务注册和发现,将要部署的服务在网页上进行管理
config 配置中心
配置相应得配置信息 类似nacso中组和d,中写的内容,内容要更加丰富
feign ribbon + hystrix 集成,并提供声明式客户端
集成 ribbon + hystrix 但主要集成ribbon的负载均衡和hystrix的熔断功能
ribbon 负载均衡、请求重试
主要功能如下图:
hystrix 断路器,服务降级、熔断
与sentinel类似,实现服务降级和熔断
主要功能如下图:
zuul API 网关,提供微服务的统一入口,并提供统一的权限验证
类似网关,转发请求,同时设置不同服务的访问权限
其他rabbitMq,turbine,bus,sleuth+zipkin 可以说是springcloud集成工具的一些加强,会在后边再一一介绍
3.项目代码
3.1 服务创建流程
1.创建项目
除第一个使用maven项目外,其余服务都使用Spring Initializr方式创建
2.配置依赖
在pom.xml 文件中进行相应的一系列配置
包括管理依赖的版本和打包方式等。
父工程依赖(说明:创建项目完成后自动生成)
3.yml配置文件
依据配置需求各自配置
4.写商品服务
启动类
控制层
service层
其他接口层
5.访问测试
通过postman或则网页访问,可以使用开发者模式,加入插件,以更便捷直观的观察测试结果
3.2 commons通用项目
1.pom.xml
<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>
说明:添加基本依赖,后续根据服务需求添加依赖
2.项目结构
说明:commons提供了许多基本的类和接口
3.单层代码–util层
cookieutil
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);
jsonresult
@Getter
@Setter
public class JsonResult<T> {
/** 成功 */
public static final int SUCCESS = 200;
/** 没有登录 */
public static final int NOT_LOGIN = 400;
/** 发生异常 */
public static final int EXCEPTION = 401;
/** 系统错误 */
public static final int SYS_ERROR = 402;
/** 参数错误 */
public static final int PARAMS_ERROR = 403;
/** 不支持或已经废弃 */
public static final int NOT_SUPPORTED = 410;
/** AuthCode错误 */
public static final int INVALID_AUTHCODE = 444;
/** 太频繁的调用 */
public static final int TOO_FREQUENT = 445;
/** 未知的错误 */
public static final int UNKNOWN_ERROR = 499;
private int code;
private String msg;
private T data;
public static JsonResult build() {
return new JsonResult();
}
public static JsonResult build(int code) {
return new JsonResult().code(code);
}
public static JsonResult build(int code, String msg) {
return new JsonResult<String>().code(code).msg(msg);
}
public static <T> JsonResult<T> build(int code, T data) {
return new JsonResult<T>().code(code).data(data);
}
public static <T> JsonResult<T> build(int code, String msg, T data) {
return new JsonResult<T>().code(code).msg(msg).data(data);
}
public JsonResult<T> code(int code) {
this.code = code;
return this;
}
public JsonResult<T> msg(String msg) {
this.msg = msg;
return this;
}
public JsonResult<T> data(T data) { this.data = data;
return this;
}
public static JsonResult ok() {
return build(SUCCESS);
}
public static JsonResult ok(String msg) {
return build(SUCCESS, msg);
}
public static <T> JsonResult<T> ok(T data) {
return build(SUCCESS, data);
}
public static JsonResult err() {
return build(EXCEPTION);
}
public static JsonResult err(String msg) {
return build(EXCEPTION, msg);
}
@Override
public String toString() {
return JsonUtil.to(this);
}
}
3.3 item service服务
1.pom.xml
<dependency>
<groupId>com.lydon</groupId>
<artifactId>sp01-commons1</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.项目结构
ItemserviceImpl
@Slf4j
@Service
public class ItemServiceImpl implements ItemService {
@Override
public List<Item> getItems(String orderId) {
log.info("获取商品,orderId="+orderId);
List<Item> items=new ArrayList<>();
items.add(new Item(1,"商品1",1));
items.add(new Item(2,"商品2",5));
items.add(new Item(3,"商品3",2));
items.add(new Item(4,"商品4",3));
items.add(new Item(5,"商品5",4));
return items;
}
@Override
public void decreaseNumbers(List<Item> items) {
for (Item item : items){
log.info("减少库存:"+item);
}
}
}
ItemController
@RestController
@Slf4j
public class ItemController {
@Autowired
private ItemService itemService;
@GetMapping("/{orderId}")
public JsonResult<List<Item>> getItems(
@PathVariable String orderId) throws InterruptedException {
List<Item> items=itemService.getItems(orderId);
//随机阻塞
if(Math.random()<0.9){
//随机延长时长,0~5秒
int t=new Random().nextInt(5000);
log.info("延迟"+t);
Thread.sleep(t);
}
return JsonResult.ok().data(items);
}
//用postman 测试post请求
@PostMapping("/decreaseNumber")
public JsonResult<?>decreaseNumber(
@RequestBody List<Item> items){
itemService.decreaseNumbers(items);
return JsonResult.ok().msg("自增");
}
3.application.yml
spring:
application:
name: item-service #注册中心名称
#item 8001
#user 8181
#order 8201
server:
port: 8001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka
4.测试
根据orderid,查询商品:http://localhost:8001/35
减少商品库存:http://localhost:8001/decreaseNumber
3.4 user service服务
1.pom.xml
<dependency>
<groupId>com.lydon</groupId>
<artifactId>sp01-commons1</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.项目结构
ItemserviceImpl
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Value("${sp.user-service.users}")
private String userJson;
@Override
public User getUser(Integer userId) {
//userJson-->List<User>
List<User> list = JsonUtil.from(
userJson, new TypeReference<List<User>>() {});
for (User u:list){
if(u.getId().equals(userId)){
return u;
}
}
return new User (userId,"用户名="+userId,"密码="+userId);
}
@Override
public void addScore(Integer userId, Integer score) {
log.info("增加用户积分","userId="+userId,"score="+score);
}
}
ItemController
@RestController
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
User user = userService.getUser(userId);
return JsonResult.ok().data(user);
}
// /8/score?score=1000
@GetMapping("/{userId}/score")
public JsonResult<?> addScore(@PathVariable Integer userId,
Integer score) {
userService.addScore(userId, score);
return JsonResult.ok().msg("积分增加成功!");
}
@GetMapping("/favicon.ico")
public void ico(){}
}
3.application.yml
sp:
user-service:
users: "[{\"id\":7, \"username\":\"abc\",\"password\":\"123\"},
{\"id\":8, \"username\":\"def\",\"password\":\"456\"},
{\"id\":9, \"username\":\"ghi\",\"password\":\"789\"}]"
spring:
application:
name: user-service
server:
port: 8101
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka
4.测试
根据userid查询用户信息:http://localhost:8101/7
根据userid,为用户增加积分:http://localhost:8101/7/score?score=100
3.5 order service服务
1.pom.xml
<dependency>
<groupId>com.lydon</groupId>
<artifactId>sp01-commons1</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.2.RELEASE</version>
</dependency>
2.项目结构
ItemserviceImpl
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private ItemClient itemClient;
@Autowired
private UserClient userClient;
@Override
public Order getOrder(String orderId) {
log.info("获取订单,orderId="+orderId);
// 远程调用商品, 获取商品列表
JsonResult<List<Item>> items = itemClient.getItems(orderId);
//TODO: 调用item-service获取商品信息
//远程调用商品, 获取用户列表
JsonResult<User> user = userClient.getUser(8);//真实项目中,要获取以登录用户信息
Order order = new Order();
order.setId(String.valueOf(user.getData()));
order.setItems(items.getData());
return order;
}
@Override
public void addOrder(Order order) {
//log.info("保存订单:"+order);
log.info("添加订单: "+order);
//TODO: 调用item-service减少商品库存
//TODO: 调用user-service增加用户积分
//TODO: 远程调用商品,减少库存
itemClient.decreaseNumber(order.getItems());
//TODO: 远程调用用户,增加积分
userClient.addScore(order.getUser().getId(),1000);
}
}
ItemController
@Slf4j
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
log.info("get order, id="+orderId);
// TODO :远程调用商品,获取商品列表
// TODO :远程调用用户,获取用户数据
Order order = orderService.getOrder(orderId);
return JsonResult.ok(order);
}
@GetMapping("/add")
public JsonResult addOrder() {
//模拟post提交的数据
Order order = new Order();
order.setId("saf1234");
order.setUser(new User(8,null,null));
order.setItems(Arrays.asList(new Item[] {
new Item(1,"aaa",2),
new Item(2,"bbb",1),
new Item(3,"ccc",3),
new Item(4,"ddd",1),
new Item(5,"eee",5),
}));
orderService.addOrder(order);
return JsonResult.ok().msg("添加订单成功~~");
}
@GetMapping("/favicon.ico")
public void ico(){
}
}
3.application.yml
spring:
application:
name: order-service
server:
port: 8201
#客户端应用请求地址:/eureka 子路径
#用浏览器查看 erueka 控制台地址: /
#defaultZone: 如果使用云服务,可以购买不停地点的服务器,
#自己的服务器必须是defaultZone
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
4.测试
根据orderid,获取订单:http://localhost:8201/123abc
保存订单,观察控制台日志输出:http://localhost:8201/
3.6 service访问测试汇总
1. item-service
根据orderid,查询商品:http://localhost:3001/item-service/
减少商品库存: http://localhost:8001/decreaseNumber
2.user-service
根据userid查询用户信息:http://localhost:8101/7
根据userid,为用户增加积分: http://localhost:8101/7/score?score=100
3.order-service
根据orderid,获取订单:http://localhost:8201/123abc
保存订单,观察控制台日志输出:http://localhost:8201/
4.eureka 服务注册发现
4.1 eureka简介
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka由多个instance(服务实例)组成,这些服务实例可以分为两种:Eureka Server和Eureka Client。为了便于理解,我们将Eureka client再分为Service Provider和Service Consumer。
Eureka Server 提供服务注册和发现 Service Provider
服务提供方,将自身服务注册到Eureka,从而使服务消费方能够找Service
Consumer服务消费方,从Eureka获取注册服务列表,从而能够消费服务
4.2 eureka项目准备
pom.xml (关键依赖)
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
application.yml
spring:
application:
name: eureka-server
server:
port: 2001
eureka:
server:
enable-self-preservation: false
instance:
hostname: eureka1
client:
register-with-eureka: false
fetch-registry: false
主程序
@EnableEurekaServer
@SpringBootApplication
public class Sp05EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(Sp05EurekaApplication.class, args);
}
}
访问测试:
- http://eureka1:2001
5.总结
本章简单学习了微服务一些基本业务的实现,并简单介绍了eureka的使用。通过eureka,网络公司可以较为便捷的获取或管理各项服务,其与nacos作用类似,但其运用的并发量更大,具体如何实现将在后边介绍。