Introduction to Boot2.0 Chapter 16 Deployment, Testing and Monitoring

  • JUnit testing, use of Mockito

Bale

  • After creating the directory with war, the IDE will help to generate the required directory for the web application
    • webapp directory
    • Also add something to pom.xml
<?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>springboot</groupId>
	<artifactId>chapter15</artifactId>
	<version>0.0.1-SNAPSHOT</version>
    
	<packaging>war</packaging>

	<name>chapter15</name>
	<description>chapter15 project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</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>
  • mvn package
  • java -jar spring-0.0.1-snapshot.war
  • java -jar spring-0.0.1-snapshot.war --server.port=9080
  • To use a third-party non-embedded server, you need to initialize the Dispatcher yourself

    public class ServletInitializer extends SpringBootServletInitializer {
    
    	@Override
    	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    		return application.sources(Chapter15Application.class);
    	}
    
    }
    
    • mvc provides the implementation class of ServletContainerinitializer: SpringServletContainerInitializer
    • This class: It will traverse the implementation class of the WebApplicationInitializer interface.
    • Among them: SprigBootServletInitializer is its implementation class
  • Just copy xxx.war to the webapps directory of tomcat.

hot deployment

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
  • true dependencies will not be passed, other projects depend on the current project, and this hot deployment will not take effect on this project.
  • Hot deployment is passed, and LiveReload supports it.
  • Hot deployment There are many configurations, see for yourself

test

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
  • Support Jpa, MongoDB, Rest, Redis
  • Mock test
@RunWith(SpringRunner.class) //The loaded class is the operation of Spring combined with JUnit

//Start the test service with a random port. Configure Test Related Features
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)

public class Chapter16ApplicationTests {

	// Inject the user service class
    @Autowired
    private UserService userService = null;

    @Test
    public void contextLoads() {
        User user = userService.getUser(1L);
        // Determine if user information is empty
        Assert.assertNotNull(user);
    }
    
 // REST test template, provided automatically by Spring Boot
    @Autowired
    private TestRestTemplate restTemplate = null;

    // Test get user function
    @Test
    public void testGetUser() {
        // Request the currently started service, note the URI abbreviation
        User user = this.restTemplate.getForObject("/user/{id}",
                User.class, 1L);
        Assert.assertNotNull(user);
    }
    
    
    
    @MockBean
    private ProductService productService = null;

    @Test
    public void testGetProduct() {
        // Build dummy objects
        Product mockProduct = new Product();
        mockProduct.setId(1L);
        mockProduct.setProductName("product_name_" + 1);
        mockProduct.setNote("note_" + 1);
        // Specify Mock Bean methods and parameters
        BDDMockito.given(this.productService.getProduct(1L))
                // Specifies the returned virtual object
                .willReturn(mockProduct);
        
        // Mock test
        Product product = productService.getProduct(1L);
        Assert.assertTrue(product.getId() == 1L);
    }

}


	public Product getProduct(Long id) {
		throw new RuntimeException("Failed to support this method");
	}

mock test

  • During testing, use a dummy object to create a test method for testing
  • getProduct(1L) currently cannot schedule product microservices, mock can give a virtual product
  • @MockBean Mock test for that bean

actuator monitoring endpoint

		<dependency>
			<groupId>org.springframework.hateoas</groupId>
			<artifactId>spring-hateoas</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
  • hateoas is a complex constraint in the REST architectural style, a dependency for building mature REST services.

actuator endpoint description

  • health
  • httptrace latest trace information (100 by default)
  • info
  • mappings all mapping paths
  • scheduledtasks shows scheduled tasks
  • shutdown

http monitoring

  • http://localhost:8080/actuator/health
  • http://localhost:8080/actuator/beans needs to be enabled
  • Default values ‚Äč‚Äčexpose info and health
# Expose all endpoints info,health,beans
management.endpoints.web.exposure.include=*

#Do not expose this endpoint
management.endpoints.web.exposure.exclude=env


# By default all endpoints are not enabled, in this case you need to enable endpoints on demand
management.endpoints.enabled-by-default=false
# enable endpoint info
management.endpoint.info.enabled=true
# Enable endpoint beans
management.endpoint.beans.enabled=true
management.endpoint.health.enabled=true
management.endpoint.dbcheck.enabled=true
# Actuator endpoint prefix
management.endpoints.web.base-path=/manage

management.endpoint.health.show-details=when-authorized

management.health.db.enabled=true

View sensitive information

  • The above is fully exposed, very incomplete
@SpringBootApplication(scanBasePackages = "com.springboot.chapter16")
@MapperScan(basePackages = "com.springboot.chapter16", annotationClass = Mapper.class)
public class Chapter16Application extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // password encoder
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        // use memory storage
        auth.inMemoryAuthentication()
                // Set password encoder
                .passwordEncoder(passwordEncoder)
                // Register the user admin, the password is abc, and give the role permissions of USER and ADMIN
                .withUser("admin")
                // The encrypted password can be obtained by passwordEncoder.encode("abc")
                .password("$2a$10$5OpFvQlTIbM9Bx2pfbKVzurdQXL9zndm1SrAjEkPyIuCcZ7CqR6je").roles("USER", "ADMIN")

                // connection method and
                .and()

                // Register the user myuser, the password is 123456, and give the role permission of USER
                .withUser("myuser")
                // The encrypted password can be obtained by passwordEncoder.encode("123456")
                .password("$2a$10$ezW1uns4ZV63FgCLiFHJqOI6oR6jaaPYn33jNrxnkHZ.ayAFmfzLS").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Endpoints that need Spring Security protection
        String[] endPoints = {"auditevents", "beans", "conditions", "configprops", "env", "flyway",
                "httptrace", "loggers", "liquibase", "metrics", "mappings", "scheduledtasks",
                "sessions", "shutdown", "threaddump"};

        // Define the endpoints that need to be authenticated
        // http.requestMatcher(EndpointRequest.to(endPoints))
        http.authorizeRequests().antMatchers("/manage/**").hasRole("ADMIN")

                // Request to close page requires ROLE_ADMIN orange
                .antMatchers("/close").hasRole("ADMIN")

                .and().formLogin()

				.and()
				
                // Start HTTP Basic Authentication
                .httpBasic();
    }

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

http.
    requestMatcher(EndpointRequest.to(endPoints)).authorizeRequests().anyRequest().hasRole("ADMIN").
				and()
				.antMatchers("/close").authorizeRequests().anyRequest().hasRole("ADMIN");


.authorizeRequests().anyRequest() //After signing in

shutdown endpoint

management.endpoint.shutdown.enabled=true
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <!-- load Query document-->
    <script src="https://code.jquery.com/jquery-3.2.0.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#submit").click(function () {
                // request shutdown endpoint
                $.post({
                    url: "./actuator/shutdown",
                    // method after success
                    success: function (result) {
                        // Check request result
                        if (result != null || result.message != null) {
                            // print message
                            alert(result.message);
                            return;
                        }
                        alert("closure Spring Boot Application failed");
                    }
                });
            });
        });
    </script>
    <title>test close request</title>
</head>
<body>
<input id="submit" type="button" value="close the app"/>
</body>
</html>


@RestController
public class CloseController {
	@GetMapping("/close")
	public ModelAndView close(ModelAndView mv) {
		// Define the view name as close and let it jump to the corresponding JSP
		mv.setViewName("close");
		return mv;
	}
}

Configure endpoints

management.server.port=8080

# expose all endpoints
management.endpoints.web.exposure.include=*

# management.endpoints is public

# By default all endpoints are not enabled, in this case you need to enable endpoints on demand
.enabled-by-default=false

# enable endpoint info
.info.enabled=true

# Enable endpoint beans
.beans.enabled=true

# enable config endpoint
.configprops.enabled=true

# start env
.env.enabled=true

# enable health
.health.enabled=true

# enable mappings
.mappings.enabled=true

# enable shutdown
.shutdown.enabled=true

# Actuator endpoint prefix
.web.base-path=/manage

# Modify the request path of the original mapping endpoint to urlMapping
.web.path-mapping.mappings=request_mappings
  • http://localhost:8000/manage/health

    {
        "status":"UP",
        "details":{
            "www":{
                "status":"UP",
                "details":{
                    "message":"The current server has access to the World Wide Web."
                }
            },
            "diskSpace":{
                "status":"UP",
                "details":{
                    "total":302643146752,
                    "free":201992957952,
                    "threshold":10485760
                }
            },
            "db":{
                "status":"UP",
                "details":{
                    "database":"MySQL",
                    "hello":1
                }
            }
        }
    }
    

custom endpoint

// Let Spring scan the class
@Component
// define endpoints
@Endpoint(
		// endpoint id
		id = "dbcheck",
		// Whether the endpoint is enabled by default
		enableByDefault = true)
public class DataBaseConnectionEndpoint {
	
	private static final String DRIVER = "com.mysql.jdbc.Driver";
	
	@Value("${spring.datasource.url}")
	private String url = null;
	
	@Value("${spring.datasource.username}")
	private String username = null;

	@Value("${spring.datasource.password}")
	private String password = null;

	// An endpoint can only have one method annotated with @ReadOperation
	// It represents an HTTP GET request
	@ReadOperation
	public Map<String, Object> test() {
		Connection conn = null;
		Map<String, Object> msgMap = new HashMap<>();
		try {
			Class.forName(DRIVER);
			conn = DriverManager.getConnection(url, username, password);
			msgMap.put("success", true);
			msgMap.put("message", "Test database connection is successful");
		} catch (Exception ex) {
			msgMap.put("success", false);
			msgMap.put("message", ex.getMessage());
		} finally {
			if (conn != null) {
				try {
					conn.close(); // close database connection
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		return msgMap;
	}

}
management.endpoint.dbcheck.enabled=true
{"success":true,"message":"Test database connection is successful"}

Custom World Wide Web Health Metrics

http://localhost:8080/manage/health has been accessed above

// The monitoring server is capable of accessing the World Wide Web
@Component
public class WwwHealthIndicator extends AbstractHealthIndicator {
	// By monitoring Baidu server, see if you can access the Internet
	private final static String BAIDU_HOST = "www.baidu.com";
	// overtime time
	private final static int TIME_OUT = 3000;

	@Override
	protected void doHealthCheck(Builder builder) throws Exception {
		boolean status = ping();
		if (status) {
			// The health indicator is available and a message item is added
			builder.withDetail("message", "The current server has access to the World Wide Web.").up();
		} else {
			// The health indicator is no longer in service, and a message item is added
			builder.withDetail("message", "The World Wide Web is currently unavailable").outOfService();
		}
	}

	// Monitoring Baidu server access to determine whether access to the World Wide Web
	private boolean ping() throws Exception {
		try {
			// When the return value is true, the host is available, and false is not.
			return InetAddress.getByName(BAIDU_HOST).isReachable(TIME_OUT);
		} catch (Exception ex) {
			return false;
		}
	}

}

JMX monitoring

jconsole.exe

Select: org.springframework.boot—endpoint—health—click health

Tags: Testing monitor and control

Posted by xkaix on Wed, 01 Jun 2022 01:10:03 +0530