Spring Cloud 入门手册(二)
application.yml
spring: application: name: order-service server: port: 8201
主程序
package cn.tedu.sp04; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @ClassName Sp04OrderserviceApplication * @Description * @Author keke * @Time 2021/7/17 16:28 * @Version 1.0 */ @SpringBootApplication @EnableFeignClients public class Sp04OrderserviceApplication { public static void main(String[] args) { SpringApplication.run(Sp04OrderserviceApplication.class, args); } }
Java 源文件
OrderServiceImpl
package cn.tedu.sp04.order.service; import cn.tedu.sp01.pojo.Order; import cn.tedu.sp01.service.OrderService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * @ClassName OrderServiceImpl * @Description * @Author keke * @Time 2021/7/17 20:07 * @Version 1.0 */ @Service @Slf4j public class OrderServiceImpl implements OrderService { @Override public Order getOrder(String id) { log.info("获取订单, orderId =" + id); // TODO:远程调用商品获取商品列表 // TODO:远程调用用户获取用户数据 Order order = new Order(); order.setId(id); return order; } @Override public void addOrder(Order order) { log.info("添加订单:" + order); // TODO:远程调用商品,减少商品库存 // TODO:远程调用用户,增加用户积分 } }
OrderController
package cn.tedu.sp04.order.controller; import cn.tedu.sp01.pojo.Item; import cn.tedu.sp01.pojo.Order; import cn.tedu.sp01.pojo.User; import cn.tedu.sp01.service.OrderService; import cn.tedu.sp01.web.util.JsonResult; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.Arrays; /** * @ClassName OrderController * @Description * @Author keke * @Time 2021/7/17 20:08 * @Version 1.0 */ @RestController @Slf4j public class OrderController { @Autowired private OrderService orderService; /** * 获取订单 */ @GetMapping("/{orderId}") public JsonResult<Order> getOrder(@PathVariable String orderId){ Order order = orderService.getOrder(orderId); return JsonResult.ok().data(order); } /** * 添加订单 */ @GetMapping("/") public JsonResult<?> addOrder(){ Order order = new Order(); order.setId("56u5645y54"); 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("添加订单成功"); } }
访问测试
根据 orderId,获取订单 http://localhost:8201/123abc
保存订单,观察窗控制台日志输出 http://localhost:8201/
service 访问测试汇总
- item-service
根据 orderId,查询商品 http://localhost:8001/35
减少商品库存 http://localhost:8001/decreaseNumber
使用 postman,POST 发送以下格式数据:
[ { "id": 1, "name": "abc", "number": 23 }, { "id": 2, "name": "def", "number": 11 } ]
- user-service
根据 userId 查询用户信息 http://localhost:8101/7
根据 userId 为用户增加积分 http://localhost:8101/7/score?score=100
- order-service
根据 orderId,获取订单 http://localhost:8201/123abc
保存订单,观察窗控制台日志输出 http://localhost:8201/
eureka 注册与发现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrf0JzOj-1639477451459)(Spring%20Cloud%20%E5%85%A5%E9%97%A8%E6%89%8B%E5%86%8C.assets/image-20210821122452430.png)]
创建 eureka 项目
配置依赖 pom.xml
配置 application.yml
主程序启用 eureka 服务器
启动,访问测试
新建 maven 项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-laqLD0TF-1639477451460)(Spring%20Cloud%20%E5%85%A5%E9%97%A8%E6%89%8B%E5%86%8C.assets/image-20210821122903650.png)]
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>order-parent</artifactId> <groupId>cn.tedu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>sp05-eureka</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </project>
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
eureka 集群服务器之间,通过 eureka.instance.hostname 来区分
eureka.server,enable-self-perservation
eureka 的自我保护状态:心跳失败的比例,在15分钟内是否超过85%,如果出现了超过的情况,Eureka Server 会将当前的实例注册信息保护起来,同时提示一个警告,一旦进入保护模式,Eureka Server 将会尝试保护其服务注册表中的信息,不再删除注册表中的数据。也就是不会注销任何微服务
eureka.client.register-with-eureka=false
不向自身注册
eureka.client.fetch-registry=false
不从自身拉取
eureka.instance.lease-expiration-duration-in-seconds
最后一次心跳后,间隔多久认定微服务不可用,默认90
主程序
添加 @EnableEurekaServer
package cn.tedu.sp05; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * @ClassName Sp05EurekaApplication * @Description * @Author keke * @Time 2021/7/17 21:30 * @Version 1.0 */ @SpringBootApplication // 触发eureka服务器的自动配置 @EnableEurekaServer public class Sp05EurekaApplication { public static void main(String[] args) { SpringApplication.run(Sp05EurekaApplication.class, args); } }
修改 hosts 文件,添加 eureka 映射
C:\Windows\System32\drivers\etc\hosts
添加内容
127.0.0.1 eureka1 127.0.0.1 eureka2
启动,并访问测试
- http://eureka1:2001
service provider 服务提供者
- 修改 item-service、user-service、order-service,把微服务注册到 eureka 服务器
- pom.xml 添加 eureka 依赖
- application.yml添加 eureka 注册信息
- 启动服务,在 eureka 中查看注册信息
pom.xml 添加 eureka 客户端依赖
利用 EditStarts 添加
上面的操作会在 pom.xml 中添加以下依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
application.yml 添加 eureka 注册配置
# defaultZone 表示默认地点 # 如果使用云服务,服务商可以通过不同的eureka服务器 # 如果没有云服务,就只能写defaultZone eureka: client: service-url: defaultZone: http://eureka1:2001/eureka
eureka.instance.lease-renewal-interval-in-seconds
心跳间隔时间,默认30秒
eureka.client.service-url.defaultZone
默认位置,可以修改为具体地点,表示 eureka 服务器的部署位置,需要云服务器提供
eureka.client.registry-fetch-interval-seconds
拉取注册时间间隔时间,默认30秒
启动服务,在 eureka 中查看注册信息
- http://eureka1:2001
eureka 和 “服务提供者” 的高可用
item-service 高可用
启动参数 --server.port
可以覆盖 yml 中的端口配置
配置启动参数
- item-service-8001
--server.port=8001
- item-service-8002
--server.port=8002
启动测试
- 访问 eureka 查看 item-service 注册信息
- 访问两个端口测试
http://localhost:8001/35
http://localhost:8002/35
eureka 高可用
添加两个服务器的 profile 配置文件
application-eureka1.yml
eureka: instance: hostname: eureka1 prefer-ip-address: true # 界面列表中显示的格式也显示ip instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} client: fetch-registry: true register-with-eureka: true service-url: defaultZone: http://eureka2:2002/eureka
application-eureka2.yml
1eureka: instance: hostname: eureka2 prefer-ip-address: true # 界面列表中显示的格式也显示ip instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} client: fetch-registry: true register-with-eureka: true service-url: defaultZone: http://eureka1:2001/eureka
配置启动参数 --spring.profiles.active
和 --server.port
- eureka1 启动参数
--spring.profiles.active=eureka1 --server.port=2001
- eureka2 启动参数
--spring.profiles.active=eureka2 --server.port=2002
如果在命令行运行,可以在命令行中添加参数:
java -jar xxx.jar --spring.profiles.active=eureka1 --server.port=2001
访问 eureka 服务器,查看注册信息
http://eureka1:2001/
http://eureka2:2002/
eureka 客户端注册时,向两个服务器注册
修改以下微服务
sp02-itemservice
sp03-userservice
sp04-orderservice
eureka: client: service-url: defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
当一个 eureka 服务宕机时,仍可以连接另一个 eureka 服务
order-service 调用商品库存服务和用户服务
修改 sp04-orderservice 项目,添加 feign,调用 item-service 和 user-service
- pom.xml
- 主程序
- ItemClient
- UserClient
- OrderServiceImpl
pom.xml
在父项目的 pom.xml 利用 EditStarts 添加
代码如下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
主程序
package cn.tedu.sp04; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @ClassName Sp04OrderserviceApplication * @Description * @Author keke * @Time 2021/8/23 23:56 * @Version 1.0 */ @EnableFeignClients @SpringBootApplication public class Sp04OrderserviceApplication { public static void main(String[] args) { SpringApplication.run(Sp04OrderserviceApplication.class, args); } }
ItemClient
package cn.tedu.sp04.order.feign; import cn.tedu.sp01.pojo.Item; import cn.tedu.sp01.web.util.JsonResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import java.util.List; /** * @ClassName ItemClient * @Description 三项配置: * 1.调用哪个服务 * 2.调用服务的哪个路径 * 3.向这个路径提交什么参数 * @Author keke * @Time 2021/7/18 16:52 * @Version 1.0 */ @FeignClient(name = "item-service") public interface ItemClient { /** * 远程调用商品,获取商品列表 * @param orderId * @return */ @GetMapping("/{orderId}") JsonResult<List<Item>> getItems(@PathVariable("orderId") String orderId); /** * 远程调用商品,减少商品库存 * @param items * @return */ @PostMapping("/decreaseNumber") JsonResult<?> decreaseNumber(@RequestBody List<Item> items); }
UserClient
package cn.tedu.sp04.order.feign; import cn.tedu.sp01.pojo.User; import cn.tedu.sp01.web.util.JsonResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; /** * @ClassName UserClient * @Description * @Author keke * @Time 2021/7/18 16:56 * @Version 1.0 */ @FeignClient(name = "user-service") public interface UserClient { /** * 远程调用用户,获取用户 * @param userId * @return */ @GetMapping("/{userId}") JsonResult<User> getUser(@PathVariable("userId") Integer userId); /** * 远程调用用户,增加用户积分 * @param userId * @param score * @return */ @GetMapping("/{userId}/score") JsonResult<?> addScore(@PathVariable("userId") Integer userId, @RequestParam("score") Integer score); }
OrderServiceImpl
package cn.tedu.sp04.order.service; import cn.tedu.sp01.pojo.Item; import cn.tedu.sp01.pojo.Order; import cn.tedu.sp01.pojo.User; import cn.tedu.sp01.service.OrderService; import cn.tedu.sp01.web.util.JsonResult; import cn.tedu.sp04.feign.ItemClient; import cn.tedu.sp04.feign.UserClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * @ClassName OrderServiceImpl * @Description * @Author keke * @Time 2021/7/17 20:07 * @Version 1.0 */ @Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired private ItemClient itemClient; @Autowired private UserClient userClient; @Override public Order getOrder(String id) { log.info("获取订单, orderId =" + id); // TODO:远程调用商品获取商品列表 JsonResult<List<Item>> items = itemClient.getItems(id); // TODO:远程调用用户获取用户数据 JsonResult<User> user = userClient.getUser(8); Order order = new Order(); order.setId(id); order.setUser(user.getData()); order.setItems(items.getData()); return order; } @Override public void addOrder(Order order) { log.info("添加订单:" + order); // TODO:远程调用商品,减少商品库存 itemClient.decreaseNumber(order.getItems()); // TODO:远程调用用户,增加用户积分 userClient.addScore(order.getUser().getId(), 1000); } }
启动服务,访问测试
- 根据 orderId,获取订单 http://localhost:8201/123abc
- 保存订单 http://localhost:8201/
zuul API 网关
zuul API 网关,为微服务应用提供同一段对外访问接口。
zuul 还提供过滤器,对所有微服务提供统一的请求校验
新建 sp06-zuul 项目
pom.xml
- 需要添加 sp01-commons 依赖
<?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>order-parent</artifactId> <groupId>cn.tedu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>sp06-zuul</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>sp01-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> </dependencies> </project>