场景描述
应用程序A登录需要访问用户的数据;
在普通的mvc程序中可能就是一个控制器和DAO的实现,假设这个用户的程序A的登录频率异常高,那么后台服务的响应能力会越来越差;
通过使用SpringCloud微服务,是将一个请求用户转发至 Zuul 网关服务、由Zuul 服务在集群服务中找个服务来给这个请求服务;这样一来请求的频率压力会被集群服务化解。
那么本文就依照以上的应用场景来写我们的SpringCloud服务
1、第一步创建EurekaServer
我们首先需要一个管理注册服务的工具Eureka服务
package com.example.eurekaServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class SpingCloudEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpingCloudEurekaServerApplication.class, args);
}
}
application.properties
server.port=8888
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.serviceUrl.defaultZone:http://localhost:8888/eureka/
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF
第二步 我们需要一个3个Userserver用来提供程序A的请求
可以写一个标准的SpringBoot的rest服务
package com.example.UserServer.Controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.UserServer.Entity.User;
import com.example.UserServer.Service.UserServiceInterface;
import javax.servlet.http.HttpServletRequest;
@RestController
@Api(value = "Shop")
public class UserServerController {
@Autowired
UserServiceInterface UserServiceInterface;
@GetMapping
@ApiOperation(value="获取指定id用户详细信息", notes="根据user的id来获取用户详细信息")
@ApiImplicitParam(name = "id",value = "用户id", dataType = "String", paramType = "path")
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public User FindUserById(@PathVariable String id , HttpServletRequest request)
{
System.out.println("我在被调用了请注意了"+request.getLocalPort());
int Userid= request.getLocalPort();
return UserServiceInterface.FindUserById(Userid);
}
}
但是我们的服务也要注册到Eureka中去
package com.example.UserServer;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableDiscoveryClient
public class SpingCloudUserServerRegistryEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpingCloudUserServerRegistryEurekaApplication.class, args);
}
@RestController
class ServiceInstanceRestController {
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/service-instances/{applicationName}")
public List serviceInstancesByApplicationName(
@PathVariable String applicationName) {
return this.discoveryClient.getInstances(applicationName);
}
}}
server.port=8280
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/
eureka.instance.prefer-ip-address=true
spring.application.name=UserServerRegistry
通过修改不同的端口来实现注册集群服务启动可以查看下图、说明已经注册完毕;
第三步、我需要一个zuul网关组件注册到Eureka并进行反向代理我注册的集群服务USERSERVERREGISTRY
package com.example.main;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class SpingCloudServerZuulEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpingCloudServerZuulEurekaApplication.class, args);
}
}
spring.application.name = Sping-Cloud-Server-Zuul
server.port=8889
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/
eureka.instance.instance-id= ${spring.application.name}:${spring.application.instance_id:${server.port}}
eureka.instance.prefer-ip-address=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds= 5000
#表示使用/userserver/user/1 代替从eureka服务器中的userserverregistry的实例IP:端口/user/1
#其中/userserver 表示的是ServerceId的实例
zuul.routes.api-a.path = /userserver/**
zuul.routes.api-a.serviceId = userserverregistry
#配置对应服务ID的负载均衡规则
#userserverregistry.ribbon.NFLoadBalancerRuleClassName= com.netflix.loadbalancer.RandomRule
启动该服务后Eureka中可看到
第四步、使用FEIGN客户端模拟(程序A)访问zuul节点代理的User服务并执行请求;
配置文件
server.port=8089
spring.application.name=user-client-byFeign
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/
首先要写FeginClient
package com.example.UserConsumer.RestController;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.example.UserConsumer.Entity.User;
@Component
@FeignClient("Sping-Cloud-Server-Zuul")
public interface UserFeignClient {
// 这样的方式是知道serviceid的情况
// @RequestMapping(value = "/zuul/user/{id}", method = RequestMethod.GET)
@RequestMapping(value = "/userserver/user/{id}", method = RequestMethod.GET)
public User FindUserById(@PathVariable("id") String id);
}
再写正常的控制器、处理请求时将请求由FeginClient类处理
package com.example.UserConsumer.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.UserConsumer.Entity.User;
import org.springframework.web.client.RestTemplate;
@RestController
public class UserServerController {
/**
* 此种访问的方式是直接通过RestTemple ribbon形式方法可以支持Serviceid访问
*/
@Autowired
private UserFeignClient UserFeignClient;
@GetMapping
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public User FindUserById(@PathVariable String id)
{
System.out.println(id);
User findUserById = UserFeignClient.FindUserById(id);
System.out.println("我在使用通过FeignClient形式方法访问");
return findUserById ;
}
}
最后是启动类
package com.example.UserConsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SpingCloudUserConsumerZuulFeignApplication {
public static void main(String[] args) {
SpringApplication.run(SpingCloudUserConsumerZuulFeignApplication.class, args);
}
}
启动后的效果图
第五步;测试集群的效果
1、有没有发生集群效应呢?
通过访问http://localhost:8089/user/16565 执行3次
通过以上请注意查看帅哥后面的端口号:
这个端口号是服务器端的端口号 为了方便测试我将端口号返回了、由此证明访问过程是集群的
2、集群返回服务的策略如何配置?
由5-1问题发现默认的集群策略是轮询,也就是我有10个服务 那么访问就一个一个来;
如果想配置其他方式也是可以的,这里可以在zuul的服务中修改
#配置对应服务ID的负载均衡规则
userserverregistry.ribbon.NFLoadBalancerRuleClassName= com.netflix.loadbalancer.RandomRule
#userserverregistry是服务的ID
#com.netflix.loadbalancer.RandomRule是规则的全路径
修改后重启即可生效;
3、如果集群中某个服务挂了、是否影响服务可用性?
如果是手动点击程序关闭是不影响的;
我们模拟一下系统强杀进程的情况来看一下是否影响
执行一下
将任务管理器中的PID为5752的java.exe立即结束;
疯狂刷新http://localhost:8089/user/16565
结果:完全不影响服务的可用性8080端口被强杀后、8180/8280端口仍然继续服务;
本文相关的源码:https://github.com/379753498/SpringCloudDevStart
本文为原创、转载请注明出处;