启动Eureka服务注册中心

  • 1、微服务的注册中心
  • 1.1、注册中心的主要作用
  • 1.2、常见的注册中心
  • 1.3、常见注册中心的异同点
  • 2、Eureka概述
  • 2.1、Eureka的基础知识
  • 2.2、Eureka的交互流程与原理
  • 3、搭建Eureka注册中心案例
  • 3.1、搭建Eureka服务中心
  • 3.2、服务注册中心管理后台
  • 4、客户端服务注册到Eureka注册中心
  • 4.1、商品模块服务注册
  • 4.1.1、创建ebuy-product模块(为了后续方便,可直接建成SpringBoot项目)
  • 4.1.2、在maven中引入`eureka-client`客户端相关依赖坐标
  • 4.1.3、在com.ebuy.product包下创建pojo、mapper、service、controller层
  • 4.1.4、**配置application.yml文件**
  • 4.1.5、修改启动类添加服务注册注解
  • 4.1.6、启动EbuyProductApplication 启动类向Eureka注册商品服务
  • 4.1.7、在地址栏访问controller层Restful接口地址`http://localhost:9011/product/816753`,火狐浏览器可以解析JSON数据,正合咱意:
  • 4.2、订单模块服务注册(步骤和上述商品微服务基本一致)
  • 4.2.1、创建ebuy-order模块(为了后续方便,可直接建成SpringBoot项目)
  • 4.2.2、引入eureka-client客户端依赖
  • 4.2.3、同样新建pojo、mapper、service、controller层
  • 4.2.4、配置application.yml文件
  • 4.2.5、修改启动类添加服务注册注解
  • 4.2.5、启动EbuyOrderApplication 启动类向Eureka注册服务
  • 5、跨服务调用
  • 5.1、RestTemplate介绍
  • 5.2、RestTemplate方法介绍
  • 5.3、通过RestTemplate调用微服务
  • 5.4、RestTemplate调用Rest微服务的弊端
  • 5.5、解决RestTemplate硬编码存在的问题
  • 5.6、Eureka的自我保护机制


1、微服务的注册中心

注册中心可以说是微服务架构中的“通讯录”,他记录了服务与服务地址之间的映射关系。在分布式架构中,所有的服务都会注册到这里,当一个服务需要调用其他服务时,在注册中心找到相应的服务地址即可调用。

java微服务启动配置nacos账号密码_RestTemplate

1.1、注册中心的主要作用

服务注册中心是微服务架构中非常重要的一个组件,在微服务架构中主要起到了协调者的一个作用。注册中心一般包含如下几个功能:

1、服务发现:

  • 服务注册/反注册:保存服务提供者和服务调用者的信息;
  • 服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功能;
  • 服务路由(可选):具有筛选整合服务提供者的能力。

2、服务配置:

  • 配置订阅:服务提供者和服务调用者订阅微服务相关的配置;
  • 配置下发:主动将配置推送给服务提供者和五福调用者。

3、服务健康检测:

  • 检测服务提供者的健康情况。

1.2、常见的注册中心

  • Zookeeper

zookeeper是一个分布式服务框架,是Apache Hadoop的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题。如:统一命名服务状态同步服务集群管理分布式应用配置项的管理等
简单来说:zookeeper = 文件系统 + 监听通知机制。

  • Eureka

Eureka是在Java语言上编写的,基于Restful Api开发的服务注册与发现组件,SpringCloud Netflix中的重要组件。

  • Consul

Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册的服务组件,采用Raft算法保证服务的一致性,且支持健康检查。

  • Nacos

Nacos是一个更易于构建云原生应用的动态服务发现,配置管理和服务管理平台。
简单来说:Nacos就是注册中心 + 配置中心的组合。
提供简单易用的特性集,帮助我们解决微服务开发必会设计到的服务注册与发现、服务配置、服务管理等问题。
Nacos还是Spring Cloud Alibaba组件之一,负责服务注册与发现。

1.3、常见注册中心的异同点

我们通过一张表格大致了解Eureka、Consul、Zookeeper之间的异同点,选择什么类型的服务注册与发现组件可以根据自身项目要求决定。

组件名

语言

CAP

一致性算法

服务健康检查

对外暴露接口

Eureka

Java

AP


可配支持

HTTP

Consul

Go

CP

Raft

支持

HTTP/DNS

Zookeeper

Java

CP

Paxos

支持

客户端

Nacos

Java

AP

Raft

支持

HTTP

2、Eureka概述

2.1、Eureka的基础知识

Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。

java微服务启动配置nacos账号密码_DiscoveryClient_02


上述简要描述了Eureka的基础架构,由三个角色组成:

  • Eureka

提供服务注册和发现

  • Service Provider

服务提供方;
将自身服务注册到Eureka注册中心,从而使服务消费方能够找到。

  • Service Consumer

服务消费方;
从Eureka注册中心获取注册服务列表,从而能够消费服务。

2.2、Eureka的交互流程与原理

java微服务启动配置nacos账号密码_eureka_03


上图来自Eureka官方的架构图,大致描述了Eureka集群的工作过程。图中包含的组件非常多,可能比较难以理解,下面各组件的介绍:

  • us-east-1c和us-east-1d和us-east-1e

属于zone(分区),他们都属于us-east-1这个region(地区)

  • Apllication Service

相当于服务提供者(属于客户端Eureka Client)

  • Application Cient

相当于服务消费者(属于客户端Eureka Client)

  • Make Remote Call

可以简单理解为调用Restful API

由上图可知,Eureka包含两个组件:Eureka Server 和 Eureka Client,它们的作用如下:

  • Eureka Client是一个Java客户端(包括服务提供者和消费者),用于简化与Eureka Server的交互;
  • Eureka Server是提供服务发现的能力,各个微服务启动时,会通过Eureka ClientEureka Server进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;
  • 微服务启动后,会周期性的向Eureka Server发送心跳(默认周期为30秒),以续约自己的在线信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务的节点(默认检测时长为90秒);
  • 每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册表的同步
  • Eureka Client会缓存Eureka Server中的信息,即使所有的Eureka Server节点都宕机,服务消费者依然可以使用缓存中的信息找到服务提供者。

综上所述,Eureka通过心跳检测健康检查客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。

3、搭建Eureka注册中心案例

3.1、搭建Eureka服务中心

(1)、创建ebuy-eureka模块(为了后续方便,可直接建成SpringBoot项目)

  • 继承ebuy-parent模块

(2)在maven中引入eureka-server依赖坐标

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

此处依赖的版本号尽量与所继承的parent项目的spring-boot-starter-parent版本号保持相同, 如果不同的话,要保证版本尽量相差不大,因为目前更高版本的SpringBoot对于netflix-eureka-server的兼容性还不是很完善。

java微服务启动配置nacos账号密码_eureka_04


为了后续一步到位,此处还有一个坑,如果你使用的是JDK8,目前来说是没有这方面的问题;如果是JDK9及以上版本就有问题了,就是JDK9以后的版本中没有关于JAXB-API默认的类路径的配置,因为JAXB-API是java ee的一部分,从jdk9开始java引入了模块的概念, 可以使用模块命令–add-modles java.xml.bind引入jaxb-api,当然还有一种简单粗暴的方法,就是在pom文件中引入以下依赖,以激活jaxb-api应用接口。

启动ebuy-eureka会出现以下错误:

java.lang.TypeNotPresentException: Type javax.xml.bind.JAXBContext not present

解决上述JAXBContext 不存在的异常:

<!--解决java.lang.TypeNotPresentException: Type javax.xml.bind.JAXBContext not present-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

(3) 配置application.yml

server:
  port: 9000 #端口号

eureka:
  instance: #eureka实例
    hostname: 127.0.0.1 #服务器ip地址
  client:
    register-with-eureka: false #是否将自己注册到注册中心(本身就监管所有服务,无需注册)
    fetch-registry: false #是否从注册中心获取服务列表(本身就监管所有服务,无需获取)
    serviceUrl: #此处有驼峰命名法的自动转换,也可以写成service-url,推荐使用驼峰命名
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#Eureka Client(客户端)与Eureka Server(服务端)进行交互的地址
serviceUrl: #承接上下文,才可以这样写
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

