继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

『高级篇』docker之开发用户服务EdgeService(13)

慕神8447489
关注TA
已关注
手记 1296
粉丝 174
获赞 957

webp

新建maven模块user-edge-service

webp

  • 引入user-thrift-service-api 和 message-thrift-service-api的pom文件

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.idig8</groupId>
    <artifactId>user-edge-service</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.10.0</version>
        </dependency>
        <dependency>
            <groupId>com.idig8</groupId>
            <artifactId>user-thrift-service-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.idig8</groupId>
            <artifactId>message-thrift-service-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>


    </dependencies></project>

webp

  • redis的工具类,用于无状态的存储,token信息 保存用的userInfo
    RedisConfig

package com.idig8.user.redis;import org.springframework.beans.factory.annotation.Value;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.CachingConfigurerSupport;import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;/**
 * Redis缓存配置类
 */@Configuration@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport {    @Value("${spring.redis.host}")    private String host;    @Value("${spring.redis.port}")    private int port;    @Value("${spring.redis.timeout}")    private int timeout;    @Value("${spring.redis.password}")    private String password;    //缓存管理器
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);        //设置缓存过期时间
        cacheManager.setDefaultExpiration(10000);        return cacheManager;
    }    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(host);
        factory.setPort(port);
        factory.setTimeout(timeout);
        factory.setPassword(password);        return factory;
    }    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
        StringRedisTemplate template = new StringRedisTemplate(factory);
        setSerializer(template);//设置序列化工具
        template.afterPropertiesSet();        return template;
    }    private void setSerializer(StringRedisTemplate template){
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
    }
}

RedisClient

package com.idig8.user.redis;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/**
 * Created by liming
 */@Componentpublic class RedisClient {    @Autowired
    private RedisTemplate redisTemplate;    public <T> T get(String key) {        return (T)redisTemplate.opsForValue().get(key);
    }    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }    public void set(String key, Object value, int timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }    public void expire(String key, int timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

}

webp

  • Response 和 LoginResponse 统一返回字符串

Response

package com.idig8.user.response;import java.io.Serializable;/**
 * Created by liming
 */public class Response implements Serializable {    public static final Response USERNAME_PASSWORD_INVALID = new Response("1001", "username or password invalid");    public static final Response MOBILE_OR_EMAIL_REQUIRED = new Response("1002", "mobile or email is required");    public static final Response SEND_VERIFYCODE_FAILED = new Response("1003", "send verify code failed");    public static final Response VERIFY_CODE_INVALID = new Response("1004", "verifyCode invalid");    public static final Response SUCCESS = new Response();    private String code;    private String message;    public Response() {        this.code = "0";        this.message = "success";
    }    public Response(String code, String message) {        this.code = code;        this.message = message;
    }    public static Response exception(Exception e) {        return new Response("9999", e.getMessage());
    }    public String getCode() {        return code;
    }    public void setCode(String code) {        this.code = code;
    }    public String getMessage() {        return message;
    }    public void setMessage(String message) {        this.message = message;
    }
}

LoginResponse

package com.idig8.user.response;/**
 * Created by liming
 */public class LoginResponse extends Response {    private String token;    public LoginResponse(String token) {        this.token = token;
    }    public String getToken() {        return token;
    }    public void setToken(String token) {        this.token = token;
    }
}

webp

  • 客户端访问通过和服务端相同的协议进行通信

package com.idig8.user.thrift;import com.idig8.thrift.message.MessageService;import com.idig8.thrift.user.UserService;import org.apache.thrift.TServiceClient;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TProtocol;import org.apache.thrift.transport.*;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic class ServiceProvider {    @Value("${thrift.user.ip}")    private String serverIp;    @Value("${thrift.user.port}")    private  int serverPort;    @Value("${thrift.message.ip}")    private String messageServerIp;    @Value("${thrift.message.port}")    private int messageServerPort;    private enum ServiceType {
        USER,
        MESSAGE
    }    public UserService.Client getUserService() {        return getService(serverIp, serverPort, ServiceType.USER);
    }    public MessageService.Client getMessasgeService() {        return getService(messageServerIp, messageServerPort, ServiceType.MESSAGE);
    }    public <T> T getService(String ip, int port, ServiceType serviceType) {
        TSocket socket = new TSocket(ip, port, 3000);
        TTransport transport = new TFramedTransport(socket);        try {
            transport.open();
        } catch (TTransportException e) {
            e.printStackTrace();            return null;
        }
        TProtocol protocol = new TBinaryProtocol(transport);

        TServiceClient result = null;        switch (serviceType) {            case USER:
                result = new UserService.Client(protocol);                break;            case MESSAGE:
                result = new MessageService.Client(protocol);                break;
        }        return (T)result;
    }

}

webp

  • controller 引入thrift的service方法和redis的操作工具类 用于redis的操作

因为userInfo是通过thrift自动升成的,里面很多方法太过麻烦,不利于开发查看数据内容,最好的方式自己创建一个对象,将自动升成userInfo转换成自定义的UserDTO,USerDTO最好的方式是在thrift的工程中进行的。如果多个项目,比较方便,单独的用户的edgeservice中进行DTO的话,只能他自己用业务不清晰。

webp

