分类目录Java

compose

    compose_version = '1.0.2'
    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.activity:activity-ktx:1.3.1'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.ui:ui-util:$compose_version"

Text()
Image(painter = painterResource(id = R.drawable.ic_launcher_foreground), contentDescription = “无障碍说明”)
//bitmap Bitmap ImageBitmap 位图 ImageVector矢量图 Painter painterResource()

https://github.com//google/accompanist
implementation ‘dev.chrisbanes.accompanist:accompanist-coil:0.6.2’
CoilImage(data = “”,contentDescription = “”)

Modifier

    Box() {

    }
    Column() {
        上下
    }
    Row() {
        左右
    }

springboot定时任务

1:
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

2:
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

3.
 <!--http://www.hutool.cn/-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.1</version>
        </dependency>

Netty

<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.48.Final</version>
        </dependency>

NioEventLoop-->thread客服端接收客户端连接
Channel连接的封装-->Socket                       PipeLine逻辑处理链-->Logic Chain
ChanneHandler-->Logic逻辑处理链的处理   ByteBut数据流读写-->IO Bytes

BIO:一个线程负责连接,一请求一应答 阻塞同步
伪异步IO:线程池负责连接,M请求N应答 阻塞同步
NIO:缓冲区Buffer,通道Channel,多路复用器Selector 非阻塞同步
AIO:连接注册读写事件和回调函数,读写方法异步,主动通知程序 非阻塞异步

服务端启动
1. 创建服务端Channel
bind()用户代码入口->initAndRegister()初始化并注册->newChannel()创建ServerSocketChannel ->newSocket()通过JDK创建底层jdk channel->NioServerSocketChannelConfig()配制TCP参数类->AbstractNioChannel()->configureBlocking(false)阻塞模式:AbstractChannel()创建id,undafe,pipeline
1. 初始化服务端Channel
init()初始化入口->set ChannelOptions,ChannelAttrs->set ChildOptions,ChildAttrs->config handler配制服务端pipeline逻辑处理链->add ServerBootstrapAcceptor添加连接器
1. 注册事件轮寻器selector
AbstractChannel.register(channel)入口->this.eventLoop=eventLoop绑定线程->resgiter0()实际注册–doRegister()调用JDK底层注册:invokeHandlerAddIfNeeded()用户回调->fireChannelRegistered)传播事件
1. 端口绑定
AbstractUnsafe.bind()入口->doBind()->javaChannel().bind()JDK底层绑定->pipeline.fireChannelActive()事件传播->HeadContext.readIfIsAutoRead()


NioEventLoop
每次执行创建一个线程实体FastThreadLocalThread,线程命名规则nioEventLoop-1-xx
创建->new NioEventLoopGroup()线程组,默认2*cpu->new ThreadPerTaskExecutor)线程创建器->for(){newChild()}构造NioEventLoop->chooserFactory.newChooser()线程选择器

newChild()
保存x线程执行器ThreadPerTaskExecutor
创建一个MpscQueue
创建一个selector

启动->bind()->execute(task)入口->startThread()->doStartThread()创建线程->ThreadPerTaskExecutor.execute()->thread=Thread.currentThread()->NioEventLoop.run()启动->for(;;)->select()检查是否有IO事件->processSelectedKeys()处理io事件->runAllTasks()处理异步任务队列


新连接接入
检测新连接->创建NioSocketChannel->分配线程及注册selector->向selector注册读事件

processSwlectdKey(key,channel)入口->NioMessageUnsafe.read()->doReadMessages()循环while->javaChannel().accept()

new NioSocketChannel(parent,ch)入口->AbstractNioByteChannel(p,ch,op_read)->configureBlocking(false)&save op:-:create id,unsafe,pipeline->new NioSocketChannelConfig()->setTcpNoDelay(true)禁止Nagle算法

channel分类
NioServerSocketChannel
NioSocketChannel
Unsafe

head->ServerBootstrapAcceptor->Tail


pipeline
1. pipeline初始化
在创建Channel的时候创建,节点数据结构ChannelHandlerContext,两大哨兵head和tail
1. 添加删除ChannelHandler
注意顺序
1. 事件和异常的传播
inBound head->a1->b1->…->tail
在处理ByteBuf时不往下传播注意处理要释放资源,SimpleChannelInboundHandler已实现自动释放,都没有处理的在tail中会释放
outBound head<-a2<-b2<-…<-tail 异常触发链 b1->…->a2->b2->…->tail 最好在链最后添加异常处理器,没有处理过的在最后能一同处理


ByteBuf
内存与内存管理器的抽象
不同规格大小和不同类别的内存的分配策略
内存的回收过程

结构:discardable bytes |readable bytes|writable bytes
批针:0<=readerIndex<=writerIndex<=capacity<=maxCapacity
方法:read write set(在当前位置操作,不会移动指针) mark(保存指针),reset(复位指针)
分类:ByteBuf<-AbstratByteBuf<-
PooledHeapByteBuf:<-PooledUnsafeHeapByteBuf
PooledUnsafeDirectByteBUf:
PooledDirectByteBuf:
UnPooledHeapByteBuf:<-UnPooledUnsafeHeapByteBuf
UnPooledUnsafeDirectByteBUf:
UnPooledDirectByteBuf
Pooled从预先分配好的内存操作 UnPooled直接API分配内存
Unsafe可以通过内存直接拿到 非Unsafe没有依赖底层内存JDK
Heap堆上分配内存 Direct虚拟机外的内存中分配了一块缓冲区,不参与GC,手动释放
Direct Buffer创建和销毁的代价很高,所以要用在尽可能重用的地方。 比如周期长传输文件大采用direct buffer,不然一般情况下就直接用heap buffer 就好。

Thread -> PoolThreadCache(tinyCaheSize smallCacheSize normalCacheSize) -> Area
(SubPage)0–>tiny(N*16B共32种)<--512B-->small<–8K(page 512B,1K,2K,4K)–normal—16M(Chunk 8K,16K,32K)—>huge

释放:连续的内存区段加到缓存,标记连续的内存区段为未使用,ByteBuf加到对象池


解码 ByteToMessageDecoder
累加字节流->调用子类的decode方法解析->解析的ByteBuf向下传播

基于固定长度 FixedLengthFrameDecoder:n长度
基本行解码 LineBasedFrameDecoder:\n,\r\n换行结束符,丢弃和非丢弃模式
基于分隔符 DelimiterBasedFrameDecoder 先行处理,找到最小分隔符,解码
基于长度域 LengthFieldBasedFrameDecoder lengthFieldOffset:2偏移lengthFieldLength:2长度


编码 writeAndFlush
从tail节点往前传播-逐个调用channelHandler的write方法->逐个调用channelHandler的flush方法

模拟百万连接
突破局部文件限制和全局文件限制

Server 8000~8100 Client 1025~65535
ulimit -n
sudo vi /etc/security/limits.conf 增加
* hard nofile 1000000
* soft nofile 1000000

cat /proc/sys/fs/file-max
临时使用,重启回到10000
sudo -s
echo 100000 > /proc/sys/fs/file-max
exit
持久使用
/etc/sysctl.conf 增加
fs.file-max=1000000
sudo sysctl -p /etc/sysctl.conf 后重启

业务优化
1. 在单个业务中使用线程池
1. 给handler指定使用JDK的线程池,做不到内存共享

socket.io for java

Server

<dependency>
      <groupId>com.corundumstudio.socketio</groupId>
      <artifactId>netty-socketio</artifactId>
      <version>1.7.16</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.2</version>
    </dependency>

public class ServerStart {