(4) 配置启动类

package com.ebuy;

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

@SpringBootApplication
@EnableEurekaServer //激活Eureka Server端配置
public class EbuyEurekaApplication {

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

@EnableEurekaServer注解: 激活Eureka Server端配置

3.2、服务注册中心管理后台

启动ebuy-eureka工程启动类,在地址栏输入地址localhost:9000,直接回车即可进入EurekaServer内置的管理控制台,显示效果如下,其实下图界面与dubbo-amdin的可视化界面虽然画面不同,但是功能基本是一样的,同样是做注册中心的监管工作:

java微服务启动配置nacos账号密码_RestTemplate_05

4、客户端服务注册到Eureka注册中心

4.1、商品模块服务注册

4.1.1、创建ebuy-product模块(为了后续方便,可直接建成SpringBoot项目)

  • 继承ebuy-parent模块

4.1.2、在maven中引入eureka-client客户端相关依赖坐标

<!--springcloud公共类依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>

        <!--eureka注册中心客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--mybatis整合springboot启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <!--mysql连接依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>

4.1.3、在com.ebuy.product包下创建pojo、mapper、service、controller层

  • pojo层创建实体类EasybuyProduct
package com.ebuy.product.pojo;

import lombok.Data; /*代表自动生成getter和setter方法*/
import lombok.ToString;/*代表自动生成toString方法*/
import java.io.Serializable;
import java.math.BigDecimal;

@Data
@ToString
public class EasybuyProduct implements Serializable {

    private Long epId;

    private String epName;

    private String epDescription;

    private BigDecimal epPrice;

    private Long epStock;

    private Long epcId;

    private Long epcChildId;

    private String epFileName;

    private static final long serialVersionUID = 1L;
}
  • 上述EasyBuyProduct实体类中使用了两个注解@Data和@ToString,分别代表自动生成getter、setter方法和toString()方法,这种方式使用的是lombok插件,可以极大的提升实体类的开发效率和节省编码空间。
  • 第一步:首先在settings的plugins中下载插件:lombok,安装后注意要重启IDEA才能生效:
  • java微服务启动配置nacos账号密码_java_06

  • 第二步:在ebuy-parent父级模块中导入lombok依赖坐标,后续只要继承ebuy-parent的模块,实体类都可以直接使用注解:
<!--lombok注解依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
  • 这样上述实体类上的注解就可以使用了!
  • 在mapper层创建EasyBuyProductMapper接口:
package com.ebuy.product.mapper;

import com.ebuy.product.pojo.EasybuyProduct;

public interface EasybuyProductMapper {

    /**
     * 根据商品id查询商品信息
     * @param epId
     * @return
     */
    EasybuyProduct selectByPrimaryKey(Long epId);
}
  • 在resources目录下新建com/ebuy/product/mapper目录,并在其下新建EasyBuyProductMapper.xml映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ebuy.product.mapper.EasybuyProductMapper" >
  <resultMap id="BaseResultMap" type="com.ebuy.product.pojo.EasybuyProduct" >
    <id column="ep_id" property="epId" jdbcType="DECIMAL" />
    <result column="ep_name" property="epName" jdbcType="VARCHAR" />
    <result column="ep_description" property="epDescription" jdbcType="VARCHAR" />
    <result column="ep_price" property="epPrice" jdbcType="DECIMAL" />
    <result column="ep_stock" property="epStock" jdbcType="DECIMAL" />
    <result column="epc_id" property="epcId" jdbcType="DECIMAL" />
    <result column="epc_child_id" property="epcChildId" jdbcType="DECIMAL" />
    <result column="ep_file_name" property="epFileName" jdbcType="VARCHAR" />
  </resultMap>

  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select ep_id, ep_name, ep_description, ep_price, ep_stock, epc_id, epc_child_id, ep_file_name from EASYBUY_PRODUCT
    where ep_id = #{epId,jdbcType=DECIMAL}
  </select>

</mapper>
  • 在service层创建EasybuyProductService业务逻辑层接口(和mapper层保持一致即可):
package com.ebuy.product.service;

import com.ebuy.product.pojo.EasybuyProduct;

/**
 * 业务逻辑层接口
 */
public interface EasybuyProductService {
	/**
     * 根据商品id查询商品信息
     * @param epId
     * @return
     */
    EasybuyProduct selectByPrimaryKey(Long epId);

}
  • 在service.impl层创建EasyBuyProductServiceImpl实现类:
package com.ebuy.product.service.impl;

import com.ebuy.product.pojo.EasybuyProduct;

@Service
@SuppressWarnings("all")
public class EasybuyProductServiceImpl implements EasybuyProductService {