package com.idig8.user.controller;import com.idig8.thrift.user.UserInfo;import com.idig8.thrift.user.dto.UserDTO;import com.idig8.user.redis.RedisClient;import com.idig8.user.response.LoginResponse;import com.idig8.user.response.Response;import com.idig8.user.thrift.ServiceProvider;import org.apache.commons.lang.StringUtils;import org.apache.thrift.TException;import org.apache.tomcat.util.buf.HexUtils;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import java.security.MessageDigest;import java.util.Random;@Controllerpublic class UserController {    @Autowired
    private ServiceProvider serviceProvider;    @Autowired
    private RedisClient redisClient;    @RequestMapping(value = "/login",method = RequestMethod.POST)    @ResponseBody
    public Response login(@RequestParam("username")String username,
                          @RequestParam("password")String password){//        1. 验证用户名密码
        UserInfo userInfo = null;        try {
             userInfo = serviceProvider.getUserService().getUserByName(username);
        } catch (TException e) {
            e.printStackTrace();            return Response.USERNAME_PASSWORD_INVALID;
        }        if (userInfo == null){            return Response.USERNAME_PASSWORD_INVALID;
        }        if(!userInfo.getPassword().equalsIgnoreCase(md5(password))){            return Response.USERNAME_PASSWORD_INVALID;
        }//        2. 生成token
        String token = genToken();//        3. 缓存用户

        //因为userInfo是通过thrift自动升成的,里面很多方法太过麻烦,不利于开发查看数据内容
        //最好的方式自己创建一个对象,将自动升成userInfo转换成自定义的UserDTO
        redisClient.set(token,toDTO(userInfo));        return new LoginResponse(token);

    }    @RequestMapping(value = "/sendVerifyCode", method = RequestMethod.POST)    @ResponseBody
    public Response sendVerifyCode(@RequestParam(value="mobile", required = false) String mobile,
                                   @RequestParam(value="email", required = false) String email) {

        String message = "Verify code is:";
        String code = randomCode("0123456789", 6);        try {            boolean result = false;            if(StringUtils.isNotBlank(mobile)) {
                result = serviceProvider.getMessasgeService().sendMobileMessage(mobile, message+code);
                redisClient.set(mobile, code);
            } else if(StringUtils.isNotBlank(email)) {
                result = serviceProvider.getMessasgeService().sendEmailMessage(email, message+code);
                redisClient.set(email, code);
            } else {                return Response.MOBILE_OR_EMAIL_REQUIRED;
            }            if(!result) {                return Response.SEND_VERIFYCODE_FAILED;
            }
        } catch (TException e) {
            e.printStackTrace();            return Response.exception(e);
        }        return Response.SUCCESS;

    }    @RequestMapping(value="/register", method = RequestMethod.POST)    @ResponseBody
    public Response register(@RequestParam("username") String username,
                             @RequestParam("password") String password,
                             @RequestParam(value="mobile", required = false) String mobile,
                             @RequestParam(value="email", required = false) String email,
                             @RequestParam("verifyCode") String verifyCode) {        if(StringUtils.isBlank(mobile) && StringUtils.isBlank(email)) {            return Response.MOBILE_OR_EMAIL_REQUIRED;
        }        if(StringUtils.isNotBlank(mobile)) {
            String redisCode = redisClient.get(mobile);            if(!verifyCode.equals(redisCode)) {                return Response.VERIFY_CODE_INVALID;
            }
        }else {
            String redisCode = redisClient.get(email);            if(!verifyCode.equals(redisCode)) {                return Response.VERIFY_CODE_INVALID;
            }
        }
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername(username);
        userInfo.setPassword(md5(password));
        userInfo.setMobile(mobile);
        userInfo.setEmail(email);        try {
            serviceProvider.getUserService().regiserUser(userInfo);
        } catch (TException e) {
            e.printStackTrace();            return Response.exception(e);
        }        return Response.SUCCESS;
    }    private UserDTO toDTO(UserInfo userInfo) {
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(userInfo, userDTO);        return userDTO;
    }    private String genToken() {        return randomCode("0123456789abcdefghijklmnopqrstuvwxyz", 32);
    }    private String randomCode(String s, int size) {
        StringBuilder result = new StringBuilder(size);

        Random random = new Random();        for(int i=0;i<size;i++) {            int loc = random.nextInt(s.length());
            result.append(s.charAt(loc));
        }        return result.toString();
    }    private String md5(String password) {        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");            byte[] md5Bytes = md5.digest(password.getBytes("utf-8"));            return HexUtils.toHexString(md5Bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }        return null;
    }
}

webp

测试准备工作

  1. 数据库内添加一条记录(如果不添加记录会报getUsername是null,通过在线网站生成MD5的密码加密)

  2. 启动user-thrift-service 和 message-thrift-service 2个服务

  3. 启动user-edge-service

  4. 工具调用接口访问user-edge-service里面的接口,查看是否正常返回调用服务是否成功

  5. 登录操作查看是否redis内有存储内容

webp

webp

webp

webp

webp

webp

流程梳理

  1. 建立2个thrift api项目 user 和 message 通过thrift文件生成对应语言的方法。

  2. 建立2个服务端项目,各自引入user和message的api项目。

  3. 多种语言比较特殊,例如message里面需要两边都通过python端需要通过thirft生成对应的python代码方便python制作server端。java端调用需要通过
    thirft升成对应的java代码方便其他项目的引用。

  4. 如果都是单语言的话,都是java的话,只需要生成一个thrift的java代码,和对应的server端服务端代码就可以了。

  5. 对于使用端只需要引用2个api就可以实现RPC的调用。但是需要记住的是他们之前的协议和传输内容必须一致才可以完成通信。

user-edge-service-client 新建--单点登录的服务

webp



作者:IT人故事会
链接:https://www.jianshu.com/p/009380d841d5


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP