Spring Boot微服务示例

时间:2020-01-09 10:44:31  来源:igfitidea点击:

在本文中,我们将看到一个带有Eureka的Spring Boot Microservices示例,该示例用于服务注册和发现服务。我们将有两个分别作为微服务开发的服务"用户"和"帐户"。

当大型整体应用程序分为两个或者多个微服务时,这些微服务可能需要相互交互。为此,这些微服务需要了解彼此的存在,并且应该能够相互查找。此过程称为服务发现。 Netflix创建了一个名为Eureka的工具,可以用作发现服务器,因为我们需要在Eureka服务器上注册微服务。

因此,在这个Spring Boot Microservices示例中,我们将创建3个单独的Spring Boot应用程序,其中两个用于User和Account功能,另一个用于Eureka Server。

Eureka Server的Spring Boot应用程序

首先,我们将创建一个用于配置Eureka Server的Spring Boot项目,该应用程序充当服务注册表。

我们需要为Eureka Server添加的启动程序是spring-cloud-starter-netflix-eureka-server

Maven依赖项– pom.xml

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/>
  </parent>
  <groupId>com.theitroad</groupId>
  <artifactId>springeureka</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>EurekaServer</name>
  <description>Eureka Server project</description>

  <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

应用类别

具有main方法的应用程序类。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaServerApplication.class, args);
  }
}

在应用程序类中,除了@SpringBootApplication注释之外,还添加了另一个注释@EnableEurekaServer。

@EnableEurekaServer注释表示我们要运行Eureka服务器。通过查看对Spring Cloud Eureaka的依赖关系,Spring Boot可以自动将应用程序配置为服务注册表。

Eureka服务器配置

在application.properties中放入以下内容。

server.port=8761
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

server.port配置Eureka Server运行的端口。
对于独立实例,我们也不希望Eureka Server成为客户端,这就是为什么这两个条目
eureka.client.register-with-eureka = false
eureka.client.fetch-registry = false

运行尤里卡服务器

Eureka服务器应用程序已准备就绪,我们可以运行EurekaServerApplication类来启动Eureka服务器。

如果一切正常,我们应该收到以下消息

2020-03-12 14:53:16.457  INFO 14400 --- [      Thread-10] e.s.EurekaServerInitializerConfiguration : Started Eureka Server
2020-03-12 14:53:16.503  INFO 14400 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8761 (http) with context path ''
2020-03-12 14:53:16.507  INFO 14400 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8761
2020-03-12 14:53:19.314  INFO 14400 --- [           main] o.n.s.EurekaServerApplication            : Started EurekaServerApplication in 30.203 seconds (JVM running for 33.929)

我们可以通过访问URLhttp:// localhost:8761 /来查看Eureka Server控制台。

如我们所见,目前没有实例在Eureka中注册。这就是创建Spring Boot微服务并将其注册到Eureka Server的下一个任务。

Spring Boot Account应用程序

为帐户微服务创建另一个Spring Boot项目,必须添加eureka客户端的启动程序相关性才能将该微服务注册为Eureka客户端。

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

休息控制器类

我们将添加一个控制器,该控制器具有查找传递的EmployeeId的所有帐户的功能。

import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AccountController {
	
  @GetMapping(value="/accounts/{empId}") 
  public List<Account>getAccounts(@PathVariable String empId) { 
    System.out.println("EmpId------" + empId);
    return findByEmpId(empId); 
  }

  private List<Account> findByEmpId(String empId){
    List<Account> accountList = getEmployees();
    List<Account> empList = new ArrayList<>();
    for(Account account :  accountList) {
      if(account.getEmpId().equals(empId))
        empList.add(account);
    }
    return empList;
  }
	
  private List<Account> getEmployees(){
    List<Account> accountList = new ArrayList<>();
    accountList.add(new Account("1", "AC1", "MT"));
    accountList.add(new Account("1", "AC2", "IN"));
    accountList.add(new Account("2", "AC3", "IN"));
    return accountList;
  }
}

理想情况下,应该有一个用于业务逻辑的Service类,但是在这里它是在Controller类本身中完成的。我们还可以看到有一种虚拟方法来获取帐户,而不是访问数据库,以便将重点放在微服务之间的交互上。

DTO等级

还有一个Account类,它充当DTO或者模型bean。

