读书笔记-springcloud微服务进阶

  • 云原生
  • SpringCloud特性
    1. Bootstrap上下文
      Bootstrap上下文中的属性优先级较高,所以它们不能被本地配置所覆盖
      2.应用上下文层级
      子级上下文将从父级中继承属性源和配置文件, 来自子级上下文的属性可以覆盖父级中的具有相同名称和属性源名称的属性
      3.修改Bootstrap配置文件的位置
      bootstrap.yml的位置可以通过在配置属性中设置spring.cloud.bootstrap.name(默认是bootstrap)或者spring.cloud.bootstrap.location来修改
      4.重载远程属性

    2. 刷新范围
      一个被标记为@RefreshScope的Spring Bean实例在配置发生变更时可以重新进行初始化,即动态刷新配置,这是为了解决状态Bean实例只能在初始化的时候才能进行属性注入的问题。

  • Spring Cloud Commons:公共抽象
    Spring Cloud将服务发现、负载均衡和断路器等通用模型封装在一个公共抽象中,可以被所有的Spring Cloud客户端使用,不依赖于具体的实现(例如服务发现就有Eureka和Consul等不同的实现),这些公共抽象位于Spring Cloud Commons项目中。
    1. @EnableDiscoveryClient注解
      @EnableDiscoveryClient注解用于在META-INF/spring.factories文件中查找DiscoveryClient(DiscoveryClient为服务发现功能抽象类)的实现。spring.factories文件的org.springframework. cloud.client.discovery.EnableDiscoveryClient配置项可以指定DiscoveryClient的实现类。DiscoveryClient目前的实现有Spring Cloud Netflix Eureka、SpringCloud Consul Discovery和Spring Cloud Zookeeper Discovery。
      DiscoveryClient的实现类会自动将本地的Spring Boot服务注册到远程服务发现中心。可以通过在@EnableDiscoveryClient中设置autoRegister=false来禁止自动注册行为。在Finchley版本的Spring Cloud中,不需要显式使用@EnableDiscoveryClient来开启客户端的服务注册与发现功能。只要在类路径中,有DiscoveryClient的实现就能使Spring Cloud应用注册到服务发现中心。
  • SpringMVC中使用了3个注解作用于Chapter3BootDemoApplication类,分别是@Configuration(2.0.0版本中添加了@SpringBootConfiguration注解来代替Spring的标准配置注解@Configuration)、@EnableAutoConfiguration和@ComponentScan。SpringBoot提供了一个统一的注解@SpringBootApplication,默认属性下等于上述3个注解。
  • @RestController组合了@Controller和@ResponseBody注解,表明该类可以处理HTTP请求,并且返回JSON类型的响应。Spring Initializer会自动为应用生成对应的启动类,一般以*Application方式进行命名。
  • springboot会搜索配置的路径
    1. 当前目录的/config 2.子目录 3.当前目录 4.classpath中的/config包 5.classpath
      spring.config.location来添加额外的属性文件的搜索路径
      1)命令行参数。SpringApplication类默认会把命令行参数转化成应用中可以使用的配置参数。
      2)通过System#getProperties方法获取的Java系统参数。
      3)操作系统环境变量。使用Docker启动时,经常会设置系统变量。
      4)从java:comp/env得到的JNDI属性。
      5)通过RandomValuePropertySource生成的random.*属性。
      6)应用jar文件之外的属性文件,如通过spring.config.location参数指定的属性文件。
      7)应用jar文件内部的属性文件,这是常用的方式。
      8)在应用配置Java类(包含@Configuration注解的Java类)中通过@PropertySource注解声明的属性文件。
      9)通过SpringApplication#setDefaultProperties方法声明的默认属性。
  • 多Profile
    Spring通过配置spring.profiles.active指定激活某个具体的Profile。除了使用spring. profiles.active激活一个或者多个Profile之外,还可以用spring.profiles.include来叠加
    Profile eg:
  • 自制一个starter
    1.创建Starter项目和导入依赖
     <dependencies>
	  <dependency>
		  <groupId>org.springframework.boot</groupId>
		  <artifactId>spring-boot-starter-web</artifactId>
	  </dependency>
     </dependencies>

2.定义过滤器

