​ Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

  • Spring Cloud Netflix
      是对Netflix开发的一套分布式服务框架的封装,包括服务的发现和注册,负载均衡、断路器、REST客户端、请求路由等。

  • Spring Cloud Config
      将配置信息中央化保存, 配置Spring Cloud Bus可以实现动态修改配置文件

  • Spring Cloud Bus
      分布式消息队列,是对Kafka, MQ的封装

  • Spring Cloud Security
      对Spring Security的封装,并能配合Netflix使用

  • Spring Cloud Zookeeper
      对Zookeeper的封装,使之能配置其它Spring Cloud的子项目使用

  • Spring Cloud Eureka

    Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件中的一部分,它基于Netflix Eureka 做了二次封装,主要负责完成微服务架构中的服务治理功能

​ Spring cloud 是微服务架构的集大成者,将一系列优秀的组件进行了整合。

本章Spring cloud 使用,一步一步带你构建Spring cloud整个应用

  1. 构建Spring cloud服务端
  2. 构建Spring cloud 服务提供端
  3. 构建Spring cloud 消费端(服务调用端)

img

一、Spring Cloud 服务端

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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <groupId>com.test.cloud</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description>

&lt;properties&gt;
    &lt;java.version&gt;1.8&lt;/java.version&gt;
    &lt;spring-cloud.version&gt;Greenwich.SR2&lt;/spring-cloud.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
        &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-server&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
        &lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
            &lt;version&gt;${spring-cloud.version}&lt;/version&gt;
            &lt;type&gt;pom&lt;/type&gt;
            &lt;scope&gt;import&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/dependencyManagement&gt;

&lt;build&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;

</project> `

> 
> application.properties配置
> 
> ```
`server.port=8761
eureka.instance.hostname=127.0.0.1
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://&lt;span class="katex math inline">{spring.security.user.name}:&lt;/span>{spring.security.user.password}@&lt;span class="katex math inline">{eureka.instance.hostname}:&lt;/span>{server.port}/eureka/

# suppress inspection "SpringBootApplicationProperties"
spring.security.basic.enabled=true
spring.security.user.name=user
spring.security.user.password=123456
`

项目入口Application配置

  1. 添加 @EnableEurekaServer 注解 在Application中

如果遇到csrf问题的时候,需要实现WebSecurityConfigurerAdapter的configure 方法

`import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); super.configure(http); } } `

