SpringCloud微服务

什么是微服务

微服务就是不同的模块部署在不同的服务器上面,通过接口去访问就是微服务
作用:利用分布式解决网站高并发带来的问题,使得项目快速迭代升级,提高了项目的复用性。
缺点:运维需要的成本提高。

什么是集群

多台服务器部署相同应用构成一个集群
作用:通过负载均衡设备共同对外提供服务

什么是RPC?

RPC是一种面向接口的远程调用形式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。比如两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或者方法,由于不在一个内存空间,不能直接调用,这时候需要通过就可以应用RPC框架的实现来解决

有哪些微服务远程调用框架:WebService,Dubbo,SpringCloud

Restful,Soap,rpc

Restful:是一种架构设计风格,提供了设计原则和约束条件,而不是架构。而满足这些约束条件和原则的应用程序或设计就是 Restful架构或服务。主要是JSON实现(可以简单的理解restful就是Json)
Soap:了解过WebService的同学可能了解过soap,他是一种数据交换规范,是基于XML协议的一种简单的、轻量级的规范。请求包格式为Xml。
SOAP的消息是基于xml并封装成了符合http协议,因此,它符合任何路由器、 防火墙或代理服务器的要求。
soap可以使用任何语言来完成,只要发送正确的soap请求即可,基于soap的服务可以在任何平台无需修改即可正常使用。
Rpc:RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)
RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。

SOA和微服务的区别

SOA:面向服务(暴露接口),业务系统分解为多个组件,让每个组件都独立提供离散,自治,可复用的服务能力。通过服务的组合和编排来实现上层的业务流程。是一种面向服务的架构理念
微服务:是SOA的升华版本,多模块之间采用RPC远程调用技术。架构设计概念,各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它特性(单一职责,边界,异步通信,独立部署)是分布式概念的跟严格执行。

SpringCloud

SpringCloud为我们开发人员提供了快速构建分布式的一套工具,包括配置管理,服务发现(Eureka注册中心),服务消费(ribbon和Feign),路由(负载均衡),断路器。。。。一套完整的分布式微服务的治理框架。SpringCloud完全使用SpringBoot构建项目。

服务提供者+消费者

服务提供者:就是提供服务被别人调用
消费者:调用别人暴露出来的接口
(举例生产者和消费者关系)

SpringCloud注册中心(Eureka)

注册中心的作用就是用来方便接口暴露出来的一个管理工具,如果所有的接口只是暴露出来,没有一个统一的管理工具,又有谁知道你暴露了那些接口了,其他的开发人员怎么知道我们暴露了哪些接口出来呢。eureka是一个SpringCloud的服务注册和发现模块。
这里我们首先去搭建我们的注册中心项目。

创建EurekaServer项目

采用Maven Project

第一步:创建一个空的Maven项目springcloud,然后在springcloud项目创建新的Module-springboot项目,方便管理微服务。

第二步: 创建注册中心

  1. 选择依赖:
    在这里插入图片描述

结果:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.entor</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-server</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

注册中心的配置文件

server:
  port: 9001
eureka:
  instance:
    #实例名称
    appname: localhost
  client:
    #是否把当前服务注册到eureka注册中心,默认为true,因为本身就是注册中心,所以设置为false
    register-with-eureka: false
    #是否从eureka注册中心获取已注册的服务信息,默认是true
    fetch-registry: false
    service-url:
      #注册中心的注册地址,提供给其他微服务注册的地址
      defaultZone: http://${eureka.instance.appname}:${server.port}/eureka
  server:
    #注册中心在5秒内没有收到服务发生过来的心跳,则认为该服务不可用,踢出该服务
    eviction-interval-timer-in-ms: 5000
    #关闭注册中心保护因网络不稳定暂时无法连接的服务不被踢出,默认是开启(建议:生产环境中开启,开发测试关闭)
    enable-self-preservation: false

接下来启动注册中心就可以了