public class Account {
  private String empId;
  private String accountId;
  private String branch;
  Account(){
    
  }
  Account(String empId, String accountId, String branch){
    this.empId = empId;
    this.accountId = accountId;
    this.branch = branch;
  }
  public String getEmpId() {
    return empId;
  }
  public void setEmpId(String empId) {
    this.empId = empId;
  }
  public String getAccountId() {
    return accountId;
  }
  public void setAccountId(String accountId) {
    this.accountId = accountId;
  }
  public String getBranch() {
    return branch;
  }
  public void setBranch(String branch) {
    this.branch = branch;
  }
}

应用类别

import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;

@SpringCloudApplication
public class SpringBootAccountApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootAccountApplication.class, args);
  }
}

应用程序类在此处用@SpringCloudApplication进行注释,而不是常规的@SpringBootApplication注释。

@SpringCloudApplication注释扩展了@SpringBootApplication,因此它提供了自动配置,组件扫描和Bean管理,就像@SpringBootApplication一样。它还添加了@EnableDiscoveryClient注释以向Eureka和@EnableCircuitBreaker注册,以启用CircuitBreaker实现,作为回退。

Eureka客户端的配置

还将以下属性添加到application.properties文件中,以将Account Microservice注册为Eureka客户端。

eureka.client.service-url.default-zone=http://localhost:8761/eureka
server.port=9000
spring.application.name=account

eureka.client.service-url.default-zone属性告诉我们的微服务在哪里寻找Eureka Server。

使用spring.application.name可以给微服务一个逻辑名称。

服务器端口配置为9000,因此此帐户应用程序在端口9000上运行。

将帐户微服务注册为Eureka客户端
运行SpringBootAccountApplication类以启动此RESTful服务。它将自动注册为Eureka客户。我们可以验证是否在控制台上看到了消息。

2020-03-12 15:23:58.585  INFO 12416 --- [  restartedMain] o.s.c.n.e.s.EurekaServiceRegistry        : Registering application ACCOUNT with eureka with status UP
2020-03-12 15:23:58.588  INFO 12416 --- [  restartedMain] com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1584006838588, current=UP, previous=STARTING]
2020-03-12 15:23:58.597  INFO 12416 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_ACCOUNT/user:account:9000: registering service...
2020-03-12 15:23:58.940  INFO 12416 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9000 (http) with context path ''
2020-03-12 15:23:58.945  INFO 12416 --- [  restartedMain] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 9000
2020-03-12 15:23:59.194  INFO 12416 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_ACCOUNT/user:account:9000 - registration status: 204
2020-03-12 15:24:02.128  INFO 12416 --- [  restartedMain] o.n.a.SpringBootAccountApplication       : Started SpringBootAccountApplication in 31.85 seconds (JVM running for 35.175)

验证Eureka服务器

如果刷新Eureka Serverhttp:// localhost:8761 /的URL,现在应该看到实例已注册。实例的名称与使用以下属性配置为逻辑名称的名称相同。

spring.application.name =帐户

Spring Boot用户应用程序

我们需要创建的另一个微服务是用户服务,因此创建另一个项目。再次添加相同的启动程序依赖关系,以将该微服务注册为Eureka客户端。

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

休息控制器类

我们将添加一个带有showEmployees方法的UserController类,该方法又调用Account微服务以获取传递的员工ID的所有关联帐户。

@RestController
public class UserController {
  @Autowired
  private RestTemplate restTemplate;
  @GetMapping(value="/{id}")
  public List<Account> showEmployees(@PathVariable("id") String id) {
    System.out.println(id);
//  List<Account> accounts = new RestTemplate().exchange(
//    "http://localhost:9000/accounts/{empId}", HttpMethod.GET, null, new
//     ParameterizedTypeReference<List<Account>>(){}, id).getBody();
        
    List<Account> accounts = restTemplate.exchange(
      "http://ACCOUNT/accounts/{empId}", HttpMethod.GET, null, new
      ParameterizedTypeReference<List<Account>>(){}, id).getBody();
        
    for(Account acct : accounts) {
      System.out.println(acct.getEmpId());
      System.out.println(acct.getAccountId());
      System.out.println(acct.getBranch());
    }

    return accounts;        	
  }
    
  //public List<Account> defaultAccounts(String id) {
  //	return Collections.emptyList();
  //}
}

