SpringMvc 项目转 SringBoot

来源:互联网 发布:php过滤所有html标签 编辑:程序博客网 时间:2024/05/19 08:03

个人实践 和 备忘

项目结构 :

这里写图片描述

分布式项目, 利用maven构建了多模块, 开发还是 ssm , 项目中 有mq 和 redis , memcached。都是通过 配置文件加载,开头是web.xml 引入spring-x.xml , spring-x.xml 再去引入 其他配置文件,znf4-common-config 用来配置 *.properties 文件,mq ,jdbc , redis 系统等相关配置。

现引入Boot 先关jar包 :

我是一次性引入了很多个相关jar , 有些也没用上,你们自行去除就行。

    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <spring-boot.version>1.3.1.RELEASE</spring-boot.version>        <tomcat.version>8.0.28</tomcat.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context-support</artifactId>        </dependency>        <!-- spring-boot start -->        <dependency>            <!-- Import dependency management from Spring Boot -->            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-dependencies</artifactId>            <version>${spring-boot.version}</version>            <type>pom</type>            <scope>import</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter</artifactId>            <version>${spring-boot.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>            <version>${spring-boot.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-jdbc</artifactId>            <version>${spring-boot.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-remote-shell</artifactId>            <version>${spring-boot.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <version>${spring-boot.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>            <version>${spring-boot.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-tomcat</artifactId>            <version>${spring-boot.version}</version>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>org.apache.tomcat.embed</groupId>            <artifactId>tomcat-embed-jasper</artifactId>            <version>${tomcat.version}</version>            <scope>provided</scope>        </dependency>        <!--spring boot end-->

加入启动模块 :

这里写图片描述

ApplicationStarter .java

package com.znf4.app;import org.springframework.boot.Banner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.ImportResource;/** * Created by agui on 15/10/26. */@SpringBootApplication@ImportResource("classpath:spring/spring-context.xml")public class ApplicationStarter {    public static void main(String[] args) {        SpringApplication app = new SpringApplication(ApplicationStarter.class);        app.setAdditionalProfiles();        app.setBannerMode(Banner.Mode.LOG);        app.run(args);    }}

上面类中 注解 :@ImportResource(“classpath:spring/spring-context.xml”) ,用来引入:

这里写图片描述

说明 :spring-contet-service.xml 这个配置文件在我其他模块,该模块通过它又引入了一些其他 xml 配置。

JvmConfiger.java

package com.znf4.app;import java.net.*;import java.util.ArrayList;import java.util.Enumeration;import java.util.ResourceBundle;/** * Created by agui on 15/11/2. */public class JvmConfiger {    public final static ResourceBundle RESOURCE = ResourceBundle.getBundle("application");    public static String getJvmConfig() {        StringBuilder buffer = new StringBuilder();        append(buffer, RESOURCE, "jvm.mem");        append(buffer, RESOURCE, "jvm.log");        append(buffer, RESOURCE, "jvm.gc");        append(buffer, RESOURCE, "jvm.others");        append(buffer, RESOURCE, "jvm.args");        return buffer.toString();    }    public static String getRun() {        StringBuilder buffer = new StringBuilder();        buffer.append("nohup java ");        buffer.append(String.format("`java -jar %s -jvm`",getPackageName()));        buffer.append(" -jar ");        buffer.append(getPackageName());        buffer.append(" > ");        buffer.append("./console.log");        buffer.append(" 2>&1 &");        return buffer.toString();    }    private static String getPackageName(){        return getAppConfig("project.name") + "." + getAppConfig("project.package");    }    private static StringBuilder append(StringBuilder buffer, ResourceBundle resource, String key) {        if (resource.containsKey(key)) {            String value = resource.getString(key);            if (value.contains("hostname=%s")) {                value = String.format(value, getLocalAddress());            }            return buffer.append(" " + value);        }        return buffer;    }    public static String getAppConfig(String key) {        if (RESOURCE.containsKey(key)) {            return RESOURCE.getString(key);        }        return null;    }    public static String getLocalAddress() {        try {            // Traversal Network interface to get the first non-loopback and non-private address            Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();            ArrayList<String> ipv4Result = new ArrayList<String>();            ArrayList<String> ipv6Result = new ArrayList<String>();            while (enumeration.hasMoreElements()) {                final NetworkInterface networkInterface = enumeration.nextElement();                final Enumeration<InetAddress> en = networkInterface.getInetAddresses();                while (en.hasMoreElements()) {                    final InetAddress address = en.nextElement();                    if (!address.isLoopbackAddress()) {                        if (address instanceof Inet6Address) {                            ipv6Result.add(normalizeHostAddress(address));                        } else {                            ipv4Result.add(normalizeHostAddress(address));                        }                    }                }            }            // prefer ipv4            if (!ipv4Result.isEmpty()) {                for (String ip : ipv4Result) {                    if (ip.startsWith("127.0") || ip.startsWith("192.168")) {                        continue;                    }                    return ip;                }                return ipv4Result.get(ipv4Result.size() - 1);            } else if (!ipv6Result.isEmpty()) {                return ipv6Result.get(0);            }            //If failed to find,fall back to localhost            final InetAddress localHost = InetAddress.getLocalHost();            return normalizeHostAddress(localHost);        } catch (SocketException e) {            e.printStackTrace();        } catch (UnknownHostException e) {            e.printStackTrace();        }        return null;    }    public static String normalizeHostAddress(final InetAddress localHost) {        if (localHost instanceof Inet6Address) {            return "[" + localHost.getHostAddress() + "]";        } else {            return localHost.getHostAddress();        }    }}

继续引入Boot相关配置:

值得说的是 : 我把 znf4-common-config 中的jdbc.properties 中的 相关数据库配置远方不动的粘贴到了 boot 约定下的 application.properties

# sys configisDebug = trueapplication.message=znf4# 容器端口server.port=9999server.sessionTimeout= 3600server.contextPath=# tomcat config#url = http://localhost:9999/merchant/trade/free?id=11111111111111&type=free server.tomcat.compression=2048 # is compression enabled (off, on, or an integer content length limit)server.tomcat.compressable-mime-types=text/html,text/xml,text/plain,text/javascript,application/json,application/xmltomcat.accessLogEnabled= falsetomcat.protocolHeader=x-forwarded-prototomcat.remoteIpHeader=x-forwarded-fortomcat.basedir=tomcat.backgroundProcessorDelay=30 # secsserver.tomcat.max-threads = 500server.tomcat.uri-encoding = UTF-8###############################JDBC Setting###############################commonjdbc.validationQuery=select current_timestamp()jdbc.maxWait=60000##webweb.jdbc.driver=com.mysql.jdbc.Driverweb.jdbc.url=jdbc:mysql://123.26.6.2:3306/znf4?useUnicode=true&characterEncoding=UTF-8web.jdbc.username=rootweb.jdbc.password=T5U+tbyGJ6mm6UVSZfo9qEDcmZdAx3Pk81KsYtpdhiN08WuPLwrVilyEK0yezprsCz25ghR2L11QJKQP/2+41w==web.jdbc.initialSize=2web.jdbc.minIdle=2web.jdbc.maxActive=10spring.http.encoding.charset=UTF-8spring.http.encoding.enabled=truespring.http.encoding.force=true#classpath中存在velocity 忽略spring-boot的VelocityAutoConfigurationspring.velocity.enabled=falsespring.velocity.checkTemplateLocation=true

最好以上我们就完成了 原生Springmvc SM 下的项目加进boot 相关组件,尝试启动一下 :

这里写图片描述

没有任何 问题 , 解释一下我为什么要引入boot :

之前设计这个项目就是根据需求直接设计成分布式的项目,各种中间件都是通过spring -xml 文件配置的,因为一些三方接口不是很给力,之前上网上找了一个 通过redis 对接口限流的方法,感觉还是有很大局限性,维护那不容易。之前在学Springcloud 中全家桶组件时,也是基于boot , 这把要用hystrix组件, 就想到项目通过boot 启动 , 用boot 去 引入 sringcloud 中的其他组件(现有的东西不动),后续也可以上 微服务(虽然对dubbo 情有独钟,还是选择了cloud)。 下面会展示 boot 引入 hystrix ,并且进行测试的相关代码和图片说明。

jar准备:

我是在partent 中下载jar, core 模块去继承, server 模块去引入core , merchant模块作为入口,去引用 core , server 。

直白说:引入下面这个jar 就行了,上面说的那些都跟我设计的maven多模块有关,跟jar没直接关系。

<!-- 限流,熔断,降级 --><dependency>     <groupId>com.netflix.hystrix</groupId>     <artifactId>hystrix-core</artifactId>     <version>1.5.13</version> </dependency>

这里写图片描述

启动一个测试服务,模拟三方服务 :

这里写图片描述

我们在我们的项目中加入测试代码 :

这里写图片描述

我们通过jemter 工具模拟并发,访问merchant 模块,merchant逻辑中有通过http 访问上面 9001 服务的代码,我们的目的就是通过 boot 去配置 hystrix 实现, 限流,降级,熔断。

我们先压测一下我们测试服务器 9001 ,直接 50 个线程 10次循环,共500次请求 :

这里写图片描述

结果 :

这里写图片描述

蹦了, 那么基本上可以肯定,我们通过merchant 去访问测试服务器,500次去访问也是扛不住的,我们不可能因为一个三方服务性能不好,出先了异常,导致我们的系统也用不了,那么我对他进行处理 :

SmsSendCommand.java 类

package com.znf4.test;import java.io.InputStream;import java.net.URL;import org.apache.tomcat.util.http.fileupload.IOUtils;import com.netflix.hystrix.HystrixCommand;import com.netflix.hystrix.HystrixCommandGroupKey;public class SmsSendCommand extends HystrixCommand<String>{    protected SmsSendCommand() {        super(HystrixCommandGroupKey.Factory.asKey("smsGroup"));    }    @Override    protected String run() throws Exception {        URL url = new URL("http://localhost:9001/send/agui");        byte[] result = new byte[3];        InputStream input = url.openStream();        IOUtils.readFully(input, result);        // 远程服务不稳定或网络抖动时暂时关闭        return new String(result);    }    @Override    protected String getFallback() {        // 降级 策略  再次查询, 查询备用接口 缓存 mock值        // 根据业务自定义        return "降级";    }}

测试 :

@RequestMapping("test_hystrix")public String test_hystrix() throws IOException{    return new SmsSendCommand().execute();}

config.properties 文件 跟 apllication.properties 文件放在同一个目录下。 配置如下

注意 : hystrix.threadpool.smsGroup.coreSize=20 中的 smsGroup 为 上述 代码中的key : HystrixCommandGroupKey.Factory.asKey(“smsGroup”) 。

# Hystrix 默认加载的配置文件 - 限流、 熔断示例# 超时时间hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000000# 信号量 netflix分布式配置中心 archaius# 线程池大小hystrix.threadpool.default.coreSize=10hystrix.threadpool.default.maxQueueSize=-1hystrix.threadpool.default.queueSizeRejectionThreshold=5# 限流策略hystrix.threadpool.smsGroup.coreSize=20hystrix.threadpool.smsGroup.maxQueueSize=1000# 超过就报错hystrix.threadpool.smsGroup.queueSizeRejectionThreshold=800

结果 :

这里写图片描述

解释 : 我们 通过jmeter 直接 80 * 10 次访问我们限流后的 接口, 可以发现服务器 并没有被压垮。 证明我门的配置好用。 继续我们补充一下降级,熔断。

降级 :

降级我们要么通过 SmsSendCommand 类中的 :

进行降级逻辑,可以 在此查询,查询别用接口,缓存mock 值等。

说明一下: 我们的限流 或者熔断 报错之后都会进行降级 。

@Override    protected String getFallback() {        // 降级 策略  再次查询, 查询备用接口 缓存 mock值        // 根据业务自定义        return "降级";    }

熔断 :

继续追加到config.properties 文件 , 都有注释,就不解释了。

# 熔断策略# 启用/禁用熔断机制hystrix.command.default.circuitBreaker.enabled=true# 强制开启 禁止访问相关接口hystrix.command.default.circuitBreaker.forceOpen=false# 强制关闭 hystrix.command.default.circuitBreaker.forceClosed=false# 在一定前提条件下,暂停接口访问# 10秒统计一次hystrix.command.default.metrics.rollingStats.timeInMilliseconds=10000# 令牌桶数量# hystrix.command.default.metrics.rollingStats.numBuckets=10# 前提条件,时间内发起一定数量的请求。  也就是10秒钟内至少请求5次,熔断器才发挥起作用hystrix.command.default.circuitBreaker.requestVolumeThreshold=5# 错误百分比。达到或超过这个百分比,启用熔断  不会访问接口hystrix.command.default.circuitBreaker.errorThresholdPercentage=50# 10秒后,半打开状态hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=10000

测试 :

完善测试服务器代码,加入随机数,对2取余,满足就响应一个错误,来触发熔断 ,。

这里写图片描述

重启测试服务9001 , 继续访问 80 * 10 。看结果 :

这里写图片描述

上面可以看到,我们 800次请求,:
错误百分比。达到或超过这个百分比,启用熔断 不会访问接口
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50 时候我们这个接口就不会在继续接收请求,以进行了熔断。

以上,我们我们完成了 boot 的引入,顺便 我用boot 引入了hystrix 进行了 限流,熔断,降级的演示。

实例代码 : 链接: https://pan.baidu.com/s/1o8n6pw2 密码: dnsf

原创粉丝点击