> 
> 直接运行项目 出现下面表示项目启动成功
> 
> [ Thread-46] o.s.c.n.e.server.EurekaServerBootstrap : Initialized server context  
> [ Thread-46] c.n.e.r.PeerAwareInstanceRegistryImpl : Got 1 instances from neighboring DS node  
> [ Thread-46] c.n.e.r.PeerAwareInstanceRegistryImpl : Renew threshold is: 1  
> [ Thread-46] c.n.e.r.PeerAwareInstanceRegistryImpl : Changing status to UP  
> [ Thread-46] e.s.EurekaServerInitializerConfiguration : Started Eureka Server  
> [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8761 (http) with context path ''  
> [ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8761  
> [ main] com.test.cloud.demo.DemoApplication : Started DemoApplication in 18.421 seconds (JVM running for 26.067)  
> [on(2)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'  
> [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'  
> [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 16 ms
> 
> 在浏览器输入 127.0.0.1:8761 出现 输入我们在application.properties中配置的用户名和密码, 出现下面界面,表示服务注册中心启动成功,等待服务注册。
> 
>![image-20190827132029270](https://tva1.sinaimg.cn/large/006y8mN6ly1g6e60ioamjj30v20ly755.jpg) 
> 
>![image-20190827132220517](https://tva1.sinaimg.cn/large/006y8mN6ly1g6e62eawd3j31ji0u0th3.jpg) 

### 二、构建Spring cloud 服务提供端 {#toc_1}

> pom.xml 配置
> 
> ```
`&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;2.1.6.RELEASE&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;
    &lt;groupId&gt;com.example&lt;/groupId&gt;
    &lt;artifactId&gt;clientdemo&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;clientdemo&lt;/name&gt;
    &lt;description&gt;Demo project for Spring Boot&lt;/description&gt;

    &lt;properties&gt;
        &lt;java.version&gt;1.8&lt;/java.version&gt;
        &lt;spring-cloud.version&gt;Greenwich.SR2&lt;/spring-cloud.version&gt;
    &lt;/properties&gt;

    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;dependencyManagement&gt;
        &lt;dependencies&gt;
            &lt;dependency&gt;
                &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
                &lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
                &lt;version&gt;${spring-cloud.version}&lt;/version&gt;
                &lt;type&gt;pom&lt;/type&gt;
                &lt;scope&gt;import&lt;/scope&gt;
            &lt;/dependency&gt;
        &lt;/dependencies&gt;
    &lt;/dependencyManagement&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;

&lt;/project&gt;
`

application.properties配置,注意名称不能包含下划线(—)

server.port=8762 eureka.client.service-url.defaultZone=http://user:123456@127.0.0.1:8761/eureka/ spring.application.name=client-test-demo spring.security.user.name=user spring.security.user.password=123456

> 
> 入口Application配置
> 
> 服务提供者需要在Application添加@EnableEurekaClient 注解
> 
> 添加一个测试的Controller
> 
> ```
`import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("index")
public class IndexController {
    @GetMapping("test")
    @ResponseBody
    public String test(){
        return "测试-服务提供者";
    }
}
`

启动服务提供者,出现如下表示启动成功,可以通过接口调用下上面写的Controller的测试方法是否成功。

scoveryClient : Starting heartbeat executor: renew interval is: 30
[ main] c.n.discovery.InstanceInfoReplicator : InstanceInfoReplicator onDemand update allowed rate per min is 4
[ main] com.netflix.discovery.DiscoveryClient : Discovery Client initialized at timestamp 1566883730293 with initial instances count: 0
[ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application CLIENT-TEST-DEMO with eureka with status UP
[ main] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1566883730299, current=UP, previous=STARTING]
[nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_CLIENT-TEST-DEMO/192.168.1.78:client-test-demo:8762: registering service…
[ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8762 (http) with context path ’’
[ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8762
[ main] c.e.clientdemo.ClientdemoApplication : Started ClientdemoApplication in 14.831 seconds (JVM running for 21.023)

查看服务注册中心是否有启动的注册的服务

image-20190827133202151

三、构建Spring cloud 消费端(服务调用端)

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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <groupId>com.example</groupId> <artifactId>springxiaofeidemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springxiaofeidemo</name> <description>Demo project for Spring Boot</description>

&lt;properties&gt;
    &lt;java.version&gt;1.8&lt;/java.version&gt;
    &lt;spring-cloud.version&gt;Greenwich.SR2&lt;/spring-cloud.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
        &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
        &lt;artifactId&gt;spring-cloud-starter-netflix-ribbon&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
        &lt;artifactId&gt;spring-cloud-starter-openfeign&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
        &lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;!-- 添加Actuator监控 --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
            &lt;version&gt;&lt;span class="katex math inline">{spring-cloud.version}&lt;/version&gt;
            &lt;type&gt;pom&lt;/type&gt;
            &lt;scope&gt;import&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/dependencyManagement&gt;

&lt;build&gt;
    &lt;!--&lt;finalName&gt;&lt;/span>{project.artifactId}&lt;/finalName&gt;--&gt;
    &lt;!--&lt;resources&gt;--&gt;
        &lt;!--&lt;resource&gt;--&gt;
            &lt;!--&lt;directory&gt;src/main/resources&lt;/directory&gt;--&gt;
            &lt;!--&lt;filtering&gt;true&lt;/filtering&gt;--&gt;
        &lt;!--&lt;/resource&gt;--&gt;
    &lt;!--&lt;/resources&gt;--&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;
        &lt;!--&lt;plugin&gt;--&gt;
            &lt;!--&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;--&gt;
            &lt;!--&lt;artifactId&gt;maven-resources-plugin&lt;/artifactId&gt;--&gt;
            &lt;!--&lt;configuration&gt;--&gt;
                &lt;!--&lt;delimiters&gt;--&gt;
                    &lt;!--&lt;delimit&gt;$&lt;/delimit&gt;--&gt;
                &lt;!--&lt;/delimiters&gt;--&gt;
            &lt;!--&lt;/configuration&gt;--&gt;
        &lt;!--&lt;/plugin&gt;--&gt;
    &lt;/plugins&gt;
&lt;/build&gt;

</project> `

> 
> application.properties配置
> 
> ```
`server.port=8763
eureka.client.service-url.defaultZone=http://user:123456@127.0.0.1:8761/eureka/
spring.application.name=client-request-demo
spring.security.user.name=user
spring.security.user.password=123456
feign.hystrix.enabled=true
`

入口Application中添加@EnableEurekaClient和@EnableFeignClients 注解 FeignClients是用来服务间调用的。

`@EnableEurekaClient @SpringBootApplication @EnableFeignClients public class SpringxiaofeidemoApplication {

public static void main(String[] args) {
    SpringApplication.run(SpringxiaofeidemoApplication.class, args);
}

@Bean
@LoadBalanced
RestTemplate restTemplate(){
    return  new RestTemplate();
}

} `

> 
> 添加测试Controller类
> 
> ```
`import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/index")
public class IndexController {
    @Autowired
    HelloService helloService;

    @Autowired
    RestTemplate restTemplate;
    @GetMapping("test")
    public String indexTest(){// 通过HelloService中的注解绑定调用 参考 Helloservice
       return this.helloService.hello();
    }

    @GetMapping("test1")
    public String ribbonTest(){
        //注解@HystrixCommand 不能加在控制器层,而应该加在Service 层  使用Hystrix进行容错和服务降级
       return restTemplate.getForObject("http://CLIENT-TEST-DEMO/index/test",String.class);
    }
}
`

`HelloService接口 import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = “CLIENT-TEST-DEMO”,fallback = HelloServiceFallback.class) public interface HelloService { @GetMapping(value = “index/test”) String hello(); } `

> 
> HelloServiceFallback 类
> 
> ```
`import org.springframework.stereotype.Component;

@Component
public class HelloServiceFallback implements HelloService{
    @Override
    public String hello() {
        return "测试-服务降权容错提供者";
    }
}
`

启动服务

[ main] com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
[ main] com.netflix.discovery.DiscoveryClient : The response status is 200
[ main] com.netflix.discovery.DiscoveryClient : Starting heartbeat executor: renew interval is: 30
[ main] c.n.discovery.InstanceInfoReplicator : InstanceInfoReplicator onDemand update allowed rate per min is 4
[ main] com.netflix.discovery.DiscoveryClient : Discovery Client initialized at timestamp 1566884583081 with initial instances count: 1
[ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application CLIENT-REQUEST-DEMO with eureka with status UP
[ main] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1566884583086, current=UP, previous=STARTING]
[nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_CLIENT-REQUEST-DEMO/192.168.1.78:client-request-demo:8763: registering service…
[ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8763 (http) with context path ’’
[ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8763
[ main] c.e.s.SpringxiaofeidemoApplication : Started SpringxiaofeidemoApplication in 17.69 seconds (JVM running for 23.897)
[on(6)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet ‘dispatcherServlet’
[on(6)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet ‘dispatcherServlet’
[on(6)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 15 ms

在浏览器中输入
http://127.0.0.1:8763/index/test

结果:测试-服务提供者

http://127.0.0.1:8763/index/test1

结果:测试-服务提供者

如果我们关闭服务提供者会是什么情况呢?

关闭服务提供者

输入:http://127.0.0.1:8763/index/test

结果:测试-服务降权容错提供者

输入:http://127.0.0.1:8763/index/test1

结果:Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Tue Aug 27 13:48:14 CST 2019

There was an unexpected error (type=Internal Server Error, status=500).

No instances available for CLIENT-TEST-DEMO

项目控制台输出java.lang.IllegalStateException: No instances available for CLIENT-TEST-DEMO

如果服务都启动之后关闭注册中心请大家自己试试?

四、其他

上面的3个工程打包为zip放到百度云盘

链接: https://pan.baidu.com/s/1IOWaF_uUzL65_6GQWf6pDA 提取码: kuw8

背景介绍:eureka默认开启了自我保护机制,导致实际上已经停止服务的实例无法从注册中心剔除!

解决方案:

在注册中心(eureka-server端,而不是eureka-client端)添加如下配置:

以下配置仅在开发环境中使用

关闭注册中心的自我保护机制,防止已关闭的实例无法从注册中心剔除

eureka.server.enable-self-preservation=false

背景:由于Eureka拥有自我保护机制,当其注册表里服务因为网络或其他原因出现故障而关停时,Eureka不会剔除服务注册,而是等待其修复。这是AP的一种实现。
为了让其有精准的 CP健康检查,可以采取让其剔除不健康节点。

server端:
eureka.server.enable-self-preservation//(设为false,关闭自我保护主要)
eureka.server.eviction-interval-timer-in-ms//清理间隔(单位毫秒,默认是60*1000)
client端:
eureka.client.healthcheck.enabled = true//开启健康检查(需要spring-boot-starter-actuator依赖)
eureka.instance.lease-renewal-interval-in-seconds =10//租期更新时间间隔(默认30秒)
eureka.instance.lease-expiration-duration-in-seconds =30//租期到期时间(默认90秒)

实例:

server端配置:
eureka:
server:
enableSelfPreservation: false
evictionIntervalTimerInMs: 4000
client配置:
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 30

lease-renewal-interval-in-seconds 每间隔10s,向服务端发送一次心跳,证明自己依然”存活“

lease-expiration-duration-in-seconds 告诉服务端,如果我30s之内没有给你发心跳,就代表我“死”了,将我踢出掉。

注意:更改Eureka更新频率将打破服务器的自我保护功能

参考

https://zhuanlan.zhihu.com/p/26472547