public class LogFilter implements Filter{
	private Logger logger = LoggerFactory.getLogger(LogFilter.class);
	@Override
	public void init(FilterConfig filterConfig)throws ServletException {
		logger.info("logFilter init...");
	}
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
		FilterChain filterChain)throws IOException, ServletException {
		  //  从request中获取到访问的地址,并在控制台中打印出来
		HttpServletRequest request =(HttpServletRequest)servletRequest;
		logger.info("uri {} is working.", request.getRequestURI());
		filterChain.doFilter(servletRequest, servletResponse);
	}
	@Override
	public void destroy(){
		logger.info("logFilter destroy...");
	}
}

定义LogFilterRegistrationBean用于将LogFilter过滤器封装成Spring Bean

public class LogFilterRegistrationBean extends FilterRegistrationBean<LogFilter>{
	public LogFilterRegistrationBean(){
		super();
		this.setFilter(new LogFilter()); //添加LogFilter过滤器
		this.addUrlPatterns("/*"); // 匹配所有路径
		this.setName("LogFilter"); // 定义过滤器名
		this.setOrder(1); // 设置优先级
	}
}
  1. 定义自动配置类
@Configuration
@ConditionalOnClass({LogFilterRegistrationBean.class, LogFilter.class})
public class LogFilterAutoConfiguration {
	  @Bean
	  @ConditionalOnMissingBean(LogFilterRegistrationBean.class)
	  public LogFilterRegistrationBean logFilterRegistrationBean(){
		  return new LogFilterRegistrationBean();
	  }

  }

@Configuration通常与@Bean相配合,使用这两个注解可以创建一个简单的Spring配置类,代替相应的xml配置文件。@ConditionalOnClass声明只有当某个或某些class位于类路径上,才会实例化一个Bean。如上述代码中只有当LogFilterRegistrationBean和LogFilter的class在类路径上,LogFilterAutoConfiguration配置类才会生效
添加@Bean注解的方法将返回一个对象,该对象会被注册为Spring上下文中的Bean。@ConditionalOnMissingBean声明仅仅在当前Spring上下文中不存在某个对象时,才会实例化一个Bean。上述代码中,当LogFilterRegistrationBean不存在于Spring上下文时,才会创建LogFilterRegistrationBean的Bean并注入到Spring上下文中
4.定义使自动配置类生效的注解
然后我们需要一个注解使LogFilterAutoConfiguration配置类生效。因为Starter是通过jar包的方式引入项目中,对应的classes并不在项目的Spring扫描范围内,所以无法自动引入项目的Spring管理中。对此需要用额外的方式将LogFilterAutoConfiguration引入到项目的Spring管理中,如通过注解的方式将配置类引入项目的Spring扫描范围内。
定义EnableLogFilter引入LogFilterAutoConfiguration配置类到项目的Spring扫描范围内,具体代码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(LogFilterAutoConfiguration.class)//引入LogFilterAutoConfiguration配置类
public @interface EnableLogFilter {
}

除了在注解中直接通过@Import引入对应的配置类外,还可以通过ImportSelector的方式从resources/META-INF/spring.factories中读入需要加载的自动配置类。
com.xuan.chapter3.starter.annotation.EnableLogFilter=
com.xuan.chapter3.starter.config.LogFilterAutoConfiguration
定义EnableLogFilterImportSelector用于从spring.factories获取需要加载的自动配置类。

public class EnableLogFilterImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware {
		  private static final Logger logger = LoggerFactory.getLogger(EnableLogFi
			  lterImportSelector.class);
		  private Class annotationClass = EnableLogFilter.class;
		  ...
		  @Override
		  public String[] selectImports(AnnotationMetadata metadata){
				// 是否生效,默认为true
			  if(! isEnabled()){
				  return new String[0];
			  }
			  // 获取注解中的属性
			  AnnotationAttributes attributes = AnnotationAttributes.fromMap(
				  meta data.getAnnotationAttributes(this.annotationClass.getName(),
					  true));
			  Assert.notNull(attributes, "can not be null…");
			  // 从spring.factories中获取所有通过EnableLogFilter注解引入的自动配置类,并
				  进行去重操作
			  List<String>  factories  =  new  ArrayList<>(new  LinkedHashSet<>(Sprin
				  gFactoriesLoader.loadFactoryNames(this.annotationClass,  this.
				  beanClassLoader)));
			  if(factories.isEmpty()&& ! hasDefaultFactory()){
				  throw new IllegalStateException("…");
			  }
			  if(factories.size()> 1){
				  logger.warn("More than one implementation ");
			  }
			  return factories.toArray(new String[factories.size()]);
		  }
	  }
	  ..
  }