	/**
     * 调用mapper层接口
     */
    @Autowired
    EasybuyProductMapper easybuyProductMapper;

    @Override
    public EasybuyProduct selectByPrimaryKey(Long epId) {
        return easybuyProductMapper.selectByPrimaryKey(epId);
    }
 }
  • 在controller层新建ProductContoller:
package com.ebuy.product.controller;

import com.ebuy.product.pojo.EasybuyProduct;
import com.ebuy.product.service.EasybuyProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/product")
public class ProductController {

	@Autowired
	private EasybuyProductService productService;

	@RequestMapping(value = "/{id}",method = RequestMethod.GET)
	public EasybuyProduct findById(@PathVariable Long id) {
		EasybuyProduct product = productService.selectByPrimaryKey(id);
 		return product;
	}
}

4.1.4、配置application.yml文件

server:
  port: 9011 #端口号

spring:
  application:
    name: ebuy-product #商品模块服务名称
  datasource:
    username: root #数据库用户名
    password: root #数据库密码
    driver-class-name: com.mysql.jdbc.Driver #mysql加载驱动
    url: jdbc:mysql://localhost:3306/ebuy?useUnicode=true&characterEncoding=utf8

mybatis:
  type-aliases-package: com.ebuy.product.pojo  #mybatis简化pojo实体类别名
  mapper-locations: com/ebuy/product/mapper/*.xml #mapper映射文件路径

logging:
  level:
    com.ebuy: DEBUG #日志级别

eureka:
  client:
    serviceUrl: #上述配置的ebuy-server的地址及端口
      defaultZone: http://127.0.0.1:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册(在注册中心显示名字以ip地址显示)

4.1.5、修改启动类添加服务注册注解

package com.ebuy;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@MapperScan("com.ebuy.product.mapper")
//@EnableEurekaClient  开启Eureka客户端
//@EnableDiscoveryClient  开启服务发现客户端
public class EbuyProductApplication {

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

从Spring Cloud Edgware版本开始, @EnableDiscoveryClient@EnableEurekaClient 可省略。只需加上相关依赖,并进行相应配置,即可将客户端微服务注册到服务发现组件上,但是上述的EurekaServer服务的启动类上必须要加上注解@EnableEurekaServer,表明当前服务是Eureka服务注册中心。

4.1.6、启动EbuyProductApplication 启动类向Eureka注册商品服务

java微服务启动配置nacos账号密码_RestTemplate_07

4.1.7、在地址栏访问controller层Restful接口地址http://localhost:9011/product/816753,火狐浏览器可以解析JSON数据,正合咱意:

java微服务启动配置nacos账号密码_RestTemplate_08

4.2、订单模块服务注册(步骤和上述商品微服务基本一致)

4.2.1、创建ebuy-order模块(为了后续方便,可直接建成SpringBoot项目)

  • 继承ebuy-parent模块

4.2.2、引入eureka-client客户端依赖

<!--springcloud相关依赖公共类-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>
        <!--springcloud 注册中心eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

4.2.3、同样新建pojo、mapper、service、controller层

  • 在mapper层新建EasyBuyOrderMapper接口
  • 在service层新建EasyBuyOrderService接口(和Mapper层保持一致即可)
  • 在service.impl包下新建EasyBuyOrderServiceImpl实体类
  • 在controller层新建OrderController类

4.2.4、配置application.yml文件

server:
  port: 9012 #端口号
spring:
  application:
    name: ebuy-order #服务名
logging:
  level:
    cn.ebuy: DEBUG

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:9000/eureka/
  instance:
    prefer-ip-address: true   #使用ip地址注册(在注册中心显示名字以ip地址显示)

4.2.5、修改启动类添加服务注册注解

package com.ebuy.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
//@EnableEurekaClient  开启Eureka客户端
//@EnableDiscoveryClient  开启服务发现客户端
public class EbuyOrderApplication {

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

4.2.5、启动EbuyOrderApplication 启动类向Eureka注册服务

java微服务启动配置nacos账号密码_eureka_09

5、跨服务调用

上述编写了基础的微服务,均已注册到了Eureka注册中心中。

java微服务启动配置nacos账号密码_eureka_10

现在有个需求,用户在下订单时需要调用商品微服务获取商品数据,通过上述案例我们应该知道这是跨服务模块之间的调用,细心的伙子肯定看到了上述我们在商品的controller层提供了供他人调用的HTTP接口,并采用的是Restful风格,所以可以在下订单的时候使用http请求的相关工具类完成,如常见的HttpClientOkHttp

其实spring还为我们提供了一个工具类RestTemplate

5.1、RestTemplate介绍

Spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了Restful的标准,我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用Restful服务的方式。

在Spring应用程序中访问第三方Rest服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与其他Spring模板类(eg:JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。

RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为,(eg:Apache HttpComponents、Netty或者OKHttp)等其他HTTP library。

考虑到RestTemplate类是为了调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、POST、PUT、DELETE和OPTIONS

RestTemplate类具有以下方法:

  • headForHeaders()
  • getForObject()
  • postForObject()
  • put()
  • delete()

5.2、RestTemplate方法介绍

该模板类的主要切入点为以下几个方法(并对应这HTTP的六个主要方法)

java微服务启动配置nacos账号密码_DiscoveryClient_11

5.3、通过RestTemplate调用微服务

现有个需求,用户在下订单时需要调用商品微服务获取商品数据,这个时候就需要在订单模块中调用商品模块。

(1)首选在pojo层也新建一个EasyBuyProduct实体类,用于跨服务调用接收结果:

package com.ebuy.order.pojo;
	
	import lombok.Data;
	import lombok.ToString;
	import java.io.Serializable;
	import java.math.BigDecimal;
	
	@Data
	@ToString
	public class EasybuyProduct implements Serializable {
	
	    private Long epId;
	
	    private String epName;
	
	    private String epDescription;
	
	    private BigDecimal epPrice;
	
	    private Long epStock;
	
	    private Long epcId;
	
	    private Long epcChildId;
	
	    private String epFileName;
	
	    private static final long serialVersionUID = 1L;
	}

(2)在ebuy-order的主启动类EbuyOrderApplication处配置RestTemplate,在服务启动的时候即将RestTemplate交给spring容器管理:

package com.ebuy.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
//@EnableEurekaClient  开启Eureka客户端
//@EnableDiscoveryClient  开启服务发现客户端
public class EbuyOrderApplication {

    /**
     * 配置RestTemplate交给spring管理
     * @return
     */
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

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

(3)在OrderController类中先注入RestTemplate类,然后编写调用product模块的方法:

package com.ebuy.order.controller;


import com.ebuy.order.pojo.EasybuyProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    RestTemplate restTemplate;