restTemplate.exchange()是用于远程调用另一个微服务的方法。

  • restTemplate.exchange()的第一个参数是帐户微服务的网址-" http:// ACCOUNT / accounts / {empId}"
  • 第二个参数指定它是HTTP Get命令。
  • 第三个参数指定要写入请求的实体(标题和/或者主体)。由于我们没有传递任何请求实体,因此它为null。
  • 第四个参数指定响应的类型。
  • 第五个参数指定要在模板中扩展的变量。我们在那里传递ID,它将替换URL中的{empId}。

使用功能区负载平衡器

在上述方法中,我们可以看到用于调用微服务的URL是http:// ACCOUNT / accounts / {empId},尽管我们也可以使用http:// localhost:9000 / accounts / {empId},但这会硬编码该位置不好

为了避免这种硬编码,我们使用了可与Eureka集成的Netflix Ribbon服务。我们需要做的是标记一个RestTemplate bean,使其配置为使用LoadBalancerClient,为此,我们可以按以下方式创建RestTemplate bean。

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration 
public class Config { 
  @Bean 
  @LoadBalanced
  RestTemplate restTemplate() { 
    return new RestTemplate(); 
  } 
}

一旦有了此负载均衡的restTemplate实例,便可以在URL中使用该服务的逻辑名,该逻辑名用于向Eureka注册该服务。这就是我们使用此URL http:// ACCOUNT / accounts / {empId}来访问Account MicroService的方式。

应用类别

import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;

@SpringCloudApplication
public class SpringBootUserApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootUserApplication.class, args);
  }
}

如我们在这里再次看到的,使用@SpringCloudApplication注释。

Eureka客户端的配置

还将以下属性添加到application.properties文件中,以将User Microservice注册为Eureka客户端。

eureka.client.service-url.default-zone=http://localhost:8761/eureka
spring.application.name=user

运行SpringBootUserApplication以启动User MicroService。它将自动注册为Eureka客户。我们可以通过查看控制台上的消息来验证。

2020-03-12 16:24:00.228  INFO 9844 --- [  restartedMain] o.s.c.n.e.s.EurekaServiceRegistry        : Registering application USER with eureka with status UP
2020-03-12 16:24:00.231  INFO 9844 --- [  restartedMain] com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1584010440231, current=UP, previous=STARTING]
2020-03-12 16:24:00.240  INFO 9844 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_USER/user:user: registering service...
2020-03-12 16:24:00.402  INFO 9844 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_USER/user:user - registration status: 204
2020-03-12 16:24:00.572  INFO 9844 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-12 16:24:00.577  INFO 9844 --- [  restartedMain] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8080
2020-03-12 16:24:03.278  INFO 9844 --- [  restartedMain] com.theitroad.user.SpringBootUserApplication   : Started SpringBootUserApplication in 28.889 seconds (JVM running for 33.647)

如果刷新Eureka Serverhttp:// localhost:8761 /的URL,则应该同时看到两个已注册为Eureka客户端的MicroService。

微服务之间的通信

现在,我们已经创建并运行了两个MicroSerivces。这两个微服务都已在Eureka注册,因此可以使用Eureka发现这些服务。

现在,当我们访问URL http:// localhost:8080/1时,它将由SpringBootUser应用程序中UserController的showEmployees()方法提供服务。从那里使用restTemplate.exchange()方法与帐户服务进行通信。

在交换方法中传递的URL(http:// ACCOUNT / accounts / {empId})触发负载平衡和发现Eureka客户端的整个过程,从日志消息中可以明显看出。

2020-03-12 16:36:37.733  INFO 9844 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-03-12 16:36:37.915  INFO 9844 --- [nio-8080-exec-1] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-ACCOUNT
2020-03-12 16:36:37.916  INFO 9844 --- [nio-8080-exec-1] c.netflix.loadbalancer.BaseLoadBalancer  : Client: ACCOUNT instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2020-03-12 16:36:37.963  INFO 9844 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2020-03-12 16:36:38.090  INFO 9844 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-03-12 16:36:38.098  INFO 9844 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client ACCOUNT initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[user:9000],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:user:9000;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 05:30:00 IST 1970;	First connection made: Thu Jan 01 05:30:00 IST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:org.springfHyman@theitroad5820f552
1
AC1
MT
1
AC2
IN
2020-03-12 16:36:38.995  INFO 9844 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
favicon.ico
2020-03-12 16:38:59.147  INFO 9844 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration
2020-03-12 16:43:59.150  INFO 9844 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration