目录
注:本篇文章主要参考周阳老师讲解的cloud进行整理的!
由于代码有很多,本篇文章也是尽可能的每一步骤都进行了记录,在文章最下方提供了源码地址!
Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理
服务治理就是提供了微服务架构中各微服务实例的快速上线或下线且保持各服务能正常通信的能力的方案总称。
服务治理的优点:
当一个服务实例不可用时,治理服务器可以将请求转给其他服务提供者,当一个新的服务实例上线时,也能够快速地分担服务调用请求。将所有请求动态地分布到其所管理的所有服务实例中进行处理。定时从服务治理服务器中复制一份服务实例信息缓存到本地中,这样即使当服务治理服务器不可用时,服务消费者也可以使用本地的缓存去访问相应的服务,而不至于中断服务。通过这种机制,极大地提高了应用的弹性。通过互相注册机制,将每个治理服务器所管辖的服务信息列表进行交换,使服务治理服务拥有更高的可用性。Eureak 是Netflix 开源微服务框架中一系列项目中的一个。Spring Cloud对其进行了二次封装,形成了Spring Cloud Netflix 子项目,但未对Netflix微服务实现原理进行更改,只是进行了Spring Boot化,使开发者更容易使用和整合。
在Eureka中,对于服务治理有如下3个概念:
Eureka Server 作为服务注册功能的服务器,也就是服务注册中心(这里的Eureka Server指的是我们自己专门写一个Java应用来引用Eureka Server的依赖,将这个应用作为注册中心)。而系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Eureka客户端,负责将微服务所提供的服务向Eureka服务器执行注册、续约和注销等操作,以使服务消费者可以发现并进行消费。在服务注册时需要向服务治理服务器提供服务名称、宿主服务器IP地址、服务端口号、域名等主要数据。Eureka客户端。它在启动时会默认从所服务治理服务器中获取所有的服务注册表信息,通过所获取到的服务注册列表信息来消费相应的服务。Eureka Server提供注册服务功能
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
EurekaClient通过注册中心进行访问
EurekaClient可以分为提供者和消费者,他们都是一个Java客户端,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
准确来说是什么场景需要使用注册中心:
大并发量的应用情况下就需要搭建集群(这里的集群指的不是Eureka注册中心集群,而是指的微服务各个模块的集群),搭建集群我们就需要通过注册中心来实时掌握每个应用的情况,如果根本没有大并发场景,项目虽然也拆分了服务,但是用不到集群,那我们大可不必使用注册中心,使用注册中心从某种意义上来讲,增加了程序的复杂度,而且还得时刻维护注册中心,有点大材小用了。
关于停更问题我在这篇博客已经讲的很清楚了:https://blog.csdn.net/weixin_43888891/article/details/125193088?spm=1001.2014.3001.5501

使用Eureka那就意味着一定是微服务架构,这就涉及到了聚合工程,本篇博客的代码完全是基于本人上一篇博客 搭建的聚合工程 来进行开发的。
idea聚合工程搭建详解:https://blog.csdn.net/weixin_43888891/article/details

1、建Module:cloud-eureka-server7001
2、改pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"><parent><artifactId>mscloud</artifactId><groupId>com.gzl.cn</groupId><version>1.0-SNAPSHOT</version></parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.gzl.cn</groupId>
<artifactId>cloud-api-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
3、写YML
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4、主启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaSever7001Application {
public static void main(String[] args) {
SpringApplication.run(EurekaSever7001Application.class, args);
}
}
5、启动测试

这里我们直接接着上一篇文章中的cloud-provider-payment8001服务进行调整
1、在原有cloud-provider-payment8001服务的pom当中添加以下依赖
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、添加yml
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
3、启动类添加注解
@EnableEurekaClient
4、测试
先要启动EurekaServer确保 http://localhost:7001/ 可以访问
然后再启动cloud-provider-payment8001
微服务注册名配置说明:

5、自我保护机制
下面一排红字不用管,他是Eureka的自我保护机制,在最文章后面我会具体讲他的作用。

这里我们直接接着上一篇文章中的cloud-provider-payment8001服务进行调整
1、在原有cloud-consumer-order80服务的pom当中添加以下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、添加yml
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
3、启动类添加注解
@EnableEurekaClient
4、测试
先要启动EurekaServer, 7001服务
再启动服务提供者provider, 8001服务
最后启动服务消费者consumer,80服务
访问:http://localhost/consumer/payment/get/1

通过上面的练习,我们会发现根本没体会到Eureka作用是什么,没有Eureka服务照样可以运行使用,这也就是上面提到的,假如微服务不使用集群的话,完全没必要使用注册中心。下面我们进行分别搭建Eureka集群,和微服务集群。

微服务RPC远程服务调用最核心的是什么
高可用,试想你的注册中心只有一个only one, 它出故障了那就呵呵( ̄▽ ̄)"了,会导致整个为服务环境不可用,所以需要搭建Eureka集群
1、参考cloud-eureka-server7001 新建 cloud-eureka-server7002(直接复制就行)
2、改pom

3、父工程的pom当中添加model
正常通过ide创建,他是会自动在父工程添加model的,而我们是复制的所以需要手动添加

4、修改启动名字:EurekaSever7002Application
5、修改映射配置

添加:
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
6、修改yml(7001和7002都需要修改),由于7002是直接复制的,所以7002的yml端口号需要改成7002
7002主要是修改端口和hostname,,7001的yml当中的hostname修改为eureka7001.com
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
7、将 支付服务8001微服务 和 订单服务80微服务 发布到上面2台Eureka集群配置中(两个服务都需要修改yml)
http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

8、测试
先要启动EurekaServer, 7001/7002服务 http://eureka7001.com:7001/
再要启动服务提供者provider, 8001
再要启动消费者,80
访问:http://localhost/consumer/payment/get/1
1、参考cloud-provider-payment8001 新建 cloud-provider-payment8002(直接复制就行)
2、改pom

3、父工程的pom当中添加model

4、修改启动名字:PaymentMain8002
5、修改yml(主要就是修改端口号为8002)
6、修改8001/8002的Controller
主要是方便区分我们调用的是哪个服务

1、订单服务访问地址不能写死
// 通过在eureka上注册过的微服务名称调用
public static final String PaymentSrv_URL = "http://CLOUD-PAYMENT-SERVICE";

2、使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
spring-cloud-starter-netflix-eureka-client依赖当中就依赖了ribbon依赖,所以我们不用添加ribbon依赖就能使用他的注解。

一旦地址改为以服务名称访问的形式"http://CLOUD-PAYMENT-SERVICE",就必须添加这个负载均衡的注解,不然访问80服务的接口就会报错。

3、测试
在测试过程发现的问题,所以记录一下!
访问:http://eureka7001.com:7001/

访问:http://eureka7002.com:7002/

这时候会发现所有服务全注册到了7001,而7002实际上并没有服务,当我们把7001服务关闭后,会发现过一段时间服务自动就注册到了7002,可能需要个几秒,这几秒的过程当中如果调用服务是会报错的!

为什么会都优先注册到7001而不是7002呢?跟配置文件写的顺序有关!

假设7001注册中心挂掉的同时8001服务也挂掉了,然后先恢复7001服务,然后再恢复8001服务。这时候会出现以下情况。8001注册到了7001注册中心,而其他没有挂掉的服务,由于7001挂掉了之后注册在7001的服务全部转移到了7002注册中心。

这个时候会引发一个非常严重的问题,就是服务提供者的集群失效!
多次访问80服务的接口,会发现一直是调用的8002服务,他不会去调用8001,因为80服务注册在了7002注册中心,而8001注册在了7001注册中心,服务都不在一个注册中心,所以他根本调用不了。

