欢迎光临
我一直在奋斗

一次发送邮件引发的线程问题

534737859阅读(197)

1.程序大致逻辑流程

出问题的代码主要是通过开放一个接口,然后当其他的服务调用这个的时候。能够推送相应的信息,如短信,邮件,系统消息。而我的处理方式是通过加上异步注解@Async来进行的,这也为了后面的坑。

2.问题出现

在本地操作的时候,可能由于处理的数量不是很大,程序并没有阻塞。在压力测试的时候也没有问题(这个地方有点迷)。到了线上,正常运行了差不多10天左右,用户反馈说收不到验证码了。我马上查看程序,并没有看见异常。查看线上的rabbitmq,发现有1条unack的消息。由于当时线上的就只开了一个消费者,默认的percount也是1.所以队列就卡在那里了。当时只有紧急重启消费者,重新消费,这一次一起正常。当时也是比较郁闷的,首先发现服务器日志上有channel shutdown的warn警告。查询了一下,大概的原因是由于spring-rabbit构建了一个CachingConnectionFactory。默认值是25个连接,多余这个就会逻辑关闭和物理关闭。Channel这个概念应该熟悉,可以认为是一个连接通道,CachingConnectionFactory中默认最多可以缓存25个Channel,也可以通过方法setChannelCacheSize()设置。如果进行物理关闭的话,在Channel的消息就无法正常确认。会提示:

PRECONDITION_FAILED - unknown   delivery tag

查看代码,发现是由于在代码里面没有加上我们自定义的connectionFactory类,导致了我们的手动确认和导入的包的自动确认。解决了这个问题后,再次将程序上线,这次加上了50个消费者。就算一个消费者堵住,其他的任然可以消费。

2.纳尼!问题再次出现

过了差不多一周,线上的服务器rabbitm队列又出现unack了。并且程序ack的速度很慢,从10分钟到一个小时不等。我判断我的代码逻辑是没有问题的,应该是线程阻塞了。直接进入线上服务器,切入docker中。使用jps 查看阻塞的进程pid。执行操作 jstack id号 >> jstack.txt。导入到本地进行查看。


3.发现端倪

这样看不好看出具体问题的,这里我们使用IMB的ThreadDump软件查看。推荐给大家一个工具IBM Thread and Monitor Dump Analyzer for Java(点击下载)

我们发现其中有大量的Waiting on condition等待,它们都是调用的sun.misc.Unsafe.park(Native Method)方法。在进入详情页面查看,大多数都是

结合自己的代码判断应该是

@Async
    public void consumeQueueMessage(QueueMessage queueMessage, PushType pushType) {

        if (!QueueType.SEND_QUEUE.equals(queueMessage.getQueueType())) {
            LOG.debug("消息所在的队列类型是[{}],不是当前队列类型,不作处理", queueMessage.getQueueType()
                    .name());
            return;
        }
        if (!MessageType.NORMAL.equals(queueMessage.getMessageType())) {
            LOG.debug("消息类型为:[{}],不是当前队列中的消息类型,不作处理", queueMessage.getMessageType()
                    .name());
            return;
        }
        Assert.notNull(queueMessage, "待推送消息为空");
        Assert.notNull(queueMessage.getPushData(), "待推送的数据为空");
        MultisendResp resp = new MultisendResp();
        List<PushResult> results = new ArrayList<>();
        for (PushData pushData : queueMessage.getPushData()) {
            LOG.debug("调用推送服务,推送每个目标对象,将推送结果放入结果列表");
            PushResult pushResult = pushService.send(pushData);
            results.add(pushResult);
        }
        LOG.debug("将推送结果组合成统一响应结构");
        resp.setCode(PusherCode.SUCCESS.getCode());
        resp.setMessage(PusherCode.SUCCESS.getMessage());
        resp.setPushResults(results);
        resp.setMessageTag(queueMessage.getMessageTag());
        LOG.debug("将推送结果通过Topic交换器发布出去,发送到对应的结果队列中去");
        switch (pushType) {
        case SMS_PUSH:
            queueTemplate.getResultTemplate()
                    .convertAndSend(RouteKey.SMS_RESULT_QUEUE_KEY.getKey(), resp);

            break;
        case EMAIL_PUSH:
            queueTemplate.getResultTemplate()
                    .convertAndSend(RouteKey.EMAIL_RESULT_QUEUE_KEY.getKey(), resp);
            break;
        case WS_PUSH:
            queueTemplate.getResultTemplate()
                    .convertAndSend(RouteKey.WS_RESULT_QUEUE_KEY.getKey(), resp);
            break;

        default:
            LOG.error("出现未知队列类型错误。SendResultQueueServiceImpl.consumeQueueMessage");
            break;
        }

    }

}

4.陷入深思

去掉了@Async这个注解后,没过几天就又出现了队列unackd的情况。由于队列当时还在堵着,我直接使用导出ThreadDump日志进行分析。