    public static void main(String [] args){
        Configuration config = new Configuration();
        config.setHostname("localhost");
        config.setPort(9999);
        SocketIOServer server = new SocketIOServer(config);
        server.addConnectListener(new ConnectListener() {
            // 添加客户端连接监听器
            public void onConnect(SocketIOClient client) {
                //logger.info(client.getRemoteAddress() + " web客户端接入");
                //不知道如何与客户端对应,好的办法是自己去写对应的函数
                client.sendEvent("connected", "hello");
            }
        });

        //监听客户端事件,client_info为事件名称,-自定义事件
         server.addEventListener("client_info", String.class, new DataListener<String>(){
            public void onData(SocketIOClient client, String data, AckRequest ackRequest) throws ClassNotFoundException {
                //客户端推送advert_info事件时,onData接受数据,这里是string类型的json数据,还可以为Byte[],object其他类型
                String sa = client.getRemoteAddress().toString();
                String clientIp = sa.substring(1,sa.indexOf(":"));//获取客户端连接的ip
                Map params = client.getHandshakeData().getUrlParams();//获取客户端url参数
                System.out.println(clientIp+":客户端:************"+data);
            }
         });

        //添加客户端断开连接事件
        server.addDisconnectListener(new DisconnectListener(){
            public void onDisconnect(SocketIOClient client) {
                String sa = client.getRemoteAddress().toString();
                String clientIp = sa.substring(1,sa.indexOf(":"));//获取设备ip
                System.out.println(clientIp+"-------------------------"+"客户端已断开连接");
                //给客户端发送消息
                client.sendEvent("advert_info",clientIp+"客户端你好,我是服务端,期待下次和你见面");
            }
        });
        server.start();

        while (true){
            try {
                Thread.sleep(1500);
                //广播消息
                server.getBroadcastOperations().sendEvent("borcast","are you live?");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Client

        <dependency>
            <groupId>io.socket</groupId>
            <artifactId>socket.io-client</artifactId>
            <version>1.0.0</version>
        </dependency>

public class AppStart {

    public static void main(String [] args){
        String url ="http://localhost:9999";
        try{
            IO.Options options = new IO.Options();
            options.transports = new String[]{"websocket"};
            options.reconnectionAttempts = 2;
            options.reconnectionDelay = 1000;//失败重连的时间间隔
            options.timeout = 500;//连接超时时间(ms)
            //par1 是任意参数
            final Socket socket = IO.socket(url+"?par1=1234", options);

            socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {

                public void call(Object... args) {
                    socket.send("hello");
                }
            });

            //自定义事件
            socket.on("borcast", new Emitter.Listener() {
                public void call(Object... objects) {
                    System.out.println("receive borcast data:" + objects[0].toString());
                }
            });

            socket.on("connected", new Emitter.Listener() {
                public void call(Object... objects) {
                    System.out.println("receive connected data:" + objects[0].toString());
                }
            });

            socket.connect();
            //循环发送数据
            while (true){
                socket.emit("client_info"," 客户端在发送数据");
                Thread.sleep(2000);
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

VUE
https://www.npmjs.com/package/vue-socket.io

import VueSocketIO from 'vue-socket.io'
import SocketIO from 'socket.io-client'

Vue.use(new VueSocketIO({
  debug: true,
  connection: SocketIO('http://192.168.88.108:443', {
    path: '', // path千万不要写“/”
    transports: ['websocket', 'xhr-polling', 'jsonp-polling']
  }) // options object is Optional
}))

Other https://github.com/probil/vue-socket.io-extended

import Vue from 'vue'
import VueSocketIOExt from 'vue-socket.io-extended'
import SocketIO from 'socket.io-client';

const socket = SocketIO('https://localhost');
export default ({ store }) => {
  Vue.use(VueSocketIOExt, socket, { store });
}

sockets: {
      // 创建连接
      connect() {
        console.log('连接成功啦')
      },
      // 监听断开连接,函数
      disconnect() {
        console.log('断开服务器连接');
        this.connectdisabled = false;
        this.leavedisabled = true;
        this.senddisabled = true;
        this.inputdisabled = true;
      },
      reconnect() {
        console.log("重新链接");
      },
      // 监听自定义事件,需与服务端emit事件名一致
      joined(room, id) {
        console.log('joined',room, id)
        this.connectdisabled = true;
        this.leavedisabled = false;
        this.senddisabled = false;
        this.inputdisabled = false;
      },

//发送加入信令
this.$socket.client.emit('join', this.room)

jpa sql

自定mysql
““
@Modifying
@Transactional
有修改数据不要忘记加上

@Query(value =”select product_at,GREATEST(max(p1),max(p2), as max from ks12p where product_plan_id=?1 group BY product_at”,nativeQuery = true)
List<Map<String,Object>> findByKsEveryDayMax(Long product_plan_id);

List<Map<String,Object>> productAtMaxes = ks12pDao.findByKsEveryDayMax(3L);
productAtMaxes.forEach(p -> {
System.out.println(“map.entrySet():”+p.entrySet());
Iterator<Map.Entry<String, Object>> iterator1 = p.entrySet().iterator();
while (iterator1.hasNext()){
System.out.println(“iterator1:”+iterator1.next());
}
});

or

@Query(value = “select new com.xx.xx.CarTraceResult(a.plateNo, a.plateColor, a.typeName, a.parkName, max(a.time), count(a.id)) ” +
” from CarTraceRecordEntity a where a.plateNo = ?1 and a.plateColor = ?2 ” +
“and a.type = ?3 group by a.parkNo order by time desc “)
List queryCarTraceRecord(String plateNo, Integer plateColor, Integer type);

or

public interface ProductAtDayMax {
LocalDate getProductAt();
BigDecimal getMax();
}

@Query(value ="select product_at,GREATEST(max(p1),max(p2), as max from ks12p where product_plan_id=?1 group BY product_at",nativeQuery = true)
List<ProductAtDayMax> findByKsEveryDayMax(Long product_plan_id);

List productAtMaxes = ks12pDao.findByKsEveryDayMax(3L);
productAtMaxes.forEach(productAtDayMax -> {
log.info(“[{}] [{}]”,productAtDayMax.getProduct_at(), productAtDayMax.getMax());
});
““`

Spring Cloud Gateway

依赖
<!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
配制
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false # true 之前的http://localhost:8081/service-consumer/user/info这样的请求地址也能正常访问,因为这时为每个服务创建了2个router。
          lower-case-service-id: true #转换服务id为小写
      routes:
        - id: service_nacos
          uri: lb://nacos #(注册中心中服务的名称)即service-consumer服务的负载均衡地址,并用StripPrefix的filter 在转发之前将/consumer去掉。
          predicates:
            - Path= /nacos/** #将以/consumer/**开头的请求都会转发到uri为lb://service-consumer的地址上
          filters:
            - StripPrefix=1

Gateway 过滤器

Spring Cloud Gateway的filter生命周期不像Zuul那么丰富,它只有两个:“pre”和“post”:

pre:这种过滤器在请求被路由之前调用。可以利用这个过滤器实现身份验证、在集群中选择请求的微服务、记录调试的信息。

post:这种过滤器在路由到服务器之后执行。这种过滤器可用来为响应添加HTTP Header、统计信息和指标、响应从微服务发送给客户端等。

Spring Cloud gateway的filter分为两种:GatewayFilter和Globalfilter。GlobalFilter会应用到所有的路由上,而Gatewayfilter将应用到单个路由或者一个分组的路由上。

利用Gatewayfilter可以修改请求的http的请求或者是响应,或者根据请求或者响应做一些特殊的限制。更多时候可以利用Gatewayfilter做一些具体的路由配置。

Gateway请求匹配

Gateway网关可以根据不同的方式进行匹配进而把请求分发到不同的后端服务上。

Gateway熔断

Spring Cloud Gateway也可以利用Hystrix的熔断特性,在流量过大时进行服务降级,同时项目中必须加上Hystrix的依赖。

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

Gateway重试路由器

Retry GatewayFilter通过四个参数来控制重试机制,参数说明如下:

retries:重试次数,默认值是 3 次。

statuses:HTTP 的状态返回码,取值请参考:org.springframework.http.HttpStatus。

methods:指定哪些方法的请求需要进行重试逻辑,默认值是 GET 方法,取值参考:org.springframework.http.HttpMethod。

series:一些列的状态码配置,取值参考:org.springframework.http.HttpStatus.Series。符合的某段状态码才会进行重试逻辑,默认值是 SERVER_ERROR,值是 5,也就是 5XX(5 开头的状态码),共有5个值。

使用上述配置进行测试,当后台服务不可用时,会在控制台看到请求三次的日志,证明此配置有效。

Gateway 限流操作

Spring Cloud Gateway本身集成了限流操作,Gateway限流需要使用Redis,pom文件中添加Redis依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>
配置了Redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:
    BurstCapacity:令牌桶的总容量。
    replenishRate:令牌通每秒填充平均速率。
    Key-resolver:用于限流的解析器的Bean对象的名字。它使用SpEL表达式#{@beanName}从Spring容器中获取bean对象。
    注意:filter下的name必须是RequestRateLimiter。
Key-resolver参数后面的bean需要自己实现,然后注入到Spring容器中。KeyResolver需要实现resolve方法,比如根据ip进行限流,则需要用hostAddress去判断。

自定义Gatewayfilter

Spring Cloud Gateway自定义过滤器,过滤器需要实现GatewayFilter和Ordered这两个接口。
再将该过滤器注册到router中

    private static final Log log = LogFactory.getLog(GatewayFilter.class);
    private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
        return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                    if (startTime != null) {
                        log.info("请求路径:"+exchange.getRequest().getURI().getRawPath() + "消耗时间: " + (System.currentTimeMillis() - startTime) + "ms");
                    }
                })
        );
    }
    @Override
    public int getOrder() {
        return 0;
    }
}
注册
    @Bean
        public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
            return builder.routes()
                    .route(r -> r.path("/user/**")
                            .filters(f -> f.filter(new RequestTimeFilter())
                                    .addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
                            .uri("http://localhost:8504/user/info")
                            .order(0)
                            .id("customer_filter_router")
                    )
                    .build();
        }
除了上述代码的方式配置我们自定义的过滤器的方式之外,也可以在application.yml文件中直接配置

自定义GlobalFilter

Spring Cloud Gateway根据作用范围分为GatewayFilter和GlobalFilter,二者区别如下:

GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。
GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
public class TokenFilter implements GlobalFilter, Ordered {
    Logger logger= LoggerFactory.getLogger( TokenFilter.class );
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.isEmpty()) {
            logger.info( "token 为空,无法进行访问." );
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

rancher 负载均衡时 default backend – 404

Nacos

官方网址:http://nacos.io
官网教程

docker

https://github.com/nacos-group/nacos-docker.git
https://hub.docker.com/r/nacos/nacos-server/tags?page=1&ordering=last_updated
docker pull nacos/nacos-server:2.0.0
docker pull nacos/nacos-server:latest

单机内存
docker run --name nacos-standalone -e MODE=standalone -p 8848:8848 -d --restart=always nacos/nacos-server:2.0.0
单机Mysql
docker run -d \
--name nacos-server \
-e PREFER_HOST_MODE=hostname \
-e MODE=standalone \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_MASTER_SERVICE_HOST=172.16.0.17 \
-e MYSQL_MASTER_SERVICE_PORT=3306 \
-e MYSQL_MASTER_SERVICE_USER=root \
-e MYSQL_MASTER_SERVICE_PASSWORD=root \
-e MYSQL_MASTER_SERVICE_DB_NAME=nacos \
-e MYSQL_SLAVE_SERVICE_HOST=192.168.1.3 \
-e MYSQL_SLAVE_SERVICE_PORT=3306 \
-p 8848:8848 \
--restart=always \
nacos/nacos-server:2.0.0

mkdir logs
mkdir properties
vi custom.properties

server.contextPath=/nacos
server.servlet.contextPath=/nacos
server.port=8848

spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://172.16.0.17:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=PW

nacos.cmdb.dumpTaskInterval=3600
nacos.cmdb.eventTaskInterval=10
nacos.cmdb.labelTaskInterval=300
nacos.cmdb.loadDataAtStart=false

management.metrics.export.elastic.enabled=false
management.metrics.export.influx.enabled=false

server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i

nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**
nacos.naming.distro.taskDispatchThreadCount=1
nacos.naming.distro.taskDispatchPeriod=200
nacos.naming.distro.batchSyncKeyCount=1000
nacos.naming.distro.initDataRatio=0.9
nacos.naming.distro.syncRetryDelay=5000
nacos.naming.data.warmup=true
nacos.naming.expireInstance=true

docker  run \
--name nacos -d \
-p 8848:8848 \
--privileged=true \
--restart=always \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-v ~/nacos/logs:/home/nacos/logs \
-v ~/nacos/custom.properties:/home/nacos/init.d/custom.properties \
nacos/nacos-server:2.0.0

2.下载源码或者安装包

你可以通过源码和发行包两种方式来获取 Nacos。
从 Github 上下载源码方式
git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
ls -al distribution/target/
// change the $version to your actual path
cd distribution/target/nacos-server-$version/nacos/bin
下载编译后压缩包方式
您可以从 最新稳定版本 下载 nacos-server-$version.zip 包。
unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz
cd nacos/bin
3.启动服务器
Linux/Unix/Mac
启动命令(standalone代表着单机模式运行,非集群模式):
sh startup.sh -m standalone
如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:

bash startup.sh -m standalone
Windows
启动命令:
cmd startup.cmd
或者双击startup.cmd运行文件。
4.服务注册&发现和配置管理
服务注册
curl -X POST ‘http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080’
服务发现
curl -X GET ‘http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName’
发布配置
curl -X POST “http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld”
获取配置
curl -X GET “http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test”
5.关闭服务器
Linux/Unix/Mac
sh shutdown.sh
Windows
cmd shutdown.cmd
或者双击shutdown.cmd运行文件。

DOCKER uick Start

Run the following command:

Clone project

git clone --depth 1 https://github.com/nacos-group/nacos-docker.git
cd nacos-docker

Standalone Derby

docker-compose -f example/standalone-derby.yaml up

Standalone Mysql

docker-compose -f example/standalone-mysql.yaml up

Cluster

docker-compose -f example/cluster-hostname.yaml up 

Service registration

curl -X PUT 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'

Service discovery

  curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=nacos.naming.serviceName'

Publish config

curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=helloWorld"

Get config

  curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"

Open the Nacos console in your browser

link:http://127.0.0.1:8848/nacos/ 默认账号密码都是nacos

Common property configuration

name description option
MODE cluster/standalone cluster/standalone default cluster
NACOS_SERVERS nacos cluster address eg. ip1:port1 ip2:port2 ip3:port3
PREFER_HOST_MODE Whether hostname are supported hostname/ip default ip
NACOS_SERVER_PORT nacos server port default 8848
NACOS_SERVER_IP custom nacos server ip when network was mutil-network
SPRING_DATASOURCE_PLATFORM standalone support mysql mysql / empty default empty
MYSQL_SERVICE_HOST mysql host
MYSQL_SERVICE_PORT mysql database port default : 3306
MYSQL_SERVICE_DB_NAME mysql database name
MYSQL_SERVICE_USER username of database
MYSQL_SERVICE_PASSWORD password of database
MYSQL_MASTER_SERVICE_HOST The latest version of the image removes this attribute, using MYSQL_SERVICE_HOST
MYSQL_MASTER_SERVICE_PORT The latest version of the image removes this attribute, using MYSQL_SERVICE_PORT default : 3306
MYSQL_MASTER_SERVICE_DB_NAME The latest version of the image removes this attribute, using MYSQL_SERVICE_DB_NAME
MYSQL_MASTER_SERVICE_USER The latest version of the image removes this attribute, using MYSQL_SERVICE_USER
MYSQL_MASTER_SERVICE_PASSWORD The latest version of the image removes this attribute, using MYSQL_SERVICE_PASSWORD
MYSQL_SLAVE_SERVICE_HOST The latest version of the image removes this attribute
MYSQL_SLAVE_SERVICE_PORT The latest version of the image removes this attribute default :3306
MYSQL_DATABASE_NUM It indicates the number of database default :1
JVM_XMS -Xms default :2g
JVM_XMX -Xmx default :2g
JVM_XMN -Xmn default :1g
JVM_MS -XX:MetaspaceSize default :128m
JVM_MMS -XX:MaxMetaspaceSize default :320m
NACOS_DEBUG enable remote debug y/n default :n
TOMCAT_ACCESSLOG_ENABLED server.tomcat.accesslog.enabled default :false
NACOS_AUTH_SYSTEM_TYPE The auth system to use, currently only ‘nacos’ is supported default :nacos
NACOS_AUTH_ENABLE If turn on auth system default :false
NACOS_AUTH_TOKEN_EXPIRE_SECONDS The token expiration in seconds default :18000
NACOS_AUTH_TOKEN The default token default :SecretKey012345678901234567890123456789012345678901234567890123456789
NACOS_AUTH_CACHE_ENABLE Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. default : false

集成

<!--阿里依赖管理-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <!--<version>${latest.version}</version>-->
        </dependency>

spring.application.name=nacos
spring.cloud.nacos.discovery.server-addr=102.168.88.127:8848

在 Nacos Spring Cloud 中,dataId 的完整格式如下:
${prefix}-${spring.profile.active}.${file-extension}

    prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。
    spring.profile.active 即为当前环境对应的 profile,详情可以参考 Spring Boot文档。 注意:当 spring.profile.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
    file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.88.127:8848
      config:
        server-addr: 192.168.88.127:8848
        #namespace: example.properties # Data Id
        file-extension: yml # yml or properties

spring注解驱动

容器 AnnotationConfigApplicationContext
组件添加
组件赋值
组件注入
AOP
声明事务

原理
BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor
ApplicationListener
Spring容器创建过程

WEB
servlet3.o
异步请求

/**
 * 给容器注册组件
 * 1.包扫描+组件标注注解(@Controller...) [我们自己写的]
 * 2.@Bean+ @Configuration [别人写的,导入的第三方包]
 * 3.@Import [快速的给容器中导入组件]
 *      1. @Import({要导入的容器的组件...}) id默认为全类名
 *      2. @ImportSelector 返回要导入的全类名数组
 *      3. ImportBeanDefinitionRegistrar 通过registerBeanDefinitions给容器中增加自己的bean
 * 4.使用spring提供的FactoryBean[工厂Bean]
 *      1.默认获取到的是工厂Bean调用getObject创建的对象
 *      2.通过前缀&获取FactoryBean注入的工厂bean本身
 * 5.@Condition按条件进行判断,满足条件的给容器中注册Bean
 *
 * 赋值方式
 * 1.xml中指定
 * 2.使用Bean创建时指定
 * 3.使用@Value赋值
 *  1.基本数值
 *  2.SpEL #{}
 *  3.配制文件,运行环境变量中的值 ${} 可以使用@PropertySource加载外部配制文件保存到运行的变量中,yml文件先在静态资源中引入
 *
 * 自动装配
 *  spring利用依赖注入DI,完成对IOC容器中各个组件的依赖关系赋值
 *  默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
 *  1.@Autowired 自动注入 [Spring规范]
 *      AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能
 *      1.默认优先按照类型去容器中找对应的组件:applicationContext.getBean(beanName.Class),找到并赋值
 *      2.如果找到多个相同类型的组件,再将属性的名字作为组件的ID去容器中查找 applicationContext.getBean(beanName),找到并赋值
 *      3.@Qualifier("beanName")明确指定要装配的组件ID,而不傅属性
 *      4.自动装配默认一定要将属性赋值好,没有就会报错,可以使用@Autowired(required = false)没有就不装配
 *      5.@Primary spring进行装配的时候,默认使用首选的Bean,也可以继续使用@Qualifier指定
 *  2.还支持@Resource(JSR250规范)和@Inject(JSR330规范)  [java规范]
 *      1.@Resource 可以和@Autowired一样实现自动装配,默认按照组件名称进行装配,不支持@Primary和@Autowired(required = false)
 *      2.@Inject 使用先导入javax.inject依赖,可以和@Autowired一样实现自动装配,支持@Primary和不支持@Autowired(required = false)
 *  3.@Autowired 可以标在构造器,方法,属性,参数;都是从容器中获取参数组件的值
 *      1.标注在方法,spring容器创建当前对角,就会调用方法,完成赋值,方法使用的参数,自定义类型的值从Ioc容器中获取;@Bean标注的方法创建对象的时候,方法参数的值从容器中获取;默认不写@Autowired效果都是一样的
 *      2.标注在有参构造器时,容器启动调用这有参构造器创建对象,构造器要用的组件,都是从容器中获取;只有一个有参构造器时,可以省略@Autowired,参数位置的组件还是从容器中获取
 *      3.标在参数时,也同2
 *  4.自定义组件想要使用spring容器底层的一些组件(applicationContext,BeanFactory,xxx),自定义组件只需实现xxxAWare接口,有回调的风格,总体参照AWare;
 *   xxxAWare的功能使用xxxAWarePostProcessor后置处理器完成
 *  5.@Profile: spring为我们提供可以根据当前环境,动态激活和切换一系列组件的功能,不指定,在任何环境下都能注册
 *      环境切换:
 *          1使用命令行动态参数-Dspring.profiles.active
 *          2使用代码方式,applicationContext不能使用有参构造器,用无参构造器
 *          //指定主配制类,获得容器
 *         //AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
 *         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
 *         //使用无参构造器设置需要激活的环境,一次可以设多个
 *         applicationContext.getEnvironment().setDefaultProfiles("test");
 *         //注册主配制类
 *         applicationContext.register(MainConfigOfProfile.class);
 *         //启动刷新容器
 *         applicationContext.refresh();
 *         log.info("容器创建完成...[{}]",applicationContext);
 *         3.使用配制文件spring.profiles.active=dev
 *      1.加了环境标识的Bean,只有在环境激活的情况下注册到容器中,默认是default环境
 *      2.写在配置类上,只有在只有在环境激活的情况下,整个配置类里面的所有配置才生效
 *      3.没有标注的,在任何环境下都是加载的
 *
 */

Pinpoint 链路监控

链路监控 Pinpoint

https://github.com/naver/pinpoint
安装服务
https://github.com/naver/pinpoint-docker

https://github.com/naver/pinpoint/releases
下载 pinpoint-agent-2.0.2.tar.gz
修改配制文件ponpoint.config
profiler.collector.iP=XX.XX.XXX.XXX #服务器地址
profiler.sampling.rate = 1 采样率1/N,1为100%全部采,20=5%
profiler.applicationservertype=TOMCAT 改SPRING_BOOT 采样服务器类型
查找修改SPRING_BOOT配制中的
profiler.sprintboot.bootstrap.main,增加要监控的main()函数类名
com.xx.xx

配制应用启动参数,指明使用代理
running the application:

-javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar
Additionally, Pinpoint Agent requires 2 command-line arguments in order to identify itself in the distributed system:
-Dpinpoint.agentId – uniquely identifies the application instance in which the agent is running on
-Dpinpoint.applicationName – groups a number of identical application instances as a single service

与日志相结合,利用 Transation 与ELK关联