@SpringBootApplication
@EnableEurekaServer//开启注册中心
public class EurekaServerApplication {

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

启动成功
在这里插入图片描述

创建EurekaProvider项目

服务提供者就是提供一个服务暴露出来给别人调用,在SpringCloud中我们需要注册服务到我们的注册中心。

第一步: 在springcloud项目下创建一个新的Module,springboot项目
第二步: 引入依赖
在这里插入图片描述
结果

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.entor</groupId>
    <artifactId>eureka-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-provider</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

第三步: 服务提供者的配置文件

server:
  port: 9101
spring:
  application:
    #应用名称,区分不同的应用
    name: eureka-provider
eureka:
  client:
    service-url:
      #服务注册的地址,eureka注册中心的地址
      defaultZone: http://localhost:9001/eureka
    #每隔5秒去注册中心拉取服务列表信息到本地
    registry-fetch-interval-seconds: 5
  instance:
    #服务重新续约持续的时间(该服务每隔5(默认30秒)秒向注册中心发生心跳,表明该服务还能够正常连接还能够提供服务)
    lease-renewal-interval-in-seconds: 5
    #注册中心每隔15秒向服务发生心跳,检测服务是否正常
    lease-expiration-duration-in-seconds: 15
    #注册中心显示服务的ip地址,默认是显示计算机名称
    prefer-ip-address: true
    #注册中心显示的服务实例名称
    instance-id: ${spring.application.name}-${spring.port}

第四步: 服务提供者对外提供的接口(控制层)

@RestController
public class HelloController {
    @Value("${server.port}")
    private String port;
    @RequestMapping(value = "hello")
    public String hello(String name){
        return "当前端口号:"+port+",hello "+name;
    }
}

第五步: 开启注册中心客户端

@SpringBootApplication
@EnableEurekaClient
public class EurekaProviderApplication {

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

接下来启动两次该服务提供者
在这里插入图片描述
第一次启动端口号为9101,第二次启动时修改为9102

刷新注册中心,发现两个服务提供者已经注册进来了。
在这里插入图片描述

服务消费者创建(Ribbon)

消费者其实和服务提供者是一个性质,都是可以提供服务和进行消费的。本次分开主要是体现一个服务调用另一个服务接口的方式。

在微服务分布式中,业务都会被拆分成一个独立的服务,并且在多个服务器上部署相同的服务。服务与服务的通讯是基于http restful的。SpringCloud我们会使用到两种消费工具,Ribbon+Feign。
Ribbon实现了服务的负载均衡,防止服务超时的雪崩效应。
Feign默认集成了Ribbon。
所以一般情况下我们使用Feign作为消费端,本次使用Ribbon。

我们这里去启动两个服务提供者,将提供者端口9101和9102分别启动,可以在9001 Eureka服务查看到两个服务,方便我们看到Ribbon实现负载均衡

第一步: 在springboot的下创建Module-springboot
第二步: 引入依赖
在这里插入图片描述
结果

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.entor</groupId>
    <artifactId>eureka-customer-ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-customer-ribbon</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <!-- 手动添加的断路器 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

第三步: 消费者配置文件
与服务提供者配置方式一样

server:
  port: 9201
spring:
  application:
    name: eureka-customer-ribbon
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9001/eureka
    registry-fetch-interval-seconds: 5
  instance:
    prefer-ip-address: true
    instance-id: eureka-customer-ribbon-9201
    lease-renewal-interval-in-seconds: 5
    lease-expiration-duration-in-seconds: 15

第四步:创建消费者(控制层)

@RestController
public class HelloController {
    @Autowired
    private HelloService helloServicel;

    @RequestMapping("/hello")
    //断路器,如果访问的服务器发生异常,则调用断路器指定的回调方法
    @HystrixCommand(fallbackMethod = "helloError")//需要添加断路器依赖
    public String hello(String name) {
        return helloServicel.hello(name);
    }

    public String helloError(String name){
        return "您好:"+name+"服务器异常";
    }
}

第五步:服务层
先注册一个工具类

@SpringBootApplication
@EnableDiscoveryClient//服务发现客户端(调用服务需要这个)
@EnableHystrix//开启断路器
public class EurekaCustomerRibbonApplication {

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

    @Bean
    @LoadBalanced//装配RestTemplate类开启负载均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

服务层

@Service
public class HelloServiceImpl implements HelloService {
    @Autowired
    private RestTemplate restTemplate;

    @Override
    public String hello(String name) {
        // 使用restTemplate工具类,restful方式调用
        // eureka-provider是服务在注册中心的名称
        // 该名称下可以有多台服务器实例,实现负载均衡访问
        return restTemplate.getForObject("http://eureka-provider/hello?name=" + name, String.class);
    }
}

启动该类就可以调用服务的接口,并且Ribbon还能帮我们实现负载均衡。
在这里插入图片描述
接口调用
在这里插入图片描述

服务消费者(Feign)

使用Ribbon还是比较繁琐,我们有更简单的方式去实现服务调用,就是我们的Feign调用工具。

继续创建新的Module-springboot
第一步导入依赖

在这里插入图片描述
结果

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.entor</groupId>
    <artifactId>eureka-customer-feign</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-customer-feign</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

配置文件
编写我们的配置文件application.yml,配置端口9301,并且将服务注册到Eureka

server:
  port: 9301
spring:
  application:
    name: eureka-customer-feign
eureka:
  client:
    registry-fetch-interval-seconds: 5
    service-url:
      defaultZone: http://localhost:9001/eureka
  instance:
    instance-id: ${spring.application.name}-${server.port}
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 5
    lease-expiration-duration-in-seconds: 15
feign:
  hystrix:
    enabled: true

创建消费端代码
控制层

@RestController
public class HelloController {
    @Autowired
    private HelloService helloService;

    @RequestMapping(value = "/hello")
    public String hello(String name) {
        return helloService.hello(name);
    }
}

服务层接口

/**
 * value:本接口所有方法都会调用注册中心名为eureka-provider的服务
 * fallback:断路器发生时,调用该实现类对应的方法
 */
@FeignClient(value = "eureka-provider",fallback = HelloServiceImpl.class)
public interface HelloService {

    /**
     * 调用注册中心名为eureka-provider的服务的请求地址为 “/hello” 的接口
     * 传递参数需要在参数前面加@RequestParam
     */
    @RequestMapping(value = "/hello")
    public String hello(@RequestParam("name") String name);
}

服务层实现

@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String hello(String name) {
        return "Hello:"+name+",请求无法送达";
    }
}

启动类

@SpringBootApplication
@EnableDiscoveryClient//使得消费者发现服务
@EnableFeignClients//开启Feign客户端调用
public class EurekaCustomerFeignApplication {

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

}

断路器

雪崩效应
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
在这里插入图片描述
较底层的服务如果出现故障,会导致连锁故障。导致断路器响应
在这里插入图片描述
断路器就是故障转移(将当前出现故障的请求重新返回特定消息)
Ribbon使用断路器
改造Eureka-ribbon 工程的代码,首先在pom.xml文件中加入spring-cloud-starter-netflix-hystri的依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

然后在我们的启动类上加上开启Hystrix的注解@EnableHystrix

然后改造我们的Service层

@Service
public class HelloService {
	@Autowired
	private RestTemplate template;

	@HystrixCommand(fallbackMethod = "helloError")
	public String helloService(String name) {
		return template.getForObject("http://eureka-provider/hello?name=" + name, String.class);
	}

