天天看点

SpringCloud(二)

SpringCloud第二天

内容回顾

(1)系统架构的演变过程

单体架构

垂直架构

分布式之RPC架构

分布式之SOA架构

分布式之微服务架构

(2)微服务概念理解

微服务: 虽小五脏俱全。

开发项目所用到的所有内容【前端、后台】,微服务里面都包含。

基于SpringBoot来构建的。

(3)微服务与微服务之间肯定要进行系统调用

RPC 【dubbo Socket /ServerSocket】

HTTP【微服务采用 HttpClient/OkHttp/RestTemplate】

(4)SpringCloud就是用来治理微服务。组件来决定。

SpringCloud不是一个具体的框架而是很多组件的一个集合。组件就是用来治理微服务的。

Spring / SpringBoot /SpringCloud

(5)Eureka

EurekaServer

(1)管理微服务的列表 (2)通过心跳检测管理微服务的状态

EurekaClient

(1)注册 (2)发现

步骤:

EurekaServer:
(1)创建一个Springboot的项目 
		<parent>   spring-boot-starter-parent
		引导 : @SpringBootApplication
		application.yml
(2)pom.xml
spring-cloud-starter-netflix-eureka-server
(3)引导类上
@EnableEurekaServer
(4)application.yml
eureka:
client:
  service-url:
    defaultZone: http://localhost:10086/eureka
  fetch-registry: false
  register-with-eureka: false
instance:
  prefer-ip-address: true
  ip-address: 127.0.0.1
EurekaClient:
(1)创建一个Springboot的项目 
		<parent>   spring-boot-starter-parent
		引导 : @SpringBootApplication
		application.yml
(2)pom.xml
spring-cloud-starter-netflix-eureka-client
(3)引导类上
@EnableDiscoveryClient
(4)application.yml
eureka:
client:
  service-url:
    defaultZone: http://localhost:10086/eureka
  fetch-registry: true
  register-with-eureka: true
instance:
  prefer-ip-address: true
  ip-address: 127.0.0.1
           

Eureka的高可用环境搭建。

执行流程:

服务消费方根据EurekaServer上注册的服务名称ServerName,根据ServerName找到服务提供者的服务列表,找到服务列表后,从服务列表中获取一个具体的服务对应的IP和端口,然后进行远程调用。

Ribbon

实现就是负载均衡,以轮询/随机的方式从服务列表中获取一个具体的服务IP和端口

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

Hystrix熔断保护(***)

Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加速 失败判定时间。

用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超 时,则会进行降级处理,什么是服务降级?

服务降级:优先保证核心服务,而非核心服务不可用或弱可用。

用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友 好的提示信息) 。

服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服 务没有响应。

触发Hystrix服务降级的情况:

线程池已满

请求超时

雪崩问题

(1)线程隔离

为每一个微服务设定一个线程池,在线程池中指定一定的线程数。当用户的请求过来以后,先从线程池中获取线程,如果能获取到继续访问,如果获取不到,判断用户此次请求失败,一旦判断请求失败,直接走降级服务。目的:加速访问失败的判定。

(2)服务降级
	服务的消费方进行编写。
》pom.xml
spring-cloud-starter-netflix-hystrix

》引导类上
@EnableCircuitBreaker  熔断保护 hystrix单独使用

@SpringCloudApplication 
集成了
@SpringBootApplication springboot启动注解
@EnableDiscoveryClient eureka客户端
@EnableCircuitBreaker 熔断保护 hystrix
           

》降级服务

类上:类中的所有方法可以使用

在类上添加

@DefaultProperties(defaultFallback=“降级服务方法”)

在方法上添加

@HystrixCommand​

SpringCloud(二)

方法上:只对当前方法有效

@HystrixCommand(fallback=“methodName”)

原服务方法和降级服务方法的[参数]和[返回值]一致即可。

SpringCloud(二)

(3)熔断保护

熔断保护原理

SpringCloud(二)

closed : 默认情况下,熔断包含关闭状态,服务正常访问远程服务。

open: 10秒访问20次有50%失败,熔断保护的状态就会被打开,一旦被打开,访问的时候直接走的是降级服务。

half Open: 状态为打开后,过5秒,熔断保护的状态会更改为half open,允许部分请求走原来的远程服务调用,调用成功,则将状态改为closed。如果调用失败继续保持为half open,下个5秒后继续进行该操作。

Feign的学习(*******)

