My environment
Windows10
JDK8
SpringCloud: Hoxton.SR1
SpringBoot: 2.2.4.RELEASE
Spring cloud Alibaba dependencies: 2.1.1.RELEASE (Note: package name starts with com.alibaba.cloud)
Nacos-server: NACOS1.1.4
2. Start nacos service (stand-alone mode & embedded database)
0. Premise: configure JAVA_HOME environment variable. If not configured, Nacos will not run
1. Download source code or installation package
Installation package address: https://github.com/alibaba/nacos/releases
2. After decompression, enter the nacos/bin directory
3. Enter the command to start the service
linux: sh startup.sh -m standalone
windows: cmd startup.cmd
4. When the console is started, the "Nacos started successfully in stand alone mode." indicates that the service has been started
5.nacos uses port 8848 by default. You can access the console interface through http://127.0.0.1:8848/nacos/index.html. The default user name / password is nacos/nacos
3. Configure related pom configuration files
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 1. nacos-Service discovery feature dependency --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.2.RELEASE</version> </dependency> <!-- 2. nacos-Configuration management function is dependent, which is not available at present --> <!-- <dependency>--> <!-- <groupId>org.springframework.cloud</groupId>--> <!-- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>--> <!-- <version>0.2.2.RELEASE</version>--> <!-- </dependency>--> Note: version 0.2.x.RELEASE The corresponding is Spring Boot 2.x Version, version 0.1.x.RELEASE The corresponding is Spring Boot 1.x Edition. For example: springboot2.2.4.RELEASE Corresponding 0.2.2RELEASE
4. Modify application.properties. Add some configuration about Nacos, and remove the configuration about Eureka
#Specify the ip and port number of nacos and register with nacos
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
5. Modify the notes related to startup class replacement. If @ EnableEurekaClient is added to your application startup class, you need to modify it to @ EnableDiscoveryClient
@SpringBootApplication
//@EnableEurekaClient
@EnableDiscoveryClient
public class ProviderApplication {...}
Just start your application, so far, you have implemented "zero line code replace Eureka with Nacos"!
Appendix 1: differences between dependency management and dependencies
1.dependencies even if the dependency is not written in the child project, the child project will inherit the dependency from the parent project (inherit all)
2. Dependency management only declares dependencies and does not implement the introduction. Therefore, the dependencies required for the declaration of the subproject need to be displayed.
If a dependency is not declared in a child project, it will not be inherited from the parent project; only if the dependency is written in the child project and no specific version is specified, it will be inherited from the parent project,
Both version and scope are read from the parent pom; in addition, if the version number is specified in the subproject, the jar version specified in the subproject will be used
Appendix 2: nacos fails to start in windows, showing that port 8848 is occupied
Plan 1: restart the computer
Scheme 2: modify the port number in the file nacos/conf/application.properties, for example:
server.port=8848 ==> server.port=18848
6. Configure the configuration file of the service gateway to the public name storage space of nacos, because the access key s of other spaces will change with the change of the system when the project is published. The default is the same for public
Configure what you want to access the database, and the service gateway connection path can be searched
Spring cloud gateway configuration
for instance:
{ "refreshGatewayRoute": true, //Refresh gateway route to true to refresh by default "routeList": [ { "id": "user-service-api02", //Distinguish id "predicates": [ { "name": "Path", "args": { "_genkey_0": "/usr02/**" //Access path + ant style expression } } ], "filters": [ { "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "lb://User service API ", / / the name of the consumer you are visiting "order": 0 } ] }
7. Configuration of service gateway
The contents are as follows:
package com.zking.gatewayserver.config; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.zking.gatewayserver.entity.FilterEntity; import com.zking.gatewayserver.entity.PredicateEntity; import com.zking.gatewayserver.entity.RouteEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Mono; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.Executor; /** * This class implements the dynamic routing of Spring Cloud Gateway + nacos. It implements an event push interface provided by Spring, ApplicationEventPublisherAware */ @Component public class DynamicRoutingConfig implements ApplicationEventPublisherAware { private final Logger logger = LoggerFactory.getLogger(DynamicRoutingConfig.class); //private static final String DATA_ID = "zuul-refresh-dev.json"; //private static final String Group = "DEFAULT_GROUP"; @Autowired private RouteDefinitionWriter routeDefinitionWriter; @Autowired private GatewayNacosProperties gatewayNacosProperties; private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } /** * This method is mainly responsible for listening to the configuration changes of Nacos. First, build a ConfigService with parameters, and then start a listening with ConfigService, * And refresh the route information in the listening method. * * @throws NacosException */ @Bean public void refreshRouting() throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, gatewayNacosProperties.getServerAddr()); // properties.put(PropertyKeyConst.NAMESPACE, gatewayNacosProperties.getNamespace()); ConfigService configService = NacosFactory.createConfigService(properties); //To solve the problem that the original configuration of nacos cannot be read and it needs to be updated to read the configuration bug 5000: the maximum waiting time String json=configService.getConfig(gatewayNacosProperties.getDataId(),gatewayNacosProperties.getGroup(),5000); this.parsejson(json); System.out.println("json:"+json); configService.addListener(gatewayNacosProperties.getDataId(), gatewayNacosProperties.getGroup(), new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { // parsejson(configInfo); logger.info(configInfo); boolean refreshGatewayRoute = JSONObject.parseObject(configInfo).getBoolean("refreshGatewayRoute"); if (refreshGatewayRoute) { List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(configInfo).getString("routeList")).toJavaList(RouteEntity.class); for (RouteEntity route : list) { update(assembleRouteDefinition(route)); } } else { logger.info("Route unchanged"); } } }); } /** *Parsing json to get route information * @param json */ public void parsejson(String json){ logger.info(json); boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute"); if (refreshGatewayRoute) { List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class); for (RouteEntity route : list) { update(assembleRouteDefinition(route)); } } else { logger.info("Route unchanged"); } } /** * Routing update * * @param routeDefinition * @return */ public void update(RouteDefinition routeDefinition) { try { this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())); logger.info("Route updated successfully"); } catch (Exception e) { logger.error(e.getMessage(), e); } try { routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe(); this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); logger.info("Route updated successfully"); } catch (Exception e) { logger.error(e.getMessage(), e); } } public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) { RouteDefinition definition = new RouteDefinition(); // ID definition.setId(routeEntity.getId()); // Predicates List<PredicateDefinition> pdList = new ArrayList<>(); for (PredicateEntity predicateEntity : routeEntity.getPredicates()) { PredicateDefinition predicateDefinition = new PredicateDefinition(); predicateDefinition.setArgs(predicateEntity.getArgs()); predicateDefinition.setName(predicateEntity.getName()); pdList.add(predicateDefinition); } definition.setPredicates(pdList); // Filters List<FilterDefinition> fdList = new ArrayList<>(); for (FilterEntity filterEntity : routeEntity.getFilters()) { FilterDefinition filterDefinition = new FilterDefinition(); filterDefinition.setArgs(filterEntity.getArgs()); filterDefinition.setName(filterEntity.getName()); fdList.add(filterDefinition); } definition.setFilters(fdList); // URI URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri(); definition.setUri(uri); return definition; } }
package com.zking.gatewayserver.config; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * 1. Save nacos related properties in gateway * 2. These are custom configuration properties, which are saved in the configuration file application.yml * 3. Read through @ ConfigurationProperties annotation */ //@ConfigurationProperties(prefix = "gateway.nacos", ignoreUnknownFields = true) @Configuration @Data public class GatewayNacosProperties { @Value("${gateway.nacos.server-addr}") private String serverAddr; // @Value("${gateway.nacos.namespace}") // private String namespace; @Value("${gateway.nacos.data-id}") private String dataId; @Value("${gateway.nacos.group}") private String group; }
package com.zking.gatewayserver.config; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import reactor.core.publisher.Mono; /** * Request current limiting configuration */ @Configuration public class RequestRateLimiterConfig { /** * Limit current by IP */ @Bean public KeyResolver ipAddrKeyResolver() {//A new feature of JDK8 -- Lambda expression return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()); } ///** // * Current limit by user // */ //@Bean //KeyResolver userKeyResolver() { // return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); //} ///** // * Flow restriction by URL, that is, the number of requests per second is grouped by URL. All URL requests exceeding the flow restriction will return 429 status // * // * @return // */ //@Bean //@Primary //KeyResolver apiKeyResolver() { // return exchange -> Mono.just(exchange.getRequest().getPath().toString()); //} }
package com.zking.gatewayserver.entity; import java.util.LinkedHashMap; import java.util.Map; /** * Filter entity class */ public class FilterEntity { //Name of filter private String name; //Routing rules private Map<String, String> args = new LinkedHashMap<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public Map<String, String> getArgs() { return args; } public void setArgs(Map<String, String> args) { this.args = args; } }
import java.util.LinkedHashMap; import java.util.Map; /** * Route assertion entity class */ public class PredicateEntity { //Assert the corresponding Name private String name; //Assertion rule private Map<String, String> args = new LinkedHashMap<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public Map<String, String> getArgs() { return args; } public void setArgs(Map<String, String> args) { this.args = args; } }
package com.zking.gatewayserver.entity; import java.util.ArrayList; import java.util.List; /** * Routing entity class */ public class RouteEntity { //Routing id private String id; //Route assertion collection private List<PredicateEntity> predicates = new ArrayList<>(); //Route filter collection private List<FilterEntity> filters = new ArrayList<>(); //Destination uri of routing and forwarding private String uri; //Order of route execution private int order = 0; public String getId() { return id; } public void setId(String id) { this.id = id; } public List<PredicateEntity> getPredicates() { return predicates; } public void setPredicates(List<PredicateEntity> predicates) { this.predicates = predicates; } public List<FilterEntity> getFilters() { return filters; } public void setFilters(List<FilterEntity> filters) { this.filters = filters; } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } }
7.1 YML file configuration
server: #The 5000 port number here is like the 8080 of the previously external tomcat. Let's access it through a browser #But this service only makes a route, it will route the request to other micro services (generally consumers) for processing port: 5000 spring: application: #Micro service name name: gateway-server cloud: nacos: discovery: #Specify the address of the nacos registry server-addr: zz.com:8848 //This zz.com is your local domain name 127.0.0.1 open / / C:\Windows\System32\drivers\etc\hosts, add 1 line at the end // 127.0.0.1 zz.com gateway: discovery: locator: #Whether to combine with the service discovery component and forward to the specific service instance through serviceid (which must be set to uppercase). Default false #true means to turn on routing rules based on service discovery. #The default rule can't be accessed enabled: false #serviceId does not need to be capitalized when accessing after configuration lower-case-service-id: true # routes: a route consisting of a unique ID, a destination service address (uri), a set of predicates, and a set of filters. Filters are not required.) routes: # http://localhost:5000/usr/hello #Routing id (id: id, unique) - id: student-router #The destination service address (uri: address, address after request forwarding) will automatically obtain the service IP from the registration center, and it does not need to be written down manually uri: lb://student-api #Priority, the smaller the priority #order: 999 #Route conditions (predicates: assertions) predicates: # Path matching, - Path=/student/** filters: - name: Hystrix args: name: fallback fallbackUri: forward:/fallback - name: RequestRateLimiter args: #The Bean Name of the object,Use SpEL Expression based on#{@ beanName} get Bean object key-resolver: '#{@ipAddrKeyResolver}' #Token bucket filling rate, how many requests can the user process per second redis-rate-limiter.replenishRate: 20 #Total capacity of token bucket, the maximum number of requests allowed to complete in one second redis-rate-limiter.burstCapacity: 100 - StripPrefix=1 redis: host: 192.168.68.133 port: 6379 password: 123 database: 0 ribbon: eager-load: enabled: true clients: user-service-api logging: level: #The log level of opening spring cloud gateway is debug, which is convenient for debugging org.springframework.cloud.gateway: trace org.springframework.http.server.reactive: debug org.springframework.web.reactive: debug reactor.ipc.netty: debug #springboot monitors the activator, exposing all endpoints management: endpoints: web: exposure: include: '*' #Custom configuration gateway: nacos: server-addr: ${spring.cloud.nacos.discovery.server-addr} #namespace: 558dc9bc-a775-41ad-9f26-43ca3aa73e6b / / comment out the default public data-id: dynamic-routing.json //The name of the configuration file in nacos group: DEFAULT_GROUP
8. After configuration and testing, it can be published as jar package
8.1
Modify pom of main module <version>0.0.1-SNAPSHOT</version> <! -- 1. Change to pom instead of jar -- > <!-- <packaging>jar</packaging> --> <packaging>pom</packaging> <! -- 2. Do not configure plug-ins in the main module -- > <build></build>
8.2. Add plug-in dependency in pom.xml file of each sub module module
<build> <plugins> <!--Add to maven Plug-in unit--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!--Add your own startup class path!--> <mainClass>com.imooc.DemoApplication</mainClass> </configuration> <executions> <execution> <goals> <!--You can package all the dependent packages into the generated ones Jar In bag--> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
8.3 click view of idea Tool windows maven projects
Double click clean (remove the previously typed package target folder) -, and then create install
First compile the parent module - common module - other modules
8.4. Copy the jar package under the target directory of each sub module of the project to the specified directory, for example: d:\temp\apps directory, and then run it directly through the java command
cmd
d:
cd d:\temp\apps
java -jar *.jar --spring.profiles.active=xxx
For example:
java -jar student-service-0.0.1-SNAPSHOT.jar / / no parameters
java -jar eureka-server-cluster-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 / / with parameters
java -jar student-service-0.0.1-SNAPSHOT.jar
java -jar student-api-0.0.1-SNAPSHOT.jar
java -jar gateway-server-0.0.1-SNAPSHOT.jar
Note: for the configuration of the specified environment (spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a), do not configure it in the bootstrap.yml of the application.
Instead, in the start command of publishing script, it is more flexible to specify dynamically by adding parameters after the command, for example:
-Dspring.profiles.active=DEV
–spring.cloud.nacos.config.namespace=f1ce84ff-c591-42b8-b441-2f0084863904
java -jar nacos-config-client-0.0.1-SNAPSHOT.jar --spring.cloud.nacos.config.namespace=f1ce84ff-c591-42b8-b441-2f0084863904
9. Turn on your Linux server
docker install nacos docker pull nacos/nacos-server docker run --env MODE=standalone --restart=always --name nacos -d -p 8848:8848 nacos/nacos-server Note 1: both windows and linux systems can access nacos by modifying the host file, because the IP address of starting the nacos container in docker is not fixed Modify in window: Open hosts, C:\Windows\System32\drivers\etc\hosts, and add 1 line at the end 127.0.0.1 zz.com How to modify in linux: 1. Modify hosts file of host vim /etc/hosts ##Add the domain name to be mapped 127.0.0.1 zz.com
1. copy your local nacos file to your server's nacos
2. The host creates the folder apps, and rz uploads the eureka-server-cluster.jar package to apps
##This directory will later serve as a data volume, sharing data between the host and the container
mkdir /apps
3. Use the jre:8 image to start the container and mount the specified directory as the data volume
--Start producer (7001) docker run --restart=always \ --net=host \ -d \ -it \ --name student-server \ --mount type=bind,source=/apps,target=/apps \ jdk8:v3.0 / / this is your docker image name - consumers docker run --restart=always \ --net=host \ -d \ -it \ --name student-api \ --mount type=bind,source=/apps,target=/apps \ jdk8:v3.0 / / this is your docker image name --Start gateway and host 5000: container 5000 port mapping docker run --restart=always \ --net=host \ -d \ -it \ --name gateway\ --mount type=bind,source=/apps,target=/apps \ jdk8:v3.0 / / this is your docker image name Note 1: jre:8 is a custom image, and JRE 1.8 is installed Note 2: the gateway 5000 is mapped to the 5000 end of the host, and the firewall can only be accessed by modifying the 5000 port
4. Enter the docker container and the apps directory using java jar The commands run one by one,
After success, you can configure nginx
10. Start to configure nginx
Nginx is also a server. We often do the work of reverse proxy, load balancing, separation of dynamic and static resources,
2. Install nginx 1. Search nginx image docker search nginx 2. Pull image docker pull nginx 3. Create the mount directory in the host mkdir -p /data/nginx/{conf,conf.d,html,log} Note 1: because the vi or vim editor is not installed in the nginx image (installation is too cumbersome), the nginx configuration file cannot be edited, So it's more convenient to mount directly through data volume Note 2: upload nginx configuration file nginx.conf to the mount directory "/ data/nginx/conf" of the host Note 3: package the vue front-end project and upload it to the mount directory "/ data/nginx/html" of the host computer Note 1: refer to Appendix II for related debugging commands of nginx configuration file 4. Create nginx container according to nginx image (test the installation and access of nginx, and do not publish any java or vue projects, and the last one to be deleted) docker run \ --name mynginx0 \ -d -p 80:80 \ nginx:latest 5. Check the local container, and you can see that the nginx container has been created successfully docker ps 6. Test whether nginx is installed successfully Open the browser and input: http://192.168.183.133 (http://host ip:80). Normally, the welcome page of nginx will be displayed
Nginx configuration file nginx.conf
#The number of working processes is generally consistent with the number of cpu cores of the computer
worker_processes 1;
events {
#Maximum number of connections for a single process (maximum number of connections = number of connections * number of processes)
worker_connections 1024;
}
http {
include mime.types; file extension and file type mapping table
Default? Type application / octet stream;? Default file type
sendfile on; ා enable the efficient file transfer mode. The sendfile instruction specifies whether nginx calls the sendfile function to output the file. For ordinary applications, it is set to on. If it is used for downloading and other applications with heavy disk IO load, it can be set to off to balance the disk and network I/O processing speed and reduce the system load. Note: if the image display is abnormal, change this to off.
keepalive_timeout 65; #Long connection timeout in seconds gzip on;#Enable Gizp compression #Cluster of servers upstream tomcats { #Server cluster name #TODO: 172.17.0.3 is the IP address of the docker container server 172.17.0.3:8080 weight=1;#Server configuration weight means weight. The greater the weight, the greater the probability of allocation. Domain name is your current server domain name + port 80 you configured server 172.17.0.4:8080 weight=2; } #Current Nginx configuration server { listen 80;#Listen to port 80, which can be changed to other ports server_name localhost;#The domain name of the current service, no domain name can be filled in freely root /usr/share/nginx/html/dist;#The directory of the web site to be visited location / { try_files $uri $uri/ /index.html;#This code is to solve the problem that history routing cannot jump. It is introduced on the official website of Vue router } location ^~/api/ { #^~/api / indicates that the matching prefix is the request of api. If the end of proxy pass has /, the path following / api / * will be directly spliced to the back, that is, api will be removed proxy_pass http://tomcats/; } }
}
3.ningx releases java and vue projects
nginx+tomcat to realize reverse proxy and balance
nginx+html static server
1. Create nginx container docker run \ --name mynginx \ -d -p 80:80 \ -v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ -v /data/nginx/log:/var/log/nginx \ -v /data/nginx/html:/usr/share/nginx/html \ nginx:latest Note 1: because the vi or vim editor is not installed in the nginx image (installation is too cumbersome), the nginx configuration file cannot be edited, so all data and configurations are mounted through the data volume The first - v: mount the main configuration file of nginx to modify the configuration file of the container directly on the host The second - v: mount the nginx logs in the container. After the container is running, you can view the nginx logs directly in this directory of the host Third - v: Mount static page directory Note line breaks and spaces between commands Note 2: if the container creation fails, you can view the startup log in the docker container through the following command docker logs -f -t --tail line number container name docker logs -f -t --tail 100 mynginx Note 3: if you want to enter the container, bash instead of sh should be used according to the actual basic image docker exec -it mynginx /bin/bash
Remember to allow the firewall to access the port!!!!!