Learn by doing, Spring Cloud-based microservice architecture best practices

This article is excerpted from the open source serverless PaaS Rainbond document, please click the link for the original text

Overview

Microservices are service units that can be deployed independently, scaled horizontally, and accessed independently (or have independent databases). Spring Cloud is an ordered collection of a series of frameworks used to manage microservices. Taking advantage of the development convenience of Spring Boot, Spring Cloud cleverly simplifies the development of distributed system infrastructure, such as service discovery registration, configuration center, message bus, load balancing, circuit breakers, etc., all of which can be achieved with Spring Boot's development style. key to start and deploy.

Spring Cloud does not reinvent the wheel, but combines the more mature and practical service frameworks developed by various companies, and repackages them through the Spring Boot style, shielding the complex configuration and implementation principles, and finally It provides developers with a set of distributed system development toolkits that are easy to understand, deploy and maintain.

Spring Cloud has many components, among which the core components are: Eureka (registry center), Hystrix (circuit breaker), Config (configuration center), Zuul (proxy, gateway) and so on.

Next, let's take a few demos to understand how Spring Cloud is built step by step.

For example source code, please click the source code

  • How to build Eureka
  • How to Build Hystrix
  • How to build Config
  • How to Build Zuul

How to build Eureka

Component introduction

The registry Eureka is a REST-based service for mutual discovery between various services. Any service that needs support from other services needs to be obtained through it; similarly, all services need to be registered here, so that other services can call them later. The advantage of Eureka is that you don't need to know what service to look for, you just need to go to the registration center to get it, and you don't need to know where the supported services are and how many services are supported, you can just come here and get them directly. In this way, stability is improved and the difficulty of building a microservice architecture is reduced.

project description

Call service A to request service B normally:

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/eureka-1.png" width="50%" height="50%"> </div>

With the service center, service A cannot directly call service B, but A and B register services in the registry and then discover each other. Service A calls service B through the registry:

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/eureka-2.png" width="75%" height="75%"> </div>

The above are just the mutual calls between two services. If there are more than a dozen or even dozens of services, any change in one of the projects may involve the restart of several projects, which is troublesome and error-prone. To obtain services through the registration center, you do not need to pay attention to the IP address of the project you are calling. It consists of several servers. You can directly go to the registration center to obtain the available services to call each time.

Deploy to cloud help

registration service

1. Add dependencies to pom

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

2. Add @EnableEurekaServerannotations to the startup code

