【Eureka】建立注册中心、消费者、提供者

一、demo结构

 二、pom文件

(1)父工程

    <parent>
        <groupId>org.springframework.boot</groupId>
        <version>2.2.0.RELEASE</version>
        <artifactId>spring-boot-starter-parent</artifactId>
    </parent>

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

(2)注册中心

使用的是netflix-eureka-server

    <dependencies>
        <!--        eureka server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--        web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--        test-->
        <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>
        <!--        security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

(3)提供者

使用的是netflix-eureka-client

    <dependencies>
        <!--        eureka server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--        web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--        test-->
        <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>

(3)消费者

使用的是netflix-eureka-client

    <dependencies>
        <dependency>
            <groupId>com.jane</groupId>
            <artifactId>service-provider</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--        eureka server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
<!--        lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--        web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--        test-->
        <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>

二、注册中心

(1)单机

关于设置prefer-ip-address和instance-id:

如果不设置,就会直接显示电脑的名字:

如果设置启用ip注册地址,和设置好实例的名字,就可以改成 ip:端口 的形式

server:
  port: 8761
spring:
  application:
    name: eureka-server

eureka:
  instance:
    hostname: eureka01 #这个主机名是指eureka注册中心的主机地址嘛?
    prefer-ip-address: true #启用ip注册,到时候那个信息显示的就不是电脑地址,是ip地址
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    register-with-eureka: false #不将自己注册到中心
    fetch-registry: false #不从中心获取服务注册信息
    service-url:
    #暴露注册地址
      defaultZone: http://localhost:8761/eureka/
#如果应用角色是 注册中心,且 单节点-》关闭 register-with-eureka 和 fetch-registry

(2)集群

和单机的区别:要将register-with-eureka和fetch-registry设置为true或者直接删掉

service-url要写入的是其他注册中心的地址,多个用逗号分隔

作为同一个集群,spring.application.name要一致

server1:

server:
  port: 8761
spring:
  application:
    name: eureka-server

eureka:
  instance:
    hostname: eureka01 #这个主机名是指eureka注册中心的主机地址嘛?
    prefer-ip-address: true #启用ip注册,到时候那个信息显示的就不是电脑地址,是ip地址
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
    #暴露注册地址
      #相互注册
      defaultZone: http://localhost:8762/eureka/

server2:

server:
  port: 8762
spring:
  application:
    name: eureka-server

eureka:
  instance:
    hostname: eureka01 #这个主机名是指eureka注册中心的主机地址嘛?
    prefer-ip-address: true #启用ip注册,到时候那个信息显示的就不是电脑地址,是ip地址
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
    #暴露注册地址
      #相互注册
      defaultZone: http://localhost:8761/eureka/

(3)启动类

要用@EnableEurekaServer标志为注册中心开启

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

三、提供者

(1)单机

client.service-url用逗号将所有注册中心写入

server:
  port: 7071

spring:
  application:
    name: sevice-product

eureka:
  instance:
    prefer-ip-address: true #启用ip注册,到时候那个信息显示的就不是电脑地址,是ip地址
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
      #注册集群相互注册,提供者/消费者是都注册到中心里
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

(2)集群

server1和上面一样

server2只需要修改server.port,和server1不同即可

(3)启动类

因为yml文件已经配置好了eureka,所以可以不用写@EnableEurekaClient

@SpringBootApplication
//开启eureka client注解,如果版本已经配置了注册中心,就会默认开启该注解
//约定>配置
//@EnableEurekaClient
public class ServiceProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderApplication.class,args);
    }
}

(4)pojo,service,controller

a. pojo

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Serializable {
    private Integer id;
    private String productName;
    private Double productPrize;
    private Integer productNum;
}

b.service

public interface ProductService {
    /**
     * 查询商品列表
     * @return
     */
    List<Product> selectProductList();
}

@Service
public class ProductServiceImpl implements ProductService{
    /**
     * 查询商品列表
     * @return
     */
    @Override
    public List<Product> selectProductList() {
        return Arrays.asList(
                new Product(1,"手机",4000D,5),
                new Product(2,"电脑",8000D,7),
                new Product(3,"耳机",400.92D,100)
        );
    }
}

 c.controller

@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    private ProductService productService;

    @GetMapping("/list")
    public List<Product> selectProductList(){
        return productService.selectProductList();
    }
}

四、消费者

(1)yml

    register-with-eureka和 registry-fetch-interval-seconds :当这个消费者只有消费者的身份,不是同时担任提供者,就可以不注册到注册中心,每间隔一定的时间去服务器拉信息

server:
  port: 9090

spring:
  application:
    name: sevice-consumer

#只是消费的服务,本身不提供服务
eureka:
  client:
    service-url:
      #注册集群相互注册,提供者/消费者是都注册到中心里
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
    register-with-eureka: false #不注册到注册中心
    registry-fetch-interval-seconds: 10 #client间隔10s去服务器拉信息

(2)启动类

消费者的启动类不需要加@注解

(3)pojo、service、controller

a.pojo

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {
    private Integer id;
    private String orderNo;
    private String orderAddress;
    private Double totalPrize;
    private List<Product> productList;

}

b.service

public interface ProductService {
    /**
     * 查询商品列表
     * @return
     */
    List<Product> selectProductList();
}

消费服务:三种形式:1、DiscoveryClient 2、LoadBalancerClient 3、@LoadBalanced

2和3和负载均衡ribbon有关

1、DiscoveryClient

请求【商品微服务】时,需要访问“提供者”,要在注册中心寻找【商品微服务】,得到主机名端口号作URL的拼接,再去请求获得结果

用discoveryClient.getServices()获取服务列表

用discoveryClient.getInstances(”服务名“)获取”服务名“对应服务

用s1.getHost()和s1.getPort()获取服务的端口号和主机名,再拼接请求的Url

用restTemplate.exchange(请求url,get/post/delete,请求参数,返回的结果类型转换),得到请求结果

注意:其中restTemplate,需要在启动类中加入

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

------------------------------------- 

@Service
public class OrderServiceImpl implements OrderService{
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 源数据对象,发现注册中心的服务列表,根据名称选择服务,
     */
    @Autowired
    private DiscoveryClient discoveryClient;
    @Autowired
    private LoadBalancerClient loadBalancerClient; //负载均衡ribbon

    @Override
    public Order selectOrderById(Integer id) {
        return new Order(id,"order-001","中国",31998D,selectProductListByDiscoveryClient());
    }

    private List<Product> selectProductListByDiscoveryClient(){
        StringBuffer sb=null;

        //获取服务列表
        List<String> serviceIds = discoveryClient.getServices();
        if (CollectionUtils.isEmpty(serviceIds)) {
            return null;
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("sevice-product");
        if (CollectionUtils.isEmpty(instances)){
            return null;
        }

        ServiceInstance s1 = instances.get(0);
        sb=new StringBuffer();
        sb.append("http://"+s1.getHost()+":"+s1.getPort()+"/product/list");

        ResponseEntity<List<Product>> response = restTemplate.exchange(
                sb.toString(),
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }
        );
        return response.getBody();

    }

2、LoadBalancerClient

中间请求【商品微服务】的代码有变化,不需要再通过获取服务列表-》获取具体服务

直接用loadBalancerClient.choose("sevice-product");得到具体服务

注意:要@Autowired    private LoadBalancerClient loadBalancerClient; //负载均衡ribbon

import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;

------------------------------------- 

    private List<Product> selectProductListByDiscoveryClient(){
        StringBuffer sb=null;

        //获取服务列表
        ServiceInstance si = loadBalancerClient.choose("sevice-product");
        if (si==null){
            return null;
        }

        sb=new StringBuffer();
        sb.append("http://"+si.getHost()+":"+si.getPort()+"/product/list");
        System.out.println(sb.toString());

        ResponseEntity<List<Product>> response = restTemplate.exchange(
                sb.toString(),
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }
        );
        return response.getBody();

    }

3、@LoadBalanced

直接使用restTemplate.exchange()即可,但是里面的URL的拼接是由 微服务名 代替了主机名端口号http://sevice-product/product/list

注:在启动类的RestTemplate中要添加

    @Bean
    @LoadBalanced //负载均衡注解
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    private List<Product> selectProductListByDiscoveryClient(){
        ResponseEntity<List<Product>> response = restTemplate.exchange(
                //地址是由服务项目的名称写下
                "http://sevice-product/product/list",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }
        );
        return response.getBody();

    }

 五、Eureka自我保护机制

通过renew发送心跳,看eureka是否可用,每30s一个心跳包,若90s没有收到回复,服务会被删除停服的原因:1、自己把服务关闭 2、网络问题

触发自我保护机制条件:15分钟失败比例低于85%,就不会过期,提示警告
直接停服:

无论什么原因,只要服务挂掉,就把服务从注册中心移除【默认是自我保护开启】

【把自我保护关闭】在注册中心的yml文件修改:

eureka:
  server:
    enable-self-preservation: false #false不开启自我保护,true开启自我保护
    eviction-interval-timer-in-ms: 60000 #清理间隔:毫秒,60s就会清理一次

优雅停服:

自己停掉的服务,会从注册中心移除;但因网络原因挂掉的服务,注册中心不会移除

需要在提供者的pom文件中添加acuator依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

提供者服务的yml文件修改:

#度量指标监控与健康检查
management:
  endpoints:
    web:
      exposure:
        include: shutdown #开启shutdown端点访问 ‘*‘所有端点(除shutdown外)
  endpoint:
    shutdown:
      enabled: true #优雅停服

六、注册中心与Security

在注册中心的pom文件中添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

在yml文件中添加访问注册中心时的用户名、密码

spring:
  security:
    user:
      name: root
      password: 123456

然后在注册中心、消费者、提供者的yml文件,都要把defaultZone作修改

defaultZone: http://root:123456@localhost:8762/eureka/

改为http://用户名:密码@主机名:端口号/eureka

注册中心添加security配置类

解决多个注册中心跨域注册问题,方法:

csrf防御机制 get post delete都是有风险的,没有csrf token会拦截
(1)忽略/eureka/**的请求
(2)保持密码验证,同时禁用csrf

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().ignoringAntMatchers("/eureka/**");
    }
}

猜你喜欢

转载自blog.csdn.net/kanseu/article/details/125645190