ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【计理01组58号】【搬运】Spring Cloud系列

2022-03-08 14:35:08  阅读:192  来源: 互联网

标签:01 服务 58 计理 spring public eureka hello cloud


搬运链接 https://www.cnblogs.com/sam-uncle/category/1205832.html

spring cloud入门系列:初识spring cloud

什么是spring cloud?


它的中文官网这样说:

微服务架构集大成者,云计算最佳业务实践。

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

spring cloud入门系列二:使用Eureka进行服务治理

服务治理可以说是微服务架构中最为核心和基础的模块,它主要用来实现各个微服务实例的自动化注册和发现。

Spring Cloud Eureka是Spring Cloud Netflix 微服务套件的一部分,主要负责完成微服务架构中的服务治理功能。

本文通过简单的小例子来分享下如何通过Eureka进行服务治理:

  • 搭建服务注册中心
  • 注册服务提供者
  • 服务发现和消费

==========我是华丽的分割线========================

一、搭建服务注册中心

先列出完整目录结构:

搭建过程如下:

  1. 创建maven工程:eureka(具体实现略)
  2. 修改pom文件,引入依赖
<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>
    <groupId>com.sam</groupId>
    <artifactId>eureka</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <javaVersion>1.8</javaVersion>
    </properties>
    <!-- 使用dependencyManagement进行版本管理 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

    <dependencies>
        <!-- 引入eureka server依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>

    </dependencies>

</project>
  1. 创建启动类
/**
* 
* @EnableEurekaServer
* 用来指定该项目为Eureka的服务注册中心
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaApp {
    
    public static void main(String[] args) {
        SpringApplication.run(EurekaApp.class, args);
    }
}
  1. 配置application.properties文件
#设置tomcat服务端口号
server.port=1111
#设置服务名称
spring.application.name=eureka-service

eureka.instance.hostname=localhost
#注册中心不需要注册自己
eureka.client.register-with-eureka=false
#注册中心不需要去发现服务
eureka.client.fetch-registry=false
#设置服务注册中心的URL
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka
  1. 启动服务并访问,我们会看到这样的画面

二、注册服务提供者

先列出完整目录结构:

搭建过程如下:

创建maven工程:hello-service(具体实现略)

修改pom文件,引入依赖

<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>
    <groupId>com.sam</groupId>
    <artifactId>hello-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <javaVersion>1.8</javaVersion>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

    <dependencies>
        <!-- 引入eureka 客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

    </dependencies>
</project>

创建启动类

/***
 * 
 * @EnableDiscoveryClient
 * 让服务使用eureka服务器
 * 实现服务注册和发现
 *
 */
@EnableDiscoveryClient
@SpringBootApplication
public class HelloApp {

    public static void main(String[] args) {

        SpringApplication.run(HelloApp.class, args);
    }

}

创建controller

@RestController
public class HelloController {
    
    Logger logger = LoggerFactory.getLogger(HelloController.class);
    
    @Autowired
    DiscoveryClient discoveryClient;
    
    @RequestMapping("/hello")
    public String hello() {
        ServiceInstance instance = discoveryClient.getLocalServiceInstance();
        //打印服务的服务id
        logger.info("*********" + instance.getServiceId());
        return "hello,this is hello-service";
    }
}

配置application.properties文件

server.port=9090
#设置服务名
spring.application.name=hello-service
#设置服务注册中心的URL,本服务要向该服务注册中心注册自己
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka

启动并测试

启动后再hello-service的控制台会有这种字样(xxx代表你的PC名)

Registered instance HELLO-SERVICE/xxx:hello-service:9090 with status UP (replication=false)

eureka的控制台会打印出如下字样(xxx代表你的PC名)

Registered instance HELLO-SERVICE/xxx:hello-service:9090 with status UP (replication=false)

再次访问localhost:1111,会发现有服务注册到注册中心了

三、服务发现和消费

完整目录结构如下:

搭建过程:

  1. 创建maven工程(具体实现略)
  2. 修改pom文件,引入依赖
<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>
    <groupId>com.sam</groupId>
    <artifactId>hello-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <javaVersion>1.8</javaVersion>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

    <dependencies>
        <!-- 引入eureka 客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!-- 引入ribbon 依赖 ,用来实现负载均衡,我们这里只是使用,先不作其他介绍-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

    </dependencies>
</project>这里比hello-service服务提供者,多了ribbon的依赖

创建启动类

@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApp {
    
    
    //@Bean 应用在方法上,用来将方法返回值设为为bean
    @Bean
    @LoadBalanced  //@LoadBalanced实现负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class, args);
    }
}
这里也要用到@EnableDiscoveryClient, 让服务使用eureka服务器, 实现服务注册和发现

创建controller

@RestController
public class ConsumerController {

    //这里注入的restTemplate就是在com.sam.ConsumerApp中通过@Bean配置的实例
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/hello-consumer")
    public String helloConsumer() {
        //调用hello-service服务,注意这里用的是服务名,而不是具体的ip+port
        restTemplate.getForObject("http://hello-service/hello", String.class);
        return "hello consumer finish !!!";
    }
}

配置application.properties文件

server.port=9999

spring.application.name=hello-consumer
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka


#这里的配置项目和服务提供者hello-service一样

启动,测试

启动eureka。为了展示负责均衡的效果,我们的hello-service启动两个服务,启动两个服务的具体步骤如下

以上是hello-service1的启动步骤,端口号为9090;同样方法设置hello-service2,端口号为9091(具体实现略)。

启动hello-consumer

再次访问http://localhost:1111/,会发现有2个hello-service服务(端口号一个是9090,一个是9091),1个hello-consume服务

多次访问http://localhost:9999/hello-consumer,会发现hello-service1和hello-service2会轮流被调用(已经实现了负责均衡),可以通过两者的控制台打印内容确认(还记得我们在hello-service的controller中有个loggerlogger.info("*********" + instance.getServiceId());吗?对,就是这个打印)

四、总结

以上实例实现了基本的服务治理:

  • 通过spring-cloud-starter-eureka-server和@EnableEurekaServer实现服务注册中心
  • 通过spring-cloud-starter-eureka和@EnableDiscoveryClient使用并注册到服务注册中心
  • 通过spring-cloud-starter-eureka和@EnableDiscoveryClient使用注册中心并发现服务,通过spring-cloud-starter-ribbon来实现负载均衡消费服务

PS:这里说明下,我用的IDE是Spring Tool Suite,是spring定制版的eclipse,方便我们使用spring进行开发,有兴趣的朋友可以自行百度了解下。

spring cloud入门系列三:使用Eureka搭建高可用服务注册中心

一、代码实现

还是使用上篇文章中的eureka服务例子,在此基础上我们进行修改:

新增/eureka/src/main/resources/application-peer1.properties

server.port=1111

spring.application.name=eureka-service
#设定主机名为peer1
eureka.instance.hostname=peer1

#eureka.client.register-with-eureka=false
#eureka.client.fetch-registry=false
#设定eureka的serviceUrl为peer2
eureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka

新增/eureka/src/main/resources/application-peer2.properties

server.port=1112

spring.application.name=eureka-service
eureka.instance.hostname=peer2
#eureka.client.register-with-eureka=false
#eureka.client.fetch-registry=false
#设定eureka的serviceUrl为peer1
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka

由于需要peer1和peer2相互注册,因此第1和第2步中,eureka.client.register-with-eureka=false和eureka.client.fetch-registry=false这两个配置就不能要了,需要注释掉或删掉。

设定host,在C:\Windows\System32\drivers\etc目录下的hosts文件中添加

127.0.0.1 peer1

127.0.0.1 peer2

以将peer1和peer2进行解析。

配置两个启动服务

启动eureka1和eureka2

页面访问http://localhost:1111/我们会发现,在DS Replicas(分片)中会有peer2; 在服务instance中会有2个,一个是1111,一个是1112; 在registered-replicas(注册分片)和available-replicas(可用分片)中出现了peer2:1112。 同样道理,我们访问http://localhost:1112/也能看到相似效果。 如果将peer2停掉,那么在访问http://localhost:1111/的时候就会发现peer2已经跑到unavailable-replicas(不可用分片)中,具体截图略。

二、调试中遇到的问题

我在调试以上内容的时候遇到一个问题,就是在peer1和peer2一直不能彼此注册成功,页面表现就是服务instance为空并且available-replicas为空,对应的服务在unavailable-replicas中。后来经过多次实现和分析,终于发现,原来是在我的application.properties文件中还保留着下面两个配置,服务在启动的时候是会读取到这个配置的,

虽然这两个配置默认是true,但是却被application.properties覆盖了,这个时候讲这两个配置注释掉就好了。

如果在application-{profiles}.properties中再配置一遍就又可以覆盖application.properties里面的配置。

代码读取顺序是这样的:

先读取默认配置-->然后读取application.properties-->读取application-{profiles}.properties。

spring cloud入门系列四:使用Hystrix实现断路器进行服务容错保护

在微服务中,我们将系统拆分为很多个服务单元,各单元之间通过服务注册和订阅消费的方式进行相互依赖。但是如果有一些服务出现问题了会怎么样?

比如说有三个服务(ABC),A调用B,B调用C。由于网络延迟或C本身代码有问题导致B迟迟得不到回应,这样B调用C的请求就会被挂起,等待。

在高并发的访问的情况下,这些挂起的线程得不到释放,使后续的请求阻塞,最终导致B也挂掉了。依次类推,A可能也会挂掉,进而使整个系统全部崩溃。

为了解决整个问题,Spring Cloud 使用Hystrix进行服务容错保护,包括断路器、线程隔离等一系列的保护功能,今天我们就来看下如何通过Hystrix实现断路器。

一、什么是Spring Cloud Hystrix?什么是断路器?

Spring Cloud Hystrix是基于Netflix的开源框架Hystrix实现的,其目的是为了通过控制那些访问远程系统、服务和第三方的节点,从而对延迟和故障提供强大的容错能力。

断路器类似于我们家里面强电箱里面用到的漏电断路保护器,当服务单元出现故障(类似于电器发生短路),通过断路器的故障监控功能(类似于保险丝),向调用方返回一个错误响应,避免长时间等待,从而避免故障蔓延到整个系统。

二、没有断路器的情况下,页面展示

还记得我们前面写的spring cloud 入门系列二:使用Eureka 进行服务治理里面的三个服务(eureka/hello-service/hello-consumer)吗?我们基于这个进行实验。

  1. 启动eureka服务注册中心,端口号1111
  2. 启动hello-service服务提供者,这里我们启动两个服务,端口号分别为9090,9091
  1. 启动hello-consumer服务消费者,端口号为9999;这个时候我们多次访问http://localhost:9999/hello-consumer是没有问题的
  2. 将hello-service端口号为9091的服务关掉,再去多次访问http://localhost:9999/hello-consumer,报错了PS:这里说明下,为什么要多次访问,是因为我们通过ribbon实现了负载均衡,访问http://localhost:9999/hello-consumer的时候,会轮询访问hello-service的两个服务,当访问到端口号是9091的服务时才报错,访问9090的服务就不会有问题。

三、断路器代码实现

接下来我们看下如何进行代码实现,我们不去修改服务注册中心和服务提供者,只需要修改服务消费者hello-consumer。

修改POM文件,引入Hystrix依赖

<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>
    <groupId>com.sam</groupId>
    <artifactId>hello-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <javaVersion>1.8</javaVersion>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

    <dependencies>
        <!-- 引入eureka 客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!-- 引入ribbon 依赖 ,用来实现负载均衡,我们这里只是使用先不作其他介绍 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
        <!-- 引入hystrix 依赖 ,用来实现服务容错保护-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>

    </dependencies>
</project>

修改启动类,追加注解@EnableCircuitBreaker,开启断路器

@EnableDiscoveryClient
@SpringBootApplication
@EnableCircuitBreaker
public class ConsumerApp {


    //@Bean 应用在方法上,用来将方法返回值设为为bean
    @Bean
    @LoadBalanced  //@LoadBalanced实现负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class, args);
    }
}

这个时候你会发现,这个启动类加了三个注解,这个是不是很麻烦?没关系,我们可以使用注解@SpringCloudApplication

@SpringCloudApplication
public class ConsumerApp {


    //@Bean 应用在方法上,用来将方法返回值设为为bean
    @Bean
    @LoadBalanced  //@LoadBalanced实现负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class, args);
    }
}


@SpringCloudApplication = @EnableDiscoveryClient +@SpringBootApplication+@EnableCircuitBreaker,从源码就能看出来:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}


追加service

@Service
public class ConsumerService {
    
    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "errorMsg")
    public String consumer() {
        // 调用hello-service服务,注意这里用的是服务名,而不是具体的ip+port
        restTemplate.getForObject("http://hello-service/hello", String.class);
        return "hello consumer finish !!!";
    }

    public String errorMsg() {
        return "error!!!";
    }
}

我们把原来controller里面的调用RestTemplate的实现放到service里面,并且通过@HystrixCommand来指定回调方法,当出现错误时调用该方法。

修改controller

/**
 *这里不再直接调用restTemplate,
 *而是通过调用service进行实现 
 *
 */
@RestController
public class ConsumerController {

    @Autowired
//    RestTemplate restTemplate;
    ConsumerService service;
    
    
    @RequestMapping("/hello-consumer")
    public String helloConsumer() {
//        //调用hello-service服务,注意这里用的是服务名,而不是具体的ip+port
//        restTemplate.getForObject("http://hello-service/hello", String.class);
        return service.consumer();
    }
}

测试,多次访问,当报错的时候,会显示如下内容

spring cloud入门系列五:使用Feign实现声明式服务调用

一、Spring Cloud Feign概念引入

通过前面的随笔,我们了解如何通过Spring Cloud ribbon进行负责均衡,如何通过Spring Cloud Hystrix进行服务断路保护,
两者作为基础工具类框架应用在各种基础设施类微服务和业务类微服务中,并且成对存在,那么有没有更高层的封装,将两者的使用
进一步简化呢? 有! 他就是Spring Cloud Feign。它基于Netflix Feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,
除了提供两者强大的功能外,还提供了一种声明式的Web服务客户端定义方式。

二、入门实例

我们还是继续使用前面随笔中的hello-service服务,这里通过Spring Cloud Feign提供的声明式服务绑定功能来实现对服务接口的调用。
我们需要新建一个feign-consumer来代替之前的hello-consumer

先给出代码结构:

代码实现:

新建maven工程(feign-consumer)

修改pom文件,引入eureka和feign依赖

<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>
  <groupId>com.sam</groupId>
  <artifactId>feign-consumer</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <javaVersion>1.8</javaVersion>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

    <dependencies>
        <!-- 引入eureka 客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!-- 引入feign 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

    </dependencies>
    
</project>

新建启动类

/**
 * 通过@EnableFeignClients来开启spring cloud feign的支持功能
 *
 */
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class FeiApp {

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

}

新建service接口

/**
 * 通过@FeignClient注解指定服务名来绑定服务,这里的服务名字不区分大小写
 * 然后再通过@RequestMapping来绑定服务下的rest接口
 *
 */
@FeignClient(name="hello-service")
public interface FeignConsumerService{

    @RequestMapping("/hello")
    public void hello();
}

新建controller

@RestController
public class FeiConsumerController {

    @Autowired
    FeignConsumerService consumerService;

    @RequestMapping("feign-consumer")
    public String feignConsumer() {
        consumerService.hello();
        return "feign consumer call finished!!!";
    }

}

新建application.properties

server.port=9001

spring.application.name=feign-consumer

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka

测试,

启动服务注册中心eureka、启动两个hello-service(端口号分别为9090和9091),启动feign-consumer

访问http://localhost:9001/feign-consumer

并且多次访问的话,会轮询调用两个hello-service服务。

三、参数绑定

在上面的例子中,我们实现的只是一个不带参数的rest服务绑定,然而现实的业务中不会这么简单,往往会有各种参数,
这个时候我们做如下事情:

  1. 如果服务提供方有对象参数(如User对象),那么feign-consumer工程中需要建一个路径和类名完全一样的类。
  2. 然后将服务提供方controller里面的所有方法声明进行copy(包括前面的@RequestMapping),粘贴到feign-consumer的service接口里面。

四、继承特性

根据上面参数绑定的做法,我们需要进行很多copy操作,这样比较麻烦,可以通过继承的方式进行简化。

这种实现步骤如下:

1.我们需要新建一个基础工程hello-service-api,

代码结构:

代码实现:

新建maven项目hello-service-api

修改pom文件

<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>
  <groupId>com.sam</groupId>
  <artifactId>hello-service-api</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <properties>
        <javaVersion>1.8</javaVersion>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
    </dependencies>
</project>

你会发现其实就是一个普通的spring boot项目。

考虑到需要掩饰参数中有对象的情况,我们加个User

package com.sam.entity;

public class User {
    
    private String name;
    private Integer age;
    
    
    public User(String name, Integer age) {
        super();
        this.name = name;
        this.age = age;
    }
    
    
    public User() {
        super();
    }
    
    
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public Integer getAge() {
        return age;
    }
    
    public void setAge(Integer age) {
        this.age = age;
    }
    
}

User必须有一个无参数的构造器

新建service接口

/**
 * 为了同前面那个hello 接口区分开了,我们加了refactor前缀
 *
 */
@RequestMapping("/refactor")
public interface HelloService {

    @RequestMapping("/hello2")
    public String hello2();
    
    @RequestMapping("/hello3")
    public User printUser(@RequestBody User user);
}

重构hello-service

修改pom文件

<!-- 引入 hello-service-api的依赖,以继承其提供的接口 -->
        <dependency>
            <groupId>com.sam</groupId>
            <artifactId>hello-service-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

HelloController implements HelloService,并实现interface中的接口

@RestController
public class HelloController implements HelloService{
    
    Logger logger = LoggerFactory.getLogger(HelloController.class);
    
    @Autowired
    DiscoveryClient discoveryClient;
    
    @RequestMapping("/hello")
    public String hello() throws Exception {
        ServiceInstance instance = discoveryClient.getLocalServiceInstance();
        //打印服务的服务id
        logger.info("*********" + instance.getServiceId());
        return "hello,this is hello-service";
    }
    
    @Override
    public String hello2() {
        return "hello,this is hello2-service";
        
    }
    
    @Override
    public User printUser(@RequestBody User user) {
        return user;
    }
    
}

controller实现接口的方法时,不需要@RequestMapping注解,只需要类注解@RestController即可。

重构feign-consumer服务

修改POM文件

<!-- 引入 hello-service-api的依赖,以继承其提供的接口 -->
<dependency>
  <groupId>com.sam</groupId>
  <artifactId>hello-service-api</artifactId>
  <version>0.0.1-SNAPSHOT</version>
        </dependency>

让FeignConsumerService extends hello-service-api中的HelloService

/**
* 通过@FeignClient注解指定服务名来绑定服务,这里的服务名字不区分大小写
* 然后再通过@RequestMapping来绑定服务下的rest接口
*
*/
@FeignClient(name="hello-service")
public interface FeignConsumerService extends HelloService{
    
    @RequestMapping("/hello")
    public void hello();
}

只需要继承即可

修改controller,追加方法

@RestController
public class FeiConsumerController {

    @Autowired
    FeignConsumerService consumerService;

    @RequestMapping("feign-consumer")
    public String feignConsumer() {
        consumerService.hello();
        return "feign consumer call finished!!!";
    }

    @RequestMapping("feign-consumer-user")
    public User feignConsumer2(User user) {
        consumerService.hello2();
        return consumerService.printUser(user);
    }
}

测试

五、其他

由于Spring Cloud Feign是通过ribbon和hystrix实现具体功能的,因此可以直接通过配置这两个来实现功能
1.ribbon配置方式:
通过ribbon.<key>=<value>的方式进行全局配置,比如
ribbon.ConnectTimeout=500
ribbon.ReadTimeout=5000
通过<client>.ribbon.<key>=<value>的方式进行指定服务配置,比如
#这里的<client>为@FeignClient(value="hello-service")指定的服务名
hello-service.ribbon.ConnectTimeout=500
hello-service.ribbon.ReadTimeout=500
2.hystrix配置方式:
通过hystrix.command.default.xxx进行全局配置
如:hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
通过hystrix.command.<commandKey>.xxx进行指定配置,这里的<commandKey>可以为方法名
如:hystrix.command.hello.execution.isolation.thread.timeoutInMilliseconds=5000
3.请求压缩配置,支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。
feign.compression.request.enabled=true;

feigan.compression.response.enabled=true;


4.日志配置
Spring Cloud Feign在构建被@FeignClient注解修饰的服务客户端是,会为每一个客户端都创建一个feign.Logger实例,我们可以利用该日志对象进行Log分析。

spring cloud入门系列六:使用Zuul 实现API网关服务

通过前面几次的分享,我们了解了微服务架构的几个核心设施,通过这些组件我们可以搭建简单的微服务架构系统。比如通过Spring Cloud Eureka搭建高可用的服务注册中心并实现服务的注册和发现;

通过Spring Cloud Ribbon或Feign进行负载均衡;通过Spring Cloud Hystrix进行服务容错保护以避免故障蔓延。微服务搭建好了之后我们肯定会提供给外部系统一些统一的RESTFul API服务接口进行调用,

但是当外部系统调用我们的RESTful API的时候,怎么确定它需要的功能具体是哪个服务提供的呢?这个就涉及到一个路由规则和服务实例列表的维护问题。

这就引入了我们今天的主角--Spring Cloud Zuul,它是基于Netflix Zuul实现的API网关组件。它可以解决两个大问题:

  1. 就是我们上面提到的路由规则和服务实例的维护问题
  2. 对于一些校验(比如登录校验等)冗余问题。 按照我们的习惯的做法,是在每个服务中都需要加入这些校验,但是这样会导致代码冗余并且维护也比较麻烦,有了Spring Cloud Zuul这个网关服务之后,我们可以将这些共通的校验放到网关里面统一维护。

好,接下来我们就来看下怎么实现这个网关服务。

一、构建网关,配置路由

这里我们还是需要使用到前面的hello-service和feign-consumer服务。我们之前把feign-consumer作为服务消费者,但是别忘了在eureka体系里面,每个服务既是服务提供者又是服务消费者,所以feign-consumer也是一个服务提供者,并且http://localhost:9001/feign-consumer等接口就是它提供的服务。

接下来我们构建一个网关服务,代码结构如下:

代码实现步骤

新建maven工程api-gateway

修改pom文件

<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>
  <groupId>com.sam</groupId>
  <artifactId>api-gateway</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
  </parent>
  
  <properties>
    <javaVersion>1.8</javaVersion>
  </properties>
  <!-- 使用dependencyManagement进行版本管理 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Camden.SR6</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
    
  </dependencyManagement>
  
  <dependencies>
    <!-- 引入zuul依赖 , 它依赖了spring-boot-starter-actuator/spring-boot-starter-hystrix/spring-boot-starter-ribbon-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
    
  </dependencies>
  
</project>

新建启动类

/**
* @EnableZuulProxy 开启Zuul 的API网关服务功能
*
*/
@EnableZuulProxy
@SpringCloudApplication
public class GateWayApp {
    
    public static void main(String[] args) {
        SpringApplication.run(GateWayApp.class, args);
    }
}

新建application.properties

server.port=5555
spring.application.name=api-gateway

