netty實現客戶端服務端心跳重連

                       

前言:

???????? ?公司的加密機調度系統一直使用的是http請求調度的方式去調度,但是會出現網絡故障導致某個客戶端或者服務端斷線的情況,導致很多請求信息以及回執信息丟失的情況,接著我們拋棄了http的方式,改為Tcp的方式去建立客戶端和服務器之間的連接,并且要去實現斷線重連的功能,經過討論后決定使用java中成熟的nio框架 – netty去解決這一系列的問題。

?

1.?????? netty簡單介紹:

在百度中對netty的解釋是:

Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。

Netty框架并不只是封裝了多路復用的IO模型,也包括提供了傳統的阻塞式/非阻塞式 同步IO的模型封,Netty 是一個利用 Java 的高級網絡的能力,隱藏其背后的復雜性而提供一個易于使用的 API 的客戶端/服務器框架。其并發高、傳輸快、封裝好的特性受到了許多大公司的青睞,在這里我們就不過多的分析netty的原理和特性了,之后我會寫一篇文章專門寫一下從io到nio,再到netty的整個過程。重點講一下netty的魅力所在,今天我們已代碼實現為主,講解一下在springboot架構中,用netty實現服務端和客戶端之間的通信以及斷線重連等機制。

?

2.?????? 服務端代碼:

首先,引入netty的pom依賴

?

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>5.0.0.Alpha2</version>
</dependency>

?

然后我們在配置文件中寫入服務端的ip和端口號,用于連接

在springboot的application啟動類中寫入服務端的啟動start方法,用于在啟動項目時自動啟動服務端

 1 @SpringBootApplication
 2 public class Application implements CommandLineRunner {
 3 
 4     @Value("${netty.server.port}")
 5     private  int port;
 6 
 7     @Value("${netty.server.host}")
 8     private  String host;
 9 
10     @Autowired
11     NettyServer server;
12 
13     public static void main(String[] args) {
14         SpringApplication.run(Application.class, args);
15     }
16 
17 
18     @Override
19     public void run(String... strings) throws Exception {
20         this.startServer();
21 
22     }
23 
24     //啟動service
25     public void startServer(){
       //這個類實現一個IP套接字地址(IP地址+端口號)  
26 InetSocketAddress address = new InetSocketAddress(host, port); 27 ChannelFuture future = server.start(address); 28 29 Runtime.getRuntime().addShutdownHook(new Thread(){ 30 @Override 31 public void run() { 32 server.destroy(); 33 } 34 }); 35 36 future.channel().closeFuture().syncUninterruptibly(); 37 } 38 } 39 40 41 }

ChannelFuture: 

  Future最早出現于JDK的java.util.concurrent.Future,它用于表示異步操作的結果.由于Netty的Future都是與異步I/O操作相關的,因此命名為ChannelFuture,代表它與Channel操作相關.由于Netty中的所有I / O操作都是異步的,因此Netty為了解決調用者如何獲取異步操作結果的問題而專門設計了ChannelFuture接口.?
  因此,Channel與ChannelFuture可以說形影不離的.

然后我們要去重點看server.start()