    /**
     * 直接跨项目调用
     * @param id
     * @PathVariable 将restful中的参数id映射到形参列表
     * @return
     */
    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public EasybuyProduct findById(@PathVariable Long id) {
        EasybuyProduct easybuyProduct=new EasybuyProduct();
        //通过restTemplate类的方法getForObejct调用product的Rest服务,并接收返回值
        easybuyProduct=restTemplate.getForObject("http://127.0.0.1:9011/product/"+id,EasybuyProduct.class);
       return easybuyProduct;
    }

}

(4)启动ebuy-prodcut和ebuy-order两个服务,在地址栏访问http://localhost:9012/order/816753

java微服务启动配置nacos账号密码_DiscoveryClient_12


上述结果显示说明ebuy-order模块成功调用ebuy-product的rest服务,并响应了结果!

5.4、RestTemplate调用Rest微服务的弊端

上述使用RestTemplate类的方法调用到了商品微服务的RESTFul API接口,但是我们可以很明显的看出,提供者的网络地址(ip,port)等被硬编码到了代码中,这种做法存在许多问题:

  • 应用场景有局限
  • 无法动态调整

5.5、解决RestTemplate硬编码存在的问题

需要通过注册中心动态的对服务注册服务发现
EurekaClient的注册,心跳及服务器上的注册信息获取在com.netflix.discovery.DiscoveryClient.initScheduledTasks()方法中实现。