#增加路由规则的配置
#通过zuul.routes.<route>.path和zuul.routes.<route>.url进行配置,<route>为路由的名字,可以任意指定,但是一组path和url的路由名要相同
  #如下面的例子:所有满足/api-a/** 规则的访问都会被路由转发到//localhost:9001的地址
  #也就是说,我们访问http://localhost:5555/api-a/hello的时候,API网关服务就会将该请#求路由到 http://localhost:9001/hello提供的微服务接口上
  zuul.routes.api-a.path=/api-a/**
  zuul.routes.api-a.url=http://localhost:9001
  
  
  zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.url=http://localhost:9090

测试,启动eureka、hello-service、feign-consumer以及本次新加的api-gateway服务,然后访问http://localhost:5555/api-a/feign-consumer

成功访问到了feign-consumer的服务接口--feign-consonsumer。

以上步骤实现了传统路由的配置,这种配置有个大的缺点,就是需要手工在application.properties文件中进行路由规则的配置,当服务很多的时候,维护工作量就会很大。为了减小维护成本,还有另外一种路由--面向服务的路由。

二、面向服务的路由

Spring Cloud Zuul和Eureka进行整合,我们可以让路由的path不是映射具体的url,而是具体的某个服务,而服务的url则交给Eureka服务发现机制自动维护,这类路由就是面向服务的路由。具体代码配置如下:

修改POM文件,引入Eureka依赖

<!-- 引入eureka依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

修改application.properties配置文件

server.port=5555
spring.application.name=api-gateway

zuul.routes.api-a.path=/api-a/**

#这里用serviceId代替url,用服务名代替ip+端口号
zuul.routes.api-a.serviceId=hello-serviceeureka.client.service-url.defaultZone=http://localhost:1111/eureka

注意:zuul.routes.api-a.url=hello-service也能实现功能,但是它不能进行正常的负载均衡和容错保护。

测试,访问http://localhost:5555/api-a/hello

三、服务路由的默认规则

在面向服务的路由中,由于<route>名字是随意起的,那么是不是可以这样:

zuul.routes.hello-service.path=/hello-service/**

zuul.routes.hello-service.serviceId=hello-service

<route>名字就是服务名,其实在实际的应用中,我们往往就是这样命名的。如果有这样的规则的话,那Zuul就可以帮我们默认实现这样的功能,进一步省去了配置的麻烦。

我们来做个实验,将配置文件改为:

server.port=5555

spring.application.name=api-gateway

eureka.client.service-url.defaultZone=http://localhost:1111/eureka

然后页面访问验证

访问成功。

但是由于默认情况下,Eureka上的服务都会被Zuul创建默认的映射关系来进行路由,使得我们不想对外开放的服务也被外部访问到,这个时候可以通过配置zuul.ignored-services来进行配置不需要自动创建路由的规则。当zuul.ignored-services=*的时候,所有的服务都不会自动创建路由规则,这个时候需要通过前面的配置进行相关路由配置了。

================华丽的分割线===================

前面说了那么多都是围绕一个问题展开的:路由规则和服务实例的维护问题,那么怎么解决第二个问题(校验冗余问题)呢?

四、请求过滤

为了在API网关中实现对客户端请求的校验,我们可以通过过滤器来实现对请求的拦截和过滤,实现方法比较简单,只需要继承ZuulFilter抽象类并实现其四个方法就行了。

修改api-gateway:

新增过滤器类

/**
* 继承ZuulFilter,并且实现其4个接口
*
* 用来进行请求过滤
*
*/
public class AccessFilter extends ZuulFilter {
    Logger logger = LoggerFactory.getLogger(AccessFilter.class);
    /* 
    * shouldFilter 判断该过滤器是否需要被执行
    * 
    * 这里直接返回true,表示该过滤器对所有请求都会生效。
    * 实际运用中我们可以利用该函数指定过滤器的有效范围
    */
    @Override
    public boolean shouldFilter() {
        return true;
    }
    
    /*
    * 过滤器的具体逻辑
    * 
    * 这里我们通过ctx.setSendZuulResponse(false)让zuul过来请求,不对其进行路由
    * 然后通过ctx.setResponseStatusCode(401)设置了返回的错误码
    * 
    */
    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        Object accessToken = request.getParameter("accessToken");
        
        logger.info("send {} request to {}", request.getMethod(),request.getRequestURL().toString());
        if(accessToken == null) {
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
        }
        
        return null;
    }
    
    /* filterType 返回过滤器类型
    * 他决定了过滤器在请求的哪个生命周期中执行。这里定义为pre,代表会在请求被路由前执行。
    * 
    * pre:请求执行之前filter 
    * route: 处理请求,进行路由 
    * post: 请求处理完成后执行的filter 
    * error:出现错误时执行的filter
    */
    @Override
    public String filterType() {
        return "pre";
    }
    
    
    /* 
    * filterOrder 返回过滤器的执行顺序
    * 
    * 当请求在一个阶段有多个过滤器是,需要根据该方法的返回值来依次执行
    * 
    */
    @Override
    public int filterOrder() {
        return 0;
    }
    
}

修改启动类

/**
 * @EnableZuulProxy 开启Zuul 的API网关服务功能
 *
 */
@EnableZuulProxy
@SpringCloudApplication
public class GateWayApp {

    //追加bean的是实现
    @Bean
    public AccessFilter accessFilter() {
        return new AccessFilter();
    }
    
    public static void main(String[] args) {
        SpringApplication.run(GateWayApp.class, args);
    }
}

测试

访问http://localhost:5555/hello-service/hello,访问失败

访问http://localhost:5555/hello-service/hello?accessToken=token,正常访问

修改后的代码结构:

五、拓展延伸

其实路由功能在真正运行时,他的路由映射和请求转发都是由几个不同的过滤器完成的。

路由映射主要通过pre类型的过滤器完成,他将请求路径与配置的路由规则进行匹配,找到需要转发的目标地址。

而请求转发的部分则是由route类型的过滤器完成的,对pre类型过滤器获取的路由地址进行转发。

所以,过滤器可以说是Zuul实现API网关功能最为核心的部件,每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。

spring cloud入门系列七:基基Git存储的分布式配置中心--Spring_ Cloud Config

我们前面接触到的spring cloud组件都是基于Netflix的组件进行实现的,这次我们来看下spring cloud 团队自己创建的一个全新项目:Spring Cloud Config.
它用来为分布式系统中的基础设施和微服务提供集中化的外部配置支持,分为服务端和客户端两个部分。

其中服务端也称为分布式配置中心,他是独立的微服务应用,用来连接配置仓库并为客户端提供获取接口(这些接口返回配置信息、加密、解密信息等);

客户端是微服务架构中的各个微服务应用或基础设施,它们通过制定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。
由于配置中心默认采用Git来存储配置信息,因此我们会用到Git相关的内容,如果没有用过Git或者忘记怎么用了,可以参考下廖雪峰老师的Git教程
另外,我自己用的Git远程仓库是码云。
====================华丽的分割线===================
接下来看下代码怎么实现。

一、准备远程Git仓库

  1. 在Gitee上新建一个项目https://gitee.com/sam-uncle/spring-cloud-learning
  2. 在项目下新建子目录spring-cloud-config-file,然后新建三个文件
    1. 内容分别是from=git-dev-1.0、from=git-test-1.0、from=git-1.0
  1. 新建一个分支config-lable-test,新分支里面新建三个同名的文件,不过内容分别是from=git-dev-2.0、from=git-test-2.0、from=git-2.0

二、构建配置中心

先给出最终代码结构:

搭建过程如下:

新建maven工程config-server

修改POM文件

<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>
  <groupId>com.sam</groupId>
  <artifactId>config-server</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
  </parent>
  
  <properties>
    <javaVersion>1.8</javaVersion>
  </properties>
  <!-- 使用dependencyManagement进行版本管理 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Camden.SR6</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
    
  </dependencyManagement>
  
  <dependencies>
    <!-- 引入config server依赖 -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    
  </dependencies>
  
</project>

创建启动类

/**
* @EnableConfigServer
* 
* 开启Spring Cloud Config 的服务端功能
*
*/
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApp {
    
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApp.class, args);
    }
}

配置application.properties文件,指定远程仓库信息

server.port=7001
spring.application.name=config-server

#配置Git仓库的地址
spring.cloud.config.server.git.uri=https://gitee.com/sam-uncle/spring-cloud-learning/
#配置仓库路径下的相对搜索位置,可以配置多个
spring.cloud.config.server.git.search-paths=spring-cloud-config-file
#这里配置你的Git仓库的用户名
spring.cloud.config.server.git.username=用户名
#这里配置你的Git仓库的密码
spring.cloud.config.server.git.password=密码

启动并验证

访问配置信息的URL与配置文件的映射关系如下:

    • /{application}/{profile} [/{label}]
    • /{application}-{profile}.yml
    • /{label}/{application}-{profile}.yml
    • /{application}-{profile}.properties
    • /{label}/{appliction}-{profile}.properties

上面的url会映射{application}-{profile}.properties对应的配置文件,其中{label}对应Git上不同的分支,默认是master。

通过浏览器访问http://localhost:7001/sam/dev/config-label-test,结果如下:

三、实现客户端

最终代码结构:

搭建过程如下:

新建maven工程config-client

修改POM文件

<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>
  <groupId>com.sam</groupId>
  <artifactId>config-client</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
  </parent>
  
  <properties>
    <javaVersion>1.8</javaVersion>
  </properties>
  <!-- 使用dependencyManagement进行版本管理 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Camden.SR6</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
    
  </dependencyManagement>
  
  <dependencies>
    <!-- 引入config依赖 -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
  </dependencies>
  
</project>

创建启动类

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

配置bootstrap.properties文件,制定config-server位置

server.port=7002
#{application}
spring.application.name=sam
#{profile}
spring.cloud.config.profile=dev
#{label}
spring.cloud.config.label=master

#config server uri
spring.cloud.config.uri=http://localhost:7001/

创建controller

@RefreshScope
@RestController
public class TestController {
    
    
    /**
    * 通过@Value 来讲配置文件中的值写入到代码中
    */
    @Value("${from}")
    private String from;
    
    @RequestMapping("/from")
    public String from() {
        return from;
    }
}

启动并测试

四、工作原理

Spring Cloud Config配置中心的工作原理如下:

  1. 客户端启动时,根据bootstrap.properties中配置的应用名{application}、环境名{profile}、分支名{label},向Config Server请求获取配置信息。
  2. Config Server根据自己维护的Git仓库信息和客户传递过来的配置定位信息去查找配置信息。
  1. 通过git clone命令将找到的配置信息下载到本地(Config Server的文件系统中)。在通过页面访问或启动客户端的时候,我们在服务端能看到如下下载的log:

2018-05-14 22:51:58.055 INFO 3084 --- [nio-7001-exec-1] o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:/C:/Users/sam/AppData/Local/Temp/config-repo-8627749771720918793/spring-cloud-config-file/sam-dev.properties

2018-05-14 22:51:58.055 INFO 3084 --- [nio-7001-exec-1] o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:/C:/Users/sam/AppData/Local/Temp/config-repo-8627749771720918793/spring-cloud-config-file/sam.properties

  1. Config Server创建Spring 的ApplicationContext实例,并从Git本地仓库中加载配置文件,最后将这些配置内容读取出来返回给客户端。
  2. 客户端在获取外部配置信息后加载到客户端的applicationContext实例。

spring cloud入门系列八:使用spring_ cloud sleuth整合zipkin进行服务链路追踪

微服务架构是一种分布式架构,微服务系统按照业务划分服务单元,一个微服务往往会有很多个服务单元,一个请求往往会有很多个单元参与,一旦请求出现异常,想要去定位问题点真心不容易,因此需要有个东西去跟踪请求链路,记录一个请求都调用了哪些服务单元,调用顺序是怎么样的以及在各个服务单元处理的时间长短。常见的服务链路追踪组件有google的dapper、twitter的zipkin、阿里的鹰眼等,它们都是出众的开源链路追踪组件。