@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurekaApplication {

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

3. Configuration file

By default, the service registry will also try to register itself as a client, so we need to disable its client registration behavior by application.propertiesadding the following configuration:

spring.application.name=spring-cloud-eureka

server.port=8000
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/

  • eureka.client.register-with-eureka: Indicates whether to register itself with Eureka Server, the default is true.
  • eureka.client.fetch-registry: Indicates whether to obtain registration information from Eureka Server, the default is true.
  • eureka.client.serviceUrl.defaultZone: Set the address for interacting with Eureka Server. Both query services and registration services need to rely on this address. The default is http://localhost:8761/eureka ; multiple addresses can be used, separated.

After starting the project, visit: http://localhost:8000/, you can see the following page, which has not found any services

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/eureka-3.png" width="100%" height="100%"> </div>

Service Provider (B)

1, pom package configuration

Create a springboot project and add the following configuration to pom.xml:

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-eureka</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

2. Configuration file

application.properties is configured as follows:

spring.application.name=spring-cloud-producer
server.port=9000
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

3. Startup class

@EnableDiscoveryClientAdd annotation to startup class

@SpringBootApplication
@EnableDiscoveryClient
public class ProducerApplication {

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

4. Service provision

Provide hello service:

@RestController
public class HelloController {
	
    @RequestMapping("/hello")
    public String index(@RequestParam String name) {
        return "hello "+name+",this is first messge";
    }
}

After adding @EnableDiscoveryClientannotations, the project has the function of service registration. After starting the project, you can see the SPRING-CLOUD-PRODUCER service on the registration center page.

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/eureka-4.png" width="100%" height="100%"> </div>

At this point, the service provider configuration is complete.

Service Consumer (A)

1, pom package configuration

consistent with service provider

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-eureka</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

2. Configuration file

application.properties is configured as follows:

spring.application.name=spring-cloud-consumer
server.port=9001
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

3. Startup class

Startup class additions @EnableDiscoveryClientand @EnableFeignClientsannotations:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {

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

}

  • @EnableDiscoveryClient: Enable service registration and discovery
  • @EnableFeignClients: enable feign to make remote calls

Feign is a declarative web service client. Using Feign can make it easier to write a Web Service client. Its use method is to define an interface, and then add annotations to it. It also supports JAX-RS standard annotations. Feign also supports pluggable encoders and decoders. Spring Cloud encapsulates Feign to support Spring MVC standard annotations and HttpMessageConverters. Feign can be combined with Eureka and Ribbon to support load balancing.

4. Feign call implementation

@FeignClient(name= "spring-cloud-producer")
public interface HelloRemote {
    @RequestMapping(value = "/hello")
    public String hello(@RequestParam(value = "name") String name);
}

  • name: The name of the remote service, and the name of the spring.application.name configuration

The methods in this class must be consistent with the method names and parameters in the controller in the remote service.

5. The web layer calls remote services

Inject HelloRemote into the controller layer and call it like a normal method.

@RestController
public class ConsumerController {

    @Autowired
    HelloRemote HelloRemote;
	
    @RequestMapping("/hello/{name}")
    public String index(@PathVariable("name") String name) {
        return HelloRemote.hello(name);
    }

}

At this point, the simplest example of service registration and invocation is completed.

test

Start the three projects spring-cloud-eureka, spring-cloud-producer, and spring-cloud-consumer in sequence.

Enter first: http://localhost:9000/hello?name=neoCheck whether the spring-cloud-producer service is normal

return:hello neo,this is first messge

It means that spring-cloud-producer starts normally and the services it provides are also normal.

Enter in the browser:http://localhost:9001/hello/neo

return:hello neo,this is first messge

It means that the client has successfully called the remote service hello through feign and returned the result to the browser.

{{site.data.alerts.callout_danger}} is deployed in the cloud helper, and you need to verify the following 3 points:<br>

  1. Port is enabled 外部访问<br>
  2. consumer is associated with producer<br>
  3. hello?name=neoand hello/neoadd 访问<br> after the generated url

The verification of the components after that is the same.

How to Build Hystrix

Component introduction

​In a microservice architecture, there are usually multiple service layer calls, and the failure of basic services may lead to cascading failures, resulting in the unavailability of the entire system. This phenomenon is called the service avalanche effect. The use of Hystrix (fuse) can avoid this problem.

The principle of the fuse is very simple, like a power overload protector. It can implement fail fast, if it detects many similar errors over a period of time, it forces multiple subsequent calls to fail fast and no longer access the remote server, preventing the application from constantly trying to perform operations that might fail , allowing the application to continue executing without waiting for errors to be fixed, or wasting CPU time waiting for a long timeout to occur. Circuit breakers can also enable the application to diagnose whether the error has been fixed, and if so, the application will try to invoke the operation again.

​When the number of Hystrix Command requests to the backend service fails over a certain percentage (default 50%), the circuit breaker will switch to the open state (Open). At this time, all requests will fail directly and will not be sent to the backend service. The circuit breaker remains at After a period of time in the open circuit state (default 5 seconds), it will automatically switch to the half open circuit state (HALF-OPEN). At this time, the return of the next request will be judged. If the request is successful, the circuit breaker will be switched back to the closed circuit state (CLOSED), otherwise it will be reset. Switch to the open state (OPEN). Hystrix's circuit breaker is like a fuse in our home circuit. Once the backend service is unavailable, the circuit breaker will directly cut off the request chain, avoiding sending a large number of invalid requests to affect the system throughput, and the circuit breaker has The ability to self-detect and recover.

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/hystrix-1.png" width="50%" height="50%"> </div>

project description

​By adding the Hystrix component to the service consumer to achieve the fuse effect, you only need to add Hystrix to the Eureka demo basis.

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/hystrix-2.png" width="50%" height="50%"> </div>

Hystrix acts on the service call side, so it needs to be added on A.

Deploy to Rainbond

Hystrix service

Because the circuit breaker only works on the service call side, we only need to change the consumer (A) service-related code according to the sample code in the previous article. Because Feign already relies on Hystrix, there is no need to make any changes to the maven configuration.

1. Add this entry to the configuration file application.properties:

feign.hystrix.enabled=true

2. Create a callback class

Create the HelloRemoteHystrix class to inherit the callback method with HelloRemote:

@Component
public class HelloRemoteHystrix implements HelloRemote{

    @Override
    public String hello(@RequestParam(value = "name") String name) {
        return "hello" +name+", this messge send failed ";
    }
}

3. Add fallback attribute

Add the HelloRemotespecified fallback class to the class, and return the content of the fallback class when the service is blown:

@FeignClient(name= "spring-cloud-producer",fallback = HelloRemoteHystrix.class)
public interface HelloRemote {

    @RequestMapping(value = "/hello")
    public String hello(@RequestParam(value = "name") String name);

}

4. Test

Then let's test it and see the effect.

Start the three projects spring-cloud-eureka, spring-cloud-producer, and spring-cloud-consumer in sequence.

Enter in the browser:http://localhost:9001/hello/neo

return:hello neo,this is first messge

Note that adding fuse-related information does not affect normal access. Next we manually stop the spring-cloud-producer project and test again:

Enter in the browser:http://localhost:9001/hello/neo

return:hello neo, this messge send failed

According to the returned result, the fusing is successful.

How to build Config

Component introduction

​ As online projects become larger and larger, each project is scattered with various configuration files. If a distributed development mode is adopted, the required configuration files will continue to increase as the number of services increases. Changes to a certain basic service information will cause a series of updates and restarts, which is inconvenient for project maintenance, and Spring Cloud Config solves this problem.

business description

​Currently Config supports git and svn as the repository for storing configuration files. In this example, the git repository is used to store configuration files. The Config-client here is equivalent to service A and service B. Their configuration files are stored centrally, and their respective configuration files are obtained through Config-server.

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/config-1.png" width="50%" height="50%"> </div>

Deploy to Rainbond

git repository

First, a folder config-repo is created on github to store configuration files. In order to simulate the production environment, we create the following three configuration files:

// 开发环境
neo-config-dev.properties
// 测试环境
neo-config-test.properties
// 生产环境
neo-config-pro.properties

A property neo.hello is written in each configuration file, and the property value is hello im dev/test/pro. Next we start to configure the server side

###config-server端

1. Add dependencies

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-config-server</artifactId>
	</dependency>
</dependencies>

Just add the spring-cloud-config-server package reference.

2. Configuration file

server:
  port: 8040
spring:
  application:
    name: spring-cloud-config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/xxx     						# 配置git仓库的地址
          search-paths: config-repo                             # git仓库地址下的相对地址,可以配置多个,用,分割。
          username:                                             # git仓库的账号
          password:                                             # git仓库的密码

Spring Cloud Config also provides a way to store configuration locally. We just need to set the properties spring.profiles.active=native, and the Config Server will src/main/resourceretrieve the configuration files from the application's directory by default. The location of the configuration file can also be specified via spring.cloud.config.server.native.searchLocations=file:E:/properties/properties. Although Spring Cloud Config provides such a function, in order to support better content management and version control functions, it is recommended to use git.

3. Startup class

Start class addition @EnableConfigServer, activate support for configuration center

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

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

At this point, the server-side related configuration has been completed

4. Test the server side

First of all, we need to test whether the server side can read the configuration information on github and directly access:http://localhost:8001/neo-config/dev

The returned information is as follows:

{
    "name": "neo-config", 
    "profiles": [
        "dev"
    ], 
    "label": null, 
    "version": null, 
    "state": null, 
    "propertySources": [
        {
            "name": "https://github.com/goodrain-apps/spring-cloud-demo/config-repo/neo-config-dev.properties", 
            "source": {
                "neo.hello": "hello im dev update"
            }
        }
    ]
}

The above returned information includes the location, version, name of the configuration file, and the specific content of the configuration file, indicating that the server has successfully obtained the configuration information of the git repository.

If you directly view the configuration information in the configuration file, you can access: http://localhost:8001/neo-config-dev.properties, return:neo.hello: hello im dev

Modify neo-config-dev.propertiesthe configuration information in the configuration file as: neo.hello=hello im dev update, access it in the browser again http://localhost:8001/neo-config-dev.properties, and return to: neo.hello: hello im dev update. Note that the server side will automatically read the latest submitted content

The configuration file in the warehouse will be converted into a web interface, and the following rules can be followed for access:

  • /{application}/{profile}[/{label}]
  • /{application}-{profile}.yml
  • /{label}/{application}-{profile}.yml
  • /{application}-{profile}.properties
  • /{label}/{application}-{profile}.properties

Take neo-config-dev.properties as an example, its application is neo-config, and its profile is dev. The client will choose to read the corresponding configuration according to the filled parameters.

###Config-client端

It mainly shows how to obtain server-side configuration information in business projects

1. Add dependencies

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-config</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

Introduce spring-boot-starter-web package to facilitate web testing

2. Configuration file

Two configuration files need to be configured, application.properties and bootstrap.properties

application.properties is as follows:

spring.application.name=spring-cloud-config-client
server.port=8002

bootstrap.properties is as follows:

spring.cloud.config.name=neo-config
spring.cloud.config.profile=dev
spring.cloud.config.uri=http://localhost:8001/
spring.cloud.config.label=master

  • spring.application.name: corresponds to the {application} part
  • spring.cloud.config.profile: corresponds to the {profile} section
  • spring.cloud.config.label: The branch corresponding to git. This parameter is useless if the configuration center is using local storage
  • spring.cloud.config.uri: The specific address of the configuration center
  • spring.cloud.config.discovery.service-id: Specifies the service-id of the configuration center, which is easy to expand into a high-availability configuration cluster.

The above spring-cloud-related properties must be configured in bootstrap.properties so that the config part can be loaded correctly. Because the relevant configuration of config will precede application.properties, and the loading of bootstrap.properties will also precede application.properties.

3. Startup class

Start class addition @EnableConfigServer, activate support for configuration center

@SpringBootApplication
public class ConfigClientApplication {

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

The startup class only needs to be @SpringBootApplicationannotated

4. Web testing

Use @Valueannotations to get the value of server-side parameters

@RestController
class HelloController {
    @Value("${neo.hello}")
    private String hello;

    @RequestMapping("/hello")
    public String from() {
        return this.hello;
    }
}

After starting the project, access: http://localhost:8002/hello, return: hello im dev updateThe parameters have been correctly obtained from the server side. At this point, a complete server provides configuration services, and the example of the client obtaining configuration parameters is completed.

How to Build Zuul

Component introduction

​In the microservice architecture, back-end services are often not directly open to the caller, but routed to the corresponding service through an API gateway according to the requested url. When the API gateway is added, a wall is created between the third-party caller and the service provider. This wall communicates directly with the caller for permission control, and then distributes requests to the background server in a balanced manner. The component used for proxy scheduling is Zuul.

project description

​In the project, only Zuul provides external access, and Gateway dispatches requests to different back-end services through different requested urls

<div align=center> <img src="http://grstatic.oss-cn-shanghai.aliyuncs.com/images/acp/docs/bestpractice/microservice/zuul-1.png" width="75%" height="75%"> </div>

Deploy to Rainbond

Gateway

1. Add dependencies

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>

import spring-cloud-starter-zuulpackage

2. Configuration file

spring.application.name=gateway-service-zuul
server.port=8888

#这里的配置表示,访问/producer/** 直接重定向到http://域名/**
zuul.routes.baidu.path=/producer/**
zuul.routes.baidu.url=http://域名/

3. Startup class

@SpringBootApplication
@EnableZuulProxy
public class GatewayServiceZuulApplication {

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

Startup classes are added @EnableZuulProxyto support gateway routing.

4. Test

To start the gateway-service-zuul-simpleproject, first enter:http://localhost:8888/producer/hello?name=neo

return:hello neo,this is first messge

Indicates that the scheduling is successful.

summary

So far, we have completed the construction of several core components of Spring Cloud such as Eureka, Hystrix, Config, Zuul, etc. For more information, please pay attention to the Rainbond documentation .

Or refer to the cloud framework project, [Cloud Framework] Spring Cloud-based microservice architecture .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324463072&siteId=291194637