DiscoveryClient默认获取服务器注册信息 间隔30秒,心跳时间30秒,Client启动以后40往服务器发送注册信息。

在OrderController类中使用DiscoveryClient重新编写服务调用:

package com.ebuy.order.controller;


import com.ebuy.order.pojo.EasybuyProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("/order")
public class OrderController {

    /**
     * 直接通过restTemplate调用远程服务,过于硬编码,不够灵活;
     * 通过DiscoveryClient默认获取服务器注册信息,
     * 动态调动所需要的服务(动态获取ip地址和端口)
     */
    @Autowired
    private DiscoveryClient discoveryClient;


    @RequestMapping(value = "/dc/{id}",method = RequestMethod.GET)
    public EasybuyProduct findDcById(@PathVariable Long id) {
        //所要调用的服务名
        List<ServiceInstance> list=discoveryClient.getInstances("ebuy-product");
        //我们目前只启动了一个服务,所以直接get(0)获取第一个服务即可
        ServiceInstance serviceInstance=list.get(0);
        /**
         * serviceInstance.getHost() 获取服务ip(动态)
         * serviceInstance.getPort() 获取服务端口(动态)
         */
        EasybuyProduct  easybuyProduct=restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/"+id,EasybuyProduct.class);
        return  easybuyProduct;
    }
}

我们在上述的两个微服务中已经配置了eureka-client相关配置,此时我们需要先启动EurekaServer服务注册中心,然后再启动eureka-client(product、order)两个客户端服务。

启动成功后,在浏览器地址栏访问地址http://localhost:9000/

java微服务启动配置nacos账号密码_微服务_13


说明客户端服务(product提供者和order消费者)已经成功注册到eureka注册中心了。

然后在地址栏访问http://localhost:9012/order/dc/816753

java微服务启动配置nacos账号密码_RestTemplate_14


动态调用成功,妥!!!

5.6、Eureka的自我保护机制

微服务第一次注册成功之后,每30秒会发送一次心跳将服务的实例信息注册到中心。通过EurekaServer该实例仍然存在。如果超过90秒没有发送更新,则服务器将从注册信息中将此服务剔除。

Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于80%,如果出现低于80%的情况(在单机调试的时候很容易满足,实际在生产环境中是由于网咯不稳定导致的),Eureka Server会将当前的实例注册信息保护起来,同时提示一串红字警告。保护模式主要用于一组Eureka Client客户端和Eureka Server服务端之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其注册表中的信息,不再删除服务注册信息中的数据(也就是不会注销任何微服务)。

验证完自我保护机制开启后,并不会马上呈现在web上,而是你人需等待5分钟(可以通过在yml文件中配置如下)

eureka:
  server:
    wait-time-in-ms-when-sync-empty: 5  #默认为5分钟

即5分钟后再次刷新注册中心可视化监管界限会出现以下提示信息:

java微服务启动配置nacos账号密码_java_15


也可以关闭自我保护机制:

server:
    enable-self-preservation: false #关闭自我保护机制

好了,今先到这吧!!!