通过覆盖selectImports方法,使用SpringFactoriesLoader从spring.factories中获取通过EnableLogFilter注解引入的自动配置类,获取的自动配置类是在spring.factories中配置的LogFilterAutoConfiguration。EnableLogFilterImportSelector可以扩展成一个通用的配置加载类,从而实现在spring.factories中通过不同的注解引入不同的自动配置类的功能。

第四章 Eureka

  • Eureka是一个RESTful风格的服务注册与发现的基础服务组件
  • Eureka由两部分组成,一个是EurekaServer,提供服务注册和发现功能,即我们上面所说的服务器端;另一个是Eureka Client,它简化了客户端与服务端之间的交互。Eureka Client会定时将自己的信息注册到EurekaServer中,并从Server中发现其他服务。Eureka Client中内置一个负载均衡器,用来进行基本的负载均衡
  • InstanceId是Eureka服务的唯一标记,主要用于区分同一服务集群的不同实例。一般来讲,一个Eureka服务实例默认注册的InstanceId是它的主机名(即一个主机只有一个服务)。但是这样会引发一个问题,一台主机不能启动多个属于同一服务的服务实例。为了解决这种情况,spring-cloud-netflix-eureka提供了一个合理的实现,如上面代码中的InstanceId设置样式。通过设置random.value可以使得每一个服务实例的InstanceId独一无二,从而可以唯一标记它自身
    eg:
eureka:
  instance:
    instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
  • Eureka服务间调用:
    RestTemplate将根据服务名eureka-client-service通过预先从eureka-service缓存到本地的注册表中获取到eureka-client-service服务的具体地址,从而发起服务间调用。

  • Eureka与服务注册中心交换信息

  • Eureka中标准元数据有主机名、IP地址、端口号、状态页url和健康检查url等,这些元数据都会保存在Eureka Server的注册表中,Eureka Client根据服务名读取这些元数据,来发现和调用其他服务实例。元数据可以自定义以适应特定的业务场景

    [{
        "host": "DESKTOP-ROB4DSR",
        "port": 8765,
        "metadata": {
            "management.port": "8765"
        },
        "secure": false,
        "uri": "http://DESKTOP-ROB4DSR:8765",
        "serviceId": "EUREKA-CLIENT",
        "instanceInfo": {
            "instanceId": "eureka-client:3bee7b34cbe3b637fcb6ebf3d2f1188c",
            "app": "EUREKA-CLIENT",
            "appGroupName": null,
            "ipAddr": "192.168.136.1",
            "sid": "na",
            "homePageUrl": "http://DESKTOP-ROB4DSR:8765/",
            "statusPageUrl": "http://DESKTOP-ROB4DSR:8765/actuator/info",
            "healthCheckUrl": "http://DESKTOP-ROB4DSR:8765/actuator/health",
            "secureHealthCheckUrl": null,
            "vipAddress": "eureka-client",
            "secureVipAddress": "eureka-client",
            "countryId": 1,
            "dataCenterInfo": {
                "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                "name": "MyOwn"
            },
            "hostName": "DESKTOP-ROB4DSR",
            "status": "UP",
            "overriddenStatus": "UNKNOWN",
            "leaseInfo": {
                "renewalIntervalInSecs": 30,
                "durationInSecs": 90,
                "registrationTimestamp": 1608531165423,
                "lastRenewalTimestamp": 1608531285249,
                "evictionTimestamp": 0,
                "serviceUpTimestamp": 1608531165423
            },
            "isCoordinatingDiscoveryServer": false,
            "metadata": {
                "management.port": "8765"
            },
            "lastUpdatedTimestamp": 1608531165423,
            "lastDirtyTimestamp": 1608531165236,
            "actionType": "ADDED",
            "asgName": null
        },
        "scheme": null
    }]
  • Consul服务注册与发现
    consul的实例叫agent
    agent有两种运行模式:server和client
    每个数据中心至少要有一个server,一般推荐3-5个server(避免单点故障)
    client模式agent是一个轻量级进程,执行健康检查,转发查询请求到server。
    服务service是注册到consul的外部应用,比如spring web server
原文地址:https://www.cnblogs.com/Baronboy/p/14166920.html