"SimpleAsyncTaskExecutor-5" #49 prio=5 os_prio=0 tid=0x000000ffe0d5f000 nid=0xe7 runnable [0x00007f1298588000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
        - locked <0x00000000fae3fcf8> (a java.net.SocksSocketImpl)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at com.sun.mail.util.SocketFetcher.createSocket(SocketFetcher.java:317)
        at com.sun.mail.util.SocketFetcher.getSocket(SocketFetcher.java:233)
        at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1938)
        at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:642)
        - locked <0x00000000fae3e1e8> (a com.sun.mail.smtp.SMTPTransport)
        at javax.mail.Service.connect(Service.java:317)
        - locked <0x00000000fae3e1e8> (a com.sun.mail.smtp.SMTPTransport)
        at javax.mail.Service.connect(Service.java:176)
        at javax.mail.Service.connect(Service.java:125)
        at jodd.mail.SendMailSession.open(SendMailSession.java:76)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:191)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender.send(EmailDirectSender.java:208)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender$$FastClassBySpringCGLIB$$ef5f1eb5.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
        at cn.signit.wesign.mss.pusher.util.msg.email.EmailDirectSender$$EnhancerBySpringCGLIB$$29ba9a66.send(<generated>)
        at cn.signit.wesign.mss.pusher.service.impl.PushServiceImpl.emailPush(PushServiceImpl.java:339)
        at cn.signit.wesign.mss.pusher.service.impl.PushServiceImpl.send(PushServiceImpl.java:223)
        at cn.signit.wesign.mss.pusher.queue.service.impl.SendQueueServiceImpl.consumeSendQueueMessage(SendQueueServiceImpl.java:104)
        at sun.reflect.GeneratedMethodAccessor338.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:180)
        at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:112)
        at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:49)
        at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:126)
        at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:106)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:822)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:745)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:97)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:189)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1276)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:726)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1219)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1189)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1500(SimpleMessageListenerContainer.java:97)
        at org.springframework.amqp.rabbit.listen

可以看到程序多次执行了邮件发送的代码,结合当时写的逻辑看。出现了发送失败的情况,邮件会自动切换账户进行发送,这导致其可能会一直调用发送邮件的函数,结果是程序最终会发送邮件成功,但消耗的时间是很长的.我怀疑使用的邮件发送商通过smtp协议发送邮件,可能存在限制的情况,导致了我的发送失败,并一直调用send函数导致了程序进入自己调用自己的循环,虽然最后会发送成功并退出,但是耗时太长.


总结:JAVA的学习之路还很漫长,本次排查历时较久,主要原因是这种情况的出现十分少见,排查CPU占用率也未见异常。通过这个问题,我学到了docker的基本操作,如何通过jstack来查看线程的情况。以及常见线程的8中情况。最后,路漫漫其修远兮,吾将上下而求索共勉。

DiyBox授权码生成

534737859阅读(520)

DIYBOX授权码生成

算法的核心思想


算法的核心思想是通过传入的手机号和特定的前缀字符生成待处理的字符串,然后使用MD5摘要加密算法对其进行加密。而由于获取到的是byte类型的数据,如果要转化为整型则需要与上0xff。否则会取到负数。然后取从第7位开始到13位的之间的6位作为验证码。


补充知识

如byte temp1 = (byte) 0xA0;
如果直接输出System.out.println(temp),结果是-96。
因为在系统了0XA0表示的是11111111 11111111 11111111 01011111(补码)
因为byte在java里面只有8位,所以前面的那24位是不存在的。只是为了方便后面的计算。
如果&0XFF(实际为0X00FF)
00000000 00000000 00000000 11111111
0xA0
11111111 11111111 11111111 01011111
这两个补码与上后为:
00000000 00000000 00000000 01011111
然后取反+1后为
00000000 00000000 00000000 10100000(原码)
结果为160

package cn.dyhack.test;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class DiyBoxDecoder {

    public static void decode(String accountId) throws NoSuchAlgorithmException
    {
        String sum = "????#"+accountId;
        MessageDigest md5 =MessageDigest.getInstance("MD5");
        md5.update(sum.getBytes());
        byte[] temp =md5.digest();
        StringBuffer result = new StringBuffer();
        System.out.println(temp.length);
        for(byte p:temp)
        {
            result.append(Integer.toHexString(p&0xff));
        }   
        System.out.println(result);
        System.out.println((String)result.subSequence(7, 13));
    }

    public static void main(String args[]) throws NoSuchAlgorithmException
    {
        DiyBoxDecoder.decode("11111111111\n");
    }

}

spring-cloud-stream学习以及常见的坑

534737859阅读(1409)

stream这个项目能够让我们不必通过繁琐的自定义ampq来建立exchange,通道名称,以及队列名称和路由方式。只需要简单几步我们就轻松使用stream完成推送到rabbitmq和kafafa,并完成监听工作。

首先,stream提供了默认的输入和输出通过。如果我们不需要多个通道,可以通过@Enbalebing(Sink.Class)来绑定输入通道。对应的application里面的

  # rabbitmq默认地址配置
  rabbitmq:
    host: asdf.me
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      bindings:
        input:
         destination: push-exchange
        output:
          destination: push-exchange

这样会自动建立一个exchange为push-exchange名字的输出通道。同理@Enbalebing(Input.Class)是绑定输入通道的。下面创建一个生产者和消费者:


@EnableBinding(Source.class)
public class Producer {
    @Autowired
    @Output(Source.OUTPUT)
    private MessageChannel channel;

    public void send() {
        channel.send(MessageBuilder.withPayload("22222222222" + UUID.randomUUID().toString()).build());
    }

@EnableBinding(Sink.class)
public class Consumer {
    @StreamListener(Sink.INPUT)
    public void receive(Message<String> message) {
        System.out.println("接收到MQ消息:" + JSONObject.toJSONString(message));
    }
}

stream默认提供的消费者和生产者接口:

public interface Sink {
  String INPUT = "input";

  @Input(Sink.INPUT)
  SubscribableChannel input();
}

public interface Source {
    String OUTPUT = "output";

    @Output("output")
    MessageChannel output();
}

可以看出,会去找到我们在application.yaml里面定义的input,output下面的destination。分别作为输入和输出通道。我们也可以自己定义接口来实现:

   String WS_INPUT = "ws-consumer";
    String EMAIL_INPUT = "email-consumer";
    String SMS_INPUT = "sms-consumer";
    @Input(MqMessageInputConfig.EMAIL_INPUT)
    SubscribableChannel emailChannel();

    @Input(MqMessageInputConfig.WS_INPUT)
    SubscribableChannel wsChannel();

    @Input(MqMessageInputConfig.SMS_INPUT)
    SubscribableChannel smChannel();

import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;

public interface MqMessageOutputConfig {
    String MESSAGE_OUTPUT = "message-producter";