	public String helloError(String name) {
		return "Hello," + name + ",服务器发生异常!!!";
	}
}

这样我们就实现了在Ribbon下使用断路器。
我们只需要先正常启动EurekaServer,然后启动生产者,并且启动我们的Ribbon消费者服务,通过http://localhost:9202/hello?name=张三 访问我们的服务
浏览器打印:Hello 张三,From Port:9101
当我们关闭生产者打印:Hello,张三,服务器发生异常!!!

这里就证明我们在Ribbon下使用断路器成功。
Feign使用断路器
Feign是自带断路器的,但是有些版本没有自动打开,我们这里需要在配置文件中配置开启断路器。配置如下

feign:
hystrix:
enabled:true

然后对我们的RurekaFeign的接口类进行改造,直接在注解上加上我们的fallback指定回调的类就行了。代码如下

@FeignClient(value = "eureka-prodiver", fallback = HelloServiceImpl.class)
public interface HelloService {
	@RequestMapping("/hello")
	public String hello(@RequestParam(value = "name") String name);
}

然后写一个我们的接口的实现类HelloServiceImpl实现我们的hello方法,并且注入到容器中。

@Component
public class HelloServiceImpl implements HelloService {
	@Override
	public String hello(String name) {
		return "Hello " + name + ",服务器发生异常!!!";
	}
}

然后同样和Ribbon那样操作,先让生产者正常启动访问,然后关闭生产者访问。
结果和Ribbon结果想过,则证明断路器起作用了。
在这里插入图片描述

ZUUL路由网关

介绍zuul
在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。
在这里插入图片描述
一般情况下,用户访问首先通过负载均衡,然后到达我们的路由配置,最后才访问到我们的具体服务。
zuul的作用是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/order转发到到order服务。
搭建zuul环境

依赖
在这里插入图片描述
结果

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.entor</groupId>
    <artifactId>eureka-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-zuul</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--请求重试的依赖-->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

配置文件

server:
  port: 9402
spring:
  application:
    name: eureka-zuul
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9001/eureka
    registry-fetch-interval-seconds: 5
  instance:
    lease-expiration-duration-in-seconds: 15
    lease-renewal-interval-in-seconds: 5
    prefer-ip-address: true
    instance-id: ${spring.application.name}-${server.port}


zuul:
  retryable: true #请求重试
  TestFilter: #过滤器的类名
    pre: #过滤器类型
      disable: true #过滤器是否禁用
  routes:
    #路由分发
    eureka-provider: /c/** #配置路由信息,请求地址/a/**,对应的服务名称eureka-customer-ribbon(自带负载均衡)

    a:
      path: /a/**
      serviceId: eureka-customer-ribbon #配置路由信息,请求地址/a/**,对应的服务名称eureka-customer-ribbon

    b:
      path: /b/**
      serviceId: eureka-customer-feign

    d:
      path: /d/**
      url: forward:/hello #本地地址跳转/hello

    baidu:
      path: /baidu/**
      url: http://www.baidu.com #本地地址跳转到百度

创建我们的启动类,注意需要添加开启zuul网关

@SpringBootApplication
@EnableZuulProxy//开启网关代理
@EnableEurekaClient//开启客户端
public class EurekaZuulApplication {

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

启动访问即可

扩展网关的过滤器

前置过滤器

@Component
public class TestFilter extends ZuulFilter {
    @Value("${server.port}")
    private String port;
    /**
     * 而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。
     * 1)pre
     * 可以在请求被路由之前调用。适用于身份认证的场景,认证通过后再继续执行下面的流程。
     * 2)route
     * 在路由请求时被调用。适用于灰度发布场景,在将要路由的时候可以做一些自定义的逻辑。
     * 3)post
     * 在 route 和 error 过滤器之后被调用。这种过滤器将请求路由到达具体的服务之后执行。适用于需要添加响应头,记录响应日志等应用场景。
     * 4)error
     * 处理请求时发生错误时被调用。在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息。
     */
    @Override
    public String filterType() {
        //过滤类型
        return "pre";
    }

    /**
     * 过滤器的执行顺序,数值越小,优先级越高。
     */

    @Override
    public int filterOrder() {
        //过滤顺序
        return 0;
    }

    /**
     * 是否执行过滤器
     */
    @Override
    public boolean shouldFilter() {
        //
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.set("name","张三");
        return true;
    }

    /**
     * 执行自己的业务逻辑
     */
    @Override
    public Object run() throws ZuulException {
        //过滤方式
        System.out.println("服务器端口:"+port+",执行run");
//        System.out.println(1/0);
        return null;
    }
}

异常过滤器

@Component
public class ErrorFilter extends ZuulFilter {
    /**
     * 异常过滤器类型,只有当其他类型的过滤器在过滤时发生异常,才会执行该过滤器
     * @return
     */
    @Override
    public String filterType() {
        return "error";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        System.out.println("其他过滤器在执行过滤服务时发生异常");
        return null;
    }
}

IP地址过滤器

@Component
public class IpFilter extends ZuulFilter {

    // IP黑名单列表
    private List<String> blackIpList = Arrays.asList("127.0.0.1");