spring cloud 有自己的组件来集成这些开源组件,它就是spring cloud sleuth,它为服务链路追踪提供了一套完整的解决方案。

今天的主题就是如何使用spring cloud sleuth整合zipkin进行服务链路追踪。本博客将围绕下面的线索进行展开:

  1. Server端代码实现
  2. Client端代码实现
  1. 执行测试

由上面的线索可以发现,zipkin分服务端和客户端。

客户端就是我们的服务单元,用来发送链路信息到服务端;

服务端用来接收客户端发送来的链路信息,并进行处理,它包括4个部分:

  • Collector组件:用来接收客户端发送的链路信息然后整理成zipkin能处理的格式,供后续存储或向外部提供查询使用。
  • Storage组件:对链路信息进行保存,默认存储在内存,通过配置还可以保存到mysql等地方。
  • Restful API组件:对其他服务单元提供api接口进行查询链路信息。
  • Web UI组件:调用API 组件的接口并将信息显示到web 画面。

废话不多说,直接上代码。

一、Server端代码实现

先给出代码结构:

结构比较简单,搭建过程如下:

新建maven工程sleuth-zipkin

修改pom文件

<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>
  <groupId>com.sam</groupId>
  <artifactId>sleuth-zipkin</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
  </parent>
  
  <properties>
    <javaVersion>1.8</javaVersion>
  </properties>
  <!-- 使用dependencyManagement进行版本管理 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Camden.SR6</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
    
  </dependencyManagement>
  
  <dependencies>
    <!-- 引入zipkin-server依赖,提供server端功能 -->
    <dependency>
      <groupId>io.zipkin.java</groupId>
      <artifactId>zipkin-server</artifactId>
    </dependency>
    <!-- 引入zipkin-autoconfigure-ui依赖,用来提供zipkin web ui组件的功能,方便查看相关信息 -->
    <dependency>
      <groupId>io.zipkin.java</groupId>
      <artifactId>zipkin-autoconfigure-ui</artifactId>
    </dependency>
    
    <!-- 引入eureka依赖 -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    
  </dependencies>
</project>

新建启动类

/**
 * @EnableZipkinServer
 * 
 * 用于开启Zipkin Server功能
 *
 */
@EnableZipkinServer
@SpringBootApplication
@EnableDiscoveryClient
public class SleuthZipkinApp {

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

}

新建配置文件

server.port=9411
spring.application.name=sleuth-zipkin
#需要使用到eureka服务注册中心
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka

二、Client端代码实现

这里我们准备使用前面的随笔中已经实现好的微服务(网关服务api-gateway消费者hello-consumer和生产者hello-server,可以点击链接查看搭建过程,这里就不详细描述了)。在这几个微服务中都做如下修改:

引入依赖

<!-- 引入zipkin 依赖 ,提供zipkin客户端的功能 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

修改配置文件,追加两项配置#指定zipkin服务端的url

#指定zipkin服务端的url
spring.zipkin.base-url=http://localhost:9411
#设定样本收集的比率为100%
spring.sleuth.sampler.percentage=1.0

由于分布式系统的请求量一般比较大,不可能把所有的请求链路进行收集整理,因此sleuth采用抽样收集的方式,设定一个抽样百分比。在开发阶段,我们一般设定百分比为100%也就是1。

三、执行测试

依次启动微服务:服务注册中心eureka、zipkin服务端sleuth-zipkin、网关服务api-gateway、消费者hello-consumer和生产者hello-server

访问http://localhost:1111/,确认4个微服务已经成功注册到了服务注册中心

访问http://localhost:5555/hello-consumer/hello-consumer?accessToken=111,通过zuul 网关进行访问,

查看api-gateway控制台:

2018-07-19 18:02:34.999 INFO [api-gateway,4c384ab23da1ae35,4c384ab23da1ae35,true] 9296 --- [nio-5555-exec-3] com.sam.filter.AccessFilter : send GET request to http://localhost:5555/hello-consumer/hello-consumer

2018-07-19 18:02:45.088 INFO [api-gateway,,,] 9296 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration

请看红字部分,有4部分,以逗号分隔。

第一部分是服务名;

第二部分是TranceId,每次请求都会有唯一的tranceId;

第三部分是spanId,每个工作单元发送一次请求就会产生一个spanId,每个请求会产生一个tranceId和多个spanId,根据tranceId和spanId就能分析出一个完整的请求都经历了哪些服务单元;

第四部分是boolean型的,用来标记是否需要将该请求链路进行抽样收集发送到zipkin等进行整理。

访问zipkin服务端http://localhost:9411/,查看UI页面选择api-gateway,然后点击 "Find Trances"能看到请求都经历了哪些服务节点。再点相关link,可以查看调用顺序,并且还能看到在各个服务节点的处理的时间长度。切换到依赖画面,能查看服务节点的依赖关系

标签:01,服务,58,计理,spring,public,eureka,hello,cloud
来源: https://www.cnblogs.com/yyyyfly1/p/15980057.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有