    @Output(MqMessageOutputConfig.MESSAGE_OUTPUT)
    MessageChannel outPutChannel();

}

坑1. 需要注意的是,最好不要自定义输入输出在同一个类里面。这样,如果我们只调用生产者发送消息。会导致提示Dispatcher has no subscribers for channel。并且会让我们发送消息的次数莫名减少几次。详细情况可以查看gihub官方issue,也给出的这种解决方法官方解决方式

建立一个testjunit类,然后使用生产者发送消息。消费者监听队列获取消息。

接收到MQ消息:{"headers":{"amqp_receivedDeliveryMode":"PERSISTENT","amqp_receivedRoutingKey":"my-test-channel","amqp_receivedExchange":"my-test-channel","amqp_deliveryTag":1,"amqp_consumerQueue":"my-test-channel.anonymous.vYA2O6ZSQE-S9MOnE0ZoJQ","amqp_redelivered":false,"id":"805e7fc3-a046-e07a-edf5-def58d9c8eab","amqp_consumerTag":"amq.ctag-QwsmRKg5f0DGSp-7wbpYxQ","contentType":"text/plain","timestamp":1523930106483},"payload":"22222222222a7d24456-5b11-4c25-9270-876e7bbc556a"}

坑2.stream生成的exchang默认是topic模式。就是按照前缀匹配,发送消息给对应的队列。

*(星号):可以(只能)匹配一个单词

#(井号):可以匹配多个单词(或者零个)

fanout:广播模式,发送到所有的队列

direct:直传。完全匹配routingKey的队列可以收到消息。

坑3.默认消息异常之后,都会往死消息队列里面写,然而异常是放到一个header里面去的。默认消息队列支持的最大frame_max 是128kb,超过这个大小,服务器就主动给你关闭连接,然后把你的消息会不断的重试。

坑4.看到国内好多博客,使用@Input和@output都是用MessageChannel,这是不对的。@Output对MessageChannel,@input对应SubscribableChannel 。切记


坑5.我使用的stream版本是1.2.1,springboot版本时1.5.6。没有办法使用routingkey属性,即在spring.cloud.stream.rabbit这个属性无法显示。应该是我的stream版本偏低吧。遇到这种情况,大家果断换新版本,或者使用自带的ampq来实现吧。

坑6.stream的destination对应生成rabbitmq的exchange。加上了group后,例如destination:wx-consumer,group:queue。那么经过stream后会变成wx-consumer.group。如果使用group对应的是持久化队列,不会被rabbitmq删除。

Spring微服务指南

534737859阅读(313)

Spring微服务简介:

首先Spring微服务是基于Spring-cloud平台开展的,而Spring-cloud又是基于Spring-boot平台。最后Spring-boot又是基于Spring的。是不是感觉这是多么奇妙的关系,也支持这spring家族的日益壮大。

Spring-Eureka服务:

这个服务是能够实现我们的分布式,集群式运行。提高了我们的应用的高可靠的以及通过Docker部署,极大的降低了单个服务的资源消耗和出现故障便于及时排除。说下需要避免的一些坑:pom要加上依赖:
<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

application.yml文件里面注册到Eureka服务器必须要加上后缀/eureka才可以。fetchRegistry此客户端是否获取eureka服务器注册表上的注册信息。registerWithEureka:表示是否将自己作为客户端注册。当我们需要自己是服务器并注册自己的时候这个就为true。

Spring-Feign服务:

这是一个客户端负载均衡服务,当我们需要访问的时候,通过feign调用。能够通过名称调用。调用是基于注册到Eureka上的服务。我们可以注册多个服务,使用同一个名称。当我们调用的时候,系统会自动帮我们做好负载均衡工作。而这样做有缺点,如果2台服务器都挂了,或者网络原因以及其他自身原因,服务并不能保证百分之百可用,如何这个服务出现问题了,会导致调用这个服务出现线程阻塞。如果这个时候有大量的请求涌入,会导致线程资源被消耗完毕,导致整个服务瘫痪。接下来就需要断路器了。

Spring-Hystrix服务:

俗称断路器,是为了应付上面那种情况的,服务与服务之间的依赖会导致故障转播,引起严重后果。断路器打开后,能够在服务失效的时候返回一个固定值。只需要在pom中引入相应的依赖:

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

在程序的启动类ServiceRibbonApplication 加@EnableHystrix注解开启Hystrix并在需要调用的方法上加上 @HystrixCommand(fallbackMethod = “回调方法名称”) 就可以了。而Feign是自带断路器的。@FeignClient(fallback=error.class,value=”eurekaclientname”)

Spring-Zuul服务:

中文名称路由网关服务,类似于路由的功能。我们只需要在配置文件中配置好对应的路由规则就好了。配合均衡负载简直不要太棒了。开启也非常简单:在其入口applicaton类加上注解@EnableZuulProxy,开启zuul的功能。然后配置application.yaml:

zuul:
  routes:
    api-a:
      path: /api-a/**
      serviceId: service-ribbon
    api-b:
      path: /api-b/**
      serviceId: service-feign

Spring-Cloud-Bus服务:

消息总线服务。通常我们使用的时rabbitmq和activemq来实现的这个功能。加上rabbitmq的依赖后。在yaml里面配置好。这个功能个人感觉结合stream推送会更加方便。非常适合微服务这种分布式的结构。发送方只管发送,后续的取出结果留给接收方以及进行下一步操作。对于事物的处理不要求同步性的可以考虑。打个比方,用户注册后需要发送邮件,红包等,这些都不是必须要马上完成的。我们完全可以放在队列中进行处理。先注册成功,然后发送邮箱,邮箱发送成功后向rabbitmq的一个队列发送消息。接收方接收到之后再发送红包,再发送消息到rabbitmq中。最开始的注册服务接收到消息,完成整个初始注册过程。

Markdown学习总结

534737859阅读(421)

什么是Markdown

在刚才的导语里提到,Markdown 是一种用来写作的轻量级「标记语言」,它用简洁的语法代替排版,而不像一般我们用的字处理软件 Word 或 Pages 有大量的排版、字体设置。它使我们专心于码字,用「标记」语法,来代替常见的排版格式。例如此文从内容到格式,甚至插图,键盘就可以通通搞定了。
像我这种一直对于格式不敏感的广大程序员们,队友以前写文章的各种调整格式很是头痛。但markdown帮助我们解决了这个问题。让我们能够专注与文字内容,而不再是格式 问题。

Markdown常用工具

Markdown常用语法

通过了解Markdown的常用语法,能够让我们快速掌握其中要点。

标题


标题是每篇文章的常用格式,在这里你可以用#表示一级标题,##表示二级标题,依此类推。是不是感觉到十分的方便。

列表

星号加上数字表示无须列表,而1.表示有序列表

引用

使用我们常见的大于符号就可以了。

这是一个引用

插入链接与图片

链接与图片的不同之处在于图片在前面多了一个感叹号!。

粗体与斜体

使用2个 **包含一个文本表示粗体,一个*包含表示斜体
这是一个粗体 粗体,这是一个斜体

表格

| id | name | sex |

| ————- |:————-:| —–:|

| 1 | lyg | man |

| 2 | zk | woman |

| 3 | cyy | man |
结果:

:—-表示左对齐,—-:表示右对齐,:—-:表示两端对齐.发现好多博客都没有讲这个呀

id name sex
1 lyg man
2 cyy woman
3 zk man

嵌入代码

对于我们这种程序员简直是一大福利呀,需要写入代码的地方直接加入```你要的代码```就可以了。

publi static void main(String args[]){

     System.out.pritln("Hello World!");
}

分割线

另起一行输入***就可以了


学习总结

Markdown的专注于内容的功能,能够让我们写入好看的博客。Markdown的其他用法有待于我们去发掘。

Git学习总结

534737859阅读(433)

一.什么是版本控制

什么是版本控制?我为什么要关心它呢?版本控制是一种记录一个或若干

文件内容变化,以便将来查阅特定版本修订情况的系统。在本书所展示的例子中,

我们仅对保存着软件源代码的文本文件作版本控制管理,但实际上,你可以对任

何类型的文件进行版本控制。

对于我来说,个人感觉git有点类似于百度网盘上面的时光回收机,能够查看任意时间段的文件,这样对于我们程序员来说就太方便了,如果代码写的有问题,能够及时回滚到任意一个版本.提升了工作的效率.让我们能够轻松的进行多人开发.

首先这里再明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。

不幸的是,Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的,前面我们举的例子只是为了演示,如果要真正使用版本控制系统,就要以纯文本方式编写文件。

因为文本是有编码的,比如中文有常用的GBK编码,日文有Shift_JIS编码,如果没有历史遗留问题,强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。

使用Windows的童鞋要特别注意:

千万不要使用Windows自带的记事本编辑任何文本文件。原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符,你会遇到很多不可思议的问题,比如,网页第一行可能会显示一个“?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。建议你下载Notepad++代替记事本,不但功能强大,而且免费!记得把Notepad++的默认编码设置为UTF-8 without BOM即可:

二.gitlab的简介

这里着重使用gitlab而不是使用github.该服务与github相似,但gitlab有一下的优势:

代码开源,能够让公司搭建自己的服务器.相对于github的免费版必须开源来说简直是不要不要的.

操作也github相似,也集成了权限管理,代码统计图表等实用工作.

三.安装git控制工具

Linux安装:如果要在 Linux 上安装预编译好的 Git 二进制安装包,可以直接用系统提
供的包管理工具。

在 Fedora 上用 yum 安装:
$ yum install git-core
在 Ubuntu 这类 Debian 体系的系统上,可以用 apt-get 安装:
$ apt-get install git

在Centos 7下安装:yum install git                                                                                                                                   在 Windows 上安装(推荐sourcetree或者其他类似工具):在 Windows 上安装 Git 同样轻松,有个叫做 msysGit 的项目提供了安装包,可以到 GitHub 的页面上下载 exe 安装文件并运行:http://msysgit.github.com/完成安装之后,就可以使用命令行的 git 工具(已经自带了 ssh 客户端)了,另外还有一个图形界面的 Git 项目管理工具

四.运行Git前的配置

用户信息:第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,
每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内
容一起被永久纳入历史记录:

$ git config –global user.name “John Doe”
$ git config –global user.email johndoe@example.com
如果用了 –global 选项, 那么更改的配置文件就是位于你用户主目录下的那
个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的
项目中使用其他名字或者电邮,只要去掉 –global 选项重新配置即可,新的设
定保存在当前项目的 .git/config 文件里。

查看配置信息:要检查已有的配置信息,可以使用 git config –list 命令

获取帮助:

想了解 Git 的各式工具该怎么用,可以阅读它们的使用帮助,方法有三:
$ git help <verb>
$ git <verb> –help
$ man git-<verb>
比如,要学习 config 命令可以怎么用,运行 :
$ git help config

五.gitlab的使用

不同与 github 自已申请账户,gitlab 只能由系统管理员为你创建账户,需
要使用 gitlab 服务,请及时与公司的 gitlab 系统管理员联系,由他为你创建账
户.

初探 gitlab 功能
登录进 gitlab(公司使用 7.0 版本)后,其主要界面如下:

剩下的就留给大家自己去查看吧.

六.Git 开发分支模型

  1. 分支介绍
    Git 提供了强大的分支功能,利用分支我们可以快速的在各个分支之间切换
    开发,为了规范开发,公司采用了业界备受推崇的 git flow 分支模型,该分支模
    型如下:
    主要分支:
    a) Production 分支
    也就是我们经常使用的 Master 分支, 这个分支最近发布到生产环境的代码,
    最近发布的 Release, 这个分支只能从其他分支合并,不能在这个分支直接修

    b) Develop 分支
    四川思安特科技 第 23 页
    这个分支是我们是我们的主开发分支, 包含所有要发布到下一个 Release 的
    代码,这个主要合并与其他分支,比如 Feature 分支
    c) Feature 分支
    这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回
    Develop 分支进入下一个 Release
    d) Release分支
    当你需要一个发布一个新 Release 的时候,我们基于 Develop 分支创建一
    个 Release 分支,完成 Release 后,我们合并到 Master 和 Develop 分支
    e) Hotfix分支
    当我们在 Production 发现新的 Bug 时候,我们需要创建一个 Hotfix, 完成
    Hotfix 后,我们合并回 Master 和 Develop 分支,所以 Hotfix 的改动会进入下
    一个 Release

7.Forking流

Forking
工作流不是使用单个服务端仓库作为『中央』代码基线,而让各个
开发者都有一个服务端仓库。这意味着各个代码贡献者有2个Git仓库而不是1个:一个本地私有的,另一个服务端公开的。

Forking工作流的一个主要优势是,贡献的代码可以被集成,而不需要所有人都能push代码到仅有的中央仓库中。开发者push到自己的服务端仓库,而只有项目维护者才能push到正式仓库。这样项目维护者可以接受任何开发者的提交,但无需给他正式代码库的写权限。效果就是一个分布式的工作流,能为大型、自发性的团队(包括了不受信的第三方)提供灵活的方式来安全的协作。也让这个工作流成为开源项目的理想工作流。

工作方式:

和其它的Git工作流一样,Forking工作流要先有一个公开的正式仓库存储在服务器上。但一个新的开发者想要在项目上工作时,不是直接从正式仓库克隆,而是fork正式项目在服务器上创建一个拷贝。
   这个仓库拷贝作为他个人公开仓库 —— 其它开发者不允许push到这个仓库,但可以pull到修改(后面我们很快就会看这点很重要)。在创建了自己服务端拷贝之后,和之前的工作流一样,开发者执行git clone命令克隆仓库到本地机器上,作为私有的开发环境。
  要提交本地修改时,push提交到自己公开仓库中 —— 而不是正式仓库中。然后,给正式仓库发起一个pull request,让项目维护者知道有更新已经准备好可以集成了。对于贡献的代码,pull request也可以很方便地作为一个讨论的地方。
  为了集成功能到正式代码库,维护者pull贡献者的变更到自己的本地仓库中,检查变更以确保不会让项目出错,合并变更到自己本地的master分支,然后pushmaster分支到服务器的正式仓库中。到此,贡献的提交成为了项目的一部分,其它的开发者应该执行pull操作与正式仓库同步自己本地仓库。

浅谈MySQL常用储存引擎

534737859阅读(365)

MySQL储存引擎是MySQL体系架构中的重要组成部分,也是MySQL结构中的核心部分,插件式的储存引擎更是它区别于其它数据库的重要特征,它处于MySQL体系架构中Server端底层,是底层物理结构的实现,用于将数据以各种不同的技术方式存储到文件或者内存中,不同的存储引擎具备不同的存储机制、索引技巧和锁定水平。常见的MySQL储存引擎有,InnoDB,MyISAM,Memory,Archive,他们具备各自的特征,我们可以根据不同的具体应用来建立对应的储存引擎表。

在谈储存引擎之前,我们需要先理解几个基本概念:

一、事务:

事务是一组原子性的SQL语句或者说是一个独立的运行单元,如果数据库引擎能成功对数据库应用这组SQL语句,要么就执行,那么就因为一条语句奔溃或者是其他原因无法执行,那么所有的语句都不会执行。也就是说事务中的语句,要么全部执行,要么全部不执行(执行失败,需要回滚)。

还是那个经典的银行转账例子:

假设银行的数据库有两张表:支票表和储蓄表,现在某个客户A要从其支票账户转移2000元到其储蓄账户,那么至少需求三个步骤:

a.检查A的支票账户余额高于2000元;

b.从A的支票账户余额中减去2000元;

c.在A的储蓄账户余额中增加2000元。

这三个操作必须要放在一个事务中,任何一个步骤失败,则必须要回滚所有的步骤,否则A作为银行的客户就可能要莫名损失2000元,就出问题了。这就是一个典型的事务,这个事务是不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,不可能只执行其中一部分,这也是事务的原子性特征。

上面只说了事务的原子性,除了原子性,事务还有隔离性,一致性和持久性。

1、原子性:事务中的操作要么都执行,要么都不执行。

2、隔离性:事务与事务之间可以相互影响的程度,隔离性主要用于数据的并发访问控制。事务隔离性从低到高分为read uncommitted(读未提交),read committed(读已提交),Repeatable read(可重复读),serializable。当事务的隔离级别为read uncommitted时可能发生脏读,不可重复读和幻读的情况,当事务的隔离级别为read committed时可能发生不可重复读和幻读,当事务的隔离级别为Repeatable read时可能会发生幻读,当事务的隔离级别为serializable时可以完全杜绝以上的脏读,不可重复读及幻读情况,下面解释下脏读,不可重复读及幻读的含义:

(1)脏读:一个事务可以读取到另外一个还未提交的事务修改的数据,这样造成的影响是如果这个事务回滚了那么另外一个事务看到的就是一笔脏数据。

(2)不可重复读:一个事务在执行过程中,针对同一笔数据的读取结果都不一样,如果事务1在事务2更新之前读取过一次,在事务2更新之后又读取过一次,那么将导致两次读取的结果不一样,这样要注意不可重复读指的是事务中操作的同一笔数据,不是数据集。

(3)幻读:一个事务在执行过程中,多次进行查询操作,但是每次查询的结果集都是不一样的,幻读针对的是多笔记录。

大多数据库默认的数据隔离级别为read commited。

3、一致性:一致性要求事务所包含的操作不能违反数据资源的一致性检查,数据资源在事务执行之前处于某个数据一致性状态,那么事务执行之后也需要保持数据之间的一致性状态。我们先说的那个银行转账的列子就是为了保持数据一致性,我们使用事务的其中一个目的就是为了避免这种不一致的情况产生。

4、持久性:当事务提交之后所有的修改操作都不能再进行任何的修改(数据库系统一般是通过冗余储存或者多数据网络备份等方式来保证数据的一致性)。

二、读锁和写锁:

无论何时,只要有多个SQL需要同一时刻修改数据,都会产生并发控制的问题。

假设一个公共邮箱,用户A正在读取邮箱,同时,用户B正在删除邮箱中的某个邮件,会产生什么结果呢?客户A可能读取时会报错退出,也可能会读取到不一致的邮箱数据。如果把邮箱当作数据库中的一张表,可见其存在同样的问题。

解决这类经典问题的方法就是并发控制,即在处理并发读或者写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种锁就是共享锁和排他锁,也叫读锁和写锁(java中读写锁可以使用JUC中ReentrantReadWriteLock来实现,其内部包含一个读锁和一个写锁,底层使用的是一个volatile 类型的int变量,高16位用作读锁的状态,后16位用作写锁的状态)。

读锁是共享的,即相互不阻塞的,多个客户在同一时刻可以读取同一资源,互不干扰。写锁是排他的,即一个写锁会阻塞其它的写锁和读锁,只有这样,才能确保给定时间内,只有一个用户能执行写入,防止其它用户读取正在写入的同一资源。写锁优先级高于读锁。

三、行锁和表锁(java中线程安全的HashTable或通过Collections.synchronizedMap得到的HashMap和ConcurrentHashMap就类似于这里的表锁和行锁):

实际数据库系统中每时每刻都在发生锁定,锁也是有粒度的,提高共享资源并发行的方式就是让锁更有选择性,尽量只锁定需要修改的部分数据,而不是所有的资源,因此要进行精确的锁定。但是由于加锁也需要消耗资源,包括获得锁、检查锁是否解除、释放锁等,都会增加系统的开销。所谓的锁策略就是要在锁的开销和数据的安全性之间寻求平衡,这种平衡也会影响性能。

每种MySQL存储引擎都有自己的锁策略和锁粒度,最常用的两种重要的锁策略分别是表锁和行锁。

表锁是开销最小的策略,会锁定整张表,用户对表做写操作时,要先获得写锁,这会阻塞其它用户对该表的所有读写操作。没有写锁时,其它读取的用户才能获得读锁,读锁之间是不相互阻塞的。行锁可以最大程度支持并发处理,但也带来了最大的锁开销,它只对指定的记录加锁,其它进程还是可以对同一表中的其它记录进行操作。表级锁速度快,但冲突多,行级锁冲突少,但速度慢(每次访问数据都需要加锁)。

理解了上面几个概念,我们就可以很好地分辨不同存储引擎之间的区别了。

一、InnoDB储存引擎:

MySQL存储引擎可以分为官方存储引擎和第三方存储引擎,InnoDB就是强大的第三方存储引擎,具备较好的性能和自动崩溃恢复特性,目前应用极为广泛,是当前MySQL存储引擎中的主流,它在事务型存储和非事务型存储中都很流行。

InnoDB存储引擎支持事务、支持行锁、支持非锁定读、支持外键。这里提一下非锁定读(采用读写分离的思想,读和写时访问的是不同的资源):一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过多版本控制(multi versionning)的方式来读取当前执行时间数据库中行的数据,如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此等待行上锁的释放。相反的,InnoDB会去读取行的一个快照数据(一个历史数据),该机制极大地提高了数据库的并发性。(有关非锁定读的详情可参看这篇博客https://www.cnblogs.com/olinux/p/5174888.html)

如非特别原因,应用建表时都可以首选考虑使用InnoDB。

二、MyISAM储存引擎:

MyISAM存储引擎是MySQL官方提供的存储引擎,它在InnoDB出现并完善之前是MySQL存储引擎的主流,但目前逐渐被淘汰主要因为其不支持事务,这或许源于MySQL的开发者认为不是所有的应用都需要事务,所以便存在了这种不支持事务的存储引擎。

MyISAM不支持事务,不支持行级锁,支持表锁,支持全文索引(MySQL5.6之后InnoDB引擎也支持全文索引了),最大的缺陷是崩溃后无法安全恢复。

MyISAM因设计简单,数据以紧密格式存储,所以某些场景下性能很好,但是它的表锁又带来了性能问题,如果你发现所有的查询都长期处于“Locked”状态,表锁就是罪魁祸首了。

因此,对于只读数据,或者表比较小,可以忍受修复操作的可以依然使用MyISAM,对于不需要事务的应用,选择MyISAM存储引擎,或许可以获得更高的性能,MySQL自带的默认的information_schema库中就存在使用MyISAM存储引擎的表。

三、Memory储存引擎:

Memory存储引擎将表中数据放在内存中,因此速度非常快,但因其只支持表锁,所以并发性能较差,最糟糕的是这个存储引擎在数据库重启或崩溃之后表中的数据将全部丢失,它只适用于存储临时数据的临时表,MySQL中一般使用这个存储引擎来存放查询的中间结果集,如MySQL自带的默认的information_schema库中就存在较多使用Memory存储引擎的表。

四、Archive储存引擎:

Archive存储引擎置只支持INSERT和SELECT操作,支持行锁,但本身并不是事务安全的存储引擎,其最大的优点是其具有较好的压缩比,压缩比一般可达到1:10,可以将同样的数据以更小的磁盘空间占用来存储。

Archive存储引擎非常适合存储归档数据,如历史数据、日志信息数据等等,这类数据往往数据量非常大,并且基本只有INSERT和SELECT操作,使用这个存储引擎可以非常节约磁盘空间。

其它存储引擎使用较少,这里就不谈了。

参考资料:《Spring揭秘》 http://blog.csdn.net/hangxing_2015/article/details/52585979

邪客安全团队 黑客闯关游戏攻略(未完待续)

534737859阅读(1073)

首先是我们群里发了这个闯关的地址出来,我有空就看了一下。
游戏地址:http://www.xksafe.com/hackergame/
(仅供娱乐!!!)

第一关:找到密码 进入第二关

简单的查看网页源代码,读懂javascript就行了,当输入的值等于xk时就跳转。让我吐槽的是作者居然没写js捕获回车键,这就导致了我敲了第一次回车没有反应,当我反应过来的时候是用鼠标去点击确定。

 

第二关:黑客闯关游戏第二关 进入下一关的答案就在图片中 仔细看哦~~

简单的通过hex打开文件查看,在最后面看到可以的后缀htm,然后前面还跟的有or3猜测这是第三关的地址。

 

第三关:开动你的大脑 挑战自我 邪客安全团队欢迎您!

做多了ctf,一看到这个就是莫尔斯密码。直接百度莫尔斯密码翻译计即可                                                                                                              翻译结果:HTTP://WWW.XKSAFE.COM/HACKERGAME/4DFSA.HTM

直接输入http://www.xksafe.cim/hackgame/dfsa.htm就跳转到了第4关

 

第四关:请输入密码和用户名进入下一关,加油哦!!

我随便输入了一个,他是我错误,当时我以为是sql注入。没想到直接打开源代码。第五关地址就在里面。

xkzuiniubi.html。这个地址我不敢恭维,,,最牛B。。。!!!

 

第五关:请输入密码,通用密码的2个字母。

这道题坑,明明说2个字母,结果确实admin。

结果被坑了一把,原来就是2个字母,不知道通用密码为什么是or   无语中

第六关:门锁着?,怎么办?看来需要破解这道密码锁 才能找到下一关的路径。

我还是老老实实的爆破吧。。有谁社工出来了下面留言,我好添加进来,附上你的名字。

目前看来爆破不行,照目前这个速度,要爆破几年去了。只能使用社工了。联想vip,10位数字,猜测是站长的qq号码

1397800550

直接给出了下一关地址:http://www.xksafe.com/hackergame/seven7seven.html

第七关:这关需要简单的社会工程学,请联想本关某个“特点”进入“第8关”

联想到给的地址是seven7seven,那么下一关的地址会不会就是eight8eight8哪。

没想到居然猜对了。

第八关: 想要进入下一关 就动起你的社工思维!

。。。。。太困了,我先睡觉了,明天再说,

 

 

 

 

蓝桥杯 第七届省赛试题 四平方和

534737859阅读(530)

四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多4个正整数的平方和。
如果把0包括进去,就正好可以表示为4个数的平方和。

比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符号表示乘方的意思)

对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对4个数排序:
0 <= a <= b <= c <= d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法
程序输入为一个正整数N (N<5000000)
要求输出4个非负整数,按从小到大排序,中间用空格分开

例如,输入:
5
则程序应该输出:
0 0 1 2

再例如,输入:
12
则程序应该输出:
0 2 2 2

再例如,输入:
773535
则程序应该输出:
1 1 267 838

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

让我吐槽一下,我一开始想成了直接穷举,就自然而然弄成了4重循环,但是结果我写代码的时候并没有用刀第4个循环。这让我很蛋疼。。。

 

 

 

 

package 第七届蓝桥;
/*
 *              四平方和定理,又称为拉格朗日定理:
				每个正整数都可以表示为至多4个正整数的平方和。
				如果把0包括进去,就正好可以表示为4个数的平方和。
				
				比如:
				5 = 0^2 + 0^2 + 1^2 + 2^2
				7 = 1^2 + 1^2 + 1^2 + 2^2
				(^符号表示乘方的意思)
				
				对于一个给定的正整数,可能存在多种平方和的表示法。
				要求你对4个数排序:
				0 <= a <= b <= c <= d
				并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法
				
				
				程序输入为一个正整数N (N<5000000)
				要求输出4个非负整数,按从小到大排序,中间用空格分开
				
				例如,输入:
				5
				则程序应该输出:
				0 0 1 2
				
				再例如,输入:
				12
				则程序应该输出:
				0 2 2 2
				
				再例如,输入:
				773535
				则程序应该输出:
				1 1 267 838
				
				资源约定:
				峰值内存消耗(含虚拟机) < 256M
				CPU消耗  < 3000ms
				
				
				请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
				
				所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
				注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
				注意:主类的名字必须是:Main,否则按无效代码处理。
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * */

import java.util.Scanner;

public class 四平方和 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		int a,b,c,d;//创建4个正整数,然后使用穷举的方法
		Scanner scan=new  Scanner(System.in);
		long n=scan.nextLong();
		int p=(int)Math.sqrt(n);
		boolean flag=false;
		for(int i=0;i<p;i++)//4层循环,遍历
		{
			a=i;
			for(int j=0;j<p;j++)
			{
				b=j;
				for(int  k=0;k<p;k++) { c=k; d=(int)Math.sqrt(n-a*a-b*b-c*c); if(Math.sqrt(n-a*a-b*b-c*c)==d) { if(a>=0&&a<=b&&b<=c&&c<=d)
							System.out.println(a+" "+b+" "+c+" "+d);
							flag=true;
							break;
						}
					
				}
					
				if(flag)
				{
					break;
				}
				
			}
			if(flag)
			{
				break;
			}
		}

	}

}


XML增删改查(Java Servlet)

534737859阅读(570)

原本早就该放假了的,但是由于这个工程实践导致了我们比较晚才放假。而我们就随便讲了个项目.使用的是dom4j这个xml解析以及修改类,已经json格式把xml转化为json格式向前端发送。

项目地址:下载地址 ( 密码: furu)

奋斗者的足迹

联系我们加入我们