    public IpFilter() {
        super();
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        String ip = IPUtil.getClientIp(ctx.getRequest());
        System.out.println(ip);
        System.out.println("获取前面过滤器的上下文设置的name键值:"+ctx.get("name"));
        HttpServletRequest request = ctx.getRequest();
        // 在黑名单中禁用
        if (StringUtils.isNotBlank(ip) && blackIpList.contains(ip)) {
            //告诉Zuul不需要将当前请求转发到其他服务
            ctx.setSendZuulResponse(false);
            //是用来拦截本地转发请求的,当我们配置了 forward:/local 的路由,
            // ctx.setSendZuulResponse(false) 对 forward 是不起作用的,
            ctx.set("sendForwardFilter.ran",true);
            ctx.setResponseBody("{\"code\":-1,\"msg\":\"黑户\"}");
            ctx.getResponse().setContentType("application/json; charset=utf-8");
            return null;
        }
        return null;
    }
}

获取IP地址工具类

public class IPUtil {
    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("http_client_ip");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        // 如果是多级代理,那么取第一个ip为客户ip
        if (ip != null && ip.indexOf(",") != -1) {
            ip = ip.substring(ip.lastIndexOf(",") + 1, ip.length()).trim();
        }
        return ip;
    }
}

请求回退

@Component
public class ZuulFailBackProvider implements FallbackProvider {
    /**
     * 指定需要回退的服务名称,当用户访问该服务的时候,如果服务不可达,触发回退机制。
     * *号指注册在eureka上的所有服务
     */
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return this.getStatusCode().value();
            }

            @Override
            public String getStatusText() throws IOException {
                return this.getStatusCode().getReasonPhrase();
            }

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws IOException {
                String jsno = "{\"code\":404,\"msg\":\"系统出错,请求不可达\"}";
                return new ByteArrayInputStream(jsno.getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
                headers.setContentType(mt);
                return headers;
            }
        };
    }
}

Nginx调用网关

第一步配置好要调用的网关
启动好两个王关,地址分别为9401和9402
在这里插入图片描述

#配置上游服务器网关端口集群
	upstream  backServer{
	    server 127.0.0.1:9401 weight=1;
	    server 127.0.0.1:9402 weight=1;
	}

### 指定上游服务器负载均衡服务器
	 proxy_pass http://backServer/;

点击nginx.exe启动Niginx到浏览器输入:localhost:9400
关闭服务:
新建一个bat文件与nginx.exe在同一目录

taskkill /im nginx.exe /f

Eureka注册中心开启密码认证

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

编写配置类

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 关闭csrf
        http.csrf().disable();
        // 支持httpBasic
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }
}

修改注册地址
其他服务注册时也需要加上admin:admin@

defaultZone: http://admin:admin@localhost:9001/eureka/

Spring Cloud使用Eureka集群搭建实现高可用服务注册中心

第一步:
在C:\Windows\System32\drivers\etc\hosts文件添加下面三行内容,把本机ip映射成3个域

127.0.0.1 www.master.com
127.0.0.1 www.slave1.com
127.0.0.1 www.slave2.com

第二部修改注册中心配置文件

在这里插入图片描述
application.yml

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
  server:
    eviction-interval-timer-in-ms: 5000
    enable-self-preservation: false
spring:
  security:
    user:
      name: admin
      password: admin
  profiles:
    active: slave1

application-master.yml

server:
  port: 9001
eureka:
  instance:
    appname: www.master.com
  client:
    service-url:
      #本机服务向其他两台eureka服务注册,相互同步注册信息
      defaultZone: http://admin:admin@www.slave1.com:9002/eureka/, http://admin:admin@www.slave2.com:9003/eureka/

application-slave1.yml

server:
  port: 9002
eureka:
  instance:
    appname: www.slave1.com
  client:
    service-url:
      defaultZone: http://admin:admin@www.master.com:9001/eureka/,http://admin:admin@www.slave2.com:9003/eureka/

application-slave-2.yml

server:
  port: 9003
eureka:
  instance:
    appname: www.slave2.com
  client:
    service-url:
      defaultZone: http://admin:admin@www.master.com:9001/eureka/,http://admin:admin@www.slave1.com:9002/eureka/

接下来启动注册中心分别修改application.yml的加载文件就可以了。
这时启动一个服务提供者就会发现三个注册中心都注册了该服务提供者

服务提供者注册
defaultZone: http://admin:admin@www.master.com:9001/eureka/

结语

Spring Boot做为下一代 web 框架,Spring Cloud 作为最新最火的微服务的翘楚,你还有什么理由拒绝。赶快上船吧,老船长带你飞。终章不是最后一篇,它是一个汇总,未来还会写很多篇。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值