解决办法: 假如注册中心和一个服务挂掉之后,一定要先想办法先恢复服务,而不是先恢复挂掉的注册中心!先恢复8001服务的话,他会直接注册到7002注册中心,就完全避免了这样的问题。当然这里启动8001服务的时候会报错但是不影响的,因为他启动的时候会先去注册到7001,然后发现7001挂掉了这时候会报错,然后自动注册到7002。
从这个问题也间接的会发现,微服务框架当中,有时候程序启动的先后顺序至关重要!
1、主机名称修改


这里一个是应用名称,一个是主机名称

2、ip提示
当我们服务多了,有时候以应用名称根本无法快速锁定他在哪台服务器部署,所以可以添加以下配置。


对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
在8001的PaymentController添加一个接口:

DiscoveryClient 导包的时候导下面的包
import org.springframework.cloud.client.discovery.DiscoveryClient;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery() {
// 获取注册的服务
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println(element);
}
// 根据服务名称获取服务详细信息
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance element : instances) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.discoveryClient;
}
测试:http://localhost:8001/payment/discovery 访问后看控制台输出内容!
在了解保护机制之前,我们有必要先了解一下Eureka的心跳机制。
使用注册中心,那我们的服务信息就需要注册到注册中心,注册中心起到的作用是服务治理,那他就需要时刻掌握服务是否宕机,心跳机制就是Eureka client一旦注册到注册中心后,就需要每隔一段时间告诉Eureka server一下,我还是活的。
在每一个Eureka Client启动的时候,都会有一个HeartbeatThread的心跳线程,这个线程的作用就是保证默认30秒的时候向Eureka Server发送一个信息的请求,告诉Eureka Server当前的Eureka Client还存活着。
下面这个参数可以来配置心跳间隔时间(这个是在Eureka的client端配置的):
eureka:
instance:
lease-renewal-interval-in-seconds: 30
Eureka Server在接收到请求之后,肯定是先去自己的注册表中去,找到请求的对应的服务信息,在这个服务信息里面有个Lease的对象,这个里面就是可以进行心跳续约操作的,更新Lease对象里面的LastUpdateTimestamp时间戳,每一次接收到都会更新这个时间戳的。
Eureka Server在启动的时候启动的时候会每60秒遍历一次注册表中的信息,然后查看注册表中的信息是否有过期的,如果过期会加入到一个列表里面单独处理。
清理间隔时间配置(单位毫秒,默认是60*1000,在Eureka的server端配置的):
eureka:
server:
eviction-interval-timer-in-ms: 60
Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务。
通过下面可以配置等待时间(这个是在Eureka的client端配置的):
eureka:
instance:
lease-expiration-duration-in-seconds: 90
通过以上了解,我们一共接触了三个时间配置,一个是注册中心的扫描过期时间,扫描过期时间就好比,租车公司要定期查看哪些车租聘日到期了,需要进行收回。还有一个客户端设置的 租期更新时间间隔,这个时间就是租户需要隔一段时间告诉租聘公司,我还要继续租这个车,还有一个是租期到期时间,这个时间就是假如客户端超过这个时间没有给租聘公司发送心跳,那么就认为不租了。
server端:
eureka.server.eviction-interval-timer-in-ms//清理间隔(单位毫秒,默认是60*1000)
client端:
eureka.instance.lease-renewal-interval-in-seconds =10//租期更新时间间隔(默认30秒)
eureka.instance.lease-expiration-duration-in-seconds =30//租期到期时间(默认90秒)
首先明确观点:Eureka自我保护机制有很多bug,而且中文资料很缺乏,网上普遍是一篇博客抄来抄去的,本人通过案例也确实发现很多bug的地方,如果钻牛角尖的话,可能几天都学不完!
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不畅通情况下(网络延迟等原因),在保护模式开启的情况下,EurekaServer不会立刻将EurekaClient服务剔除。
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:

