在这篇文章中我们将学习如何创建分布式微服务,并使用springboot、Feign和Eureka服务器使这些微服务协同工作。我们将讨论如何使用springboot创建分布式微服务。为此,我们将使用包Spring Cloud NetFlix(https://spring.io/projects/spring-cloud-netflix)。
任何微服务都应该能够定位另一个服务的不同实例,而不必在代码中定义它们的地址。
如果它需要访问另一个服务,理想情况下,您可以查询其他服务器实例运行的不同地址,因为最常见的情况是实例的数量及其地址是动态的。
为了在Spring实现这一点,我们将使用Spring Cloud NetFlix包中的Eureka服务器。我们的应用程序还将使用Ribbon和Feign来查找微服务的不同实例并平衡请求。
在本文中,我将解释如何创建一个服务来调用请求一个国家的资本。这个服务反过来调用另一个服务来定位请求的数据,因为第一个服务只是一个入口点。
使用的程序如下:
- Project: Capitals-service Port: 8100
- Project: countries-Service Port: 8000 and 8001
- Project: eureka-server Port: 8761
Project: countries-service实例将有一个不同国家的数据库。这个服务的两个实例将被执行,这样我们就可以看到projectcapitals服务程序如何调用一个实例,以及这两个实例中的另一个如何执行负载平衡。
本文中的示例代码位于GitHub上:https://github.com/chuchip/springEureka
创建Eureka服务器
我们首先需要一个地方,所有的微服务在初始化时都可以注册。反过来,每当我们需要定位不同的实例时,就会咨询这个服务。在这个例子中,我们将使用一个非常容易创建的Eureka服务器。
为此,我们将使用starter eureka服务器创建一个新的Spring Boot项目。
在此项目中,我们将更改application.properties
文件以包括以下内容:
spring.application.name = eureka-server
server.port = 8761
eureka.client.register-with-eureka = false
eureka.client.fetch-registry = false
使用spring.application.name
,我们指定程序的名称。服务将侦听的端口是用server.port
指定的。而且,最重要的是,因为上述值是可选的,所以它们是Eureka服务器的参数。
eureka.client.register-with-eureka=false
-使其成为服务器不会尝试注册自身。eureka.client.fetch-registry=false
—因此,我们通知客户,他们不能将可用实例的数据存储在本地缓存中。这意味着,当他们需要访问服务时,必须咨询Eureka服务器。在生产中,这通常设置为true以加快请求。我必须说明,默认情况下,此缓存每30秒更新一次。
现在,在我们的类中,在进入Spring Boot的地方,我们放置了注释EnableEurekaServer
:
@SpringBootApplication
@EnableEurekaServer
public class NetflixEurekaNamingServerApplication
{
public static void main (String [] args) {
SpringApplication.run (NetflixEurekaNamingServerApplication.class, args);
}
}
准备好了!我们的Eureka服务器已创建。要查看您的状态,我们可以使用最喜爱的浏览器并导航到http://localhost: 8761/查看我们已注册的应用程序。如下面的截图所示,仍然没有人。
在同一个屏幕里,服务器的状态如下:
请记住,运行多个Eureka服务器是正常的。在我们的示例中,我们只运行了一个,尽管这在实际用例中并不正常。
“countries-service
”微服务
现在我们有了服务器,让我们创建第一个客户。为此,我们将使用以下启动器创建另一个Spring Boot项目:
- Eureka Discovery
- Web
- Lombok
- H2
- JPA
正如我前面提到的,这个微服务将拥有数据库,并由资本服务公司查询以找到一个国家的首都。
这个项目的显著之处在于Spring引导文件application.properties
。
spring.application.name = countries-service
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka
server.port = 8000
# JPA Configuration
spring.jpa.show-sql = true
spring.h2.console.enabled = true
如您所见,通过参数eureka.client.serviceUrl.defaultZone
,我们可以指定eureka服务器的位置。Spring Boot会自动检测到您有可用的Eureka发现包,并尝试向相应的服务器注册。
要使用Eclipse在端口8001上启动application countries服务的第二个实例,请转到Run菜单中的Run configurations选项,并在第一次运行应用程序时复制Eclipse为countries服务创建的内容。在tab参数中,我们添加参数--server.port=8001
在下面的屏幕截图中,您可以看到如果我们启动这个程序的两个实例,一个在端口8000上,另一个在端口8001上,我们可以看到在Eureka服务器中有多个实例。用于register的名称是application.properties
文件的变量spring.application.name
中声明的应用程序名称。
因此,我们看到application-countries
服务有两个实例,都是在宿主端口chuchi中提出的—一个在端口8000
上,另一个在端口8001
上。
这个简单的应用程序将使用H2数据库进行数据持久化,我只有一个名为countries的简单表,我们将通过JPA访问它。表结构在com.profesorp.countriesservice.entities.Countries.java
中定义
在capitalsServiceControllert
类中,定义了以下入口点。
1. 获取请求. / {country}
接收:Country code. ( 'it is', 'eu', 'in' ....)
返回:CapitalsBean
类型的对象
2. 获取请求. / time/{time}
设置输入/{country}
在返回结果之前暂停的时间。
“capitals-service
”微服务
此服务调用上述服务来请求一个国家的所有数据。它只显示呼叫的首都、国家和服务港。
我们需要以下启动器:
- Eureka Discovery
- Feign
- Lombok
- Web
首先,与前面的服务一样,application.properties
文件将包含以下内容:
spring.application.name = capitals-service
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka
server.port = 8100
也就是说,我们定义应用程序名,然后指定Eureka服务器的位置和它必须注册的位置,最后指定您将听到程序的端口。
使用RestTemplate
为了发出RESTful请求,countries-services
使用org.springframework.web.client
包中的类RestTemplate
。
@GetMapping("/template/{country}")
public CapitalsBean getCountryUsingRestTemplate(@PathVariable String country) {
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("country", country);
ResponseEntity<CapitalsBean> responseEntity = new RestTemplate().getForEntity(
"http://localhost:8000/{country}",
CapitalsBean.class,
uriVariables );
CapitalsBean response = responseEntity.getBody();
return response;
}
如图所示,只需将请求中发送的变量放入一个hashmap
中,在本例中,该hashmap只是国家。然后它创建一个对象ResponseEntity
,调用静态函数RestTemplate.getForEntity()
,作为参数传递URL、存储响应的类以及请求中发送的变量。
然后我们捕获CapitalsBean
对象,我们在ResponseEntity
的Body
对象中使用它。
但是,使用这种方法有一个问题:我们编写了程序中不同微服务实例所在的URL。我们还需要编写大量的代码来进行简单的调用。
简单的请求使用Feign
一种更优雅的方法是使用假动作。Feign是Spring的一个工具,它允许我们使用声明性函数进行调用。
要使用Feign,我们必须在类中包含标签@EnableFeignClients
。在本例中,我们将它放在classCapitalsServiceApplication
中
@SpringBootApplication
@EnableFeignClients("com.profesorp.capitalsservice")
public class CapitalsServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CapitalsServiceApplication.class, args);
}
}
如果没有将任何参数传递给标记@EnableFeignClients
,请在主包中查找Feign的客户机。如果我们输入一个值,只在发送的包中查找客户机。因此,在这个示例中,只需查看packagecom.profesorp.capitalsservice
现在我们可以用interfaceCapitalsServiceProxy
定义客户端
@FeignClient(name="simpleFeign",url="http://localhost:8000/")
public interface CapitalsServiceProxySimple {
@GetMapping("/{country}")
public CapitalsBean getCountry(@PathVariable("country") String country);
}
首先,类被标记为@FeignClient
,指定服务器所在的URL以及它必须调用的URL。请注意,我们只编写主机名和端口(localhost:8000
)。必须设置参数名称,但其内容不重要。
然后我们定义不同的入口点。在我们的例子中,只定义了一个,但是我们可以包括对/time/{time}
的调用。
要使用此客户端,我们只需在程序中输入以下代码:
@Autowired
private CapitalsServiceProxySimple simpleProxy;
@GetMapping("/feign/{country}")
public CapitalsBean getCountryUsingFeign(@PathVariable String country) {
CapitalsBean response = simpleProxy.getCountry(country);
return response;
}
Spring注入变量CapitalsServiceProxySimple
,然后调用函数getCountry()
。
干净多了,对吧?假设我们的REST服务器有许多入口点,我们将节省大量的样板代码。
但是,我们仍然存在这样一个问题:地址服务器是用我们的代码编写的,这使得我们无法访问同一服务的不同实例,并且我们的微服务将无法真正扩展。
使用Eureka服务器发出Feign请求
为了解决这个问题,我们将不输入服务器地址,而是输入应用程序的名称,Spring Boot将调用Eureka服务器,询问服务所在的地址。
为此,我们创建一个外部接口,如下所示:
@FeignClient(name="countries-service")
public interface CapitalsServiceProxy {
@GetMapping("/{country}")
public CapitalsBean getCountry(@PathVariable("country") String country);
}
如您所见,我们没有指定服务的地址,只是简单地输入了名称。在这种情况下,countries-service
记录在Eureka应用服务器中。
这将依次对一个实例和另一个实例执行。因此,第一个请求将转到端口8000
,下一个请求将转到端口8001
。
因此,我们的应用程序将自动使用所有服务实例。
设置RIBBON
在Feign下使用包装Ribbon,这才是真正的要求。默认情况下,Ribbon使用RoundRobinRule
规则。此规则将依次选择Eureka显示的每个实例,与每个实例的响应时间无关
如果您希望使用其他三个可用规则中的任何一个,甚至定义一个新规则,那么我们必须为Ribbon创建一个配置类,如下所示:
import org.springframework.context.annotation.Bean;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.NoOpPing;
import com.netflix.loadbalancer.WeightedResponseTimeRule;
public class RibbonConfiguration {
@Bean
public IPing ribbonPing() {
return new NoOpPing();
}
@Bean
public IRule ribbonRule() {
return new WeightedResponseTimeRule();
}
}
在函数ribbonRule()
中,如果希望平衡逻辑考虑每个实例的响应时间,则返回对象weightedResponseTimeRules
。
现在,要指定要使用此类配置Ribbon,请添加标签:
@RibbonClient(name=“countries service”,configuration=RibbonConfiguration.class)
到我们的class CapitalsServiceApplication
@SpringBootApplication
@EnableFeignClients
@RibbonClient(name="countries-service", configuration = RibbonConfiguration.class)
public class CapitalsServiceApplication {
....
}
为了检查这是如何执行的,我们使用service-countries
服务的call/time/{time}
对监听端口8001
的服务器建立了10毫秒的暂停,对监听端口8000
的服务器建立了300毫秒的暂停
> curl localhost:8001/time/10
> curl localhost:8000/time/300
假设我们在Linux上工作,我们可以使用下面的Bash代码生成100个请求。
COUNTER=0; while [ $COUNTER -lt 100 ]; do
curl http://localhost:8100/es
let COUNTER=COUNTER+1
done
过了一会儿,我们看到通过调用http://localhost:8100/puertos
.
如您所见,对端口8001
到端口8000
的请求更多,考虑到端口8000
的延迟为300毫秒,而8001
只接收到10毫秒,这是正常的。
在本文的结尾,我要说通过直接使用restemplate
,Ribbon可以毫无伪装地使用,但是关于这个案例的研究我将留到另一个时间。
另外,为了测试和平衡,我使用了Docker;可以在我的GitHub上找到的源代码:https://github.com/chuchip/springEureka
您将在countries-service
项目的application.properties
文件中看到以下行:
eureka.client.serviceUrl.defaultZone:http://eurekaserver:8761/eureka
server.port=${SERVER_PORT}
这还用于使用环境变量SERVER_PORT
(每个实例必须侦听的端口)动态定义何时将容器释放给Docker。
原文地址:https://dzone.com/articles/microservices-in-spring-eureka
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2149.html
暂无评论