Bootstrap 是 Netty 提供的客户端和服务器启动入口类,其提供了完善的服务配置参数,供开发者合理化配置自身服务。源码分析将从Bootstrap开始着手,逐个分析Netty的各个核心组建及设计理念。
写在前面
Java NIO已经将Java对于网络IO的操作提供了完善的API,Netty只是将其包装更方便使用,因此Netty源码分析的重点应该是Java NIO,因此需要先行了解Java NIO的API。
入手
开发一个TCP服务一般从EchoServer和EcheClient开始,顾名思义就是讲发送的内容原封不动的返回至客户端。
客户端代码核心:
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true )
.handler(new ChannelInitializer() {
@Override
public void initChannel (SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect(HOST, PORT).sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
服务器代码核心:
EventLoopGroup bossGroup = new NioEventLoopGroup(1 );
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100 )
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
public void initChannel (SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoServerHandler());
}
});
ChannelFuture f = b.bind(PORT).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
上述demo是一个netty的入门demo,但是这里包含了很多接下来将会重点介绍的内容,包括Reactor模型的NioEventLoop,抽象连接的Channel,处理数据的ChannelHandler和非阻塞相关的ChannelFuture。
1.EventLoop
客户端和服务器初始化EventLoopGroup时明显存在差异,服务器会初始化两个EventLoopGroup,其中bossGroup用户处理连接请求,workerGroup用于处理后续连接上的IO事件。因此客户端只需要一个workerGroup去处理IO事件。
在初始化bootstrap时,传入的EventLoop类型都是NioEventLoopGroup,分析其构造函数最终调用的是MultithreadEventExecutorGroup的构造函数。Netty允许我们设置EventLoop中线程数量,如果没有设置则使用处理器核心数 * 2。
protected MultithreadEventExecutorGroup (int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
...
children = new EventExecutor[nThreads];
for (int i = 0 ; i < nThreads; i ++) {
boolean success = false ;
try {
children[i] = newChild(executor, args);
success = true ;
} catch (Exception e) {
throw new IllegalStateException("failed to create a child event loop" , e);
} finally {
...
}
}
chooser = chooserFactory.newChooser(children);
...
}
根据代码, 我们可以发现生成了一个大小为 nThreads 的 EventExecutor数组,newChild方法的实现在NioEventLoopGroup中,生成的实例是NioEventLoop。另外根据 nThreads是否是2的幂选择chooser,如果是2的幂使用PowerOfTwoEventExecutorChooser,反之使用 GenericEventExecutorChooser。chooser的功能是每当 Netty 需要一个 EventLoop 时,会调用 next() 方法获取一个可用的 EventLoop,其实现方式基本类似。
public EventExecutor next () {
return chooser.next();
}
2.Channel
我们以TCP为例,Netty使用Channel抽象一个Socket连接,它提供了完善的Socket连接状态查询以及网络IO读写等操作。另外Netty也提供了很多不同阻塞类型和不同协议的Channel,其中NIO为前缀的表示非阻塞,OIO为前缀的便是阻塞:
NioSocketChannel, 代表异步的客户端 TCP Socket 连接.
NioServerSocketChannel, 异步的服务器端 TCP Socket 连接.
NioDatagramChannel, 异步的 UDP 连接
NioSctpChannel, 异步的客户端 Sctp 连接.
NioSctpServerChannel, 异步的 Sctp 服务器端连接.
OioSocketChannel, 同步的客户端 TCP Socket 连接.
OioServerSocketChannel, 同步的服务器端 TCP Socket 连接.
OioDatagramChannel, 同步的 UDP 连接
OioSctpChannel, 同步的 Sctp 服务器端连接.
OioSctpServerChannel, 同步的客户端 TCP Socket 连接.
Bootstrap通过channel()方法设置创建的Channel类型,其实现过程是设置了ChannelFactory
public B channel (Class channelClass) {
if (channelClass == null ) {
throw new NullPointerException("channelClass" );
}
return channelFactory(new BootstrapChannelFactory(channelClass));
}
而 BootstrapChannelFactory 实现了 ChannelFactory 接口, 它提供了唯一的方法, 即 newChannel ()。其核心代码如下:
@Override
public T newChannel () {
try {
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
客户端和服务器的行为是不同的,因此在实例化Channel这个动作也存在不同,客户端通过connect()触发,而服务器通过bind()触发,这两个操作对于客户端或者服务器来说虽然不同但是却必须的操作。
初始化Channel的函数是AbstractBootstrap#initAndRegister(),共三步:
创建Channel实例
第二步初始化Channel
Channel注册至EventLoop。
final ChannelFuture initAndRegister() {
final Channel channel = channelFactory().newChannel();
try {
init(channel);
} catch (Throwable t) {
...
}
ChannelFuture regFuture = group().register(channel);
...
}
2.1创建Channel
ChannelFactory#newChannel 中,通过newInstance 来创建一个新 Channel 实例,客户端和服务器传入的Channel类型不同,其中客户端传入的是NioSocketChannel ,而服务器传入的是ServerSocketChannel。
我们首先分析NioSocketChannel 默认构造器代码如下:
public NioSocketChannel () {
this (DEFAULT_SELECTOR_PROVIDER);
}
/**
* Create a new instance using the given {@link SelectorProvider}.
*/
public NioSocketChannel (SelectorProvider provider) {
this (newSocket(provider));
}
/**
* Create a new instance using the given {@link SocketChannel}.
*/
public NioSocketChannel (SocketChannel socket) {
this (null , socket);
}
public NioSocketChannel (Channel parent, SocketChannel socket) {
super (parent, socket);
config = new NioSocketChannelConfig(this , socket.socket());
}
其调用的父类构造函数过程:NioSocketChannel->AbstractNioByteChannel->AbstractNioChannel->AbstractChannel。AbstractNioByteChannel的构造函数中可以看到客户端主动连接的channel的监听事件是OP_READ
protected AbstractNioByteChannel (Channel parent, SelectableChannel ch) {
super (parent, ch, SelectionKey.OP_READ);
}
其中newSocket方法会通过创建一个新的Java NIO socketChannel
private static SocketChannel newSocket (SelectorProvider provider) {
try {
return provider.openSocketChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a socket." , e);
}
}
AbstractNioChannel中可以看到将channel设置为非阻塞。
protected AbstractNioChannel (Channel parent, SelectableChannel ch, int readInterestOp) {
super (parent);
this .ch = ch;
this .readInterestOp = readInterestOp;
try {
ch.configureBlocking(false );
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket." , e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode." , e);
}
}
这里我们可以得到几个结论:
parent为null
每个channel都会生成一个id
在创建channel时会创建一个与之相关联的unsafe。unsafe是java对于底层实现的封装,不同类型的channel的不同表现主要通过unsafe来体现,关于unsafe会在后续章节详细介绍。
同样每个channel还会创建出与之相关联的ChannelPipeline。ChannelPipeline是netty对于数据流转及数据读写的重要通道,其根据IO读写分为两个数据流向,是Netty中非常关键的核心流程。关于ChannelPipeline会在后续章节详细介绍。
protected AbstractChannel (Channel parent) {
this .parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
而NioServerSocketChannel的默认构造函数代码如下,可以看到服务器创建的channel的监听事件是OP_ACCEPT。:
public NioServerSocketChannel () {
this (newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel (SelectorProvider provider) {
this (newSocket(provider));
}
public NioServerSocketChannel (ServerSocketChannel channel) {
super (null , channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this , javaChannel().socket());
}
NioServerSocketChannel的构造过程NioServerSocketChannel->AbstractNioMessageChannel->AbstractNioChannel->AbstractChannel。可以看到服务器和客户端拥有不同的channel父类,可想而知其unsafe的实现也是不一样的。
2.2初始化Channel
其中init(channel)方法是个抽象方法,由子类Bootstrap和ServerBootstrap
abstract void init(Channel channel) throws Exception;
Bootstrap中init的方法的主要功能是设置Channel的属性
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(handler());
final Map, Object> options = options();
synchronized (options) {
for (Entry, Object> e: options.entrySet()) {
try {
if (!channel.config().setOption((ChannelOption) e.getKey(), e.getValue())) {
logger.warn("Unknown channel option: " + e);
}
} catch (Throwable t) {
logger.warn("Failed to set a channel option: " + channel, t);
}
}
}
final Map, Object> attrs = attrs();
synchronized (attrs) {
for (Entry, Object> e: attrs.entrySet()) {
channel.attr((AttributeKey) e.getKey()).set(e.getValue());
}
}
}
而ServerBootstrap的init方法不同点在于,客户端init的是主动连接的channel的option和attrs,而服务器需要初始化两部分channel,第一个是用于接收客户端连接请求,也就是OP_ACCEPT事件,第二部分则是接收客户端的数据,也就是OP_READ事件。
void init(Channel channel) throws Exception {
final Map, Object> options = options();
synchronized (options) {
channel.config().setOptions(options);
}
final Map, Object> attrs = attrs();
synchronized (attrs) {
for (Entry, Object> e: attrs.entrySet()) {
@SuppressWarnings ("unchecked" )
AttributeKey key = (AttributeKey) e.getKey();
channel.attr(key).set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
if (handler() != null ) {
p.addLast(handler());
}
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry, Object>[] currentChildOptions;
final Entry, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
p.addLast(new ChannelInitializer() {
@Override
public void initChannel (Channel ch) throws Exception {
ch.pipeline().addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
其中ServerBootstrapAcceptor既是负责接收客户端连接请求:
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter
可以看到ServerBootstrapAcceptor是关心Inbound事件的ChannelHandler,关于ChannelHandler会在后面章节详细介绍,其需要实现的主要函数channelRead如下:
public void channelRead (ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
for (Entry, Object> e: childOptions) {
try {
if (!child.config().setOption((ChannelOption) e.getKey(), e.getValue())) {
logger.warn("Unknown channel option: " + e);
}
} catch (Throwable t) {
logger.warn("Failed to set a channel option: " + child, t);
}
}
for (Entry, Object> e: childAttrs) {
child.attr((AttributeKey) e.getKey()).set(e.getValue());
}
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
...
});
} catch (Throwable t) {
...
}
}
其主要既是设置channel的channelHandler,option和attrs并将其注册至对应EventLoop。
最后我们来总结一下服务器端的 handler 与 childHandler 的区别与联系:
在服务器 NioServerSocketChannel 的 pipeline 中添加的是 handler 与 ServerBootstrapAcceptor。
当有新的客户端连接请求时, ServerBootstrapAcceptor.channelRead 中负责新建此连接的 NioSocketChannel 并添加 childHandler 到 NioSocketChannel 对应的 pipeline 中, 并将此 channel 绑定到 workerGroup 中的某个 eventLoop 中。
handler 是在 accept 阶段起作用, 它处理客户端的连接请求。
childHandler 是在客户端连接建立以后起作用, 它负责处理客户端的 IO 事件。
2.3将Channel注册至EventLoop
当Channel 初始化后,会紧接着调用 group().register() 方法来注册 Channel, 我们继续跟踪的话, 会发现其调用链如下:
AbstractBootstrap#initAndRegister -> SingleThreadEventLoop#register -> AbstractUnsafe#register->AbstractNioChannel#doRegister
@Override
public ChannelFuture register (Channel channel) {
return register(new DefaultChannelPromise(channel, this ));
}
@Override
public ChannelFuture register (final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise" );
promise.channel().unsafe().register(this , promise);
return promise;
}
unsafe方法中有着Netty特色的代码块if(inEvenLoop) {…}else {execute},这是Netty线程模型的体现,会在后续章节EventLoop中重点介绍。
@Override
public final void register (EventLoop eventLoop, final ChannelPromise promise) {
...
AbstractChannel.this .eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run () {
register0(promise);
}
});
} catch (Throwable t) {
...
}
}
}
其最终调用的方法,其中pipeline.invokeXXX和pipeline.fireXXX是NettyChannel事件传递的前缀写法。
private void register0 (ChannelPromise promise) {
try {
...
boolean firstRegistration = neverRegistered;
doRegister();
neverRegistered = false ;
registered = true ;
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
...
}
}
其中doRegister最终我们将Channel注册到与 eventLoop 关联的 selector 上。
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0 , this);
return ;
} catch (CancelledKeyException e) {
...
}
}
}
另外register事件会通过fireChannelRegistered()在ChannelPipeline中传递,其中ChannelInitializer就是在channelRegistered事件中将用户定义的ChannelHandler加入ChannelPipeline中:
public final void channelRegistered (ChannelHandlerContext ctx) throws Exception {
ChannelPipeline pipeline = ctx.pipeline();
boolean success = false ;
try {
initChannel((C) ctx.channel());
pipeline.remove(this );
ctx.fireChannelRegistered();
success = true ;
} catch (Throwable t) {
logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
} finally {
if (pipeline.context(this ) != null ) {
pipeline.remove(this );
}
if (!success) {
ctx.close();
}
}
}
同时通过pipeline.remove(this)方法将自身从pipeline中删除,即ChannelInitializer在channel的register事件中会被删除。
总的来说,Channel 注册过程所做的工作就是将 Channel 与对应的 EventLoop 关联,之后这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的,最终的注册还是将 Java NIO SocketChannel 注册到指定的 selector 中,并通过selector完成网络事件的轮询。
3.connect
对于客户端,最核心的工作就是发起TCP连接,其核心方法是connect,最终调用的是 doConnect方法, 其实现如下:
private static void doConnect (
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
final Channel channel = connectPromise.channel();
channel.eventLoop().execute(new Runnable() {
@Override
public void run () {
if (localAddress == null ) {
channel.connect(remoteAddress, connectPromise);
} else {
channel.connect(remoteAddress, localAddress, connectPromise);
}
connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
}
});
}
channel的具体类型是NioSocketChannel ,其源码如下:
@Override
public ChannelFuture connect (SocketAddress remoteAddress, ChannelPromise promise) {
return pipeline.connect(remoteAddress, promise);
}
pipeline的默认实现是DefaultChannelPipeline,其源码如下:
@Override
public final ChannelFuture connect (SocketAddress remoteAddress, ChannelPromise promise) {
return tail.connect(remoteAddress, promise);
}
在DefaultChannelPipeline由一个head和tail组成的双向链表,其链表上的entry是ChannelHandlerContext,像demo中的EchoClientHandler就会挂在这条双向链表中。IO写事件是由tail->head的,IO读事件是由head->tail的,因此unsafe操作都在head中实现。
public void connect (
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
unsafe.connect(remoteAddress, localAddress, promise);
}
最终的发送代码都在unsafe中实现,而不同的Channel也会拥有不同的unsafe,根据我们的NioSocketChannel 实例可以找到其unsafe的connect实现:
public final void connect (
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
...
boolean wasActive = isActive();
if (doConnect(remoteAddress, localAddress)) {
fulfillConnectPromise(promise, wasActive);
} else {
...
}
} catch (Throwable t) {
...
}
}
其中doConnect实现在NioSocketChannel 中:
protected boolean doConnect (SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null ) {
doBind0(localAddress);
}
boolean success = false ;
try {
boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
if (!connected) {
selectionKey().interestOps(SelectionKey.OP_CONNECT);
}
success = true ;
return connected;
} finally {
if (!success) {
doClose();
}
}
}
到此我们终于找到了Netty封装的Java NIO函数完成了具体的socket连接。
4.bind
bind方法是服务器必经方法,其最终调用代码如下:
private ChannelFuture doBind (final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
...
if (regFuture.isDone()) {
...
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
...
return promise;
}
}
其中initAndRegister在上述文章中已经介绍,主要完成channel初始化及注册至Eventloop。
private static void doBind0 (
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
...
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
}
接着又是我们熟悉的流程,从channel->tail->head->unsafe
@Override
public ChannelFuture bind (SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
pipeline的bind实现在DefaultChannelPipeline的tail节点中函数中:
@Override
public final ChannelFuture bind (SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
bind函数传递过程:
public ChannelFuture bind (final SocketAddress localAddress, final ChannelPromise promise) {
...
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
...
}
return promise;
}
最后走到pipeline中的head节点:
@Override
public void bind (
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
unsafe.bind(localAddress, promise);
}
unsafe的定义在AbstractChannel中:
@Override
public final void bind (final SocketAddress localAddress, final ChannelPromise promise) {
...
try {
doBind(localAddress);
} catch (Throwable t) {
...
}
...
safeSetSuccess(promise);
}
其中doBind是个抽象方法,因为在服务器端指定的channel类型是NioServerSocketChannel,因此该方法的具体实现在NioServerSocketChannel中:
@Override
protected void doBind (SocketAddress localAddress) throws Exception {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
总结
我们从bootstrap为源头分析了Netty作为客户端和服务器的核心流程,以及揭开了Netty众多模块的面纱,之后的章节将会逐个详细分析。希望读者在阅读源码之前能够有一定的Netty开发经验,并且不必陷入复杂琐碎的细节中,先了解整体流程,再逐个击破,实在无法理解的可以先有个概念,日后再慢慢思考。
你可能感兴趣的:(Java,Netty)
Java语言的物联网
岚溪韵
包罗万象 golang 开发语言 后端
以Java语言的物联网引言物联网(IoT,InternetofThings)是近年来科技发展的一个重要趋势,它连接着各种设备和传感器,通过互联网实现数据的采集、传输和分析,从而为我们的生活和工作带来了便利与高效。随着物联网的发展,编程语言在其中扮演着至关重要的角色。本文将深入探讨Java语言在物联网领域的应用,分析其优势与挑战,并通过实际案例来阐明Java在物联网开发中的实际效果。一、物联网的概念
mac安装jmeter
weixin_41849663
jmeter
前言:最近换了mac,又要安装环境了,记录一下安装过程。一、Jmeter简介ApacheJMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。目前也是测试领域最常用的接口测试和压力测试工具之一。因为JMeter是基于Java开发的,所以使用Jmeter前,需要先安装JDK,然后再安装JMeter。先确认是否有
高级 Java 资源管理:文件和网络优化完整指南
谏君之
java java 网络 开发语言 编程
Java资源管理是构建健壮高效的应用程序的基础。让我们探索有效管理文件和网络资源的高级技术。Java中的资源管理围绕正确处理系统资源(如文件、网络连接和数据库连接)展开。主要目标是确保资源在使用后及时释放,防止内存泄漏和系统资源耗尽。Try-with-resources是处理AutoCloseable资源的最有效模式。下面是一个全面的示例:publicclassResourceHandler{pu
python 高级特性之迭代
网罗开发
python集 Python 技术汇总 python 高级特性 迭代
python学习笔记,特做记录,分享给大家,希望对大家有所帮助。迭代如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。在Python中,迭代是通过for…in来完成的,而很多语言比如C语言,迭代list是通过下标完成的,比如Java代码:for(i=0;i
Java正则表达式
L_!!!
java java
正则表达式1.1正则表达式的概念及演示在Java中,我们经常需要验证一些字符串,例如:年龄必须是2位的数字、用户名必须是8位长度而且只能包含大小写字母、数字等。正则表达式就是用来验证各种字符串的规则。它内部描述了一些规则,我们可以验证用户输入的字符串是否匹配这个规则。先看一个不使用正则表达式验证的例子:下面的程序让用户输入一个QQ号码,我们要验证:QQ号码必须是5--15位长度而且必须全部是数字而
Java 8 Stream API
优知blog
程序员 java 后端
Lambda表达式如何匹配类型接口?=>函数式接口函数式接口是一种只有单一抽象方法的接口,使用@FunctionalInterface来描述,可以隐式地转换成Lambda表达式使用Lambda表达式创建函数式接口的示例,可以让函数成为程序的一等公民,从而像普通数据一样当作参数传递JDK的java.util.function中提供了许多原生的函数式接口,如Supplier@FunctionalInt
【Java学习】多态
Brookty
java 学习
目录一、方法相同二、方法重写1.概念2.条件三、向上转型1.概念2.方式四、方法绑定五、多态一、方法相同方法相同要求方法名相同、参数列表相同、返回值类型相同(与两方法修饰的访问限定符相不相同、静态非静态状态相不相同无关),而且在子类与父类相同的方法中,子类那边方法的访问权限必须大于等于父类那边方法的访问权限二、方法重写1.概念重写是由子类类变量引用赋给父类类变量引用后父类类变量引用里对原子类类变量
多个文件压缩成一个zip包
敲键盘的小猴子
Java基础 java 开发语言 后端
importorg.apache.log4j.Logger;importjava.io.BufferedInputStream;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.util.List;importjava.util.zip.CRC32;importjav
find 和 filter 都是 JavaScript 数组的常用方法
Libby博仙
javascript 前端 开发语言
find和filter都是JavaScript数组的常用方法,用来查找符合条件的元素,但它们有一些关键的区别:1.find方法返回值:find方法返回数组中第一个符合条件的元素,如果没有找到符合条件的元素,返回undefined。用途:用于查找并返回第一个符合条件的元素。结束早:一旦找到符合条件的元素,find就会停止遍历,并返回该元素。语法:constresult=array.find(call
Java在物流自动化领域的核心优势分析-3,500台仓库机器人调度设计
爱吃青菜的大力水手
java 自动化 机器人
3,500台仓库机器人调度系统设计方案Java作为仓库机器人调度核心语言的综合优势分析一、Java的高性能特性支撑复杂业务场景JIT编译优化Java的即时编译器(JIT)将字节码动态编译为本地机器码,在调度机器的实时路径规划场景中,能实现每秒600,000次约束条件调整的计算效率。这种优化使Java在长期运行的高负载系统中保持稳定性能(evi3)(evi1)。内存管理机制自动化垃圾回收(GC)机制
Selenium 启动 ChromeDriver 报错 - org.openqa.selenium.net.UrlChecker
likeflower950
java异常处理
前提条件chrome版本72selenium-server版本3.8.1(同时实验3.11.0等高级版本同样报错)错误内容Exceptioninthread"main"java.lang.IllegalAccessError:triedtoaccessmethodcom.google.common.util.concurrent.SimpleTimeLimiter.(Ljava/util/conc
Log4j
言慢行善
log4j
为什么使用日志框架:Log4j是Apache开发,现已到了Log4j2.x版本在Java程序中记录日志,帮助开发者在运行时追踪信息、警告、错误等内容可以控制每一条日志的输出格式通过一个配置文件,而不需要改应用的代码实际场景:周六周日程序出错!周一上班查原因。若请求报错,直接现场重新发送?有的请求因业务原因,不能重复发送。那当时错误怎么查看?只能从开发者设置的日志文件中找原因了。导包+简单使用开发过
软件测试——白盒测试
J‘s土味blink
软件测试 白盒测试
问题:一、使用java实现如下功能的程序:输入三条边a,b,c,满足0<=a<=200,0<=b<=200,0<=c<=200,判断是否能构成三角形,分别输出不能构成三角形、等边三角形、等腰三角形、直角三角形、一般三角形。(1)判断三条边是否合法(2)判断两条边之和是否大于第三边(3)判断三条边是否能够组成三角形(4)判断两条边是否相等(5)求三角形有几条边相等,返回值:相等边的数量
将 Google CDN 替换为国内源的 Chrome 扩展,实现网站加速⚡️
GitHub源码地址:https://github.com/justjavac/...。将GoogleCDN替换为国内的。缘起由于众所周知的原因,只需替换一个域名就可以继续使用Google提供的前端公共库了。同样,通过script标记引用这些资源,让网站访问速度瞬间提速!很多网站,尤其是国外网站,为了加快网站的速度,都使用了Google的CDN。但是在天朝,由于某些原因,导致全球最快的CDN变成了
Unity 接入SDK (Android)
乃敢与君决
java unity c#
前几篇帖子主要是写了一些安卓平台的基本操作,unity接入sdk是有多平台的,android,ios等,今天呢先写写如何接入安卓sdk;首先呢sdk是个啥呢,官方定义咱们总结出来就是一个工具包,他可以使应用连接到其他平台我们要了解unity接入安卓平台的话他这个交互方式是怎么样的,安卓的话是主体是java,然后unity这边呢主体看项目封装吧,这个不限制,接下来,咱们就说具体步骤。首先在你的Uni
【SpringBoot】34、SpringBoot整合Redis实现序列化存储Java对象
Asurplus
SpringBoot 2.x系列 redis 序列化 springboot java对象
前面我们已经介绍过【SpringBoot】十七、SpringBoot中整合Redis,我们可以看出,在SpringBoot对Redis做了一系列的自动装配,使用还是非常方便的一、背景1、思考通过我们前面的学习,我们已经可以往Redis中存入字符串,那么我们要往Redis中存入Java对象该怎么办呢?2、方案我们可以将Java对象转化为JSON对象,然后转为JSON字符串,存入Redis,那么我们从
SpringCloud03—服务治理:SpringCloud Eureka
m0_75011249
程序员 spring cloud eureka java
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!spring-boot-starter-parent2.5.1org.springframework.cloudspring-cloud-starter-eureka1.4.7.RELEASEorg.springframework.cloudspring-cloud-dependencies2020
Java 代理模式 (Proxy)详解
冰糖心书房
设计模式 java 代理模式
一、什么是代理模式?定义:代理模式是一种结构型设计模式。它为另一个对象(目标对象/被代理对象)提供一个代理(或占位符),以控制对这个对象的访问。核心思想:通过引入一个代理对象,客户端不直接访问目标对象,而是通过代理对象来间接访问目标对象。代理对象可以控制对目标对象的访问,并可以在访问前后添加额外的操作。意图:控制对一个对象的访问,可以延迟加载、访问控制、增强功能等。二、代理模式的结构代理模式通常包
Java中的自然语言处理(NLP)工具:Stanford NLP、Apache OpenNLP、DL4J
花千树-010
RAG java 自然语言处理 apache nlp AIGC
随着人工智能技术的快速发展,自然语言处理(NLP)已经成为各行各业中不可或缺的技术。对于Java开发者来说,选择合适的NLP工具可以极大地提升开发效率。今天,我们将探讨几款常用的JavaNLP工具:StanfordNLP、ApacheOpenNLP和DL4J,并通过代码实例展示如何使用它们。1.StanfordNLP:功能全面的NLP工具StanfordNLP是由斯坦福大学开发的自然语言处理工具包
Java处理PDF合集
花千树-010
RAG java pdf 开发语言 ocr AIGC
1、Java生态系统中处理PDF的库和工具这篇文章介绍了三款JavaPDF处理库,分别是ApachePDFBox、iText和OpenPDF。ApachePDFBox提供了全面的功能,适用于生成、修改、渲染PDF文档,特别是在文本提取方面很强大;iText功能强大,支持生成复杂的PDF文件以及表单填写、数字签名等操作,但它采用AGPL许可;OpenPDF是iText的开源分支,适用于不受AGPL限
【硬核对比】ReentrantReadWriteLock 被全面碾压?阿里百万级压测揭秘 StampedLock 的真实性能!
努力的靠近目标
并发编程 java
关键词:Java高并发、读写锁性能、StampedLock源码、锁优化、线程安全设计开篇暴击:一个锁的选择失误,让公司一夜损失百万!某金融系统因错误使用ReentrantReadWriteLock,导致对账延迟12小时,直接经济损失300万!同一场景改用StampedLock后,吞吐量提升6倍,延迟降低90%!本文将用源码层暴力拆解+阿里云压测数据+蚂蚁金服实战代码,深度对比两大读写锁,带你避开高
spring boot 发送邮件功能
??????4?A???
spring boot java 前端
邮件发送是一个非常常见的功能,注册时的身份认证、重要通知发送等都会用到邮件发送。Sun公司提供了JavaMail用来实现邮件发送,但是配置烦琐,Spring中提供了JavaMailSender用来简化邮件配置,SpringBoot则提供了MailSenderAutoConfiguration对邮件的发送做了进一步简化,本文将介绍SpringBoot如何实现邮件发送功能。一、环境配置1、导入依赖使用
JavaScript 类型转换的意外
神明木佑
javascript 开发语言 ecmascript
在JavaScript中,类型转换是将一个数据类型转换为另一个数据类型的过程。它可以是显式的,即通过使用特定的转换函数或操作符来实现,也可以是隐式的,即由JavaScript引擎自动完成。以下是JavaScript中的一些常见类型转换规则:字符串转换:使用String()函数或toString()方法可以将其他类型的值转换为字符串类型。varnum=42;varstr=String(num);//
【Kafka专栏 12】实时数据流与任务队列的较量 :Kafka与RabbitMQ有什么不同
夏之以寒
夏之以寒-kafka专栏 kafka rabbitmq 数据流 任务队列
作者名称:夏之以寒作者简介:专注于Java和大数据领域,致力于探索技术的边界,分享前沿的实践和洞见文章专栏:夏之以寒-kafka专栏专栏介绍:本专栏旨在以浅显易懂的方式介绍Kafka的基本概念、核心组件和使用场景,一步步构建起消息队列和流处理的知识体系,无论是对分布式系统感兴趣,还是准备在大数据领域迈出第一步,本专栏都提供所需的一切资源、指导,以及相关面试题,立刻免费订阅,开启Kafka学习之旅!
java设计模式单件模式_Head First设计模式(5):单件模式
weixin_39822493
java设计模式单件模式
更多的可以参考我的博客,也在陆续更新inghttp://www.hspweb.cn/单件模式确保一个类只有一个实例,并提供一个全局访点。例子:学生的学号生成方案,是在学生注册后,通过录入学生的基本信息,包括入学学年、学院、专业、班级等信息后,保存相应的资料后自动生成的。学号生成器的业务算法为:入学学年(2位)+学院代码(2位)+专业代码(2位)+班级代码(2位)+序号(2位)1.目录image2.
基于Java+Spring+vue+element实现旅游信息管理平台系统
网顺技术团队
成品程序项目 java spring vue.js spring boot 课程设计
基于Java+Spring+vue+element实现旅游信息管理平台系统作者主页网顺技术团队欢迎点赞收藏⭐留言文末获取源码联系方式查看下方微信号获取联系方式承接各种定制系统精彩系列推荐精彩专栏推荐订阅不然下次找不到哟Java毕设项目精品实战案例《1000套》感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人文章目录基于Java+Spring
JavaScript之BOM编程
qq_39095899
前端知识入门 javascript
BOM编程什么是BOM?BrowerObjectModel(浏览器对象模型,)关闭浏览器窗口、打开一个新的浏览器窗口、后退、前进、浏览器地址栏上的地址等,都是BOM编程BOM和DOM的区别与联系?BOM的顶级对象是:windowDOM的顶级对象是:document实际上BOM是包括DOM的!1、BOM编程中,window对象是顶级对象,代表浏览器窗口2、window有open和close方法,可以
JVM直接内存详解
fengdongnan
jvm 开发语言 java
直接内存学习JVM内存结构部分时遇到的最后一部分,直接内存。虽然和其他堆栈等不是核心部分,但其类似缓存的特点和与GC相关的特性显得有点特殊,比较好奇这个高速缓存有没有实际开发使用场景,所以写这篇博客记录直接内存的相关知识点与使用场景。概念直接内存(DirectMemory)是操作系统内存和Java内存共用的一片内存区域读写性能高,常见于NIO操作作为数据缓存区可以通过ByteBuffer.allo
Vue+Jest 单元测试
arron4210
前端 vue 单元测试 vue
新到一个公司,要求单元测试覆盖率达50%以上,我们都是后补的单测,其实单测的意义是根据需求提前写好,驱动开发,代替手动测试。然鹅这只是理想。。。这里总结一下各种遇到的单测场景挂载组件,调用elementui,mock函数```javascriptdescribe('页面验证',()=>{constwrapper=getVue({component:onlineFixedPrice,callback
聊聊这两年学习slam啃过的书!
3D视觉工坊
3D视觉从入门到精通 定位 编程语言 人工智能 机器学习 slam
入坑2年多,零七零八买了7、8本书,正好最近研一的新师弟让我来推荐几本,那么,独乐乐不如众乐乐,我就来巴拉巴拉一下我买的这些书吧。以下测评,仅代表个人观点,与书的作者无关(狗头保命)1、【C++PrimerPlus】嗯~这个灰常灰常厚的c++书是我买的第一本书,也是我所有书里除了java最厚的一本(java买了就没看),But,这本巨厚的c++我竟然翻完了!!!当年,年轻的我以为,看完这本书,我就
多线程编程之卫生间
周凡杨
java 并发 卫生间 线程 厕所
如大家所知,火车上车厢的卫生间很小,每次只能容纳一个人,一个车厢只有一个卫生间,这个卫生间会被多个人同时使用,在实际使用时,当一个人进入卫生间时则会把卫生间锁上,等出来时打开门,下一个人进去把门锁上,如果有一个人在卫生间内部则别人的人发现门是锁的则只能在外面等待。问题分析:首先问题中有两个实体,一个是人,一个是厕所,所以设计程序时就可以设计两个类。人是多数的,厕所只有一个(暂且模拟的是一个车厢)。
How to Install GUI to Centos Minimal
sunjing
linux Install Desktop GUI
http://www.namhuy.net/475/how-to-install-gui-to-centos-minimal.html
I have centos 6.3 minimal running as web server. I’m looking to install gui to my server to vnc to my server. You can insta
Shell 函数
daizj
shell 函数
Shell 函数
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
shell中函数的定义格式如下:
[function] funname [()]{
action;
[return int;]
}
说明:
1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、参数返回
Linux服务器新手操作之一
周凡杨
Linux 简单 操作
1.whoami
当一个用户登录Linux系统之后,也许他想知道自己是发哪个用户登录的。
此时可以使用whoami命令。
[ecuser@HA5-DZ05 ~]$ whoami
e
浅谈Socket通信(一)
朱辉辉33
socket
在java中ServerSocket用于服务器端,用来监听端口。通过服务器监听,客户端发送请求,双方建立链接后才能通信。当服务器和客户端建立链接后,两边都会产生一个Socket实例,我们可以通过操作Socket来建立通信。
首先我建立一个ServerSocket对象。当然要导入java.net.ServerSocket包
ServerSock
关于框架的简单认识
西蜀石兰
框架
入职两个月多,依然是一个不会写代码的小白,每天的工作就是看代码,写wiki。
前端接触CSS、HTML、JS等语言,一直在用的CS模型,自然免不了数据库的链接及使用,真心涉及框架,项目中用到的BootStrap算一个吧,哦,JQuery只能算半个框架吧,我更觉得它是另外一种语言。
后台一直是纯Java代码,涉及的框架是Quzrtz和log4j。
都说学前端的要知道三大框架,目前node.
You have an error in your SQL syntax; check the manual that corresponds to your
林鹤霄
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'option,changed_ids ) values('0ac91f167f754c8cbac00e9e3dc372
MySQL5.6的my.ini配置
aigo
mysql
注意:以下配置的服务器硬件是:8核16G内存
[client]
port=3306
[mysql]
default-character-set=utf8
[mysqld]
port=3306
basedir=D:/mysql-5.6.21-win
mysql 全文模糊查找 便捷解决方案
alxw4616
mysql
mysql 全文模糊查找 便捷解决方案
2013/6/14 by 半仙 alxw4616@Msn.com
目的: 项目需求实现模糊查找.
原则: 查询不能超过 1秒.
问题: 目标表中有超过1千万条记录. 使用like '%str%' 进行模糊查询无法达到性能需求.
解决方案: 使用mysql全文索引.
1.全文索引 : MySQL支持全文索引和搜索功能。MySQL中的全文索
自定义数据结构 链表(单项 ,双向,环形)
百合不是茶
单项链表 双向链表
链表与动态数组的实现方式差不多, 数组适合快速删除某个元素 链表则可以快速的保存数组并且可以是不连续的
单项链表;数据从第一个指向最后一个
实现代码:
//定义动态链表
clas
threadLocal实例
bijian1013
java thread java多线程 threadLocal
实例1:
package com.bijian.thread;
public class MyThread extends Thread {
private static ThreadLocal tl = new ThreadLocal() {
protected synchronized Object initialValue() {
return new Inte
activemq安全设置—设置admin的用户名和密码
bijian1013
java activemq
ActiveMQ使用的是jetty服务器, 打开conf/jetty.xml文件,找到
<bean id="adminSecurityConstraint" class="org.eclipse.jetty.util.security.Constraint">
<p
【Java范型一】Java范型详解之范型集合和自定义范型类
bit1129
java
本文详细介绍Java的范型,写一篇关于范型的博客原因有两个,前几天要写个范型方法(返回值根据传入的类型而定),竟然想了半天,最后还是从网上找了个范型方法的写法;再者,前一段时间在看Gson, Gson这个JSON包的精华就在于对范型的优雅简单的处理,看它的源代码就比较迷糊,只其然不知其所以然。所以,还是花点时间系统的整理总结下范型吧。
范型内容
范型集合类
范型类
【HBase十二】HFile存储的是一个列族的数据
bit1129
hbase
在HBase中,每个HFile存储的是一个表中一个列族的数据,也就是说,当一个表中有多个列簇时,针对每个列簇插入数据,最后产生的数据是多个HFile,每个对应一个列族,通过如下操作验证
1. 建立一个有两个列族的表
create 'members','colfam1','colfam2'
2. 在members表中的colfam1中插入50*5
Nginx 官方一个配置实例
ronin47
nginx 配置实例
user www www;
worker_processes 5;
error_log logs/error.log;
pid logs/nginx.pid;
worker_rlimit_nofile 8192;
events {
worker_connections 4096;}
http {
include conf/mim
java-15.输入一颗二元查找树,将该树转换为它的镜像, 即在转换后的二元查找树中,左子树的结点都大于右子树的结点。 用递归和循环
bylijinnan
java
//use recursion
public static void mirrorHelp1(Node node){
if(node==null)return;
swapChild(node);
mirrorHelp1(node.getLeft());
mirrorHelp1(node.getRight());
}
//use no recursion bu
返回null还是empty
bylijinnan
java apache spring 编程
第一个问题,函数是应当返回null还是长度为0的数组(或集合)?
第二个问题,函数输入参数不当时,是异常还是返回null?
先看第一个问题
有两个约定我觉得应当遵守:
1.返回零长度的数组或集合而不是null(详见《Effective Java》)
理由就是,如果返回empty,就可以少了很多not-null判断:
List<Person> list
[科技与项目]工作流厂商的战略机遇期
comsci
工作流
在新的战略平衡形成之前,这里有一个短暂的战略机遇期,只有大概最短6年,最长14年的时间,这段时间就好像我们森林里面的小动物,在秋天中,必须抓紧一切时间存储坚果一样,否则无法熬过漫长的冬季。。。。
在微软,甲骨文,谷歌,IBM,SONY
过度设计-举例
cuityang
过度设计
过度设计,需要更多设计时间和测试成本,如无必要,还是尽量简洁一些好。
未来的事情,比如 访问量,比如数据库的容量,比如是否需要改成分布式 都是无法预料的
再举一个例子,对闰年的判断逻辑:
1、 if($Year%4==0) return True; else return Fasle;
2、if ( ($Year%4==0 &am
java进阶,《Java性能优化权威指南》试读
darkblue086
java性能优化
记得当年随意读了微软出版社的.NET 2.0应用程序调试,才发现调试器如此强大,应用程序开发调试其实真的简单了很多,不仅仅是因为里面介绍了很多调试器工具的使用,更是因为里面寻找问题并重现问题的思想让我震撼,时隔多年,Java已经如日中天,成为许多大型企业应用的首选,而今天,这本《Java性能优化权威指南》让我再次找到了这种感觉,从不经意的开发过程让我刮目相看,原来性能调优不是简单地看看热点在哪里,
网络学习笔记初识OSI七层模型与TCP协议
dcj3sjt126com
学习笔记
协议:在计算机网络中通信各方面所达成的、共同遵守和执行的一系列约定 计算机网络的体系结构:计算机网络的层次结构和各层协议的集合。 两类服务: 面向连接的服务通信双方在通信之前先建立某种状态,并在通信过程中维持这种状态的变化,同时为服务对象预先分配一定的资源。这种服务叫做面向连接的服务。 面向无连接的服务通信双方在通信前后不建立和维持状态,不为服务对象
mac中用命令行运行mysql
dcj3sjt126com
mysql linux mac
参考这篇博客:http://www.cnblogs.com/macro-cheng/archive/2011/10/25/mysql-001.html 感觉workbench不好用(有点先入为主了)。
1,安装mysql
在mysql的官方网站下载 mysql 5.5.23 http://www.mysql.com/downloads/mysql/,根据我的机器的配置情况选择了64
MongDB查询(1)——基本查询[五]
eksliang
mongodb mongodb 查询 mongodb find
MongDB查询
转载请出自出处:http://eksliang.iteye.com/blog/2174452 一、find简介
MongoDB中使用find来进行查询。
API:如下
function ( query , fields , limit , skip, batchSize, options ){.....}
参数含义:
query:查询参数
fie
base64,加密解密 经融加密,对接
y806839048
经融加密 对接
String data0 = new String(Base64.encode(bo.getPaymentResult().getBytes(("GBK"))));
String data1 = new String(Base64.decode(data0.toCharArray()),"GBK");
// 注意编码格式,注意用于加密,解密的要是同
JavaWeb之JSP概述
ihuning
javaweb
什么是JSP?为什么使用JSP?
JSP表示Java Server Page,即嵌有Java代码的HTML页面。使用JSP是因为在HTML中嵌入Java代码比在Java代码中拼接字符串更容易、更方便和更高效。
JSP起源
在很多动态网页中,绝大部分内容都是固定不变的,只有局部内容需要动态产生和改变。
如果使用Servl
apple watch 指南
啸笑天
apple
1. 文档
WatchKit Programming Guide(中译在线版 By @CocoaChina) 译文 译者 原文 概览 - 开始为 Apple Watch 进行开发 @星夜暮晨 Overview - Developing for Apple Watch 概览 - 配置 Xcode 项目 - Overview - Configuring Yo
java经典的基础题目
macroli
java 编程
1.列举出 10个JAVA语言的优势 a:免费,开源,跨平台(平台独立性),简单易用,功能完善,面向对象,健壮性,多线程,结构中立,企业应用的成熟平台, 无线应用 2.列举出JAVA中10个面向对象编程的术语 a:包,类,接口,对象,属性,方法,构造器,继承,封装,多态,抽象,范型 3.列举出JAVA中6个比较常用的包 Java.lang;java.util;java.io;java.sql;ja
你所不知道神奇的js replace正则表达式
qiaolevip
每天进步一点点 学习永无止境 纵观千象 regex
var v = 'C9CFBAA3CAD0';
console.log(v);
var arr = v.split('');
for (var i = 0; i < arr.length; i ++) {
if (i % 2 == 0) arr[i] = '%' + arr[i];
}
console.log(arr.join(''));
console.log(v.r
[一起学Hive]之十五-分析Hive表和分区的统计信息(Statistics)
superlxw1234
hive hive分析表 hive统计信息 hive Statistics
关键字:Hive统计信息、分析Hive表、Hive Statistics
类似于Oracle的分析表,Hive中也提供了分析表和分区的功能,通过自动和手动分析Hive表,将Hive表的一些统计信息存储到元数据中。
表和分区的统计信息主要包括:行数、文件数、原始数据大小、所占存储大小、最后一次操作时间等;
14.1 新表的统计信息
对于一个新创建
Spring Boot 1.2.5 发布
wiselyman
spring boot
Spring Boot 1.2.5已在7月2日发布,现在可以从spring的maven库和maven中心库下载。
这个版本是一个维护的发布版,主要是一些修复以及将Spring的依赖提升至4.1.7(包含重要的安全修复)。
官方建议所有的Spring Boot用户升级这个版本。
项目首页 | 源