spring cloud连载第三篇之Spring Cloud Netflix

1. Service Discovery: Eureka Server(服务发现:eureka服务器) 1.1 依赖 复制代码 1 2 org.springframework.cloud 3 spring-cloud-starter-netflix-eureka-server 4 复制代码 1.2 How to Run a Eureka Server(怎样启动eureka服务器) 下面是一个小型的eureka服务器: 复制代码 1 @SpringBootApplication 2 @EnableEurekaServer 3 public class Application { 4 5 public static void main(String[] args) { 6 new SpringApplicationBuilder(Application.class).web(true).run(args); 7 } 8 9 } 复制代码 启动后通过http://localhost:{port}来查看主页。 1.3 High Availability, Zones and Regions(高可用,zones和regions) eureka服务没有后端存储系统,但是服务实例需要不停发送心跳检测来保持他们的注册信息是最新的,所以这些注册信息是存储在内存中的。 客户端也是使用内存来缓存从服务器获取的服务注册信息的。(这样他们就不用每次请求一个服务时都要经过eureka服务器)。 默认情况下,eureka服务器也是一个eureka客户端,并且需要至少一个service URL来定位其他节点。如果你不提供这个URL也可以工作,但是你的日志会出现很多无用的报错信息(它无法注册自己到集群节点中)。 这个问题可以用下面介绍的单节点模式来解决^_^。 1.4 Standalone Mode(单节点模式) 两个缓存(客户端和服务器)和心跳机制的组合使得单节点的Eureka服务器对故障具有相当的弹性,只要有某种监视器或弹性运行时(如Cloud Foundry)保持它的活力。 在单节点模式下需要关闭服务器的客户端行为(不断的尝试连接其他集群节点),如下(application.yml): 复制代码 1 server: 2 port: 8761 3 4 eureka: 5 instance: 6 hostname: localhost 7 client: 8 registerWithEureka: false 9 fetchRegistry: false 10 serviceUrl: 11 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 复制代码 注意:其中serviceUrl是指向本身的。 1.5 Peer Awareness(节点感知) Eureka服务器可以使用启动多个实例并且相互注册的方法来达到高可用。实际上这是默认行为,你需要做的就是添加节点的serviceUrl。如下(application.yml): 复制代码 1 --- 2 spring: 3 profiles: peer1 4 eureka: 5 instance: 6 hostname: peer1 7 client: 8 serviceUrl: 9 defaultZone: http://peer2/eureka/ 10 11 --- 12 spring: 13 profiles: peer2 14 eureka: 15 instance: 16 hostname: peer2 17 client: 18 serviceUrl: 19 defaultZone: http://peer1/eureka/ 复制代码 在上面的例子中我们可以以不同的host来启动同一个应用。启动时指定参数--spring.profiles.active=peer1或者peer2。通过修改操作系统的hosts文件(windows位置C:\Windows\System32\drivers\etc\hosts,linux位置/etc/hosts)就可以解决hostname问题。这样我们就可以在一台主机上测试eureka服务器的节点感知功能了。 你可以添加多个节点到一个系统中,只要它们至少在一端是彼此相连的。它们在自己内部同步注册信息。如果节点在物理上是分离的(在数据中心内部或多个数据中心之间),那么系统原则上可以在“脑裂”类型的故障中存活。(脑裂其实就是在高可用(HA)系统中,当联系2个节点的“心跳线”断开时,本来为一整体、动作协调的HA系统,就分裂成为2个独立的个体。由于相互失去了联系,都以为是对方出了故障。两个节点上的HA软件像“裂脑人”一样,争抢“共享资源”、争起“应用服务”,就会发生严重后果——或者共享资源被瓜分、2边“服务”都起不来了;或者2边“服务”都起来了,但同时读写“共享存储”,导致数据损坏)。 1.6 When to Prefer IP Address(何时使用ip地址) 在有些情况下,Eureka推荐使用ip地址而不是hostname。通过设置eureka.instance.preferIpAddress=true。 注意:如果java决定不了hostname那么就会把ip地址发送给eureka。通过eureka.instance.hostname可以显式的设置hostname。或者设置环境变量。 1.7 Securing The Eureka Server(保护eureka服务器) 添加spring-boot-starter-security到你的classpath。默认情况下,当spring security在classpath中时,它要求每个请求都必须含有CSRF token。不过一般Eureka客户端都没有CSRF token,所以需要将路径/eureka/**放开。 如下: 复制代码 1 @EnableWebSecurity 2 class WebSecurityConfig extends WebSecurityConfigurerAdapter { 3 4 @Override 5 protected void configure(HttpSecurity http) throws Exception { 6 http.csrf().ignoringAntMatchers("/eureka/**"); 7 super.configure(http); 8 } 9 } 复制代码 2 Service Discovery: Eureka Clients(服务发现:eureka客户端) 2.1 依赖 复制代码 1 2 org.springframework.cloud 3 spring-cloud-starter-netflix-eureka-client 4 复制代码 2.2 Registering with Eureka(通过eureka注册) 当一个客户端注册到Eureka时,它会提供自身的一些元数据(主机,端口,健康状态指示器url,主页,和其他一些细节)。Eureka会接受到来自服务实例的心跳信息。如果心跳在一段时间没有接收到,则Eureka将会移除对应的实例。 下面是一个简单的eureka客户端: 复制代码 1 @SpringBootApplication 2 @RestController 3 public class Application { 4 5 @RequestMapping("/") 6 public String home() { 7 return "Hello world"; 8 } 9 10 public static void main(String[] args) { 11 new SpringApplicationBuilder(Application.class).web(true).run(args); 12 } 13 14 } 复制代码 application.yml: 复制代码 1 eureka: 2 client: 3 serviceUrl: 4 defaultZone: http://localhost:8761/eureka/ 复制代码 在classpath上存在spring-cloud-starter-netflix-eureka-client会是你的app变成eureka“实例”(将自身注册到eureka)和eureka“客户端”(从eureka获取其他服务的信息)。其中实例的行为可以使用eureka.instance.*来设置。 一般来说默认值就够用了,你只需要设置一下spring.application.name,因为它是eureka默认的service ID。 如果你只对外提供服务而不需要从eureka获取其他服务信息,那么你可以关闭对应的“客户端”行为。使用eureka.client.enabled=false。 2.3 Authenticating with the Eureka Server(使用eureka服务器进行身份验证) 如果eureka.client.serviceUrl.defaultZone的URL中含有用户凭证信息(http://user:password@localhost:8761/eureka),HTTP基本验证将会自动添加到eureka客户端中。但是如果有更复杂的需求的话,需要定义一个 DiscoveryClientOptionalArgs类型的bean,并且在其中注入一个ClientFilter实例,它会应用到客户端到服务端的所有请求上。 2.4 Using the EurekaClient(使用EurekaClient) 只要你的app是一个eureka客户端,你就可以从eureka服务器获取其他服务的信息。其中一种方法就是使用原生的com.netflix.discovery.EurekaClient,如下: 复制代码 1 @Autowired 2 private EurekaClient discoveryClient; 3 4 public String serviceUrl() { 5 InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false); 6 return instance.getHomePageUrl(); 7 } 复制代码 注意:别在@PostConstruct或者@Scheduled方法里使用EurekaClient,或者任何其他ApplicationContext还没启动的地方。 2.5 Alternatives to the Native Netflix EurekaClient(原生Netflix EurekaClient的替代方法) 可以使用org.springframework.cloud.client.discovery.DiscoveryClient,提供了一些简单的API来获取其他注册服务信息。例如: 复制代码 1 @Autowired 2 private DiscoveryClient discoveryClient; 3 4 public String serviceUrl() { 5 List list = discoveryClient.getInstances("STORES"); 6 if (list != null && list.size() > 0 ) { 7 return list.get(0).getUri(); 8 } 9 return null; 10 } 复制代码 2.6 Why Is It so Slow to Register a Service?(为啥注册一个服务这么慢?) 注册成为一个实例需要发送周期性的心跳到注册中心,默认值为30秒。直到实例,服务器,客户端在他们本地的缓存中有同样的元数据(可能需要3次心跳)一个服务才能被其他客户端发现。 可以通过设置eureka.instance.leaseRenewalIntervalInSeconds来改变周期。把它设置成一个小于30的值会使客户端更快的连接到其他服务上。但是在生产环境中最好使用默认值,因为服务器内部的计算假设了租赁续期。 2.7 Zones 如果你把客户端部署到不同的区域,你可以通过设置来使客户端优先使用同一区域中的服务。 第一步,确保你的eureka服务器部署到了各个区域,并且是彼此的节点。 第二步,你需要告诉eureka服务器你的服务处于哪个区域,使用metadataMap来设置。下面的例子中service 1被部署到了zone1和zone2. Service 1 in Zone 1: 复制代码 1 eureka.instance.metadataMap.zone = zone1 2 eureka.client.preferSameZoneEureka = true 复制代码 Service 1 in Zone 2: 复制代码 eureka.instance.metadataMap.zone = zone2 eureka.client.preferSameZoneEureka = true 复制代码 3. Circuit Breaker: Hystrix Clients(断路器:Hystrix客户端) 3.1 依赖 复制代码 1 2 org.springframework.cloud 3 spring-cloud-starter-netflix-hystrix 4 复制代码 下面是一个简单的含有Hystrix断路器的eureka服务器: 复制代码 1 @SpringBootApplication 2 @EnableCircuitBreaker 3 public class Application { 4 5 public static void main(String[] args) { 6 new SpringApplicationBuilder(Application.class).web(true).run(args); 7 } 8 9 } 10 11 @Component 12 public class StoreIntegration { 13 14 @HystrixCommand(fallbackMethod = "defaultStores") 15 public Object getStores(Map parameters) { 16 //do stuff that might fail 17 } 18 19 public Object defaultStores(Map parameters) { 20 return /* something useful */; 21 } 22 } 复制代码 3.2 Propagating the Security Context or Using Spring Scopes(传播安全上下文或者使用spring scopes) 如果你想传递线程本地上下文到@HystrixCommand中,默认配置是不行的,因为它是在线程池中执行命令的。你可以通过配置文件或者直接在注解中配置来使Hystrix使用调用者的线程。即使用一个不同的“Isolation Strategy”。 下面的例子展示了如果在注解中设置线程: 复制代码 1 @HystrixCommand(fallbackMethod = "stubMyService", 2 commandProperties = { 3 @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE") 4 } 5 ) 6 ... 复制代码 你也可以选择使用hystrix.shareSecurityContext=true。这么做将会自动配置一个Hystrix并发性策略插件钩子来将SecurityContext从主线程中传递到Hystrix线程中。Hystrix不允许多个并发性策略被注册,所以你可以配一个你自己的 HystrixConcurrencyStrategy bean。Spring Cloud会在spring上下文中查找你的实现并且将它包装到它自己的插件中去。 4. Hystrix Timeouts And Ribbon Clients(Hystrix超时和Ribbon客户端) 当使用包含Ribbon客户端的Hystrix命令时,你要保证Hystrix的超时时间比Ribbon的超时时间长,包含潜在的重试机制。举个例子:Ribbon的超时时间是1秒,而且会在超时时重试3次,那么你的Hystrix的超时时间应该设置大于3秒。 4.1 Hystrix Dashboard依赖 复制代码 1 2 org.springframework.cloud 3 spring-cloud-starter-netflix-hystrix-dashboard 4 复制代码 在spring boot的启动类上添加@EnableHystrixDashboard注解。然后访问/hystrix,并且指向一个Hystrix客户端的/hystrix.stream端点。 4.2 Turbine 查看一个单独实例的Hystrix的数据对于整个系统整体而言通常意义不大。Turbine可以将所有相关的/hystrix.stream关联到/turbine.stream来供Hystrix Dashboard使用。每个单独的应用实例可以使用eureka来定位。 启动Turbine需要在启动类上添加注解@EnableTurbine,不同的是turbine.instanceUrlSuffix不需要提供端口,因为它会自动添加,除非设置了turbine.instanceInsertPort=false。 注意:默认情况下,Turbine查找/hystrix.stream是通过注册实例在eureka中的hostName和port信息并且在后面添加/hystrix.stream。如果注册实例的元数据中存在management.port属性的话,那么/hystrix.stream端点将会使用 它代替之前的port。默认,元数据中的management.port和配置属性的management.port是相同的。可以通过下面的设置来覆盖: 复制代码 1 eureka: 2 instance: 3 metadata-map: 4 management.port: ${management.port:8081} 复制代码 turbine.appConfig配置是一个eureka serviceId的列表,Turbine用它来查找实例。Turbine stream在Hystrix Dashboard中使用的url如下: 1 http://my.turbine.server:8080/turbine.stream?cluster=CLUSTERNAME 其中cluster参数可以省略,如果名称是默认的话。cluster参数必须匹配turbine.aggregator.clusterConfig。从Eureka返回的值都是大写的。 复制代码 1 turbine: 2 aggregator: 3 clusterConfig: CUSTOMERS 4 appConfig: customers 复制代码 你可以通过定义一个TurbineClustersProvider的bean来定制化cluster名称。 cluster name还可以使用SPEL在turbine.clusterNameExpression配置。cluster name默认就是实例在eureka注册的serviceId。下面使用一个不一样的例子: 复制代码 1 turbine: 2 aggregator: 3 clusterConfig: SYSTEM,USER 4 appConfig: customers,stores,ui,admin 5 clusterNameExpression: metadata['cluster'] 复制代码 在上面的例子中,四个服务的cluster name是从他们在注册中心实例信息的metadata map中获取的。 如果将所有服务都归纳到一个cluster name下,则可以使用“default”,如下(注意单引号和双引号): 复制代码 1 turbine: 2 appConfig: customers,stores 3 clusterNameExpression: "'default'" 复制代码 4.2.1 Clusters Endpoint(Clusters端点) 通过/clusters路径可以查看当前Turbine存在哪些clusters: 复制代码 1 [ 2 { 3 "name": "RACES", 4 "link": "http://localhost:8383/turbine.stream?cluster=RACES" 5 }, 6 { 7 "name": "WEB", 8 "link": "http://localhost:8383/turbine.stream?cluster=WEB" 9 } 10 ] 复制代码 如果要禁用这个端点可以使用turbine.endpoints.clusters.enabled=false。 5. Client Side Load Balancer: Ribbon(客户端负载均衡器:Ribbon) Ribbon是一个端客户端的负载均衡器,它给了HTTP或者TCP客户端行为的很多控制。Feign用的就是Ribbon。如果你使用了Feign那么本节也值得一看。 5.1 How to Include Ribbon(依赖) 复制代码 1 2 org.springframework.cloud 3 spring-cloud-starter-netflix-ribbon 4 复制代码 5.2 Customizing the Ribbon Client(自定义Ribbon客户端) 可以通过外部配置.ribbon.*来配置一些Ribbon客户端属性。Spring Cloud也允许你使用@RibbonClient来申明额外的配置来完全掌控Ribbon客户端。如下: 复制代码 1 @Configuration 2 @RibbonClient(name = "custom", configuration = CustomConfiguration.class) 3 public class TestConfiguration { 4 } 复制代码 在上面这个例子中,客户端是由RibbonClientConfiguration中的组件和CustomConfiguration中的所有组件组成(后者会覆盖前者)。 注意:CustomConfiguration必须是一个@Configuration类,并且要保证它不在@ComponentScan扫描范围内。否则它就会被所有Ribbon客户端共享。 下表是Spring Cloud Netflix为Ribbon提供的默认值: Bean Type Bean Name Class Name IClientConfig ribbonClientConfig DefaultClientConfigImpl IRule ribbonRule ZoneAvoidanceRule IPing ribbonPing DummyPing ServerList ribbonServerList ConfigurationBasedServerList ServerListFilter ribbonServerListFilter ZonePreferenceServerListFilter ILoadBalancer ribbonLoadBalancer ZoneAwareLoadBalancer ServerListUpdater ribbonServerListUpdater PollingServerListUpdater 创建上面类型的bean并且将它放在@RibbonClient配置中可以覆盖默认的配置。如下: 复制代码 1 @Configuration 2 protected static class FooConfiguration { 3 @Bean 4 public ZonePreferenceServerListFilter serverListFilter() { 5 ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter(); 6 filter.setZone("myTestZone"); 7 return filter; 8 } 9 10 @Bean 11 public IPing ribbonPing() { 12 return new PingUrl(); 13 } 14 } 复制代码 5.3 Customizing the Default for All Ribbon Clients(为所有Ribbon客户端自定义默认配置) 使用@RibbonClients注解并且注册一个默认配置。如下: 复制代码 1 @RibbonClients(defaultConfiguration = DefaultRibbonConfig.class) 2 public class RibbonClientDefaultConfigurationTestsConfig { 3 4 public static class BazServiceList extends ConfigurationBasedServerList { 5 public BazServiceList(IClientConfig config) { 6 super.initWithNiwsConfig(config); 7 } 8 } 9 } 10 11 @Configuration 12 class DefaultRibbonConfig { 13 14 @Bean 15 public IRule ribbonRule() { 16 return new BestAvailableRule(); 17 } 18 19 @Bean 20 public IPing ribbonPing() { 21 return new PingUrl(); 22 } 23 24 @Bean 25 public ServerList ribbonServerList(IClientConfig config) { 26 return new RibbonClientDefaultConfigurationTestsConfig.BazServiceList(config); 27 } 28 29 @Bean 30 public ServerListSubsetFilter serverListFilter() { 31 ServerListSubsetFilter filter = new ServerListSubsetFilter(); 32 return filter; 33 } 34 35 } 复制代码 5.4 Customizing the Ribbon Client by Setting Properties(使用配置文件自定义Ribbon客户端) 从版本1.2.0后Spring Cloud Netflix允许通过属性设置来自定义Ribbon客户端。下面是支持的属性设置: 复制代码 1 .ribbon.NFLoadBalancerClassName: Should implement ILoadBalancer 2 .ribbon.NFLoadBalancerRuleClassName: Should implement IRule 3 .ribbon.NFLoadBalancerPingClassName: Should implement IPing 4 .ribbon.NIWSServerListClassName: Should implement ServerList 5 .ribbon.NIWSServerListFilterClassName: Should implement ServerListFilter 复制代码 注意:在上面这些属性中定义的类比@RibbonClient(configuration=MyRibbonConfig.class和Spring Cloud Netflix提供的默认值具有更高的优先级。 例如(application.yml): 复制代码
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信