前言
前面的系列文章里,介绍了ladybugflow的业务可视化的设计以及常见场景的使用方法。
感谢大家对项目的关注。
本篇文章介绍一下基于ladybugflow的微服务编排场景及使用方法。
1. 业务场景
和上一篇文章使用同样的酒店预定业务场景,本篇文章我们将它继承到SpringBoot中。
如下图所示:
不同的是,这里我们将【查询用户信息】和【查询酒店信息】业务作为远程微服务调用,项目架构如下图所示:
- 查询酒店信息节点调用远程微服务App2的查询酒店信息接口
- 查询用户信息节点调用远程微服务App3的查询用户信息接口
- 在下单节点中将用户信息和酒店信息输出到日志中。
2. 与SpringBoot的集成
接下来我们将微服务App1,App2,App3做成3个SpringBoot工程。
App2:酒店信息查询微服务
我们创建一个SpringBoot项目,添加如下接口:
http://localhost:8082/getHotel?hotel_id=XXX
@Controller public class HotelInfoController { @RequestMapping(method = RequestMethod.GET, value = "/getHotel") @ResponseBody public String getHotelInfo(@RequestParam("hotel_id") int hotelId) { return "HOTEL:"+hotelId; } }
App3:用户信息查询微服务
我们创建一个SpringBoot项目,添加如下接口:
http://localhost:8083/getUser?user_id=XXX
@Controller public class UserInfoController { @RequestMapping(method = RequestMethod.GET, value = "/getUser") @ResponseBody public String getUserInfo(@RequestParam("user_id") int userId) { return "USER:"+userId; } }
App1:酒店预定微服务
我们创建一个SpringBoot项目,工程结构如下:
加入ladybugflow依赖,如下:
build.gradle
plugins { id 'org.springframework.boot' version '2.7.2' id 'io.spring.dependency-management' version '1.0.12.RELEASE' id 'java' } group = 'nobuglady' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } ext { set('springCloudVersion', "2021.0.3") } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'io.github.nobuglady:ladybugflow:0.0.6' } dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } } tasks.named('test') { useJUnitPlatform() }
然后分别实现酒店预定的Controler,Service和Flow层代码,如下:
LadybugflowDemoMicroservice1Application.java
@SpringBootApplication @EnableFeignClients public class LadybugflowDemoMicroservice1Application { public static void main(String[] args) { SpringApplication.run(LadybugflowDemoMicroservice1Application.class, args); } @PreDestroy public void onExit() { FlowStarter.shutdown(); } }
BookHotelController.java
@Controller public class BookHotelController { @Autowired private BookHotelService bookHotelService; @RequestMapping(value = "/book_hotel", method = RequestMethod.GET) @ResponseBody public String bookHotel() { bookHotelService.bookHotel(); return "ok"; } }
BookHotelService.java
@Service public class BookHotelService { @Autowired private BookHotelFlow bookHotelFlow; public void bookHotel() { bookHotelFlow.startFlow(false); } }
BookHotelFlow.java
@Component public class BookHotelFlow extends FlowRunner{ @Autowired private HotelInfoNode hotelInfoNode; @Autowired private UserInfoNode userInfoNode; private String userInfo; private String hotelInfo; @Node(label = "start") public void processStart() throws InterruptedException { System.out.println("启动开始 (模拟业务等待3秒)"); Thread.sleep(3000); System.out.println("启动结束"); } @Node(label = "查询用户信息") public void processSearchUser() throws InterruptedException { System.out.println("查询用户信息开始 (模拟业务等待3秒)"); userInfo = userInfoNode.getUser(456); System.out.println("查询用户信息结束"); } @Node(label = "查询酒店信息") public void processSearchHotel() throws InterruptedException { System.out.println("查询酒店信息开始 (模拟业务等待3秒)"); hotelInfo = hotelInfoNode.getHotel(123); System.out.println("查询酒店信息结束"); } @Node(label = "下单") public void processOrder() throws InterruptedException { System.out.println("下单开始 (模拟业务等待3秒)"); Thread.sleep(3000); System.out.println("下单结束"); } @Node(label = "下单成功") public void processSuccess() throws InterruptedException { System.out.println("下单成功开始 (模拟业务等待3秒)"); Thread.sleep(3000); System.out.println("下单成功结束"); System.out.println("userInfo:"+userInfo); System.out.println("hotelInfo:"+hotelInfo); } }
BookHotelFlow.json
{ "flowId": "your flow id", "nodes": [ { "id": "1", "label": "start" }, { "id": "a1a38c2e-0e05-4c68-bd49-f12aea070876", "label": "查询用户信息", "readyCheck": 0 }, { "id": "1a90a997-4390-470a-ae7c-626a725438d2", "label": "查询酒店信息", "readyCheck": 0 }, { "id": "52289e99-363d-4453-8077-ca8bdc6d35bf", "label": "下单", "readyCheck": 0 }, { "id": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2", "label": "下单成功", "readyCheck": 0 } ], "edges": [ { "id": "1", "from": "1", "to": "2", "arrows": "to" }, { "id": "b3ad7ab3-8fb6-4527-8cae-6845e03da3e4", "from": "1", "to": "a1a38c2e-0e05-4c68-bd49-f12aea070876", "arrows": "to" }, { "id": "001375c7-19e7-436b-bbcd-68e36c8f23b7", "from": "1", "to": "1a90a997-4390-470a-ae7c-626a725438d2", "arrows": "to" }, { "id": "dd830043-c7a7-4c71-b91c-10c007b7b19c", "from": "1a90a997-4390-470a-ae7c-626a725438d2", "to": "52289e99-363d-4453-8077-ca8bdc6d35bf", "arrows": "to" }, { "id": "21c2c69d-0050-4eca-8283-5a2bcbdc6c37", "from": "52289e99-363d-4453-8077-ca8bdc6d35bf", "to": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2", "arrows": "to" }, { "id": "19f2f329-8163-4dc6-a353-800df79d18a6", "from": "a1a38c2e-0e05-4c68-bd49-f12aea070876", "to": "52289e99-363d-4453-8077-ca8bdc6d35bf", "arrows": "to" } ] }
HotelInfoNode.java
@FeignClient(name = "hotelInfo", url = "localhost:8082") public interface HotelInfoNode { @RequestMapping(method = RequestMethod.GET, value = "/getHotel") public String getHotel(@RequestParam("hotel_id") int hotelId); }
UserInfoNode.java
@FeignClient(name = "userInfo", url = "localhost:8083") public interface UserInfoNode { @RequestMapping(method = RequestMethod.GET, value = "/getUser") public String getUser(@RequestParam("user_id") int userId); }
3. 运行
启动微服务App1,App2,App3,
然后再浏览器输入 http://localhost:8080/book_hotel
可以看到正常结束日志
. ____ _ __ _ _ /\ / ___'_ __ _ _(_)_ __ __ _ ( ( )___ | '_ | '_| | '_ / _` | \/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |___, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.7.2) 2022-08-03 19:31:54.203 INFO 17856 --- [ main] .LadybugflowDemoMicroservice1Application : No active profile set, falling back to 1 default profile: "default" 2022-08-03 19:31:54.854 INFO 17856 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=c8301046-975c-3f5d-a827-434b9d12c7f5 2022-08-03 19:31:55.145 INFO 17856 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2022-08-03 19:31:55.155 INFO 17856 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2022-08-03 19:31:55.155 INFO 17856 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.65] 2022-08-03 19:31:55.313 INFO 17856 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2022-08-03 19:31:55.314 INFO 17856 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1069 ms 2022-08-03 19:31:56.432 INFO 17856 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2022-08-03 19:31:56.982 INFO 17856 --- [ main] .LadybugflowDemoMicroservice1Application : Started LadybugflowDemoMicroservice1Application in 3.676 seconds (JVM running for 4.615) 2022-08-03 19:32:02.853 INFO 17856 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2022-08-03 19:32:02.853 INFO 17856 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2022-08-03 19:32:02.854 INFO 17856 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms [I]2022/08/03 19:32:02.877 http-nio-8080-exec-1:ladybugflow.properties in root path not found, use default configuration [I]2022/08/03 19:32:02.879 http-nio-8080-exec-1:NodePool started. [I]2022/08/03 19:32:02.880 http-nio-8080-exec-1:Ready queue consumer thread started. [I]2022/08/03 19:32:02.881 http-nio-8080-exec-1:Complete queue consumer thread started. [I]2022/08/03 19:32:02.962 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] http-nio-8080-exec-1:json: {"flowId":"your flow id","nodes":[{"id":"1","label":"start","readyCheck":0},{"id":"a1a38c2e-0e05-4c68-bd49-f12aea070876","label":"查询用户信息","readyCheck":0},{"id":"1a90a997-4390-470a-ae7c-626a725438d2","label":"查询酒店信息","readyCheck":0},{"id":"52289e99-363d-4453-8077-ca8bdc6d35bf","label":"下单","readyCheck":0},{"id":"16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","label":"下单成功","readyCheck":0}],"edges":[{"id":"1","from":"1","to":"2","condition":null,"arrows":"to"},{"id":"b3ad7ab3-8fb6-4527-8cae-6845e03da3e4","from":"1","to":"a1a38c2e-0e05-4c68-bd49-f12aea070876","condition":null,"arrows":"to"},{"id":"001375c7-19e7-436b-bbcd-68e36c8f23b7","from":"1","to":"1a90a997-4390-470a-ae7c-626a725438d2","condition":null,"arrows":"to"},{"id":"dd830043-c7a7-4c71-b91c-10c007b7b19c","from":"1a90a997-4390-470a-ae7c-626a725438d2","to":"52289e99-363d-4453-8077-ca8bdc6d35bf","condition":null,"arrows":"to"},{"id":"21c2c69d-0050-4eca-8283-5a2bcbdc6c37","from":"52289e99-363d-4453-8077-ca8bdc6d35bf","to":"16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","condition":null,"arrows":"to"},{"id":"19f2f329-8163-4dc6-a353-800df79d18a6","from":"a1a38c2e-0e05-4c68-bd49-f12aea070876","to":"52289e99-363d-4453-8077-ca8bdc6d35bf","condition":null,"arrows":"to"}]} [I]2022/08/03 19:32:02.964 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node id:1 [I]2022/08/03 19:32:02.964 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node name:start 启动开始 (模拟业务等待3秒) 启动结束 [I]2022/08/03 19:32:05.980 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node id:a1a38c2e-0e05-4c68-bd49-f12aea070876 [I]2022/08/03 19:32:05.980 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node name:查询用户信息 查询用户信息开始 (模拟业务等待3秒) [I]2022/08/03 19:32:05.980 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-2:execute node id:1a90a997-4390-470a-ae7c-626a725438d2 [I]2022/08/03 19:32:05.981 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-2:execute node name:查询酒店信息 查询酒店信息开始 (模拟业务等待3秒) 查询酒店信息结束 查询用户信息结束 [I]2022/08/03 19:32:06.109 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node id:52289e99-363d-4453-8077-ca8bdc6d35bf [I]2022/08/03 19:32:06.109 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node name:下单 下单开始 (模拟业务等待3秒) 下单结束 [I]2022/08/03 19:32:09.118 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node id:16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2 [I]2022/08/03 19:32:09.118 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] pool-1-thread-1:execute node name:下单成功 下单成功开始 (模拟业务等待3秒) 下单成功结束 userInfo:USER:456 hotelInfo:HOTEL:123 [I]2022/08/03 19:32:12.132 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] Thread-6:Complete success. [I]2022/08/03 19:32:12.133 [your flow id][ae83277a-e7db-4787-9826-cfb44512a020] Thread-6:json: {"nodes":[{"id": "1","label": "start" ,"color": "#36AE7C"},{"id": "a1a38c2e-0e05-4c68-bd49-f12aea070876","label": "查询用户信息" ,"color": "#36AE7C"},{"id": "1a90a997-4390-470a-ae7c-626a725438d2","label": "查询酒店信息" ,"color": "#36AE7C"},{"id": "52289e99-363d-4453-8077-ca8bdc6d35bf","label": "下单" ,"color": "#36AE7C"},{"id": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","label": "下单成功" ,"color": "#36AE7C"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "b3ad7ab3-8fb6-4527-8cae-6845e03da3e4","from": "1","to": "a1a38c2e-0e05-4c68-bd49-f12aea070876","arrows": "to"},{"id": "001375c7-19e7-436b-bbcd-68e36c8f23b7","from": "1","to": "1a90a997-4390-470a-ae7c-626a725438d2","arrows": "to"},{"id": "dd830043-c7a7-4c71-b91c-10c007b7b19c","from": "1a90a997-4390-470a-ae7c-626a725438d2","to": "52289e99-363d-4453-8077-ca8bdc6d35bf","arrows": "to"},{"id": "21c2c69d-0050-4eca-8283-5a2bcbdc6c37","from": "52289e99-363d-4453-8077-ca8bdc6d35bf","to": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","arrows": "to"},{"id": "19f2f329-8163-4dc6-a353-800df79d18a6","from": "a1a38c2e-0e05-4c68-bd49-f12aea070876","to": "52289e99-363d-4453-8077-ca8bdc6d35bf","arrows": "to"}]}
我们将日志中的结束节点json复制到可视化工具中
4. 业务流程变更
ladybugflow的优点是将业务流程与业务代码分开管理,从而可以轻松的应对业务流程变更而不需要修改代码。
比如将业务流程修改为如下图所示的串行执行的时候
步骤1:将json文件拷贝到可视化工具中,生成流程图
步骤2:修改流程图
步骤3:更新json
步骤4:将更新后的json替换到工程中
{ "flowId": "your flow id", "nodes": [ { "id": "1", "label": "start" }, { "id": "a1a38c2e-0e05-4c68-bd49-f12aea070876", "label": "查询用户信息", "readyCheck": 0 }, { "id": "1a90a997-4390-470a-ae7c-626a725438d2", "label": "查询酒店信息", "readyCheck": 0 }, { "id": "52289e99-363d-4453-8077-ca8bdc6d35bf", "label": "下单", "readyCheck": 0 }, { "id": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2", "label": "下单成功", "readyCheck": 0 } ], "edges": [ { "id": "1", "from": "1", "to": "2", "arrows": "to" }, { "id": "001375c7-19e7-436b-bbcd-68e36c8f23b7", "from": "1", "to": "1a90a997-4390-470a-ae7c-626a725438d2", "arrows": "to" }, { "id": "dd830043-c7a7-4c71-b91c-10c007b7b19c", "from": "1a90a997-4390-470a-ae7c-626a725438d2", "to": "a1a38c2e-0e05-4c68-bd49-f12aea070876", "arrows": "to" }, { "id": "21c2c69d-0050-4eca-8283-5a2bcbdc6c37", "from": "52289e99-363d-4453-8077-ca8bdc6d35bf", "to": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2", "arrows": "to" }, { "id": "19f2f329-8163-4dc6-a353-800df79d18a6", "from": "a1a38c2e-0e05-4c68-bd49-f12aea070876", "to": "52289e99-363d-4453-8077-ca8bdc6d35bf", "arrows": "to" } ] }
运行结果:
[I]2022/08/03 19:40:30.625 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] http-nio-8080-exec-4:json: {"flowId":"your flow id","nodes":[{"id":"1","label":"start","readyCheck":0},{"id":"a1a38c2e-0e05-4c68-bd49-f12aea070876","label":"查询用户信息","readyCheck":0},{"id":"1a90a997-4390-470a-ae7c-626a725438d2","label":"查询酒店信息","readyCheck":0},{"id":"52289e99-363d-4453-8077-ca8bdc6d35bf","label":"下单","readyCheck":0},{"id":"16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","label":"下单成功","readyCheck":0}],"edges":[{"id":"1","from":"1","to":"2","condition":null,"arrows":"to"},{"id":"001375c7-19e7-436b-bbcd-68e36c8f23b7","from":"1","to":"1a90a997-4390-470a-ae7c-626a725438d2","condition":null,"arrows":"to"},{"id":"dd830043-c7a7-4c71-b91c-10c007b7b19c","from":"1a90a997-4390-470a-ae7c-626a725438d2","to":"a1a38c2e-0e05-4c68-bd49-f12aea070876","condition":null,"arrows":"to"},{"id":"21c2c69d-0050-4eca-8283-5a2bcbdc6c37","from":"52289e99-363d-4453-8077-ca8bdc6d35bf","to":"16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","condition":null,"arrows":"to"},{"id":"19f2f329-8163-4dc6-a353-800df79d18a6","from":"a1a38c2e-0e05-4c68-bd49-f12aea070876","to":"52289e99-363d-4453-8077-ca8bdc6d35bf","condition":null,"arrows":"to"}]} [I]2022/08/03 19:40:30.626 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node id:1 [I]2022/08/03 19:40:30.626 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node name:start 启动开始 (模拟业务等待3秒) 启动结束 [I]2022/08/03 19:40:33.640 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node id:1a90a997-4390-470a-ae7c-626a725438d2 [I]2022/08/03 19:40:33.640 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node name:查询酒店信息 查询酒店信息开始 (模拟业务等待3秒) 查询酒店信息结束 [I]2022/08/03 19:40:33.647 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node id:a1a38c2e-0e05-4c68-bd49-f12aea070876 [I]2022/08/03 19:40:33.647 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node name:查询用户信息 查询用户信息开始 (模拟业务等待3秒) 查询用户信息结束 [I]2022/08/03 19:40:33.651 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node id:52289e99-363d-4453-8077-ca8bdc6d35bf [I]2022/08/03 19:40:33.651 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node name:下单 下单开始 (模拟业务等待3秒) 下单结束 [I]2022/08/03 19:40:36.662 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node id:16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2 [I]2022/08/03 19:40:36.662 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] pool-1-thread-3:execute node name:下单成功 下单成功开始 (模拟业务等待3秒) 下单成功结束 userInfo:USER:456 hotelInfo:HOTEL:123 [I]2022/08/03 19:40:39.676 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] Thread-6:Complete success. [I]2022/08/03 19:40:39.676 [your flow id][f15f25c8-7bfd-4df8-8c74-9b5f2c5c9257] Thread-6:json: {"nodes":[{"id": "1","label": "start" ,"color": "#36AE7C"},{"id": "a1a38c2e-0e05-4c68-bd49-f12aea070876","label": "查询用户信息" ,"color": "#36AE7C"},{"id": "1a90a997-4390-470a-ae7c-626a725438d2","label": "查询酒店信息" ,"color": "#36AE7C"},{"id": "52289e99-363d-4453-8077-ca8bdc6d35bf","label": "下单" ,"color": "#36AE7C"},{"id": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","label": "下单成功" ,"color": "#36AE7C"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "001375c7-19e7-436b-bbcd-68e36c8f23b7","from": "1","to": "1a90a997-4390-470a-ae7c-626a725438d2","arrows": "to"},{"id": "dd830043-c7a7-4c71-b91c-10c007b7b19c","from": "1a90a997-4390-470a-ae7c-626a725438d2","to": "a1a38c2e-0e05-4c68-bd49-f12aea070876","arrows": "to"},{"id": "21c2c69d-0050-4eca-8283-5a2bcbdc6c37","from": "52289e99-363d-4453-8077-ca8bdc6d35bf","to": "16422cbb-ccb0-4fe2-952b-e3ad5c3acbb2","arrows": "to"},{"id": "19f2f329-8163-4dc6-a353-800df79d18a6","from": "a1a38c2e-0e05-4c68-bd49-f12aea070876","to": "52289e99-363d-4453-8077-ca8bdc6d35bf","arrows": "to"}]}
感谢您读文章到这里。
3. 最后
源码:https://github.com/nobuglady/ladybugflow
运行例源码:https://github.com/nobuglady/ladybugflow-demo-microservice
设计资料和详细的使用方法可以参照上一篇文章:https://www.cnblogs.com/nobuglady/p/16474433.html