微服务与微服务之间的调用。

RestTemplate(HttpClient/OkHttp)
			restTemplate.getForObject("http://localhost:9091/user/findById/8",返回类.class)
	Ribbon
				restTemplate.getForObject("http://server_name/user/findById/8",返回类.class)
(1)server_name
(2)url   [/user/findById]
(3)请求的参数
(4)请求的返回值
更想要的效果是像调用本地方法一样调用远程服务。
(1)pom.xml
spring-cloud-starter-openfeign
(2)引导类上
@EnableFeignClients
(3)编写一个接口
@FeignClient("??")  //server_name
public interface FeignClient{

	@RequestMapping("/.....")  //url
	public xx methodName(....); //....  请求的参数  
			//xx方法的放回值  请求的返回值
}

(4)需要远程调用的类中
@Autowired
private FeignClient feignClient;

xx = feignClient.method(...);
           

Feign底层是使用的是Ribbon+RestTemplate+动态代理实现远程调用。

Feign解决的问题是:微服务与微服务之间的系统调用

Feign支持Hystrix

feign:

hystrix:

enable: true

Gateway学习(*******)

SpringCloud(二)
SpringCloud(二)
SpringCloud(二)

(1)过滤拦截

服务鉴权(判断用户是否已经登录、判断用户是否具有访问某一个资源的权限)

日志记录

(2) 路由

建立的是一种对应关系

SpringMVC: 用户发送请求的URL和handler之间的关系 @RequestMapping

RabbitMQ: 交换机和队列之间的关系,routingKey

路由:用户发送请求的URL和访问的微服务之间的关系 ?

使用Gateway

pom.xml
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-gateway

引导类
@EnableDiscoveryClient

application.yml
spring:
  application:
    name: heima-gateway
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: http://127.0.0.1:9092
          predicates:
            - Path=/user/**
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka  #EurekaServer的地址
    register-with-eureka: true
    fetch-registry: true

           

访问微服务的时候,未实现负载均衡的功能

路由配置中uri所用的协议为lb时(以uri: lb://user-service为例),gateway将使用 LoadBalancerClient把 user-service通过eureka解析为实际的主机和端口,并进行ribbon负载均衡。

spring:
  application:
    name: heima-gateway
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: lb://server_name   #@LoadBalanced
          predicates:
            - Path=/user/**

           

添加和去除一层访问地址的配置方式

  • id: user-service-route2

    uri: lb://user-service

    predicates:

    - Path=/api/user/**

    filters:

    - StripPrefix=1 #去除一层

    - My=name,age,gender

    • id: user-service-route3

      uri: lb://user-service

      predicates:

      • Path=/**

        filters:

      • PrefixPath=/user #添加一层
SpringCloud(二)
SpringCloud(二)

过滤器

Web中的过滤器

(1)编写一个过滤器

public class MyFilter implements Filter{
	init
	destroy
	doFilter(request,reponse,filterChain){
			
			//拦截前的业务操作1
			filterChain.doFilter(request,reponse);
			//拦截后的业务操作1
		
	}
}
public class MyFilter implements Filter{
	init
	destroy
	doFilter(request,reponse,filterChain){
			
			//拦截前的业务操作2
			filterChain.doFilter(request,reponse);
			//拦截后的业务操作2
		
	}
}
//拦截前的业务操作1
/拦截前的业务操作2
//拦截后的业务操作2
//拦截后的业务操作1
           

(2)配置过滤器

web.xml  <filter> <filter-name> <filter-class>  。。。。。
           
SpringCloud(二)

常见的应用场景如下:

请求鉴权:一般 GatewayFilterChain 执行filter方法前,如果发现没有访问权限,直接就返回空。

异常处理:一般 GatewayFilterChain 执行filter方法后,记录异常并返回。

服务调用时长统计: GatewayFilterChain 执行filter方法前后根据时间统计。

局部过滤器 [某一些路由有效]

(1)GatewayFilterFactory
@Component
public class MyFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<MyFilterGatewayFilterFactory.MyConfig> {

    /**
     * 构造函数,自己编写的MyCOnfig.class告诉给父类
     */
    public MyFilterGatewayFilterFactory(){
        super(MyConfig.class);
    }
    /**
     * 设定参数的顺序
     * param1:JERRY
     * param2:laoshu
     * param3:200
     * @return
     */
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param1","param2","param3");
    }
    /**
     * 过滤拦截方法
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(MyConfig config) {
        return (exchange, chain) -> {

            //System.out.printf("=====>%s是一个%s,今年%s岁",config.param3,config.param1,config.param2);
            ServerHttpRequest request = exchange.getRequest();//请求数据
            MultiValueMap<String, String> queryParams = request.getQueryParams();//请求数据
            if(queryParams.containsKey("name")){
                List<String> valueList = queryParams.get("name");
                String nameValue = valueList.get(0);
                System.out.println("name对应的值是:"+nameValue);
            }
            if(queryParams.containsKey("age")){
                List<String> valueList = queryParams.get("age");
                String ageValue = valueList.get(0);
                System.out.println("age对应的值是:"+ageValue);
            }
            if(queryParams.containsKey("gender")){
                List<String> valueList = queryParams.get("gender");
                String genderValue = valueList.get(0);
                System.out.println("gender对应的值是:"+genderValue);
            }
            System.out.println();
            return chain.filter(exchange); // doFilter 放行
        };
    }

    /*
    用来接收过滤器配置传递的参数
     */
    public static class MyConfig {
        private String param1;
        private String param2;
        private String param3;

        public String getParam1() {
            return param1;
        }

        public void setParam1(String param1) {
            this.param1 = param1;
        }

        public String getParam2() {
            return param2;
        }

        public void setParam2(String param2) {
            this.param2 = param2;
        }

        public String getParam3() {
            return param3;
        }

        public void setParam3(String param3) {
            this.param3 = param3;
        }
    }
}

通过过滤器获取请求路径中的请求参数,并进行相应的操作

全局过滤器[所有的请求有效]
@Component
public class MyGlobalFilter1 implements GlobalFilter,Ordered{ //implemnts Filter

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//doFilter方法

        System.out.println("MyGlobalFilter1全局过滤器执行了");
        return chain.filter(exchange);//filterChain.doFiler()放行
    }

    @Override
    /**
     * 返回值决定了过滤器的执行顺序,值越小越先执行
     */
    public int getOrder() {
        return 4;
    }
}

@Component
public class MyGlobalFilter2 implements GlobalFilter,Ordered{


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("MyGlobalFilter2全局过滤器执行了");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
           

补充:跨域问题。

浏览器的同源策略:

当请求的协议、主机、PORT,只要有一个不一样,违法了浏览器的同源策略。

http://localhost:8080/user/findById
http://192.168.200.131:8080/user/findById   不满足
http://localhost:8080/user/findById
https://localhost:8080/user/findById  不满足
http://localhost:8080/user/findById
http://localhost:8081/user/findById  不满足
http://localhost:80/user/findById
http://localhost/user/findById  满足
http://localhost:8080/user/findById
http://localhost:8080/teacher/findById 满足
           

两台服务器,A和B,当从A服务器的页面发送异步请求请求B服务器的后台数据,如果服务器A和B不满足浏览器的同源策略,就会产生跨域问题。

跨域是浏览器报的错误,请求能正确发送也能正确返回,但是浏览器不会去解析返回的数据,并且报了上述错误,这就是我们说的跨域问题。

spring:

application:

name: heima-gateway

cloud:

gateway:

routes:

- id: user-service-route

uri: http://127.0.0.1:9092

predicates:

- Path=/user/**

- id: user-service-route2

uri: lb://user-service

predicates:

- Path=/api/user/**

filters:

- StripPrefix=1 #去除一层

- MyFilter=name,age,gender

- id: user-service-route3

uri: lb://user-service

predicates:

- Path=/**

filters:

- PrefixPath=/user #添加一层

globalcors:

cors-configurations:

‘[/**]’: #所有请求

allowedOrigins: “*” #服务器允许哪些服务地址进行跨域访问

allowedMethods: #允许跨域的请求方式

- GET

- POST

- PUT

- DELETE

发生跨域问题时,可以配置允许哪些服务地址通过某种方式进行跨域访问

globalcors:

cors-configurations:

‘[/**]’: #所有请求

allowedOrigins: “*” #服务器允许哪些服务地址进行跨域访问

allowedMethods: #允许跨域的请求方式

- GET

- POST

- PUT

- DELETE

Config学习

作用:

SpringCloud(二)

实现:

(1)使用码云创建GIT远程仓库

(2)在GIT仓库中创建配置文件

xxxx-yyyy.yml

xxxx:应用名称

yyyy:环境名称 dev 开发环境 test 测试环境 pro 生产环境

(3)搭建配置中心微服务

》创建Springboot项目

》pom.xml添加依赖

<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-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

》启动引导类
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }
}

》application.yml
server:
  port: 12000
spring:
  application:
    name: config-springcloud
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/lgang1217/git_cloud.git  #存放配置文件的GIT地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

》启动测试
微服务中
(1)pom.xml
<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-config</artifactId>
        </dependency>

(2)引导类中添加
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }
}

(3)application.yml删除
(4)bootstrap.yml
xxxx-yyyy.yml
spring:
  cloud:
    config:
      name: xxxx
      profile: yyyy
      label: master
      discovery:
        service-id: config-springcloud
        enabled: true
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka


(5)启动
           

总结

SpringCloud

创建一个springboot项目

pom.xml添加起步依赖

引导类上添加一些注解

application.yml添加配置

Eureka[必须]
	注册中心
	EurekaServer
		(1)记录服务提供者的服务列表
		(2)通过心跳检测管理服务的状态
	EurekaClient
		(1)注册
		(2)发现
	
	步骤:
		EurekaServer
			spring-cloud-starter-netflix-eureka-server
			@EnableEurekaServer
			eureka:
			  client:
				service-url:
				  defaultZone: http://127.0.0.1:10086/eureka
				register-with-eureka: false
				fetch-registry: false
		EurekaClient
			spring-cloud-starter-netflix-eureka-client
			@EnableDiscoveryClient
			eureka:
			  client:
				service-url:
				  defaultZone: http://127.0.0.1:10086/eureka
				register-with-eureka: true
				fetch-registry: true
				
		通过RestTemplate是可以实现远程调用,但是未能实现负载均衡
Ribbon[服务的消费方][次次之]
	
	@LoadBalanced
	restTemplate.getForObject("http://service_name/...",返回值类型.class);

Hystrix[服务的消费方][次之]
	由于某个微服务请求失败导致整体出现雪崩问题。
	
	(1)线程隔离
		加速用户请求的失败判定,如果判定失败,则走降级服务
	(2)熔断保护
		closed    10秒20次请求50%失败      open      5秒          half open
	(3)服务降级
		spring-cloud-starter-netflix-hystrix
		
		@EnableCircuitBreaker  / @SpringCloudApplication
		
		
		方法
			@HystrixCommand(fallback="methodName")
			降级服务和原服务对应的参数和返回值一定要保持一致
			
		类上
			@DefaultProperties(defaultFallback="methodName")
			@HystrixCommand
			

Feign[必须]
	实现的微服务与微服务之间的系统调用。对Ribbon+RestTemplate通过动态代理进行一层封装。
	pom.xml
		spring-cloud-starter-openfeign
	引导类上添加一些注解
		@FeignClients
	编写一个Feign的接口
		@FeignCient("server_name")
		接口中的方法参考服务提供方Controller方法进行编写
		@RequestMapping("")
		public Xxx methodName(...);
	需要远程调用的地方
		@Autowired
		通过接口调用方法的方式来完成远程调用。

	(1)Feign对Ribbon+RestTemplate通过动态代理进行一层封装。
	(2)Feign支持Hystrix的使用
			》application.yml开启服务
			feign:
			  hystrix:
				enable:
				  true

			》编写一个实现类实现FeignClient接口
				public class Xxx implements FeignClient{}
			》@FeignClient指定使用实现类
				@FeignClient(value = "server_name",fallback = Xxx.class)
	(3)微服务与微服务之间的系统调用
Gateway[必须]
	(1) 过滤拦截
	(2) 路由:分发请求
	pom.xml
		spring-cloud-starter-netflix-eureka-client
		spring-cloud-starter-gateway
	引导类上添加一些注解
		@EnableDiscoveryClient
	application.yml
	spring:
	  cloud:
		gateway:
		  routes:
			- id: user-service-route2
			  uri: lb://user-service
			  predicates:
				- Path=/api/user/**
			  filters:
				- PrefixPath=/user  加
				- StripPrefix=1     减
	(3)过滤器
	局部过滤器
		自定义:GatewayFilerFactory /  AbstractGatewayFilterFactory
		内置的局部过滤器
	全局过滤器
		GlobalFilter,Ordred
		
	(4)跨域问题


Config[整理了解]
	管理所有微服务的配置文件
Bus[整理了解]
           

继续阅读