1.Spring Cloudweb
Spring Cloud是一个工具集,集成了多种工具,来解决微服务中的各类问题
微服务的总体解决方案 微服务全家桶spring
微服务治理,服务注册和发现 eureka
负载均衡、请求重试 ribbon
断路器,服务降级、熔断 hystrix
ribbon + hystrix 集成,并提供声明式客户端 feign
hystrix 数据监控 hystrix dashboard 和 turbine
API 网关,提供微服务的统一入口,并提供统一的权限验证 zuul
配置中心 config
消息总线, 配置刷新 bus
链路跟踪 sleuth+zipkin
...json
Spring Cloud不是一个解决单一问题的框架!!!api
2.开发环境
IDEA或STS
Lombok
Mavenspringboot
3.Spring Cloud和Dubbo的区别服务器
Dubbo
1.Dubbo只是一个远程调用(RPC)框架
2.默认基于长链接,支持多种序列化格式网络
Spring Cloud
1.框架集
2.提供了一整套微服务解决方案(全家桶)
3.基于http调用, Rest APIapp
4.service- 服务负载均衡
五.item service商品服务
1.新建项目
2.配置依赖pom.xml 添加commons依赖
3.配置application.yml
4.配置主程序
5.编写代码框架
业务实现层
@Slf4j
@Service
public class ItemServiceImpl implements ItemService {
@Override public List<Item> getItems(String orderId) { ArrayList<Item> list = new ArrayList<Item>(); list.add(new Item(1, "商品 1",1)); list.add(new Item(2, "商品 2",2)); list.add(new Item(3, "商品 3",3)); list.add(new Item(4, "商品 4",4)); list.add(new Item(5, "商品 5",5)); return list; } @Override public void decreaseNumbers(List<Item> list) { for(Item item : list) { log.info("减小库存 - "+item); } }
}
控制层
@Slf4j
@RestController
public class ItemController {
@Autowired private ItemService itemService; @Value("${server.port}") private int port; @GetMapping("/{orderId}") public JsonResult<List<Item>> getItems(@PathVariable String orderId) { log.info("server.port="+port+", orderId="+orderId); List<Item> items = itemService.getItems(orderId); return JsonResult.ok(items).msg("port="+port); } @PostMapping("/decreaseNumber") public JsonResult decreaseNumber(@RequestBody List<Item> items) { itemService.decreaseNumbers(items); return JsonResult.ok(); }
}
Spring MVC接收参数的几个注解
@RequestParam
1.表单参数,键值对参数
2.能够接受get或post请求提交的参数
3.name1=value&name2=value2&name3=value3
@PathVariable
1.请求路径参数
2.http://localhost:8080/123
http://localhost:8080/123/aa/bb
@RequestBody
1.post请求协议体中的数据
2.完整的接受协议中的json数据
六.user service用户服务
1.新建项目
2.配置依赖pom.xml
3.配置yml文件
4.配置主程序
5.编写代码
业务层
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Value("${sp.user-service.users}") private String userJson; @Override public User getUser(Integer id) { log.info("users json string : "+userJson); List<User> list = JsonUtil.from(userJson, new TypeReference<List<User>>() {}); for (User u : list) { if (u.getId().equals(id)) { return u; } } return new User(id, "name-"+id, "pwd-"+id); } @Override public void addScore(Integer id, Integer score) { // 这里增长积分 log.info("user "+id+" - 增长积分 "+score); }
}
控制层
@Slf4j
@RestController
public class UserController {
@Autowired private UserService userService; @GetMapping("/{userId}") public JsonResult<User> getUser(@PathVariable Integer userId) { log.info("get user, userId="+userId); User u = userService.getUser(userId); return JsonResult.ok(u); } @GetMapping("/{userId}/score") public JsonResult addScore( @PathVariable Integer userId, Integer score) { userService.addScore(userId, score); return JsonResult.ok(); }
}
七. order service订单服务
业务层
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Override public Order getOrder(String orderId) { //TODO: 调用user-service获取用户信息 //TODO: 调用item-service获取商品信息 Order order = new Order(); order.setId(orderId); return order; } @Override public void addOrder(Order order) { //TODO: 调用item-service减小商品库存 //TODO: 调用user-service增长用户积分 log.info("保存订单:"+order); }
}
控制层
@Slf4j
@RestController
public class OrderController {
@Autowired private OrderService orderService; @GetMapping("/{orderId}") public JsonResult<Order> getOrder(@PathVariable String orderId) { log.info("get order, id="+orderId); Order order = orderService.getOrder(orderId); return JsonResult.ok(order); } @GetMapping("/") public JsonResult addOrder() { //模拟post提交的数据 Order order = new Order(); order.setId("123abc"); order.setUser(new User(7,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(); }
}
八 eureka 注册与发现
server:
port: 2001
eureka:
server:
enable-self-preservation: false 关闭保护模式
instance:
hostname: eureka1 eureka集群服务器之间 经过此标签区分
client:
#不一样自身注册 不从自身拉取信息 register-with-eureka: false fetch-registry: false
eureka注册中心
做用:服务注册和发现
提供者(Provider)
向注册中心注册本身的地址
消费者(Consumer)
从注册中心发现其余服务
eureka的运行机制
注册 一次次反复链接eureka 直到注册成功为止
拉取 每隔30秒拉取一次注册表 更新注册信息
心跳 每30秒发送一次心跳 3次收不到心跳 ureka会删除这个服务
自我保护模式 特殊状况 因为网络不稳定15分钟内85%服务器出现心跳异常
保护全部的注册信息不删除
网络恢复后 能够自动退出保护模式
开发测试期间 能够关闭保护模式
搭建eureka服务
1.eureka server依赖
2.yml文件配置
关闭保护模式
主机名(集群中区分每台服务器)
针对单台服务器,不向本身注册,不从本身拉取
3.启动类
@EnableEurekaServer 触发eureka服务器的自动配置
服务提供者
2.yml配置eureka链接地址
主程序添加@EnableEurekaServer注解
修改hosts文件
127.0.0.1 eureka1
127.0.0.1 eureka2
eureka和"服务提供者"的高可用(集群)
远程调用
RestTemplate
springboot 提供的远程调用工具,相似 HttpClient
RestTemplate 对http Rest API 调用作了高度封装,只须要调用一个方法就能够完成请求、响应、json转换
用 RestTemplate 调用 2,3,4 项目
Ribbon
springcloud 提供的工具,对 RestTemplate 进行了加强封装,提供了负载均衡和重试的功能
item-service 高可用
启动参数 --server.port 能够覆盖yml中的端口配置
配置启动参数
--server.port=8001
复制一份
--server.port=8002
eureka高可用
添加两个服务器的profile配置文件
配置启动参数和端口
--spring.profiles.active=eureka1 --server.port=2001
--spring.profiles.active=eureka2 --server.port=2002
eureka客户端注册时,向两个服务器注册
ribbon 服务消费者
ribbon 提供了负载均衡和重试功能 它底层是使用RestTemplate进行Rest api调用
RestTemplate
RestTemplate是SpringBoot提供的一个Rest远程调用工具
经常使用方法
getForObject() 执行get请求
postForObject() 执行post请求
1.新建ribbon项目
2.pom.xml
添加Eureka Discovery Client 和 web 依赖
3.yml文件
spring:
application:
name: ribbon
server:
port: 3001
eureka:
client:
service-url: defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
4.主程序
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
//建立 RestTemplate 实例,并存入 spring 容器
@Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Sp06RibbonApplication.class, args); }
}
5.controller
@RestController
public class RibbonController {
@Autowired private RestTemplate rt; @GetMapping("/item-service/{orderId}") public JsonResult<List<Item>> getItems(@PathVariable String orderId) { //向指定微服务地址发送 get 请求,并得到该服务的返回结果 //{1} 占位符,用 orderId 填充 return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId); } @PostMapping("/item-service/decreaseNumber") public JsonResult decreaseNumber(@RequestBody List<Item> items) { //发送 post 请求 return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class); } / @GetMapping("/user-service/{userId}") public JsonResult<User> getUser(@PathVariable Integer userId) { return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId); } @GetMapping("/user-service/{userId}/score") public JsonResult addScore( @PathVariable Integer userId, Integer score) { return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score); } / @GetMapping("/order-service/{orderId}") public JsonResult<Order> getOrder(@PathVariable String orderId) { return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId); } @GetMapping("/order-service") public JsonResult addOrder() { return rt.getForObject("http://localhost:8201/", JsonResult.class); }
}
6.启动 并访问测试
ribbon负载均衡和测试
负载均衡
修改sp06-ribbon项目
1.添加ribbon起步依赖
2.RestTemplate设置@loadBalanced
3.访问路径设置为服务id
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
@LoadBalanced //负载均衡注解 @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Sp06RibbonApplication.class, args); }
}
访问路径设置问服务id
将控制层的本地地址和端口改成服务的id
例如 "http://item-service/{1}"
pom.xml文件中添加spring-retry依赖
<dependency>
<groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId>
</dependency>
yml文件配置ribbon重试
spring:
application:
name: ribbon
server:
port: 3001
eureka:
client:
service-url: defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
MaxAutoRetriesNextServer: 2 更换实例的次数
MaxAutoRetries: 1 当前实例重试次数 尝试失败会更换下一个实例
OkToRetryOnAllOperations: true 可不写 默认支队GET请求重试 默认为false 当设置为true时 对POST等全部的类型请求进行都重试
主程序设置RestTemplate的请求工厂的超时属性
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
@LoadBalanced @Bean public RestTemplate getRestTemplate() { SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory(); f.setConnectTimeout(1000); f.setReadTimeout(1000); return new RestTemplate(f); //RestTemplate 中默认的 Factory 实例中,两个超时属性默认是 -1, //未启用超时,也不会触发重试 //return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Sp06RibbonApplication.class, args); }
}
item-service 的 ItemController 添加延迟代码,以便测试 ribbon 的重试机制
///--设置随机延迟
if(Math.random()<0.6) { long t = new Random().nextInt(5000); log.info("item-service-"+port+" - 暂停 "+t); Thread.sleep(t); }