Spring Cloud Eureka distributed development of the service registry, load balancing, declarative service call implementation

Introduction

This example describes the Spring Cloud series Eureka, so you can quickly get started load balancing, declarative services, service registry, etc.

Eureka Server

Eureka is Netflix sub-module, which is a REST-based services, for location services, the intermediate layer in order to achieve the cloud service discovery and failover.

Service registration and discovery services for micro-architecture, it is very important. With service discovery and registration, just use the identifier of the service will have access to the service without the need to modify the configuration file service call. This functionality is similar registry Dubbo, such Zookeeper.

Eureka using the CS design architecture. Eureka Server as a server service registration function, which is a service registry. The system is used in other micro-service client connections to Eureka Eureka Server and maintain a heartbeat connection

Eureka Server provides registration services services. After starting each service node is registered in the service in Eureka Server, Eureka Server service in the registry stores all available service node information.

Eureka Client is a Java client to simplify the interaction, the client Eureka Server and also to have a built-in algorithm polling load load balancer. After starting the application, send the heartbeat (default period 30 seconds) to the Eureka Server. If Eureka Server does not receive a heartbeat nodes in multiple heartbeat period, Eureka Server will be removed from the service registry service node information.

简单理解:各个微服务将自己的信息注册到server上,需要调用的时候从server中获取到其他微服务信息

Ribbon

Spring Cloud Ribbon is based on a set of end customers Netflix Ribbon achieve load balancing tool, and its main function is to provide load balancing algorithm client software, connect Netflix's middle tier service together.

Ribbon provides more load balancing strategy: such as polling, random, like response time weighting.

Feign

Feign is declarative, template-based HTTP client can invoke HTTP API faster and more elegant. Ribbon in some scenes and the like, is a request for processing data, but clearly more convenient to use when requesting entity class parameter, and also support for security, authorization control.
Feign is the integration of Ribbon, meaning that if the introduction of Feign, so Ribbon functionality can also be used, such as modifying load balancing strategy

Code

1. Create a eureka-server service registry

pom.xml pom configuration

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.easy</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>eureka-server</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <artifactId>cloud-feign</artifactId>
        <groupId>com.easy</groupId>
        <version>1.0.0</version>
    </parent>

    <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>
        </dependency>
    </dependencies>

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

application.yml profile

server:
    port: 9000

spring:
  application:
    name: eureka-server
    
eureka:
    instance:
        hostname: localhost   # eureka 实例名称
    client:
        register-with-eureka: false # 不向注册中心注册自己
        fetch-registry: false       # 是否检索服务
        service-url:
            defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/  # 注册中心访问地址

EurekaServerApplication.java startup class

package com.easy.eurekaServer;

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);
    }
}

2. Create the hello-service-api interfaces

Result.java unified entity return

package com.easy.helloServiceApi.vo;


import lombok.Getter;

import java.io.Serializable;

@Getter
public class Result implements Serializable {

    private static final long serialVersionUID = -8143412915723961070L;

    private int code;

    private String msg;

    private Object data;

    private Result() {
    }

    private Result(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    private Result(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }


    public static Result success() {
        return success(null);
    }

    public static Result success(Object data) {
        return new Result(200, "success", data);
    }

    public static Result fail() {
        return fail(500, "fail");
    }

    public static Result fail(int code, String message) {
        return new Result(code, message);
    }
}

Order.java Orders entity classes

package com.easy.helloServiceApi.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 订单类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {

    private String orderId;

    private String goodsId;

    private int num;

}

GoodsServiceClient.java statement Goods Services category

package com.easy.helloServiceApi.client;

import com.easy.helloServiceApi.vo.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(value = "hello-server")
public interface GoodsServiceClient {

    @RequestMapping("/goods/goodsInfo/{goodsId}")
    Result goodsInfo(@PathVariable("goodsId") String goodsId);
}

Goods.java commodity entity class

package com.easy.helloServiceApi.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * 商品类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Goods {

    private String goodsId;

    private String name;

    private String descr;

    // 测试端口
    private int port;
}

3. Create a hello-service-01 service providers (here to create three different service provider to do load balancing with)

GoodsController.java Goods Services Entrance


package com.easy.helloService.controller;

import com.easy.helloService.service.GoodsService;
import com.easy.helloServiceApi.model.Goods;
import com.easy.helloServiceApi.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/goods")
public class GoodsController {

    @Autowired
    private GoodsService goodsService;