这个需要在注册中心配置的!
server:
#关闭自我保护机制,保证不可用服务被及时踢除,默认为true开启的
enable-self-preservation: false
一句话:某时刻某一个微服务因为网络原因不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存,属于CAP里面的AP分支。
因为网络通信是可能恢复的,但是Eureka客户端只会在启动时才去服务端注册。如果因为网络的原因而剔除了客户端,将造成客户端无法再注册到服务端。为了避免此问题,Eureka提供了自我保护机制。
CAP即:

清理条件:
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种
应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
Eureka服务端默认情况下是会开启自我保护机制的。但我们在不同环境应该选择是否开启保护机制。
自我保护机制的配置是(默认是开启的):eureka.server.enable-self-preservation=true
一般情况下,我们会选择在 开发环境下关闭自我保护机制,而在生产环境下启动自我保护机制。
开发环境下,我们我们启动的服务数量较少而且会经常修改重启。如果开启自我保护机制,很容易触发Eureka客户端心跳占比低于85%的情况。使得Eureka不会剔除我们的服务,从而在我们访问的时候,会访问到可能已经失效的服务,导致请求失败,影响我们的开发。
在生产环境下,我们启动的服务多且不会反复启动修改。环境也相对稳定,影响服务正常运行的人为情况较少。适合开启自我保护机制,让Eureka进行管理。
如下我启动了3个服务:

Lease expiration enabled:Eureka自动保护机制启动后该值为false。
Renews threshold:Eureka Server 期望每分钟收到客户端实例续约的阅值。
Renews threshold = 服务实例总数 *(60/续约间隔)*自我保护续约百分比阈值因子(阈值因子默认85%,续约间隔默认是30)。
Renews (last min): Eureka Server 最后1分钟收到客户端实例续约的总数。
Renews(last min) = 服务实例总数 * (60/续约间隔)
如果自我保护模式开启了,且当续约阈值>0,上一分钟的续约数>阈值,那么可以清理。言外之意就是,当上一分钟续约数<阈值,那么就不清理(保护了)(Renews (last min) < Renews threshold)
什么都没配置,只是启动了三个服务,然后等了一会就进入保护模式了。对于这一点我也不是很理解。进入保护模式后,我随便关掉了一个服务,Eureka控制台上也就几秒钟立马就消失了,并没有向上面所说的,进入保护模式后,不会轻易移除服务!

我开启了自我保护机制,并且给每个要注册的服务设置如下配置:
eureka:
instance:
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
注意要观察Renews (last min)和Renews threshold值的时候,启动项目后一定要等个1分钟才可以,等一分钟过后如下:

Renews threshold完全跟上面提到的算法是不一致的,而且差距还很大,而Renews (last min)却差距不是很大。针对于此问题,我也搜了很多相关资料,由于Eureka已经停更,很多人已经弃用,并没有太多的人关注这个问题了。
当我关闭8001服务的时候,Eureka上的8001服务并没有立马消失,而是停留了几秒钟,状态显示的是DOWN。

eureka:
server:
enable-self-preservation: false
关闭后,启动注册中心就会看到以下字样!

由于server和client通过心跳保持服务状态,而只有状态为UP的服务才能被访问
看eureka界面中的status
比如心跳一直正常,服务一直UP,但是此服务DB连不上了,无法正常提供服务
这个需要在Eureka client端设置,而且默认就是开启的。
eureka:
client:
healthcheck:
enabled: true
刚开始我以为是只要这个设置true,然后当服务的mysql挂掉后,会自动更新注册中心的状态,然后可以避免其他服务调用异常的服务,然而并不是,他还需要配置很多东西,并没有我想象的那么神奇!
想了解的可以看一下这篇文章:https://my.oschina.net/u/3727895/blog/4481621
中文的cloud官网介绍的Eureka:https://www.bookstack.cn/read/spring-cloud-docs/docs-user-guide-eureka.md
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or