preface

The previous article provided a brief introduction to SpringBoot+SpringCloud building microservices. But it’s only a Demo application. This time, we will build this set of services according to the actual production requirements.

Swagger application

As mentioned last time, we used PostMan to simulate the request by calling our OWN HTTP interface, which is not a problem in normal debugging, but when we need to coordinate development with the front-end, the efficiency is relatively low.

Generally speaking, projects that are now separated from the front and back end usually have the back end interface first.

The back end will first define the interface (input and output parameters), the front end will determine whether it meets the requirements, and then the back end will start to write implementation, so that the overall efficiency is much higher.

The problem is that frequently changing the interface definition during the interface definition phase without a document or something like that makes it difficult to communicate and debug the front end.

Based on this demand, there are various solutions on the Internet, such as Ali’s RAP is a good example.

But springCould gives us swagger, a more convenient tool for developing springCloud projects.

The actual effect is as follows:

01.png

Configuration swagger

Taking SBC-ORDER as an example, I divided the project into three modules:

├ ─ ─ the order// Order service implementation│ ├─ SRC/Main ├─ Order - API/ / internal API
│   ├── src/main
├── order-client                             // External clientAPI│ ├─ SRC/Main ├─.Flag School ── LICENSE Flag SchoolCopy the code

Because the implementation is written in the ORDER module, you only need to configure it in that module.

I need to add dependencies first, since I rely on them in the ORDER module:

<dependency>
    <groupId>com.crossoverJie</groupId>
    <artifactId>order-api</artifactId>
    <version>${target.version}</version>
</dependency>Copy the code

Order-api again relies on:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <scope>compile</scope>
</dependency>Copy the code

You then need to configure a SwaggerConfig

@configuration @enablesWagger2 /** ConditionalOnExpression("'${swagger.enable}' == 'true'")
public class SwaggerConfig {


    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.crossoverJie.sbcorder.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("sbc order api")
                .description("sbc order api")
                .termsOfServiceUrl("http://crossoverJie.top")
                .contact("crossoverJie")
                .version("1.0.0") .build(); }}Copy the code

That’s the basic information for swagger. Then start the project and enter http://ip:port/swagger-ui.html#/ in the address bar. You can see the list of interfaces as shown in the figure above. Click the parameter example shown in the figure below to invoke the interface.

02.jpg

Custom switch Swagger

Swagger’s convenience can bring us many benefits, but the slightest mistake can cause problems.

For example, if swagger could be accessed via IP in a production environment, the consequences would be disastrous. So we need the flexibility to switch swagger on and off.

This can be done using Spring’s conditional configuration (conditional configurations can be configured to exist in the application and cancelled once certain conditions are met) :

@ConditionalOnExpression("'${swagger.enable}' == 'true'")Copy the code

This annotation means that a given SpEL expression evaluates to true before swagger’s bean is created.

Swagger. Enable This configuration is configured in application.properties:

# Enable enable swagger SwaggerCopy the code

So when we are in production we just need to change this configuration to false.

Ps: More Spring conditional configuration:

ConditionalOnBean // ConditionalOnMissingBean // ConditionalOnMissingBean // ConditionalOnClass //Classpath has the specified class @ ConditionalOnMissingClass / / lack of specified class in the Classpath @ ConditionalOnExpression / / given Spring Expression Language (SpEL) Expression for computing resultstrueConditionalOnJava // the Java version matches a specific value or a range of values @conditionalonJNDI // the JNDI location given in the parameter must exist. ConditionalOnProperty @conditionalonResource // The Classpath has the specified resource @ ConditionalOnWebApplication / / this is a Web application @ ConditionalOnNotWebApplication / / this is not a Web application (see SpringBoot practical)Copy the code

High availability Eureka

In the previous article, Eureka was used as a service registry, where all producers register services and consumers access services.

However, all the previous ones are single nodes, which is a huge risk in the production environment. We must make the registry highly available and set upEurekaThe cluster.

The idea is that each Eureka registers with the other as an application, thus forming a highly available service registry.

In actual production there would be one server per registry, but for demonstration purposes I will start both registries locally, but with different ports.

First, you need to configure a host locally:

127.0.0.1 node1 2Copy the code

This allows access to both node1 and node2 to be called locally (without host, of course, but via IP, which is not obvious).

Add two configuration files to SBC-service:

application-node1.properties:

spring.application.name=sbc-service
server.port=8888
eureka.instance.hostname=node1

## Do not register yourself with the registry
#eureka.client.register-with-eureka=false
#
## No retrieval service is required
#eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://node2:9999/eureka/Copy the code

application-node2.properties:

Name = sbC-service server.port=9999 eureka.instance.hostname=node2 ## Do not register yourself with the registry #eureka.client.register-with-eureka=false ## # No retrieval service is required #eureka.client.fetch-registry=false eureka.client.serviceUrl.defaultZone=http://node1:8888/eureka/Copy the code

The most important of these are:

eureka.client.serviceUrl.defaultZone=http://node2:9999/eureka/
eureka.client.serviceUrl.defaultZone=http://node1:8888/eureka/Copy the code

The two applications register with each other.

For startup we follow: Java -jar sbC-service-1.0.0-snapshot.jar –spring.profiles. Active =node1 start, Will be in accordance with the incoming node1 or 2 to read application – node1. Properties, application – 2. The properties of these two configuration files (configuration file must be in accordance with the application – {name}. Prope Rties way named).

Start the two registries separately and see the following:

03.jpg


04.jpg

You can see that the two registries are registered with each other. Registered in service when you just need to will address both plus: eureka. Client. ServiceUrl. DefaultZone = http://node1:8888/eureka/, http://node2:9999/eureka/

You can try to close one of them when the service is called, and the service can still be called normally.

Feign is called declaratively

Let’s talk about service invocations. Last time I mentioned that you can use the Ribbon to make service invocations, but it’s obviously not as simple and direct as RPC invocations.

For this reason, we use Feign to make declarative calls as simple as calling a normal method.

order-client

I divided the application into three modules order, order-API and Order-client, among which the client module is the key.

Take a look at the contents. There is only one interface:

@RequestMapping(value="/orderService")
@FeignClient(name="sbc-order")
@RibbonClient
public interface OrderServiceClient extends OrderService{


    @ApiOperation("Get order Number")
    @RequestMapping(value = "/getOrderNo", method = RequestMethod.POST)
    BaseResponse<OrderNoResVO> getOrderNo(@RequestBody OrderNoReqVO orderNoReq) ;
}Copy the code

Note the @feignClient annotation, where the name is the name of the application specified in the spring.application. properties file.

It inherits an OrderService in the order-API module. Let’s take a look at the contents of the order-API.

order-api

There is only one interface:

@RestController
@Api("Order Service API")
@RequestMapping(value = "/orderService")
@Validated
public interface OrderService {

    @ApiOperation("Get order Number")
    @RequestMapping(value = "/getOrderNo", method = RequestMethod.POST)
    BaseResponse<OrderNoResVO> getOrderNo(@RequestBody OrderNoReqVO orderNoReq) ;
}Copy the code

This interface serves two purposes.

  1. To the realcontrollerTo implement.
  2. toclientInterface for inheritance.

The class relationship is as follows:

05.jpg

Notes there’s nothing to say about all of this, it’s easy to understand.

order

Order is the module that implements the interface, just like controller. Let’s see how to use client for declarative calls:

This time look at the SBC-user project, where the SBC-order service is called. The user module relies on order-client:

<dependency>
    <groupId>com.crossoverJie</groupId>
    <artifactId>order-client</artifactId>
</dependency>Copy the code

Specific call:

    @Autowired
    private OrderServiceClient orderServiceClient ;

    @Override
    public BaseResponse<UserResVO> getUserByFeign(@RequestBody UserReqVO userReq) {
        // Invoke the remote service
        OrderNoReqVO vo = new OrderNoReqVO() ;
        vo.setReqNo(userReq.getReqNo());
        BaseResponse<OrderNoResVO> orderNo = orderServiceClient.getOrderNo(vo);

        logger.info("Remote return :"+JSON.toJSONString(orderNo));

        UserRes userRes = new UserRes() ;
        userRes.setUserId(123);
        userRes.setUserName("Zhang");

        userRes.setReqNo(userReq.getReqNo());
        userRes.setCode(StatusEnum.SUCCESS.getCode());
        userRes.setMessage("Success");

        return userRes ;
    }Copy the code

You can see that all you need to do is inject the Order service from the Order-Client package.

Call in swagger of SBC-client:

06.jpg


07.jpg

The order service returned an error because I did not pass the appId.

conclusion

When an application needs to expose the interface to the outside world, it needs to provide a client package in this way for consumer use.

In fact, the application itself also needs to do high availability, just like Eureka high availability, and then start one or more services on different servers and register with Eureka cluster.

We will continue to talk about zuul gateway, fault tolerance, circuit breaker and so on. Welcome to discuss.

Project: github.com/crossoverJi…

Blog: Crossoverjie.top.

weixinchat.jpg