    @RequestMapping("/goodsInfo/{goodsId}")
    public Result goodsInfo(@PathVariable String goodsId) {

        Goods goods = this.goodsService.findGoodsById(goodsId);
        return Result.success(goods);
    }
}

GoodsService.java Interface

package com.easy.helloService.service;

import com.easy.helloServiceApi.model.Goods;

public interface GoodsService {

    Goods findGoodsById(String goodsId);
}

GoodsServiceImpl.java implements the interface

package com.easy.helloService.service.impl;

import com.easy.helloService.service.GoodsService;
import com.easy.helloServiceApi.model.Goods;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class GoodsServiceImpl implements GoodsService {

    // 模拟数据库
    private static Map<String, Goods> data;

    static {
        data = new HashMap<>();
        data.put("1", new Goods("1", "华为", "华为手机", 8081));  //表示调用8081端口的数据,实际上数据会放在数据库或缓存中
        data.put("2", new Goods("2", "苹果", "苹果", 8081));
    }

    @Override
    public Goods findGoodsById(String goodsId) {
        return data.get(goodsId);
    }
}

HelloServiceApplication.java startup class

package com.easy.helloService;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class HelloServiceApplication {

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

application.yml profile, do 01 service port 8081

server:
    port: 8081

spring:
  application:
    name: hello-server
    
eureka:
    instance:
        instance-id: goods-api-8081
        prefer-ip-address: true # 访问路径可以显示 IP
    client:
        service-url:
            defaultZone: http://localhost:9000/eureka/  # 注册中心访问地址

4. Create a hello-service-02 Service Provider (posted 01 service and not the same place)

application.yml profile, do 02 service port 8082

server:
    port: 8082

spring:
  application:
    name: hello-server
    
eureka:
    instance:
        instance-id: goods-api-8082
        prefer-ip-address: true # 访问路径可以显示 IP
    client:
        service-url:
            defaultZone: http://localhost:9000/eureka/  # 注册中心访问地址

GoodsServiceImpl.java here deliberately set different data sources used to test the use of load balancing has not entered into force

package com.easy.helloService.service.impl;

import com.easy.helloService.service.GoodsService;
import com.easy.helloServiceApi.model.Goods;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class GoodsServiceImpl implements GoodsService {

    // 模拟数据库
    private static Map<String, Goods> data;

    static {
        data = new HashMap<>();
        data.put("1", new Goods("1", "华为", "华为手机", 8082));  //表示8082端口的数据,实际上数据会放在数据库或缓存中
        data.put("2", new Goods("2", "苹果", "苹果", 8082));
    }

    @Override
    public Goods findGoodsById(String goodsId) {
        return data.get(goodsId);
    }
}

5. Create a hello-service-02 Service Provider (posted 01 service and not the same place)

application.yml profile, do 02 service port 8082

server:
    port: 8083

spring:
  application:
    name: hello-server
    
eureka:
    instance:
        instance-id: goods-api-8083
        prefer-ip-address: true # 访问路径可以显示 IP
    client:
        service-url:
            defaultZone: http://localhost:9000/eureka/  # 注册中心访问地址

GoodsServiceImpl.java here deliberately set different data sources used to test the use of load balancing has not entered into force

package com.easy.helloService.service.impl;

import com.easy.helloService.service.GoodsService;
import com.easy.helloServiceApi.model.Goods;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class GoodsServiceImpl implements GoodsService {

    // 模拟数据库
    private static Map<String, Goods> data;

    static {
        data = new HashMap<>();
        data.put("1", new Goods("1", "华为", "华为手机", 8083));  //表示8083端口的数据,实际上数据会放在数据库或缓存中
        data.put("2", new Goods("2", "苹果", "苹果", 8083));
    }

    @Override
    public Goods findGoodsById(String goodsId) {
        return data.get(goodsId);
    }
}

6. Create feign-consumer services to consumers, the introduction Ribbon implement a service call load balancing and implement declarative service call

pom.xml configuration

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.easy</groupId>
    <artifactId>feign-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>feign-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <artifactId>cloud-feign</artifactId>
        <groupId>com.easy</groupId>
        <version>1.0.0</version>
    </parent>

    <dependencies>

        <!-- springmvc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!-- ribbon -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <!-- feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.easy</groupId>
            <artifactId>hello-service-api</artifactId>
            <version>0.0.1</version>
        </dependency>
    </dependencies>

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

The introduction of openfeign, ribbon, eureka-client and other dependent, openfeign used to implement declarative service call, ribbon used to achieve load balancing, eureka-client for registration, discovery service

RestConfiguration.java Configuration

package com.easy.feignConsumer.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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 RestConfiguration {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    /**
     * 随机选取负载均衡策略
     * @return
     */
    @Bean
    public IRule testRule() {
        return new RandomRule();
    }
}

GoodsService service class interface

package com.easy.feignConsumer.service;

import com.easy.helloServiceApi.model.Goods;
import com.easy.helloServiceApi.vo.Result;

public interface GoodsService {
    Result placeGoods(Goods goods);
}

GoodsServiceImpl.java implementation class

package com.easy.feignConsumer.service.impl;

import com.easy.feignConsumer.service.GoodsService;
import com.easy.helloServiceApi.client.GoodsServiceClient;
import com.easy.helloServiceApi.model.Goods;
import com.easy.helloServiceApi.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class GoodsServiceImpl implements GoodsService {

    @Autowired
    private GoodsServiceClient goodsServiceClient;

    @Override
    public Result placeGoods(Goods order) {

        Result result = this.goodsServiceClient.goodsInfo(order.getGoodsId());

        if (result != null && result.getCode() == 200) {
            log.info("=====获取本地商品====");
            log.info("接口返回数据为==>{}", ToStringBuilder.reflectionToString(result.getData()));
        }
        return result;
    }
}

GoodsController.java controller

package com.easy.feignConsumer.controller;

import com.easy.feignConsumer.service.GoodsService;
import com.easy.helloServiceApi.model.Goods;
import com.easy.helloServiceApi.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/goods")
public class GoodsController {

    @Autowired
    private GoodsService orderService;

    @RequestMapping("/place")
    public Result placeGoods(Goods goods) {
        Result result = this.orderService.placeGoods(goods);
        return result;
    }
}

FeignConsumerApplication.java informers startup class

package com.easy.feignConsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients(basePackages = {"com.easy"})
@EnableEurekaClient
@SpringBootApplication
public class FeignConsumerApplication {

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

application.yml profile

server:
  port: 8100

spring:
  application:
    name: feign-consumer

eureka:
  instance:
    instance-id: order-api-8100
    prefer-ip-address: true # 访问路径可以显示 IP
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/  # 注册中心访问地址

Examples of Use

Run created five service

A service registry, three service providers, a consumer service

Check into the service registry service

Address bar: http: // localhost: 9000 /, we see five services are running successfully registered and the state (UP state), the effect is as follows:

  • Application listed under the two services (FEIGN-CONSUMER, HELLO-SERVER)
  • It represents the number of available services are listed in Availability Zones (1 and 3 are shown here)
  • Status column displays service status, UP indicates that the service is running, the internal address followed behind, respectively, services: goods-api-8100 (customer service), goods-api-8081 (service providers 01), goods-api-8082 ( service provider 02), goods-api-8083 (service providers 03)

Call Interface Testing

Address bar: http: // localhost: 8100 / goods / place goodsId = 1, return data results?:

{
code: 200,
msg: "success",
data: {
goodsId: "1",
name: "华为",
descr: "华为手机",
port: 8081
}
}
  • Multi-page refresh a few times, we found that port will vary randomly 808,180,828,083, to express our random load balancer in force
  • Free to turn off two or a service provider, refresh the page interface function has no effect, can return to normal data, to achieve high availability

Declarative services and non-declarative services comparison

Non-declarative services calling code

    @Test
    public void testFeignConsumer() {
        Goods goods = new Goods();
        goods.setGoodsId("1");
        Result result = this.restTemplate.getForObject("http://HELLO-SERVER/goods/goodsInfo/" + goods.getGoodsId(), Result.class);
        log.info("成功调用了服务,返回结果==>{}", ToStringBuilder.reflectionToString(result));
    }

Consumer end of each request method requires splicing the service request URL address, there is a problem and this is not hard-coded in line with the thinking of object-oriented programming

Declarative service call

@FeignClient(value = "hello-server")
public interface GoodsServiceClient {

    @RequestMapping("/goods/goodsInfo/{goodsId}")
    Result goodsInfo(@PathVariable("goodsId") String goodsId);
}
    @Autowired
    private GoodsServiceClient goodsServiceClient;

    @Override
    public Result placeGoods(Goods order) {
        Result result = this.goodsServiceClient.goodsInfo(order.getGoodsId());
        return result;
    }

Through a simple interface to write and insert annotations, you can define the information parameters, formats, addresses, and other good HTTP request, the remote interface calls, so will our code and use more scalable, complex object-oriented programming.

data

Guess you like

Origin www.cnblogs.com/tqlin/p/11532661.html