public class NettyServer {private static final Logger logger = LoggerFactory.getLogger(ConnectionWatchdog.class);private final ChannelGroup channelGroup = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);private final EventLoopGroup bossGroup = new NioEventLoopGroup();private final EventLoopGroup workGroup = new NioEventLoopGroup();private Channel channel;/*** 開啟及服務線程*/public ChannelFuture start(InetSocketAddress address) {//服務端引導類ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workGroup)//通過ServerBootstrap的group方法,設置(1)中初始化的主從"線程池".channel(NioServerSocketChannel.class)//指定通道channel的類型,由于是服務端,故而是NioServerSocketChannel.childHandler(new NettyServerInitializer())//設置ServerSocketChannel的處理器.option(ChannelOption.SO_BACKLOG, 100)// 設置tcp協議的請求等待隊列.childOption(ChannelOption.SO_KEEPALIVE, true);//配置子通道也就是SocketChannel的選項ChannelFuture future = bootstrap.bind(address).syncUninterruptibly();logger.info("準備接收——————");channel = future.channel();return future;}public void destroy() {if(channel != null) {channel.close();}channelGroup.close();workGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}

?在這里的設置中,.childHandler(new NettyServerInitializer()) 用于設置了服務器管道 NioServerSocketChannel 的處理器handler,

這個handler是我們自定義封裝的一些對channel的public class NettyServerInitializer extends ChannelInitializer<Channel>{

    
@Component
public class TcpMsgHandler extends ChannelInboundHandlerAdapter {
@Overrideprotected void initChannel(Channel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();//處理日志//pipeline.addLast(new LoggingHandler(LogLevel.INFO));//處理心跳pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
     //消息編碼pipeline.addLast(new MessageEncoder());
   
     //粘包長度控制pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4));
     //消息解碼pipeline.addLast(new MessageDecoder());
     //自定義handerpipeline.addLast(new TcpMsgHandler());} }
 
ChannelPipeline :
Netty 的 Channel 過濾器實現原理與 Servlet Filter 機制一致,它將 Channel 的數據管道抽象為 ChannelPipeline,消息在 ChannelPipeline 中流動和傳遞。ChannelPipeline 持有 I/O 事件攔截器 ChannelHandler 的鏈表,由 ChannelHandler 來對 I/O 事件進行具體的攔截和處理,可以方便地通過新增和刪除 ChannelHandler 來實現不同業務邏輯的定制,能夠實現對修改封閉和對擴展到支持。


我們看到我們添加了idleStateHandler用來處理心跳,那么心跳究竟是什么呢,我們先來介紹一下心跳
  

心跳機制

  • 心跳是在TCP長連接中,客戶端和服務端定時向對方發送數據包通知對方自己還在線,保證連接的有效性的一種機制
  • 在服務器和客戶端之間一定時間內沒有數據交互時, 即處于 idle 狀態時, 客戶端或服務器會發送一個特殊的數據包給對方, 當接收方收到這個數據報文后, 也立即發送一個特殊的數據報文, 回應發送方, 此即一個 PING-PONG 交互. 自然地, 當某一端收到心跳消息后, 就知道了對方仍然在線, 這就確保 TCP 連接的有效性

在我們的服務端中,不會主動發心跳給客戶端,只會對對應的心跳消息,進行回應,告訴那些給我發心跳的客戶端說:我還活著!

  • 服務端添加IdleStateHandler心跳檢測處理器,并添加自定義處理Handler類實現userEventTriggered()方法作為超時事件的邏輯處理;

  • 設定IdleStateHandler心跳檢測每五秒進行一次讀檢測,如果五秒內ChannelRead()方法未被調用則觸發一次userEventTrigger()方法

?

?

TcpMsgHandler.java
 
@Component
public class TcpMsgHandler extends ChannelInboundHandlerAdapter {private final static Logger logger = LoggerFactory.getLogger("");@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {		}IdleState state = ((IdleStateEvent) evt).state();if (state == IdleState.READER_IDLE) {ctx.close();}} else {super.userEventTriggered(ctx, evt);}}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {TcpMsg msg = (TcpMsg) obj;try {//處理心跳...ctx.writeAndFlush(msg);}}catch(Exception ex){logger.info(ex.getMessage());}}
}    
在這里,我們的channelRead比較簡單,只是將客戶端發來的心跳直接發回去了,實現了響應客戶端心跳請求的目的,除了心跳,我們還可以去定義不同的消息類別,比如說是加密請求,還是處理數據的請求,入庫的請求等等,
我們可以自己從channel中獲取到客戶端發過來的信息做處理,記得要即使響應,比如,心跳中,我們將msg又返回給了channel:
ctx.writeAndFlush(msg);

在handler中,decoder用于解碼的作用,將客戶端發來的ByteBuf流的形式,轉為我們需要的格式,可以轉為我們要的對象,或者是一個string字符串

MessageDecoder.java
public class MessageDecoder extends ByteToMessageDecoder {private Logger logger = LoggerFactory.getLogger("");@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {int len = in.readableBytes();byte[] bytes = new byte[len];
       //將ByteBuf轉為byte數組in.readBytes(bytes);try {TcpMsg msg = TcpMsg.ByteToObj(bytes);out.add(msg);} catch (Exception ex) {logger.error("MessageDecoder",ex);}}}
encoder負責在我們發送數據的時候,將我們的對象、或者是字符串轉為byte數組,然后輸出
public class MessageEncoder extends MessageToByteEncoder<TcpMsg>{private Logger logger = LoggerFactory.getLogger("");@Overrideprotected void encode(ChannelHandlerContext ctx, TcpMsg msg, ByteBuf out) throws Exception {try{if (msg.getType() != 0){//logger.info("send: " + msg.getType() + ":" + msg.getGuid() + ":" + msg.getBody());}byte[] src = msg.ToBytes();out.writeBytes(src);}catch (Exception e){logger.error("MessageEncoder",e);}}
}

?

3.?????? 客戶端代碼:

在application配置文件中加入服務端的主機名和端口號

?

netty.server.host = 127.0.0.1
netty.server.port = 9090

啟動類Application

@SpringBootApplication
public class Application{@Autowiredprivate NettyClient client;@Value("${netty.server.port}")private int port;@Value("${netty.server.host}")private String host;public static void main(String[] args) throws Exception {SpringApplication.run(NettyClientApplication.class, args);}@Beanpublic NettyClient nettyClient() {return new NettyClient();}@Overridepublic void run(String... arg0) throws Exception {client.start(host, port);}}

NettyClient.java: 客戶端啟動類

?

@Component
public class NettyClient {//日志輸出private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);//主要連接地址private static String nettyHost = "";//備用連接地址private static String nettyHostRe = "";private static Integer nettyPort = 0;public boolean start(String host1,String host2,int port){//主要連接地址nettyHost = host1;//備用連接地址nettyHostRe = host2;nettyPort = port;//EventLoopGroup可以理解為是一個線程池,這個線程池用來處理連接、接受數據、發送數據EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();//NioEventLoop//客戶端引導類Bootstrap bootstrap = new Bootstrap();//多線程處理bootstrap.group(nioEventLoopGroup);//指定通道類型為NioServerSocketChannel,一種異步模式bootstrap.channel(NioSocketChannel.class);//指定請求地址bootstrap.remoteAddress(new InetSocketAddress(nettyHost,port));bootstrap.option(ChannelOption.TCP_NODELAY,true);final ConnectionWatchdog watchDog = new ConnectionWatchdog(bootstrap, new HashedWheelTimer(), nettyHost,nettyHostRe, port) {@Overridepublic ChannelHandler[] handlers() {return new ChannelHandler[]{new MessageEncoder(),new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4),new MessageDecoder(),this,// 每隔5s的時間觸發一次userEventTriggered的方法,并且指定IdleState的狀態位是WRITER_IDLEnew IdleStateHandler(0, 1, 0, TimeUnit.SECONDS),// 實現userEventTriggered方法,并在state是WRITER_IDLE的時候發送一個心跳包到sever端,告訴server端我還活著new ClientHeartBeatHandler(),};}};final ChannelFuture future;try {synchronized (bootstrap) {bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(watchDog.handlers());}});future = bootstrap.connect().sync();// 鏈接服務器.調用sync()方法會同步阻塞//服務端連接ip:logger.info("目前服務端連接ip為" + nettyHost);}if (!future.isSuccess()) {logger.info("---- 連接服務器失敗,2秒后重試 ---------port=" + port);future.channel().eventLoop().schedule(new Runnable() {@Overridepublic void run() {start(nettyHost,nettyHostRe,nettyPort);}}, 2L, TimeUnit.SECONDS);}} catch (Exception e) {logger.info("exception happends e {}", e);return false;}return true;}}

?

  

?

?

ConnectionWatchdog.java ?:重連檢測狗,當發現當前的鏈路不穩定關閉之后,進行重連

?

@ChannelHandler.Sharable
public abstract class ConnectionWatchdog extends ChannelInboundHandlerAdapter implements TimerTask,ChannelHandlerHolder{//日志輸出private static final Logger logger = LoggerFactory.getLogger(ConnectionWatchdog.class);//客戶端引導類private Bootstrap bootstrap;private Timer timer;private final String host;//備用服務端ipprivate final String host2;//使用ipprivate String useHost;private final int port;private volatile boolean reconnect = true;private int attempts;//刷新時間private volatile long refreshTime = 0L;//心跳連接標識private volatile boolean heartBeatCheck = false;//通道private volatile Channel channel;//失敗次數private static int failCount;public ConnectionWatchdog(Bootstrap boot, Timer timer, String host,String host2, int port) {this.bootstrap = boot;this.timer = timer;this.host = host;this.host2 = host2;this.port = port;}public boolean isReconnect() {return reconnect;}public void setReconnect(boolean reconnect) {this.reconnect = reconnect;}
  
   //連接成功時調用的方法@Overridepublic void channelActive(final ChannelHandlerContext ctx) throws Exception {channel = ctx.channel();attempts = 0;reconnect =false;refreshTime = new Date().getTime();if (!heartBeatCheck) {heartBeatCheck = true;channel.eventLoop().scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {long time = new Date().getTime() - refreshTime;logger.info(String.valueOf(time));if (time > 5 * 1000L) {channel.close();logger.info("心跳檢查失敗");} else {logger.info("心跳檢查Successs");}}}, 5L, 5L, TimeUnit.SECONDS);}logger.info("Connects with {}.", channel);ctx.fireChannelActive();}/*** 因為鏈路斷掉之后,會觸發channelInActive方法,進行重連 2秒重連一次*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {reconnect = true;logger.warn("Disconnects with {}, doReconnect = {},attempts == {}", ctx.channel(), reconnect, attempts);if (reconnect) {/*if (attempts < 12) {attempts++;} else {reconnect = false;}*/long timeout = 2;logger.info("再過 {} 秒客戶端將進行重連",timeout);timer.newTimeout(this, timeout, TimeUnit.SECONDS);}}/** run啟動方法* */public void run(Timeout timeout) throws Exception {//Future表示異步操作的結果final ChannelFuture future;if(failCount > 2){//使用備用ipif(host.equals(useHost)){useHost = host2;}else{useHost = host;}}else {if(StrUtil.IsNullOrEmpty(useHost)) {//首次重連useHost = host;}}synchronized (bootstrap) {future = bootstrap.connect(useHost, port);}//使用future監聽結果,執行異步操作結束后的回調.future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(final ChannelFuture f) throws Exception {boolean succeed = f.isSuccess();logger.warn("連接通過 {}, {}.", useHost + ":" + port, succeed ? "成功" : "失敗");if (!succeed) {logger.info("重連失敗");failCount ++;f.channel().pipeline().fireChannelInactive();}else{failCount = 0;logger.info("重連成功");}}});}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof TcpMsg) {TcpMsg heartMsg = (TcpMsg) msg;if (heartMsg.getType()>=0) {refreshTime = new Date().getTime();}logger.warn("得到服務器響應,響應內容為"+ ((TcpMsg) msg).getBody());}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);Channel channel = ctx.channel();logger.info("客戶端:"+channel.remoteAddress()+"網絡異常");cause.printStackTrace();if(channel.isActive())ctx.close();}} 

這里我們定義了一個變量:?refreshTime,當我們從channel中read到了服務端發來的心跳響應消息的話,就刷新refreshTime為當前時間

當連接成功時,會觸發channelActive 方法,在這里我們開啟了一個定時任務去判斷refreshTime和當前時間的時間差,超過5秒說明斷線了,要進行重連,我這里由于配置了兩個服務器,所有在我的邏輯中,嘗試連接2次以上連不上就去連另一個服務器去了

?

下面的handler用于發送心跳消息,實現userEventTriggered方法,并在stateWRITER_IDLE的時候發送一個心跳包到sever端,告訴server端我還活著

?

@Component
public class ClientHeartBeatHandler extends ChannelInboundHandlerAdapter {private static final Logger logger = LoggerFactory.getLogger(ClientHeartBeatHandler.class);@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {clientname = ReadFileUtil.readFile("C:/CrawlProgram/wrapper_nettyClient/name.txt");if (evt instanceof IdleStateEvent) {IdleState state = ((IdleStateEvent) evt).state();if (state == IdleState.WRITER_IDLE) {//用于心跳的客戶端類型為0int type = 0;//客戶端機器名String body = clientname;TcpMsg msg = new TcpMsg(type,body);try {ctx.writeAndFlush(msg).sync();logger.info("發送消息成功,消息類型為:"+type+",請求id為" + msg.getGuid() + ",客戶端機器號為:" + msg.getBody());} catch (Exception ex) {ex.printStackTrace();logger.info("發送失敗");}}} else {super.userEventTriggered(ctx, evt);}}}

?

  然后就是和服務端一樣的decoder、encoder過程,不同的是,我們在decoder的時候使用了線程池去將任務放入隊列中去,防止請求慢的時候丟失任務請求

MessageDecoder.java

public class MessageDecoder extends ByteToMessageDecoder {private static final Logger logger = LoggerFactory.getLogger(MessageDecoder.class);@Autowiredprivate VisiableThreadPoolTaskExecutor visiableThreadPoolTaskExecutor;//線程池常量public static VisiableThreadPoolTaskExecutor executor;private TcpMsg tcpMsg;List<Object> out;// 用@PostConstruct方法引導綁定@PostConstructpublic void init() {executor = visiableThreadPoolTaskExecutor;encryptService =  encrypt;orderService = order;}@Overridepublic void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {this.context = ctx;this.out = out;int len = in.readableBytes();if (len > 0) {logger.info("得到返回數據,長度為" + len);byte[] bytes = new byte[len];in.readBytes(bytes);TcpMsg msg = TcpMsg.ByteToObj(bytes);this.tcpMsg = msg;logger.info("start asyncServiceExecutor");executor.execute(new Runnable() {@Overridepublic void run() {executeTask();}});logger.info("end executeAsync");}}}

  這里,我們使用了netty來實現了服務端、客戶端通信、心跳檢測的功能。體會到了netty的傳輸效率高、封裝好的特性,用起來簡單、實用。我們不僅可以做斷線重連、還可以做很多業務請求,可以配置多臺客戶端去做不同的事情,來達到服務器調度的目的。

?  歸根結底,netty還是一個框架的東西,我們還是沒有過多的去看透nio的本質、我們要做的不僅僅是會用netty,而且還要了解nio、了解netty的實現原理,它的底層是如何封裝的,希望大家多去研究,我們一起去搞懂它

?

Netty 的 Channel 過濾器實現原理與 Servlet Filter 機制一致,它將 Channel 的數據管道抽象為 ChannelPipeline,消息在 ChannelPipeline 中流動和傳遞。ChannelPipeline 持有 I/O 事件攔截器 ChannelHandler 的鏈表,由 ChannelHandler 來對 I/O 事件進行具體的攔截和處理,可以方便地通過新增和刪除 ChannelHandler 來實現不同業務邏輯的定制,能夠實現對修改封閉和對擴展到支持。

轉載于:https://www.cnblogs.com/GodHeng/p/9480726.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279600.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279600.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279600.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

為什么您仍然不應該購買《星球大戰:前線II》

If you’ve been following video game news at all for the last couple of weeks, you’ve probably heard that EA’s Star Wars: Battlefront II is having some teething troubles. EA has backpedaled to avoid more controversy, but we’re here to say: don’t fall f…

web 后臺返回json格式數據的方式(status 406)

1.在類上使用注解 RestController public class HttpComentInterface {} 2.在方法是使用注解 ResponseBody RequestMapping(path "/interface/queryRemote", method RequestMethod.POST) //可以指定請求方式ResponseBody public RemoteCommentResultData queryCo…

OpenStack Juno系列之計算節點搭建

OpenStack Juno系列之計算節點搭建 nova-compute安裝配置 -------------------- apt-get install nova-compute sysfsutils 編輯配置文件 vi /etc/nova/nova.conf [DEFAULT] verbose True rpc_backend rabbit rabbit_host controller rabbit_password RABBIT_PASS auth_str…

quantum_如何從Firefox Quantum刪除Pocket

quantumFirefox Quantum has deep integration with the Pocket read-it-later service, which is now owned by Mozilla. You’ll see a Pocket page action in the address bar, a “View Pocket List” feature in the Library, and recommended articles from Pocket on th…

vue-typescript

教你搭建typescript的vue項目 自尤大神去年9月推出vue對typescript的支持后&#xff0c;一直想開箱嘗試vuets&#xff0c;最近一個新項目準備入手typescript&#xff0c;也遇到了很多坑&#xff0c;下面就一步步來吧&#xff01;&#xff01;&#xff01; 1. 項目創建和初始化 …

Couchbase概述

Couchbase概述 Couchbase概述 Couchbase概述Couchbase最早叫Membase&#xff0c;是由Memcached項目組的一些頭目另立的山頭。2011年與CouchDB合并&#xff0c;正式命名為Couchbase。2013年&#xff0c;作為NoSQL技術初創企業&#xff0c;拿到了2500萬美元的D輪投資。截稿時止&a…

Windows 2012 - Dynamic Access Control 淺析

Windows 2012相對于前幾個版本而已&#xff0c;做出了大量的改進&#xff0c;尤其體現在安全性和虛擬化方面。Dynamic Access Control ( 動態訪問控制&#xff09;是微軟在文件服務器的訪問控制上的新功能&#xff0c;極大的提高了安全性和靈活性。經過一天的研究學習&#xff…

windows rt_如何在Windows RT上輕松將網站添加到Flash白名單

windows rtMicrosoft’s Surface RT and other Windows RT-based machines include the Flash browser plugin, but it only runs on websites Microsoft has whitelisted. We have covered how you can add any website to the Flash whitelist, but now there’s an easier w…

powershell實現設置程序相關性腳本

公司一直有臺服務器cpu占用很高&#xff0c;分析出是恒生監控程序java占用很高&#xff0c;且三個java程序&#xff0c;僅其中一個很高&#xff0c;要恒生解決&#xff0c;一直未解決&#xff0c;導致每周重啟&#xff0c;我司運維都要手動進行程序相關性設置&#xff0c;給運維…

解決npm 的 shasum check failed for錯誤

使用npm安裝一些包失敗&#xff0c;類似如下報錯情況&#xff1a; C:\Program Files\nodejs>npm update npm npm ERR! Windows_NT 10.0.14393 npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\np…

chromebook刷機_您可以購買的最好的Chromebook,2017年版

chromebook刷機While once considered a novelty item by many tech enthusiasts, Chromebooks have broken out of the “just a browser” mold and become legitimate laptops. They’re full-featured, lightweight machines that can do everything most users need them …

Jmeter JDBC請求-----數據庫讀取數據進行參數化 通過SSH跳板機連接數據庫

前期準備&#xff1a; jdbc驅動&#xff1a;mysql-connector-java-5.1.7-bin.jar Jmeter 要鏈接MySQL數據庫&#xff0c;首選需要下載mysql jdbc驅動包&#xff08;注&#xff1a;驅動包的版本一定要與你數據庫的版本匹配&#xff0c;驅動版本低于mysql版本有可能會導致連接失敗…

Exchange server 2010 beta安裝部署流程

本文使用了微軟公開發布的exchange server 2010 beta進行部署測試。這篇文檔將用到下列產品 windows server 2008 64bit enterprise AD function at windows server 2008 exchange server 2010 beta ----------該exchange server 2010 beta版本屬于早期版本&#xff0c;目前最新…

08.15《CEP職業發展規劃課》

在12,13號的放假后&#xff0c;14,15號安排了CEP職業發展規劃課&#xff0c;為期兩天的課&#xff0c;內容也是很豐富。 在14號的早上&#xff0c;學習了職場中的基本禮儀、和基本素養的培訓&#xff0c;同時對未來的職業規劃做出了大致的分析。 在14號的下午開始了簡歷寫作的課…

usb 驅動修復_您可以修復物理損壞的USB驅動器嗎?

usb 驅動修復Sometimes accidents happen to a USB drive, and you find yourself in a very bad position when your only copy of an important document is on there. When something like this happens, is it possible to fix a physically broken USB drive? Today’s S…

大白話5分鐘帶你走進人工智能-第二十節邏輯回歸和Softmax多分類問題(5)

大白話5分鐘帶你走進人工智能-第二十節邏輯回歸和Softmax多分類問題(5) 上一節中&#xff0c;我們講解了邏輯回歸的優化&#xff0c;本節的話我們講解邏輯回歸做多分類問題以及傳統的多分類問題&#xff0c;我們用什么手段解決。 先看一個場景&#xff0c;假如我們現在的數據集…

Exchange 2016部署實施案例篇-01.架構設計篇(上)

年前就答應大家要給大家寫一個關于Exchange規劃部署的文章&#xff0c;一直忙到現在也沒有倒出時間來寫這個東西。特別是節后&#xff0c;更是開工不利啊&#xff0c;各種奇葩問題&#xff0c;真心無語了。廢話就不多說了&#xff0c;開始今天的議題。 相信各位對Microsoft Exc…

bzoj 4598: [Sdoi2016]模式字符串

題目描述 給出n個結點的樹結構T&#xff0c;其中每一個結點上有一個字符&#xff0c;這里我們所說的字符只考慮大寫字母A到Z&#xff0c;再給出長度為m的模式串s&#xff0c;其中每一位仍然是A到z的大寫字母。 Alice希望知道&#xff0c;有多少對結點<u&#xff0c;v>滿足…

[譯] 機器學習可以建模簡單的數學函數嗎?

原文地址&#xff1a;Can Machine Learning model simple Math functions?原文作者&#xff1a;Harsh Sahu譯文出自&#xff1a;掘金翻譯計劃本文永久鏈接&#xff1a;github.com/xitu/gold-m…譯者&#xff1a;Minghao23校對者&#xff1a;lsvih&#xff0c;zoomdong機器學習…

下載spotify音樂_如何在Spotify上播放更高質量的音樂

下載spotify音樂With Spotify Premium, you get access to higher quality music streaming. By default (and if you’re on the free plan), Spotify streams at 96kbps on mobile and 160kbps on your computer. At these sort of bitrates, you’ll hear a small but notic…